Index: pkg/polymer/lib/src/declaration.dart |
diff --git a/pkg/polymer/lib/src/declaration.dart b/pkg/polymer/lib/src/declaration.dart |
index 7eaf11a1a8bc19f5885ca6910398ec3eaf9e3ab2..8e14b058cb71d521f745a35a1dcfcd817355c119 100644 |
--- a/pkg/polymer/lib/src/declaration.dart |
+++ b/pkg/polymer/lib/src/declaration.dart |
@@ -41,23 +41,29 @@ class PolymerDeclaration extends HtmlElement { |
PolymerDeclaration _super; |
PolymerDeclaration get superDeclaration => _super; |
+ String _extendsName; |
+ |
String _name; |
String get name => _name; |
/** |
* Map of publish properties. Can be a [VariableMirror] or a [MethodMirror] |
* representing a getter. If it is a getter, there will also be a setter. |
+ * |
+ * Note: technically these are always single properties, so we could use |
+ * a Symbol instead of a PropertyPath. However there are lookups between |
+ * this map and [_observe] so it is easier to just track paths. |
*/ |
- Map<Symbol, DeclarationMirror> _publish; |
+ Map<PropertyPath, DeclarationMirror> _publish; |
/** The names of published properties for this polymer-element. */ |
- Iterable<Symbol> get publishedProperties => |
- _publish != null ? _publish.keys : const []; |
+ Iterable<String> get publishedProperties => |
+ _publish != null ? _publish.keys.map((p) => '$p') : const []; |
/** Same as [_publish] but with lower case names. */ |
Map<String, DeclarationMirror> _publishLC; |
- Map<Symbol, Symbol> _observe; |
+ Map<PropertyPath, Symbol> _observe; |
Map<String, Object> _instanceAttributes; |
@@ -83,6 +89,8 @@ class PolymerDeclaration extends HtmlElement { |
PolymerDeclaration.created() : super.created() { |
// fetch the element name |
_name = attributes['name']; |
+ // fetch our extendee name |
+ _extendsName = attributes['extends']; |
// install element definition, if ready |
registerWhenReady(); |
} |
@@ -92,9 +100,7 @@ class PolymerDeclaration extends HtmlElement { |
if (waitingForType(name)) { |
return; |
} |
- // fetch our extendee name |
- var extendee = attributes['extends']; |
- if (waitingForExtendee(extendee)) { |
+ if (waitingForExtendee(_extendsName)) { |
//console.warn(name + ': waitingForExtendee:' + extendee); |
return; |
} |
@@ -106,7 +112,7 @@ class PolymerDeclaration extends HtmlElement { |
// finalizing elements in the main document |
// TODO(jmesserly): Polymer.dart waits for HTMLImportsLoaded, so I've |
// removed "whenImportsLoaded" for now. Restore the workaround if needed. |
- _register(extendee); |
+ _register(_extendsName); |
} |
void _register(extendee) { |
@@ -188,10 +194,13 @@ class PolymerDeclaration extends HtmlElement { |
// transcribe `attributes` declarations onto own prototype's `publish` |
publishAttributes(cls, _super); |
- publishProperties(type); |
+ publishProperties(_type); |
inferObservers(cls); |
+ // desugar compound observer syntax, e.g. @ObserveProperty('a b c') |
+ explodeObservers(cls); |
+ |
// Skip the rest in Dart: |
// chain various meta-data objects to inherited versions |
// chain custom api to inherited |
@@ -209,6 +218,8 @@ class PolymerDeclaration extends HtmlElement { |
// install external stylesheets as if they are inline |
installSheets(); |
+ adjustShadowElement(); |
+ |
// TODO(sorvell): install a helper method this.resolvePath to aid in |
// setting resource paths. e.g. |
// this.$.image.src = this.resolvePath('images/foo.png') |
@@ -231,6 +242,22 @@ class PolymerDeclaration extends HtmlElement { |
} |
} |
+ // TODO(sorvell): remove when spec addressed: |
+ // https://www.w3.org/Bugs/Public/show_bug.cgi?id=22460 |
+ // make <shadow></shadow> be <shadow><content></content></shadow> |
+ void adjustShadowElement() { |
+ // TODO(sorvell): avoid under SD polyfill until this bug is addressed: |
+ // https://github.com/Polymer/ShadowDOM/issues/297 |
+ if (!_hasShadowDomPolyfill) { |
+ final content = templateContent; |
+ if (content == null) return; |
+ |
+ for (var s in content.querySelectorAll('shadow')) { |
+ if (s.nodes.isEmpty) s.append(new ContentElement()); |
+ } |
+ } |
+ } |
+ |
void registerType(String name) { |
var baseTag; |
var decl = this; |
@@ -257,16 +284,19 @@ class PolymerDeclaration extends HtmlElement { |
if (attrs != null) { |
// names='a b c' or names='a,b,c' |
// record each name for publishing |
- for (var attr in attrs.split(attrs.contains(',') ? ',' : ' ')) { |
+ for (var attr in attrs.split(_ATTRIBUTES_REGEX)) { |
// remove excess ws |
attr = attr.trim(); |
// do not override explicit entries |
- if (attr != '' && _publish != null && _publish.containsKey(attr)) { |
+ if (attr == '') continue; |
+ |
+ var property = new Symbol(attr); |
+ var path = new PropertyPath([property]); |
+ if (_publish != null && _publish.containsKey(path)) { |
continue; |
} |
- var property = new Symbol(attr); |
var mirror = _getProperty(cls, property); |
if (mirror == null) { |
window.console.warn('property for attribute $attr of polymer-element ' |
@@ -274,7 +304,7 @@ class PolymerDeclaration extends HtmlElement { |
continue; |
} |
if (_publish == null) _publish = {}; |
- _publish[property] = mirror; |
+ _publish[path] = mirror; |
} |
} |
@@ -378,7 +408,7 @@ class PolymerDeclaration extends HtmlElement { |
var nodes = this.querySelectorAll(selector).toList(); |
var content = templateContent; |
if (content != null) { |
- nodes = nodes..addAll(content.queryAll(selector)); |
+ nodes = nodes..addAll(content.querySelectorAll(selector)); |
} |
if (matcher != null) return nodes.where(matcher).toList(); |
return nodes; |
@@ -408,7 +438,7 @@ class PolymerDeclaration extends HtmlElement { |
} |
// handle cached style elements |
for (var style in styles.where(matcher)) { |
- cssText..write(style.textContent)..write('\n\n'); |
Siggi Cherem (dart-lang)
2014/02/03 22:52:48
was this just broken before :/?
Jennifer Messerly
2014/02/04 00:33:06
apparently, yes :)
|
+ cssText..write(style.text)..write('\n\n'); |
} |
return cssText.toString(); |
} |
@@ -430,11 +460,8 @@ class PolymerDeclaration extends HtmlElement { |
* fetch a list of all observable properties names in our inheritance chain |
* above Polymer. |
*/ |
- // TODO(sjmiles): perf: reflection is slow, relatively speaking |
- // If an element may take 6us to create, getCustomPropertyNames might |
- // cost 1.6us more. |
void inferObservers(ClassMirror cls) { |
- if (cls == _objectType) return; |
+ if (cls == _htmlElementType) return; |
inferObservers(cls.superclass); |
for (var method in cls.declarations.values) { |
if (method is! MethodMirror || method.isStatic |
@@ -442,22 +469,43 @@ class PolymerDeclaration extends HtmlElement { |
String name = MirrorSystem.getName(method.simpleName); |
if (name.endsWith(_OBSERVE_SUFFIX) && name != 'attributeChanged') { |
- if (_observe == null) _observe = new Map(); |
+ // TODO(jmesserly): now that we have a better system, should we |
+ // deprecate *Changed methods? |
+ if (_observe == null) _observe = new HashMap(); |
name = name.substring(0, name.length - 7); |
- _observe[new Symbol(name)] = method.simpleName; |
+ _observe[new PropertyPath(name)] = method.simpleName; |
} |
} |
} |
+ void explodeObservers(ClassMirror cls) { |
Siggi Cherem (dart-lang)
2014/02/03 22:52:48
nit: consider adding a short doc here to point to
Jennifer Messerly
2014/02/04 00:33:06
sure. wish polymer-js would doc these :|
|
+ if (cls == _htmlElementType) return; |
+ |
+ explodeObservers(cls.superclass); |
+ for (var method in cls.declarations.values) { |
+ if (method is! MethodMirror || method.isStatic |
+ || !method.isRegularMethod) continue; |
+ |
+ for (var meta in _safeGetMetadata(method)) { |
+ if (meta.reflectee is! ObserveProperty) continue; |
+ |
+ if (_observe == null) _observe = new HashMap(); |
+ |
+ for (String name in meta.reflectee.names) { |
+ _observe[new PropertyPath(name)] = method.simpleName; |
Siggi Cherem (dart-lang)
2014/02/03 22:52:48
what if 2 methods react on the same path?
@Observ
Jennifer Messerly
2014/02/04 00:33:06
good catch. JS doesn't support this. Added support
|
+ } |
+ } |
+ } |
+ } |
void publishProperties(Type type) { |
Siggi Cherem (dart-lang)
2014/02/03 22:52:48
nit: + empty line above
Jennifer Messerly
2014/02/04 00:33:06
Done.
|
// Dart note: _publish was already populated by publishAttributes |
if (_publish != null) _publishLC = _lowerCaseMap(_publish); |
} |
- Map<String, dynamic> _lowerCaseMap(Map<Symbol, dynamic> properties) { |
+ Map<String, dynamic> _lowerCaseMap(Map<PropertyPath, dynamic> properties) { |
final map = new Map<String, dynamic>(); |
properties.forEach((name, value) { |
- map[MirrorSystem.getName(name).toLowerCase()] = value; |
+ map['$name'.toLowerCase()] = value; |
Siggi Cherem (dart-lang)
2014/02/03 22:52:48
'$name' -> name ? or is this to protect from key b
Jennifer Messerly
2014/02/04 00:33:06
name is a PropertyPath, not a String. Clarified.
|
}); |
return map; |
} |
@@ -495,10 +543,10 @@ bool _isRegistered(String name) => _declarations.containsKey(name); |
PolymerDeclaration _getDeclaration(String name) => _declarations[name]; |
final _objectType = reflectClass(Object); |
- |
+final _htmlElementType = reflectClass(HtmlElement); |
Map _getPublishedProperties(ClassMirror cls, Map props) { |
- if (cls == _objectType) return props; |
+ if (cls == _htmlElementType) return props; |
props = _getPublishedProperties(cls.superclass, props); |
for (var member in cls.declarations.values) { |
if (member.isStatic || member.isPrivate) continue; |
@@ -512,7 +560,7 @@ Map _getPublishedProperties(ClassMirror cls, Map props) { |
// it's a tad expensive. |
if (member is! MethodMirror || _hasSetter(cls, member)) { |
if (props == null) props = {}; |
- props[member.simpleName] = member; |
+ props[new PropertyPath([member.simpleName])] = member; |
} |
break; |
} |
@@ -545,6 +593,19 @@ DeclarationMirror _getProperty(ClassMirror cls, Symbol property) { |
return null; |
} |
+List _safeGetMetadata(MethodMirror method) { |
+ // TODO(jmesserly): dart2js blows up getting metadata from methods in some |
+ // cases. Why does this happen? It seems like the culprit might be named |
+ // arguments. Unfortunately even calling method.parameters also |
+ // triggers the bug in computeFunctionRti. For now we guard against it |
+ // with this check. |
+ try { |
+ return method.metadata; |
+ } catch (e) { |
+ return []; |
+ } |
+} |
+ |
bool _hasSetter(ClassMirror cls, MethodMirror getter) { |
var setterName = new Symbol('${MirrorSystem.getName(getter.simpleName)}='); |
var mirror = cls.declarations[setterName]; |
@@ -565,8 +626,7 @@ String _removeEventPrefix(String name) => name.substring(_EVENT_PREFIX.length); |
*/ |
void _shimShadowDomStyling(DocumentFragment template, String name, |
String extendee) { |
- if (js.context == null || template == null) return; |
- if (!js.context.hasProperty('ShadowDOMPolyfill')) return; |
+ if (template == null || !_hasShadowDomPolyfill) return; |
var platform = js.context['Platform']; |
if (platform == null) return; |
@@ -575,6 +635,9 @@ void _shimShadowDomStyling(DocumentFragment template, String name, |
shadowCss.callMethod('shimStyling', [template, name, extendee]); |
} |
+final bool _hasShadowDomPolyfill = js.context != null && |
+ js.context.hasProperty('ShadowDOMPolyfill'); |
+ |
const _STYLE_SELECTOR = 'style'; |
const _SHEET_SELECTOR = '[rel=stylesheet]'; |
const _STYLE_GLOBAL_SCOPE = 'global'; |
@@ -627,6 +690,7 @@ final _eventTranslations = const { |
'domfocusout': 'DOMFocusOut', |
'domfocusin': 'DOMFocusIn', |
+ 'dommousescroll': 'DOMMouseScroll', |
// TODO(jmesserly): Dart specific renames. Reconcile with Polymer.js |
'animationend': 'webkitAnimationEnd', |
@@ -657,3 +721,5 @@ String _eventNameFromType(String eventType) { |
final result = _reverseEventTranslations[eventType]; |
return result != null ? result : eventType; |
} |
+ |
+final _ATTRIBUTES_REGEX = new RegExp(r'\s|,'); |