From a7073c4fd54771522da06b759b7c974c3dd55c21 Mon Sep 17 00:00:00 2001 From: Russell Bicknell Date: Wed, 14 Dec 2016 12:33:49 -0800 Subject: [PATCH 01/21] Remove test assumption that `customElements` exists. --- src/native-shim.js | 3 +++ tests/html/imports.html | 1 + tests/html/shim.html | 6 ++++++ tests/index.html | 1 + 4 files changed, 11 insertions(+) diff --git a/src/native-shim.js b/src/native-shim.js index d55bfec..735f3d7 100644 --- a/src/native-shim.js +++ b/src/native-shim.js @@ -61,6 +61,9 @@ (() => { 'use strict'; + // Do nothing if `customElements` does not exist. + if (!window.customElements) return; + const NativeHTMLElement = window.HTMLElement; const nativeDefine = window.customElements.define; const nativeGet = window.customElements.get; diff --git a/tests/html/imports.html b/tests/html/imports.html index 8dbe461..88633b6 100644 --- a/tests/html/imports.html +++ b/tests/html/imports.html @@ -12,6 +12,7 @@ Custom Elements: imports integration From ee326ffd7bd77e72fad4aff0121995e5059cc5b6 Mon Sep 17 00:00:00 2001 From: Russell Bicknell Date: Fri, 16 Dec 2016 16:15:04 -0800 Subject: [PATCH 02/21] Use one-liner for forcing the polyfill. --- tests/html/imports.html | 3 +-- tests/index.html | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/html/imports.html b/tests/html/imports.html index 88633b6..cec31dc 100644 --- a/tests/html/imports.html +++ b/tests/html/imports.html @@ -12,8 +12,7 @@ Custom Elements: imports integration From f0eb664e1be43779539ed31ac203049ae59592fb Mon Sep 17 00:00:00 2001 From: Russell Bicknell Date: Fri, 16 Dec 2016 17:14:29 -0800 Subject: [PATCH 03/21] Update WCT and webcomponentsjs. --- bower.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/bower.json b/bower.json index 8238110..5daef7b 100644 --- a/bower.json +++ b/bower.json @@ -16,7 +16,7 @@ "license": "BSD", "ignore": [], "devDependencies": { - "web-component-tester": "^4.0.1", - "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.22" + "web-component-tester": "^5.0.0", + "webcomponentsjs": "webcomponents/webcomponentsjs#v1" } } diff --git a/package.json b/package.json index c2d6567..a4b2a2e 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,6 @@ "google-closure-compiler": "^20160822.1.0", "gulp": "^3.8.8", "gulp-sourcemaps": "^1.6.0", - "web-component-tester": "^4.0.1" + "web-component-tester": "^5.0.0" } } From 8ffa12b8e0752eec2d5ac5bb7f1ca27e09bac749 Mon Sep 17 00:00:00 2001 From: Russell Bicknell Date: Fri, 16 Dec 2016 17:18:35 -0800 Subject: [PATCH 04/21] Update HTMLImports polyfill path. --- tests/html/imports.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/html/imports.html b/tests/html/imports.html index cec31dc..7beffc8 100644 --- a/tests/html/imports.html +++ b/tests/html/imports.html @@ -34,7 +34,7 @@ - + + diff --git a/tests/html/shim.html b/tests/html/shim.html index 1ef9ee7..a68f878 100644 --- a/tests/html/shim.html +++ b/tests/html/shim.html @@ -10,6 +10,7 @@ --> Custom Elements Tests + diff --git a/tests/index.html b/tests/index.html index b64effa..368c058 100644 --- a/tests/index.html +++ b/tests/index.html @@ -13,6 +13,7 @@ + From e6eddb5b632752530d28a6ef48e9b63706cc19a0 Mon Sep 17 00:00:00 2001 From: Russell Bicknell Date: Mon, 19 Dec 2016 14:25:36 -0800 Subject: [PATCH 09/21] Test for 'HTMLElement#constructor' writable / configurable flags that match native, rather than being specifically true. They should be true per spec but it seems like Safari sets writable to false. --- tests/js/patching.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/js/patching.js b/tests/js/patching.js index 2694751..ace348a 100644 --- a/tests/js/patching.js +++ b/tests/js/patching.js @@ -13,9 +13,11 @@ suite('patching', function() { suite('HTMLElement', function () { test('constructor is configurable and writable', function() { + var nativeHTMLElement = customElements.nativeHTMLElement; + var nativeDescriptor = Object.getOwnPropertyDescriptor(nativeHTMLElement.prototype, 'constructor') var descriptor = Object.getOwnPropertyDescriptor(HTMLElement.prototype, 'constructor'); - assert.isTrue(descriptor.configurable); - assert.isTrue(descriptor.writable); + assert.equal(nativeDescriptor.configurable, descriptor.configurable); + assert.equal(nativeDescriptor.writable, descriptor.writable); }); }); From 0c82e24446c1d1f74eb2f5a8366a61388e30e7ec Mon Sep 17 00:00:00 2001 From: Russell Bicknell Date: Mon, 19 Dec 2016 14:53:02 -0800 Subject: [PATCH 10/21] Remove unnecessary `HTMLElement#constructor` descriptor test. --- tests/index.html | 1 - tests/js/patching.js | 25 ------------------------- 2 files changed, 26 deletions(-) delete mode 100644 tests/js/patching.js diff --git a/tests/index.html b/tests/index.html index 368c058..34d9eac 100644 --- a/tests/index.html +++ b/tests/index.html @@ -27,7 +27,6 @@ 'js/closure.js', 'js/upgrade.js', 'js/shadow-dom.js', - 'js/patching.js', 'html/imports.html', 'html/shim.html', // 'chromium/custom-elements/spec/callback.html', diff --git a/tests/js/patching.js b/tests/js/patching.js deleted file mode 100644 index ace348a..0000000 --- a/tests/js/patching.js +++ /dev/null @@ -1,25 +0,0 @@ -/** - * @license - * Copyright (c) 2016 The Polymer Project Authors. All rights reserved. - * This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt - * The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt - * The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt - * Code distributed by Google as part of the polymer project is also - * subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt - */ - -suite('patching', function() { - - suite('HTMLElement', function () { - - test('constructor is configurable and writable', function() { - var nativeHTMLElement = customElements.nativeHTMLElement; - var nativeDescriptor = Object.getOwnPropertyDescriptor(nativeHTMLElement.prototype, 'constructor') - var descriptor = Object.getOwnPropertyDescriptor(HTMLElement.prototype, 'constructor'); - assert.equal(nativeDescriptor.configurable, descriptor.configurable); - assert.equal(nativeDescriptor.writable, descriptor.writable); - }); - - }); - -}); From 4485b37cb05e425b0aadfe06371aaad9568f25f5 Mon Sep 17 00:00:00 2001 From: Joel Denning Date: Wed, 4 Jan 2017 20:09:41 -0700 Subject: [PATCH 11/21] It's sort of working --- src/custom-elements.js | 38 +++++++++++++++++++++++++++++++------- 1 file changed, 31 insertions(+), 7 deletions(-) diff --git a/src/custom-elements.js b/src/custom-elements.js index 0e64c1a..027544d 100644 --- a/src/custom-elements.js +++ b/src/custom-elements.js @@ -212,9 +212,25 @@ let Deferred; // 8: /** @type {string} */ - const localName = name; + let localName = name; + + // 9, 10 + if (options.extends) { + // 7.1 + const extendsNameError = checkValidCustomElementName(options.extends); + if (!extendsNameError) { + throw new Error(`Cannot extend '${options.extends}': A custom element cannot extend a custom element.`); + } + + // 7.2 + const el = document.createElement(options.extends); + if (el.constructor === window.HTMLUnknownElement) { + throw new Error(`Cannot extend '${options.extends}': is not a real HTMLElement`); + } - // 9, 10: We do not support extends currently. + // 7.3 + localName = options.extends; + } // 11, 12, 13: Our define() isn't rentrant-safe @@ -269,7 +285,7 @@ let Deferred; }; // 16: - this._definitions.set(localName, definition); + this._definitions.set(name, definition); this._constructors.set(constructor, localName); // 17, 18, 19: @@ -468,7 +484,11 @@ let Deferred; visitedNodes.add(element); /** @type {?CustomElementDefinition} */ - const definition = this._definitions.get(element.localName); + const isAttr = element.getAttribute('is'); + if (typeof isAttr === 'string') { + element.is = isAttr; + } + const definition = this._definitions.get(getCustomElementName(element)); if (definition) { if (!element[_upgradedProp]) { this._upgradeElement(element, definition, true); @@ -565,7 +585,7 @@ let Deferred; const node = walker.currentNode; if (node[_upgradedProp] && node[_attachedProp]) { node[_attachedProp] = false; - const definition = this._definitions.get(node.localName); + const definition = this._definitions.get(getCustomElementName(node)); if (definition && definition.disconnectedCallback) { definition.disconnectedCallback.call(node); } @@ -624,7 +644,7 @@ let Deferred; const target = /** @type {HTMLElement} */(mutation.target); // We should be gaurenteed to have a definition because this mutation // observer is only observing custom elements observedAttributes - const definition = this._definitions.get(target.localName); + const definition = this._definitions.get(getCustomElementName(target)); const name = /** @type {!string} */(mutation.attributeName); const oldValue = mutation.oldValue; const newValue = target.getAttribute(name); @@ -638,6 +658,10 @@ let Deferred; } } + function getCustomElementName(element) { + return typeof element.is === 'string' ? element.is : element.tagName; + } + // Closure Compiler Exports window['CustomElementRegistry'] = CustomElementRegistry; CustomElementRegistry.prototype['define'] = CustomElementRegistry.prototype.define; @@ -772,7 +796,7 @@ let Deferred; // Bail if this wasn't a fully upgraded custom element if (element[_upgradedProp] == true) { - const definition = _customElements()._definitions.get(element.localName); + const definition = _customElements()._definitions.get(getCustomElementName(element)); const observedAttributes = definition.observedAttributes; const attributeChangedCallback = definition.attributeChangedCallback; if (attributeChangedCallback && observedAttributes.indexOf(name) >= 0) { From 851fd1ba54d131e893d2d7a9e63a360ac321b76a Mon Sep 17 00:00:00 2001 From: Joel Denning Date: Mon, 9 Jan 2017 09:25:39 -0700 Subject: [PATCH 12/21] subclassing things --- src/custom-elements.js | 70 ++++++++++++++++++++++++++---------------- 1 file changed, 44 insertions(+), 26 deletions(-) diff --git a/src/custom-elements.js b/src/custom-elements.js index 027544d..b24127a 100644 --- a/src/custom-elements.js +++ b/src/custom-elements.js @@ -215,7 +215,7 @@ let Deferred; let localName = name; // 9, 10 - if (options.extends) { + if (options && options.extends) { // 7.1 const extendsNameError = checkValidCustomElementName(options.extends); if (!extendsNameError) { @@ -673,34 +673,43 @@ let Deferred; CustomElementRegistry.prototype['_observeRoot'] = CustomElementRegistry.prototype._observeRoot; CustomElementRegistry.prototype['_addImport'] = CustomElementRegistry.prototype._addImport; - // patch window.HTMLElement + patchElement('HTMLElement') + var htmlElementSubclasses = [ 'Button', 'Canvas', 'Data', 'Head', 'Mod', 'TableCell', 'TableCol', 'Anchor', 'Area', 'Base', 'Body', 'BR', 'DataList', 'Details', 'Dialog', 'Div', 'DList', 'Embed', 'FieldSet', 'Form', 'Heading', 'HR', 'Html', 'IFrame', 'Image', 'Input', 'Keygen', 'Label', 'Legend', 'LI', 'Link', 'Map', 'Media', 'Menu', 'MenuItem', 'Meta', 'Meter', 'Object', 'OList', 'OptGroup', 'Option', 'Output', 'Paragraph', 'Param', 'Picture', 'Pre', 'Progress', 'Quote', 'Script', 'Select', 'Slot', 'Source', 'Span', 'Style', 'TableCaption', 'Table', 'TableRow', 'TableSection', 'Template', 'TextArea', 'Time', 'Title', 'Track', 'UList', 'Unknown']; + for (let index in htmlElementSubclasses) { + patchElement(`HTML${htmlElementSubclasses[index]}Element`); + } - /** @const */ - const origHTMLElement = win.HTMLElement; - CustomElementRegistry.prototype['nativeHTMLElement'] = origHTMLElement; - /** - * @type {function(new: HTMLElement)} - */ - const newHTMLElement = function HTMLElement() { - const customElements = _customElements(); + function patchElement(varName) { + /** @const */ + const origHTMLElement = win[varName]; + if (!origHTMLElement) { + return; + } + CustomElementRegistry.prototype[`native${varName}`] = origHTMLElement; + /** + * @type {function(new: HTMLElement)} + */ + const newHTMLElement = function() { + const customElements = _customElements(); - // If there's an being upgraded, return that - if (customElements._newInstance) { - const i = customElements._newInstance; - customElements._newInstance = null; - return i; - } - if (this.constructor) { - // Find the tagname of the constructor and create a new element with it - const tagName = customElements._constructors.get(this.constructor); - return _createElement(doc, tagName, undefined, false); - } - throw new Error('Unknown constructor. Did you call customElements.define()?'); + // If there's an being upgraded, return that + if (customElements._newInstance) { + const i = customElements._newInstance; + customElements._newInstance = null; + return i; + } + if (this.constructor) { + // Find the tagname of the constructor and create a new element with it + const tagName = customElements._constructors.get(this.constructor); + return _createElement(doc, tagName, undefined, false); + } + throw new Error('Unknown constructor. Did you call customElements.define()?'); + } + win[varName] = newHTMLElement; + win[varName].prototype = Object.create(origHTMLElement.prototype, { + constructor: {value: newHTMLElement, configurable: true, writable: true}, + }); } - win.HTMLElement = newHTMLElement; - win.HTMLElement.prototype = Object.create(origHTMLElement.prototype, { - constructor: {value: win.HTMLElement, configurable: true, writable: true}, - }); // patch doc.createElement // TODO(justinfagnani): why is the cast neccessary? @@ -721,8 +730,17 @@ let Deferred; */ function _createElement(doc, tagName, options, callConstructor) { const customElements = _customElements(); + let isAttr; + if (options && options.is) { + // We're going to take care of setting the is attribute ourselves + isAttr = options.is; + delete options.is; + } const element = options ? _origCreateElement.call(doc, tagName, options) : _origCreateElement.call(doc, tagName); + if (isAttr) { + element.setAttribute('is', isAttr); + } const definition = customElements._definitions.get(tagName.toLowerCase()); if (definition) { customElements._upgradeElement(element, definition, callConstructor); From 93d812ae7e2938b10c963ff7c95dbb8b7aa8a8ed Mon Sep 17 00:00:00 2001 From: Joel Denning Date: Mon, 9 Jan 2017 14:54:35 -0700 Subject: [PATCH 13/21] Tests passing --- CONTRIBUTING.md | 4 +- DEVELOPING.md | 8 +- custom-elements.min.js | 27 ++--- custom-elements.min.js.map | 2 +- src/README.md | 6 +- src/custom-elements.js | 50 +++++---- tests/index.html | 2 +- tests/js/babel.js | 32 ++++++ tests/js/reactions.js | 218 ++++++++++++++++++++++++++++++++++++- tests/js/registry.js | 15 +++ 10 files changed, 314 insertions(+), 50 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 00381ed..298e63e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -30,9 +30,9 @@ Other projects require a similar agreement: jQuery, Firefox, Apache, Node, and m cd $REPO npm install - gulp build + npm run build -The builds will be placed into the `dist/` directory if all goes well. +The builds will be placed into the custom-elements.min.js file if all goes well. 1. Commit your code and make a pull request. diff --git a/DEVELOPING.md b/DEVELOPING.md index 9b5f4f8..f7ceeb5 100644 --- a/DEVELOPING.md +++ b/DEVELOPING.md @@ -11,10 +11,8 @@ reactions. The source references old versions of the spec. ### To do 1. Implement Node#isConnected - 2. Implement built-in element extension (is=) - 3. Add reaction callback ordering tests - 4. Reorganize tests to be closer to spec structure - 5. Performance tests + 2. Reorganize tests to be closer to spec structure + 3. Performance tests ## Building & Running Tests @@ -76,7 +74,7 @@ the other webcomponentsjs polyfills, and its build is not integrated into the de To build run: - gulp CustomElementsV1 + npm run build This creates a CustomElementsV1.min.js file in dist/ diff --git a/custom-elements.min.js b/custom-elements.min.js index ff12241..cad341e 100644 --- a/custom-elements.min.js +++ b/custom-elements.min.js @@ -7,18 +7,19 @@ Code distributed by Google as part of the polymer project is also subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt */ -'use strict';(function(){function c(){this.a=new Map;this.j=new Map;this.h=new Map;this.o=new Set;this.C=new MutationObserver(this.D.bind(this));this.f=null;this.F=new Set;this.enableFlush=!0;this.s=!1;this.m=null}function g(){return h.customElements}function l(a){if(!/^[a-z][.0-9_a-z]*-[\-.0-9_a-z]*$/.test(a)||-1!==t.indexOf(a))return Error("The element name '"+a+"' is not valid.")}function m(a,b,e,d){var c=g();a=e?n.call(a,b,e):n.call(a,b);(b=c.a.get(b.toLowerCase()))&&c.u(a,b,d);c.b(a);return a} -function p(a,b,e,d){b=b.toLowerCase();var c=a.getAttribute(b);d.call(a,b,e);1==a.__$CE_upgraded&&(d=g().a.get(a.localName),e=d.A,(d=d.i)&&0<=e.indexOf(b)&&(e=a.getAttribute(b),e!==c&&d.call(a,b,c,e,null)))}var f=document,h=window;if(g()&&(g().g=function(){},!g().forcePolyfill))return;var t="annotation-xml color-profile font-face font-face-src font-face-uri font-face-format font-face-name missing-glyph".split(" ");c.prototype.L=function(a,b){function e(a){var b=f[a];if(void 0!==b&&"function"!==typeof b)throw Error(c+ -" '"+a+"' is not a Function");return b}if("function"!==typeof b)throw new TypeError("constructor must be a Constructor");var d=l(a);if(d)throw d;if(this.a.has(a))throw Error("An element with name '"+a+"' is already defined");if(this.j.has(b))throw Error("Definition failed for '"+a+"': The constructor is already used.");var c=a,f=b.prototype;if("object"!==typeof f)throw new TypeError("Definition failed for '"+a+"': constructor.prototype must be an object");var d=e("connectedCallback"),g=e("disconnectedCallback"), -k=e("attributeChangedCallback");this.a.set(c,{name:a,localName:c,constructor:b,v:d,w:g,i:k,A:k&&b.observedAttributes||[]});this.j.set(b,c);this.K();if(a=this.h.get(c))a.resolve(void 0),this.h.delete(c)};c.prototype.get=function(a){return(a=this.a.get(a))?a.constructor:void 0};c.prototype.M=function(a){var b=l(a);if(b)return Promise.reject(b);if(this.a.has(a))return Promise.resolve();if(b=this.h.get(a))return b.N;var e,d=new Promise(function(a){e=a}),b={N:d,resolve:e};this.h.set(a,b);return d};c.prototype.g= -function(){this.enableFlush&&(this.l(this.m.takeRecords()),this.D(this.C.takeRecords()),this.o.forEach(function(a){this.l(a.takeRecords())},this))};c.prototype.K=function(){var a=this;if(!this.s){this.s=!0;var b=function(){a.s=!1;a.m||(a.m=a.b(f));a.c(f.childNodes)};window.HTMLImports?window.HTMLImports.whenReady(b):b()}};c.prototype.I=function(a){this.f=a};c.prototype.b=function(a){if(null!=a.__$CE_observer)return a.__$CE_observer;a.__$CE_observer=new MutationObserver(this.l.bind(this));a.__$CE_observer.observe(a, -{childList:!0,subtree:!0});this.enableFlush&&this.o.add(a.__$CE_observer);return a.__$CE_observer};c.prototype.J=function(a){null!=a.__$CE_observer&&(a.__$CE_observer.disconnect(),this.enableFlush&&this.o.delete(a.__$CE_observer),a.__$CE_observer=null)};c.prototype.l=function(a){for(var b=0;b,\n * }}\n */\nlet CustomElementDefinition;\n\n/**\n * @typedef {{\n * resolve: !function(undefined),\n * promise: !Promise,\n * }}\n */\nlet Deferred;\n\n(function() {\n 'use strict';\n\n const doc = document;\n const win = window;\n\n /**\n * Gets 'customElement' from window so that it could be modified after\n * the polyfill loads.\n * @function\n * @return {CustomElementRegistry}\n */\n const _customElements = () => win['customElements'];\n\n const _observerProp = '__$CE_observer';\n const _attachedProp = '__$CE_attached';\n const _upgradedProp = '__$CE_upgraded';\n\n if (_customElements()) {\n _customElements().flush = function() {};\n if (!_customElements().forcePolyfill) {\n return;\n }\n }\n\n // name validation\n // https://html.spec.whatwg.org/multipage/scripting.html#valid-custom-element-name\n\n /**\n * @const\n * @type {Array}\n */\n const reservedTagList = [\n 'annotation-xml',\n 'color-profile',\n 'font-face',\n 'font-face-src',\n 'font-face-uri',\n 'font-face-format',\n 'font-face-name',\n 'missing-glyph',\n ];\n\n /**\n * @param {!string} name\n * @return {!Error|undefined}\n */\n function checkValidCustomElementName(name) {\n if (!(/^[a-z][.0-9_a-z]*-[\\-.0-9_a-z]*$/.test(name) &&\n reservedTagList.indexOf(name) === -1)) {\n return new Error(`The element name '${name}' is not valid.`)\n }\n }\n\n /**\n * @param {!Node} root\n * @return {TreeWalker}\n */\n function createTreeWalker(root) {\n // IE 11 requires the third and fourth arguments be present. If the third\n // arg is null, it applies the default behaviour. However IE also requires\n // the fourth argument be present even though the other browsers ignore it.\n return doc.createTreeWalker(root, NodeFilter.SHOW_ELEMENT, null, false);\n }\n\n /**\n * @param {!Node} node\n * @return {boolean}\n */\n function isElement(node) {\n return node.nodeType === Node.ELEMENT_NODE;\n }\n\n /**\n * @param {!Element} element\n * @return {boolean}\n */\n function isHtmlImport(element) {\n return element.tagName === 'LINK' &&\n element.rel &&\n element.rel.toLowerCase().split(' ').indexOf('import') !== -1;\n }\n\n /**\n * @param {!Element} element\n * @return {boolean}\n */\n function isConnected(element) {\n let n = element;\n do {\n if (n[_attachedProp] || n.nodeType === Node.DOCUMENT_NODE) return true;\n n = n.parentNode || n.nodeType === Node.DOCUMENT_FRAGMENT_NODE && n.host;\n } while(n);\n return false;\n }\n\n /**\n * A registry of custom element definitions.\n *\n * See https://html.spec.whatwg.org/multipage/scripting.html#customelementsregistry\n *\n * @property {boolean} enableFlush Set to true to enable the flush() method\n * to work. This should only be done for tests, as it causes a memory leak.\n */\n class CustomElementRegistry {\n\n constructor() {\n /** @private {!Map} **/\n this._definitions = new Map();\n\n /** @private {!Map} **/\n this._constructors = new Map();\n\n /** @private {!Map} **/\n this._whenDefinedMap = new Map();\n\n /** @private {!Set} **/\n this._observers = new Set();\n\n /** @private {!MutationObserver} **/\n this._attributeObserver = new MutationObserver(\n /** @type {function(Array, MutationObserver)} */\n (this._handleAttributeChange.bind(this)));\n\n /** @private {?HTMLElement} **/\n this._newInstance = null;\n\n /** @private {!Set} **/\n this._pendingHtmlImportUrls = new Set();\n\n /** @type {boolean} **/\n this.enableFlush = true;\n\n /** @private {boolean} **/\n this._upgradeScheduled = false;\n\n /** @type {MutationObserver} **/\n this._mainDocumentObserver = null;\n }\n\n // HTML spec part 4.13.4\n // https://html.spec.whatwg.org/multipage/scripting.html#dom-customelementsregistry-define\n /**\n * @param {string} name\n * @param {function(new:HTMLElement)} constructor\n * @param {{extends: string}} options\n * @return {undefined}\n */\n define(name, constructor, options) {\n // 1:\n if (typeof constructor !== 'function') {\n throw new TypeError('constructor must be a Constructor');\n }\n\n // 2. If constructor is an interface object whose corresponding interface\n // either is HTMLElement or has HTMLElement in its set of inherited\n // interfaces, throw a TypeError and abort these steps.\n //\n // It doesn't appear possible to check this condition from script\n\n // 3:\n const nameError = checkValidCustomElementName(name);\n if (nameError) throw nameError;\n\n // 4, 5:\n // Note: we don't track being-defined names and constructors because\n // define() isn't normally reentrant. The only time user code can run\n // during define() is when getting callbacks off the prototype, which\n // would be highly-unusual. We can make define() reentrant-safe if needed.\n if (this._definitions.has(name)) {\n throw new Error(`An element with name '${name}' is already defined`);\n }\n\n // 6, 7:\n if (this._constructors.has(constructor)) {\n throw new Error(`Definition failed for '${name}': ` +\n `The constructor is already used.`);\n }\n\n // 8:\n /** @type {string} */\n const localName = name;\n\n // 9, 10: We do not support extends currently.\n\n // 11, 12, 13: Our define() isn't rentrant-safe\n\n // 14.1:\n /** @type {Object} */\n const prototype = constructor.prototype;\n\n // 14.2:\n if (typeof prototype !== 'object') {\n throw new TypeError(`Definition failed for '${name}': ` +\n `constructor.prototype must be an object`);\n }\n\n /**\n * @param {string} callbackName\n * @return {Function|undefined}\n */\n function getCallback(callbackName) {\n const callback = prototype[callbackName];\n if (callback !== undefined && typeof callback !== 'function') {\n throw new Error(`${localName} '${callbackName}' is not a Function`);\n }\n return callback;\n }\n\n // 3, 4:\n const connectedCallback = getCallback('connectedCallback');\n\n // 5, 6:\n const disconnectedCallback = getCallback('disconnectedCallback');\n\n // Divergence from spec: we always throw if attributeChangedCallback is\n // not a function.\n\n // 7, 9.1:\n const attributeChangedCallback = getCallback('attributeChangedCallback');\n\n // 8, 9.2, 9.3:\n const observedAttributes =\n (attributeChangedCallback && constructor['observedAttributes']) || [];\n\n // 15:\n /** @type {CustomElementDefinition} */\n const definition = {\n name: name,\n localName: localName,\n constructor: constructor,\n connectedCallback: connectedCallback,\n disconnectedCallback: disconnectedCallback,\n attributeChangedCallback: attributeChangedCallback,\n observedAttributes: observedAttributes,\n };\n\n // 16:\n this._definitions.set(localName, definition);\n this._constructors.set(constructor, localName);\n\n // 17, 18, 19:\n this._upgradeDoc();\n\n // 20:\n /** @type {Deferred} **/\n const deferred = this._whenDefinedMap.get(localName);\n if (deferred) {\n deferred.resolve(undefined);\n this._whenDefinedMap.delete(localName);\n }\n }\n\n /**\n * Returns the constructor defined for `name`, or `null`.\n *\n * @param {string} name\n * @return {Function|undefined}\n */\n get(name) {\n // https://html.spec.whatwg.org/multipage/scripting.html#custom-elements-api\n const def = this._definitions.get(name);\n return def ? def.constructor : undefined;\n }\n\n /**\n * Returns a `Promise` that resolves when a custom element for `name` has\n * been defined.\n *\n * @param {string} name\n * @return {!Promise}\n */\n whenDefined(name) {\n // https://html.spec.whatwg.org/multipage/scripting.html#dom-customelementsregistry-whendefined\n const nameError = checkValidCustomElementName(name);\n if (nameError) return Promise.reject(nameError);\n if (this._definitions.has(name)) return Promise.resolve();\n\n /** @type {Deferred} **/\n let deferred = this._whenDefinedMap.get(name);\n if (deferred) return deferred.promise;\n\n let resolve;\n const promise = new Promise(function(_resolve, _) {\n resolve = _resolve;\n });\n deferred = {promise, resolve};\n this._whenDefinedMap.set(name, deferred);\n return promise;\n }\n\n /**\n * Causes all pending mutation records to be processed, and thus all\n * customization, upgrades and custom element reactions to be called.\n * `enableFlush` must be true for this to work. Only use during tests!\n */\n flush() {\n if (this.enableFlush) {\n // console.warn(\"flush!!!\");\n this._handleMutations(this._mainDocumentObserver.takeRecords());\n this._handleAttributeChange(this._attributeObserver.takeRecords());\n this._observers.forEach(\n /**\n * @param {!MutationObserver} observer\n * @this {CustomElementRegistry}\n */\n function(observer) {\n this._handleMutations(observer.takeRecords());\n }, this);\n }\n }\n\n /**\n * Upgrade all existing in document elements. This process is expensive so\n * is optionally batched based on the state of HTMLImports. (Note,\n * this batching might not be necessary if instead of walking the dom,\n * a map of upgrade candidates was maintained.)\n * @private\n */\n _upgradeDoc() {\n if (!this._upgradeScheduled) {\n this._upgradeScheduled = true;\n const onReady = () => {\n this._upgradeScheduled = false;\n if (!this._mainDocumentObserver) {\n this._mainDocumentObserver = this._observeRoot(doc);\n }\n this._addNodes(doc.childNodes);\n\n };\n if (window['HTMLImports']) {\n window['HTMLImports']['whenReady'](onReady);\n } else {\n onReady();\n }\n }\n }\n\n /**\n * @param {?HTMLElement} instance\n * @private\n */\n _setNewInstance(instance) {\n this._newInstance = instance;\n }\n\n /**\n * Observes a DOM root for mutations that trigger upgrades and reactions.\n * @param {Node} root\n * @private\n */\n _observeRoot(root) {\n //console.log('_observeRoot', root, root.baseURI);\n // console.assert(!root[_observerProp]);\n if (root[_observerProp] != null) {\n //console.warn(`Root ${root} is already observed`);\n return root[_observerProp];\n }\n root[_observerProp] = new MutationObserver(\n /** @type {function(Array, MutationObserver)} */\n (this._handleMutations.bind(this)));\n root[_observerProp].observe(root, {childList: true, subtree: true});\n if (this.enableFlush) {\n // this is memory leak, only use in tests\n this._observers.add(root[_observerProp]);\n }\n return root[_observerProp];\n }\n\n /**\n * @param {Node} root\n * @private\n */\n _unobserveRoot(root) {\n if (root[_observerProp] != null) {\n root[_observerProp].disconnect();\n if (this.enableFlush) {\n this._observers.delete(root[_observerProp]);\n }\n root[_observerProp] = null;\n }\n }\n\n /**\n * @param {!Array} mutations\n * @private\n */\n _handleMutations(mutations) {\n for (let i = 0; i < mutations.length; i++) {\n /** @type {!MutationRecord} */\n const mutation = mutations[i];\n if (mutation.type === 'childList') {\n // Note: we can't get an ordering between additions and removals, and\n // so might diverge from spec reaction ordering\n const addedNodes = /** @type {!NodeList} */(mutation.addedNodes);\n const removedNodes = /** @type {!NodeList} */(mutation.removedNodes);\n this._addNodes(addedNodes);\n this._removeNodes(removedNodes);\n }\n }\n }\n\n /**\n * @param {!(NodeList|Array)} nodeList\n * @param {?Set=} visitedNodes\n * @private\n */\n _addNodes(nodeList, visitedNodes) {\n visitedNodes = visitedNodes || new Set();\n\n for (let i = 0; i < nodeList.length; i++) {\n const root = nodeList[i];\n\n if (!isElement(root)) {\n continue;\n }\n\n // Since we're adding this node to an observed tree, we can unobserve\n this._unobserveRoot(root);\n\n const walker = createTreeWalker(root);\n do {\n const node = /** @type {!HTMLElement} */ (walker.currentNode);\n this._addElement(node, visitedNodes);\n } while (walker.nextNode())\n }\n }\n\n /**\n * @param {!HTMLElement} element\n * @param {!Set=} visitedNodes\n */\n _addElement(element, visitedNodes) {\n if (visitedNodes.has(element)) return;\n visitedNodes.add(element);\n\n /** @type {?CustomElementDefinition} */\n const definition = this._definitions.get(element.localName);\n if (definition) {\n if (!element[_upgradedProp]) {\n this._upgradeElement(element, definition, true);\n }\n if (element[_upgradedProp] && !element[_attachedProp] && isConnected(element)) {\n element[_attachedProp] = true;\n if (definition.connectedCallback) {\n definition.connectedCallback.call(element);\n }\n }\n }\n if (element.shadowRoot) {\n // TODO(justinfagnani): do we need to check that the shadowRoot\n // is observed?\n this._addNodes(element.shadowRoot.childNodes, visitedNodes);\n }\n if (isHtmlImport(element)) {\n this._addImport(/** @type {!HTMLLinkElement} */(element), visitedNodes);\n }\n }\n\n /**\n * @param {!HTMLLinkElement} link\n * @param {!Set=} visitedNodes\n */\n _addImport(link, visitedNodes) {\n // During a tree walk to add or upgrade nodes, we may encounter multiple\n // HTML imports that reference the same document, and may encounter\n // imports in various states of loading.\n\n // First, we only want to process the first import for a document in a\n // walk, so we check visitedNodes for the document, not the link.\n //\n // Second, for documents that haven't loaded yet, we only want to add one\n // listener, regardless of the number of links or walks, so we track\n // pending loads in _pendingHtmlImportUrls.\n\n // Check to see if the import is loaded\n /** @type {?Document} */\n const _import = link.import;\n if (_import) {\n // The import is loaded, but only process the first link element\n if (visitedNodes.has(_import)) return;\n visitedNodes.add(_import);\n\n // The import is loaded observe it\n if (!_import[_observerProp]) this._observeRoot(_import);\n\n // walk the document\n this._addNodes(_import.childNodes, visitedNodes);\n } else {\n // The import is not loaded, so wait for it\n /** @type {string} */\n const importUrl = link.href;\n if (this._pendingHtmlImportUrls.has(importUrl)) return;\n this._pendingHtmlImportUrls.add(importUrl);\n\n /**\n * @const\n * @type {CustomElementRegistry}\n */\n const _this = this;\n const onLoad = function() {\n link.removeEventListener('load', /** @type {function(Event)} */(onLoad));\n if (!link.import[_observerProp]) _this._observeRoot(link.import);\n // We don't pass visitedNodes because this is async and not part of\n // the current tree walk.\n _this._addNodes(link.import.childNodes);\n };\n link.addEventListener('load', onLoad);\n }\n }\n\n /**\n * @param {NodeList} nodeList\n * @private\n */\n _removeNodes(nodeList) {\n for (let i = 0; i < nodeList.length; i++) {\n const root = nodeList[i];\n\n if (!isElement(root)) {\n continue;\n }\n\n // Since we're detatching this element from an observed root, we need to\n // reobserve it.\n // TODO(justinfagnani): can we do this in a microtask so we don't thrash\n // on creating and destroying MutationObservers on batch DOM mutations?\n this._observeRoot(root);\n\n const walker = createTreeWalker(root);\n do {\n const node = walker.currentNode;\n if (node[_upgradedProp] && node[_attachedProp]) {\n node[_attachedProp] = false;\n const definition = this._definitions.get(node.localName);\n if (definition && definition.disconnectedCallback) {\n definition.disconnectedCallback.call(node);\n }\n }\n } while (walker.nextNode())\n }\n }\n\n /**\n * Upgrades or customizes a custom element.\n *\n * @param {HTMLElement} element\n * @param {CustomElementDefinition} definition\n * @param {boolean} callConstructor\n * @private\n */\n _upgradeElement(element, definition, callConstructor) {\n const prototype = definition.constructor.prototype;\n element.__proto__ = prototype;\n if (callConstructor) {\n this._setNewInstance(element);\n new (definition.constructor)();\n element[_upgradedProp] = true;\n console.assert(this._newInstance == null);\n }\n\n const observedAttributes = definition.observedAttributes;\n const attributeChangedCallback = definition.attributeChangedCallback;\n if (attributeChangedCallback && observedAttributes.length > 0) {\n this._attributeObserver.observe(element, {\n attributes: true,\n attributeOldValue: true,\n attributeFilter: observedAttributes,\n });\n\n // Trigger attributeChangedCallback for existing attributes.\n // https://html.spec.whatwg.org/multipage/scripting.html#upgrades\n for (let i = 0; i < observedAttributes.length; i++) {\n const name = observedAttributes[i];\n if (element.hasAttribute(name)) {\n const value = element.getAttribute(name);\n attributeChangedCallback.call(element, name, null, value, null);\n }\n }\n }\n }\n\n /**\n * @param {!Array} mutations\n * @private\n */\n _handleAttributeChange(mutations) {\n for (let i = 0; i < mutations.length; i++) {\n const mutation = mutations[i];\n if (mutation.type === 'attributes') {\n const target = /** @type {HTMLElement} */(mutation.target);\n // We should be gaurenteed to have a definition because this mutation\n // observer is only observing custom elements observedAttributes\n const definition = this._definitions.get(target.localName);\n const name = /** @type {!string} */(mutation.attributeName);\n const oldValue = mutation.oldValue;\n const newValue = target.getAttribute(name);\n // Skip changes that were handled synchronously by setAttribute\n if (newValue !== oldValue) {\n const namespace = mutation.attributeNamespace;\n definition.attributeChangedCallback.call(target, name, oldValue, newValue, namespace);\n }\n }\n }\n }\n }\n\n // Closure Compiler Exports\n window['CustomElementRegistry'] = CustomElementRegistry;\n CustomElementRegistry.prototype['define'] = CustomElementRegistry.prototype.define;\n CustomElementRegistry.prototype['get'] = CustomElementRegistry.prototype.get;\n CustomElementRegistry.prototype['whenDefined'] = CustomElementRegistry.prototype.whenDefined;\n CustomElementRegistry.prototype['flush'] = CustomElementRegistry.prototype.flush;\n CustomElementRegistry.prototype['polyfilled'] = true;\n // TODO(justinfagnani): remove these in production code\n CustomElementRegistry.prototype['_observeRoot'] = CustomElementRegistry.prototype._observeRoot;\n CustomElementRegistry.prototype['_addImport'] = CustomElementRegistry.prototype._addImport;\n\n // patch window.HTMLElement\n\n /** @const */\n const origHTMLElement = win.HTMLElement;\n CustomElementRegistry.prototype['nativeHTMLElement'] = origHTMLElement;\n /**\n * @type {function(new: HTMLElement)}\n */\n const newHTMLElement = function HTMLElement() {\n const customElements = _customElements();\n\n // If there's an being upgraded, return that\n if (customElements._newInstance) {\n const i = customElements._newInstance;\n customElements._newInstance = null;\n return i;\n }\n if (this.constructor) {\n // Find the tagname of the constructor and create a new element with it\n const tagName = customElements._constructors.get(this.constructor);\n return _createElement(doc, tagName, undefined, false);\n }\n throw new Error('Unknown constructor. Did you call customElements.define()?');\n }\n win.HTMLElement = newHTMLElement;\n // By setting the patched HTMLElement's prototype property to the native\n // HTMLElement's prototype we make sure that:\n // document.createElement('a') instanceof HTMLElement\n // works because instanceof uses HTMLElement.prototype, which is on the\n // ptototype chain of built-in elements.\n win.HTMLElement.prototype = origHTMLElement.prototype;\n\n // patch doc.createElement\n // TODO(justinfagnani): why is the cast neccessary?\n // Can we fix the Closure DOM externs?\n const _origCreateElement =\n /** @type {function(this:Document, string, (Object|undefined)=): !HTMLElement}}*/\n (doc.createElement);\n\n /**\n * Creates a new element and upgrades it if it's a custom element.\n * @param {!Document} doc\n * @param {!string} tagName\n * @param {Object|undefined} options\n * @param {boolean} callConstructor whether or not to call the elements\n * constructor after upgrading. If an element is created by calling its\n * constructor, then `callConstructor` should be false to prevent double\n * initialization.\n */\n function _createElement(doc, tagName, options, callConstructor) {\n const customElements = _customElements();\n const element = options ? _origCreateElement.call(doc, tagName, options) :\n _origCreateElement.call(doc, tagName);\n const definition = customElements._definitions.get(tagName.toLowerCase());\n if (definition) {\n customElements._upgradeElement(element, definition, callConstructor);\n }\n customElements._observeRoot(element);\n return element;\n };\n doc.createElement = function(tagName, options) {\n return _createElement(doc, tagName, options, true);\n }\n\n // patch doc.createElementNS\n\n const HTMLNS = 'http://www.w3.org/1999/xhtml';\n\n /** @type {function(this:Document,string,string):Element} */\n const _origCreateElementNS = doc.createElementNS;\n doc.createElementNS =\n /** @type {function(this:Document,(string|null),string):!Element} */\n (function(namespaceURI, qualifiedName) {\n if (namespaceURI === 'http://www.w3.org/1999/xhtml') {\n return doc.createElement(qualifiedName);\n } else {\n return _origCreateElementNS.call(doc, namespaceURI, qualifiedName);\n }\n });\n\n // patch Element.attachShadow\n\n /** @type {function({closed: boolean})} */\n const _origAttachShadow = Element.prototype['attachShadow'];\n if (_origAttachShadow) {\n Object.defineProperty(Element.prototype, 'attachShadow', {\n value: function(options) {\n /** @type {!Node} */\n const root = _origAttachShadow.call(this, options);\n /** @type {CustomElementRegistry} */\n const customElements = _customElements();\n customElements._observeRoot(root);\n return root;\n },\n });\n }\n\n // patch doc.importNode\n\n const rawImportNode = doc.importNode;\n doc.importNode = function(node, deep) {\n const clone = /** @type{!Node} */(rawImportNode.call(doc, node, deep));\n const customElements = _customElements();\n const nodes = isElement(clone) ? [clone] : clone.childNodes;\n /** @type {CustomElementRegistry} */(_customElements())._addNodes(nodes);\n return clone;\n };\n\n // patch Element.setAttribute & removeAttribute\n\n const _origSetAttribute = Element.prototype.setAttribute;\n Element.prototype['setAttribute'] = function(name, value) {\n changeAttribute(this, name, value, _origSetAttribute);\n };\n const _origRemoveAttribute = Element.prototype.removeAttribute;\n Element.prototype['removeAttribute'] = function(name) {\n changeAttribute(this, name, null, _origRemoveAttribute);\n };\n\n function changeAttribute(element, name, value, operation) {\n name = name.toLowerCase();\n const oldValue = element.getAttribute(name);\n operation.call(element, name, value);\n\n // Bail if this wasn't a fully upgraded custom element\n if (element[_upgradedProp] == true) {\n const definition = _customElements()._definitions.get(element.localName);\n const observedAttributes = definition.observedAttributes;\n const attributeChangedCallback = definition.attributeChangedCallback;\n if (attributeChangedCallback && observedAttributes.indexOf(name) >= 0) {\n const newValue = element.getAttribute(name);\n if (newValue !== oldValue) {\n attributeChangedCallback.call(element, name, oldValue, newValue, null);\n }\n }\n }\n }\n\n Object.defineProperty(window, 'customElements', {\n value: new CustomElementRegistry(),\n configurable: true,\n enumerable: true,\n });\n\n // TODO(justinfagnani): Remove. Temporary for backward-compatibility\n window['CustomElements'] = {\n takeRecords() {\n if (_customElements().flush) _customElements().flush();\n }\n }\n})();\n"],"sourceRoot":"/source/"} \ No newline at end of file +{"version":3,"sources":["src/custom-elements.js"],"names":["constructor","CustomElementRegistry","_definitions","Map","_constructors","_whenDefinedMap","_observers","Set","_attributeObserver","MutationObserver","_handleAttributeChange","bind","_newInstance","_pendingHtmlImportUrls","enableFlush","_upgradeScheduled","_mainDocumentObserver","_customElements","window","checkValidCustomElementName","name","test","reservedTagList","indexOf","Error","getDefinition","definitions","node","definition","get","is","tagName","toLowerCase","localName","patchElement","varName","origHTMLElement","prototype","newHTMLElement","customElements","i","constructorInfo","_createElement","options","undefined","callConstructor","document","isAttr","element","_nativeCreateElement","call","doc","setAttribute","_upgradeElement","_observeRoot","changeAttribute","value","operation","oldValue","getAttribute","observedAttributes","attributeChangedCallback","newValue","flush","forcePolyfill","define","getCallback","callbackName","callback","TypeError","nameError","has","extends","extendsNameError","createElement","el","HTMLUnknownElement","connectedCallback","disconnectedCallback","set","_upgradeDoc","deferred","resolve","delete","def","whenDefined","Promise","reject","promise","_resolve","_handleMutations","takeRecords","forEach","observer","onReady","_addNodes","childNodes","_setNewInstance","instance","root","observe","childList","subtree","add","_unobserveRoot","disconnect","mutations","length","mutation","type","removedNodes","addedNodes","_removeNodes","nodeList","visitedNodes","nodeType","Node","ELEMENT_NODE","walker","createTreeWalker","NodeFilter","SHOW_ELEMENT","currentNode","_addElement","nextNode","n","DOCUMENT_NODE","parentNode","DOCUMENT_FRAGMENT_NODE","host","shadowRoot","rel","split","_addImport","link","_import","import","importUrl","href","_this","onLoad","removeEventListener","addEventListener","__proto__","console","assert","attributes","attributeOldValue","attributeFilter","hasAttribute","target","attributeName","attributeNamespace","htmlElementSubclasses","index","document.createElement","_nativeCreateElementNS","createElementNS","namespaceURI","qualifiedName","HTMLNS","_nativeAttachShadow","Element","Object","defineProperty","_nativeImportNode","importNode","document.importNode","deep","clone","nodes","_nativeSetAttribute","_nativeRemoveAttribute","removeAttribute","configurable","enumerable"],"mappings":"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","file":"custom-elements.min.js","sourcesContent":["/**\n * @license\n * Copyright (c) 2016 The Polymer Project Authors. All rights reserved.\n * This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt\n * The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt\n * The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt\n * Code distributed by Google as part of the polymer project is also\n * subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt\n */\n\n/**\n * 2.3\n * http://w3c.github.io/webcomponents/spec/custom/#dfn-element-definition\n * @typedef {{\n * name: string,\n * localName: string,\n * constructor: function(new:HTMLElement),\n * connectedCallback: (Function|undefined),\n * disconnectedCallback: (Function|undefined),\n * attributeChangedCallback: (Function|undefined),\n * observedAttributes: Array,\n * }}\n */\nlet CustomElementDefinition;\n\n/**\n * @typedef {{\n * resolve: !function(undefined),\n * promise: !Promise,\n * }}\n */\nlet Deferred;\n\n(function() {\n 'use strict';\n\n /**\n * Gets 'customElement' from window so that it could be modified after\n * the polyfill loads.\n * @function\n * @return {CustomElementRegistry}\n */\n const _customElements = () => window['customElements'];\n\n const _observerProp = '__$CE_observer';\n const _attachedProp = '__$CE_attached';\n const _upgradedProp = '__$CE_upgraded';\n\n if (_customElements()) {\n _customElements().flush = function() {};\n if (!_customElements().forcePolyfill) {\n return;\n }\n }\n\n // name validation\n // https://html.spec.whatwg.org/multipage/scripting.html#valid-custom-element-name\n\n /**\n * @const\n * @type {Array}\n */\n const reservedTagList = [\n 'annotation-xml',\n 'color-profile',\n 'font-face',\n 'font-face-src',\n 'font-face-uri',\n 'font-face-format',\n 'font-face-name',\n 'missing-glyph',\n ];\n\n /**\n * @param {!string} name\n * @return {!Error|undefined}\n */\n function checkValidCustomElementName(name) {\n if (!(/^[a-z][.0-9_a-z]*-[\\-.0-9_a-z]*$/.test(name) &&\n reservedTagList.indexOf(name) === -1)) {\n return new Error(`The element name '${name}' is not valid.`)\n }\n }\n\n /**\n * @param {!Node} root\n * @return {TreeWalker}\n */\n function createTreeWalker(root) {\n // IE 11 requires the third and fourth arguments be present. If the third\n // arg is null, it applies the default behaviour. However IE also requires\n // the fourth argument be present even though the other browsers ignore it.\n return document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT, null, false);\n }\n\n /**\n * @param {!Node} node\n * @return {boolean}\n */\n function isElement(node) {\n return node.nodeType === Node.ELEMENT_NODE;\n }\n\n /**\n * @param {!Element} element\n * @return {boolean}\n */\n function isHtmlImport(element) {\n return element.tagName === 'LINK' &&\n element.rel &&\n element.rel.toLowerCase().split(' ').indexOf('import') !== -1;\n }\n\n /**\n * @param {!Element} element\n * @return {boolean}\n */\n function isConnected(element) {\n let n = element;\n do {\n if (n[_attachedProp] || n.nodeType === Node.DOCUMENT_NODE) return true;\n n = n.parentNode || n.nodeType === Node.DOCUMENT_FRAGMENT_NODE && n.host;\n } while(n);\n return false;\n }\n\n /**\n * A registry of custom element definitions.\n *\n * See https://html.spec.whatwg.org/multipage/scripting.html#customelementsregistry\n *\n * @property {boolean} enableFlush Set to true to enable the flush() method\n * to work. This should only be done for tests, as it causes a memory leak.\n */\n class CustomElementRegistry {\n\n constructor() {\n /** @private {!Map} **/\n this._definitions = new Map();\n\n /** @private {!Map} **/\n this._constructors = new Map();\n\n /** @private {!Map} **/\n this._whenDefinedMap = new Map();\n\n /** @private {!Set} **/\n this._observers = new Set();\n\n /** @private {!MutationObserver} **/\n this._attributeObserver = new MutationObserver(\n /** @type {function(Array, MutationObserver)} */\n (this._handleAttributeChange.bind(this)));\n\n /** @private {?HTMLElement} **/\n this._newInstance = null;\n\n /** @private {!Set} **/\n this._pendingHtmlImportUrls = new Set();\n\n /** @type {boolean} **/\n this.enableFlush = true;\n\n /** @private {boolean} **/\n this._upgradeScheduled = false;\n\n /** @type {MutationObserver} **/\n this._mainDocumentObserver = null;\n }\n\n // HTML spec part 4.13.4\n // https://html.spec.whatwg.org/multipage/scripting.html#dom-customelementsregistry-define\n /**\n * @param {string} name\n * @param {function(new:HTMLElement)} constructor\n * @param {{extends: string}} options\n * @return {undefined}\n */\n define(name, constructor, options) {\n // 1:\n if (typeof constructor !== 'function') {\n throw new TypeError('constructor must be a Constructor');\n }\n\n // 2. If constructor is an interface object whose corresponding interface\n // either is HTMLElement or has HTMLElement in its set of inherited\n // interfaces, throw a TypeError and abort these steps.\n //\n // It doesn't appear possible to check this condition from script\n\n // 3:\n const nameError = checkValidCustomElementName(name);\n if (nameError) throw nameError;\n\n // 4, 5:\n // Note: we don't track being-defined names and constructors because\n // define() isn't normally reentrant. The only time user code can run\n // during define() is when getting callbacks off the prototype, which\n // would be highly-unusual. We can make define() reentrant-safe if needed.\n if (this._definitions.has(name)) {\n throw new Error(`An element with name '${name}' is already defined`);\n }\n\n // 6, 7:\n if (this._constructors.has(constructor)) {\n throw new Error(`Definition failed for '${name}': ` +\n `The constructor is already used.`);\n }\n\n // 8:\n /** @type {string} */\n let localName = name;\n\n // 9, 10\n if (options && options.extends) {\n // 7.1\n const extendsNameError = checkValidCustomElementName(options.extends);\n if (!extendsNameError) {\n throw new Error(`Cannot extend '${options.extends}': A custom element cannot extend a custom element.`);\n }\n\n // 7.2\n const el = document.createElement(options.extends);\n if (el instanceof window.HTMLUnknownElement) {\n throw new Error(`Cannot extend '${options.extends}': is not a real HTMLElement`);\n }\n\n // 7.3\n localName = options.extends;\n }\n\n // 11, 12, 13: Our define() isn't rentrant-safe\n\n // 14.1:\n /** @type {Object} */\n const prototype = constructor.prototype;\n\n // 14.2:\n if (typeof prototype !== 'object') {\n throw new TypeError(`Definition failed for '${name}': ` +\n `constructor.prototype must be an object`);\n }\n\n /**\n * @param {string} callbackName\n * @return {Function|undefined}\n */\n function getCallback(callbackName) {\n const callback = prototype[callbackName];\n if (callback !== undefined && typeof callback !== 'function') {\n throw new Error(`${localName} '${callbackName}' is not a Function`);\n }\n return callback;\n }\n\n // 3, 4:\n const connectedCallback = getCallback('connectedCallback');\n\n // 5, 6:\n const disconnectedCallback = getCallback('disconnectedCallback');\n\n // Divergence from spec: we always throw if attributeChangedCallback is\n // not a function.\n\n // 7, 9.1:\n const attributeChangedCallback = getCallback('attributeChangedCallback');\n\n // 8, 9.2, 9.3:\n const observedAttributes =\n (attributeChangedCallback && constructor['observedAttributes']) || [];\n\n // 15:\n /** @type {CustomElementDefinition} */\n const definition = {\n name: name,\n localName: localName,\n constructor: constructor,\n connectedCallback: connectedCallback,\n disconnectedCallback: disconnectedCallback,\n attributeChangedCallback: attributeChangedCallback,\n observedAttributes: observedAttributes,\n };\n\n // 16:\n this._definitions.set(name, definition);\n this._constructors.set(constructor, {localName, name});\n\n // 17, 18, 19:\n this._upgradeDoc();\n\n // 20:\n /** @type {Deferred} **/\n const deferred = this._whenDefinedMap.get(localName);\n if (deferred) {\n deferred.resolve(undefined);\n this._whenDefinedMap.delete(localName);\n }\n }\n\n /**\n * Returns the constructor defined for `name`, or `null`.\n *\n * @param {string} name\n * @return {Function|undefined}\n */\n get(name) {\n // https://html.spec.whatwg.org/multipage/scripting.html#custom-elements-api\n const def = this._definitions.get(name);\n return def ? def.constructor : undefined;\n }\n\n /**\n * Returns a `Promise` that resolves when a custom element for `name` has\n * been defined.\n *\n * @param {string} name\n * @return {!Promise}\n */\n whenDefined(name) {\n // https://html.spec.whatwg.org/multipage/scripting.html#dom-customelementsregistry-whendefined\n const nameError = checkValidCustomElementName(name);\n if (nameError) return Promise.reject(nameError);\n if (this._definitions.has(name)) return Promise.resolve();\n\n /** @type {Deferred} **/\n let deferred = this._whenDefinedMap.get(name);\n if (deferred) return deferred.promise;\n\n let resolve;\n const promise = new Promise(function(_resolve, _) {\n resolve = _resolve;\n });\n deferred = {promise, resolve};\n this._whenDefinedMap.set(name, deferred);\n return promise;\n }\n\n /**\n * Causes all pending mutation records to be processed, and thus all\n * customization, upgrades and custom element reactions to be called.\n * `enableFlush` must be true for this to work. Only use during tests!\n */\n flush() {\n if (this.enableFlush) {\n // console.warn(\"flush!!!\");\n this._handleMutations(this._mainDocumentObserver.takeRecords());\n this._handleAttributeChange(this._attributeObserver.takeRecords());\n this._observers.forEach(\n /**\n * @param {!MutationObserver} observer\n * @this {CustomElementRegistry}\n */\n function(observer) {\n this._handleMutations(observer.takeRecords());\n }, this);\n }\n }\n\n /**\n * Upgrade all existing in document elements. This process is expensive so\n * is optionally batched based on the state of HTMLImports. (Note,\n * this batching might not be necessary if instead of walking the dom,\n * a map of upgrade candidates was maintained.)\n * @private\n */\n _upgradeDoc() {\n if (!this._upgradeScheduled) {\n this._upgradeScheduled = true;\n const onReady = () => {\n this._upgradeScheduled = false;\n if (!this._mainDocumentObserver) {\n this._mainDocumentObserver = this._observeRoot(document);\n }\n this._addNodes(document.childNodes);\n\n };\n if (window['HTMLImports']) {\n window['HTMLImports']['whenReady'](onReady);\n } else {\n onReady();\n }\n }\n }\n\n /**\n * @param {?HTMLElement} instance\n * @private\n */\n _setNewInstance(instance) {\n this._newInstance = instance;\n }\n\n /**\n * Observes a DOM root for mutations that trigger upgrades and reactions.\n * @param {Node} root\n * @private\n */\n _observeRoot(root) {\n //console.log('_observeRoot', root, root.baseURI);\n // console.assert(!root[_observerProp]);\n if (root[_observerProp] != null) {\n //console.warn(`Root ${root} is already observed`);\n return root[_observerProp];\n }\n root[_observerProp] = new MutationObserver(\n /** @type {function(Array, MutationObserver)} */\n (this._handleMutations.bind(this)));\n root[_observerProp].observe(root, {childList: true, subtree: true});\n if (this.enableFlush) {\n // this is memory leak, only use in tests\n this._observers.add(root[_observerProp]);\n }\n return root[_observerProp];\n }\n\n /**\n * @param {Node} root\n * @private\n */\n _unobserveRoot(root) {\n if (root[_observerProp] != null) {\n root[_observerProp].disconnect();\n if (this.enableFlush) {\n this._observers.delete(root[_observerProp]);\n }\n root[_observerProp] = null;\n }\n }\n\n /**\n * @param {!Array} mutations\n * @private\n */\n _handleMutations(mutations) {\n for (let i = 0; i < mutations.length; i++) {\n /** @type {!MutationRecord} */\n const mutation = mutations[i];\n if (mutation.type === 'childList') {\n // Note: we can't get an ordering between additions and removals, and\n // so might diverge from spec reaction ordering\n const addedNodes = /** @type {!NodeList} */(mutation.addedNodes);\n const removedNodes = /** @type {!NodeList} */(mutation.removedNodes);\n this._addNodes(addedNodes);\n this._removeNodes(removedNodes);\n }\n }\n }\n\n /**\n * @param {!(NodeList|Array)} nodeList\n * @param {?Set=} visitedNodes\n * @private\n */\n _addNodes(nodeList, visitedNodes) {\n visitedNodes = visitedNodes || new Set();\n\n for (let i = 0; i < nodeList.length; i++) {\n const root = nodeList[i];\n\n if (!isElement(root)) {\n continue;\n }\n\n // Since we're adding this node to an observed tree, we can unobserve\n this._unobserveRoot(root);\n\n const walker = createTreeWalker(root);\n do {\n const node = /** @type {!HTMLElement} */ (walker.currentNode);\n if (node.getAttribute('is')) {\n node.is = node.getAttribute('is');\n }\n this._addElement(node, visitedNodes);\n } while (walker.nextNode())\n }\n }\n\n /**\n * @param {!HTMLElement} element\n * @param {!Set=} visitedNodes\n */\n _addElement(element, visitedNodes) {\n if (visitedNodes.has(element)) return;\n visitedNodes.add(element);\n\n /** @type {?CustomElementDefinition} */\n const definition = getDefinition(this._definitions, element);\n if (definition) {\n if (!element[_upgradedProp]) {\n this._upgradeElement(element, definition, true);\n }\n if (element[_upgradedProp] && !element[_attachedProp] && isConnected(element)) {\n element[_attachedProp] = true;\n if (definition.connectedCallback) {\n definition.connectedCallback.call(element);\n }\n }\n }\n if (element.shadowRoot) {\n // TODO(justinfagnani): do we need to check that the shadowRoot\n // is observed?\n this._addNodes(element.shadowRoot.childNodes, visitedNodes);\n }\n if (isHtmlImport(element)) {\n this._addImport(/** @type {!HTMLLinkElement} */(element), visitedNodes);\n }\n }\n\n /**\n * @param {!HTMLLinkElement} link\n * @param {!Set=} visitedNodes\n */\n _addImport(link, visitedNodes) {\n // During a tree walk to add or upgrade nodes, we may encounter multiple\n // HTML imports that reference the same document, and may encounter\n // imports in various states of loading.\n\n // First, we only want to process the first import for a document in a\n // walk, so we check visitedNodes for the document, not the link.\n //\n // Second, for documents that haven't loaded yet, we only want to add one\n // listener, regardless of the number of links or walks, so we track\n // pending loads in _pendingHtmlImportUrls.\n\n // Check to see if the import is loaded\n /** @type {?Document} */\n const _import = link.import;\n if (_import) {\n // The import is loaded, but only process the first link element\n if (visitedNodes.has(_import)) return;\n visitedNodes.add(_import);\n\n // The import is loaded observe it\n if (!_import[_observerProp]) this._observeRoot(_import);\n\n // walk the document\n this._addNodes(_import.childNodes, visitedNodes);\n } else {\n // The import is not loaded, so wait for it\n /** @type {string} */\n const importUrl = link.href;\n if (this._pendingHtmlImportUrls.has(importUrl)) return;\n this._pendingHtmlImportUrls.add(importUrl);\n\n /**\n * @const\n * @type {CustomElementRegistry}\n */\n const _this = this;\n const onLoad = function() {\n link.removeEventListener('load', /** @type {function(Event)} */(onLoad));\n if (!link.import[_observerProp]) _this._observeRoot(link.import);\n // We don't pass visitedNodes because this is async and not part of\n // the current tree walk.\n _this._addNodes(link.import.childNodes);\n };\n link.addEventListener('load', onLoad);\n }\n }\n\n /**\n * @param {NodeList} nodeList\n * @private\n */\n _removeNodes(nodeList) {\n for (let i = 0; i < nodeList.length; i++) {\n const root = nodeList[i];\n\n if (!isElement(root)) {\n continue;\n }\n\n // Since we're detatching this element from an observed root, we need to\n // reobserve it.\n // TODO(justinfagnani): can we do this in a microtask so we don't thrash\n // on creating and destroying MutationObservers on batch DOM mutations?\n this._observeRoot(root);\n\n const walker = createTreeWalker(root);\n do {\n const node = walker.currentNode;\n if (node[_upgradedProp] && node[_attachedProp]) {\n node[_attachedProp] = false;\n const definition = getDefinition(this._definitions, node);\n if (definition && definition.disconnectedCallback) {\n definition.disconnectedCallback.call(node);\n }\n }\n } while (walker.nextNode())\n }\n }\n\n /**\n * Upgrades or customizes a custom element.\n *\n * @param {HTMLElement} element\n * @param {CustomElementDefinition} definition\n * @param {boolean} callConstructor\n * @private\n */\n _upgradeElement(element, definition, callConstructor) {\n const prototype = definition.constructor.prototype;\n element.__proto__ = prototype;\n if (callConstructor) {\n this._setNewInstance(element);\n new (definition.constructor)();\n element[_upgradedProp] = true;\n console.assert(this._newInstance == null);\n }\n\n const observedAttributes = definition.observedAttributes;\n const attributeChangedCallback = definition.attributeChangedCallback;\n if (attributeChangedCallback && observedAttributes.length > 0) {\n this._attributeObserver.observe(element, {\n attributes: true,\n attributeOldValue: true,\n attributeFilter: observedAttributes,\n });\n\n // Trigger attributeChangedCallback for existing attributes.\n // https://html.spec.whatwg.org/multipage/scripting.html#upgrades\n for (let i = 0; i < observedAttributes.length; i++) {\n const name = observedAttributes[i];\n if (element.hasAttribute(name)) {\n const value = element.getAttribute(name);\n attributeChangedCallback.call(element, name, null, value, null);\n }\n }\n }\n }\n\n /**\n * @param {!Array} mutations\n * @private\n */\n _handleAttributeChange(mutations) {\n for (let i = 0; i < mutations.length; i++) {\n const mutation = mutations[i];\n if (mutation.type === 'attributes') {\n const target = /** @type {HTMLElement} */(mutation.target);\n // We should be gaurenteed to have a definition because this mutation\n // observer is only observing custom elements observedAttributes\n const definition = getDefinition(this._definitions, target);\n const name = /** @type {!string} */(mutation.attributeName);\n const oldValue = mutation.oldValue;\n const newValue = target.getAttribute(name);\n // Skip changes that were handled synchronously by setAttribute\n if (newValue !== oldValue) {\n const namespace = mutation.attributeNamespace;\n definition.attributeChangedCallback.call(target, name, oldValue, newValue, namespace);\n }\n }\n }\n }\n }\n\n function getDefinition(definitions, node) {\n const name = typeof node.is === 'string' ? node.is : node.tagName.toLowerCase();\n const definition = definitions.get(name);\n if (definition) {\n // Make sure local name matches the actual node's tagName\n return definition.localName === node.tagName.toLowerCase() || definition.localName === node.is ? definition : null;\n } else {\n return null;\n }\n }\n\n // Closure Compiler Exports\n window['CustomElementRegistry'] = CustomElementRegistry;\n CustomElementRegistry.prototype['define'] = CustomElementRegistry.prototype.define;\n CustomElementRegistry.prototype['get'] = CustomElementRegistry.prototype.get;\n CustomElementRegistry.prototype['whenDefined'] = CustomElementRegistry.prototype.whenDefined;\n CustomElementRegistry.prototype['flush'] = CustomElementRegistry.prototype.flush;\n CustomElementRegistry.prototype['polyfilled'] = true;\n // TODO(justinfagnani): remove these in production code\n CustomElementRegistry.prototype['_observeRoot'] = CustomElementRegistry.prototype._observeRoot;\n CustomElementRegistry.prototype['_addImport'] = CustomElementRegistry.prototype._addImport;\n\n patchElement('HTMLElement')\n var htmlElementSubclasses = [ 'Button', 'Canvas', 'Data', 'Head', 'Mod', 'TableCell', 'TableCol', 'Anchor', 'Area', 'Base', 'Body', 'BR', 'DataList', 'Details', 'Dialog', 'Div', 'DList', 'Embed', 'FieldSet', 'Form', 'Heading', 'HR', 'Html', 'IFrame', 'Image', 'Input', 'Keygen', 'Label', 'Legend', 'LI', 'Link', 'Map', 'Media', 'Menu', 'MenuItem', 'Meta', 'Meter', 'Object', 'OList', 'OptGroup', 'Option', 'Output', 'Paragraph', 'Param', 'Picture', 'Pre', 'Progress', 'Quote', 'Script', 'Select', 'Slot', 'Source', 'Span', 'Style', 'TableCaption', 'Table', 'TableRow', 'TableSection', 'Template', 'TextArea', 'Time', 'Title', 'Track', 'UList', 'Unknown'];\n for (let index in htmlElementSubclasses) {\n patchElement(`HTML${htmlElementSubclasses[index]}Element`);\n }\n\n function patchElement(varName) {\n /** @const */\n const origHTMLElement = window[varName];\n if (!origHTMLElement) {\n return;\n }\n CustomElementRegistry.prototype[`native${varName}`] = origHTMLElement;\n /**\n * @type {function(new: HTMLElement)}\n */\n const newHTMLElement = function() {\n const customElements = _customElements();\n\n // If there's an being upgraded, return that\n if (customElements._newInstance) {\n const i = customElements._newInstance;\n customElements._newInstance = null;\n return i;\n }\n if (this.constructor) {\n // Find the tagname of the constructor and create a new element with it\n const constructorInfo = customElements._constructors.get(this.constructor);\n const options = constructorInfo.name ? {is: constructorInfo.name} : undefined;\n return _createElement(document, constructorInfo.localName, options, false);\n }\n throw new Error('Unknown constructor. Did you call customElements.define()?');\n }\n window[varName] = newHTMLElement;\n window[varName].prototype = origHTMLElement.prototype;\n }\n\n // patch doc.createElement\n // TODO(justinfagnani): why is the cast neccessary?\n // Can we fix the Closure DOM externs?\n const _nativeCreateElement =\n /** @type {function(this:Document, string, (Object|undefined)=): !HTMLElement}}*/\n (document.createElement);\n\n /**\n * Creates a new element and upgrades it if it's a custom element.\n * @param {!Document} doc\n * @param {!string} tagName\n * @param {Object|undefined} options\n * @param {boolean} callConstructor whether or not to call the elements\n * constructor after upgrading. If an element is created by calling its\n * constructor, then `callConstructor` should be false to prevent double\n * initialization.\n */\n function _createElement(doc, tagName, options, callConstructor) {\n const customElements = _customElements();\n let isAttr;\n if (options && options.is) {\n // We're going to take care of setting the is attribute ourselves\n isAttr = options.is;\n delete options.is;\n }\n const element = options ? _nativeCreateElement.call(doc, tagName, options) :\n _nativeCreateElement.call(doc, tagName);\n if (isAttr) {\n element.setAttribute('is', isAttr);\n element.is = isAttr;\n }\n const definition = getDefinition(customElements._definitions, element);\n if (definition) {\n customElements._upgradeElement(element, definition, callConstructor);\n }\n customElements._observeRoot(element);\n return element;\n };\n document.createElement = function(tagName, options) {\n return _createElement(document, tagName, options, true);\n }\n\n // patch document.createElementNS\n\n const HTMLNS = 'http://www.w3.org/1999/xhtml';\n\n /** @type {function(this:Document,string,string):Element} */\n const _nativeCreateElementNS = document.createElementNS;\n document.createElementNS =\n /** @type {function(this:Document,(string|null),string):!Element} */\n (function(namespaceURI, qualifiedName) {\n if (namespaceURI === HTMLNS) {\n return document.createElement(qualifiedName);\n } else {\n return _nativeCreateElementNS.call(document, namespaceURI, qualifiedName);\n }\n });\n\n // patch Element.attachShadow\n\n /** @type {function({closed: boolean})} */\n const _nativeAttachShadow = Element.prototype['attachShadow'];\n if (_nativeAttachShadow) {\n Object.defineProperty(Element.prototype, 'attachShadow', {\n value: function(options) {\n /** @type {!Node} */\n const root = _nativeAttachShadow.call(this, options);\n /** @type {CustomElementRegistry} */\n const customElements = _customElements();\n customElements._observeRoot(root);\n return root;\n },\n });\n }\n\n // patch document.importNode\n\n const _nativeImportNode = document.importNode;\n document.importNode = function(node, deep) {\n const clone = /** @type{!Node} */(_nativeImportNode.call(document, node, deep));\n const customElements = _customElements();\n const nodes = isElement(clone) ? [clone] : clone.childNodes;\n /** @type {CustomElementRegistry} */(_customElements())._addNodes(nodes);\n return clone;\n };\n\n // patch Element.setAttribute & removeAttribute\n\n const _nativeSetAttribute = Element.prototype.setAttribute;\n Element.prototype['setAttribute'] = function(name, value) {\n changeAttribute(this, name, value, _nativeSetAttribute);\n };\n const _nativeRemoveAttribute = Element.prototype.removeAttribute;\n Element.prototype['removeAttribute'] = function(name) {\n changeAttribute(this, name, null, _nativeRemoveAttribute);\n };\n\n function changeAttribute(element, name, value, operation) {\n name = name.toLowerCase();\n const oldValue = element.getAttribute(name);\n operation.call(element, name, value);\n\n // Bail if this wasn't a fully upgraded custom element\n if (element[_upgradedProp] == true) {\n const definition = getDefinition(_customElements()._definitions, element);\n const observedAttributes = definition.observedAttributes;\n const attributeChangedCallback = definition.attributeChangedCallback;\n if (attributeChangedCallback && observedAttributes.indexOf(name) >= 0) {\n const newValue = element.getAttribute(name);\n if (newValue !== oldValue) {\n attributeChangedCallback.call(element, name, oldValue, newValue, null);\n }\n }\n }\n }\n\n Object.defineProperty(window, 'customElements', {\n value: new CustomElementRegistry(),\n configurable: true,\n enumerable: true,\n });\n\n // TODO(justinfagnani): Remove. Temporary for backward-compatibility\n window['CustomElements'] = {\n takeRecords() {\n if (_customElements().flush) _customElements().flush();\n }\n }\n})();\n"]} \ No newline at end of file diff --git a/src/README.md b/src/README.md index 9b5f4f8..f5da492 100644 --- a/src/README.md +++ b/src/README.md @@ -11,10 +11,8 @@ reactions. The source references old versions of the spec. ### To do 1. Implement Node#isConnected - 2. Implement built-in element extension (is=) - 3. Add reaction callback ordering tests - 4. Reorganize tests to be closer to spec structure - 5. Performance tests + 2. Reorganize tests to be closer to spec structure + 3. Performance tests ## Building & Running Tests diff --git a/src/custom-elements.js b/src/custom-elements.js index 7d03308..de9238e 100644 --- a/src/custom-elements.js +++ b/src/custom-elements.js @@ -221,7 +221,7 @@ let Deferred; // 7.2 const el = document.createElement(options.extends); - if (el.constructor === window.HTMLUnknownElement) { + if (el instanceof window.HTMLUnknownElement) { throw new Error(`Cannot extend '${options.extends}': is not a real HTMLElement`); } @@ -283,7 +283,7 @@ let Deferred; // 16: this._definitions.set(name, definition); - this._constructors.set(constructor, localName); + this._constructors.set(constructor, {localName, name}); // 17, 18, 19: this._upgradeDoc(); @@ -467,6 +467,9 @@ let Deferred; const walker = createTreeWalker(root); do { const node = /** @type {!HTMLElement} */ (walker.currentNode); + if (node.getAttribute('is')) { + node.is = node.getAttribute('is'); + } this._addElement(node, visitedNodes); } while (walker.nextNode()) } @@ -481,11 +484,7 @@ let Deferred; visitedNodes.add(element); /** @type {?CustomElementDefinition} */ - const isAttr = element.getAttribute('is'); - if (typeof isAttr === 'string') { - element.is = isAttr; - } - const definition = this._definitions.get(getCustomElementName(element)); + const definition = getDefinition(this._definitions, element); if (definition) { if (!element[_upgradedProp]) { this._upgradeElement(element, definition, true); @@ -582,7 +581,7 @@ let Deferred; const node = walker.currentNode; if (node[_upgradedProp] && node[_attachedProp]) { node[_attachedProp] = false; - const definition = this._definitions.get(getCustomElementName(node)); + const definition = getDefinition(this._definitions, node); if (definition && definition.disconnectedCallback) { definition.disconnectedCallback.call(node); } @@ -641,7 +640,7 @@ let Deferred; const target = /** @type {HTMLElement} */(mutation.target); // We should be gaurenteed to have a definition because this mutation // observer is only observing custom elements observedAttributes - const definition = this._definitions.get(getCustomElementName(target)); + const definition = getDefinition(this._definitions, target); const name = /** @type {!string} */(mutation.attributeName); const oldValue = mutation.oldValue; const newValue = target.getAttribute(name); @@ -655,8 +654,15 @@ let Deferred; } } - function getCustomElementName(element) { - return typeof element.is === 'string' ? element.is : element.tagName; + function getDefinition(definitions, node) { + const name = typeof node.is === 'string' ? node.is : node.tagName.toLowerCase(); + const definition = definitions.get(name); + if (definition) { + // Make sure local name matches the actual node's tagName + return definition.localName === node.tagName.toLowerCase() || definition.localName === node.is ? definition : null; + } else { + return null; + } } // Closure Compiler Exports @@ -678,7 +684,7 @@ let Deferred; function patchElement(varName) { /** @const */ - const origHTMLElement = win[varName]; + const origHTMLElement = window[varName]; if (!origHTMLElement) { return; } @@ -697,15 +703,14 @@ let Deferred; } if (this.constructor) { // Find the tagname of the constructor and create a new element with it - const tagName = customElements._constructors.get(this.constructor); - return _createElement(doc, tagName, undefined, false); + const constructorInfo = customElements._constructors.get(this.constructor); + const options = constructorInfo.name ? {is: constructorInfo.name} : undefined; + return _createElement(document, constructorInfo.localName, options, false); } throw new Error('Unknown constructor. Did you call customElements.define()?'); } - win[varName] = newHTMLElement; - win[varName].prototype = Object.create(origHTMLElement.prototype, { - constructor: {value: newHTMLElement, configurable: true, writable: true}, - }); + window[varName] = newHTMLElement; + window[varName].prototype = origHTMLElement.prototype; } // patch doc.createElement @@ -733,12 +738,13 @@ let Deferred; isAttr = options.is; delete options.is; } - const element = options ? _origCreateElement.call(doc, tagName, options) : - _origCreateElement.call(doc, tagName); + const element = options ? _nativeCreateElement.call(doc, tagName, options) : + _nativeCreateElement.call(doc, tagName); if (isAttr) { element.setAttribute('is', isAttr); + element.is = isAttr; } - const definition = customElements._definitions.get(tagName.toLowerCase()); + const definition = getDefinition(customElements._definitions, element); if (definition) { customElements._upgradeElement(element, definition, callConstructor); } @@ -811,7 +817,7 @@ let Deferred; // Bail if this wasn't a fully upgraded custom element if (element[_upgradedProp] == true) { - const definition = _customElements()._definitions.get(getCustomElementName(element)); + const definition = getDefinition(_customElements()._definitions, element); const observedAttributes = definition.observedAttributes; const attributeChangedCallback = definition.attributeChangedCallback; if (attributeChangedCallback && observedAttributes.indexOf(name) >= 0) { diff --git a/tests/index.html b/tests/index.html index 34d9eac..70005e4 100644 --- a/tests/index.html +++ b/tests/index.html @@ -14,7 +14,7 @@ (window.customElements = window.customElements || {}).forcePolyfill = true; - +