| Index: dart/pkg/polymer/lib/src/declaration.dart
|
| ===================================================================
|
| --- dart/pkg/polymer/lib/src/declaration.dart (revision 37358)
|
| +++ dart/pkg/polymer/lib/src/declaration.dart (working copy)
|
| @@ -9,6 +9,9 @@
|
| /// The data associated with a polymer-element declaration, if it is backed
|
| /// by a Dart class instead of a JavaScript prototype.
|
| class PolymerDeclaration {
|
| + /// The one syntax to rule them all.
|
| + static final BindingDelegate _polymerSyntax = new PolymerExpressions();
|
| +
|
| /// The polymer-element for this declaration.
|
| final HtmlElement element;
|
|
|
| @@ -40,14 +43,22 @@
|
|
|
| Map<String, Object> _instanceAttributes;
|
|
|
| + /// A set of properties that should be automatically reflected to attributes.
|
| + /// Typically this is used for CSS styling. If none, this variable will be
|
| + /// left as null.
|
| + Set<String> _reflect;
|
| +
|
| List<Element> _sheets;
|
| List<Element> get sheets => _sheets;
|
|
|
| List<Element> _styles;
|
| List<Element> get styles => _styles;
|
|
|
| + // The default syntax for polymer-elements.
|
| + PolymerExpressions syntax = _polymerSyntax;
|
| +
|
| DocumentFragment get templateContent {
|
| - final template = element.querySelector('template');
|
| + final template = fetchTemplate();
|
| return template != null ? templateBind(template).content : null;
|
| }
|
|
|
| @@ -62,19 +73,15 @@
|
| String get extendee => superDeclaration != null ?
|
| superDeclaration.name : null;
|
|
|
| + /// The root URI for assets.
|
| + Uri _rootUri;
|
| +
|
| // Dart note: since polymer-element is handled in JS now, we have a simplified
|
| // flow for registering. We don't need to wait for the supertype or the code
|
| // to be noticed.
|
| PolymerDeclaration(this.element, this.name, this.type, this.superDeclaration);
|
|
|
| void register() {
|
| - // build prototype combining extendee, Polymer base, and named api
|
| - buildType();
|
| -
|
| - // back reference declaration element
|
| - // TODO(sjmiles): replace `element` with `elementElement` or `declaration`
|
| - _declarations[name] = this;
|
| -
|
| // more declarative features
|
| desugar();
|
| // register our custom element
|
| @@ -85,12 +92,14 @@
|
| // publishConstructor();
|
| }
|
|
|
| - /// Gets the Dart type registered for this name, and sets up declarative
|
| - /// features. Fills in the [type] and [supertype] fields.
|
| - ///
|
| - /// *Note*: unlike the JavaScript version, we do not have to metaprogram the
|
| - /// prototype, which simplifies this method.
|
| - void buildType() {
|
| + /// Implement various declarative features.
|
| + // Dart note: this merges "buildPrototype" "desugarBeforeChaining" and
|
| + // "desugarAfterChaining", because we don't have prototypes.
|
| + void desugar() {
|
| +
|
| + // back reference declaration element
|
| + _declarations[name] = this;
|
| +
|
| // transcribe `attributes` declarations onto own prototype's `publish`
|
| publishAttributes(superDeclaration);
|
|
|
| @@ -101,32 +110,20 @@
|
| // desugar compound observer syntax, e.g. @ObserveProperty('a b c')
|
| explodeObservers();
|
|
|
| - // Skip the rest in Dart:
|
| - // chain various meta-data objects to inherited versions
|
| - // chain custom api to inherited
|
| - // build side-chained lists to optimize iterations
|
| - // inherit publishing meta-data
|
| - // x-platform fixup
|
| - }
|
| -
|
| - /// Implement various declarative features.
|
| - void desugar() {
|
| + // install mdv delegate on template
|
| + installBindingDelegate(fetchTemplate());
|
| + // install external stylesheets as if they are inline
|
| + installSheets();
|
| + // adjust any paths in dom from imports
|
| + resolveElementPaths(element);
|
| // compile list of attributes to copy to instances
|
| accumulateInstanceAttributes();
|
| // parse on-* delegates declared on `this` element
|
| parseHostEvents();
|
| - // 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.
|
| + // install a helper method this.resolvePath to aid in
|
| + // setting resource urls. e.g.
|
| // this.$.image.src = this.resolvePath('images/foo.png')
|
| - // Potentially remove when spec bug is addressed.
|
| - // https://www.w3.org/Bugs/Public/show_bug.cgi?id=21407
|
| - // TODO(jmesserly): resolvePath not ported, see first comment in this class.
|
| -
|
| + initResolvePath();
|
| // under ShadowDOMPolyfill, transforms to approximate missing CSS features
|
| _shimShadowDomStyling(templateContent, name, extendee);
|
|
|
| @@ -137,22 +134,6 @@
|
| }
|
| }
|
|
|
| - // 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;
|
| @@ -160,19 +141,65 @@
|
| baseTag = decl.element.attributes['extends'];
|
| decl = decl.superDeclaration;
|
| }
|
| - document.register(name, type, extendsTag: baseTag);
|
| + document.registerElement(name, type, extendsTag: baseTag);
|
| }
|
|
|
| + // from declaration/mdv.js
|
| + Element fetchTemplate() => element.querySelector('template');
|
| +
|
| + void installBindingDelegate(Element template) {
|
| + if (template != null) {
|
| + templateBind(template).bindingDelegate = this.syntax;
|
| + }
|
| + }
|
| +
|
| + // from declaration/path.js
|
| + void resolveElementPaths(Node node) {
|
| + if (_Platform == null) return;
|
| + _Platform['urlResolver'].callMethod('resolveDom', [node]);
|
| + }
|
| +
|
| + // Dart note: renamed from "addResolvePathApi".
|
| + void initResolvePath() {
|
| + // let assetpath attribute modify the resolve path
|
| + var assetPath = element.attributes['assetpath'];
|
| + if (assetPath == null) assetPath = '';
|
| + var base = Uri.parse(element.ownerDocument.baseUri);
|
| + _rootUri = base.resolve(assetPath);
|
| + }
|
| +
|
| + String resolvePath(String urlPath, [baseUrlOrString]) {
|
| + Uri base;
|
| + if (baseUrlOrString == null) {
|
| + // Dart note: this enforces the same invariant as JS, where you need to
|
| + // call addResolvePathApi first.
|
| + if (_rootUri == null) {
|
| + throw new StateError('call initResolvePath before calling resolvePath');
|
| + }
|
| + base = _rootUri;
|
| + } else if (baseUrlOrString is Uri) {
|
| + base = baseUrlOrString;
|
| + } else {
|
| + base = Uri.parse(baseUrlOrString);
|
| + }
|
| + return base.resolve(urlPath).toString();
|
| + }
|
| +
|
| void publishAttributes(PolymerDeclaration superDecl) {
|
| // get properties to publish
|
| - if (superDecl != null && superDecl._publish != null) {
|
| + if (superDecl != null) {
|
| // Dart note: even though we walk the type hierarchy in
|
| // _getPublishedProperties, this will additionally include any names
|
| // published via the `attributes` attribute.
|
| - _publish = new Map.from(superDecl._publish);
|
| + if (superDecl._publish != null) {
|
| + _publish = new Map.from(superDecl._publish);
|
| + }
|
| + if (superDecl._reflect != null) {
|
| + _reflect = new Set.from(superDecl._reflect);
|
| + }
|
| }
|
|
|
| - _publish = _getPublishedProperties(type, _publish);
|
| + _getPublishedProperties(type);
|
|
|
| // merge names from 'attributes' attribute
|
| var attrs = element.attributes['attributes'];
|
| @@ -208,6 +235,26 @@
|
| // but don't override
|
| }
|
|
|
| + void _getPublishedProperties(Type type) {
|
| + var options = const smoke.QueryOptions(includeInherited: true,
|
| + includeUpTo: HtmlElement, withAnnotations: const [PublishedProperty]);
|
| + for (var decl in smoke.query(type, options)) {
|
| + if (decl.isFinal) continue;
|
| + if (_publish == null) _publish = {};
|
| + _publish[new PropertyPath([decl.name])] = decl;
|
| +
|
| + // Should we reflect the property value to the attribute automatically?
|
| + if (decl.annotations
|
| + .where((a) => a is PublishedProperty)
|
| + .any((a) => a.reflect)) {
|
| +
|
| + if (_reflect == null) _reflect = new Set();
|
| + _reflect.add(smoke.symbolToName(decl.name));
|
| + }
|
| + }
|
| + }
|
| +
|
| +
|
| void accumulateInstanceAttributes() {
|
| // inherit instance attributes
|
| _instanceAttributes = new Map<String, Object>();
|
| @@ -226,8 +273,15 @@
|
| static bool isInstanceAttribute(name) {
|
| // do not clone these attributes onto instances
|
| final blackList = const {
|
| - 'name': 1, 'extends': 1, 'constructor': 1, 'noscript': 1,
|
| - 'attributes': 1};
|
| + 'name': 1,
|
| + 'extends': 1,
|
| + 'constructor': 1,
|
| + 'noscript': 1,
|
| + 'assetpath': 1,
|
| + 'cache-csstext': 1,
|
| + // add ATTRIBUTES_ATTRIBUTE to the blacklist
|
| + 'attributes': 1,
|
| + };
|
|
|
| return !blackList.containsKey(name) && !name.startsWith('on-');
|
| }
|
| @@ -255,6 +309,10 @@
|
| return (url.split('/')..removeLast()..add('')).join('/');
|
| }
|
|
|
| + // Dart note: loadStyles, convertSheetsToStyles, copySheetAttribute and
|
| + // findLoadableStyles are not ported because they're handled by Polymer JS
|
| + // before we get into [register].
|
| +
|
| /// Install external stylesheets loaded in <element> elements into the
|
| /// element's template.
|
| void installSheets() {
|
| @@ -290,9 +348,10 @@
|
| cssText..write(_cssTextFromSheet(sheet))..write('\n');
|
| }
|
| if (cssText.length > 0) {
|
| - content.insertBefore(
|
| - new StyleElement()..text = '$cssText',
|
| - content.firstChild);
|
| + var style = element.ownerDocument.createElement('style')
|
| + ..text = '$cssText';
|
| +
|
| + content.insertBefore(style, content.firstChild);
|
| }
|
| }
|
| }
|
| @@ -402,42 +461,19 @@
|
| bool _isRegistered(String name) => _declarations.containsKey(name);
|
| PolymerDeclaration _getDeclaration(String name) => _declarations[name];
|
|
|
| -Map<PropertyPath, smoke.Declaration> _getPublishedProperties(
|
| - Type type, Map<PropertyPath, smoke.Declaration> props) {
|
| - var options = const smoke.QueryOptions(includeInherited: true,
|
| - includeUpTo: HtmlElement, withAnnotations: const [PublishedProperty]);
|
| - for (var decl in smoke.query(type, options)) {
|
| - if (decl.isFinal) continue;
|
| - if (props == null) props = {};
|
| - props[new PropertyPath([decl.name])] = decl;
|
| - }
|
| - return props;
|
| -}
|
| -
|
| -/// Attribute prefix used for declarative event handlers.
|
| -const _EVENT_PREFIX = 'on-';
|
| -
|
| -/// Whether an attribute declares an event.
|
| -bool _hasEventPrefix(String attr) => attr.startsWith(_EVENT_PREFIX);
|
| -
|
| -String _removeEventPrefix(String name) => name.substring(_EVENT_PREFIX.length);
|
| -
|
| /// Using Polymer's platform/src/ShadowCSS.js passing the style tag's content.
|
| void _shimShadowDomStyling(DocumentFragment template, String name,
|
| String extendee) {
|
| - if (template == null || !_hasShadowDomPolyfill) return;
|
| + if (template == null || _ShadowCss == null) return;
|
|
|
| - var platform = js.context['Platform'];
|
| - if (platform == null) return;
|
| - var shadowCss = platform['ShadowCSS'];
|
| - if (shadowCss == null) return;
|
| - shadowCss.callMethod('shimStyling', [template, name, extendee]);
|
| + _ShadowCss.callMethod('shimStyling', [template, name, extendee]);
|
| }
|
|
|
| final bool _hasShadowDomPolyfill = js.context.hasProperty('ShadowDOMPolyfill');
|
| +final JsObject _ShadowCss = _Platform != null ? _Platform['ShadowCSS'] : null;
|
|
|
| const _STYLE_SELECTOR = 'style';
|
| -const _SHEET_SELECTOR = '[rel=stylesheet]';
|
| +const _SHEET_SELECTOR = 'link[rel=stylesheet]';
|
| const _STYLE_GLOBAL_SCOPE = 'global';
|
| const _SCOPE_ATTR = 'polymer-scope';
|
| const _STYLE_SCOPE_ATTRIBUTE = 'element';
|
| @@ -485,46 +521,7 @@
|
| return name.endsWith('Changed') && name != 'attributeChanged';
|
| }
|
|
|
| -// TODO(jmesserly): is this list complete?
|
| -final _eventTranslations = const {
|
| - // TODO(jmesserly): these three Polymer.js translations won't work in Dart,
|
| - // because we strip the webkit prefix (below). Reconcile.
|
| - 'webkitanimationstart': 'webkitAnimationStart',
|
| - 'webkitanimationend': 'webkitAnimationEnd',
|
| - 'webkittransitionend': 'webkitTransitionEnd',
|
|
|
| - 'domfocusout': 'DOMFocusOut',
|
| - 'domfocusin': 'DOMFocusIn',
|
| - 'dommousescroll': 'DOMMouseScroll',
|
| -
|
| - // TODO(jmesserly): Dart specific renames. Reconcile with Polymer.js
|
| - 'animationend': 'webkitAnimationEnd',
|
| - 'animationiteration': 'webkitAnimationIteration',
|
| - 'animationstart': 'webkitAnimationStart',
|
| - 'doubleclick': 'dblclick',
|
| - 'fullscreenchange': 'webkitfullscreenchange',
|
| - 'fullscreenerror': 'webkitfullscreenerror',
|
| - 'keyadded': 'webkitkeyadded',
|
| - 'keyerror': 'webkitkeyerror',
|
| - 'keymessage': 'webkitkeymessage',
|
| - 'needkey': 'webkitneedkey',
|
| - 'speechchange': 'webkitSpeechChange',
|
| -};
|
| -
|
| -final _reverseEventTranslations = () {
|
| - final map = new Map<String, String>();
|
| - _eventTranslations.forEach((onName, eventType) {
|
| - map[eventType] = onName;
|
| - });
|
| - return map;
|
| -}();
|
| -
|
| -// Dart note: we need this function because we have additional renames JS does
|
| -// not have. The JS renames are simply case differences, whereas we have ones
|
| -// like doubleclick -> dblclick and stripping the webkit prefix.
|
| -String _eventNameFromType(String eventType) {
|
| - final result = _reverseEventTranslations[eventType];
|
| - return result != null ? result : eventType;
|
| -}
|
| -
|
| final _ATTRIBUTES_REGEX = new RegExp(r'\s|,');
|
| +
|
| +final JsObject _Platform = js.context['Platform'];
|
|
|