| // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| // Teaches dart2js about the wrapping that is done by the Shadow DOM polyfill. |
| (function() { |
| var ShadowDOMPolyfill = window.ShadowDOMPolyfill; |
| if (!ShadowDOMPolyfill) return; |
| |
| if (navigator.userAgent.indexOf('(Dart)') !== -1) { |
| console.error("ShadowDOMPolyfill polyfill was loaded in Dartium. This " + |
| "will not work. This indicates that Dartium's Chrome version is " + |
| "not compatible with this version of web_components."); |
| } |
| |
| var needsConstructorFix = window.constructor === window.Window; |
| |
| // TODO(jmesserly): we need to wrap document somehow (a dart:html hook?) |
| |
| // dartNativeDispatchHooksTransformer is described on initHooks() in |
| // sdk/lib/_internal/lib/native_helper.dart. |
| if (typeof window.dartNativeDispatchHooksTransformer == 'undefined') |
| window.dartNativeDispatchHooksTransformer = []; |
| |
| window.dartNativeDispatchHooksTransformer.push(function(hooks) { |
| var NodeList = ShadowDOMPolyfill.wrappers.NodeList; |
| var ShadowRoot = ShadowDOMPolyfill.wrappers.ShadowRoot; |
| var unwrapIfNeeded = ShadowDOMPolyfill.unwrapIfNeeded; |
| var originalGetTag = hooks.getTag; |
| hooks.getTag = function getTag(obj) { |
| // TODO(jmesserly): do we still need these? |
| if (obj instanceof NodeList) return 'NodeList'; |
| if (obj instanceof ShadowRoot) return 'ShadowRoot'; |
| if (MutationRecord && (obj instanceof MutationRecord)) |
| return 'MutationRecord'; |
| if (MutationObserver && (obj instanceof MutationObserver)) |
| return 'MutationObserver'; |
| |
| // TODO(jmesserly): this prevents incorrect interaction between ShadowDOM |
| // and dart:html's <template> polyfill. Essentially, ShadowDOM is |
| // polyfilling native template, but our Dart polyfill fails to detect this |
| // because the unwrapped node is an HTMLUnknownElement, leading it to |
| // think the node has no content. |
| if (obj instanceof HTMLTemplateElement) return 'HTMLTemplateElement'; |
| |
| var unwrapped = unwrapIfNeeded(obj); |
| if (unwrapped && (needsConstructorFix || obj !== unwrapped)) { |
| // Fix up class names for Firefox, or if using the minified polyfill. |
| // dart2js prefers .constructor.name, but there are all kinds of cases |
| // where this will give the wrong answer. |
| var ctor = obj.constructor |
| if (ctor === unwrapped.constructor) { |
| var name = ctor._ShadowDOMPolyfill$cacheTag_; |
| if (!name) { |
| name = originalGetTag(unwrapped); |
| ctor._ShadowDOMPolyfill$cacheTag_ = name; |
| } |
| return name; |
| } |
| |
| obj = unwrapped; |
| } |
| return originalGetTag(obj); |
| } |
| }); |
| })(); |
| |
| // Updates document.registerElement so Dart can see when Javascript custom |
| // elements are created, and wrap them to provide a Dart friendly API. |
| (function (doc) { |
| var upgraders = {}; // upgrader associated with a custom-tag. |
| var unpatchableTags = {}; // set of custom-tags that can't be patched. |
| var pendingElements = {}; // will upgrade when/if an upgrader is installed. |
| var upgradeOldElements = true; |
| |
| var originalRegisterElement = doc.registerElement; |
| if (!originalRegisterElement) { |
| throw new Error('document.registerElement is not present.'); |
| } |
| |
| function reportError(name) { |
| console.error("Couldn't patch prototype to notify Dart when " + name + |
| " elements are created. This can be fixed by making the " + |
| "createdCallback in " + name + " a configurable property."); |
| } |
| |
| function registerElement(name, options) { |
| var proto, extendsOption; |
| if (options !== undefined) { |
| proto = options.prototype; |
| } else { |
| proto = Object.create(HTMLElement.prototype); |
| options = {protoptype: proto}; |
| } |
| |
| var original = proto.createdCallback; |
| var newCallback = function() { |
| original.call(this); |
| var name = (this.getAttribute('is') || this.localName).toLowerCase(); |
| var upgrader = upgraders[name]; |
| if (upgrader) { |
| upgrader(this); |
| } else if (upgradeOldElements) { |
| // Save this element in case we can upgrade it later when an upgrader is |
| // registered. |
| var list = pendingElements[name]; |
| if (!list) { |
| list = pendingElements[name] = []; |
| } |
| list.push(this); |
| } |
| }; |
| |
| var descriptor = Object.getOwnPropertyDescriptor(proto, 'createdCallback'); |
| if (!descriptor || descriptor.writable) { |
| proto.createdCallback = newCallback; |
| } else if (descriptor.configurable) { |
| descriptor['value'] = newCallback; |
| Object.defineProperty(proto, 'createdCallback', descriptor); |
| } else { |
| unpatchableTags[name] = true; |
| if (upgraders[name]) reportError(name); |
| } |
| return originalRegisterElement.call(this, name, options); |
| } |
| |
| function registerDartTypeUpgrader(name, upgrader) { |
| if (!upgrader) return; |
| name = name.toLowerCase(); |
| var existing = upgraders[name]; |
| if (existing) { |
| console.error('Already have a Dart type associated with ' + name); |
| return; |
| } |
| upgraders[name] = upgrader; |
| if (unpatchableTags[name]) reportError(name); |
| if (upgradeOldElements) { |
| // Upgrade elements that were created before the upgrader was registered. |
| var list = pendingElements[name]; |
| if (list) { |
| for (var i = 0; i < list.length; i++) { |
| upgrader(list[i]); |
| } |
| } |
| delete pendingElements[name]; |
| } else { |
| console.warn("Didn't expect more Dart types to be registered. '" + name |
| + "' elements that already exist in the page might not be wrapped."); |
| } |
| } |
| |
| function onlyUpgradeNewElements() { |
| upgradeOldElements = false; |
| pendingElements = null; |
| } |
| |
| // Native custom elements outside the app in Chrome have constructor |
| // names like "x-tag", which need to be translated to the DOM |
| // element they extend. When using the shadow dom polyfill this is |
| // take care of above. |
| var ShadowDOMPolyfill = window.ShadowDOMPolyfill; |
| if (!ShadowDOMPolyfill) { |
| // dartNativeDispatchHooksTransformer is described on initHooks() in |
| // sdk/lib/_internal/lib/native_helper.dart. |
| if (typeof window.dartNativeDispatchHooksTransformer == 'undefined') |
| window.dartNativeDispatchHooksTransformer = []; |
| |
| window.dartNativeDispatchHooksTransformer.push(function(hooks) { |
| var originalGetUnknownTag = hooks.getUnknownTag; |
| hooks.getUnknownTag = function(o, tag) { |
| if (/-/.test(tag)) { // "x-tag" |
| var s = Object.prototype.toString.call(o); |
| var match = s.match(/^\[object ([A-Za-z]*Element)\]$/); |
| if (match) { |
| return match[1]; |
| } |
| return originalGetUnknownTag(o, tag); |
| } |
| }; |
| }); |
| } |
| |
| doc._registerDartTypeUpgrader = registerDartTypeUpgrader; |
| doc._onlyUpgradeNewElements = onlyUpgradeNewElements; |
| doc.registerElement = registerElement; |
| })(document); |