';
74 | CustomElements.takeRecords();
75 | testElements(target, 'x-auto-sub1', 'auto-sub1');
76 | testElements(target, 'x-auto-sub2', 'auto-sub2');
77 | done();
78 | });
79 | });
80 |
--------------------------------------------------------------------------------
/src/traverse.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
3 | * This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
4 | * The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
5 | * The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
6 | * Code distributed by Google as part of the polymer project is also
7 | * subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
8 | */
9 |
10 | // helper methods for traversing through element trees
11 | CustomElements.addModule(function(scope){
12 |
13 | // imports
14 | var IMPORT_LINK_TYPE = window.HTMLImports ? HTMLImports.IMPORT_LINK_TYPE : 'none';
15 |
16 | // walk the subtree rooted at node, including descent into shadow-roots,
17 | // applying 'cb' to each element
18 | function forSubtree(node, cb) {
19 | //flags.dom && node.childNodes && node.childNodes.length && console.group('subTree: ', node);
20 | findAllElements(node, function(e) {
21 | if (cb(e)) {
22 | return true;
23 | }
24 | forRoots(e, cb);
25 | });
26 | forRoots(node, cb);
27 | //flags.dom && node.childNodes && node.childNodes.length && console.groupEnd();
28 | }
29 |
30 |
31 | // walk the subtree rooted at node, applying 'find(element, data)' function
32 | // to each element
33 | // if 'find' returns true for 'element', do not search element's subtree
34 | function findAllElements(node, find, data) {
35 | var e = node.firstElementChild;
36 | if (!e) {
37 | e = node.firstChild;
38 | while (e && e.nodeType !== Node.ELEMENT_NODE) {
39 | e = e.nextSibling;
40 | }
41 | }
42 | while (e) {
43 | if (find(e, data) !== true) {
44 | findAllElements(e, find, data);
45 | }
46 | e = e.nextElementSibling;
47 | }
48 | return null;
49 | }
50 |
51 | // walk all shadowRoots on a given node.
52 | function forRoots(node, cb) {
53 | var root = node.shadowRoot;
54 | while(root) {
55 | forSubtree(root, cb);
56 | root = root.olderShadowRoot;
57 | }
58 | }
59 |
60 | /*
61 | Note that the import tree can consume itself and therefore special care
62 | must be taken to avoid recursion.
63 | */
64 | var processingDocuments;
65 | function forDocumentTree(doc, cb) {
66 | processingDocuments = [];
67 | _forDocumentTree(doc, cb);
68 | processingDocuments = null;
69 | }
70 |
71 |
72 | function _forDocumentTree(doc, cb) {
73 | doc = wrap(doc);
74 | if (processingDocuments.indexOf(doc) >= 0) {
75 | return;
76 | }
77 | processingDocuments.push(doc);
78 | var imports = doc.querySelectorAll('link[rel=' + IMPORT_LINK_TYPE + ']');
79 | for (var i=0, l=imports.length, n; (i' +
108 | '
';
109 | CustomElements.upgradeAll(work);
110 | var b$ = work.querySelectorAll('[is=y-button]');
111 | Array.prototype.forEach.call(b$, function(b, i) {
112 | assert.equal(b.test, 'ybutton');
113 | assert.equal(b.textContent, i);
114 | });
115 | });
116 |
117 | });
--------------------------------------------------------------------------------
/src/boot.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
3 | * This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
4 | * The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
5 | * The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
6 | * Code distributed by Google as part of the polymer project is also
7 | * subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
8 | */
9 | (function(scope){
10 |
11 | // imports
12 | var useNative = scope.useNative;
13 | var initializeModules = scope.initializeModules;
14 |
15 | // If native, setup stub api and bail.
16 | // NOTE: we fire `WebComponentsReady` under native for api compatibility
17 | if (useNative) {
18 | // stub
19 | var nop = function() {};
20 |
21 | // exports
22 | scope.watchShadow = nop;
23 | scope.upgradeAll = nop;
24 | scope.upgradeDocumentTree = nop;
25 | scope.takeRecords = nop;
26 | scope.instanceof = function(obj, base) {
27 | return obj instanceof base;
28 | };
29 |
30 | } else {
31 | // Initialize polyfill modules. Note, polyfill modules are loaded but not
32 | // executed; this is a convenient way to control which modules run when
33 | // the polyfill is required and allows the polyfill to load even when it's
34 | // not needed.
35 | initializeModules();
36 | }
37 |
38 | // imports
39 | var upgradeDocumentTree = scope.upgradeDocumentTree;
40 |
41 | // ShadowDOM polyfill wraps elements but some elements like `document`
42 | // cannot be wrapped so we help the polyfill by wrapping some elements.
43 | if (!window.wrap) {
44 | if (window.ShadowDOMPolyfill) {
45 | window.wrap = ShadowDOMPolyfill.wrapIfNeeded;
46 | window.unwrap = ShadowDOMPolyfill.unwrapIfNeeded;
47 | } else {
48 | window.wrap = window.unwrap = function(node) {
49 | return node;
50 | };
51 | }
52 | }
53 |
54 | // bootstrap parsing
55 | function bootstrap() {
56 | // parse document
57 | upgradeDocumentTree(wrap(document));
58 | // install upgrade hook if HTMLImports are available
59 | if (window.HTMLImports) {
60 | HTMLImports.__importsParsingHook = function(elt) {
61 | upgradeDocumentTree(wrap(elt.import));
62 | //CustomElements.parser.parse(elt.import);
63 | };
64 | }
65 | // set internal 'ready' flag, now document.registerElement will trigger
66 | // synchronous upgrades
67 | CustomElements.ready = true;
68 | // async to ensure *native* custom elements upgrade prior to this
69 | // DOMContentLoaded can fire before elements upgrade (e.g. when there's
70 | // an external script)
71 | setTimeout(function() {
72 | // capture blunt profiling data
73 | CustomElements.readyTime = Date.now();
74 | if (window.HTMLImports) {
75 | CustomElements.elapsed = CustomElements.readyTime - HTMLImports.readyTime;
76 | }
77 | // notify the system that we are bootstrapped
78 | document.dispatchEvent(
79 | new CustomEvent('WebComponentsReady', {bubbles: true})
80 | );
81 | });
82 | }
83 |
84 | // CustomEvent shim for IE
85 | if (typeof window.CustomEvent !== 'function') {
86 | window.CustomEvent = function(inType, params) {
87 | params = params || {};
88 | var e = document.createEvent('CustomEvent');
89 | e.initCustomEvent(inType, Boolean(params.bubbles), Boolean(params.cancelable), params.detail);
90 | return e;
91 | };
92 | window.CustomEvent.prototype = window.Event.prototype;
93 | }
94 |
95 | // When loading at readyState complete time (or via flag), boot custom elements
96 | // immediately.
97 | // If relevant, HTMLImports must already be loaded.
98 | if (document.readyState === 'complete' || scope.flags.eager) {
99 | bootstrap();
100 | // When loading at readyState interactive time, bootstrap only if HTMLImports
101 | // are not pending. Also avoid IE as the semantics of this state are unreliable.
102 | } else if (document.readyState === 'interactive' && !window.attachEvent &&
103 | (!window.HTMLImports || window.HTMLImports.ready)) {
104 | bootstrap();
105 | // When loading at other readyStates, wait for the appropriate DOM event to
106 | // bootstrap.
107 | } else {
108 | var loadEvent = window.HTMLImports && !HTMLImports.ready ?
109 | 'HTMLImportsLoaded' : 'DOMContentLoaded';
110 | window.addEventListener(loadEvent, bootstrap);
111 | }
112 |
113 | })(window.CustomElements);
114 |
--------------------------------------------------------------------------------
/src/upgrade.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
3 | * This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
4 | * The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
5 | * The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
6 | * Code distributed by Google as part of the polymer project is also
7 | * subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
8 | */
9 |
10 | /**
11 | * Implements custom element upgrading
12 | * @module upgrade
13 | */
14 |
15 | CustomElements.addModule(function(scope) {
16 |
17 | // imports
18 | var flags = scope.flags;
19 |
20 | /**
21 | * Upgrade an element to a custom element. Upgrading an element
22 | * causes the custom prototype to be applied, an `is` attribute
23 | * to be attached (as needed), and invocation of the `readyCallback`.
24 | * If the element is in the main document, the `attachedkCallback` method
25 | * will be invoked.
26 | * `upgrade` does nothing if the element is already upgraded, or
27 | * if it matches no registered custom tag name.
28 | *
29 | * @method ugprade
30 | * @param {Element} element The element to upgrade.
31 | * @return {Element} The upgraded element.
32 | */
33 | // Upgrade a node if it can be upgraded and is not already.
34 | function upgrade(node) {
35 | if (!node.__upgraded__ && (node.nodeType === Node.ELEMENT_NODE)) {
36 | var is = node.getAttribute('is');
37 | var definition = scope.getRegisteredDefinition(is || node.localName);
38 | if (definition) {
39 | if (is && definition.tag == node.localName) {
40 | return upgradeWithDefinition(node, definition);
41 | } else if (!is && !definition.extends) {
42 | return upgradeWithDefinition(node, definition);
43 | }
44 | }
45 | }
46 | }
47 |
48 | function upgradeWithDefinition(element, definition) {
49 | flags.upgrade && console.group('upgrade:', element.localName);
50 | // some definitions specify an 'is' attribute
51 | if (definition.is) {
52 | element.setAttribute('is', definition.is);
53 | }
54 | // make 'element' implement definition.prototype
55 | implementPrototype(element, definition);
56 | // flag as upgraded
57 | element.__upgraded__ = true;
58 | // lifecycle management
59 | created(element);
60 | // attachedCallback fires in tree order, call before recursing
61 | scope.attachedNode(element);
62 | // there should never be a shadow root on element at this point
63 | scope.upgradeSubtree(element);
64 | flags.upgrade && console.groupEnd();
65 | // OUTPUT
66 | return element;
67 | }
68 |
69 | // Set __proto__ on supported platforms and use a mixin strategy when
70 | // this is not supported; e.g. on IE10.
71 | function implementPrototype(element, definition) {
72 | // prototype swizzling is best
73 | if (Object.__proto__) {
74 | element.__proto__ = definition.prototype;
75 | } else {
76 | // where above we can re-acquire inPrototype via
77 | // getPrototypeOf(Element), we cannot do so when
78 | // we use mixin, so we install a magic reference
79 | customMixin(element, definition.prototype, definition.native);
80 | element.__proto__ = definition.prototype;
81 | }
82 | }
83 |
84 | function customMixin(inTarget, inSrc, inNative) {
85 | // TODO(sjmiles): 'used' allows us to only copy the 'youngest' version of
86 | // any property. This set should be precalculated. We also need to
87 | // consider this for supporting 'super'.
88 | var used = {};
89 | // start with inSrc
90 | var p = inSrc;
91 | // The default is HTMLElement.prototype, so we add a test to avoid mixing in
92 | // native prototypes
93 | while (p !== inNative && p !== HTMLElement.prototype) {
94 | var keys = Object.getOwnPropertyNames(p);
95 | for (var i=0, k; k=keys[i]; i++) {
96 | if (!used[k]) {
97 | Object.defineProperty(inTarget, k,
98 | Object.getOwnPropertyDescriptor(p, k));
99 | used[k] = 1;
100 | }
101 | }
102 | p = Object.getPrototypeOf(p);
103 | }
104 | }
105 |
106 | function created(element) {
107 | // invoke createdCallback
108 | if (element.createdCallback) {
109 | element.createdCallback();
110 | }
111 | }
112 |
113 | scope.upgrade = upgrade;
114 | scope.upgradeWithDefinition = upgradeWithDefinition;
115 | scope.implementPrototype = implementPrototype;
116 |
117 | });
118 |
--------------------------------------------------------------------------------
/test/js/documentRegister.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013 The Polymer Authors. All rights reserved.
3 | * Use of this source code is governed by a BSD-style
4 | * license that can be found in the LICENSE file.
5 | */
6 |
7 |
8 | // Adapted from:
9 | // https://code.google.com/p/chromium/codesearch#chromium/src/third_party/WebKit/LayoutTests/fast/dom/custom/document-register-type-extensions.html
10 | var testForm = document.createElement('form');
11 |
12 | function isFormControl(element)
13 | {
14 | testForm.appendChild(element);
15 | return element.form == testForm;
16 | }
17 |
18 |
19 | /*
20 | * Work around IE's insertion of XML Namespace elements into .outerHTML of HTMLUnknownElements
21 | *
22 | * Clone the input node, insert it into a div, and then read back the outerHTML, which is now stripped of the XML *
23 | * Namespace element
24 | */
25 | var isIE = navigator.userAgent.indexOf('Trident') > -1;
26 | function assertOuterHTML(element, expected) {
27 | var outerHTML = element.outerHTML;
28 | if (isIE) {
29 | var div = document.createElement('div');
30 | div.appendChild(element.cloneNode(true));
31 | outerHTML = div.firstChild.outerHTML;
32 | }
33 | chai.assert.equal(outerHTML, expected);
34 | }
35 |
36 | var hasProto = ({}.__proto__);
37 | function assertInstanceOf(element, constructor) {
38 | if (hasProto) {
39 | chai.assert.instanceOf(element, constructor);
40 | }
41 | }
42 |
43 | function assertNotInstanceOf(element, constructor) {
44 | if (hasProto) {
45 | chai.assert.notInstanceOf(element, constructor);
46 | }
47 | }
48 |
49 | suite('register-type-extensions', function() {
50 | var assert = chai.assert;
51 |
52 | var fooConstructor = document.registerElement('x-foo-x', {
53 | prototype: Object.create(HTMLElement.prototype) });
54 | var fooOuterHTML = '';
55 | var barConstructor = document.registerElement('x-bar-x', {
56 | prototype: Object.create(HTMLInputElement.prototype),
57 | extends:'input'});
58 | var barOuterHTML = '';
59 | var bazConstructor = document.registerElement('x-baz', {
60 | prototype: Object.create(fooConstructor.prototype) });
61 | var quxConstructor = document.registerElement('x-qux', {
62 | prototype: Object.create(barConstructor.prototype),
63 | extends:'input'});
64 |
65 | test('cannot register twice', function() {
66 | assert.throws(function() {
67 | document.registerElement('x-foo-x', {
68 | prototype: Object.create(HTMLDivElement.prototype) });
69 | });
70 | });
71 |
72 | suite('generated constructors', function() {
73 | test('custom tag', function() {
74 | var fooNewed = new fooConstructor();
75 | assertOuterHTML(fooNewed, fooOuterHTML);
76 | assertInstanceOf(fooNewed, fooConstructor);
77 | assertInstanceOf(fooNewed, HTMLElement);
78 | // This is part of the Blink tests, but not supported in Firefox with
79 | // polyfill. Similar assertions are also commented out below.
80 | // assertNotInstanceOf(fooNewed, HTMLUnknownElement);
81 |
82 | test('custom tag constructor', function() {
83 | assert.equal('a', 'b');
84 | });
85 | });
86 |
87 | test('type extension', function() {
88 | var barNewed = new barConstructor();
89 | assertOuterHTML(barNewed, barOuterHTML);
90 | assertInstanceOf(barNewed, barConstructor);
91 | assertInstanceOf(barNewed, HTMLInputElement);
92 | assert.ok(isFormControl(barNewed));
93 | });
94 |
95 | test('custom tag deriving from custom tag', function() {
96 | var bazNewed = new bazConstructor();
97 | var bazOuterHTML = '';
98 | assertOuterHTML(bazNewed, bazOuterHTML);
99 | assertInstanceOf(bazNewed, bazConstructor);
100 | assertInstanceOf(bazNewed, HTMLElement);
101 | // assertNotInstanceOf(bazNewed, HTMLUnknownElement);
102 | });
103 |
104 | test('type extension deriving from custom tag', function() {
105 | var quxNewed = new quxConstructor();
106 | var quxOuterHTML = '';
107 | assertInstanceOf(quxNewed, quxConstructor);
108 | assertInstanceOf(quxNewed, barConstructor);
109 | assertInstanceOf(quxNewed, HTMLInputElement);
110 | assertOuterHTML(quxNewed, quxOuterHTML);
111 | assert.ok(isFormControl(quxNewed));
112 | });
113 | });
114 |
115 | suite('single-parameter createElement', function() {
116 | test('custom tag', function() {
117 | var fooCreated = document.createElement('x-foo-x');
118 | assertOuterHTML(fooCreated, fooOuterHTML);
119 | assertInstanceOf(fooCreated, fooConstructor);
120 | });
121 |
122 | test('type extension', function() {
123 | var barCreated = document.createElement('x-bar-x');
124 | assertOuterHTML(barCreated, '');
125 | assertNotInstanceOf(barCreated, barConstructor);
126 | // assertNotInstanceOf(barCreated, HTMLUnknownElement);
127 | assertInstanceOf(barCreated, HTMLElement);
128 | });
129 |
130 | test('custom tag deriving from custom tag', function() {
131 | bazCreated = document.createElement('x-baz');
132 | assertOuterHTML(bazCreated, '');
133 | assertInstanceOf(bazCreated, bazConstructor);
134 | // assertNotInstanceOf(bazCreated, HTMLUnknownElement);
135 | });
136 |
137 | test('type extension deriving from custom tag', function() {
138 | quxCreated = document.createElement('x-qux');
139 | assertOuterHTML(quxCreated, '');
140 | assertNotInstanceOf(quxCreated, quxConstructor);
141 | // assertNotInstanceOf(quxCreated, HTMLUnknownElement);
142 | assertInstanceOf(quxCreated, HTMLElement);
143 | });
144 | });
145 |
146 | suite('createElement with type extensions', function() {
147 | test('extension is custom tag', function() {
148 | var divFooCreated = document.createElement('div', 'x-foo-x');
149 | assertOuterHTML(divFooCreated, '');
150 | assertNotInstanceOf(divFooCreated, fooConstructor);
151 | assertInstanceOf(divFooCreated, HTMLDivElement);
152 | });
153 |
154 | test('valid extension', function() {
155 | var inputBarCreated = document.createElement('input', 'x-bar-x');
156 | assertOuterHTML(inputBarCreated, barOuterHTML);
157 | assertInstanceOf(inputBarCreated, barConstructor);
158 | assertNotInstanceOf(inputBarCreated, HTMLUnknownElement);
159 | assert.ok(isFormControl(inputBarCreated));
160 | });
161 |
162 | test('type extension of incorrect tag', function() {
163 | var divBarCreated = document.createElement('div', 'x-bar-x');
164 | assertOuterHTML(divBarCreated, '');
165 | assertNotInstanceOf(divBarCreated, barConstructor);
166 | assertInstanceOf(divBarCreated, HTMLDivElement);
167 | });
168 |
169 | test('incorrect extension of custom tag', function() {
170 | var fooBarCreated = document.createElement('x-foo-x', 'x-bar-x');
171 | assertOuterHTML(fooBarCreated, '');
172 | assertInstanceOf(fooBarCreated, fooConstructor);
173 | });
174 |
175 | test('incorrect extension of type extension', function() {
176 | var barFooCreated = document.createElement('x-bar-x', 'x-foo-x');
177 | assertOuterHTML(barFooCreated, '');
178 | // assertNotInstanceOf(barFooCreated, HTMLUnknownElement);
179 | assertInstanceOf(barFooCreated, HTMLElement);
180 | });
181 |
182 | test('null type extension', function() {
183 | var fooCreatedNull = document.createElement('x-foo-x', null);
184 | assertOuterHTML(fooCreatedNull, fooOuterHTML);
185 | assertInstanceOf(fooCreatedNull, fooConstructor);
186 | });
187 |
188 | test('empty type extension', function() {
189 | fooCreatedEmpty = document.createElement('x-foo-x', '');
190 | assertOuterHTML(fooCreatedEmpty, fooOuterHTML);
191 | assertInstanceOf(fooCreatedEmpty, fooConstructor);
192 | });
193 |
194 | test('invalid tag name', function() {
195 | assert.throws(function() {
196 | document.createElement('@invalid', 'x-bar-x');
197 | });
198 | });
199 | });
200 |
201 | suite('parser', function() {
202 | function createElementFromHTML(html) {
203 | var container = document.createElement('div');
204 | container.innerHTML = html;
205 | if (window.CustomElements) {
206 | window.CustomElements.upgradeAll(container);
207 | }
208 | return container.firstChild;
209 | }
210 |
211 | test('custom tag', function() {
212 | var fooParsed = createElementFromHTML('');
213 | assertInstanceOf(fooParsed, fooConstructor);
214 | });
215 |
216 | test('type extension', function() {
217 | var barParsed = createElementFromHTML('');
218 | assertInstanceOf(barParsed, barConstructor);
219 | assert.ok(isFormControl(barParsed));
220 | });
221 |
222 | test('custom tag as type extension', function() {
223 | var divFooParsed = createElementFromHTML('