|
7 | 7 |
|
8 | 8 | // imports
|
9 | 9 |
|
10 |
| - var extend = Polymer.extend; |
| 10 | + var extend = scope.extend; |
11 | 11 | var apis = scope.api.declaration;
|
12 | 12 |
|
13 | 13 | // imperative implementation: Polymer()
|
14 | 14 |
|
15 |
| - // maps tag names to prototypes |
16 |
| - var prototypesByName = {}; |
17 |
| - |
18 |
| - function getRegisteredPrototype(name) { |
19 |
| - return prototypesByName[name]; |
20 |
| - } |
21 |
| - |
22 |
| - // elements waiting for prototype, by name |
23 |
| - var waitPrototype = {}; |
24 |
| - |
25 | 15 | // specify an 'own' prototype for tag `name`
|
26 | 16 | function element(name, prototype) {
|
27 | 17 | //console.log('registering [' + name + ']');
|
|
31 | 21 | notifyPrototype(name);
|
32 | 22 | }
|
33 | 23 |
|
34 |
| - function notifyPrototype(name) { |
35 |
| - if (waitPrototype[name]) { |
36 |
| - waitPrototype[name].registerWhenReady(); |
37 |
| - delete waitPrototype[name]; |
38 |
| - } |
39 |
| - } |
40 |
| - |
41 |
| - // elements waiting for super, by name |
42 |
| - var waitSuper = {}; |
43 |
| - |
44 |
| - function notifySuper(name) { |
45 |
| - registered[name] = true; |
46 |
| - var waiting = waitSuper[name]; |
47 |
| - if (waiting) { |
48 |
| - waiting.forEach(function(w) { |
49 |
| - w.registerWhenReady(); |
50 |
| - }); |
51 |
| - delete waitSuper[name]; |
52 |
| - } |
53 |
| - } |
54 |
| - |
55 |
| - // track document.register'ed tag names |
56 |
| - |
57 |
| - var registered = {}; |
58 |
| - |
59 |
| - function isRegistered(name) { |
60 |
| - return registered[name]; |
61 |
| - } |
62 |
| - |
63 |
| - // returns a prototype that chains to <tag> or HTMLElement |
64 |
| - function generatePrototype(tag) { |
65 |
| - return Object.create(HTMLElement.getPrototypeForTag(tag)); |
66 |
| - } |
67 |
| - |
68 |
| - // On platforms that do not support __proto__ (IE10), the prototype chain |
69 |
| - // of a custom element is simulated via installation of __proto__. |
70 |
| - // Although custom elements manages this, we install it here so it's |
71 |
| - // available during desugaring. |
72 |
| - function ensurePrototypeTraversal(prototype) { |
73 |
| - if (!Object.__proto__) { |
74 |
| - var ancestor = Object.getPrototypeOf(prototype); |
75 |
| - prototype.__proto__ = ancestor; |
76 |
| - if (scope.isBase(ancestor)) { |
77 |
| - ancestor.__proto__ = Object.getPrototypeOf(ancestor); |
78 |
| - } |
79 |
| - } |
80 |
| - } |
81 |
| - |
82 |
| - function whenImportsLoaded(doThis) { |
83 |
| - if (window.HTMLImports && !HTMLImports.readyTime) { |
84 |
| - addEventListener('HTMLImportsLoaded', doThis); |
85 |
| - } else { |
86 |
| - doThis(); |
87 |
| - } |
88 |
| - } |
89 |
| - |
90 | 24 | // declarative implementation: <polymer-element>
|
91 | 25 |
|
92 |
| - var prototype = generatePrototype(); |
93 |
| - |
94 |
| - extend(prototype, { |
95 |
| - // TODO(sjmiles): temporary BC |
96 |
| - readyCallback: function() { |
97 |
| - this.createdCallback(); |
98 |
| - }, |
| 26 | + var prototype = extend(Object.create(HTMLElement.prototype), { |
99 | 27 | createdCallback: function() {
|
100 | 28 | // fetch the element name
|
101 | 29 | this.name = this.getAttribute('name');
|
|
108 | 36 | if (!getRegisteredPrototype(name)) {
|
109 | 37 | // then wait for a prototype
|
110 | 38 | waitPrototype[name] = this;
|
111 |
| - // TODO(sjmiles): 'noscript' gambit is mutually exclusive |
112 |
| - // with 'async' gambit below |
113 |
| - // |
114 | 39 | // if explicitly marked as 'noscript'
|
115 | 40 | if (this.hasAttribute('noscript')) {
|
116 |
| - // go async to allow children to parse |
117 |
| - setTimeout(function() { |
118 |
| - // register with the default prototype |
119 |
| - element(name, null); |
120 |
| - }, 0); |
121 |
| - } |
122 |
| - // TODO(sjmiles): 'async' gambit is deemed too dangerous |
123 |
| - // because it changes the timing of noscript elements |
124 |
| - // in import from 'timeout 0' to 'HTMLImportsReady' |
125 |
| - /* |
126 |
| - // if we are not explicitly async... |
127 |
| - if (!this.hasAttribute('async')) { |
128 |
| - // then we expect the script to be registered |
129 |
| - // by end of microtask(-ish) and can otherwise |
130 |
| - // consider this element to have no script |
131 |
| - // |
132 |
| - // TODO(sjmiles): |
133 |
| - // we have to wait for end-of-microtask because |
134 |
| - // native CE upgrades polymer-element (any custom |
135 |
| - // element, really) *before* it's children are |
136 |
| - // parsed, and it's common for the script to |
137 |
| - // exist as a child of the polymer-element |
138 |
| - // |
139 |
| - // additionally, there is a massive asynchrony |
140 |
| - // between parsing HTML in imports and executing |
141 |
| - // script that foils the end of microtask gambit |
142 |
| - // Waiting on HTMLImportsLoaded signal solves |
143 |
| - // both timing problems for imports loaded |
144 |
| - // at startup under the import polyfill |
145 |
| - whenImportsLoaded(function() { |
146 |
| - if (!getRegisteredPrototype(name)) { |
147 |
| - console.warn('giving up waiting for script for [' + name + ']'); |
148 |
| - element(name, null); |
149 |
| - } |
150 |
| - }); |
| 41 | + // TODO(sorvell): CustomElements polyfill awareness: |
| 42 | + // noscript elements should upgrade in logical order |
| 43 | + // script injection ensures this under native custom elements; |
| 44 | + // under imports + ce polyfill, scripts run before upgrades |
| 45 | + // dependencies should be ready at upgrade time so register |
| 46 | + // prototype at this time. |
| 47 | + if (window.CustomElements && !CustomElements.useNative) { |
| 48 | + element(name); |
| 49 | + } else { |
| 50 | + var script = document.createElement('script'); |
| 51 | + script.textContent = 'Polymer(\'' + name + '\');'; |
| 52 | + this.appendChild(script); |
| 53 | + } |
151 | 54 | }
|
152 |
| - */ |
153 | 55 | return;
|
154 | 56 | }
|
155 | 57 | // fetch our extendee name
|
|
162 | 64 | return;
|
163 | 65 | }
|
164 | 66 | }
|
165 |
| - // TODO(sjmiles): HTMLImports polyfill awareness |
| 67 | + // TODO(sjmiles): HTMLImports polyfill awareness: |
166 | 68 | // elements in the main document are likely to parse
|
167 | 69 | // in advance of elements in imports because the
|
168 | 70 | // polyfill parser is simulated
|
|
177 | 79 | }
|
178 | 80 | },
|
179 | 81 | register: function(name, extendee) {
|
180 |
| - //console.log('register', name, extendee); |
181 | 82 | // build prototype combining extendee, Polymer base, and named api
|
182 | 83 | this.prototype = this.generateCustomPrototype(name, extendee);
|
183 | 84 | // backref
|
|
188 | 89 | // Potentially remove when spec bug is addressed.
|
189 | 90 | // https://www.w3.org/Bugs/Public/show_bug.cgi?id=21407
|
190 | 91 | this.addResolvePathApi();
|
191 |
| - ensurePrototypeTraversal(this.prototype); |
192 | 92 | // declarative features
|
193 | 93 | this.desugar();
|
194 | 94 | // under ShadowDOMPolyfill, transforms to approximate missing CSS features
|
|
219 | 119 | // cache the list of custom prototype names for faster reflection
|
220 | 120 | this.cacheProperties();
|
221 | 121 | },
|
222 |
| - // prototype marshaling |
223 |
| - // build prototype combining extendee, Polymer base, and named api |
224 |
| - generateCustomPrototype: function (name, extnds) { |
225 |
| - // basal prototype |
226 |
| - var prototype = this.generateBasePrototype(extnds); |
227 |
| - // mixin registered custom api |
228 |
| - return this.addNamedApi(prototype, name); |
229 |
| - }, |
230 |
| - // build prototype combining extendee, Polymer base, and named api |
231 |
| - generateBasePrototype: function(extnds) { |
232 |
| - // create a prototype based on tag-name extension |
233 |
| - var prototype = generatePrototype(extnds); |
234 |
| - // insert base api in inheritance chain (if needed) |
235 |
| - return this.ensureBaseApi(prototype); |
236 |
| - }, |
237 |
| - // install Polymer instance api into prototype chain, as needed |
238 |
| - ensureBaseApi: function(prototype) { |
239 |
| - if (!prototype.PolymerBase) { |
240 |
| - Object.keys(scope.api.instance).forEach(function(n) { |
241 |
| - extend(prototype, scope.api.instance[n]); |
242 |
| - }); |
243 |
| - prototype = Object.create(prototype); |
244 |
| - } |
245 |
| - // inherit publishing meta-data |
246 |
| - this.inheritAttributesObjects(prototype); |
247 |
| - // inherit event delegates |
248 |
| - this.inheritDelegates(prototype); |
249 |
| - // return buffed-up prototype |
250 |
| - return prototype; |
251 |
| - }, |
252 |
| - // mix api registered to 'name' into 'prototype' |
253 |
| - addNamedApi: function(prototype, name) { |
254 |
| - // combine custom api into prototype |
255 |
| - return extend(prototype, getRegisteredPrototype(name)); |
256 |
| - }, |
257 |
| - // make a fresh object that inherits from a prototype object |
258 |
| - inheritObject: function(prototype, name) { |
259 |
| - // copy inherited properties onto a new object |
260 |
| - prototype[name] = extend({}, Object.getPrototypeOf(prototype)[name]); |
261 |
| - }, |
262 |
| - // register 'prototype' to custom element 'name', store constructor |
263 |
| - registerPrototype: function(name) { |
264 |
| - // register the custom type |
265 |
| - this.ctor = document.register(name, { |
266 |
| - prototype: this.prototype |
267 |
| - }); |
268 |
| - // constructor shenanigans |
269 |
| - this.prototype.constructor = this.ctor; |
270 |
| - // register the prototype with HTMLElement for name lookup |
271 |
| - HTMLElement.register(name, this.prototype); |
272 |
| - }, |
273 | 122 | // if a named constructor is requested in element, map a reference
|
274 | 123 | // to the constructor to the given symbol
|
275 | 124 | publishConstructor: function() {
|
|
280 | 129 | }
|
281 | 130 | });
|
282 | 131 |
|
| 132 | + // semi-pluggable APIs |
| 133 | + // TODO(sjmiles): should be fully pluggable |
283 | 134 | Object.keys(apis).forEach(function(n) {
|
284 | 135 | extend(prototype, apis[n]);
|
285 | 136 | });
|
|
288 | 139 |
|
289 | 140 | document.register('polymer-element', {prototype: prototype});
|
290 | 141 |
|
291 |
| - // namespace shenanigans so we can expose our scope on the registration function |
| 142 | + // utility and bookkeeping |
| 143 | + |
| 144 | + // maps tag names to prototypes |
| 145 | + var prototypesByName = {}; |
| 146 | + |
| 147 | + function getRegisteredPrototype(name) { |
| 148 | + return prototypesByName[name]; |
| 149 | + } |
| 150 | + |
| 151 | + // elements waiting for prototype, by name |
| 152 | + var waitPrototype = {}; |
| 153 | + |
| 154 | + function notifyPrototype(name) { |
| 155 | + if (waitPrototype[name]) { |
| 156 | + waitPrototype[name].registerWhenReady(); |
| 157 | + delete waitPrototype[name]; |
| 158 | + } |
| 159 | + } |
| 160 | + |
| 161 | + // elements waiting for super, by name |
| 162 | + var waitSuper = {}; |
| 163 | + |
| 164 | + function notifySuper(name) { |
| 165 | + registered[name] = true; |
| 166 | + var waiting = waitSuper[name]; |
| 167 | + if (waiting) { |
| 168 | + waiting.forEach(function(w) { |
| 169 | + w.registerWhenReady(); |
| 170 | + }); |
| 171 | + delete waitSuper[name]; |
| 172 | + } |
| 173 | + } |
| 174 | + |
| 175 | + // track document.register'ed tag names |
| 176 | + |
| 177 | + var registered = {}; |
| 178 | + |
| 179 | + function isRegistered(name) { |
| 180 | + return registered[name]; |
| 181 | + } |
| 182 | + |
| 183 | + function whenImportsLoaded(doThis) { |
| 184 | + if (window.HTMLImports && !HTMLImports.readyTime) { |
| 185 | + addEventListener('HTMLImportsLoaded', doThis); |
| 186 | + } else { |
| 187 | + doThis(); |
| 188 | + } |
| 189 | + } |
| 190 | + |
| 191 | + // exports |
| 192 | + |
| 193 | + scope.getRegisteredPrototype = getRegisteredPrototype; |
| 194 | + |
| 195 | + // namespace shenanigans so we can expose our scope on the registration |
| 196 | + // function |
292 | 197 |
|
293 | 198 | // TODO(sjmiles): find a way to do this that is less terrible
|
294 | 199 | // copy window.Polymer properties onto `element()`
|
|
0 commit comments