OLD | NEW |
| (Empty) |
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://polyme
r.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/CON
TRIBUTORS.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/PA
TENTS.txt | |
8 */ | |
9 | |
10 window.Platform = window.Platform || {}; | |
11 // prepopulate window.logFlags if necessary | |
12 window.logFlags = window.logFlags || {}; | |
13 // process flags | |
14 (function(scope){ | |
15 // import | |
16 var flags = scope.flags || {}; | |
17 // populate flags from location | |
18 location.search.slice(1).split('&').forEach(function(o) { | |
19 o = o.split('='); | |
20 o[0] && (flags[o[0]] = o[1] || true); | |
21 }); | |
22 var entryPoint = document.currentScript || | |
23 document.querySelector('script[src*="platform.js"]'); | |
24 if (entryPoint) { | |
25 var a = entryPoint.attributes; | |
26 for (var i = 0, n; i < a.length; i++) { | |
27 n = a[i]; | |
28 if (n.name !== 'src') { | |
29 flags[n.name] = n.value || true; | |
30 } | |
31 } | |
32 } | |
33 if (flags.log) { | |
34 flags.log.split(',').forEach(function(f) { | |
35 window.logFlags[f] = true; | |
36 }); | |
37 } | |
38 // If any of these flags match 'native', then force native ShadowDOM; any | |
39 // other truthy value, or failure to detect native | |
40 // ShadowDOM, results in polyfill | |
41 flags.shadow = flags.shadow || flags.shadowdom || flags.polyfill; | |
42 if (flags.shadow === 'native') { | |
43 flags.shadow = false; | |
44 } else { | |
45 flags.shadow = flags.shadow || !HTMLElement.prototype.createShadowRoot; | |
46 } | |
47 | |
48 if (flags.shadow && document.querySelectorAll('script').length > 1) { | |
49 console.log('Warning: platform.js is not the first script on the page. ' + | |
50 'See http://www.polymer-project.org/docs/start/platform.html#setup ' + | |
51 'for details.'); | |
52 } | |
53 | |
54 // CustomElements polyfill flag | |
55 if (flags.register) { | |
56 window.CustomElements = window.CustomElements || {flags: {}}; | |
57 window.CustomElements.flags.register = flags.register; | |
58 } | |
59 | |
60 if (flags.imports) { | |
61 window.HTMLImports = window.HTMLImports || {flags: {}}; | |
62 window.HTMLImports.flags.imports = flags.imports; | |
63 } | |
64 | |
65 // export | |
66 scope.flags = flags; | |
67 })(Platform); | |
68 | |
69 /* | |
70 * Copyright 2012 The Polymer Authors. All rights reserved. | |
71 * Use of this source code is governed by a BSD-style | |
72 * license that can be found in the LICENSE file. | |
73 */ | |
74 | |
75 if (typeof WeakMap === 'undefined') { | |
76 (function() { | |
77 var defineProperty = Object.defineProperty; | |
78 var counter = Date.now() % 1e9; | |
79 | |
80 var WeakMap = function() { | |
81 this.name = '__st' + (Math.random() * 1e9 >>> 0) + (counter++ + '__'); | |
82 }; | |
83 | |
84 WeakMap.prototype = { | |
85 set: function(key, value) { | |
86 var entry = key[this.name]; | |
87 if (entry && entry[0] === key) | |
88 entry[1] = value; | |
89 else | |
90 defineProperty(key, this.name, {value: [key, value], writable: true}); | |
91 return this; | |
92 }, | |
93 get: function(key) { | |
94 var entry; | |
95 return (entry = key[this.name]) && entry[0] === key ? | |
96 entry[1] : undefined; | |
97 }, | |
98 delete: function(key) { | |
99 var entry = key[this.name]; | |
100 if (!entry) return false; | |
101 var hasValue = entry[0] === key; | |
102 entry[0] = entry[1] = undefined; | |
103 return hasValue; | |
104 }, | |
105 has: function(key) { | |
106 var entry = key[this.name]; | |
107 if (!entry) return false; | |
108 return entry[0] === key; | |
109 } | |
110 }; | |
111 | |
112 window.WeakMap = WeakMap; | |
113 })(); | |
114 } | |
115 | |
116 // select ShadowDOM impl | |
117 if (Platform.flags.shadow) { | |
118 | |
119 /* | |
120 * Copyright (c) 2014 The Polymer Project Authors. All rights reserved. | |
121 * This code may only be used under the BSD style license found at http://polyme
r.github.io/LICENSE.txt | |
122 * The complete set of authors may be found at http://polymer.github.io/AUTHORS.
txt | |
123 * The complete set of contributors may be found at http://polymer.github.io/CON
TRIBUTORS.txt | |
124 * Code distributed by Google as part of the polymer project is also | |
125 * subject to an additional IP rights grant found at http://polymer.github.io/PA
TENTS.txt | |
126 */ | |
127 | |
128 (function(global) { | |
129 'use strict'; | |
130 | |
131 var testingExposeCycleCount = global.testingExposeCycleCount; | |
132 | |
133 // Detect and do basic sanity checking on Object/Array.observe. | |
134 function detectObjectObserve() { | |
135 if (typeof Object.observe !== 'function' || | |
136 typeof Array.observe !== 'function') { | |
137 return false; | |
138 } | |
139 | |
140 var records = []; | |
141 | |
142 function callback(recs) { | |
143 records = recs; | |
144 } | |
145 | |
146 var test = {}; | |
147 var arr = []; | |
148 Object.observe(test, callback); | |
149 Array.observe(arr, callback); | |
150 test.id = 1; | |
151 test.id = 2; | |
152 delete test.id; | |
153 arr.push(1, 2); | |
154 arr.length = 0; | |
155 | |
156 Object.deliverChangeRecords(callback); | |
157 if (records.length !== 5) | |
158 return false; | |
159 | |
160 if (records[0].type != 'add' || | |
161 records[1].type != 'update' || | |
162 records[2].type != 'delete' || | |
163 records[3].type != 'splice' || | |
164 records[4].type != 'splice') { | |
165 return false; | |
166 } | |
167 | |
168 Object.unobserve(test, callback); | |
169 Array.unobserve(arr, callback); | |
170 | |
171 return true; | |
172 } | |
173 | |
174 var hasObserve = detectObjectObserve(); | |
175 | |
176 function detectEval() { | |
177 // Don't test for eval if we're running in a Chrome App environment. | |
178 // We check for APIs set that only exist in a Chrome App context. | |
179 if (typeof chrome !== 'undefined' && chrome.app && chrome.app.runtime) { | |
180 return false; | |
181 } | |
182 | |
183 // Firefox OS Apps do not allow eval. This feature detection is very hacky | |
184 // but even if some other platform adds support for this function this code | |
185 // will continue to work. | |
186 if (typeof navigator != 'undefined' && navigator.getDeviceStorage) { | |
187 return false; | |
188 } | |
189 | |
190 try { | |
191 var f = new Function('', 'return true;'); | |
192 return f(); | |
193 } catch (ex) { | |
194 return false; | |
195 } | |
196 } | |
197 | |
198 var hasEval = detectEval(); | |
199 | |
200 function isIndex(s) { | |
201 return +s === s >>> 0 && s !== ''; | |
202 } | |
203 | |
204 function toNumber(s) { | |
205 return +s; | |
206 } | |
207 | |
208 function isObject(obj) { | |
209 return obj === Object(obj); | |
210 } | |
211 | |
212 var numberIsNaN = global.Number.isNaN || function(value) { | |
213 return typeof value === 'number' && global.isNaN(value); | |
214 } | |
215 | |
216 function areSameValue(left, right) { | |
217 if (left === right) | |
218 return left !== 0 || 1 / left === 1 / right; | |
219 if (numberIsNaN(left) && numberIsNaN(right)) | |
220 return true; | |
221 | |
222 return left !== left && right !== right; | |
223 } | |
224 | |
225 var createObject = ('__proto__' in {}) ? | |
226 function(obj) { return obj; } : | |
227 function(obj) { | |
228 var proto = obj.__proto__; | |
229 if (!proto) | |
230 return obj; | |
231 var newObject = Object.create(proto); | |
232 Object.getOwnPropertyNames(obj).forEach(function(name) { | |
233 Object.defineProperty(newObject, name, | |
234 Object.getOwnPropertyDescriptor(obj, name)); | |
235 }); | |
236 return newObject; | |
237 }; | |
238 | |
239 var identStart = '[\$_a-zA-Z]'; | |
240 var identPart = '[\$_a-zA-Z0-9]'; | |
241 var identRegExp = new RegExp('^' + identStart + '+' + identPart + '*' + '$'); | |
242 | |
243 function getPathCharType(char) { | |
244 if (char === undefined) | |
245 return 'eof'; | |
246 | |
247 var code = char.charCodeAt(0); | |
248 | |
249 switch(code) { | |
250 case 0x5B: // [ | |
251 case 0x5D: // ] | |
252 case 0x2E: // . | |
253 case 0x22: // " | |
254 case 0x27: // ' | |
255 case 0x30: // 0 | |
256 return char; | |
257 | |
258 case 0x5F: // _ | |
259 case 0x24: // $ | |
260 return 'ident'; | |
261 | |
262 case 0x20: // Space | |
263 case 0x09: // Tab | |
264 case 0x0A: // Newline | |
265 case 0x0D: // Return | |
266 case 0xA0: // No-break space | |
267 case 0xFEFF: // Byte Order Mark | |
268 case 0x2028: // Line Separator | |
269 case 0x2029: // Paragraph Separator | |
270 return 'ws'; | |
271 } | |
272 | |
273 // a-z, A-Z | |
274 if ((0x61 <= code && code <= 0x7A) || (0x41 <= code && code <= 0x5A)) | |
275 return 'ident'; | |
276 | |
277 // 1-9 | |
278 if (0x31 <= code && code <= 0x39) | |
279 return 'number'; | |
280 | |
281 return 'else'; | |
282 } | |
283 | |
284 var pathStateMachine = { | |
285 'beforePath': { | |
286 'ws': ['beforePath'], | |
287 'ident': ['inIdent', 'append'], | |
288 '[': ['beforeElement'], | |
289 'eof': ['afterPath'] | |
290 }, | |
291 | |
292 'inPath': { | |
293 'ws': ['inPath'], | |
294 '.': ['beforeIdent'], | |
295 '[': ['beforeElement'], | |
296 'eof': ['afterPath'] | |
297 }, | |
298 | |
299 'beforeIdent': { | |
300 'ws': ['beforeIdent'], | |
301 'ident': ['inIdent', 'append'] | |
302 }, | |
303 | |
304 'inIdent': { | |
305 'ident': ['inIdent', 'append'], | |
306 '0': ['inIdent', 'append'], | |
307 'number': ['inIdent', 'append'], | |
308 'ws': ['inPath', 'push'], | |
309 '.': ['beforeIdent', 'push'], | |
310 '[': ['beforeElement', 'push'], | |
311 'eof': ['afterPath', 'push'] | |
312 }, | |
313 | |
314 'beforeElement': { | |
315 'ws': ['beforeElement'], | |
316 '0': ['afterZero', 'append'], | |
317 'number': ['inIndex', 'append'], | |
318 "'": ['inSingleQuote', 'append', ''], | |
319 '"': ['inDoubleQuote', 'append', ''] | |
320 }, | |
321 | |
322 'afterZero': { | |
323 'ws': ['afterElement', 'push'], | |
324 ']': ['inPath', 'push'] | |
325 }, | |
326 | |
327 'inIndex': { | |
328 '0': ['inIndex', 'append'], | |
329 'number': ['inIndex', 'append'], | |
330 'ws': ['afterElement'], | |
331 ']': ['inPath', 'push'] | |
332 }, | |
333 | |
334 'inSingleQuote': { | |
335 "'": ['afterElement'], | |
336 'eof': ['error'], | |
337 'else': ['inSingleQuote', 'append'] | |
338 }, | |
339 | |
340 'inDoubleQuote': { | |
341 '"': ['afterElement'], | |
342 'eof': ['error'], | |
343 'else': ['inDoubleQuote', 'append'] | |
344 }, | |
345 | |
346 'afterElement': { | |
347 'ws': ['afterElement'], | |
348 ']': ['inPath', 'push'] | |
349 } | |
350 } | |
351 | |
352 function noop() {} | |
353 | |
354 function parsePath(path) { | |
355 var keys = []; | |
356 var index = -1; | |
357 var c, newChar, key, type, transition, action, typeMap, mode = 'beforePath'; | |
358 | |
359 var actions = { | |
360 push: function() { | |
361 if (key === undefined) | |
362 return; | |
363 | |
364 keys.push(key); | |
365 key = undefined; | |
366 }, | |
367 | |
368 append: function() { | |
369 if (key === undefined) | |
370 key = newChar | |
371 else | |
372 key += newChar; | |
373 } | |
374 }; | |
375 | |
376 function maybeUnescapeQuote() { | |
377 if (index >= path.length) | |
378 return; | |
379 | |
380 var nextChar = path[index + 1]; | |
381 if ((mode == 'inSingleQuote' && nextChar == "'") || | |
382 (mode == 'inDoubleQuote' && nextChar == '"')) { | |
383 index++; | |
384 newChar = nextChar; | |
385 actions.append(); | |
386 return true; | |
387 } | |
388 } | |
389 | |
390 while (mode) { | |
391 index++; | |
392 c = path[index]; | |
393 | |
394 if (c == '\\' && maybeUnescapeQuote(mode)) | |
395 continue; | |
396 | |
397 type = getPathCharType(c); | |
398 typeMap = pathStateMachine[mode]; | |
399 transition = typeMap[type] || typeMap['else'] || 'error'; | |
400 | |
401 if (transition == 'error') | |
402 return; // parse error; | |
403 | |
404 mode = transition[0]; | |
405 action = actions[transition[1]] || noop; | |
406 newChar = transition[2] === undefined ? c : transition[2]; | |
407 action(); | |
408 | |
409 if (mode === 'afterPath') { | |
410 return keys; | |
411 } | |
412 } | |
413 | |
414 return; // parse error | |
415 } | |
416 | |
417 function isIdent(s) { | |
418 return identRegExp.test(s); | |
419 } | |
420 | |
421 var constructorIsPrivate = {}; | |
422 | |
423 function Path(parts, privateToken) { | |
424 if (privateToken !== constructorIsPrivate) | |
425 throw Error('Use Path.get to retrieve path objects'); | |
426 | |
427 for (var i = 0; i < parts.length; i++) { | |
428 this.push(String(parts[i])); | |
429 } | |
430 | |
431 if (hasEval && this.length) { | |
432 this.getValueFrom = this.compiledGetValueFromFn(); | |
433 } | |
434 } | |
435 | |
436 // TODO(rafaelw): Make simple LRU cache | |
437 var pathCache = {}; | |
438 | |
439 function getPath(pathString) { | |
440 if (pathString instanceof Path) | |
441 return pathString; | |
442 | |
443 if (pathString == null || pathString.length == 0) | |
444 pathString = ''; | |
445 | |
446 if (typeof pathString != 'string') { | |
447 if (isIndex(pathString.length)) { | |
448 // Constructed with array-like (pre-parsed) keys | |
449 return new Path(pathString, constructorIsPrivate); | |
450 } | |
451 | |
452 pathString = String(pathString); | |
453 } | |
454 | |
455 var path = pathCache[pathString]; | |
456 if (path) | |
457 return path; | |
458 | |
459 var parts = parsePath(pathString); | |
460 if (!parts) | |
461 return invalidPath; | |
462 | |
463 var path = new Path(parts, constructorIsPrivate); | |
464 pathCache[pathString] = path; | |
465 return path; | |
466 } | |
467 | |
468 Path.get = getPath; | |
469 | |
470 function formatAccessor(key) { | |
471 if (isIndex(key)) { | |
472 return '[' + key + ']'; | |
473 } else { | |
474 return '["' + key.replace(/"/g, '\\"') + '"]'; | |
475 } | |
476 } | |
477 | |
478 Path.prototype = createObject({ | |
479 __proto__: [], | |
480 valid: true, | |
481 | |
482 toString: function() { | |
483 var pathString = ''; | |
484 for (var i = 0; i < this.length; i++) { | |
485 var key = this[i]; | |
486 if (isIdent(key)) { | |
487 pathString += i ? '.' + key : key; | |
488 } else { | |
489 pathString += formatAccessor(key); | |
490 } | |
491 } | |
492 | |
493 return pathString; | |
494 }, | |
495 | |
496 getValueFrom: function(obj, directObserver) { | |
497 for (var i = 0; i < this.length; i++) { | |
498 if (obj == null) | |
499 return; | |
500 obj = obj[this[i]]; | |
501 } | |
502 return obj; | |
503 }, | |
504 | |
505 iterateObjects: function(obj, observe) { | |
506 for (var i = 0; i < this.length; i++) { | |
507 if (i) | |
508 obj = obj[this[i - 1]]; | |
509 if (!isObject(obj)) | |
510 return; | |
511 observe(obj, this[0]); | |
512 } | |
513 }, | |
514 | |
515 compiledGetValueFromFn: function() { | |
516 var str = ''; | |
517 var pathString = 'obj'; | |
518 str += 'if (obj != null'; | |
519 var i = 0; | |
520 var key; | |
521 for (; i < (this.length - 1); i++) { | |
522 key = this[i]; | |
523 pathString += isIdent(key) ? '.' + key : formatAccessor(key); | |
524 str += ' &&\n ' + pathString + ' != null'; | |
525 } | |
526 str += ')\n'; | |
527 | |
528 var key = this[i]; | |
529 pathString += isIdent(key) ? '.' + key : formatAccessor(key); | |
530 | |
531 str += ' return ' + pathString + ';\nelse\n return undefined;'; | |
532 return new Function('obj', str); | |
533 }, | |
534 | |
535 setValueFrom: function(obj, value) { | |
536 if (!this.length) | |
537 return false; | |
538 | |
539 for (var i = 0; i < this.length - 1; i++) { | |
540 if (!isObject(obj)) | |
541 return false; | |
542 obj = obj[this[i]]; | |
543 } | |
544 | |
545 if (!isObject(obj)) | |
546 return false; | |
547 | |
548 obj[this[i]] = value; | |
549 return true; | |
550 } | |
551 }); | |
552 | |
553 var invalidPath = new Path('', constructorIsPrivate); | |
554 invalidPath.valid = false; | |
555 invalidPath.getValueFrom = invalidPath.setValueFrom = function() {}; | |
556 | |
557 var MAX_DIRTY_CHECK_CYCLES = 1000; | |
558 | |
559 function dirtyCheck(observer) { | |
560 var cycles = 0; | |
561 while (cycles < MAX_DIRTY_CHECK_CYCLES && observer.check_()) { | |
562 cycles++; | |
563 } | |
564 if (testingExposeCycleCount) | |
565 global.dirtyCheckCycleCount = cycles; | |
566 | |
567 return cycles > 0; | |
568 } | |
569 | |
570 function objectIsEmpty(object) { | |
571 for (var prop in object) | |
572 return false; | |
573 return true; | |
574 } | |
575 | |
576 function diffIsEmpty(diff) { | |
577 return objectIsEmpty(diff.added) && | |
578 objectIsEmpty(diff.removed) && | |
579 objectIsEmpty(diff.changed); | |
580 } | |
581 | |
582 function diffObjectFromOldObject(object, oldObject) { | |
583 var added = {}; | |
584 var removed = {}; | |
585 var changed = {}; | |
586 | |
587 for (var prop in oldObject) { | |
588 var newValue = object[prop]; | |
589 | |
590 if (newValue !== undefined && newValue === oldObject[prop]) | |
591 continue; | |
592 | |
593 if (!(prop in object)) { | |
594 removed[prop] = undefined; | |
595 continue; | |
596 } | |
597 | |
598 if (newValue !== oldObject[prop]) | |
599 changed[prop] = newValue; | |
600 } | |
601 | |
602 for (var prop in object) { | |
603 if (prop in oldObject) | |
604 continue; | |
605 | |
606 added[prop] = object[prop]; | |
607 } | |
608 | |
609 if (Array.isArray(object) && object.length !== oldObject.length) | |
610 changed.length = object.length; | |
611 | |
612 return { | |
613 added: added, | |
614 removed: removed, | |
615 changed: changed | |
616 }; | |
617 } | |
618 | |
619 var eomTasks = []; | |
620 function runEOMTasks() { | |
621 if (!eomTasks.length) | |
622 return false; | |
623 | |
624 for (var i = 0; i < eomTasks.length; i++) { | |
625 eomTasks[i](); | |
626 } | |
627 eomTasks.length = 0; | |
628 return true; | |
629 } | |
630 | |
631 var runEOM = hasObserve ? (function(){ | |
632 var eomObj = { pingPong: true }; | |
633 var eomRunScheduled = false; | |
634 | |
635 Object.observe(eomObj, function() { | |
636 runEOMTasks(); | |
637 eomRunScheduled = false; | |
638 }); | |
639 | |
640 return function(fn) { | |
641 eomTasks.push(fn); | |
642 if (!eomRunScheduled) { | |
643 eomRunScheduled = true; | |
644 eomObj.pingPong = !eomObj.pingPong; | |
645 } | |
646 }; | |
647 })() : | |
648 (function() { | |
649 return function(fn) { | |
650 eomTasks.push(fn); | |
651 }; | |
652 })(); | |
653 | |
654 var observedObjectCache = []; | |
655 | |
656 function newObservedObject() { | |
657 var observer; | |
658 var object; | |
659 var discardRecords = false; | |
660 var first = true; | |
661 | |
662 function callback(records) { | |
663 if (observer && observer.state_ === OPENED && !discardRecords) | |
664 observer.check_(records); | |
665 } | |
666 | |
667 return { | |
668 open: function(obs) { | |
669 if (observer) | |
670 throw Error('ObservedObject in use'); | |
671 | |
672 if (!first) | |
673 Object.deliverChangeRecords(callback); | |
674 | |
675 observer = obs; | |
676 first = false; | |
677 }, | |
678 observe: function(obj, arrayObserve) { | |
679 object = obj; | |
680 if (arrayObserve) | |
681 Array.observe(object, callback); | |
682 else | |
683 Object.observe(object, callback); | |
684 }, | |
685 deliver: function(discard) { | |
686 discardRecords = discard; | |
687 Object.deliverChangeRecords(callback); | |
688 discardRecords = false; | |
689 }, | |
690 close: function() { | |
691 observer = undefined; | |
692 Object.unobserve(object, callback); | |
693 observedObjectCache.push(this); | |
694 } | |
695 }; | |
696 } | |
697 | |
698 /* | |
699 * The observedSet abstraction is a perf optimization which reduces the total | |
700 * number of Object.observe observations of a set of objects. The idea is that | |
701 * groups of Observers will have some object dependencies in common and this | |
702 * observed set ensures that each object in the transitive closure of | |
703 * dependencies is only observed once. The observedSet acts as a write barrier | |
704 * such that whenever any change comes through, all Observers are checked for | |
705 * changed values. | |
706 * | |
707 * Note that this optimization is explicitly moving work from setup-time to | |
708 * change-time. | |
709 * | |
710 * TODO(rafaelw): Implement "garbage collection". In order to move work off | |
711 * the critical path, when Observers are closed, their observed objects are | |
712 * not Object.unobserve(d). As a result, it's possible that if the observedSet | |
713 * is kept open, but some Observers have been closed, it could cause "leaks" | |
714 * (prevent otherwise collectable objects from being collected). At some | |
715 * point, we should implement incremental "gc" which keeps a list of | |
716 * observedSets which may need clean-up and does small amounts of cleanup on a | |
717 * timeout until all is clean. | |
718 */ | |
719 | |
720 function getObservedObject(observer, object, arrayObserve) { | |
721 var dir = observedObjectCache.pop() || newObservedObject(); | |
722 dir.open(observer); | |
723 dir.observe(object, arrayObserve); | |
724 return dir; | |
725 } | |
726 | |
727 var observedSetCache = []; | |
728 | |
729 function newObservedSet() { | |
730 var observerCount = 0; | |
731 var observers = []; | |
732 var objects = []; | |
733 var rootObj; | |
734 var rootObjProps; | |
735 | |
736 function observe(obj, prop) { | |
737 if (!obj) | |
738 return; | |
739 | |
740 if (obj === rootObj) | |
741 rootObjProps[prop] = true; | |
742 | |
743 if (objects.indexOf(obj) < 0) { | |
744 objects.push(obj); | |
745 Object.observe(obj, callback); | |
746 } | |
747 | |
748 observe(Object.getPrototypeOf(obj), prop); | |
749 } | |
750 | |
751 function allRootObjNonObservedProps(recs) { | |
752 for (var i = 0; i < recs.length; i++) { | |
753 var rec = recs[i]; | |
754 if (rec.object !== rootObj || | |
755 rootObjProps[rec.name] || | |
756 rec.type === 'setPrototype') { | |
757 return false; | |
758 } | |
759 } | |
760 return true; | |
761 } | |
762 | |
763 function callback(recs) { | |
764 if (allRootObjNonObservedProps(recs)) | |
765 return; | |
766 | |
767 var observer; | |
768 for (var i = 0; i < observers.length; i++) { | |
769 observer = observers[i]; | |
770 if (observer.state_ == OPENED) { | |
771 observer.iterateObjects_(observe); | |
772 } | |
773 } | |
774 | |
775 for (var i = 0; i < observers.length; i++) { | |
776 observer = observers[i]; | |
777 if (observer.state_ == OPENED) { | |
778 observer.check_(); | |
779 } | |
780 } | |
781 } | |
782 | |
783 var record = { | |
784 object: undefined, | |
785 objects: objects, | |
786 open: function(obs, object) { | |
787 if (!rootObj) { | |
788 rootObj = object; | |
789 rootObjProps = {}; | |
790 } | |
791 | |
792 observers.push(obs); | |
793 observerCount++; | |
794 obs.iterateObjects_(observe); | |
795 }, | |
796 close: function(obs) { | |
797 observerCount--; | |
798 if (observerCount > 0) { | |
799 return; | |
800 } | |
801 | |
802 for (var i = 0; i < objects.length; i++) { | |
803 Object.unobserve(objects[i], callback); | |
804 Observer.unobservedCount++; | |
805 } | |
806 | |
807 observers.length = 0; | |
808 objects.length = 0; | |
809 rootObj = undefined; | |
810 rootObjProps = undefined; | |
811 observedSetCache.push(this); | |
812 } | |
813 }; | |
814 | |
815 return record; | |
816 } | |
817 | |
818 var lastObservedSet; | |
819 | |
820 function getObservedSet(observer, obj) { | |
821 if (!lastObservedSet || lastObservedSet.object !== obj) { | |
822 lastObservedSet = observedSetCache.pop() || newObservedSet(); | |
823 lastObservedSet.object = obj; | |
824 } | |
825 lastObservedSet.open(observer, obj); | |
826 return lastObservedSet; | |
827 } | |
828 | |
829 var UNOPENED = 0; | |
830 var OPENED = 1; | |
831 var CLOSED = 2; | |
832 var RESETTING = 3; | |
833 | |
834 var nextObserverId = 1; | |
835 | |
836 function Observer() { | |
837 this.state_ = UNOPENED; | |
838 this.callback_ = undefined; | |
839 this.target_ = undefined; // TODO(rafaelw): Should be WeakRef | |
840 this.directObserver_ = undefined; | |
841 this.value_ = undefined; | |
842 this.id_ = nextObserverId++; | |
843 } | |
844 | |
845 Observer.prototype = { | |
846 open: function(callback, target) { | |
847 if (this.state_ != UNOPENED) | |
848 throw Error('Observer has already been opened.'); | |
849 | |
850 addToAll(this); | |
851 this.callback_ = callback; | |
852 this.target_ = target; | |
853 this.connect_(); | |
854 this.state_ = OPENED; | |
855 return this.value_; | |
856 }, | |
857 | |
858 close: function() { | |
859 if (this.state_ != OPENED) | |
860 return; | |
861 | |
862 removeFromAll(this); | |
863 this.disconnect_(); | |
864 this.value_ = undefined; | |
865 this.callback_ = undefined; | |
866 this.target_ = undefined; | |
867 this.state_ = CLOSED; | |
868 }, | |
869 | |
870 deliver: function() { | |
871 if (this.state_ != OPENED) | |
872 return; | |
873 | |
874 dirtyCheck(this); | |
875 }, | |
876 | |
877 report_: function(changes) { | |
878 try { | |
879 this.callback_.apply(this.target_, changes); | |
880 } catch (ex) { | |
881 Observer._errorThrownDuringCallback = true; | |
882 console.error('Exception caught during observer callback: ' + | |
883 (ex.stack || ex)); | |
884 } | |
885 }, | |
886 | |
887 discardChanges: function() { | |
888 this.check_(undefined, true); | |
889 return this.value_; | |
890 } | |
891 } | |
892 | |
893 var collectObservers = !hasObserve; | |
894 var allObservers; | |
895 Observer._allObserversCount = 0; | |
896 | |
897 if (collectObservers) { | |
898 allObservers = []; | |
899 } | |
900 | |
901 function addToAll(observer) { | |
902 Observer._allObserversCount++; | |
903 if (!collectObservers) | |
904 return; | |
905 | |
906 allObservers.push(observer); | |
907 } | |
908 | |
909 function removeFromAll(observer) { | |
910 Observer._allObserversCount--; | |
911 } | |
912 | |
913 var runningMicrotaskCheckpoint = false; | |
914 | |
915 global.Platform = global.Platform || {}; | |
916 | |
917 global.Platform.performMicrotaskCheckpoint = function() { | |
918 if (runningMicrotaskCheckpoint) | |
919 return; | |
920 | |
921 if (!collectObservers) | |
922 return; | |
923 | |
924 runningMicrotaskCheckpoint = true; | |
925 | |
926 var cycles = 0; | |
927 var anyChanged, toCheck; | |
928 | |
929 do { | |
930 cycles++; | |
931 toCheck = allObservers; | |
932 allObservers = []; | |
933 anyChanged = false; | |
934 | |
935 for (var i = 0; i < toCheck.length; i++) { | |
936 var observer = toCheck[i]; | |
937 if (observer.state_ != OPENED) | |
938 continue; | |
939 | |
940 if (observer.check_()) | |
941 anyChanged = true; | |
942 | |
943 allObservers.push(observer); | |
944 } | |
945 if (runEOMTasks()) | |
946 anyChanged = true; | |
947 } while (cycles < MAX_DIRTY_CHECK_CYCLES && anyChanged); | |
948 | |
949 if (testingExposeCycleCount) | |
950 global.dirtyCheckCycleCount = cycles; | |
951 | |
952 runningMicrotaskCheckpoint = false; | |
953 }; | |
954 | |
955 if (collectObservers) { | |
956 global.Platform.clearObservers = function() { | |
957 allObservers = []; | |
958 }; | |
959 } | |
960 | |
961 function ObjectObserver(object) { | |
962 Observer.call(this); | |
963 this.value_ = object; | |
964 this.oldObject_ = undefined; | |
965 } | |
966 | |
967 ObjectObserver.prototype = createObject({ | |
968 __proto__: Observer.prototype, | |
969 | |
970 arrayObserve: false, | |
971 | |
972 connect_: function(callback, target) { | |
973 if (hasObserve) { | |
974 this.directObserver_ = getObservedObject(this, this.value_, | |
975 this.arrayObserve); | |
976 } else { | |
977 this.oldObject_ = this.copyObject(this.value_); | |
978 } | |
979 | |
980 }, | |
981 | |
982 copyObject: function(object) { | |
983 var copy = Array.isArray(object) ? [] : {}; | |
984 for (var prop in object) { | |
985 copy[prop] = object[prop]; | |
986 }; | |
987 if (Array.isArray(object)) | |
988 copy.length = object.length; | |
989 return copy; | |
990 }, | |
991 | |
992 check_: function(changeRecords, skipChanges) { | |
993 var diff; | |
994 var oldValues; | |
995 if (hasObserve) { | |
996 if (!changeRecords) | |
997 return false; | |
998 | |
999 oldValues = {}; | |
1000 diff = diffObjectFromChangeRecords(this.value_, changeRecords, | |
1001 oldValues); | |
1002 } else { | |
1003 oldValues = this.oldObject_; | |
1004 diff = diffObjectFromOldObject(this.value_, this.oldObject_); | |
1005 } | |
1006 | |
1007 if (diffIsEmpty(diff)) | |
1008 return false; | |
1009 | |
1010 if (!hasObserve) | |
1011 this.oldObject_ = this.copyObject(this.value_); | |
1012 | |
1013 this.report_([ | |
1014 diff.added || {}, | |
1015 diff.removed || {}, | |
1016 diff.changed || {}, | |
1017 function(property) { | |
1018 return oldValues[property]; | |
1019 } | |
1020 ]); | |
1021 | |
1022 return true; | |
1023 }, | |
1024 | |
1025 disconnect_: function() { | |
1026 if (hasObserve) { | |
1027 this.directObserver_.close(); | |
1028 this.directObserver_ = undefined; | |
1029 } else { | |
1030 this.oldObject_ = undefined; | |
1031 } | |
1032 }, | |
1033 | |
1034 deliver: function() { | |
1035 if (this.state_ != OPENED) | |
1036 return; | |
1037 | |
1038 if (hasObserve) | |
1039 this.directObserver_.deliver(false); | |
1040 else | |
1041 dirtyCheck(this); | |
1042 }, | |
1043 | |
1044 discardChanges: function() { | |
1045 if (this.directObserver_) | |
1046 this.directObserver_.deliver(true); | |
1047 else | |
1048 this.oldObject_ = this.copyObject(this.value_); | |
1049 | |
1050 return this.value_; | |
1051 } | |
1052 }); | |
1053 | |
1054 function ArrayObserver(array) { | |
1055 if (!Array.isArray(array)) | |
1056 throw Error('Provided object is not an Array'); | |
1057 ObjectObserver.call(this, array); | |
1058 } | |
1059 | |
1060 ArrayObserver.prototype = createObject({ | |
1061 | |
1062 __proto__: ObjectObserver.prototype, | |
1063 | |
1064 arrayObserve: true, | |
1065 | |
1066 copyObject: function(arr) { | |
1067 return arr.slice(); | |
1068 }, | |
1069 | |
1070 check_: function(changeRecords) { | |
1071 var splices; | |
1072 if (hasObserve) { | |
1073 if (!changeRecords) | |
1074 return false; | |
1075 splices = projectArraySplices(this.value_, changeRecords); | |
1076 } else { | |
1077 splices = calcSplices(this.value_, 0, this.value_.length, | |
1078 this.oldObject_, 0, this.oldObject_.length); | |
1079 } | |
1080 | |
1081 if (!splices || !splices.length) | |
1082 return false; | |
1083 | |
1084 if (!hasObserve) | |
1085 this.oldObject_ = this.copyObject(this.value_); | |
1086 | |
1087 this.report_([splices]); | |
1088 return true; | |
1089 } | |
1090 }); | |
1091 | |
1092 ArrayObserver.applySplices = function(previous, current, splices) { | |
1093 splices.forEach(function(splice) { | |
1094 var spliceArgs = [splice.index, splice.removed.length]; | |
1095 var addIndex = splice.index; | |
1096 while (addIndex < splice.index + splice.addedCount) { | |
1097 spliceArgs.push(current[addIndex]); | |
1098 addIndex++; | |
1099 } | |
1100 | |
1101 Array.prototype.splice.apply(previous, spliceArgs); | |
1102 }); | |
1103 }; | |
1104 | |
1105 function PathObserver(object, path) { | |
1106 Observer.call(this); | |
1107 | |
1108 this.object_ = object; | |
1109 this.path_ = getPath(path); | |
1110 this.directObserver_ = undefined; | |
1111 } | |
1112 | |
1113 PathObserver.prototype = createObject({ | |
1114 __proto__: Observer.prototype, | |
1115 | |
1116 get path() { | |
1117 return this.path_; | |
1118 }, | |
1119 | |
1120 connect_: function() { | |
1121 if (hasObserve) | |
1122 this.directObserver_ = getObservedSet(this, this.object_); | |
1123 | |
1124 this.check_(undefined, true); | |
1125 }, | |
1126 | |
1127 disconnect_: function() { | |
1128 this.value_ = undefined; | |
1129 | |
1130 if (this.directObserver_) { | |
1131 this.directObserver_.close(this); | |
1132 this.directObserver_ = undefined; | |
1133 } | |
1134 }, | |
1135 | |
1136 iterateObjects_: function(observe) { | |
1137 this.path_.iterateObjects(this.object_, observe); | |
1138 }, | |
1139 | |
1140 check_: function(changeRecords, skipChanges) { | |
1141 var oldValue = this.value_; | |
1142 this.value_ = this.path_.getValueFrom(this.object_); | |
1143 if (skipChanges || areSameValue(this.value_, oldValue)) | |
1144 return false; | |
1145 | |
1146 this.report_([this.value_, oldValue, this]); | |
1147 return true; | |
1148 }, | |
1149 | |
1150 setValue: function(newValue) { | |
1151 if (this.path_) | |
1152 this.path_.setValueFrom(this.object_, newValue); | |
1153 } | |
1154 }); | |
1155 | |
1156 function CompoundObserver(reportChangesOnOpen) { | |
1157 Observer.call(this); | |
1158 | |
1159 this.reportChangesOnOpen_ = reportChangesOnOpen; | |
1160 this.value_ = []; | |
1161 this.directObserver_ = undefined; | |
1162 this.observed_ = []; | |
1163 } | |
1164 | |
1165 var observerSentinel = {}; | |
1166 | |
1167 CompoundObserver.prototype = createObject({ | |
1168 __proto__: Observer.prototype, | |
1169 | |
1170 connect_: function() { | |
1171 if (hasObserve) { | |
1172 var object; | |
1173 var needsDirectObserver = false; | |
1174 for (var i = 0; i < this.observed_.length; i += 2) { | |
1175 object = this.observed_[i] | |
1176 if (object !== observerSentinel) { | |
1177 needsDirectObserver = true; | |
1178 break; | |
1179 } | |
1180 } | |
1181 | |
1182 if (needsDirectObserver) | |
1183 this.directObserver_ = getObservedSet(this, object); | |
1184 } | |
1185 | |
1186 this.check_(undefined, !this.reportChangesOnOpen_); | |
1187 }, | |
1188 | |
1189 disconnect_: function() { | |
1190 for (var i = 0; i < this.observed_.length; i += 2) { | |
1191 if (this.observed_[i] === observerSentinel) | |
1192 this.observed_[i + 1].close(); | |
1193 } | |
1194 this.observed_.length = 0; | |
1195 this.value_.length = 0; | |
1196 | |
1197 if (this.directObserver_) { | |
1198 this.directObserver_.close(this); | |
1199 this.directObserver_ = undefined; | |
1200 } | |
1201 }, | |
1202 | |
1203 addPath: function(object, path) { | |
1204 if (this.state_ != UNOPENED && this.state_ != RESETTING) | |
1205 throw Error('Cannot add paths once started.'); | |
1206 | |
1207 var path = getPath(path); | |
1208 this.observed_.push(object, path); | |
1209 if (!this.reportChangesOnOpen_) | |
1210 return; | |
1211 var index = this.observed_.length / 2 - 1; | |
1212 this.value_[index] = path.getValueFrom(object); | |
1213 }, | |
1214 | |
1215 addObserver: function(observer) { | |
1216 if (this.state_ != UNOPENED && this.state_ != RESETTING) | |
1217 throw Error('Cannot add observers once started.'); | |
1218 | |
1219 this.observed_.push(observerSentinel, observer); | |
1220 if (!this.reportChangesOnOpen_) | |
1221 return; | |
1222 var index = this.observed_.length / 2 - 1; | |
1223 this.value_[index] = observer.open(this.deliver, this); | |
1224 }, | |
1225 | |
1226 startReset: function() { | |
1227 if (this.state_ != OPENED) | |
1228 throw Error('Can only reset while open'); | |
1229 | |
1230 this.state_ = RESETTING; | |
1231 this.disconnect_(); | |
1232 }, | |
1233 | |
1234 finishReset: function() { | |
1235 if (this.state_ != RESETTING) | |
1236 throw Error('Can only finishReset after startReset'); | |
1237 this.state_ = OPENED; | |
1238 this.connect_(); | |
1239 | |
1240 return this.value_; | |
1241 }, | |
1242 | |
1243 iterateObjects_: function(observe) { | |
1244 var object; | |
1245 for (var i = 0; i < this.observed_.length; i += 2) { | |
1246 object = this.observed_[i] | |
1247 if (object !== observerSentinel) | |
1248 this.observed_[i + 1].iterateObjects(object, observe) | |
1249 } | |
1250 }, | |
1251 | |
1252 check_: function(changeRecords, skipChanges) { | |
1253 var oldValues; | |
1254 for (var i = 0; i < this.observed_.length; i += 2) { | |
1255 var object = this.observed_[i]; | |
1256 var path = this.observed_[i+1]; | |
1257 var value; | |
1258 if (object === observerSentinel) { | |
1259 var observable = path; | |
1260 value = this.state_ === UNOPENED ? | |
1261 observable.open(this.deliver, this) : | |
1262 observable.discardChanges(); | |
1263 } else { | |
1264 value = path.getValueFrom(object); | |
1265 } | |
1266 | |
1267 if (skipChanges) { | |
1268 this.value_[i / 2] = value; | |
1269 continue; | |
1270 } | |
1271 | |
1272 if (areSameValue(value, this.value_[i / 2])) | |
1273 continue; | |
1274 | |
1275 oldValues = oldValues || []; | |
1276 oldValues[i / 2] = this.value_[i / 2]; | |
1277 this.value_[i / 2] = value; | |
1278 } | |
1279 | |
1280 if (!oldValues) | |
1281 return false; | |
1282 | |
1283 // TODO(rafaelw): Having observed_ as the third callback arg here is | |
1284 // pretty lame API. Fix. | |
1285 this.report_([this.value_, oldValues, this.observed_]); | |
1286 return true; | |
1287 } | |
1288 }); | |
1289 | |
1290 function identFn(value) { return value; } | |
1291 | |
1292 function ObserverTransform(observable, getValueFn, setValueFn, | |
1293 dontPassThroughSet) { | |
1294 this.callback_ = undefined; | |
1295 this.target_ = undefined; | |
1296 this.value_ = undefined; | |
1297 this.observable_ = observable; | |
1298 this.getValueFn_ = getValueFn || identFn; | |
1299 this.setValueFn_ = setValueFn || identFn; | |
1300 // TODO(rafaelw): This is a temporary hack. PolymerExpressions needs this | |
1301 // at the moment because of a bug in it's dependency tracking. | |
1302 this.dontPassThroughSet_ = dontPassThroughSet; | |
1303 } | |
1304 | |
1305 ObserverTransform.prototype = { | |
1306 open: function(callback, target) { | |
1307 this.callback_ = callback; | |
1308 this.target_ = target; | |
1309 this.value_ = | |
1310 this.getValueFn_(this.observable_.open(this.observedCallback_, this)); | |
1311 return this.value_; | |
1312 }, | |
1313 | |
1314 observedCallback_: function(value) { | |
1315 value = this.getValueFn_(value); | |
1316 if (areSameValue(value, this.value_)) | |
1317 return; | |
1318 var oldValue = this.value_; | |
1319 this.value_ = value; | |
1320 this.callback_.call(this.target_, this.value_, oldValue); | |
1321 }, | |
1322 | |
1323 discardChanges: function() { | |
1324 this.value_ = this.getValueFn_(this.observable_.discardChanges()); | |
1325 return this.value_; | |
1326 }, | |
1327 | |
1328 deliver: function() { | |
1329 return this.observable_.deliver(); | |
1330 }, | |
1331 | |
1332 setValue: function(value) { | |
1333 value = this.setValueFn_(value); | |
1334 if (!this.dontPassThroughSet_ && this.observable_.setValue) | |
1335 return this.observable_.setValue(value); | |
1336 }, | |
1337 | |
1338 close: function() { | |
1339 if (this.observable_) | |
1340 this.observable_.close(); | |
1341 this.callback_ = undefined; | |
1342 this.target_ = undefined; | |
1343 this.observable_ = undefined; | |
1344 this.value_ = undefined; | |
1345 this.getValueFn_ = undefined; | |
1346 this.setValueFn_ = undefined; | |
1347 } | |
1348 } | |
1349 | |
1350 var expectedRecordTypes = { | |
1351 add: true, | |
1352 update: true, | |
1353 delete: true | |
1354 }; | |
1355 | |
1356 function diffObjectFromChangeRecords(object, changeRecords, oldValues) { | |
1357 var added = {}; | |
1358 var removed = {}; | |
1359 | |
1360 for (var i = 0; i < changeRecords.length; i++) { | |
1361 var record = changeRecords[i]; | |
1362 if (!expectedRecordTypes[record.type]) { | |
1363 console.error('Unknown changeRecord type: ' + record.type); | |
1364 console.error(record); | |
1365 continue; | |
1366 } | |
1367 | |
1368 if (!(record.name in oldValues)) | |
1369 oldValues[record.name] = record.oldValue; | |
1370 | |
1371 if (record.type == 'update') | |
1372 continue; | |
1373 | |
1374 if (record.type == 'add') { | |
1375 if (record.name in removed) | |
1376 delete removed[record.name]; | |
1377 else | |
1378 added[record.name] = true; | |
1379 | |
1380 continue; | |
1381 } | |
1382 | |
1383 // type = 'delete' | |
1384 if (record.name in added) { | |
1385 delete added[record.name]; | |
1386 delete oldValues[record.name]; | |
1387 } else { | |
1388 removed[record.name] = true; | |
1389 } | |
1390 } | |
1391 | |
1392 for (var prop in added) | |
1393 added[prop] = object[prop]; | |
1394 | |
1395 for (var prop in removed) | |
1396 removed[prop] = undefined; | |
1397 | |
1398 var changed = {}; | |
1399 for (var prop in oldValues) { | |
1400 if (prop in added || prop in removed) | |
1401 continue; | |
1402 | |
1403 var newValue = object[prop]; | |
1404 if (oldValues[prop] !== newValue) | |
1405 changed[prop] = newValue; | |
1406 } | |
1407 | |
1408 return { | |
1409 added: added, | |
1410 removed: removed, | |
1411 changed: changed | |
1412 }; | |
1413 } | |
1414 | |
1415 function newSplice(index, removed, addedCount) { | |
1416 return { | |
1417 index: index, | |
1418 removed: removed, | |
1419 addedCount: addedCount | |
1420 }; | |
1421 } | |
1422 | |
1423 var EDIT_LEAVE = 0; | |
1424 var EDIT_UPDATE = 1; | |
1425 var EDIT_ADD = 2; | |
1426 var EDIT_DELETE = 3; | |
1427 | |
1428 function ArraySplice() {} | |
1429 | |
1430 ArraySplice.prototype = { | |
1431 | |
1432 // Note: This function is *based* on the computation of the Levenshtein | |
1433 // "edit" distance. The one change is that "updates" are treated as two | |
1434 // edits - not one. With Array splices, an update is really a delete | |
1435 // followed by an add. By retaining this, we optimize for "keeping" the | |
1436 // maximum array items in the original array. For example: | |
1437 // | |
1438 // 'xxxx123' -> '123yyyy' | |
1439 // | |
1440 // With 1-edit updates, the shortest path would be just to update all seven | |
1441 // characters. With 2-edit updates, we delete 4, leave 3, and add 4. This | |
1442 // leaves the substring '123' intact. | |
1443 calcEditDistances: function(current, currentStart, currentEnd, | |
1444 old, oldStart, oldEnd) { | |
1445 // "Deletion" columns | |
1446 var rowCount = oldEnd - oldStart + 1; | |
1447 var columnCount = currentEnd - currentStart + 1; | |
1448 var distances = new Array(rowCount); | |
1449 | |
1450 // "Addition" rows. Initialize null column. | |
1451 for (var i = 0; i < rowCount; i++) { | |
1452 distances[i] = new Array(columnCount); | |
1453 distances[i][0] = i; | |
1454 } | |
1455 | |
1456 // Initialize null row | |
1457 for (var j = 0; j < columnCount; j++) | |
1458 distances[0][j] = j; | |
1459 | |
1460 for (var i = 1; i < rowCount; i++) { | |
1461 for (var j = 1; j < columnCount; j++) { | |
1462 if (this.equals(current[currentStart + j - 1], old[oldStart + i - 1])) | |
1463 distances[i][j] = distances[i - 1][j - 1]; | |
1464 else { | |
1465 var north = distances[i - 1][j] + 1; | |
1466 var west = distances[i][j - 1] + 1; | |
1467 distances[i][j] = north < west ? north : west; | |
1468 } | |
1469 } | |
1470 } | |
1471 | |
1472 return distances; | |
1473 }, | |
1474 | |
1475 // This starts at the final weight, and walks "backward" by finding | |
1476 // the minimum previous weight recursively until the origin of the weight | |
1477 // matrix. | |
1478 spliceOperationsFromEditDistances: function(distances) { | |
1479 var i = distances.length - 1; | |
1480 var j = distances[0].length - 1; | |
1481 var current = distances[i][j]; | |
1482 var edits = []; | |
1483 while (i > 0 || j > 0) { | |
1484 if (i == 0) { | |
1485 edits.push(EDIT_ADD); | |
1486 j--; | |
1487 continue; | |
1488 } | |
1489 if (j == 0) { | |
1490 edits.push(EDIT_DELETE); | |
1491 i--; | |
1492 continue; | |
1493 } | |
1494 var northWest = distances[i - 1][j - 1]; | |
1495 var west = distances[i - 1][j]; | |
1496 var north = distances[i][j - 1]; | |
1497 | |
1498 var min; | |
1499 if (west < north) | |
1500 min = west < northWest ? west : northWest; | |
1501 else | |
1502 min = north < northWest ? north : northWest; | |
1503 | |
1504 if (min == northWest) { | |
1505 if (northWest == current) { | |
1506 edits.push(EDIT_LEAVE); | |
1507 } else { | |
1508 edits.push(EDIT_UPDATE); | |
1509 current = northWest; | |
1510 } | |
1511 i--; | |
1512 j--; | |
1513 } else if (min == west) { | |
1514 edits.push(EDIT_DELETE); | |
1515 i--; | |
1516 current = west; | |
1517 } else { | |
1518 edits.push(EDIT_ADD); | |
1519 j--; | |
1520 current = north; | |
1521 } | |
1522 } | |
1523 | |
1524 edits.reverse(); | |
1525 return edits; | |
1526 }, | |
1527 | |
1528 /** | |
1529 * Splice Projection functions: | |
1530 * | |
1531 * A splice map is a representation of how a previous array of items | |
1532 * was transformed into a new array of items. Conceptually it is a list of | |
1533 * tuples of | |
1534 * | |
1535 * <index, removed, addedCount> | |
1536 * | |
1537 * which are kept in ascending index order of. The tuple represents that at | |
1538 * the |index|, |removed| sequence of items were removed, and counting forwa
rd | |
1539 * from |index|, |addedCount| items were added. | |
1540 */ | |
1541 | |
1542 /** | |
1543 * Lacking individual splice mutation information, the minimal set of | |
1544 * splices can be synthesized given the previous state and final state of an | |
1545 * array. The basic approach is to calculate the edit distance matrix and | |
1546 * choose the shortest path through it. | |
1547 * | |
1548 * Complexity: O(l * p) | |
1549 * l: The length of the current array | |
1550 * p: The length of the old array | |
1551 */ | |
1552 calcSplices: function(current, currentStart, currentEnd, | |
1553 old, oldStart, oldEnd) { | |
1554 var prefixCount = 0; | |
1555 var suffixCount = 0; | |
1556 | |
1557 var minLength = Math.min(currentEnd - currentStart, oldEnd - oldStart); | |
1558 if (currentStart == 0 && oldStart == 0) | |
1559 prefixCount = this.sharedPrefix(current, old, minLength); | |
1560 | |
1561 if (currentEnd == current.length && oldEnd == old.length) | |
1562 suffixCount = this.sharedSuffix(current, old, minLength - prefixCount); | |
1563 | |
1564 currentStart += prefixCount; | |
1565 oldStart += prefixCount; | |
1566 currentEnd -= suffixCount; | |
1567 oldEnd -= suffixCount; | |
1568 | |
1569 if (currentEnd - currentStart == 0 && oldEnd - oldStart == 0) | |
1570 return []; | |
1571 | |
1572 if (currentStart == currentEnd) { | |
1573 var splice = newSplice(currentStart, [], 0); | |
1574 while (oldStart < oldEnd) | |
1575 splice.removed.push(old[oldStart++]); | |
1576 | |
1577 return [ splice ]; | |
1578 } else if (oldStart == oldEnd) | |
1579 return [ newSplice(currentStart, [], currentEnd - currentStart) ]; | |
1580 | |
1581 var ops = this.spliceOperationsFromEditDistances( | |
1582 this.calcEditDistances(current, currentStart, currentEnd, | |
1583 old, oldStart, oldEnd)); | |
1584 | |
1585 var splice = undefined; | |
1586 var splices = []; | |
1587 var index = currentStart; | |
1588 var oldIndex = oldStart; | |
1589 for (var i = 0; i < ops.length; i++) { | |
1590 switch(ops[i]) { | |
1591 case EDIT_LEAVE: | |
1592 if (splice) { | |
1593 splices.push(splice); | |
1594 splice = undefined; | |
1595 } | |
1596 | |
1597 index++; | |
1598 oldIndex++; | |
1599 break; | |
1600 case EDIT_UPDATE: | |
1601 if (!splice) | |
1602 splice = newSplice(index, [], 0); | |
1603 | |
1604 splice.addedCount++; | |
1605 index++; | |
1606 | |
1607 splice.removed.push(old[oldIndex]); | |
1608 oldIndex++; | |
1609 break; | |
1610 case EDIT_ADD: | |
1611 if (!splice) | |
1612 splice = newSplice(index, [], 0); | |
1613 | |
1614 splice.addedCount++; | |
1615 index++; | |
1616 break; | |
1617 case EDIT_DELETE: | |
1618 if (!splice) | |
1619 splice = newSplice(index, [], 0); | |
1620 | |
1621 splice.removed.push(old[oldIndex]); | |
1622 oldIndex++; | |
1623 break; | |
1624 } | |
1625 } | |
1626 | |
1627 if (splice) { | |
1628 splices.push(splice); | |
1629 } | |
1630 return splices; | |
1631 }, | |
1632 | |
1633 sharedPrefix: function(current, old, searchLength) { | |
1634 for (var i = 0; i < searchLength; i++) | |
1635 if (!this.equals(current[i], old[i])) | |
1636 return i; | |
1637 return searchLength; | |
1638 }, | |
1639 | |
1640 sharedSuffix: function(current, old, searchLength) { | |
1641 var index1 = current.length; | |
1642 var index2 = old.length; | |
1643 var count = 0; | |
1644 while (count < searchLength && this.equals(current[--index1], old[--index2
])) | |
1645 count++; | |
1646 | |
1647 return count; | |
1648 }, | |
1649 | |
1650 calculateSplices: function(current, previous) { | |
1651 return this.calcSplices(current, 0, current.length, previous, 0, | |
1652 previous.length); | |
1653 }, | |
1654 | |
1655 equals: function(currentValue, previousValue) { | |
1656 return currentValue === previousValue; | |
1657 } | |
1658 }; | |
1659 | |
1660 var arraySplice = new ArraySplice(); | |
1661 | |
1662 function calcSplices(current, currentStart, currentEnd, | |
1663 old, oldStart, oldEnd) { | |
1664 return arraySplice.calcSplices(current, currentStart, currentEnd, | |
1665 old, oldStart, oldEnd); | |
1666 } | |
1667 | |
1668 function intersect(start1, end1, start2, end2) { | |
1669 // Disjoint | |
1670 if (end1 < start2 || end2 < start1) | |
1671 return -1; | |
1672 | |
1673 // Adjacent | |
1674 if (end1 == start2 || end2 == start1) | |
1675 return 0; | |
1676 | |
1677 // Non-zero intersect, span1 first | |
1678 if (start1 < start2) { | |
1679 if (end1 < end2) | |
1680 return end1 - start2; // Overlap | |
1681 else | |
1682 return end2 - start2; // Contained | |
1683 } else { | |
1684 // Non-zero intersect, span2 first | |
1685 if (end2 < end1) | |
1686 return end2 - start1; // Overlap | |
1687 else | |
1688 return end1 - start1; // Contained | |
1689 } | |
1690 } | |
1691 | |
1692 function mergeSplice(splices, index, removed, addedCount) { | |
1693 | |
1694 var splice = newSplice(index, removed, addedCount); | |
1695 | |
1696 var inserted = false; | |
1697 var insertionOffset = 0; | |
1698 | |
1699 for (var i = 0; i < splices.length; i++) { | |
1700 var current = splices[i]; | |
1701 current.index += insertionOffset; | |
1702 | |
1703 if (inserted) | |
1704 continue; | |
1705 | |
1706 var intersectCount = intersect(splice.index, | |
1707 splice.index + splice.removed.length, | |
1708 current.index, | |
1709 current.index + current.addedCount); | |
1710 | |
1711 if (intersectCount >= 0) { | |
1712 // Merge the two splices | |
1713 | |
1714 splices.splice(i, 1); | |
1715 i--; | |
1716 | |
1717 insertionOffset -= current.addedCount - current.removed.length; | |
1718 | |
1719 splice.addedCount += current.addedCount - intersectCount; | |
1720 var deleteCount = splice.removed.length + | |
1721 current.removed.length - intersectCount; | |
1722 | |
1723 if (!splice.addedCount && !deleteCount) { | |
1724 // merged splice is a noop. discard. | |
1725 inserted = true; | |
1726 } else { | |
1727 var removed = current.removed; | |
1728 | |
1729 if (splice.index < current.index) { | |
1730 // some prefix of splice.removed is prepended to current.removed. | |
1731 var prepend = splice.removed.slice(0, current.index - splice.index); | |
1732 Array.prototype.push.apply(prepend, removed); | |
1733 removed = prepend; | |
1734 } | |
1735 | |
1736 if (splice.index + splice.removed.length > current.index + current.add
edCount) { | |
1737 // some suffix of splice.removed is appended to current.removed. | |
1738 var append = splice.removed.slice(current.index + current.addedCount
- splice.index); | |
1739 Array.prototype.push.apply(removed, append); | |
1740 } | |
1741 | |
1742 splice.removed = removed; | |
1743 if (current.index < splice.index) { | |
1744 splice.index = current.index; | |
1745 } | |
1746 } | |
1747 } else if (splice.index < current.index) { | |
1748 // Insert splice here. | |
1749 | |
1750 inserted = true; | |
1751 | |
1752 splices.splice(i, 0, splice); | |
1753 i++; | |
1754 | |
1755 var offset = splice.addedCount - splice.removed.length | |
1756 current.index += offset; | |
1757 insertionOffset += offset; | |
1758 } | |
1759 } | |
1760 | |
1761 if (!inserted) | |
1762 splices.push(splice); | |
1763 } | |
1764 | |
1765 function createInitialSplices(array, changeRecords) { | |
1766 var splices = []; | |
1767 | |
1768 for (var i = 0; i < changeRecords.length; i++) { | |
1769 var record = changeRecords[i]; | |
1770 switch(record.type) { | |
1771 case 'splice': | |
1772 mergeSplice(splices, record.index, record.removed.slice(), record.adde
dCount); | |
1773 break; | |
1774 case 'add': | |
1775 case 'update': | |
1776 case 'delete': | |
1777 if (!isIndex(record.name)) | |
1778 continue; | |
1779 var index = toNumber(record.name); | |
1780 if (index < 0) | |
1781 continue; | |
1782 mergeSplice(splices, index, [record.oldValue], 1); | |
1783 break; | |
1784 default: | |
1785 console.error('Unexpected record type: ' + JSON.stringify(record)); | |
1786 break; | |
1787 } | |
1788 } | |
1789 | |
1790 return splices; | |
1791 } | |
1792 | |
1793 function projectArraySplices(array, changeRecords) { | |
1794 var splices = []; | |
1795 | |
1796 createInitialSplices(array, changeRecords).forEach(function(splice) { | |
1797 if (splice.addedCount == 1 && splice.removed.length == 1) { | |
1798 if (splice.removed[0] !== array[splice.index]) | |
1799 splices.push(splice); | |
1800 | |
1801 return | |
1802 }; | |
1803 | |
1804 splices = splices.concat(calcSplices(array, splice.index, splice.index + s
plice.addedCount, | |
1805 splice.removed, 0, splice.removed.len
gth)); | |
1806 }); | |
1807 | |
1808 return splices; | |
1809 } | |
1810 | |
1811 global.Observer = Observer; | |
1812 global.Observer.runEOM_ = runEOM; | |
1813 global.Observer.observerSentinel_ = observerSentinel; // for testing. | |
1814 global.Observer.hasObjectObserve = hasObserve; | |
1815 global.ArrayObserver = ArrayObserver; | |
1816 global.ArrayObserver.calculateSplices = function(current, previous) { | |
1817 return arraySplice.calculateSplices(current, previous); | |
1818 }; | |
1819 | |
1820 global.ArraySplice = ArraySplice; | |
1821 global.ObjectObserver = ObjectObserver; | |
1822 global.PathObserver = PathObserver; | |
1823 global.CompoundObserver = CompoundObserver; | |
1824 global.Path = Path; | |
1825 global.ObserverTransform = ObserverTransform; | |
1826 })(typeof global !== 'undefined' && global && typeof module !== 'undefined' && m
odule ? global : this || window); | |
1827 | |
1828 // Copyright 2012 The Polymer Authors. All rights reserved. | |
1829 // Use of this source code is goverened by a BSD-style | |
1830 // license that can be found in the LICENSE file. | |
1831 | |
1832 window.ShadowDOMPolyfill = {}; | |
1833 | |
1834 (function(scope) { | |
1835 'use strict'; | |
1836 | |
1837 var constructorTable = new WeakMap(); | |
1838 var nativePrototypeTable = new WeakMap(); | |
1839 var wrappers = Object.create(null); | |
1840 | |
1841 function detectEval() { | |
1842 // Don't test for eval if we're running in a Chrome App environment. | |
1843 // We check for APIs set that only exist in a Chrome App context. | |
1844 if (typeof chrome !== 'undefined' && chrome.app && chrome.app.runtime) { | |
1845 return false; | |
1846 } | |
1847 | |
1848 // Firefox OS Apps do not allow eval. This feature detection is very hacky | |
1849 // but even if some other platform adds support for this function this code | |
1850 // will continue to work. | |
1851 if (navigator.getDeviceStorage) { | |
1852 return false; | |
1853 } | |
1854 | |
1855 try { | |
1856 var f = new Function('return true;'); | |
1857 return f(); | |
1858 } catch (ex) { | |
1859 return false; | |
1860 } | |
1861 } | |
1862 | |
1863 var hasEval = detectEval(); | |
1864 | |
1865 function assert(b) { | |
1866 if (!b) | |
1867 throw new Error('Assertion failed'); | |
1868 }; | |
1869 | |
1870 var defineProperty = Object.defineProperty; | |
1871 var getOwnPropertyNames = Object.getOwnPropertyNames; | |
1872 var getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor; | |
1873 | |
1874 function mixin(to, from) { | |
1875 var names = getOwnPropertyNames(from); | |
1876 for (var i = 0; i < names.length; i++) { | |
1877 var name = names[i]; | |
1878 defineProperty(to, name, getOwnPropertyDescriptor(from, name)); | |
1879 } | |
1880 return to; | |
1881 }; | |
1882 | |
1883 function mixinStatics(to, from) { | |
1884 var names = getOwnPropertyNames(from); | |
1885 for (var i = 0; i < names.length; i++) { | |
1886 var name = names[i]; | |
1887 switch (name) { | |
1888 case 'arguments': | |
1889 case 'caller': | |
1890 case 'length': | |
1891 case 'name': | |
1892 case 'prototype': | |
1893 case 'toString': | |
1894 continue; | |
1895 } | |
1896 defineProperty(to, name, getOwnPropertyDescriptor(from, name)); | |
1897 } | |
1898 return to; | |
1899 }; | |
1900 | |
1901 function oneOf(object, propertyNames) { | |
1902 for (var i = 0; i < propertyNames.length; i++) { | |
1903 if (propertyNames[i] in object) | |
1904 return propertyNames[i]; | |
1905 } | |
1906 } | |
1907 | |
1908 var nonEnumerableDataDescriptor = { | |
1909 value: undefined, | |
1910 configurable: true, | |
1911 enumerable: false, | |
1912 writable: true | |
1913 }; | |
1914 | |
1915 function defineNonEnumerableDataProperty(object, name, value) { | |
1916 nonEnumerableDataDescriptor.value = value; | |
1917 defineProperty(object, name, nonEnumerableDataDescriptor); | |
1918 } | |
1919 | |
1920 // Mozilla's old DOM bindings are bretty busted: | |
1921 // https://bugzilla.mozilla.org/show_bug.cgi?id=855844 | |
1922 // Make sure they are create before we start modifying things. | |
1923 getOwnPropertyNames(window); | |
1924 | |
1925 function getWrapperConstructor(node) { | |
1926 var nativePrototype = node.__proto__ || Object.getPrototypeOf(node); | |
1927 var wrapperConstructor = constructorTable.get(nativePrototype); | |
1928 if (wrapperConstructor) | |
1929 return wrapperConstructor; | |
1930 | |
1931 var parentWrapperConstructor = getWrapperConstructor(nativePrototype); | |
1932 | |
1933 var GeneratedWrapper = createWrapperConstructor(parentWrapperConstructor); | |
1934 registerInternal(nativePrototype, GeneratedWrapper, node); | |
1935 | |
1936 return GeneratedWrapper; | |
1937 } | |
1938 | |
1939 function addForwardingProperties(nativePrototype, wrapperPrototype) { | |
1940 installProperty(nativePrototype, wrapperPrototype, true); | |
1941 } | |
1942 | |
1943 function registerInstanceProperties(wrapperPrototype, instanceObject) { | |
1944 installProperty(instanceObject, wrapperPrototype, false); | |
1945 } | |
1946 | |
1947 var isFirefox = /Firefox/.test(navigator.userAgent); | |
1948 | |
1949 // This is used as a fallback when getting the descriptor fails in | |
1950 // installProperty. | |
1951 var dummyDescriptor = { | |
1952 get: function() {}, | |
1953 set: function(v) {}, | |
1954 configurable: true, | |
1955 enumerable: true | |
1956 }; | |
1957 | |
1958 function isEventHandlerName(name) { | |
1959 return /^on[a-z]+$/.test(name); | |
1960 } | |
1961 | |
1962 function isIdentifierName(name) { | |
1963 return /^\w[a-zA-Z_0-9]*$/.test(name); | |
1964 } | |
1965 | |
1966 // The name of the implementation property is intentionally hard to | |
1967 // remember. Unfortunately, browsers are slower doing obj[expr] than | |
1968 // obj.foo so we resort to repeat this ugly name. This ugly name is never | |
1969 // used outside of this file though. | |
1970 | |
1971 function getGetter(name) { | |
1972 return hasEval && isIdentifierName(name) ? | |
1973 new Function('return this.__impl4cf1e782hg__.' + name) : | |
1974 function() { return this.__impl4cf1e782hg__[name]; }; | |
1975 } | |
1976 | |
1977 function getSetter(name) { | |
1978 return hasEval && isIdentifierName(name) ? | |
1979 new Function('v', 'this.__impl4cf1e782hg__.' + name + ' = v') : | |
1980 function(v) { this.__impl4cf1e782hg__[name] = v; }; | |
1981 } | |
1982 | |
1983 function getMethod(name) { | |
1984 return hasEval && isIdentifierName(name) ? | |
1985 new Function('return this.__impl4cf1e782hg__.' + name + | |
1986 '.apply(this.__impl4cf1e782hg__, arguments)') : | |
1987 function() { | |
1988 return this.__impl4cf1e782hg__[name].apply( | |
1989 this.__impl4cf1e782hg__, arguments); | |
1990 }; | |
1991 } | |
1992 | |
1993 function getDescriptor(source, name) { | |
1994 try { | |
1995 return Object.getOwnPropertyDescriptor(source, name); | |
1996 } catch (ex) { | |
1997 // JSC and V8 both use data properties instead of accessors which can | |
1998 // cause getting the property desciptor to throw an exception. | |
1999 // https://bugs.webkit.org/show_bug.cgi?id=49739 | |
2000 return dummyDescriptor; | |
2001 } | |
2002 } | |
2003 | |
2004 // Safari 8 exposes WebIDL attributes as an invalid accessor property. Its | |
2005 // descriptor has {get: undefined, set: undefined}. We therefore ignore the | |
2006 // shape of the descriptor and make all properties read-write. | |
2007 // https://bugs.webkit.org/show_bug.cgi?id=49739 | |
2008 var isBrokenSafari = function() { | |
2009 var descr = Object.getOwnPropertyDescriptor(Node.prototype, 'nodeType'); | |
2010 return !!descr && 'set' in descr; | |
2011 }(); | |
2012 | |
2013 function installProperty(source, target, allowMethod, opt_blacklist) { | |
2014 var names = getOwnPropertyNames(source); | |
2015 for (var i = 0; i < names.length; i++) { | |
2016 var name = names[i]; | |
2017 if (name === 'polymerBlackList_') | |
2018 continue; | |
2019 | |
2020 if (name in target) | |
2021 continue; | |
2022 | |
2023 if (source.polymerBlackList_ && source.polymerBlackList_[name]) | |
2024 continue; | |
2025 | |
2026 if (isFirefox) { | |
2027 // Tickle Firefox's old bindings. | |
2028 source.__lookupGetter__(name); | |
2029 } | |
2030 var descriptor = getDescriptor(source, name); | |
2031 var getter, setter; | |
2032 if (allowMethod && typeof descriptor.value === 'function') { | |
2033 target[name] = getMethod(name); | |
2034 continue; | |
2035 } | |
2036 | |
2037 var isEvent = isEventHandlerName(name); | |
2038 if (isEvent) | |
2039 getter = scope.getEventHandlerGetter(name); | |
2040 else | |
2041 getter = getGetter(name); | |
2042 | |
2043 if (descriptor.writable || descriptor.set || isBrokenSafari) { | |
2044 if (isEvent) | |
2045 setter = scope.getEventHandlerSetter(name); | |
2046 else | |
2047 setter = getSetter(name); | |
2048 } | |
2049 | |
2050 defineProperty(target, name, { | |
2051 get: getter, | |
2052 set: setter, | |
2053 configurable: descriptor.configurable, | |
2054 enumerable: descriptor.enumerable | |
2055 }); | |
2056 } | |
2057 } | |
2058 | |
2059 /** | |
2060 * @param {Function} nativeConstructor | |
2061 * @param {Function} wrapperConstructor | |
2062 * @param {Object=} opt_instance If present, this is used to extract | |
2063 * properties from an instance object. | |
2064 */ | |
2065 function register(nativeConstructor, wrapperConstructor, opt_instance) { | |
2066 var nativePrototype = nativeConstructor.prototype; | |
2067 registerInternal(nativePrototype, wrapperConstructor, opt_instance); | |
2068 mixinStatics(wrapperConstructor, nativeConstructor); | |
2069 } | |
2070 | |
2071 function registerInternal(nativePrototype, wrapperConstructor, opt_instance) { | |
2072 var wrapperPrototype = wrapperConstructor.prototype; | |
2073 assert(constructorTable.get(nativePrototype) === undefined); | |
2074 | |
2075 constructorTable.set(nativePrototype, wrapperConstructor); | |
2076 nativePrototypeTable.set(wrapperPrototype, nativePrototype); | |
2077 | |
2078 addForwardingProperties(nativePrototype, wrapperPrototype); | |
2079 if (opt_instance) | |
2080 registerInstanceProperties(wrapperPrototype, opt_instance); | |
2081 | |
2082 defineNonEnumerableDataProperty( | |
2083 wrapperPrototype, 'constructor', wrapperConstructor); | |
2084 // Set it again. Some VMs optimizes objects that are used as prototypes. | |
2085 wrapperConstructor.prototype = wrapperPrototype; | |
2086 } | |
2087 | |
2088 function isWrapperFor(wrapperConstructor, nativeConstructor) { | |
2089 return constructorTable.get(nativeConstructor.prototype) === | |
2090 wrapperConstructor; | |
2091 } | |
2092 | |
2093 /** | |
2094 * Creates a generic wrapper constructor based on |object| and its | |
2095 * constructor. | |
2096 * @param {Node} object | |
2097 * @return {Function} The generated constructor. | |
2098 */ | |
2099 function registerObject(object) { | |
2100 var nativePrototype = Object.getPrototypeOf(object); | |
2101 | |
2102 var superWrapperConstructor = getWrapperConstructor(nativePrototype); | |
2103 var GeneratedWrapper = createWrapperConstructor(superWrapperConstructor); | |
2104 registerInternal(nativePrototype, GeneratedWrapper, object); | |
2105 | |
2106 return GeneratedWrapper; | |
2107 } | |
2108 | |
2109 function createWrapperConstructor(superWrapperConstructor) { | |
2110 function GeneratedWrapper(node) { | |
2111 superWrapperConstructor.call(this, node); | |
2112 } | |
2113 var p = Object.create(superWrapperConstructor.prototype); | |
2114 p.constructor = GeneratedWrapper; | |
2115 GeneratedWrapper.prototype = p; | |
2116 | |
2117 return GeneratedWrapper; | |
2118 } | |
2119 | |
2120 function isWrapper(object) { | |
2121 return object && object.__impl4cf1e782hg__; | |
2122 } | |
2123 | |
2124 function isNative(object) { | |
2125 return !isWrapper(object); | |
2126 } | |
2127 | |
2128 /** | |
2129 * Wraps a node in a WrapperNode. If there already exists a wrapper for the | |
2130 * |node| that wrapper is returned instead. | |
2131 * @param {Node} node | |
2132 * @return {WrapperNode} | |
2133 */ | |
2134 function wrap(impl) { | |
2135 if (impl === null) | |
2136 return null; | |
2137 | |
2138 assert(isNative(impl)); | |
2139 return impl.__wrapper8e3dd93a60__ || | |
2140 (impl.__wrapper8e3dd93a60__ = new (getWrapperConstructor(impl))(impl)); | |
2141 } | |
2142 | |
2143 /** | |
2144 * Unwraps a wrapper and returns the node it is wrapping. | |
2145 * @param {WrapperNode} wrapper | |
2146 * @return {Node} | |
2147 */ | |
2148 function unwrap(wrapper) { | |
2149 if (wrapper === null) | |
2150 return null; | |
2151 assert(isWrapper(wrapper)); | |
2152 return wrapper.__impl4cf1e782hg__; | |
2153 } | |
2154 | |
2155 function unsafeUnwrap(wrapper) { | |
2156 return wrapper.__impl4cf1e782hg__; | |
2157 } | |
2158 | |
2159 function setWrapper(impl, wrapper) { | |
2160 wrapper.__impl4cf1e782hg__ = impl; | |
2161 impl.__wrapper8e3dd93a60__ = wrapper; | |
2162 } | |
2163 | |
2164 /** | |
2165 * Unwraps object if it is a wrapper. | |
2166 * @param {Object} object | |
2167 * @return {Object} The native implementation object. | |
2168 */ | |
2169 function unwrapIfNeeded(object) { | |
2170 return object && isWrapper(object) ? unwrap(object) : object; | |
2171 } | |
2172 | |
2173 /** | |
2174 * Wraps object if it is not a wrapper. | |
2175 * @param {Object} object | |
2176 * @return {Object} The wrapper for object. | |
2177 */ | |
2178 function wrapIfNeeded(object) { | |
2179 return object && !isWrapper(object) ? wrap(object) : object; | |
2180 } | |
2181 | |
2182 /** | |
2183 * Overrides the current wrapper (if any) for node. | |
2184 * @param {Node} node | |
2185 * @param {WrapperNode=} wrapper If left out the wrapper will be created as | |
2186 * needed next time someone wraps the node. | |
2187 */ | |
2188 function rewrap(node, wrapper) { | |
2189 if (wrapper === null) | |
2190 return; | |
2191 assert(isNative(node)); | |
2192 assert(wrapper === undefined || isWrapper(wrapper)); | |
2193 node.__wrapper8e3dd93a60__ = wrapper; | |
2194 } | |
2195 | |
2196 var getterDescriptor = { | |
2197 get: undefined, | |
2198 configurable: true, | |
2199 enumerable: true | |
2200 }; | |
2201 | |
2202 function defineGetter(constructor, name, getter) { | |
2203 getterDescriptor.get = getter; | |
2204 defineProperty(constructor.prototype, name, getterDescriptor); | |
2205 } | |
2206 | |
2207 function defineWrapGetter(constructor, name) { | |
2208 defineGetter(constructor, name, function() { | |
2209 return wrap(this.__impl4cf1e782hg__[name]); | |
2210 }); | |
2211 } | |
2212 | |
2213 /** | |
2214 * Forwards existing methods on the native object to the wrapper methods. | |
2215 * This does not wrap any of the arguments or the return value since the | |
2216 * wrapper implementation already takes care of that. | |
2217 * @param {Array.<Function>} constructors | |
2218 * @parem {Array.<string>} names | |
2219 */ | |
2220 function forwardMethodsToWrapper(constructors, names) { | |
2221 constructors.forEach(function(constructor) { | |
2222 names.forEach(function(name) { | |
2223 constructor.prototype[name] = function() { | |
2224 var w = wrapIfNeeded(this); | |
2225 return w[name].apply(w, arguments); | |
2226 }; | |
2227 }); | |
2228 }); | |
2229 } | |
2230 | |
2231 scope.assert = assert; | |
2232 scope.constructorTable = constructorTable; | |
2233 scope.defineGetter = defineGetter; | |
2234 scope.defineWrapGetter = defineWrapGetter; | |
2235 scope.forwardMethodsToWrapper = forwardMethodsToWrapper; | |
2236 scope.isWrapper = isWrapper; | |
2237 scope.isWrapperFor = isWrapperFor; | |
2238 scope.mixin = mixin; | |
2239 scope.nativePrototypeTable = nativePrototypeTable; | |
2240 scope.oneOf = oneOf; | |
2241 scope.registerObject = registerObject; | |
2242 scope.registerWrapper = register; | |
2243 scope.rewrap = rewrap; | |
2244 scope.setWrapper = setWrapper; | |
2245 scope.unsafeUnwrap = unsafeUnwrap; | |
2246 scope.unwrap = unwrap; | |
2247 scope.unwrapIfNeeded = unwrapIfNeeded; | |
2248 scope.wrap = wrap; | |
2249 scope.wrapIfNeeded = wrapIfNeeded; | |
2250 scope.wrappers = wrappers; | |
2251 | |
2252 })(window.ShadowDOMPolyfill); | |
2253 | |
2254 /* | |
2255 * Copyright 2013 The Polymer Authors. All rights reserved. | |
2256 * Use of this source code is goverened by a BSD-style | |
2257 * license that can be found in the LICENSE file. | |
2258 */ | |
2259 | |
2260 (function(context) { | |
2261 'use strict'; | |
2262 | |
2263 var OriginalMutationObserver = window.MutationObserver; | |
2264 var callbacks = []; | |
2265 var pending = false; | |
2266 var timerFunc; | |
2267 | |
2268 function handle() { | |
2269 pending = false; | |
2270 var copies = callbacks.slice(0); | |
2271 callbacks = []; | |
2272 for (var i = 0; i < copies.length; i++) { | |
2273 (0, copies[i])(); | |
2274 } | |
2275 } | |
2276 | |
2277 if (OriginalMutationObserver) { | |
2278 var counter = 1; | |
2279 var observer = new OriginalMutationObserver(handle); | |
2280 var textNode = document.createTextNode(counter); | |
2281 observer.observe(textNode, {characterData: true}); | |
2282 | |
2283 timerFunc = function() { | |
2284 counter = (counter + 1) % 2; | |
2285 textNode.data = counter; | |
2286 }; | |
2287 | |
2288 } else { | |
2289 timerFunc = window.setImmediate || window.setTimeout; | |
2290 } | |
2291 | |
2292 function setEndOfMicrotask(func) { | |
2293 callbacks.push(func); | |
2294 if (pending) | |
2295 return; | |
2296 pending = true; | |
2297 timerFunc(handle, 0); | |
2298 } | |
2299 | |
2300 context.setEndOfMicrotask = setEndOfMicrotask; | |
2301 | |
2302 })(window.ShadowDOMPolyfill); | |
2303 | |
2304 /* | |
2305 * Copyright 2013 The Polymer Authors. All rights reserved. | |
2306 * Use of this source code is goverened by a BSD-style | |
2307 * license that can be found in the LICENSE file. | |
2308 */ | |
2309 | |
2310 (function(scope) { | |
2311 'use strict'; | |
2312 | |
2313 var setEndOfMicrotask = scope.setEndOfMicrotask | |
2314 var wrapIfNeeded = scope.wrapIfNeeded | |
2315 var wrappers = scope.wrappers; | |
2316 | |
2317 var registrationsTable = new WeakMap(); | |
2318 var globalMutationObservers = []; | |
2319 var isScheduled = false; | |
2320 | |
2321 function scheduleCallback(observer) { | |
2322 if (observer.scheduled_) | |
2323 return; | |
2324 | |
2325 observer.scheduled_ = true; | |
2326 globalMutationObservers.push(observer); | |
2327 | |
2328 if (isScheduled) | |
2329 return; | |
2330 setEndOfMicrotask(notifyObservers); | |
2331 isScheduled = true; | |
2332 } | |
2333 | |
2334 // http://dom.spec.whatwg.org/#mutation-observers | |
2335 function notifyObservers() { | |
2336 isScheduled = false; | |
2337 | |
2338 while (globalMutationObservers.length) { | |
2339 var notifyList = globalMutationObservers; | |
2340 globalMutationObservers = []; | |
2341 | |
2342 // Deliver changes in birth order of the MutationObservers. | |
2343 notifyList.sort(function(x, y) { return x.uid_ - y.uid_; }); | |
2344 | |
2345 for (var i = 0; i < notifyList.length; i++) { | |
2346 var mo = notifyList[i]; | |
2347 mo.scheduled_ = false; | |
2348 var queue = mo.takeRecords(); | |
2349 removeTransientObserversFor(mo); | |
2350 if (queue.length) { | |
2351 mo.callback_(queue, mo); | |
2352 } | |
2353 } | |
2354 } | |
2355 } | |
2356 | |
2357 | |
2358 /** | |
2359 * @param {string} type | |
2360 * @param {Node} target | |
2361 * @constructor | |
2362 */ | |
2363 function MutationRecord(type, target) { | |
2364 this.type = type; | |
2365 this.target = target; | |
2366 this.addedNodes = new wrappers.NodeList(); | |
2367 this.removedNodes = new wrappers.NodeList(); | |
2368 this.previousSibling = null; | |
2369 this.nextSibling = null; | |
2370 this.attributeName = null; | |
2371 this.attributeNamespace = null; | |
2372 this.oldValue = null; | |
2373 } | |
2374 | |
2375 /** | |
2376 * Registers transient observers to ancestor and its ancesors for the node | |
2377 * which was removed. | |
2378 * @param {!Node} ancestor | |
2379 * @param {!Node} node | |
2380 */ | |
2381 function registerTransientObservers(ancestor, node) { | |
2382 for (; ancestor; ancestor = ancestor.parentNode) { | |
2383 var registrations = registrationsTable.get(ancestor); | |
2384 if (!registrations) | |
2385 continue; | |
2386 for (var i = 0; i < registrations.length; i++) { | |
2387 var registration = registrations[i]; | |
2388 if (registration.options.subtree) | |
2389 registration.addTransientObserver(node); | |
2390 } | |
2391 } | |
2392 } | |
2393 | |
2394 function removeTransientObserversFor(observer) { | |
2395 for (var i = 0; i < observer.nodes_.length; i++) { | |
2396 var node = observer.nodes_[i]; | |
2397 var registrations = registrationsTable.get(node); | |
2398 if (!registrations) | |
2399 return; | |
2400 for (var j = 0; j < registrations.length; j++) { | |
2401 var registration = registrations[j]; | |
2402 if (registration.observer === observer) | |
2403 registration.removeTransientObservers(); | |
2404 } | |
2405 } | |
2406 } | |
2407 | |
2408 // http://dom.spec.whatwg.org/#queue-a-mutation-record | |
2409 function enqueueMutation(target, type, data) { | |
2410 // 1. | |
2411 var interestedObservers = Object.create(null); | |
2412 var associatedStrings = Object.create(null); | |
2413 | |
2414 // 2. | |
2415 for (var node = target; node; node = node.parentNode) { | |
2416 // 3. | |
2417 var registrations = registrationsTable.get(node); | |
2418 if (!registrations) | |
2419 continue; | |
2420 for (var j = 0; j < registrations.length; j++) { | |
2421 var registration = registrations[j]; | |
2422 var options = registration.options; | |
2423 // 1. | |
2424 if (node !== target && !options.subtree) | |
2425 continue; | |
2426 | |
2427 // 2. | |
2428 if (type === 'attributes' && !options.attributes) | |
2429 continue; | |
2430 | |
2431 // 3. If type is "attributes", options's attributeFilter is present, and | |
2432 // either options's attributeFilter does not contain name or namespace | |
2433 // is non-null, continue. | |
2434 if (type === 'attributes' && options.attributeFilter && | |
2435 (data.namespace !== null || | |
2436 options.attributeFilter.indexOf(data.name) === -1)) { | |
2437 continue; | |
2438 } | |
2439 | |
2440 // 4. | |
2441 if (type === 'characterData' && !options.characterData) | |
2442 continue; | |
2443 | |
2444 // 5. | |
2445 if (type === 'childList' && !options.childList) | |
2446 continue; | |
2447 | |
2448 // 6. | |
2449 var observer = registration.observer; | |
2450 interestedObservers[observer.uid_] = observer; | |
2451 | |
2452 // 7. If either type is "attributes" and options's attributeOldValue is | |
2453 // true, or type is "characterData" and options's characterDataOldValue | |
2454 // is true, set the paired string of registered observer's observer in | |
2455 // interested observers to oldValue. | |
2456 if (type === 'attributes' && options.attributeOldValue || | |
2457 type === 'characterData' && options.characterDataOldValue) { | |
2458 associatedStrings[observer.uid_] = data.oldValue; | |
2459 } | |
2460 } | |
2461 } | |
2462 | |
2463 // 4. | |
2464 for (var uid in interestedObservers) { | |
2465 var observer = interestedObservers[uid]; | |
2466 var record = new MutationRecord(type, target); | |
2467 | |
2468 // 2. | |
2469 if ('name' in data && 'namespace' in data) { | |
2470 record.attributeName = data.name; | |
2471 record.attributeNamespace = data.namespace; | |
2472 } | |
2473 | |
2474 // 3. | |
2475 if (data.addedNodes) | |
2476 record.addedNodes = data.addedNodes; | |
2477 | |
2478 // 4. | |
2479 if (data.removedNodes) | |
2480 record.removedNodes = data.removedNodes; | |
2481 | |
2482 // 5. | |
2483 if (data.previousSibling) | |
2484 record.previousSibling = data.previousSibling; | |
2485 | |
2486 // 6. | |
2487 if (data.nextSibling) | |
2488 record.nextSibling = data.nextSibling; | |
2489 | |
2490 // 7. | |
2491 if (associatedStrings[uid] !== undefined) | |
2492 record.oldValue = associatedStrings[uid]; | |
2493 | |
2494 // 8. | |
2495 scheduleCallback(observer); | |
2496 observer.records_.push(record); | |
2497 } | |
2498 } | |
2499 | |
2500 var slice = Array.prototype.slice; | |
2501 | |
2502 /** | |
2503 * @param {!Object} options | |
2504 * @constructor | |
2505 */ | |
2506 function MutationObserverOptions(options) { | |
2507 this.childList = !!options.childList; | |
2508 this.subtree = !!options.subtree; | |
2509 | |
2510 // 1. If either options' attributeOldValue or attributeFilter is present | |
2511 // and options' attributes is omitted, set options' attributes to true. | |
2512 if (!('attributes' in options) && | |
2513 ('attributeOldValue' in options || 'attributeFilter' in options)) { | |
2514 this.attributes = true; | |
2515 } else { | |
2516 this.attributes = !!options.attributes; | |
2517 } | |
2518 | |
2519 // 2. If options' characterDataOldValue is present and options' | |
2520 // characterData is omitted, set options' characterData to true. | |
2521 if ('characterDataOldValue' in options && !('characterData' in options)) | |
2522 this.characterData = true; | |
2523 else | |
2524 this.characterData = !!options.characterData; | |
2525 | |
2526 // 3. & 4. | |
2527 if (!this.attributes && | |
2528 (options.attributeOldValue || 'attributeFilter' in options) || | |
2529 // 5. | |
2530 !this.characterData && options.characterDataOldValue) { | |
2531 throw new TypeError(); | |
2532 } | |
2533 | |
2534 this.characterData = !!options.characterData; | |
2535 this.attributeOldValue = !!options.attributeOldValue; | |
2536 this.characterDataOldValue = !!options.characterDataOldValue; | |
2537 if ('attributeFilter' in options) { | |
2538 if (options.attributeFilter == null || | |
2539 typeof options.attributeFilter !== 'object') { | |
2540 throw new TypeError(); | |
2541 } | |
2542 this.attributeFilter = slice.call(options.attributeFilter); | |
2543 } else { | |
2544 this.attributeFilter = null; | |
2545 } | |
2546 } | |
2547 | |
2548 var uidCounter = 0; | |
2549 | |
2550 /** | |
2551 * The class that maps to the DOM MutationObserver interface. | |
2552 * @param {Function} callback. | |
2553 * @constructor | |
2554 */ | |
2555 function MutationObserver(callback) { | |
2556 this.callback_ = callback; | |
2557 this.nodes_ = []; | |
2558 this.records_ = []; | |
2559 this.uid_ = ++uidCounter; | |
2560 this.scheduled_ = false; | |
2561 } | |
2562 | |
2563 MutationObserver.prototype = { | |
2564 constructor: MutationObserver, | |
2565 | |
2566 // http://dom.spec.whatwg.org/#dom-mutationobserver-observe | |
2567 observe: function(target, options) { | |
2568 target = wrapIfNeeded(target); | |
2569 | |
2570 var newOptions = new MutationObserverOptions(options); | |
2571 | |
2572 // 6. | |
2573 var registration; | |
2574 var registrations = registrationsTable.get(target); | |
2575 if (!registrations) | |
2576 registrationsTable.set(target, registrations = []); | |
2577 | |
2578 for (var i = 0; i < registrations.length; i++) { | |
2579 if (registrations[i].observer === this) { | |
2580 registration = registrations[i]; | |
2581 // 6.1. | |
2582 registration.removeTransientObservers(); | |
2583 // 6.2. | |
2584 registration.options = newOptions; | |
2585 } | |
2586 } | |
2587 | |
2588 // 7. | |
2589 if (!registration) { | |
2590 registration = new Registration(this, target, newOptions); | |
2591 registrations.push(registration); | |
2592 this.nodes_.push(target); | |
2593 } | |
2594 }, | |
2595 | |
2596 // http://dom.spec.whatwg.org/#dom-mutationobserver-disconnect | |
2597 disconnect: function() { | |
2598 this.nodes_.forEach(function(node) { | |
2599 var registrations = registrationsTable.get(node); | |
2600 for (var i = 0; i < registrations.length; i++) { | |
2601 var registration = registrations[i]; | |
2602 if (registration.observer === this) { | |
2603 registrations.splice(i, 1); | |
2604 // Each node can only have one registered observer associated with | |
2605 // this observer. | |
2606 break; | |
2607 } | |
2608 } | |
2609 }, this); | |
2610 this.records_ = []; | |
2611 }, | |
2612 | |
2613 takeRecords: function() { | |
2614 var copyOfRecords = this.records_; | |
2615 this.records_ = []; | |
2616 return copyOfRecords; | |
2617 } | |
2618 }; | |
2619 | |
2620 /** | |
2621 * Class used to represent a registered observer. | |
2622 * @param {MutationObserver} observer | |
2623 * @param {Node} target | |
2624 * @param {MutationObserverOptions} options | |
2625 * @constructor | |
2626 */ | |
2627 function Registration(observer, target, options) { | |
2628 this.observer = observer; | |
2629 this.target = target; | |
2630 this.options = options; | |
2631 this.transientObservedNodes = []; | |
2632 } | |
2633 | |
2634 Registration.prototype = { | |
2635 /** | |
2636 * Adds a transient observer on node. The transient observer gets removed | |
2637 * next time we deliver the change records. | |
2638 * @param {Node} node | |
2639 */ | |
2640 addTransientObserver: function(node) { | |
2641 // Don't add transient observers on the target itself. We already have all | |
2642 // the required listeners set up on the target. | |
2643 if (node === this.target) | |
2644 return; | |
2645 | |
2646 // Make sure we remove transient observers at the end of microtask, even | |
2647 // if we didn't get any change records. | |
2648 scheduleCallback(this.observer); | |
2649 | |
2650 this.transientObservedNodes.push(node); | |
2651 var registrations = registrationsTable.get(node); | |
2652 if (!registrations) | |
2653 registrationsTable.set(node, registrations = []); | |
2654 | |
2655 // We know that registrations does not contain this because we already | |
2656 // checked if node === this.target. | |
2657 registrations.push(this); | |
2658 }, | |
2659 | |
2660 removeTransientObservers: function() { | |
2661 var transientObservedNodes = this.transientObservedNodes; | |
2662 this.transientObservedNodes = []; | |
2663 | |
2664 for (var i = 0; i < transientObservedNodes.length; i++) { | |
2665 var node = transientObservedNodes[i]; | |
2666 var registrations = registrationsTable.get(node); | |
2667 for (var j = 0; j < registrations.length; j++) { | |
2668 if (registrations[j] === this) { | |
2669 registrations.splice(j, 1); | |
2670 // Each node can only have one registered observer associated with | |
2671 // this observer. | |
2672 break; | |
2673 } | |
2674 } | |
2675 } | |
2676 } | |
2677 }; | |
2678 | |
2679 scope.enqueueMutation = enqueueMutation; | |
2680 scope.registerTransientObservers = registerTransientObservers; | |
2681 scope.wrappers.MutationObserver = MutationObserver; | |
2682 scope.wrappers.MutationRecord = MutationRecord; | |
2683 | |
2684 })(window.ShadowDOMPolyfill); | |
2685 | |
2686 /** | |
2687 * Copyright 2014 The Polymer Authors. All rights reserved. | |
2688 * Use of this source code is goverened by a BSD-style | |
2689 * license that can be found in the LICENSE file. | |
2690 */ | |
2691 | |
2692 (function(scope) { | |
2693 'use strict'; | |
2694 | |
2695 /** | |
2696 * A tree scope represents the root of a tree. All nodes in a tree point to | |
2697 * the same TreeScope object. The tree scope of a node get set the first time | |
2698 * it is accessed or when a node is added or remove to a tree. | |
2699 * | |
2700 * The root is a Node that has no parent. | |
2701 * | |
2702 * The parent is another TreeScope. For ShadowRoots, it is the TreeScope of | |
2703 * the host of the ShadowRoot. | |
2704 * | |
2705 * @param {!Node} root | |
2706 * @param {TreeScope} parent | |
2707 * @constructor | |
2708 */ | |
2709 function TreeScope(root, parent) { | |
2710 /** @type {!Node} */ | |
2711 this.root = root; | |
2712 | |
2713 /** @type {TreeScope} */ | |
2714 this.parent = parent; | |
2715 } | |
2716 | |
2717 TreeScope.prototype = { | |
2718 get renderer() { | |
2719 if (this.root instanceof scope.wrappers.ShadowRoot) { | |
2720 return scope.getRendererForHost(this.root.host); | |
2721 } | |
2722 return null; | |
2723 }, | |
2724 | |
2725 contains: function(treeScope) { | |
2726 for (; treeScope; treeScope = treeScope.parent) { | |
2727 if (treeScope === this) | |
2728 return true; | |
2729 } | |
2730 return false; | |
2731 } | |
2732 }; | |
2733 | |
2734 function setTreeScope(node, treeScope) { | |
2735 if (node.treeScope_ !== treeScope) { | |
2736 node.treeScope_ = treeScope; | |
2737 for (var sr = node.shadowRoot; sr; sr = sr.olderShadowRoot) { | |
2738 sr.treeScope_.parent = treeScope; | |
2739 } | |
2740 for (var child = node.firstChild; child; child = child.nextSibling) { | |
2741 setTreeScope(child, treeScope); | |
2742 } | |
2743 } | |
2744 } | |
2745 | |
2746 function getTreeScope(node) { | |
2747 if (node instanceof scope.wrappers.Window) { | |
2748 debugger; | |
2749 } | |
2750 | |
2751 if (node.treeScope_) | |
2752 return node.treeScope_; | |
2753 var parent = node.parentNode; | |
2754 var treeScope; | |
2755 if (parent) | |
2756 treeScope = getTreeScope(parent); | |
2757 else | |
2758 treeScope = new TreeScope(node, null); | |
2759 return node.treeScope_ = treeScope; | |
2760 } | |
2761 | |
2762 scope.TreeScope = TreeScope; | |
2763 scope.getTreeScope = getTreeScope; | |
2764 scope.setTreeScope = setTreeScope; | |
2765 | |
2766 })(window.ShadowDOMPolyfill); | |
2767 | |
2768 // Copyright 2013 The Polymer Authors. All rights reserved. | |
2769 // Use of this source code is goverened by a BSD-style | |
2770 // license that can be found in the LICENSE file. | |
2771 | |
2772 (function(scope) { | |
2773 'use strict'; | |
2774 | |
2775 var forwardMethodsToWrapper = scope.forwardMethodsToWrapper; | |
2776 var getTreeScope = scope.getTreeScope; | |
2777 var mixin = scope.mixin; | |
2778 var registerWrapper = scope.registerWrapper; | |
2779 var setWrapper = scope.setWrapper; | |
2780 var unsafeUnwrap = scope.unsafeUnwrap; | |
2781 var unwrap = scope.unwrap; | |
2782 var wrap = scope.wrap; | |
2783 var wrappers = scope.wrappers; | |
2784 | |
2785 var wrappedFuns = new WeakMap(); | |
2786 var listenersTable = new WeakMap(); | |
2787 var handledEventsTable = new WeakMap(); | |
2788 var currentlyDispatchingEvents = new WeakMap(); | |
2789 var targetTable = new WeakMap(); | |
2790 var currentTargetTable = new WeakMap(); | |
2791 var relatedTargetTable = new WeakMap(); | |
2792 var eventPhaseTable = new WeakMap(); | |
2793 var stopPropagationTable = new WeakMap(); | |
2794 var stopImmediatePropagationTable = new WeakMap(); | |
2795 var eventHandlersTable = new WeakMap(); | |
2796 var eventPathTable = new WeakMap(); | |
2797 | |
2798 function isShadowRoot(node) { | |
2799 return node instanceof wrappers.ShadowRoot; | |
2800 } | |
2801 | |
2802 function rootOfNode(node) { | |
2803 return getTreeScope(node).root; | |
2804 } | |
2805 | |
2806 // http://w3c.github.io/webcomponents/spec/shadow/#event-paths | |
2807 function getEventPath(node, event) { | |
2808 var path = []; | |
2809 var current = node; | |
2810 path.push(current); | |
2811 while (current) { | |
2812 // 4.1. | |
2813 var destinationInsertionPoints = getDestinationInsertionPoints(current); | |
2814 if (destinationInsertionPoints && destinationInsertionPoints.length > 0) { | |
2815 // 4.1.1 | |
2816 for (var i = 0; i < destinationInsertionPoints.length; i++) { | |
2817 var insertionPoint = destinationInsertionPoints[i]; | |
2818 // 4.1.1.1 | |
2819 if (isShadowInsertionPoint(insertionPoint)) { | |
2820 var shadowRoot = rootOfNode(insertionPoint); | |
2821 // 4.1.1.1.2 | |
2822 var olderShadowRoot = shadowRoot.olderShadowRoot; | |
2823 if (olderShadowRoot) | |
2824 path.push(olderShadowRoot); | |
2825 } | |
2826 | |
2827 // 4.1.1.2 | |
2828 path.push(insertionPoint); | |
2829 } | |
2830 | |
2831 // 4.1.2 | |
2832 current = destinationInsertionPoints[ | |
2833 destinationInsertionPoints.length - 1]; | |
2834 | |
2835 // 4.2 | |
2836 } else { | |
2837 if (isShadowRoot(current)) { | |
2838 if (inSameTree(node, current) && eventMustBeStopped(event)) { | |
2839 // Stop this algorithm | |
2840 break; | |
2841 } | |
2842 current = current.host; | |
2843 path.push(current); | |
2844 | |
2845 // 4.2.2 | |
2846 } else { | |
2847 current = current.parentNode; | |
2848 if (current) | |
2849 path.push(current); | |
2850 } | |
2851 } | |
2852 } | |
2853 | |
2854 return path; | |
2855 } | |
2856 | |
2857 // http://w3c.github.io/webcomponents/spec/shadow/#dfn-events-always-stopped | |
2858 function eventMustBeStopped(event) { | |
2859 if (!event) | |
2860 return false; | |
2861 | |
2862 switch (event.type) { | |
2863 case 'abort': | |
2864 case 'error': | |
2865 case 'select': | |
2866 case 'change': | |
2867 case 'load': | |
2868 case 'reset': | |
2869 case 'resize': | |
2870 case 'scroll': | |
2871 case 'selectstart': | |
2872 return true; | |
2873 } | |
2874 return false; | |
2875 } | |
2876 | |
2877 // http://w3c.github.io/webcomponents/spec/shadow/#dfn-shadow-insertion-point | |
2878 function isShadowInsertionPoint(node) { | |
2879 return node instanceof HTMLShadowElement; | |
2880 // and make sure that there are no shadow precing this? | |
2881 // and that there is no content ancestor? | |
2882 } | |
2883 | |
2884 function getDestinationInsertionPoints(node) { | |
2885 return scope.getDestinationInsertionPoints(node); | |
2886 } | |
2887 | |
2888 // http://w3c.github.io/webcomponents/spec/shadow/#event-retargeting | |
2889 function eventRetargetting(path, currentTarget) { | |
2890 if (path.length === 0) | |
2891 return currentTarget; | |
2892 | |
2893 // The currentTarget might be the window object. Use its document for the | |
2894 // purpose of finding the retargetted node. | |
2895 if (currentTarget instanceof wrappers.Window) | |
2896 currentTarget = currentTarget.document; | |
2897 | |
2898 var currentTargetTree = getTreeScope(currentTarget); | |
2899 var originalTarget = path[0]; | |
2900 var originalTargetTree = getTreeScope(originalTarget); | |
2901 var relativeTargetTree = | |
2902 lowestCommonInclusiveAncestor(currentTargetTree, originalTargetTree); | |
2903 | |
2904 for (var i = 0; i < path.length; i++) { | |
2905 var node = path[i]; | |
2906 if (getTreeScope(node) === relativeTargetTree) | |
2907 return node; | |
2908 } | |
2909 | |
2910 return path[path.length - 1]; | |
2911 } | |
2912 | |
2913 function getTreeScopeAncestors(treeScope) { | |
2914 var ancestors = []; | |
2915 for (;treeScope; treeScope = treeScope.parent) { | |
2916 ancestors.push(treeScope); | |
2917 } | |
2918 return ancestors; | |
2919 } | |
2920 | |
2921 function lowestCommonInclusiveAncestor(tsA, tsB) { | |
2922 var ancestorsA = getTreeScopeAncestors(tsA); | |
2923 var ancestorsB = getTreeScopeAncestors(tsB); | |
2924 | |
2925 var result = null; | |
2926 while (ancestorsA.length > 0 && ancestorsB.length > 0) { | |
2927 var a = ancestorsA.pop(); | |
2928 var b = ancestorsB.pop(); | |
2929 if (a === b) | |
2930 result = a; | |
2931 else | |
2932 break; | |
2933 } | |
2934 return result; | |
2935 } | |
2936 | |
2937 function getTreeScopeRoot(ts) { | |
2938 if (!ts.parent) | |
2939 return ts; | |
2940 return getTreeScopeRoot(ts.parent); | |
2941 } | |
2942 | |
2943 function relatedTargetResolution(event, currentTarget, relatedTarget) { | |
2944 // In case the current target is a window use its document for the purpose | |
2945 // of retargetting the related target. | |
2946 if (currentTarget instanceof wrappers.Window) | |
2947 currentTarget = currentTarget.document; | |
2948 | |
2949 var currentTargetTree = getTreeScope(currentTarget); | |
2950 var relatedTargetTree = getTreeScope(relatedTarget); | |
2951 | |
2952 var relatedTargetEventPath = getEventPath(relatedTarget, event); | |
2953 | |
2954 var lowestCommonAncestorTree; | |
2955 | |
2956 // 4 | |
2957 var lowestCommonAncestorTree = | |
2958 lowestCommonInclusiveAncestor(currentTargetTree, relatedTargetTree); | |
2959 | |
2960 // 5 | |
2961 if (!lowestCommonAncestorTree) | |
2962 lowestCommonAncestorTree = relatedTargetTree.root; | |
2963 | |
2964 // 6 | |
2965 for (var commonAncestorTree = lowestCommonAncestorTree; | |
2966 commonAncestorTree; | |
2967 commonAncestorTree = commonAncestorTree.parent) { | |
2968 // 6.1 | |
2969 var adjustedRelatedTarget; | |
2970 for (var i = 0; i < relatedTargetEventPath.length; i++) { | |
2971 var node = relatedTargetEventPath[i]; | |
2972 if (getTreeScope(node) === commonAncestorTree) | |
2973 return node; | |
2974 } | |
2975 } | |
2976 | |
2977 return null; | |
2978 } | |
2979 | |
2980 function inSameTree(a, b) { | |
2981 return getTreeScope(a) === getTreeScope(b); | |
2982 } | |
2983 | |
2984 var NONE = 0; | |
2985 var CAPTURING_PHASE = 1; | |
2986 var AT_TARGET = 2; | |
2987 var BUBBLING_PHASE = 3; | |
2988 | |
2989 // pendingError is used to rethrow the first error we got during an event | |
2990 // dispatch. The browser actually reports all errors but to do that we would | |
2991 // need to rethrow the error asynchronously. | |
2992 var pendingError; | |
2993 | |
2994 function dispatchOriginalEvent(originalEvent) { | |
2995 // Make sure this event is only dispatched once. | |
2996 if (handledEventsTable.get(originalEvent)) | |
2997 return; | |
2998 handledEventsTable.set(originalEvent, true); | |
2999 dispatchEvent(wrap(originalEvent), wrap(originalEvent.target)); | |
3000 if (pendingError) { | |
3001 var err = pendingError; | |
3002 pendingError = null; | |
3003 throw err; | |
3004 } | |
3005 } | |
3006 | |
3007 | |
3008 function isLoadLikeEvent(event) { | |
3009 switch (event.type) { | |
3010 // http://www.whatwg.org/specs/web-apps/current-work/multipage/webappapis.
html#events-and-the-window-object | |
3011 case 'load': | |
3012 // http://www.whatwg.org/specs/web-apps/current-work/multipage/browsers.ht
ml#unloading-documents | |
3013 case 'beforeunload': | |
3014 case 'unload': | |
3015 return true; | |
3016 } | |
3017 return false; | |
3018 } | |
3019 | |
3020 function dispatchEvent(event, originalWrapperTarget) { | |
3021 if (currentlyDispatchingEvents.get(event)) | |
3022 throw new Error('InvalidStateError'); | |
3023 | |
3024 currentlyDispatchingEvents.set(event, true); | |
3025 | |
3026 // Render to ensure that the event path is correct. | |
3027 scope.renderAllPending(); | |
3028 var eventPath; | |
3029 | |
3030 // http://www.whatwg.org/specs/web-apps/current-work/multipage/webappapis.ht
ml#events-and-the-window-object | |
3031 // All events dispatched on Nodes with a default view, except load events, | |
3032 // should propagate to the Window. | |
3033 | |
3034 // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-end.html#
the-end | |
3035 var overrideTarget; | |
3036 var win; | |
3037 | |
3038 // Should really be not cancelable too but since Firefox has a bug there | |
3039 // we skip that check. | |
3040 // https://bugzilla.mozilla.org/show_bug.cgi?id=999456 | |
3041 if (isLoadLikeEvent(event) && !event.bubbles) { | |
3042 var doc = originalWrapperTarget; | |
3043 if (doc instanceof wrappers.Document && (win = doc.defaultView)) { | |
3044 overrideTarget = doc; | |
3045 eventPath = []; | |
3046 } | |
3047 } | |
3048 | |
3049 if (!eventPath) { | |
3050 if (originalWrapperTarget instanceof wrappers.Window) { | |
3051 win = originalWrapperTarget; | |
3052 eventPath = []; | |
3053 } else { | |
3054 eventPath = getEventPath(originalWrapperTarget, event); | |
3055 | |
3056 if (!isLoadLikeEvent(event)) { | |
3057 var doc = eventPath[eventPath.length - 1]; | |
3058 if (doc instanceof wrappers.Document) | |
3059 win = doc.defaultView; | |
3060 } | |
3061 } | |
3062 } | |
3063 | |
3064 eventPathTable.set(event, eventPath); | |
3065 | |
3066 if (dispatchCapturing(event, eventPath, win, overrideTarget)) { | |
3067 if (dispatchAtTarget(event, eventPath, win, overrideTarget)) { | |
3068 dispatchBubbling(event, eventPath, win, overrideTarget); | |
3069 } | |
3070 } | |
3071 | |
3072 eventPhaseTable.set(event, NONE); | |
3073 currentTargetTable.delete(event, null); | |
3074 currentlyDispatchingEvents.delete(event); | |
3075 | |
3076 return event.defaultPrevented; | |
3077 } | |
3078 | |
3079 function dispatchCapturing(event, eventPath, win, overrideTarget) { | |
3080 var phase = CAPTURING_PHASE; | |
3081 | |
3082 if (win) { | |
3083 if (!invoke(win, event, phase, eventPath, overrideTarget)) | |
3084 return false; | |
3085 } | |
3086 | |
3087 for (var i = eventPath.length - 1; i > 0; i--) { | |
3088 if (!invoke(eventPath[i], event, phase, eventPath, overrideTarget)) | |
3089 return false; | |
3090 } | |
3091 | |
3092 return true; | |
3093 } | |
3094 | |
3095 function dispatchAtTarget(event, eventPath, win, overrideTarget) { | |
3096 var phase = AT_TARGET; | |
3097 var currentTarget = eventPath[0] || win; | |
3098 return invoke(currentTarget, event, phase, eventPath, overrideTarget); | |
3099 } | |
3100 | |
3101 function dispatchBubbling(event, eventPath, win, overrideTarget) { | |
3102 var phase = BUBBLING_PHASE; | |
3103 for (var i = 1; i < eventPath.length; i++) { | |
3104 if (!invoke(eventPath[i], event, phase, eventPath, overrideTarget)) | |
3105 return; | |
3106 } | |
3107 | |
3108 if (win && eventPath.length > 0) { | |
3109 invoke(win, event, phase, eventPath, overrideTarget); | |
3110 } | |
3111 } | |
3112 | |
3113 function invoke(currentTarget, event, phase, eventPath, overrideTarget) { | |
3114 var listeners = listenersTable.get(currentTarget); | |
3115 if (!listeners) | |
3116 return true; | |
3117 | |
3118 var target = overrideTarget || eventRetargetting(eventPath, currentTarget); | |
3119 | |
3120 if (target === currentTarget) { | |
3121 if (phase === CAPTURING_PHASE) | |
3122 return true; | |
3123 | |
3124 if (phase === BUBBLING_PHASE) | |
3125 phase = AT_TARGET; | |
3126 | |
3127 } else if (phase === BUBBLING_PHASE && !event.bubbles) { | |
3128 return true; | |
3129 } | |
3130 | |
3131 if ('relatedTarget' in event) { | |
3132 var originalEvent = unwrap(event); | |
3133 var unwrappedRelatedTarget = originalEvent.relatedTarget; | |
3134 | |
3135 // X-Tag sets relatedTarget on a CustomEvent. If they do that there is no | |
3136 // way to have relatedTarget return the adjusted target but worse is that | |
3137 // the originalEvent might not have a relatedTarget so we hit an assert | |
3138 // when we try to wrap it. | |
3139 if (unwrappedRelatedTarget) { | |
3140 // In IE we can get objects that are not EventTargets at this point. | |
3141 // Safari does not have an EventTarget interface so revert to checking | |
3142 // for addEventListener as an approximation. | |
3143 if (unwrappedRelatedTarget instanceof Object && | |
3144 unwrappedRelatedTarget.addEventListener) { | |
3145 var relatedTarget = wrap(unwrappedRelatedTarget); | |
3146 | |
3147 var adjusted = | |
3148 relatedTargetResolution(event, currentTarget, relatedTarget); | |
3149 if (adjusted === target) | |
3150 return true; | |
3151 } else { | |
3152 adjusted = null; | |
3153 } | |
3154 relatedTargetTable.set(event, adjusted); | |
3155 } | |
3156 } | |
3157 | |
3158 eventPhaseTable.set(event, phase); | |
3159 var type = event.type; | |
3160 | |
3161 var anyRemoved = false; | |
3162 targetTable.set(event, target); | |
3163 currentTargetTable.set(event, currentTarget); | |
3164 | |
3165 // Keep track of the invoke depth so that we only clean up the removed | |
3166 // listeners if we are in the outermost invoke. | |
3167 listeners.depth++; | |
3168 | |
3169 for (var i = 0, len = listeners.length; i < len; i++) { | |
3170 var listener = listeners[i]; | |
3171 if (listener.removed) { | |
3172 anyRemoved = true; | |
3173 continue; | |
3174 } | |
3175 | |
3176 if (listener.type !== type || | |
3177 !listener.capture && phase === CAPTURING_PHASE || | |
3178 listener.capture && phase === BUBBLING_PHASE) { | |
3179 continue; | |
3180 } | |
3181 | |
3182 try { | |
3183 if (typeof listener.handler === 'function') | |
3184 listener.handler.call(currentTarget, event); | |
3185 else | |
3186 listener.handler.handleEvent(event); | |
3187 | |
3188 if (stopImmediatePropagationTable.get(event)) | |
3189 return false; | |
3190 | |
3191 } catch (ex) { | |
3192 if (!pendingError) | |
3193 pendingError = ex; | |
3194 } | |
3195 } | |
3196 | |
3197 listeners.depth--; | |
3198 | |
3199 if (anyRemoved && listeners.depth === 0) { | |
3200 var copy = listeners.slice(); | |
3201 listeners.length = 0; | |
3202 for (var i = 0; i < copy.length; i++) { | |
3203 if (!copy[i].removed) | |
3204 listeners.push(copy[i]); | |
3205 } | |
3206 } | |
3207 | |
3208 return !stopPropagationTable.get(event); | |
3209 } | |
3210 | |
3211 function Listener(type, handler, capture) { | |
3212 this.type = type; | |
3213 this.handler = handler; | |
3214 this.capture = Boolean(capture); | |
3215 } | |
3216 Listener.prototype = { | |
3217 equals: function(that) { | |
3218 return this.handler === that.handler && this.type === that.type && | |
3219 this.capture === that.capture; | |
3220 }, | |
3221 get removed() { | |
3222 return this.handler === null; | |
3223 }, | |
3224 remove: function() { | |
3225 this.handler = null; | |
3226 } | |
3227 }; | |
3228 | |
3229 var OriginalEvent = window.Event; | |
3230 OriginalEvent.prototype.polymerBlackList_ = { | |
3231 returnValue: true, | |
3232 // TODO(arv): keyLocation is part of KeyboardEvent but Firefox does not | |
3233 // support constructable KeyboardEvent so we keep it here for now. | |
3234 keyLocation: true | |
3235 }; | |
3236 | |
3237 /** | |
3238 * Creates a new Event wrapper or wraps an existin native Event object. | |
3239 * @param {string|Event} type | |
3240 * @param {Object=} options | |
3241 * @constructor | |
3242 */ | |
3243 function Event(type, options) { | |
3244 if (type instanceof OriginalEvent) { | |
3245 var impl = type; | |
3246 // In browsers that do not correctly support BeforeUnloadEvent we get to | |
3247 // the generic Event wrapper but we still want to ensure we create a | |
3248 // BeforeUnloadEvent. Since BeforeUnloadEvent calls super, we need to | |
3249 // prevent reentrancty. | |
3250 if (!OriginalBeforeUnloadEvent && impl.type === 'beforeunload' && | |
3251 !(this instanceof BeforeUnloadEvent)) { | |
3252 return new BeforeUnloadEvent(impl); | |
3253 } | |
3254 setWrapper(impl, this); | |
3255 } else { | |
3256 return wrap(constructEvent(OriginalEvent, 'Event', type, options)); | |
3257 } | |
3258 } | |
3259 Event.prototype = { | |
3260 get target() { | |
3261 return targetTable.get(this); | |
3262 }, | |
3263 get currentTarget() { | |
3264 return currentTargetTable.get(this); | |
3265 }, | |
3266 get eventPhase() { | |
3267 return eventPhaseTable.get(this); | |
3268 }, | |
3269 get path() { | |
3270 var eventPath = eventPathTable.get(this); | |
3271 if (!eventPath) | |
3272 return []; | |
3273 // TODO(arv): Event path should contain window. | |
3274 return eventPath.slice(); | |
3275 }, | |
3276 stopPropagation: function() { | |
3277 stopPropagationTable.set(this, true); | |
3278 }, | |
3279 stopImmediatePropagation: function() { | |
3280 stopPropagationTable.set(this, true); | |
3281 stopImmediatePropagationTable.set(this, true); | |
3282 } | |
3283 }; | |
3284 registerWrapper(OriginalEvent, Event, document.createEvent('Event')); | |
3285 | |
3286 function unwrapOptions(options) { | |
3287 if (!options || !options.relatedTarget) | |
3288 return options; | |
3289 return Object.create(options, { | |
3290 relatedTarget: {value: unwrap(options.relatedTarget)} | |
3291 }); | |
3292 } | |
3293 | |
3294 function registerGenericEvent(name, SuperEvent, prototype) { | |
3295 var OriginalEvent = window[name]; | |
3296 var GenericEvent = function(type, options) { | |
3297 if (type instanceof OriginalEvent) | |
3298 setWrapper(type, this); | |
3299 else | |
3300 return wrap(constructEvent(OriginalEvent, name, type, options)); | |
3301 }; | |
3302 GenericEvent.prototype = Object.create(SuperEvent.prototype); | |
3303 if (prototype) | |
3304 mixin(GenericEvent.prototype, prototype); | |
3305 if (OriginalEvent) { | |
3306 // - Old versions of Safari fails on new FocusEvent (and others?). | |
3307 // - IE does not support event constructors. | |
3308 // - createEvent('FocusEvent') throws in Firefox. | |
3309 // => Try the best practice solution first and fallback to the old way | |
3310 // if needed. | |
3311 try { | |
3312 registerWrapper(OriginalEvent, GenericEvent, new OriginalEvent('temp')); | |
3313 } catch (ex) { | |
3314 registerWrapper(OriginalEvent, GenericEvent, | |
3315 document.createEvent(name)); | |
3316 } | |
3317 } | |
3318 return GenericEvent; | |
3319 } | |
3320 | |
3321 var UIEvent = registerGenericEvent('UIEvent', Event); | |
3322 var CustomEvent = registerGenericEvent('CustomEvent', Event); | |
3323 | |
3324 var relatedTargetProto = { | |
3325 get relatedTarget() { | |
3326 var relatedTarget = relatedTargetTable.get(this); | |
3327 // relatedTarget can be null. | |
3328 if (relatedTarget !== undefined) | |
3329 return relatedTarget; | |
3330 return wrap(unwrap(this).relatedTarget); | |
3331 } | |
3332 }; | |
3333 | |
3334 function getInitFunction(name, relatedTargetIndex) { | |
3335 return function() { | |
3336 arguments[relatedTargetIndex] = unwrap(arguments[relatedTargetIndex]); | |
3337 var impl = unwrap(this); | |
3338 impl[name].apply(impl, arguments); | |
3339 }; | |
3340 } | |
3341 | |
3342 var mouseEventProto = mixin({ | |
3343 initMouseEvent: getInitFunction('initMouseEvent', 14) | |
3344 }, relatedTargetProto); | |
3345 | |
3346 var focusEventProto = mixin({ | |
3347 initFocusEvent: getInitFunction('initFocusEvent', 5) | |
3348 }, relatedTargetProto); | |
3349 | |
3350 var MouseEvent = registerGenericEvent('MouseEvent', UIEvent, mouseEventProto); | |
3351 var FocusEvent = registerGenericEvent('FocusEvent', UIEvent, focusEventProto); | |
3352 | |
3353 // In case the browser does not support event constructors we polyfill that | |
3354 // by calling `createEvent('Foo')` and `initFooEvent` where the arguments to | |
3355 // `initFooEvent` are derived from the registered default event init dict. | |
3356 var defaultInitDicts = Object.create(null); | |
3357 | |
3358 var supportsEventConstructors = (function() { | |
3359 try { | |
3360 new window.FocusEvent('focus'); | |
3361 } catch (ex) { | |
3362 return false; | |
3363 } | |
3364 return true; | |
3365 })(); | |
3366 | |
3367 /** | |
3368 * Constructs a new native event. | |
3369 */ | |
3370 function constructEvent(OriginalEvent, name, type, options) { | |
3371 if (supportsEventConstructors) | |
3372 return new OriginalEvent(type, unwrapOptions(options)); | |
3373 | |
3374 // Create the arguments from the default dictionary. | |
3375 var event = unwrap(document.createEvent(name)); | |
3376 var defaultDict = defaultInitDicts[name]; | |
3377 var args = [type]; | |
3378 Object.keys(defaultDict).forEach(function(key) { | |
3379 var v = options != null && key in options ? | |
3380 options[key] : defaultDict[key]; | |
3381 if (key === 'relatedTarget') | |
3382 v = unwrap(v); | |
3383 args.push(v); | |
3384 }); | |
3385 event['init' + name].apply(event, args); | |
3386 return event; | |
3387 } | |
3388 | |
3389 if (!supportsEventConstructors) { | |
3390 var configureEventConstructor = function(name, initDict, superName) { | |
3391 if (superName) { | |
3392 var superDict = defaultInitDicts[superName]; | |
3393 initDict = mixin(mixin({}, superDict), initDict); | |
3394 } | |
3395 | |
3396 defaultInitDicts[name] = initDict; | |
3397 }; | |
3398 | |
3399 // The order of the default event init dictionary keys is important, the | |
3400 // arguments to initFooEvent is derived from that. | |
3401 configureEventConstructor('Event', {bubbles: false, cancelable: false}); | |
3402 configureEventConstructor('CustomEvent', {detail: null}, 'Event'); | |
3403 configureEventConstructor('UIEvent', {view: null, detail: 0}, 'Event'); | |
3404 configureEventConstructor('MouseEvent', { | |
3405 screenX: 0, | |
3406 screenY: 0, | |
3407 clientX: 0, | |
3408 clientY: 0, | |
3409 ctrlKey: false, | |
3410 altKey: false, | |
3411 shiftKey: false, | |
3412 metaKey: false, | |
3413 button: 0, | |
3414 relatedTarget: null | |
3415 }, 'UIEvent'); | |
3416 configureEventConstructor('FocusEvent', {relatedTarget: null}, 'UIEvent'); | |
3417 } | |
3418 | |
3419 // Safari 7 does not yet have BeforeUnloadEvent. | |
3420 // https://bugs.webkit.org/show_bug.cgi?id=120849 | |
3421 var OriginalBeforeUnloadEvent = window.BeforeUnloadEvent; | |
3422 | |
3423 function BeforeUnloadEvent(impl) { | |
3424 Event.call(this, impl); | |
3425 } | |
3426 BeforeUnloadEvent.prototype = Object.create(Event.prototype); | |
3427 mixin(BeforeUnloadEvent.prototype, { | |
3428 get returnValue() { | |
3429 return unsafeUnwrap(this).returnValue; | |
3430 }, | |
3431 set returnValue(v) { | |
3432 unsafeUnwrap(this).returnValue = v; | |
3433 } | |
3434 }); | |
3435 | |
3436 if (OriginalBeforeUnloadEvent) | |
3437 registerWrapper(OriginalBeforeUnloadEvent, BeforeUnloadEvent); | |
3438 | |
3439 function isValidListener(fun) { | |
3440 if (typeof fun === 'function') | |
3441 return true; | |
3442 return fun && fun.handleEvent; | |
3443 } | |
3444 | |
3445 function isMutationEvent(type) { | |
3446 switch (type) { | |
3447 case 'DOMAttrModified': | |
3448 case 'DOMAttributeNameChanged': | |
3449 case 'DOMCharacterDataModified': | |
3450 case 'DOMElementNameChanged': | |
3451 case 'DOMNodeInserted': | |
3452 case 'DOMNodeInsertedIntoDocument': | |
3453 case 'DOMNodeRemoved': | |
3454 case 'DOMNodeRemovedFromDocument': | |
3455 case 'DOMSubtreeModified': | |
3456 return true; | |
3457 } | |
3458 return false; | |
3459 } | |
3460 | |
3461 var OriginalEventTarget = window.EventTarget; | |
3462 | |
3463 /** | |
3464 * This represents a wrapper for an EventTarget. | |
3465 * @param {!EventTarget} impl The original event target. | |
3466 * @constructor | |
3467 */ | |
3468 function EventTarget(impl) { | |
3469 setWrapper(impl, this); | |
3470 } | |
3471 | |
3472 // Node and Window have different internal type checks in WebKit so we cannot | |
3473 // use the same method as the original function. | |
3474 var methodNames = [ | |
3475 'addEventListener', | |
3476 'removeEventListener', | |
3477 'dispatchEvent' | |
3478 ]; | |
3479 | |
3480 [Node, Window].forEach(function(constructor) { | |
3481 var p = constructor.prototype; | |
3482 methodNames.forEach(function(name) { | |
3483 Object.defineProperty(p, name + '_', {value: p[name]}); | |
3484 }); | |
3485 }); | |
3486 | |
3487 function getTargetToListenAt(wrapper) { | |
3488 if (wrapper instanceof wrappers.ShadowRoot) | |
3489 wrapper = wrapper.host; | |
3490 return unwrap(wrapper); | |
3491 } | |
3492 | |
3493 EventTarget.prototype = { | |
3494 addEventListener: function(type, fun, capture) { | |
3495 if (!isValidListener(fun) || isMutationEvent(type)) | |
3496 return; | |
3497 | |
3498 var listener = new Listener(type, fun, capture); | |
3499 var listeners = listenersTable.get(this); | |
3500 if (!listeners) { | |
3501 listeners = []; | |
3502 listeners.depth = 0; | |
3503 listenersTable.set(this, listeners); | |
3504 } else { | |
3505 // Might have a duplicate. | |
3506 for (var i = 0; i < listeners.length; i++) { | |
3507 if (listener.equals(listeners[i])) | |
3508 return; | |
3509 } | |
3510 } | |
3511 | |
3512 listeners.push(listener); | |
3513 | |
3514 var target = getTargetToListenAt(this); | |
3515 target.addEventListener_(type, dispatchOriginalEvent, true); | |
3516 }, | |
3517 removeEventListener: function(type, fun, capture) { | |
3518 capture = Boolean(capture); | |
3519 var listeners = listenersTable.get(this); | |
3520 if (!listeners) | |
3521 return; | |
3522 var count = 0, found = false; | |
3523 for (var i = 0; i < listeners.length; i++) { | |
3524 if (listeners[i].type === type && listeners[i].capture === capture) { | |
3525 count++; | |
3526 if (listeners[i].handler === fun) { | |
3527 found = true; | |
3528 listeners[i].remove(); | |
3529 } | |
3530 } | |
3531 } | |
3532 | |
3533 if (found && count === 1) { | |
3534 var target = getTargetToListenAt(this); | |
3535 target.removeEventListener_(type, dispatchOriginalEvent, true); | |
3536 } | |
3537 }, | |
3538 dispatchEvent: function(event) { | |
3539 // We want to use the native dispatchEvent because it triggers the default | |
3540 // actions (like checking a checkbox). However, if there are no listeners | |
3541 // in the composed tree then there are no events that will trigger and | |
3542 // listeners in the non composed tree that are part of the event path are | |
3543 // not notified. | |
3544 // | |
3545 // If we find out that there are no listeners in the composed tree we add | |
3546 // a temporary listener to the target which makes us get called back even | |
3547 // in that case. | |
3548 | |
3549 var nativeEvent = unwrap(event); | |
3550 var eventType = nativeEvent.type; | |
3551 | |
3552 // Allow dispatching the same event again. This is safe because if user | |
3553 // code calls this during an existing dispatch of the same event the | |
3554 // native dispatchEvent throws (that is required by the spec). | |
3555 handledEventsTable.set(nativeEvent, false); | |
3556 | |
3557 // Force rendering since we prefer native dispatch and that works on the | |
3558 // composed tree. | |
3559 scope.renderAllPending(); | |
3560 | |
3561 var tempListener; | |
3562 if (!hasListenerInAncestors(this, eventType)) { | |
3563 tempListener = function() {}; | |
3564 this.addEventListener(eventType, tempListener, true); | |
3565 } | |
3566 | |
3567 try { | |
3568 return unwrap(this).dispatchEvent_(nativeEvent); | |
3569 } finally { | |
3570 if (tempListener) | |
3571 this.removeEventListener(eventType, tempListener, true); | |
3572 } | |
3573 } | |
3574 }; | |
3575 | |
3576 function hasListener(node, type) { | |
3577 var listeners = listenersTable.get(node); | |
3578 if (listeners) { | |
3579 for (var i = 0; i < listeners.length; i++) { | |
3580 if (!listeners[i].removed && listeners[i].type === type) | |
3581 return true; | |
3582 } | |
3583 } | |
3584 return false; | |
3585 } | |
3586 | |
3587 function hasListenerInAncestors(target, type) { | |
3588 for (var node = unwrap(target); node; node = node.parentNode) { | |
3589 if (hasListener(wrap(node), type)) | |
3590 return true; | |
3591 } | |
3592 return false; | |
3593 } | |
3594 | |
3595 if (OriginalEventTarget) | |
3596 registerWrapper(OriginalEventTarget, EventTarget); | |
3597 | |
3598 function wrapEventTargetMethods(constructors) { | |
3599 forwardMethodsToWrapper(constructors, methodNames); | |
3600 } | |
3601 | |
3602 var originalElementFromPoint = document.elementFromPoint; | |
3603 | |
3604 function elementFromPoint(self, document, x, y) { | |
3605 scope.renderAllPending(); | |
3606 | |
3607 var element = | |
3608 wrap(originalElementFromPoint.call(unsafeUnwrap(document), x, y)); | |
3609 if (!element) | |
3610 return null; | |
3611 var path = getEventPath(element, null); | |
3612 | |
3613 // scope the path to this TreeScope | |
3614 var idx = path.lastIndexOf(self); | |
3615 if (idx == -1) | |
3616 return null; | |
3617 else | |
3618 path = path.slice(0, idx); | |
3619 | |
3620 // TODO(dfreedm): pass idx to eventRetargetting to avoid array copy | |
3621 return eventRetargetting(path, self); | |
3622 } | |
3623 | |
3624 /** | |
3625 * Returns a function that is to be used as a getter for `onfoo` properties. | |
3626 * @param {string} name | |
3627 * @return {Function} | |
3628 */ | |
3629 function getEventHandlerGetter(name) { | |
3630 return function() { | |
3631 var inlineEventHandlers = eventHandlersTable.get(this); | |
3632 return inlineEventHandlers && inlineEventHandlers[name] && | |
3633 inlineEventHandlers[name].value || null; | |
3634 }; | |
3635 } | |
3636 | |
3637 /** | |
3638 * Returns a function that is to be used as a setter for `onfoo` properties. | |
3639 * @param {string} name | |
3640 * @return {Function} | |
3641 */ | |
3642 function getEventHandlerSetter(name) { | |
3643 var eventType = name.slice(2); | |
3644 return function(value) { | |
3645 var inlineEventHandlers = eventHandlersTable.get(this); | |
3646 if (!inlineEventHandlers) { | |
3647 inlineEventHandlers = Object.create(null); | |
3648 eventHandlersTable.set(this, inlineEventHandlers); | |
3649 } | |
3650 | |
3651 var old = inlineEventHandlers[name]; | |
3652 if (old) | |
3653 this.removeEventListener(eventType, old.wrapped, false); | |
3654 | |
3655 if (typeof value === 'function') { | |
3656 var wrapped = function(e) { | |
3657 var rv = value.call(this, e); | |
3658 if (rv === false) | |
3659 e.preventDefault(); | |
3660 else if (name === 'onbeforeunload' && typeof rv === 'string') | |
3661 e.returnValue = rv; | |
3662 // mouseover uses true for preventDefault but preventDefault for | |
3663 // mouseover is ignored by browsers these day. | |
3664 }; | |
3665 | |
3666 this.addEventListener(eventType, wrapped, false); | |
3667 inlineEventHandlers[name] = { | |
3668 value: value, | |
3669 wrapped: wrapped | |
3670 }; | |
3671 } | |
3672 }; | |
3673 } | |
3674 | |
3675 scope.elementFromPoint = elementFromPoint; | |
3676 scope.getEventHandlerGetter = getEventHandlerGetter; | |
3677 scope.getEventHandlerSetter = getEventHandlerSetter; | |
3678 scope.wrapEventTargetMethods = wrapEventTargetMethods; | |
3679 scope.wrappers.BeforeUnloadEvent = BeforeUnloadEvent; | |
3680 scope.wrappers.CustomEvent = CustomEvent; | |
3681 scope.wrappers.Event = Event; | |
3682 scope.wrappers.EventTarget = EventTarget; | |
3683 scope.wrappers.FocusEvent = FocusEvent; | |
3684 scope.wrappers.MouseEvent = MouseEvent; | |
3685 scope.wrappers.UIEvent = UIEvent; | |
3686 | |
3687 })(window.ShadowDOMPolyfill); | |
3688 | |
3689 /* | |
3690 * Copyright 2014 The Polymer Authors. All rights reserved. | |
3691 * Use of this source code is goverened by a BSD-style | |
3692 * license that can be found in the LICENSE file. | |
3693 */ | |
3694 | |
3695 (function(scope) { | |
3696 'use strict'; | |
3697 | |
3698 var UIEvent = scope.wrappers.UIEvent; | |
3699 var mixin = scope.mixin; | |
3700 var registerWrapper = scope.registerWrapper; | |
3701 var setWrapper = scope.setWrapper; | |
3702 var unsafeUnwrap = scope.unsafeUnwrap; | |
3703 var wrap = scope.wrap; | |
3704 | |
3705 // TouchEvent is WebKit/Blink only. | |
3706 var OriginalTouchEvent = window.TouchEvent; | |
3707 if (!OriginalTouchEvent) | |
3708 return; | |
3709 | |
3710 var nativeEvent; | |
3711 try { | |
3712 nativeEvent = document.createEvent('TouchEvent'); | |
3713 } catch (ex) { | |
3714 // In Chrome creating a TouchEvent fails if the feature is not turned on | |
3715 // which it isn't on desktop Chrome. | |
3716 return; | |
3717 } | |
3718 | |
3719 var nonEnumDescriptor = {enumerable: false}; | |
3720 | |
3721 function nonEnum(obj, prop) { | |
3722 Object.defineProperty(obj, prop, nonEnumDescriptor); | |
3723 } | |
3724 | |
3725 function Touch(impl) { | |
3726 setWrapper(impl, this); | |
3727 } | |
3728 | |
3729 Touch.prototype = { | |
3730 get target() { | |
3731 return wrap(unsafeUnwrap(this).target); | |
3732 } | |
3733 }; | |
3734 | |
3735 var descr = { | |
3736 configurable: true, | |
3737 enumerable: true, | |
3738 get: null | |
3739 }; | |
3740 | |
3741 [ | |
3742 'clientX', | |
3743 'clientY', | |
3744 'screenX', | |
3745 'screenY', | |
3746 'pageX', | |
3747 'pageY', | |
3748 'identifier', | |
3749 'webkitRadiusX', | |
3750 'webkitRadiusY', | |
3751 'webkitRotationAngle', | |
3752 'webkitForce' | |
3753 ].forEach(function(name) { | |
3754 descr.get = function() { | |
3755 return unsafeUnwrap(this)[name]; | |
3756 }; | |
3757 Object.defineProperty(Touch.prototype, name, descr); | |
3758 }); | |
3759 | |
3760 function TouchList() { | |
3761 this.length = 0; | |
3762 nonEnum(this, 'length'); | |
3763 } | |
3764 | |
3765 TouchList.prototype = { | |
3766 item: function(index) { | |
3767 return this[index]; | |
3768 } | |
3769 }; | |
3770 | |
3771 function wrapTouchList(nativeTouchList) { | |
3772 var list = new TouchList(); | |
3773 for (var i = 0; i < nativeTouchList.length; i++) { | |
3774 list[i] = new Touch(nativeTouchList[i]); | |
3775 } | |
3776 list.length = i; | |
3777 return list; | |
3778 } | |
3779 | |
3780 function TouchEvent(impl) { | |
3781 UIEvent.call(this, impl); | |
3782 } | |
3783 | |
3784 TouchEvent.prototype = Object.create(UIEvent.prototype); | |
3785 | |
3786 mixin(TouchEvent.prototype, { | |
3787 get touches() { | |
3788 return wrapTouchList(unsafeUnwrap(this).touches); | |
3789 }, | |
3790 | |
3791 get targetTouches() { | |
3792 return wrapTouchList(unsafeUnwrap(this).targetTouches); | |
3793 }, | |
3794 | |
3795 get changedTouches() { | |
3796 return wrapTouchList(unsafeUnwrap(this).changedTouches); | |
3797 }, | |
3798 | |
3799 initTouchEvent: function() { | |
3800 // The only way to use this is to reuse the TouchList from an existing | |
3801 // TouchEvent. Since this is WebKit/Blink proprietary API we will not | |
3802 // implement this until someone screams. | |
3803 throw new Error('Not implemented'); | |
3804 } | |
3805 }); | |
3806 | |
3807 registerWrapper(OriginalTouchEvent, TouchEvent, nativeEvent); | |
3808 | |
3809 scope.wrappers.Touch = Touch; | |
3810 scope.wrappers.TouchEvent = TouchEvent; | |
3811 scope.wrappers.TouchList = TouchList; | |
3812 | |
3813 })(window.ShadowDOMPolyfill); | |
3814 | |
3815 | |
3816 // Copyright 2012 The Polymer Authors. All rights reserved. | |
3817 // Use of this source code is goverened by a BSD-style | |
3818 // license that can be found in the LICENSE file. | |
3819 | |
3820 (function(scope) { | |
3821 'use strict'; | |
3822 | |
3823 var unsafeUnwrap = scope.unsafeUnwrap; | |
3824 var wrap = scope.wrap; | |
3825 | |
3826 var nonEnumDescriptor = {enumerable: false}; | |
3827 | |
3828 function nonEnum(obj, prop) { | |
3829 Object.defineProperty(obj, prop, nonEnumDescriptor); | |
3830 } | |
3831 | |
3832 function NodeList() { | |
3833 this.length = 0; | |
3834 nonEnum(this, 'length'); | |
3835 } | |
3836 NodeList.prototype = { | |
3837 item: function(index) { | |
3838 return this[index]; | |
3839 } | |
3840 }; | |
3841 nonEnum(NodeList.prototype, 'item'); | |
3842 | |
3843 function wrapNodeList(list) { | |
3844 if (list == null) | |
3845 return list; | |
3846 var wrapperList = new NodeList(); | |
3847 for (var i = 0, length = list.length; i < length; i++) { | |
3848 wrapperList[i] = wrap(list[i]); | |
3849 } | |
3850 wrapperList.length = length; | |
3851 return wrapperList; | |
3852 } | |
3853 | |
3854 function addWrapNodeListMethod(wrapperConstructor, name) { | |
3855 wrapperConstructor.prototype[name] = function() { | |
3856 return wrapNodeList( | |
3857 unsafeUnwrap(this)[name].apply(unsafeUnwrap(this), arguments)); | |
3858 }; | |
3859 } | |
3860 | |
3861 scope.wrappers.NodeList = NodeList; | |
3862 scope.addWrapNodeListMethod = addWrapNodeListMethod; | |
3863 scope.wrapNodeList = wrapNodeList; | |
3864 | |
3865 })(window.ShadowDOMPolyfill); | |
3866 | |
3867 /* | |
3868 * Copyright 2014 The Polymer Authors. All rights reserved. | |
3869 * Use of this source code is goverened by a BSD-style | |
3870 * license that can be found in the LICENSE file. | |
3871 */ | |
3872 | |
3873 (function(scope) { | |
3874 'use strict'; | |
3875 | |
3876 // TODO(arv): Implement. | |
3877 | |
3878 scope.wrapHTMLCollection = scope.wrapNodeList; | |
3879 scope.wrappers.HTMLCollection = scope.wrappers.NodeList; | |
3880 | |
3881 })(window.ShadowDOMPolyfill); | |
3882 | |
3883 /** | |
3884 * Copyright 2012 The Polymer Authors. All rights reserved. | |
3885 * Use of this source code is goverened by a BSD-style | |
3886 * license that can be found in the LICENSE file. | |
3887 */ | |
3888 | |
3889 (function(scope) { | |
3890 'use strict'; | |
3891 | |
3892 var EventTarget = scope.wrappers.EventTarget; | |
3893 var NodeList = scope.wrappers.NodeList; | |
3894 var TreeScope = scope.TreeScope; | |
3895 var assert = scope.assert; | |
3896 var defineWrapGetter = scope.defineWrapGetter; | |
3897 var enqueueMutation = scope.enqueueMutation; | |
3898 var getTreeScope = scope.getTreeScope; | |
3899 var isWrapper = scope.isWrapper; | |
3900 var mixin = scope.mixin; | |
3901 var registerTransientObservers = scope.registerTransientObservers; | |
3902 var registerWrapper = scope.registerWrapper; | |
3903 var setTreeScope = scope.setTreeScope; | |
3904 var unsafeUnwrap = scope.unsafeUnwrap; | |
3905 var unwrap = scope.unwrap; | |
3906 var unwrapIfNeeded = scope.unwrapIfNeeded; | |
3907 var wrap = scope.wrap; | |
3908 var wrapIfNeeded = scope.wrapIfNeeded; | |
3909 var wrappers = scope.wrappers; | |
3910 | |
3911 function assertIsNodeWrapper(node) { | |
3912 assert(node instanceof Node); | |
3913 } | |
3914 | |
3915 function createOneElementNodeList(node) { | |
3916 var nodes = new NodeList(); | |
3917 nodes[0] = node; | |
3918 nodes.length = 1; | |
3919 return nodes; | |
3920 } | |
3921 | |
3922 var surpressMutations = false; | |
3923 | |
3924 /** | |
3925 * Called before node is inserted into a node to enqueue its removal from its | |
3926 * old parent. | |
3927 * @param {!Node} node The node that is about to be removed. | |
3928 * @param {!Node} parent The parent node that the node is being removed from. | |
3929 * @param {!NodeList} nodes The collected nodes. | |
3930 */ | |
3931 function enqueueRemovalForInsertedNodes(node, parent, nodes) { | |
3932 enqueueMutation(parent, 'childList', { | |
3933 removedNodes: nodes, | |
3934 previousSibling: node.previousSibling, | |
3935 nextSibling: node.nextSibling | |
3936 }); | |
3937 } | |
3938 | |
3939 function enqueueRemovalForInsertedDocumentFragment(df, nodes) { | |
3940 enqueueMutation(df, 'childList', { | |
3941 removedNodes: nodes | |
3942 }); | |
3943 } | |
3944 | |
3945 /** | |
3946 * Collects nodes from a DocumentFragment or a Node for removal followed | |
3947 * by an insertion. | |
3948 * | |
3949 * This updates the internal pointers for node, previousNode and nextNode. | |
3950 */ | |
3951 function collectNodes(node, parentNode, previousNode, nextNode) { | |
3952 if (node instanceof DocumentFragment) { | |
3953 var nodes = collectNodesForDocumentFragment(node); | |
3954 | |
3955 // The extra loop is to work around bugs with DocumentFragments in IE. | |
3956 surpressMutations = true; | |
3957 for (var i = nodes.length - 1; i >= 0; i--) { | |
3958 node.removeChild(nodes[i]); | |
3959 nodes[i].parentNode_ = parentNode; | |
3960 } | |
3961 surpressMutations = false; | |
3962 | |
3963 for (var i = 0; i < nodes.length; i++) { | |
3964 nodes[i].previousSibling_ = nodes[i - 1] || previousNode; | |
3965 nodes[i].nextSibling_ = nodes[i + 1] || nextNode; | |
3966 } | |
3967 | |
3968 if (previousNode) | |
3969 previousNode.nextSibling_ = nodes[0]; | |
3970 if (nextNode) | |
3971 nextNode.previousSibling_ = nodes[nodes.length - 1]; | |
3972 | |
3973 return nodes; | |
3974 } | |
3975 | |
3976 var nodes = createOneElementNodeList(node); | |
3977 var oldParent = node.parentNode; | |
3978 if (oldParent) { | |
3979 // This will enqueue the mutation record for the removal as needed. | |
3980 oldParent.removeChild(node); | |
3981 } | |
3982 | |
3983 node.parentNode_ = parentNode; | |
3984 node.previousSibling_ = previousNode; | |
3985 node.nextSibling_ = nextNode; | |
3986 if (previousNode) | |
3987 previousNode.nextSibling_ = node; | |
3988 if (nextNode) | |
3989 nextNode.previousSibling_ = node; | |
3990 | |
3991 return nodes; | |
3992 } | |
3993 | |
3994 function collectNodesNative(node) { | |
3995 if (node instanceof DocumentFragment) | |
3996 return collectNodesForDocumentFragment(node); | |
3997 | |
3998 var nodes = createOneElementNodeList(node); | |
3999 var oldParent = node.parentNode; | |
4000 if (oldParent) | |
4001 enqueueRemovalForInsertedNodes(node, oldParent, nodes); | |
4002 return nodes; | |
4003 } | |
4004 | |
4005 function collectNodesForDocumentFragment(node) { | |
4006 var nodes = new NodeList(); | |
4007 var i = 0; | |
4008 for (var child = node.firstChild; child; child = child.nextSibling) { | |
4009 nodes[i++] = child; | |
4010 } | |
4011 nodes.length = i; | |
4012 enqueueRemovalForInsertedDocumentFragment(node, nodes); | |
4013 return nodes; | |
4014 } | |
4015 | |
4016 function snapshotNodeList(nodeList) { | |
4017 // NodeLists are not live at the moment so just return the same object. | |
4018 return nodeList; | |
4019 } | |
4020 | |
4021 // http://dom.spec.whatwg.org/#node-is-inserted | |
4022 function nodeWasAdded(node, treeScope) { | |
4023 setTreeScope(node, treeScope); | |
4024 node.nodeIsInserted_(); | |
4025 } | |
4026 | |
4027 function nodesWereAdded(nodes, parent) { | |
4028 var treeScope = getTreeScope(parent); | |
4029 for (var i = 0; i < nodes.length; i++) { | |
4030 nodeWasAdded(nodes[i], treeScope); | |
4031 } | |
4032 } | |
4033 | |
4034 // http://dom.spec.whatwg.org/#node-is-removed | |
4035 function nodeWasRemoved(node) { | |
4036 setTreeScope(node, new TreeScope(node, null)); | |
4037 } | |
4038 | |
4039 function nodesWereRemoved(nodes) { | |
4040 for (var i = 0; i < nodes.length; i++) { | |
4041 nodeWasRemoved(nodes[i]); | |
4042 } | |
4043 } | |
4044 | |
4045 function ensureSameOwnerDocument(parent, child) { | |
4046 var ownerDoc = parent.nodeType === Node.DOCUMENT_NODE ? | |
4047 parent : parent.ownerDocument; | |
4048 if (ownerDoc !== child.ownerDocument) | |
4049 ownerDoc.adoptNode(child); | |
4050 } | |
4051 | |
4052 function adoptNodesIfNeeded(owner, nodes) { | |
4053 if (!nodes.length) | |
4054 return; | |
4055 | |
4056 var ownerDoc = owner.ownerDocument; | |
4057 | |
4058 // All nodes have the same ownerDocument when we get here. | |
4059 if (ownerDoc === nodes[0].ownerDocument) | |
4060 return; | |
4061 | |
4062 for (var i = 0; i < nodes.length; i++) { | |
4063 scope.adoptNodeNoRemove(nodes[i], ownerDoc); | |
4064 } | |
4065 } | |
4066 | |
4067 function unwrapNodesForInsertion(owner, nodes) { | |
4068 adoptNodesIfNeeded(owner, nodes); | |
4069 var length = nodes.length; | |
4070 | |
4071 if (length === 1) | |
4072 return unwrap(nodes[0]); | |
4073 | |
4074 var df = unwrap(owner.ownerDocument.createDocumentFragment()); | |
4075 for (var i = 0; i < length; i++) { | |
4076 df.appendChild(unwrap(nodes[i])); | |
4077 } | |
4078 return df; | |
4079 } | |
4080 | |
4081 function clearChildNodes(wrapper) { | |
4082 if (wrapper.firstChild_ !== undefined) { | |
4083 var child = wrapper.firstChild_; | |
4084 while (child) { | |
4085 var tmp = child; | |
4086 child = child.nextSibling_; | |
4087 tmp.parentNode_ = tmp.previousSibling_ = tmp.nextSibling_ = undefined; | |
4088 } | |
4089 } | |
4090 wrapper.firstChild_ = wrapper.lastChild_ = undefined; | |
4091 } | |
4092 | |
4093 function removeAllChildNodes(wrapper) { | |
4094 if (wrapper.invalidateShadowRenderer()) { | |
4095 var childWrapper = wrapper.firstChild; | |
4096 while (childWrapper) { | |
4097 assert(childWrapper.parentNode === wrapper); | |
4098 var nextSibling = childWrapper.nextSibling; | |
4099 var childNode = unwrap(childWrapper); | |
4100 var parentNode = childNode.parentNode; | |
4101 if (parentNode) | |
4102 originalRemoveChild.call(parentNode, childNode); | |
4103 childWrapper.previousSibling_ = childWrapper.nextSibling_ = | |
4104 childWrapper.parentNode_ = null; | |
4105 childWrapper = nextSibling; | |
4106 } | |
4107 wrapper.firstChild_ = wrapper.lastChild_ = null; | |
4108 } else { | |
4109 var node = unwrap(wrapper); | |
4110 var child = node.firstChild; | |
4111 var nextSibling; | |
4112 while (child) { | |
4113 nextSibling = child.nextSibling; | |
4114 originalRemoveChild.call(node, child); | |
4115 child = nextSibling; | |
4116 } | |
4117 } | |
4118 } | |
4119 | |
4120 function invalidateParent(node) { | |
4121 var p = node.parentNode; | |
4122 return p && p.invalidateShadowRenderer(); | |
4123 } | |
4124 | |
4125 function cleanupNodes(nodes) { | |
4126 for (var i = 0, n; i < nodes.length; i++) { | |
4127 n = nodes[i]; | |
4128 n.parentNode.removeChild(n); | |
4129 } | |
4130 } | |
4131 | |
4132 var originalImportNode = document.importNode; | |
4133 var originalCloneNode = window.Node.prototype.cloneNode; | |
4134 | |
4135 function cloneNode(node, deep, opt_doc) { | |
4136 var clone; | |
4137 if (opt_doc) | |
4138 clone = wrap(originalImportNode.call(opt_doc, unsafeUnwrap(node), false)); | |
4139 else | |
4140 clone = wrap(originalCloneNode.call(unsafeUnwrap(node), false)); | |
4141 | |
4142 if (deep) { | |
4143 for (var child = node.firstChild; child; child = child.nextSibling) { | |
4144 clone.appendChild(cloneNode(child, true, opt_doc)); | |
4145 } | |
4146 | |
4147 if (node instanceof wrappers.HTMLTemplateElement) { | |
4148 var cloneContent = clone.content; | |
4149 for (var child = node.content.firstChild; | |
4150 child; | |
4151 child = child.nextSibling) { | |
4152 cloneContent.appendChild(cloneNode(child, true, opt_doc)); | |
4153 } | |
4154 } | |
4155 } | |
4156 // TODO(arv): Some HTML elements also clone other data like value. | |
4157 return clone; | |
4158 } | |
4159 | |
4160 function contains(self, child) { | |
4161 if (!child || getTreeScope(self) !== getTreeScope(child)) | |
4162 return false; | |
4163 | |
4164 for (var node = child; node; node = node.parentNode) { | |
4165 if (node === self) | |
4166 return true; | |
4167 } | |
4168 return false; | |
4169 } | |
4170 | |
4171 var OriginalNode = window.Node; | |
4172 | |
4173 /** | |
4174 * This represents a wrapper of a native DOM node. | |
4175 * @param {!Node} original The original DOM node, aka, the visual DOM node. | |
4176 * @constructor | |
4177 * @extends {EventTarget} | |
4178 */ | |
4179 function Node(original) { | |
4180 assert(original instanceof OriginalNode); | |
4181 | |
4182 EventTarget.call(this, original); | |
4183 | |
4184 // These properties are used to override the visual references with the | |
4185 // logical ones. If the value is undefined it means that the logical is the | |
4186 // same as the visual. | |
4187 | |
4188 /** | |
4189 * @type {Node|undefined} | |
4190 * @private | |
4191 */ | |
4192 this.parentNode_ = undefined; | |
4193 | |
4194 /** | |
4195 * @type {Node|undefined} | |
4196 * @private | |
4197 */ | |
4198 this.firstChild_ = undefined; | |
4199 | |
4200 /** | |
4201 * @type {Node|undefined} | |
4202 * @private | |
4203 */ | |
4204 this.lastChild_ = undefined; | |
4205 | |
4206 /** | |
4207 * @type {Node|undefined} | |
4208 * @private | |
4209 */ | |
4210 this.nextSibling_ = undefined; | |
4211 | |
4212 /** | |
4213 * @type {Node|undefined} | |
4214 * @private | |
4215 */ | |
4216 this.previousSibling_ = undefined; | |
4217 | |
4218 this.treeScope_ = undefined; | |
4219 } | |
4220 | |
4221 var OriginalDocumentFragment = window.DocumentFragment; | |
4222 var originalAppendChild = OriginalNode.prototype.appendChild; | |
4223 var originalCompareDocumentPosition = | |
4224 OriginalNode.prototype.compareDocumentPosition; | |
4225 var originalInsertBefore = OriginalNode.prototype.insertBefore; | |
4226 var originalRemoveChild = OriginalNode.prototype.removeChild; | |
4227 var originalReplaceChild = OriginalNode.prototype.replaceChild; | |
4228 | |
4229 var isIe = /Trident/.test(navigator.userAgent); | |
4230 | |
4231 var removeChildOriginalHelper = isIe ? | |
4232 function(parent, child) { | |
4233 try { | |
4234 originalRemoveChild.call(parent, child); | |
4235 } catch (ex) { | |
4236 if (!(parent instanceof OriginalDocumentFragment)) | |
4237 throw ex; | |
4238 } | |
4239 } : | |
4240 function(parent, child) { | |
4241 originalRemoveChild.call(parent, child); | |
4242 }; | |
4243 | |
4244 Node.prototype = Object.create(EventTarget.prototype); | |
4245 mixin(Node.prototype, { | |
4246 appendChild: function(childWrapper) { | |
4247 return this.insertBefore(childWrapper, null); | |
4248 }, | |
4249 | |
4250 insertBefore: function(childWrapper, refWrapper) { | |
4251 assertIsNodeWrapper(childWrapper); | |
4252 | |
4253 var refNode; | |
4254 if (refWrapper) { | |
4255 if (isWrapper(refWrapper)) { | |
4256 refNode = unwrap(refWrapper); | |
4257 } else { | |
4258 refNode = refWrapper; | |
4259 refWrapper = wrap(refNode); | |
4260 } | |
4261 } else { | |
4262 refWrapper = null; | |
4263 refNode = null; | |
4264 } | |
4265 | |
4266 refWrapper && assert(refWrapper.parentNode === this); | |
4267 | |
4268 var nodes; | |
4269 var previousNode = | |
4270 refWrapper ? refWrapper.previousSibling : this.lastChild; | |
4271 | |
4272 var useNative = !this.invalidateShadowRenderer() && | |
4273 !invalidateParent(childWrapper); | |
4274 | |
4275 if (useNative) | |
4276 nodes = collectNodesNative(childWrapper); | |
4277 else | |
4278 nodes = collectNodes(childWrapper, this, previousNode, refWrapper); | |
4279 | |
4280 if (useNative) { | |
4281 ensureSameOwnerDocument(this, childWrapper); | |
4282 clearChildNodes(this); | |
4283 originalInsertBefore.call(unsafeUnwrap(this), unwrap(childWrapper), refN
ode); | |
4284 } else { | |
4285 if (!previousNode) | |
4286 this.firstChild_ = nodes[0]; | |
4287 if (!refWrapper) { | |
4288 this.lastChild_ = nodes[nodes.length - 1]; | |
4289 if (this.firstChild_ === undefined) | |
4290 this.firstChild_ = this.firstChild; | |
4291 } | |
4292 | |
4293 var parentNode = refNode ? refNode.parentNode : unsafeUnwrap(this); | |
4294 | |
4295 // insertBefore refWrapper no matter what the parent is? | |
4296 if (parentNode) { | |
4297 originalInsertBefore.call(parentNode, | |
4298 unwrapNodesForInsertion(this, nodes), refNode); | |
4299 } else { | |
4300 adoptNodesIfNeeded(this, nodes); | |
4301 } | |
4302 } | |
4303 | |
4304 enqueueMutation(this, 'childList', { | |
4305 addedNodes: nodes, | |
4306 nextSibling: refWrapper, | |
4307 previousSibling: previousNode | |
4308 }); | |
4309 | |
4310 nodesWereAdded(nodes, this); | |
4311 | |
4312 return childWrapper; | |
4313 }, | |
4314 | |
4315 removeChild: function(childWrapper) { | |
4316 assertIsNodeWrapper(childWrapper); | |
4317 if (childWrapper.parentNode !== this) { | |
4318 // IE has invalid DOM trees at times. | |
4319 var found = false; | |
4320 var childNodes = this.childNodes; | |
4321 for (var ieChild = this.firstChild; ieChild; | |
4322 ieChild = ieChild.nextSibling) { | |
4323 if (ieChild === childWrapper) { | |
4324 found = true; | |
4325 break; | |
4326 } | |
4327 } | |
4328 if (!found) { | |
4329 // TODO(arv): DOMException | |
4330 throw new Error('NotFoundError'); | |
4331 } | |
4332 } | |
4333 | |
4334 var childNode = unwrap(childWrapper); | |
4335 var childWrapperNextSibling = childWrapper.nextSibling; | |
4336 var childWrapperPreviousSibling = childWrapper.previousSibling; | |
4337 | |
4338 if (this.invalidateShadowRenderer()) { | |
4339 // We need to remove the real node from the DOM before updating the | |
4340 // pointers. This is so that that mutation event is dispatched before | |
4341 // the pointers have changed. | |
4342 var thisFirstChild = this.firstChild; | |
4343 var thisLastChild = this.lastChild; | |
4344 | |
4345 var parentNode = childNode.parentNode; | |
4346 if (parentNode) | |
4347 removeChildOriginalHelper(parentNode, childNode); | |
4348 | |
4349 if (thisFirstChild === childWrapper) | |
4350 this.firstChild_ = childWrapperNextSibling; | |
4351 if (thisLastChild === childWrapper) | |
4352 this.lastChild_ = childWrapperPreviousSibling; | |
4353 if (childWrapperPreviousSibling) | |
4354 childWrapperPreviousSibling.nextSibling_ = childWrapperNextSibling; | |
4355 if (childWrapperNextSibling) { | |
4356 childWrapperNextSibling.previousSibling_ = | |
4357 childWrapperPreviousSibling; | |
4358 } | |
4359 | |
4360 childWrapper.previousSibling_ = childWrapper.nextSibling_ = | |
4361 childWrapper.parentNode_ = undefined; | |
4362 } else { | |
4363 clearChildNodes(this); | |
4364 removeChildOriginalHelper(unsafeUnwrap(this), childNode); | |
4365 } | |
4366 | |
4367 if (!surpressMutations) { | |
4368 enqueueMutation(this, 'childList', { | |
4369 removedNodes: createOneElementNodeList(childWrapper), | |
4370 nextSibling: childWrapperNextSibling, | |
4371 previousSibling: childWrapperPreviousSibling | |
4372 }); | |
4373 } | |
4374 | |
4375 registerTransientObservers(this, childWrapper); | |
4376 | |
4377 return childWrapper; | |
4378 }, | |
4379 | |
4380 replaceChild: function(newChildWrapper, oldChildWrapper) { | |
4381 assertIsNodeWrapper(newChildWrapper); | |
4382 | |
4383 var oldChildNode; | |
4384 if (isWrapper(oldChildWrapper)) { | |
4385 oldChildNode = unwrap(oldChildWrapper); | |
4386 } else { | |
4387 oldChildNode = oldChildWrapper; | |
4388 oldChildWrapper = wrap(oldChildNode); | |
4389 } | |
4390 | |
4391 if (oldChildWrapper.parentNode !== this) { | |
4392 // TODO(arv): DOMException | |
4393 throw new Error('NotFoundError'); | |
4394 } | |
4395 | |
4396 var nextNode = oldChildWrapper.nextSibling; | |
4397 var previousNode = oldChildWrapper.previousSibling; | |
4398 var nodes; | |
4399 | |
4400 var useNative = !this.invalidateShadowRenderer() && | |
4401 !invalidateParent(newChildWrapper); | |
4402 | |
4403 if (useNative) { | |
4404 nodes = collectNodesNative(newChildWrapper); | |
4405 } else { | |
4406 if (nextNode === newChildWrapper) | |
4407 nextNode = newChildWrapper.nextSibling; | |
4408 nodes = collectNodes(newChildWrapper, this, previousNode, nextNode); | |
4409 } | |
4410 | |
4411 if (!useNative) { | |
4412 if (this.firstChild === oldChildWrapper) | |
4413 this.firstChild_ = nodes[0]; | |
4414 if (this.lastChild === oldChildWrapper) | |
4415 this.lastChild_ = nodes[nodes.length - 1]; | |
4416 | |
4417 oldChildWrapper.previousSibling_ = oldChildWrapper.nextSibling_ = | |
4418 oldChildWrapper.parentNode_ = undefined; | |
4419 | |
4420 // replaceChild no matter what the parent is? | |
4421 if (oldChildNode.parentNode) { | |
4422 originalReplaceChild.call( | |
4423 oldChildNode.parentNode, | |
4424 unwrapNodesForInsertion(this, nodes), | |
4425 oldChildNode); | |
4426 } | |
4427 } else { | |
4428 ensureSameOwnerDocument(this, newChildWrapper); | |
4429 clearChildNodes(this); | |
4430 originalReplaceChild.call(unsafeUnwrap(this), unwrap(newChildWrapper), | |
4431 oldChildNode); | |
4432 } | |
4433 | |
4434 enqueueMutation(this, 'childList', { | |
4435 addedNodes: nodes, | |
4436 removedNodes: createOneElementNodeList(oldChildWrapper), | |
4437 nextSibling: nextNode, | |
4438 previousSibling: previousNode | |
4439 }); | |
4440 | |
4441 nodeWasRemoved(oldChildWrapper); | |
4442 nodesWereAdded(nodes, this); | |
4443 | |
4444 return oldChildWrapper; | |
4445 }, | |
4446 | |
4447 /** | |
4448 * Called after a node was inserted. Subclasses override this to invalidate | |
4449 * the renderer as needed. | |
4450 * @private | |
4451 */ | |
4452 nodeIsInserted_: function() { | |
4453 for (var child = this.firstChild; child; child = child.nextSibling) { | |
4454 child.nodeIsInserted_(); | |
4455 } | |
4456 }, | |
4457 | |
4458 hasChildNodes: function() { | |
4459 return this.firstChild !== null; | |
4460 }, | |
4461 | |
4462 /** @type {Node} */ | |
4463 get parentNode() { | |
4464 // If the parentNode has not been overridden, use the original parentNode. | |
4465 return this.parentNode_ !== undefined ? | |
4466 this.parentNode_ : wrap(unsafeUnwrap(this).parentNode); | |
4467 }, | |
4468 | |
4469 /** @type {Node} */ | |
4470 get firstChild() { | |
4471 return this.firstChild_ !== undefined ? | |
4472 this.firstChild_ : wrap(unsafeUnwrap(this).firstChild); | |
4473 }, | |
4474 | |
4475 /** @type {Node} */ | |
4476 get lastChild() { | |
4477 return this.lastChild_ !== undefined ? | |
4478 this.lastChild_ : wrap(unsafeUnwrap(this).lastChild); | |
4479 }, | |
4480 | |
4481 /** @type {Node} */ | |
4482 get nextSibling() { | |
4483 return this.nextSibling_ !== undefined ? | |
4484 this.nextSibling_ : wrap(unsafeUnwrap(this).nextSibling); | |
4485 }, | |
4486 | |
4487 /** @type {Node} */ | |
4488 get previousSibling() { | |
4489 return this.previousSibling_ !== undefined ? | |
4490 this.previousSibling_ : wrap(unsafeUnwrap(this).previousSibling); | |
4491 }, | |
4492 | |
4493 get parentElement() { | |
4494 var p = this.parentNode; | |
4495 while (p && p.nodeType !== Node.ELEMENT_NODE) { | |
4496 p = p.parentNode; | |
4497 } | |
4498 return p; | |
4499 }, | |
4500 | |
4501 get textContent() { | |
4502 // TODO(arv): This should fallback to unsafeUnwrap(this).textContent if th
ere | |
4503 // are no shadow trees below or above the context node. | |
4504 var s = ''; | |
4505 for (var child = this.firstChild; child; child = child.nextSibling) { | |
4506 if (child.nodeType != Node.COMMENT_NODE) { | |
4507 s += child.textContent; | |
4508 } | |
4509 } | |
4510 return s; | |
4511 }, | |
4512 set textContent(textContent) { | |
4513 if (textContent == null) textContent = ''; | |
4514 var removedNodes = snapshotNodeList(this.childNodes); | |
4515 | |
4516 if (this.invalidateShadowRenderer()) { | |
4517 removeAllChildNodes(this); | |
4518 if (textContent !== '') { | |
4519 var textNode = unsafeUnwrap(this).ownerDocument.createTextNode(textCon
tent); | |
4520 this.appendChild(textNode); | |
4521 } | |
4522 } else { | |
4523 clearChildNodes(this); | |
4524 unsafeUnwrap(this).textContent = textContent; | |
4525 } | |
4526 | |
4527 var addedNodes = snapshotNodeList(this.childNodes); | |
4528 | |
4529 enqueueMutation(this, 'childList', { | |
4530 addedNodes: addedNodes, | |
4531 removedNodes: removedNodes | |
4532 }); | |
4533 | |
4534 nodesWereRemoved(removedNodes); | |
4535 nodesWereAdded(addedNodes, this); | |
4536 }, | |
4537 | |
4538 get childNodes() { | |
4539 var wrapperList = new NodeList(); | |
4540 var i = 0; | |
4541 for (var child = this.firstChild; child; child = child.nextSibling) { | |
4542 wrapperList[i++] = child; | |
4543 } | |
4544 wrapperList.length = i; | |
4545 return wrapperList; | |
4546 }, | |
4547 | |
4548 cloneNode: function(deep) { | |
4549 return cloneNode(this, deep); | |
4550 }, | |
4551 | |
4552 contains: function(child) { | |
4553 return contains(this, wrapIfNeeded(child)); | |
4554 }, | |
4555 | |
4556 compareDocumentPosition: function(otherNode) { | |
4557 // This only wraps, it therefore only operates on the composed DOM and not | |
4558 // the logical DOM. | |
4559 return originalCompareDocumentPosition.call(unsafeUnwrap(this), | |
4560 unwrapIfNeeded(otherNode)); | |
4561 }, | |
4562 | |
4563 normalize: function() { | |
4564 var nodes = snapshotNodeList(this.childNodes); | |
4565 var remNodes = []; | |
4566 var s = ''; | |
4567 var modNode; | |
4568 | |
4569 for (var i = 0, n; i < nodes.length; i++) { | |
4570 n = nodes[i]; | |
4571 if (n.nodeType === Node.TEXT_NODE) { | |
4572 if (!modNode && !n.data.length) | |
4573 this.removeNode(n); | |
4574 else if (!modNode) | |
4575 modNode = n; | |
4576 else { | |
4577 s += n.data; | |
4578 remNodes.push(n); | |
4579 } | |
4580 } else { | |
4581 if (modNode && remNodes.length) { | |
4582 modNode.data += s; | |
4583 cleanupNodes(remNodes); | |
4584 } | |
4585 remNodes = []; | |
4586 s = ''; | |
4587 modNode = null; | |
4588 if (n.childNodes.length) | |
4589 n.normalize(); | |
4590 } | |
4591 } | |
4592 | |
4593 // handle case where >1 text nodes are the last children | |
4594 if (modNode && remNodes.length) { | |
4595 modNode.data += s; | |
4596 cleanupNodes(remNodes); | |
4597 } | |
4598 } | |
4599 }); | |
4600 | |
4601 defineWrapGetter(Node, 'ownerDocument'); | |
4602 | |
4603 // We use a DocumentFragment as a base and then delete the properties of | |
4604 // DocumentFragment.prototype from the wrapper Node. Since delete makes | |
4605 // objects slow in some JS engines we recreate the prototype object. | |
4606 registerWrapper(OriginalNode, Node, document.createDocumentFragment()); | |
4607 delete Node.prototype.querySelector; | |
4608 delete Node.prototype.querySelectorAll; | |
4609 Node.prototype = mixin(Object.create(EventTarget.prototype), Node.prototype); | |
4610 | |
4611 scope.cloneNode = cloneNode; | |
4612 scope.nodeWasAdded = nodeWasAdded; | |
4613 scope.nodeWasRemoved = nodeWasRemoved; | |
4614 scope.nodesWereAdded = nodesWereAdded; | |
4615 scope.nodesWereRemoved = nodesWereRemoved; | |
4616 scope.originalInsertBefore = originalInsertBefore; | |
4617 scope.originalRemoveChild = originalRemoveChild; | |
4618 scope.snapshotNodeList = snapshotNodeList; | |
4619 scope.wrappers.Node = Node; | |
4620 | |
4621 })(window.ShadowDOMPolyfill); | |
4622 | |
4623 // Copyright 2013 The Polymer Authors. All rights reserved. | |
4624 // Use of this source code is governed by a BSD-style | |
4625 // license that can be found in the LICENSE file. | |
4626 | |
4627 (function(scope) { | |
4628 'use strict'; | |
4629 | |
4630 var HTMLCollection = scope.wrappers.HTMLCollection; | |
4631 var NodeList = scope.wrappers.NodeList; | |
4632 var getTreeScope = scope.getTreeScope; | |
4633 var unsafeUnwrap = scope.unsafeUnwrap; | |
4634 var wrap = scope.wrap; | |
4635 | |
4636 var originalDocumentQuerySelector = document.querySelector; | |
4637 var originalElementQuerySelector = document.documentElement.querySelector; | |
4638 | |
4639 var originalDocumentQuerySelectorAll = document.querySelectorAll; | |
4640 var originalElementQuerySelectorAll = document.documentElement.querySelectorAl
l; | |
4641 | |
4642 var originalDocumentGetElementsByTagName = document.getElementsByTagName; | |
4643 var originalElementGetElementsByTagName = document.documentElement.getElements
ByTagName; | |
4644 | |
4645 var originalDocumentGetElementsByTagNameNS = document.getElementsByTagNameNS; | |
4646 var originalElementGetElementsByTagNameNS = document.documentElement.getElemen
tsByTagNameNS; | |
4647 | |
4648 var OriginalElement = window.Element; | |
4649 var OriginalDocument = window.HTMLDocument || window.Document; | |
4650 | |
4651 function filterNodeList(list, index, result, deep) { | |
4652 var wrappedItem = null; | |
4653 var root = null; | |
4654 for (var i = 0, length = list.length; i < length; i++) { | |
4655 wrappedItem = wrap(list[i]); | |
4656 if (!deep && (root = getTreeScope(wrappedItem).root)) { | |
4657 if (root instanceof scope.wrappers.ShadowRoot) { | |
4658 continue; | |
4659 } | |
4660 } | |
4661 result[index++] = wrappedItem; | |
4662 } | |
4663 | |
4664 return index; | |
4665 } | |
4666 | |
4667 function shimSelector(selector) { | |
4668 return String(selector).replace(/\/deep\//g, ' '); | |
4669 } | |
4670 | |
4671 function findOne(node, selector) { | |
4672 var m, el = node.firstElementChild; | |
4673 while (el) { | |
4674 if (el.matches(selector)) | |
4675 return el; | |
4676 m = findOne(el, selector); | |
4677 if (m) | |
4678 return m; | |
4679 el = el.nextElementSibling; | |
4680 } | |
4681 return null; | |
4682 } | |
4683 | |
4684 function matchesSelector(el, selector) { | |
4685 return el.matches(selector); | |
4686 } | |
4687 | |
4688 var XHTML_NS = 'http://www.w3.org/1999/xhtml'; | |
4689 | |
4690 function matchesTagName(el, localName, localNameLowerCase) { | |
4691 var ln = el.localName; | |
4692 return ln === localName || | |
4693 ln === localNameLowerCase && el.namespaceURI === XHTML_NS; | |
4694 } | |
4695 | |
4696 function matchesEveryThing() { | |
4697 return true; | |
4698 } | |
4699 | |
4700 function matchesLocalNameOnly(el, ns, localName) { | |
4701 return el.localName === localName; | |
4702 } | |
4703 | |
4704 function matchesNameSpace(el, ns) { | |
4705 return el.namespaceURI === ns; | |
4706 } | |
4707 | |
4708 function matchesLocalNameNS(el, ns, localName) { | |
4709 return el.namespaceURI === ns && el.localName === localName; | |
4710 } | |
4711 | |
4712 function findElements(node, index, result, p, arg0, arg1) { | |
4713 var el = node.firstElementChild; | |
4714 while (el) { | |
4715 if (p(el, arg0, arg1)) | |
4716 result[index++] = el; | |
4717 index = findElements(el, index, result, p, arg0, arg1); | |
4718 el = el.nextElementSibling; | |
4719 } | |
4720 return index; | |
4721 } | |
4722 | |
4723 // find and findAll will only match Simple Selectors, | |
4724 // Structural Pseudo Classes are not guarenteed to be correct | |
4725 // http://www.w3.org/TR/css3-selectors/#simple-selectors | |
4726 | |
4727 function querySelectorAllFiltered(p, index, result, selector, deep) { | |
4728 var target = unsafeUnwrap(this); | |
4729 var list; | |
4730 var root = getTreeScope(this).root; | |
4731 if (root instanceof scope.wrappers.ShadowRoot) { | |
4732 // We are in the shadow tree and the logical tree is | |
4733 // going to be disconnected so we do a manual tree traversal | |
4734 return findElements(this, index, result, p, selector, null); | |
4735 } else if (target instanceof OriginalElement) { | |
4736 list = originalElementQuerySelectorAll.call(target, selector); | |
4737 } else if (target instanceof OriginalDocument) { | |
4738 list = originalDocumentQuerySelectorAll.call(target, selector); | |
4739 } else { | |
4740 // When we get a ShadowRoot the logical tree is going to be disconnected | |
4741 // so we do a manual tree traversal | |
4742 return findElements(this, index, result, p, selector, null); | |
4743 } | |
4744 | |
4745 return filterNodeList(list, index, result, deep); | |
4746 } | |
4747 | |
4748 var SelectorsInterface = { | |
4749 querySelector: function(selector) { | |
4750 var shimmed = shimSelector(selector); | |
4751 var deep = shimmed !== selector; | |
4752 selector = shimmed; | |
4753 | |
4754 var target = unsafeUnwrap(this); | |
4755 var wrappedItem; | |
4756 var root = getTreeScope(this).root; | |
4757 if (root instanceof scope.wrappers.ShadowRoot) { | |
4758 // We are in the shadow tree and the logical tree is | |
4759 // going to be disconnected so we do a manual tree traversal | |
4760 return findOne(this, selector); | |
4761 } else if (target instanceof OriginalElement) { | |
4762 wrappedItem = wrap(originalElementQuerySelector.call(target, selector)); | |
4763 } else if (target instanceof OriginalDocument) { | |
4764 wrappedItem = wrap(originalDocumentQuerySelector.call(target, selector))
; | |
4765 } else { | |
4766 // When we get a ShadowRoot the logical tree is going to be disconnected | |
4767 // so we do a manual tree traversal | |
4768 return findOne(this, selector); | |
4769 } | |
4770 | |
4771 if (!wrappedItem) { | |
4772 // When the original query returns nothing | |
4773 // we return nothing (to be consistent with the other wrapped calls) | |
4774 return wrappedItem; | |
4775 } else if (!deep && (root = getTreeScope(wrappedItem).root)) { | |
4776 if (root instanceof scope.wrappers.ShadowRoot) { | |
4777 // When the original query returns an element in the ShadowDOM | |
4778 // we must do a manual tree traversal | |
4779 return findOne(this, selector); | |
4780 } | |
4781 } | |
4782 | |
4783 return wrappedItem; | |
4784 }, | |
4785 querySelectorAll: function(selector) { | |
4786 var shimmed = shimSelector(selector); | |
4787 var deep = shimmed !== selector; | |
4788 selector = shimmed; | |
4789 | |
4790 var result = new NodeList(); | |
4791 | |
4792 result.length = querySelectorAllFiltered.call(this, | |
4793 matchesSelector, | |
4794 0, | |
4795 result, | |
4796 selector, | |
4797 deep); | |
4798 | |
4799 return result; | |
4800 } | |
4801 }; | |
4802 | |
4803 function getElementsByTagNameFiltered(p, index, result, localName, | |
4804 lowercase) { | |
4805 var target = unsafeUnwrap(this); | |
4806 var list; | |
4807 var root = getTreeScope(this).root; | |
4808 if (root instanceof scope.wrappers.ShadowRoot) { | |
4809 // We are in the shadow tree and the logical tree is | |
4810 // going to be disconnected so we do a manual tree traversal | |
4811 return findElements(this, index, result, p, localName, lowercase); | |
4812 } else if (target instanceof OriginalElement) { | |
4813 list = originalElementGetElementsByTagName.call(target, localName, | |
4814 lowercase); | |
4815 } else if (target instanceof OriginalDocument) { | |
4816 list = originalDocumentGetElementsByTagName.call(target, localName, | |
4817 lowercase); | |
4818 } else { | |
4819 // When we get a ShadowRoot the logical tree is going to be disconnected | |
4820 // so we do a manual tree traversal | |
4821 return findElements(this, index, result, p, localName, lowercase); | |
4822 } | |
4823 | |
4824 return filterNodeList(list, index, result, false); | |
4825 } | |
4826 | |
4827 function getElementsByTagNameNSFiltered(p, index, result, ns, localName) { | |
4828 var target = unsafeUnwrap(this); | |
4829 var list; | |
4830 var root = getTreeScope(this).root; | |
4831 if (root instanceof scope.wrappers.ShadowRoot) { | |
4832 // We are in the shadow tree and the logical tree is | |
4833 // going to be disconnected so we do a manual tree traversal | |
4834 return findElements(this, index, result, p, ns, localName); | |
4835 } else if (target instanceof OriginalElement) { | |
4836 list = originalElementGetElementsByTagNameNS.call(target, ns, localName); | |
4837 } else if (target instanceof OriginalDocument) { | |
4838 list = originalDocumentGetElementsByTagNameNS.call(target, ns, localName); | |
4839 } else { | |
4840 // When we get a ShadowRoot the logical tree is going to be disconnected | |
4841 // so we do a manual tree traversal | |
4842 return findElements(this, index, result, p, ns, localName); | |
4843 } | |
4844 | |
4845 return filterNodeList(list, index, result, false); | |
4846 } | |
4847 | |
4848 var GetElementsByInterface = { | |
4849 getElementsByTagName: function(localName) { | |
4850 var result = new HTMLCollection(); | |
4851 var match = localName === '*' ? matchesEveryThing : matchesTagName; | |
4852 | |
4853 result.length = getElementsByTagNameFiltered.call(this, | |
4854 match, | |
4855 0, | |
4856 result, | |
4857 localName, | |
4858 localName.toLowerCase()); | |
4859 | |
4860 return result; | |
4861 }, | |
4862 | |
4863 getElementsByClassName: function(className) { | |
4864 // TODO(arv): Check className? | |
4865 return this.querySelectorAll('.' + className); | |
4866 }, | |
4867 | |
4868 getElementsByTagNameNS: function(ns, localName) { | |
4869 var result = new HTMLCollection(); | |
4870 var match = null; | |
4871 | |
4872 if (ns === '*') { | |
4873 match = localName === '*' ? matchesEveryThing : matchesLocalNameOnly; | |
4874 } else { | |
4875 match = localName === '*' ? matchesNameSpace : matchesLocalNameNS; | |
4876 } | |
4877 | |
4878 result.length = getElementsByTagNameNSFiltered.call(this, | |
4879 match, | |
4880 0, | |
4881 result, | |
4882 ns || null, | |
4883 localName); | |
4884 | |
4885 return result; | |
4886 } | |
4887 }; | |
4888 | |
4889 scope.GetElementsByInterface = GetElementsByInterface; | |
4890 scope.SelectorsInterface = SelectorsInterface; | |
4891 | |
4892 })(window.ShadowDOMPolyfill); | |
4893 | |
4894 // Copyright 2013 The Polymer Authors. All rights reserved. | |
4895 // Use of this source code is goverened by a BSD-style | |
4896 // license that can be found in the LICENSE file. | |
4897 | |
4898 (function(scope) { | |
4899 'use strict'; | |
4900 | |
4901 var NodeList = scope.wrappers.NodeList; | |
4902 | |
4903 function forwardElement(node) { | |
4904 while (node && node.nodeType !== Node.ELEMENT_NODE) { | |
4905 node = node.nextSibling; | |
4906 } | |
4907 return node; | |
4908 } | |
4909 | |
4910 function backwardsElement(node) { | |
4911 while (node && node.nodeType !== Node.ELEMENT_NODE) { | |
4912 node = node.previousSibling; | |
4913 } | |
4914 return node; | |
4915 } | |
4916 | |
4917 var ParentNodeInterface = { | |
4918 get firstElementChild() { | |
4919 return forwardElement(this.firstChild); | |
4920 }, | |
4921 | |
4922 get lastElementChild() { | |
4923 return backwardsElement(this.lastChild); | |
4924 }, | |
4925 | |
4926 get childElementCount() { | |
4927 var count = 0; | |
4928 for (var child = this.firstElementChild; | |
4929 child; | |
4930 child = child.nextElementSibling) { | |
4931 count++; | |
4932 } | |
4933 return count; | |
4934 }, | |
4935 | |
4936 get children() { | |
4937 var wrapperList = new NodeList(); | |
4938 var i = 0; | |
4939 for (var child = this.firstElementChild; | |
4940 child; | |
4941 child = child.nextElementSibling) { | |
4942 wrapperList[i++] = child; | |
4943 } | |
4944 wrapperList.length = i; | |
4945 return wrapperList; | |
4946 }, | |
4947 | |
4948 remove: function() { | |
4949 var p = this.parentNode; | |
4950 if (p) | |
4951 p.removeChild(this); | |
4952 } | |
4953 }; | |
4954 | |
4955 var ChildNodeInterface = { | |
4956 get nextElementSibling() { | |
4957 return forwardElement(this.nextSibling); | |
4958 }, | |
4959 | |
4960 get previousElementSibling() { | |
4961 return backwardsElement(this.previousSibling); | |
4962 } | |
4963 }; | |
4964 | |
4965 scope.ChildNodeInterface = ChildNodeInterface; | |
4966 scope.ParentNodeInterface = ParentNodeInterface; | |
4967 | |
4968 })(window.ShadowDOMPolyfill); | |
4969 | |
4970 // Copyright 2013 The Polymer Authors. All rights reserved. | |
4971 // Use of this source code is goverened by a BSD-style | |
4972 // license that can be found in the LICENSE file. | |
4973 | |
4974 (function(scope) { | |
4975 'use strict'; | |
4976 | |
4977 var ChildNodeInterface = scope.ChildNodeInterface; | |
4978 var Node = scope.wrappers.Node; | |
4979 var enqueueMutation = scope.enqueueMutation; | |
4980 var mixin = scope.mixin; | |
4981 var registerWrapper = scope.registerWrapper; | |
4982 var unsafeUnwrap = scope.unsafeUnwrap; | |
4983 | |
4984 var OriginalCharacterData = window.CharacterData; | |
4985 | |
4986 function CharacterData(node) { | |
4987 Node.call(this, node); | |
4988 } | |
4989 CharacterData.prototype = Object.create(Node.prototype); | |
4990 mixin(CharacterData.prototype, { | |
4991 get textContent() { | |
4992 return this.data; | |
4993 }, | |
4994 set textContent(value) { | |
4995 this.data = value; | |
4996 }, | |
4997 get data() { | |
4998 return unsafeUnwrap(this).data; | |
4999 }, | |
5000 set data(value) { | |
5001 var oldValue = unsafeUnwrap(this).data; | |
5002 enqueueMutation(this, 'characterData', { | |
5003 oldValue: oldValue | |
5004 }); | |
5005 unsafeUnwrap(this).data = value; | |
5006 } | |
5007 }); | |
5008 | |
5009 mixin(CharacterData.prototype, ChildNodeInterface); | |
5010 | |
5011 registerWrapper(OriginalCharacterData, CharacterData, | |
5012 document.createTextNode('')); | |
5013 | |
5014 scope.wrappers.CharacterData = CharacterData; | |
5015 })(window.ShadowDOMPolyfill); | |
5016 | |
5017 // Copyright 2014 The Polymer Authors. All rights reserved. | |
5018 // Use of this source code is goverened by a BSD-style | |
5019 // license that can be found in the LICENSE file. | |
5020 | |
5021 (function(scope) { | |
5022 'use strict'; | |
5023 | |
5024 var CharacterData = scope.wrappers.CharacterData; | |
5025 var enqueueMutation = scope.enqueueMutation; | |
5026 var mixin = scope.mixin; | |
5027 var registerWrapper = scope.registerWrapper; | |
5028 | |
5029 function toUInt32(x) { | |
5030 return x >>> 0; | |
5031 } | |
5032 | |
5033 var OriginalText = window.Text; | |
5034 | |
5035 function Text(node) { | |
5036 CharacterData.call(this, node); | |
5037 } | |
5038 Text.prototype = Object.create(CharacterData.prototype); | |
5039 mixin(Text.prototype, { | |
5040 splitText: function(offset) { | |
5041 offset = toUInt32(offset); | |
5042 var s = this.data; | |
5043 if (offset > s.length) | |
5044 throw new Error('IndexSizeError'); | |
5045 var head = s.slice(0, offset); | |
5046 var tail = s.slice(offset); | |
5047 this.data = head; | |
5048 var newTextNode = this.ownerDocument.createTextNode(tail); | |
5049 if (this.parentNode) | |
5050 this.parentNode.insertBefore(newTextNode, this.nextSibling); | |
5051 return newTextNode; | |
5052 } | |
5053 }); | |
5054 | |
5055 registerWrapper(OriginalText, Text, document.createTextNode('')); | |
5056 | |
5057 scope.wrappers.Text = Text; | |
5058 })(window.ShadowDOMPolyfill); | |
5059 | |
5060 // Copyright 2014 The Polymer Authors. All rights reserved. | |
5061 // Use of this source code is goverened by a BSD-style | |
5062 // license that can be found in the LICENSE file. | |
5063 | |
5064 (function(scope) { | |
5065 'use strict'; | |
5066 | |
5067 var setWrapper = scope.setWrapper; | |
5068 var unsafeUnwrap = scope.unsafeUnwrap; | |
5069 | |
5070 function invalidateClass(el) { | |
5071 scope.invalidateRendererBasedOnAttribute(el, 'class'); | |
5072 } | |
5073 | |
5074 function DOMTokenList(impl, ownerElement) { | |
5075 setWrapper(impl, this); | |
5076 this.ownerElement_ = ownerElement; | |
5077 } | |
5078 | |
5079 DOMTokenList.prototype = { | |
5080 constructor: DOMTokenList, | |
5081 get length() { | |
5082 return unsafeUnwrap(this).length; | |
5083 }, | |
5084 item: function(index) { | |
5085 return unsafeUnwrap(this).item(index); | |
5086 }, | |
5087 contains: function(token) { | |
5088 return unsafeUnwrap(this).contains(token); | |
5089 }, | |
5090 add: function() { | |
5091 unsafeUnwrap(this).add.apply(unsafeUnwrap(this), arguments); | |
5092 invalidateClass(this.ownerElement_); | |
5093 }, | |
5094 remove: function() { | |
5095 unsafeUnwrap(this).remove.apply(unsafeUnwrap(this), arguments); | |
5096 invalidateClass(this.ownerElement_); | |
5097 }, | |
5098 toggle: function(token) { | |
5099 var rv = unsafeUnwrap(this).toggle.apply(unsafeUnwrap(this), arguments); | |
5100 invalidateClass(this.ownerElement_); | |
5101 return rv; | |
5102 }, | |
5103 toString: function() { | |
5104 return unsafeUnwrap(this).toString(); | |
5105 } | |
5106 }; | |
5107 | |
5108 scope.wrappers.DOMTokenList = DOMTokenList; | |
5109 })(window.ShadowDOMPolyfill); | |
5110 | |
5111 // Copyright 2013 The Polymer Authors. All rights reserved. | |
5112 // Use of this source code is goverened by a BSD-style | |
5113 // license that can be found in the LICENSE file. | |
5114 | |
5115 (function(scope) { | |
5116 'use strict'; | |
5117 | |
5118 var ChildNodeInterface = scope.ChildNodeInterface; | |
5119 var GetElementsByInterface = scope.GetElementsByInterface; | |
5120 var Node = scope.wrappers.Node; | |
5121 var DOMTokenList = scope.wrappers.DOMTokenList; | |
5122 var ParentNodeInterface = scope.ParentNodeInterface; | |
5123 var SelectorsInterface = scope.SelectorsInterface; | |
5124 var addWrapNodeListMethod = scope.addWrapNodeListMethod; | |
5125 var enqueueMutation = scope.enqueueMutation; | |
5126 var mixin = scope.mixin; | |
5127 var oneOf = scope.oneOf; | |
5128 var registerWrapper = scope.registerWrapper; | |
5129 var unsafeUnwrap = scope.unsafeUnwrap; | |
5130 var wrappers = scope.wrappers; | |
5131 | |
5132 var OriginalElement = window.Element; | |
5133 | |
5134 var matchesNames = [ | |
5135 'matches', // needs to come first. | |
5136 'mozMatchesSelector', | |
5137 'msMatchesSelector', | |
5138 'webkitMatchesSelector', | |
5139 ].filter(function(name) { | |
5140 return OriginalElement.prototype[name]; | |
5141 }); | |
5142 | |
5143 var matchesName = matchesNames[0]; | |
5144 | |
5145 var originalMatches = OriginalElement.prototype[matchesName]; | |
5146 | |
5147 function invalidateRendererBasedOnAttribute(element, name) { | |
5148 // Only invalidate if parent node is a shadow host. | |
5149 var p = element.parentNode; | |
5150 if (!p || !p.shadowRoot) | |
5151 return; | |
5152 | |
5153 var renderer = scope.getRendererForHost(p); | |
5154 if (renderer.dependsOnAttribute(name)) | |
5155 renderer.invalidate(); | |
5156 } | |
5157 | |
5158 function enqueAttributeChange(element, name, oldValue) { | |
5159 // This is not fully spec compliant. We should use localName (which might | |
5160 // have a different case than name) and the namespace (which requires us | |
5161 // to get the Attr object). | |
5162 enqueueMutation(element, 'attributes', { | |
5163 name: name, | |
5164 namespace: null, | |
5165 oldValue: oldValue | |
5166 }); | |
5167 } | |
5168 | |
5169 var classListTable = new WeakMap(); | |
5170 | |
5171 function Element(node) { | |
5172 Node.call(this, node); | |
5173 } | |
5174 Element.prototype = Object.create(Node.prototype); | |
5175 mixin(Element.prototype, { | |
5176 createShadowRoot: function() { | |
5177 var newShadowRoot = new wrappers.ShadowRoot(this); | |
5178 unsafeUnwrap(this).polymerShadowRoot_ = newShadowRoot; | |
5179 | |
5180 var renderer = scope.getRendererForHost(this); | |
5181 renderer.invalidate(); | |
5182 | |
5183 return newShadowRoot; | |
5184 }, | |
5185 | |
5186 get shadowRoot() { | |
5187 return unsafeUnwrap(this).polymerShadowRoot_ || null; | |
5188 }, | |
5189 | |
5190 // getDestinationInsertionPoints added in ShadowRenderer.js | |
5191 | |
5192 setAttribute: function(name, value) { | |
5193 var oldValue = unsafeUnwrap(this).getAttribute(name); | |
5194 unsafeUnwrap(this).setAttribute(name, value); | |
5195 enqueAttributeChange(this, name, oldValue); | |
5196 invalidateRendererBasedOnAttribute(this, name); | |
5197 }, | |
5198 | |
5199 removeAttribute: function(name) { | |
5200 var oldValue = unsafeUnwrap(this).getAttribute(name); | |
5201 unsafeUnwrap(this).removeAttribute(name); | |
5202 enqueAttributeChange(this, name, oldValue); | |
5203 invalidateRendererBasedOnAttribute(this, name); | |
5204 }, | |
5205 | |
5206 matches: function(selector) { | |
5207 return originalMatches.call(unsafeUnwrap(this), selector); | |
5208 }, | |
5209 | |
5210 get classList() { | |
5211 var list = classListTable.get(this); | |
5212 if (!list) { | |
5213 classListTable.set(this, | |
5214 list = new DOMTokenList(unsafeUnwrap(this).classList, this)); | |
5215 } | |
5216 return list; | |
5217 }, | |
5218 | |
5219 get className() { | |
5220 return unsafeUnwrap(this).className; | |
5221 }, | |
5222 | |
5223 set className(v) { | |
5224 this.setAttribute('class', v); | |
5225 }, | |
5226 | |
5227 get id() { | |
5228 return unsafeUnwrap(this).id; | |
5229 }, | |
5230 | |
5231 set id(v) { | |
5232 this.setAttribute('id', v); | |
5233 } | |
5234 }); | |
5235 | |
5236 matchesNames.forEach(function(name) { | |
5237 if (name !== 'matches') { | |
5238 Element.prototype[name] = function(selector) { | |
5239 return this.matches(selector); | |
5240 }; | |
5241 } | |
5242 }); | |
5243 | |
5244 if (OriginalElement.prototype.webkitCreateShadowRoot) { | |
5245 Element.prototype.webkitCreateShadowRoot = | |
5246 Element.prototype.createShadowRoot; | |
5247 } | |
5248 | |
5249 mixin(Element.prototype, ChildNodeInterface); | |
5250 mixin(Element.prototype, GetElementsByInterface); | |
5251 mixin(Element.prototype, ParentNodeInterface); | |
5252 mixin(Element.prototype, SelectorsInterface); | |
5253 | |
5254 registerWrapper(OriginalElement, Element, | |
5255 document.createElementNS(null, 'x')); | |
5256 | |
5257 scope.invalidateRendererBasedOnAttribute = invalidateRendererBasedOnAttribute; | |
5258 scope.matchesNames = matchesNames; | |
5259 scope.wrappers.Element = Element; | |
5260 })(window.ShadowDOMPolyfill); | |
5261 | |
5262 // Copyright 2013 The Polymer Authors. All rights reserved. | |
5263 // Use of this source code is goverened by a BSD-style | |
5264 // license that can be found in the LICENSE file. | |
5265 | |
5266 (function(scope) { | |
5267 'use strict'; | |
5268 | |
5269 var Element = scope.wrappers.Element; | |
5270 var defineGetter = scope.defineGetter; | |
5271 var enqueueMutation = scope.enqueueMutation; | |
5272 var mixin = scope.mixin; | |
5273 var nodesWereAdded = scope.nodesWereAdded; | |
5274 var nodesWereRemoved = scope.nodesWereRemoved; | |
5275 var registerWrapper = scope.registerWrapper; | |
5276 var snapshotNodeList = scope.snapshotNodeList; | |
5277 var unsafeUnwrap = scope.unsafeUnwrap; | |
5278 var unwrap = scope.unwrap; | |
5279 var wrap = scope.wrap; | |
5280 var wrappers = scope.wrappers; | |
5281 | |
5282 ///////////////////////////////////////////////////////////////////////////// | |
5283 // innerHTML and outerHTML | |
5284 | |
5285 // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-end.html#es
capingString | |
5286 var escapeAttrRegExp = /[&\u00A0"]/g; | |
5287 var escapeDataRegExp = /[&\u00A0<>]/g; | |
5288 | |
5289 function escapeReplace(c) { | |
5290 switch (c) { | |
5291 case '&': | |
5292 return '&'; | |
5293 case '<': | |
5294 return '<'; | |
5295 case '>': | |
5296 return '>'; | |
5297 case '"': | |
5298 return '"' | |
5299 case '\u00A0': | |
5300 return ' '; | |
5301 } | |
5302 } | |
5303 | |
5304 function escapeAttr(s) { | |
5305 return s.replace(escapeAttrRegExp, escapeReplace); | |
5306 } | |
5307 | |
5308 function escapeData(s) { | |
5309 return s.replace(escapeDataRegExp, escapeReplace); | |
5310 } | |
5311 | |
5312 function makeSet(arr) { | |
5313 var set = {}; | |
5314 for (var i = 0; i < arr.length; i++) { | |
5315 set[arr[i]] = true; | |
5316 } | |
5317 return set; | |
5318 } | |
5319 | |
5320 // http://www.whatwg.org/specs/web-apps/current-work/#void-elements | |
5321 var voidElements = makeSet([ | |
5322 'area', | |
5323 'base', | |
5324 'br', | |
5325 'col', | |
5326 'command', | |
5327 'embed', | |
5328 'hr', | |
5329 'img', | |
5330 'input', | |
5331 'keygen', | |
5332 'link', | |
5333 'meta', | |
5334 'param', | |
5335 'source', | |
5336 'track', | |
5337 'wbr' | |
5338 ]); | |
5339 | |
5340 var plaintextParents = makeSet([ | |
5341 'style', | |
5342 'script', | |
5343 'xmp', | |
5344 'iframe', | |
5345 'noembed', | |
5346 'noframes', | |
5347 'plaintext', | |
5348 'noscript' | |
5349 ]); | |
5350 | |
5351 function getOuterHTML(node, parentNode) { | |
5352 switch (node.nodeType) { | |
5353 case Node.ELEMENT_NODE: | |
5354 var tagName = node.tagName.toLowerCase(); | |
5355 var s = '<' + tagName; | |
5356 var attrs = node.attributes; | |
5357 for (var i = 0, attr; attr = attrs[i]; i++) { | |
5358 s += ' ' + attr.name + '="' + escapeAttr(attr.value) + '"'; | |
5359 } | |
5360 s += '>'; | |
5361 if (voidElements[tagName]) | |
5362 return s; | |
5363 | |
5364 return s + getInnerHTML(node) + '</' + tagName + '>'; | |
5365 | |
5366 case Node.TEXT_NODE: | |
5367 var data = node.data; | |
5368 if (parentNode && plaintextParents[parentNode.localName]) | |
5369 return data; | |
5370 return escapeData(data); | |
5371 | |
5372 case Node.COMMENT_NODE: | |
5373 return '<!--' + node.data + '-->'; | |
5374 | |
5375 default: | |
5376 console.error(node); | |
5377 throw new Error('not implemented'); | |
5378 } | |
5379 } | |
5380 | |
5381 function getInnerHTML(node) { | |
5382 if (node instanceof wrappers.HTMLTemplateElement) | |
5383 node = node.content; | |
5384 | |
5385 var s = ''; | |
5386 for (var child = node.firstChild; child; child = child.nextSibling) { | |
5387 s += getOuterHTML(child, node); | |
5388 } | |
5389 return s; | |
5390 } | |
5391 | |
5392 function setInnerHTML(node, value, opt_tagName) { | |
5393 var tagName = opt_tagName || 'div'; | |
5394 node.textContent = ''; | |
5395 var tempElement = unwrap(node.ownerDocument.createElement(tagName)); | |
5396 tempElement.innerHTML = value; | |
5397 var firstChild; | |
5398 while (firstChild = tempElement.firstChild) { | |
5399 node.appendChild(wrap(firstChild)); | |
5400 } | |
5401 } | |
5402 | |
5403 // IE11 does not have MSIE in the user agent string. | |
5404 var oldIe = /MSIE/.test(navigator.userAgent); | |
5405 | |
5406 var OriginalHTMLElement = window.HTMLElement; | |
5407 var OriginalHTMLTemplateElement = window.HTMLTemplateElement; | |
5408 | |
5409 function HTMLElement(node) { | |
5410 Element.call(this, node); | |
5411 } | |
5412 HTMLElement.prototype = Object.create(Element.prototype); | |
5413 mixin(HTMLElement.prototype, { | |
5414 get innerHTML() { | |
5415 return getInnerHTML(this); | |
5416 }, | |
5417 set innerHTML(value) { | |
5418 // IE9 does not handle set innerHTML correctly on plaintextParents. It | |
5419 // creates element children. For example | |
5420 // | |
5421 // scriptElement.innerHTML = '<a>test</a>' | |
5422 // | |
5423 // Creates a single HTMLAnchorElement child. | |
5424 if (oldIe && plaintextParents[this.localName]) { | |
5425 this.textContent = value; | |
5426 return; | |
5427 } | |
5428 | |
5429 var removedNodes = snapshotNodeList(this.childNodes); | |
5430 | |
5431 if (this.invalidateShadowRenderer()) { | |
5432 if (this instanceof wrappers.HTMLTemplateElement) | |
5433 setInnerHTML(this.content, value); | |
5434 else | |
5435 setInnerHTML(this, value, this.tagName); | |
5436 | |
5437 // If we have a non native template element we need to handle this | |
5438 // manually since setting impl.innerHTML would add the html as direct | |
5439 // children and not be moved over to the content fragment. | |
5440 } else if (!OriginalHTMLTemplateElement && | |
5441 this instanceof wrappers.HTMLTemplateElement) { | |
5442 setInnerHTML(this.content, value); | |
5443 } else { | |
5444 unsafeUnwrap(this).innerHTML = value; | |
5445 } | |
5446 | |
5447 var addedNodes = snapshotNodeList(this.childNodes); | |
5448 | |
5449 enqueueMutation(this, 'childList', { | |
5450 addedNodes: addedNodes, | |
5451 removedNodes: removedNodes | |
5452 }); | |
5453 | |
5454 nodesWereRemoved(removedNodes); | |
5455 nodesWereAdded(addedNodes, this); | |
5456 }, | |
5457 | |
5458 get outerHTML() { | |
5459 return getOuterHTML(this, this.parentNode); | |
5460 }, | |
5461 set outerHTML(value) { | |
5462 var p = this.parentNode; | |
5463 if (p) { | |
5464 p.invalidateShadowRenderer(); | |
5465 var df = frag(p, value); | |
5466 p.replaceChild(df, this); | |
5467 } | |
5468 }, | |
5469 | |
5470 insertAdjacentHTML: function(position, text) { | |
5471 var contextElement, refNode; | |
5472 switch (String(position).toLowerCase()) { | |
5473 case 'beforebegin': | |
5474 contextElement = this.parentNode; | |
5475 refNode = this; | |
5476 break; | |
5477 case 'afterend': | |
5478 contextElement = this.parentNode; | |
5479 refNode = this.nextSibling; | |
5480 break; | |
5481 case 'afterbegin': | |
5482 contextElement = this; | |
5483 refNode = this.firstChild; | |
5484 break; | |
5485 case 'beforeend': | |
5486 contextElement = this; | |
5487 refNode = null; | |
5488 break; | |
5489 default: | |
5490 return; | |
5491 } | |
5492 | |
5493 var df = frag(contextElement, text); | |
5494 contextElement.insertBefore(df, refNode); | |
5495 }, | |
5496 | |
5497 get hidden() { | |
5498 return this.hasAttribute('hidden'); | |
5499 }, | |
5500 set hidden(v) { | |
5501 if (v) { | |
5502 this.setAttribute('hidden', ''); | |
5503 } else { | |
5504 this.removeAttribute('hidden'); | |
5505 } | |
5506 } | |
5507 }); | |
5508 | |
5509 function frag(contextElement, html) { | |
5510 // TODO(arv): This does not work with SVG and other non HTML elements. | |
5511 var p = unwrap(contextElement.cloneNode(false)); | |
5512 p.innerHTML = html; | |
5513 var df = unwrap(document.createDocumentFragment()); | |
5514 var c; | |
5515 while (c = p.firstChild) { | |
5516 df.appendChild(c); | |
5517 } | |
5518 return wrap(df); | |
5519 } | |
5520 | |
5521 function getter(name) { | |
5522 return function() { | |
5523 scope.renderAllPending(); | |
5524 return unsafeUnwrap(this)[name]; | |
5525 }; | |
5526 } | |
5527 | |
5528 function getterRequiresRendering(name) { | |
5529 defineGetter(HTMLElement, name, getter(name)); | |
5530 } | |
5531 | |
5532 [ | |
5533 'clientHeight', | |
5534 'clientLeft', | |
5535 'clientTop', | |
5536 'clientWidth', | |
5537 'offsetHeight', | |
5538 'offsetLeft', | |
5539 'offsetTop', | |
5540 'offsetWidth', | |
5541 'scrollHeight', | |
5542 'scrollWidth', | |
5543 ].forEach(getterRequiresRendering); | |
5544 | |
5545 function getterAndSetterRequiresRendering(name) { | |
5546 Object.defineProperty(HTMLElement.prototype, name, { | |
5547 get: getter(name), | |
5548 set: function(v) { | |
5549 scope.renderAllPending(); | |
5550 unsafeUnwrap(this)[name] = v; | |
5551 }, | |
5552 configurable: true, | |
5553 enumerable: true | |
5554 }); | |
5555 } | |
5556 | |
5557 [ | |
5558 'scrollLeft', | |
5559 'scrollTop', | |
5560 ].forEach(getterAndSetterRequiresRendering); | |
5561 | |
5562 function methodRequiresRendering(name) { | |
5563 Object.defineProperty(HTMLElement.prototype, name, { | |
5564 value: function() { | |
5565 scope.renderAllPending(); | |
5566 return unsafeUnwrap(this)[name].apply(unsafeUnwrap(this), arguments); | |
5567 }, | |
5568 configurable: true, | |
5569 enumerable: true | |
5570 }); | |
5571 } | |
5572 | |
5573 [ | |
5574 'getBoundingClientRect', | |
5575 'getClientRects', | |
5576 'scrollIntoView' | |
5577 ].forEach(methodRequiresRendering); | |
5578 | |
5579 // HTMLElement is abstract so we use a subclass that has no members. | |
5580 registerWrapper(OriginalHTMLElement, HTMLElement, | |
5581 document.createElement('b')); | |
5582 | |
5583 scope.wrappers.HTMLElement = HTMLElement; | |
5584 | |
5585 // TODO: Find a better way to share these two with WrapperShadowRoot. | |
5586 scope.getInnerHTML = getInnerHTML; | |
5587 scope.setInnerHTML = setInnerHTML | |
5588 })(window.ShadowDOMPolyfill); | |
5589 | |
5590 // Copyright 2013 The Polymer Authors. All rights reserved. | |
5591 // Use of this source code is goverened by a BSD-style | |
5592 // license that can be found in the LICENSE file. | |
5593 | |
5594 (function(scope) { | |
5595 'use strict'; | |
5596 | |
5597 var HTMLElement = scope.wrappers.HTMLElement; | |
5598 var mixin = scope.mixin; | |
5599 var registerWrapper = scope.registerWrapper; | |
5600 var unsafeUnwrap = scope.unsafeUnwrap; | |
5601 var wrap = scope.wrap; | |
5602 | |
5603 var OriginalHTMLCanvasElement = window.HTMLCanvasElement; | |
5604 | |
5605 function HTMLCanvasElement(node) { | |
5606 HTMLElement.call(this, node); | |
5607 } | |
5608 HTMLCanvasElement.prototype = Object.create(HTMLElement.prototype); | |
5609 | |
5610 mixin(HTMLCanvasElement.prototype, { | |
5611 getContext: function() { | |
5612 var context = unsafeUnwrap(this).getContext.apply(unsafeUnwrap(this), argu
ments); | |
5613 return context && wrap(context); | |
5614 } | |
5615 }); | |
5616 | |
5617 registerWrapper(OriginalHTMLCanvasElement, HTMLCanvasElement, | |
5618 document.createElement('canvas')); | |
5619 | |
5620 scope.wrappers.HTMLCanvasElement = HTMLCanvasElement; | |
5621 })(window.ShadowDOMPolyfill); | |
5622 | |
5623 // Copyright 2013 The Polymer Authors. All rights reserved. | |
5624 // Use of this source code is goverened by a BSD-style | |
5625 // license that can be found in the LICENSE file. | |
5626 | |
5627 (function(scope) { | |
5628 'use strict'; | |
5629 | |
5630 var HTMLElement = scope.wrappers.HTMLElement; | |
5631 var mixin = scope.mixin; | |
5632 var registerWrapper = scope.registerWrapper; | |
5633 | |
5634 var OriginalHTMLContentElement = window.HTMLContentElement; | |
5635 | |
5636 function HTMLContentElement(node) { | |
5637 HTMLElement.call(this, node); | |
5638 } | |
5639 HTMLContentElement.prototype = Object.create(HTMLElement.prototype); | |
5640 mixin(HTMLContentElement.prototype, { | |
5641 constructor: HTMLContentElement, | |
5642 | |
5643 get select() { | |
5644 return this.getAttribute('select'); | |
5645 }, | |
5646 set select(value) { | |
5647 this.setAttribute('select', value); | |
5648 }, | |
5649 | |
5650 setAttribute: function(n, v) { | |
5651 HTMLElement.prototype.setAttribute.call(this, n, v); | |
5652 if (String(n).toLowerCase() === 'select') | |
5653 this.invalidateShadowRenderer(true); | |
5654 } | |
5655 | |
5656 // getDistributedNodes is added in ShadowRenderer | |
5657 }); | |
5658 | |
5659 if (OriginalHTMLContentElement) | |
5660 registerWrapper(OriginalHTMLContentElement, HTMLContentElement); | |
5661 | |
5662 scope.wrappers.HTMLContentElement = HTMLContentElement; | |
5663 })(window.ShadowDOMPolyfill); | |
5664 | |
5665 /* | |
5666 * Copyright 2014 The Polymer Authors. All rights reserved. | |
5667 * Use of this source code is governed by a BSD-style | |
5668 * license that can be found in the LICENSE file. | |
5669 */ | |
5670 | |
5671 (function(scope) { | |
5672 'use strict'; | |
5673 | |
5674 var HTMLElement = scope.wrappers.HTMLElement; | |
5675 var mixin = scope.mixin; | |
5676 var registerWrapper = scope.registerWrapper; | |
5677 var wrapHTMLCollection = scope.wrapHTMLCollection; | |
5678 var unwrap = scope.unwrap; | |
5679 | |
5680 var OriginalHTMLFormElement = window.HTMLFormElement; | |
5681 | |
5682 function HTMLFormElement(node) { | |
5683 HTMLElement.call(this, node); | |
5684 } | |
5685 HTMLFormElement.prototype = Object.create(HTMLElement.prototype); | |
5686 mixin(HTMLFormElement.prototype, { | |
5687 get elements() { | |
5688 // Note: technically this should be an HTMLFormControlsCollection, but | |
5689 // that inherits from HTMLCollection, so should be good enough. Spec: | |
5690 // http://www.whatwg.org/specs/web-apps/current-work/multipage/common-dom-
interfaces.html#htmlformcontrolscollection | |
5691 return wrapHTMLCollection(unwrap(this).elements); | |
5692 } | |
5693 }); | |
5694 | |
5695 registerWrapper(OriginalHTMLFormElement, HTMLFormElement, | |
5696 document.createElement('form')); | |
5697 | |
5698 scope.wrappers.HTMLFormElement = HTMLFormElement; | |
5699 })(window.ShadowDOMPolyfill); | |
5700 | |
5701 // Copyright 2013 The Polymer Authors. All rights reserved. | |
5702 // Use of this source code is goverened by a BSD-style | |
5703 // license that can be found in the LICENSE file. | |
5704 | |
5705 (function(scope) { | |
5706 'use strict'; | |
5707 | |
5708 var HTMLElement = scope.wrappers.HTMLElement; | |
5709 var registerWrapper = scope.registerWrapper; | |
5710 var unwrap = scope.unwrap; | |
5711 var rewrap = scope.rewrap; | |
5712 | |
5713 var OriginalHTMLImageElement = window.HTMLImageElement; | |
5714 | |
5715 function HTMLImageElement(node) { | |
5716 HTMLElement.call(this, node); | |
5717 } | |
5718 HTMLImageElement.prototype = Object.create(HTMLElement.prototype); | |
5719 | |
5720 registerWrapper(OriginalHTMLImageElement, HTMLImageElement, | |
5721 document.createElement('img')); | |
5722 | |
5723 function Image(width, height) { | |
5724 if (!(this instanceof Image)) { | |
5725 throw new TypeError( | |
5726 'DOM object constructor cannot be called as a function.'); | |
5727 } | |
5728 | |
5729 var node = unwrap(document.createElement('img')); | |
5730 HTMLElement.call(this, node); | |
5731 rewrap(node, this); | |
5732 | |
5733 if (width !== undefined) | |
5734 node.width = width; | |
5735 if (height !== undefined) | |
5736 node.height = height; | |
5737 } | |
5738 | |
5739 Image.prototype = HTMLImageElement.prototype; | |
5740 | |
5741 scope.wrappers.HTMLImageElement = HTMLImageElement; | |
5742 scope.wrappers.Image = Image; | |
5743 })(window.ShadowDOMPolyfill); | |
5744 | |
5745 // Copyright 2013 The Polymer Authors. All rights reserved. | |
5746 // Use of this source code is goverened by a BSD-style | |
5747 // license that can be found in the LICENSE file. | |
5748 | |
5749 (function(scope) { | |
5750 'use strict'; | |
5751 | |
5752 var HTMLElement = scope.wrappers.HTMLElement; | |
5753 var mixin = scope.mixin; | |
5754 var NodeList = scope.wrappers.NodeList; | |
5755 var registerWrapper = scope.registerWrapper; | |
5756 | |
5757 var OriginalHTMLShadowElement = window.HTMLShadowElement; | |
5758 | |
5759 function HTMLShadowElement(node) { | |
5760 HTMLElement.call(this, node); | |
5761 } | |
5762 HTMLShadowElement.prototype = Object.create(HTMLElement.prototype); | |
5763 HTMLShadowElement.prototype.constructor = HTMLShadowElement; | |
5764 | |
5765 // getDistributedNodes is added in ShadowRenderer | |
5766 | |
5767 if (OriginalHTMLShadowElement) | |
5768 registerWrapper(OriginalHTMLShadowElement, HTMLShadowElement); | |
5769 | |
5770 scope.wrappers.HTMLShadowElement = HTMLShadowElement; | |
5771 })(window.ShadowDOMPolyfill); | |
5772 | |
5773 // Copyright 2013 The Polymer Authors. All rights reserved. | |
5774 // Use of this source code is goverened by a BSD-style | |
5775 // license that can be found in the LICENSE file. | |
5776 | |
5777 (function(scope) { | |
5778 'use strict'; | |
5779 | |
5780 var HTMLElement = scope.wrappers.HTMLElement; | |
5781 var mixin = scope.mixin; | |
5782 var registerWrapper = scope.registerWrapper; | |
5783 var unsafeUnwrap = scope.unsafeUnwrap; | |
5784 var unwrap = scope.unwrap; | |
5785 var wrap = scope.wrap; | |
5786 | |
5787 var contentTable = new WeakMap(); | |
5788 var templateContentsOwnerTable = new WeakMap(); | |
5789 | |
5790 // http://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/templates/index.html#
dfn-template-contents-owner | |
5791 function getTemplateContentsOwner(doc) { | |
5792 if (!doc.defaultView) | |
5793 return doc; | |
5794 var d = templateContentsOwnerTable.get(doc); | |
5795 if (!d) { | |
5796 // TODO(arv): This should either be a Document or HTMLDocument depending | |
5797 // on doc. | |
5798 d = doc.implementation.createHTMLDocument(''); | |
5799 while (d.lastChild) { | |
5800 d.removeChild(d.lastChild); | |
5801 } | |
5802 templateContentsOwnerTable.set(doc, d); | |
5803 } | |
5804 return d; | |
5805 } | |
5806 | |
5807 function extractContent(templateElement) { | |
5808 // templateElement is not a wrapper here. | |
5809 var doc = getTemplateContentsOwner(templateElement.ownerDocument); | |
5810 var df = unwrap(doc.createDocumentFragment()); | |
5811 var child; | |
5812 while (child = templateElement.firstChild) { | |
5813 df.appendChild(child); | |
5814 } | |
5815 return df; | |
5816 } | |
5817 | |
5818 var OriginalHTMLTemplateElement = window.HTMLTemplateElement; | |
5819 | |
5820 function HTMLTemplateElement(node) { | |
5821 HTMLElement.call(this, node); | |
5822 if (!OriginalHTMLTemplateElement) { | |
5823 var content = extractContent(node); | |
5824 contentTable.set(this, wrap(content)); | |
5825 } | |
5826 } | |
5827 HTMLTemplateElement.prototype = Object.create(HTMLElement.prototype); | |
5828 | |
5829 mixin(HTMLTemplateElement.prototype, { | |
5830 constructor: HTMLTemplateElement, | |
5831 get content() { | |
5832 if (OriginalHTMLTemplateElement) | |
5833 return wrap(unsafeUnwrap(this).content); | |
5834 return contentTable.get(this); | |
5835 }, | |
5836 | |
5837 // TODO(arv): cloneNode needs to clone content. | |
5838 | |
5839 }); | |
5840 | |
5841 if (OriginalHTMLTemplateElement) | |
5842 registerWrapper(OriginalHTMLTemplateElement, HTMLTemplateElement); | |
5843 | |
5844 scope.wrappers.HTMLTemplateElement = HTMLTemplateElement; | |
5845 })(window.ShadowDOMPolyfill); | |
5846 | |
5847 // Copyright 2013 The Polymer Authors. All rights reserved. | |
5848 // Use of this source code is goverened by a BSD-style | |
5849 // license that can be found in the LICENSE file. | |
5850 | |
5851 (function(scope) { | |
5852 'use strict'; | |
5853 | |
5854 var HTMLElement = scope.wrappers.HTMLElement; | |
5855 var registerWrapper = scope.registerWrapper; | |
5856 | |
5857 var OriginalHTMLMediaElement = window.HTMLMediaElement; | |
5858 | |
5859 if (!OriginalHTMLMediaElement) return; | |
5860 | |
5861 function HTMLMediaElement(node) { | |
5862 HTMLElement.call(this, node); | |
5863 } | |
5864 HTMLMediaElement.prototype = Object.create(HTMLElement.prototype); | |
5865 | |
5866 registerWrapper(OriginalHTMLMediaElement, HTMLMediaElement, | |
5867 document.createElement('audio')); | |
5868 | |
5869 scope.wrappers.HTMLMediaElement = HTMLMediaElement; | |
5870 })(window.ShadowDOMPolyfill); | |
5871 | |
5872 // Copyright 2013 The Polymer Authors. All rights reserved. | |
5873 // Use of this source code is goverened by a BSD-style | |
5874 // license that can be found in the LICENSE file. | |
5875 | |
5876 (function(scope) { | |
5877 'use strict'; | |
5878 | |
5879 var HTMLMediaElement = scope.wrappers.HTMLMediaElement; | |
5880 var registerWrapper = scope.registerWrapper; | |
5881 var unwrap = scope.unwrap; | |
5882 var rewrap = scope.rewrap; | |
5883 | |
5884 var OriginalHTMLAudioElement = window.HTMLAudioElement; | |
5885 | |
5886 if (!OriginalHTMLAudioElement) return; | |
5887 | |
5888 function HTMLAudioElement(node) { | |
5889 HTMLMediaElement.call(this, node); | |
5890 } | |
5891 HTMLAudioElement.prototype = Object.create(HTMLMediaElement.prototype); | |
5892 | |
5893 registerWrapper(OriginalHTMLAudioElement, HTMLAudioElement, | |
5894 document.createElement('audio')); | |
5895 | |
5896 function Audio(src) { | |
5897 if (!(this instanceof Audio)) { | |
5898 throw new TypeError( | |
5899 'DOM object constructor cannot be called as a function.'); | |
5900 } | |
5901 | |
5902 var node = unwrap(document.createElement('audio')); | |
5903 HTMLMediaElement.call(this, node); | |
5904 rewrap(node, this); | |
5905 | |
5906 node.setAttribute('preload', 'auto'); | |
5907 if (src !== undefined) | |
5908 node.setAttribute('src', src); | |
5909 } | |
5910 | |
5911 Audio.prototype = HTMLAudioElement.prototype; | |
5912 | |
5913 scope.wrappers.HTMLAudioElement = HTMLAudioElement; | |
5914 scope.wrappers.Audio = Audio; | |
5915 })(window.ShadowDOMPolyfill); | |
5916 | |
5917 // Copyright 2013 The Polymer Authors. All rights reserved. | |
5918 // Use of this source code is goverened by a BSD-style | |
5919 // license that can be found in the LICENSE file. | |
5920 | |
5921 (function(scope) { | |
5922 'use strict'; | |
5923 | |
5924 var HTMLElement = scope.wrappers.HTMLElement; | |
5925 var mixin = scope.mixin; | |
5926 var registerWrapper = scope.registerWrapper; | |
5927 var rewrap = scope.rewrap; | |
5928 var unwrap = scope.unwrap; | |
5929 var wrap = scope.wrap; | |
5930 | |
5931 var OriginalHTMLOptionElement = window.HTMLOptionElement; | |
5932 | |
5933 function trimText(s) { | |
5934 return s.replace(/\s+/g, ' ').trim(); | |
5935 } | |
5936 | |
5937 function HTMLOptionElement(node) { | |
5938 HTMLElement.call(this, node); | |
5939 } | |
5940 HTMLOptionElement.prototype = Object.create(HTMLElement.prototype); | |
5941 mixin(HTMLOptionElement.prototype, { | |
5942 get text() { | |
5943 return trimText(this.textContent); | |
5944 }, | |
5945 set text(value) { | |
5946 this.textContent = trimText(String(value)); | |
5947 }, | |
5948 get form() { | |
5949 return wrap(unwrap(this).form); | |
5950 } | |
5951 }); | |
5952 | |
5953 registerWrapper(OriginalHTMLOptionElement, HTMLOptionElement, | |
5954 document.createElement('option')); | |
5955 | |
5956 function Option(text, value, defaultSelected, selected) { | |
5957 if (!(this instanceof Option)) { | |
5958 throw new TypeError( | |
5959 'DOM object constructor cannot be called as a function.'); | |
5960 } | |
5961 | |
5962 var node = unwrap(document.createElement('option')); | |
5963 HTMLElement.call(this, node); | |
5964 rewrap(node, this); | |
5965 | |
5966 if (text !== undefined) | |
5967 node.text = text; | |
5968 if (value !== undefined) | |
5969 node.setAttribute('value', value); | |
5970 if (defaultSelected === true) | |
5971 node.setAttribute('selected', ''); | |
5972 node.selected = selected === true; | |
5973 } | |
5974 | |
5975 Option.prototype = HTMLOptionElement.prototype; | |
5976 | |
5977 scope.wrappers.HTMLOptionElement = HTMLOptionElement; | |
5978 scope.wrappers.Option = Option; | |
5979 })(window.ShadowDOMPolyfill); | |
5980 | |
5981 // Copyright 2014 The Polymer Authors. All rights reserved. | |
5982 // Use of this source code is goverened by a BSD-style | |
5983 // license that can be found in the LICENSE file. | |
5984 | |
5985 (function(scope) { | |
5986 'use strict'; | |
5987 | |
5988 var HTMLElement = scope.wrappers.HTMLElement; | |
5989 var mixin = scope.mixin; | |
5990 var registerWrapper = scope.registerWrapper; | |
5991 var unwrap = scope.unwrap; | |
5992 var wrap = scope.wrap; | |
5993 | |
5994 var OriginalHTMLSelectElement = window.HTMLSelectElement; | |
5995 | |
5996 function HTMLSelectElement(node) { | |
5997 HTMLElement.call(this, node); | |
5998 } | |
5999 HTMLSelectElement.prototype = Object.create(HTMLElement.prototype); | |
6000 mixin(HTMLSelectElement.prototype, { | |
6001 add: function(element, before) { | |
6002 if (typeof before === 'object') // also includes null | |
6003 before = unwrap(before); | |
6004 unwrap(this).add(unwrap(element), before); | |
6005 }, | |
6006 | |
6007 remove: function(indexOrNode) { | |
6008 // Spec only allows index but implementations allow index or node. | |
6009 // remove() is also allowed which is same as remove(undefined) | |
6010 if (indexOrNode === undefined) { | |
6011 HTMLElement.prototype.remove.call(this); | |
6012 return; | |
6013 } | |
6014 | |
6015 if (typeof indexOrNode === 'object') | |
6016 indexOrNode = unwrap(indexOrNode); | |
6017 | |
6018 unwrap(this).remove(indexOrNode); | |
6019 }, | |
6020 | |
6021 get form() { | |
6022 return wrap(unwrap(this).form); | |
6023 } | |
6024 }); | |
6025 | |
6026 registerWrapper(OriginalHTMLSelectElement, HTMLSelectElement, | |
6027 document.createElement('select')); | |
6028 | |
6029 scope.wrappers.HTMLSelectElement = HTMLSelectElement; | |
6030 })(window.ShadowDOMPolyfill); | |
6031 | |
6032 /* | |
6033 * Copyright 2014 The Polymer Authors. All rights reserved. | |
6034 * Use of this source code is goverened by a BSD-style | |
6035 * license that can be found in the LICENSE file. | |
6036 */ | |
6037 | |
6038 (function(scope) { | |
6039 'use strict'; | |
6040 | |
6041 var HTMLElement = scope.wrappers.HTMLElement; | |
6042 var mixin = scope.mixin; | |
6043 var registerWrapper = scope.registerWrapper; | |
6044 var unwrap = scope.unwrap; | |
6045 var wrap = scope.wrap; | |
6046 var wrapHTMLCollection = scope.wrapHTMLCollection; | |
6047 | |
6048 var OriginalHTMLTableElement = window.HTMLTableElement; | |
6049 | |
6050 function HTMLTableElement(node) { | |
6051 HTMLElement.call(this, node); | |
6052 } | |
6053 HTMLTableElement.prototype = Object.create(HTMLElement.prototype); | |
6054 mixin(HTMLTableElement.prototype, { | |
6055 get caption() { | |
6056 return wrap(unwrap(this).caption); | |
6057 }, | |
6058 createCaption: function() { | |
6059 return wrap(unwrap(this).createCaption()); | |
6060 }, | |
6061 | |
6062 get tHead() { | |
6063 return wrap(unwrap(this).tHead); | |
6064 }, | |
6065 createTHead: function() { | |
6066 return wrap(unwrap(this).createTHead()); | |
6067 }, | |
6068 | |
6069 createTFoot: function() { | |
6070 return wrap(unwrap(this).createTFoot()); | |
6071 }, | |
6072 get tFoot() { | |
6073 return wrap(unwrap(this).tFoot); | |
6074 }, | |
6075 | |
6076 get tBodies() { | |
6077 return wrapHTMLCollection(unwrap(this).tBodies); | |
6078 }, | |
6079 createTBody: function() { | |
6080 return wrap(unwrap(this).createTBody()); | |
6081 }, | |
6082 | |
6083 get rows() { | |
6084 return wrapHTMLCollection(unwrap(this).rows); | |
6085 }, | |
6086 insertRow: function(index) { | |
6087 return wrap(unwrap(this).insertRow(index)); | |
6088 } | |
6089 }); | |
6090 | |
6091 registerWrapper(OriginalHTMLTableElement, HTMLTableElement, | |
6092 document.createElement('table')); | |
6093 | |
6094 scope.wrappers.HTMLTableElement = HTMLTableElement; | |
6095 })(window.ShadowDOMPolyfill); | |
6096 | |
6097 /* | |
6098 * Copyright 2014 The Polymer Authors. All rights reserved. | |
6099 * Use of this source code is goverened by a BSD-style | |
6100 * license that can be found in the LICENSE file. | |
6101 */ | |
6102 | |
6103 (function(scope) { | |
6104 'use strict'; | |
6105 | |
6106 var HTMLElement = scope.wrappers.HTMLElement; | |
6107 var mixin = scope.mixin; | |
6108 var registerWrapper = scope.registerWrapper; | |
6109 var wrapHTMLCollection = scope.wrapHTMLCollection; | |
6110 var unwrap = scope.unwrap; | |
6111 var wrap = scope.wrap; | |
6112 | |
6113 var OriginalHTMLTableSectionElement = window.HTMLTableSectionElement; | |
6114 | |
6115 function HTMLTableSectionElement(node) { | |
6116 HTMLElement.call(this, node); | |
6117 } | |
6118 HTMLTableSectionElement.prototype = Object.create(HTMLElement.prototype); | |
6119 mixin(HTMLTableSectionElement.prototype, { | |
6120 constructor: HTMLTableSectionElement, | |
6121 get rows() { | |
6122 return wrapHTMLCollection(unwrap(this).rows); | |
6123 }, | |
6124 insertRow: function(index) { | |
6125 return wrap(unwrap(this).insertRow(index)); | |
6126 } | |
6127 }); | |
6128 | |
6129 registerWrapper(OriginalHTMLTableSectionElement, HTMLTableSectionElement, | |
6130 document.createElement('thead')); | |
6131 | |
6132 scope.wrappers.HTMLTableSectionElement = HTMLTableSectionElement; | |
6133 })(window.ShadowDOMPolyfill); | |
6134 | |
6135 /* | |
6136 * Copyright 2014 The Polymer Authors. All rights reserved. | |
6137 * Use of this source code is goverened by a BSD-style | |
6138 * license that can be found in the LICENSE file. | |
6139 */ | |
6140 | |
6141 (function(scope) { | |
6142 'use strict'; | |
6143 | |
6144 var HTMLElement = scope.wrappers.HTMLElement; | |
6145 var mixin = scope.mixin; | |
6146 var registerWrapper = scope.registerWrapper; | |
6147 var wrapHTMLCollection = scope.wrapHTMLCollection; | |
6148 var unwrap = scope.unwrap; | |
6149 var wrap = scope.wrap; | |
6150 | |
6151 var OriginalHTMLTableRowElement = window.HTMLTableRowElement; | |
6152 | |
6153 function HTMLTableRowElement(node) { | |
6154 HTMLElement.call(this, node); | |
6155 } | |
6156 HTMLTableRowElement.prototype = Object.create(HTMLElement.prototype); | |
6157 mixin(HTMLTableRowElement.prototype, { | |
6158 get cells() { | |
6159 return wrapHTMLCollection(unwrap(this).cells); | |
6160 }, | |
6161 | |
6162 insertCell: function(index) { | |
6163 return wrap(unwrap(this).insertCell(index)); | |
6164 } | |
6165 }); | |
6166 | |
6167 registerWrapper(OriginalHTMLTableRowElement, HTMLTableRowElement, | |
6168 document.createElement('tr')); | |
6169 | |
6170 scope.wrappers.HTMLTableRowElement = HTMLTableRowElement; | |
6171 })(window.ShadowDOMPolyfill); | |
6172 | |
6173 // Copyright 2013 The Polymer Authors. All rights reserved. | |
6174 // Use of this source code is goverened by a BSD-style | |
6175 // license that can be found in the LICENSE file. | |
6176 | |
6177 (function(scope) { | |
6178 'use strict'; | |
6179 | |
6180 var HTMLContentElement = scope.wrappers.HTMLContentElement; | |
6181 var HTMLElement = scope.wrappers.HTMLElement; | |
6182 var HTMLShadowElement = scope.wrappers.HTMLShadowElement; | |
6183 var HTMLTemplateElement = scope.wrappers.HTMLTemplateElement; | |
6184 var mixin = scope.mixin; | |
6185 var registerWrapper = scope.registerWrapper; | |
6186 | |
6187 var OriginalHTMLUnknownElement = window.HTMLUnknownElement; | |
6188 | |
6189 function HTMLUnknownElement(node) { | |
6190 switch (node.localName) { | |
6191 case 'content': | |
6192 return new HTMLContentElement(node); | |
6193 case 'shadow': | |
6194 return new HTMLShadowElement(node); | |
6195 case 'template': | |
6196 return new HTMLTemplateElement(node); | |
6197 } | |
6198 HTMLElement.call(this, node); | |
6199 } | |
6200 HTMLUnknownElement.prototype = Object.create(HTMLElement.prototype); | |
6201 registerWrapper(OriginalHTMLUnknownElement, HTMLUnknownElement); | |
6202 scope.wrappers.HTMLUnknownElement = HTMLUnknownElement; | |
6203 })(window.ShadowDOMPolyfill); | |
6204 | |
6205 // Copyright 2014 The Polymer Authors. All rights reserved. | |
6206 // Use of this source code is goverened by a BSD-style | |
6207 // license that can be found in the LICENSE file. | |
6208 | |
6209 (function(scope) { | |
6210 'use strict'; | |
6211 | |
6212 var Element = scope.wrappers.Element; | |
6213 var HTMLElement = scope.wrappers.HTMLElement; | |
6214 var registerObject = scope.registerObject; | |
6215 | |
6216 var SVG_NS = 'http://www.w3.org/2000/svg'; | |
6217 var svgTitleElement = document.createElementNS(SVG_NS, 'title'); | |
6218 var SVGTitleElement = registerObject(svgTitleElement); | |
6219 var SVGElement = Object.getPrototypeOf(SVGTitleElement.prototype).constructor; | |
6220 | |
6221 // IE11 does not have classList for SVG elements. The spec says that classList | |
6222 // is an accessor on Element, but IE11 puts classList on HTMLElement, leaving | |
6223 // SVGElement without a classList property. We therefore move the accessor for | |
6224 // IE11. | |
6225 if (!('classList' in svgTitleElement)) { | |
6226 var descr = Object.getOwnPropertyDescriptor(Element.prototype, 'classList'); | |
6227 Object.defineProperty(HTMLElement.prototype, 'classList', descr); | |
6228 delete Element.prototype.classList; | |
6229 } | |
6230 | |
6231 scope.wrappers.SVGElement = SVGElement; | |
6232 })(window.ShadowDOMPolyfill); | |
6233 | |
6234 // Copyright 2014 The Polymer Authors. All rights reserved. | |
6235 // Use of this source code is goverened by a BSD-style | |
6236 // license that can be found in the LICENSE file. | |
6237 | |
6238 (function(scope) { | |
6239 'use strict'; | |
6240 | |
6241 var mixin = scope.mixin; | |
6242 var registerWrapper = scope.registerWrapper; | |
6243 var unwrap = scope.unwrap; | |
6244 var wrap = scope.wrap; | |
6245 | |
6246 var OriginalSVGUseElement = window.SVGUseElement; | |
6247 | |
6248 // IE uses SVGElement as parent interface, SVG2 (Blink & Gecko) uses | |
6249 // SVGGraphicsElement. Use the <g> element to get the right prototype. | |
6250 | |
6251 var SVG_NS = 'http://www.w3.org/2000/svg'; | |
6252 var gWrapper = wrap(document.createElementNS(SVG_NS, 'g')); | |
6253 var useElement = document.createElementNS(SVG_NS, 'use'); | |
6254 var SVGGElement = gWrapper.constructor; | |
6255 var parentInterfacePrototype = Object.getPrototypeOf(SVGGElement.prototype); | |
6256 var parentInterface = parentInterfacePrototype.constructor; | |
6257 | |
6258 function SVGUseElement(impl) { | |
6259 parentInterface.call(this, impl); | |
6260 } | |
6261 | |
6262 SVGUseElement.prototype = Object.create(parentInterfacePrototype); | |
6263 | |
6264 // Firefox does not expose instanceRoot. | |
6265 if ('instanceRoot' in useElement) { | |
6266 mixin(SVGUseElement.prototype, { | |
6267 get instanceRoot() { | |
6268 return wrap(unwrap(this).instanceRoot); | |
6269 }, | |
6270 get animatedInstanceRoot() { | |
6271 return wrap(unwrap(this).animatedInstanceRoot); | |
6272 }, | |
6273 }); | |
6274 } | |
6275 | |
6276 registerWrapper(OriginalSVGUseElement, SVGUseElement, useElement); | |
6277 | |
6278 scope.wrappers.SVGUseElement = SVGUseElement; | |
6279 })(window.ShadowDOMPolyfill); | |
6280 | |
6281 // Copyright 2014 The Polymer Authors. All rights reserved. | |
6282 // Use of this source code is goverened by a BSD-style | |
6283 // license that can be found in the LICENSE file. | |
6284 | |
6285 (function(scope) { | |
6286 'use strict'; | |
6287 | |
6288 var EventTarget = scope.wrappers.EventTarget; | |
6289 var mixin = scope.mixin; | |
6290 var registerWrapper = scope.registerWrapper; | |
6291 var unsafeUnwrap = scope.unsafeUnwrap; | |
6292 var wrap = scope.wrap; | |
6293 | |
6294 var OriginalSVGElementInstance = window.SVGElementInstance; | |
6295 if (!OriginalSVGElementInstance) | |
6296 return; | |
6297 | |
6298 function SVGElementInstance(impl) { | |
6299 EventTarget.call(this, impl); | |
6300 } | |
6301 | |
6302 SVGElementInstance.prototype = Object.create(EventTarget.prototype); | |
6303 mixin(SVGElementInstance.prototype, { | |
6304 /** @type {SVGElement} */ | |
6305 get correspondingElement() { | |
6306 return wrap(unsafeUnwrap(this).correspondingElement); | |
6307 }, | |
6308 | |
6309 /** @type {SVGUseElement} */ | |
6310 get correspondingUseElement() { | |
6311 return wrap(unsafeUnwrap(this).correspondingUseElement); | |
6312 }, | |
6313 | |
6314 /** @type {SVGElementInstance} */ | |
6315 get parentNode() { | |
6316 return wrap(unsafeUnwrap(this).parentNode); | |
6317 }, | |
6318 | |
6319 /** @type {SVGElementInstanceList} */ | |
6320 get childNodes() { | |
6321 throw new Error('Not implemented'); | |
6322 }, | |
6323 | |
6324 /** @type {SVGElementInstance} */ | |
6325 get firstChild() { | |
6326 return wrap(unsafeUnwrap(this).firstChild); | |
6327 }, | |
6328 | |
6329 /** @type {SVGElementInstance} */ | |
6330 get lastChild() { | |
6331 return wrap(unsafeUnwrap(this).lastChild); | |
6332 }, | |
6333 | |
6334 /** @type {SVGElementInstance} */ | |
6335 get previousSibling() { | |
6336 return wrap(unsafeUnwrap(this).previousSibling); | |
6337 }, | |
6338 | |
6339 /** @type {SVGElementInstance} */ | |
6340 get nextSibling() { | |
6341 return wrap(unsafeUnwrap(this).nextSibling); | |
6342 } | |
6343 }); | |
6344 | |
6345 registerWrapper(OriginalSVGElementInstance, SVGElementInstance); | |
6346 | |
6347 scope.wrappers.SVGElementInstance = SVGElementInstance; | |
6348 })(window.ShadowDOMPolyfill); | |
6349 | |
6350 // Copyright 2013 The Polymer Authors. All rights reserved. | |
6351 // Use of this source code is goverened by a BSD-style | |
6352 // license that can be found in the LICENSE file. | |
6353 | |
6354 (function(scope) { | |
6355 'use strict'; | |
6356 | |
6357 var mixin = scope.mixin; | |
6358 var registerWrapper = scope.registerWrapper; | |
6359 var setWrapper = scope.setWrapper; | |
6360 var unsafeUnwrap = scope.unsafeUnwrap; | |
6361 var unwrap = scope.unwrap; | |
6362 var unwrapIfNeeded = scope.unwrapIfNeeded; | |
6363 var wrap = scope.wrap; | |
6364 | |
6365 var OriginalCanvasRenderingContext2D = window.CanvasRenderingContext2D; | |
6366 | |
6367 function CanvasRenderingContext2D(impl) { | |
6368 setWrapper(impl, this); | |
6369 } | |
6370 | |
6371 mixin(CanvasRenderingContext2D.prototype, { | |
6372 get canvas() { | |
6373 return wrap(unsafeUnwrap(this).canvas); | |
6374 }, | |
6375 | |
6376 drawImage: function() { | |
6377 arguments[0] = unwrapIfNeeded(arguments[0]); | |
6378 unsafeUnwrap(this).drawImage.apply(unsafeUnwrap(this), arguments); | |
6379 }, | |
6380 | |
6381 createPattern: function() { | |
6382 arguments[0] = unwrap(arguments[0]); | |
6383 return unsafeUnwrap(this).createPattern.apply(unsafeUnwrap(this), argument
s); | |
6384 } | |
6385 }); | |
6386 | |
6387 registerWrapper(OriginalCanvasRenderingContext2D, CanvasRenderingContext2D, | |
6388 document.createElement('canvas').getContext('2d')); | |
6389 | |
6390 scope.wrappers.CanvasRenderingContext2D = CanvasRenderingContext2D; | |
6391 })(window.ShadowDOMPolyfill); | |
6392 | |
6393 // Copyright 2013 The Polymer Authors. All rights reserved. | |
6394 // Use of this source code is goverened by a BSD-style | |
6395 // license that can be found in the LICENSE file. | |
6396 | |
6397 (function(scope) { | |
6398 'use strict'; | |
6399 | |
6400 var mixin = scope.mixin; | |
6401 var registerWrapper = scope.registerWrapper; | |
6402 var setWrapper = scope.setWrapper; | |
6403 var unsafeUnwrap = scope.unsafeUnwrap; | |
6404 var unwrapIfNeeded = scope.unwrapIfNeeded; | |
6405 var wrap = scope.wrap; | |
6406 | |
6407 var OriginalWebGLRenderingContext = window.WebGLRenderingContext; | |
6408 | |
6409 // IE10 does not have WebGL. | |
6410 if (!OriginalWebGLRenderingContext) | |
6411 return; | |
6412 | |
6413 function WebGLRenderingContext(impl) { | |
6414 setWrapper(impl, this); | |
6415 } | |
6416 | |
6417 mixin(WebGLRenderingContext.prototype, { | |
6418 get canvas() { | |
6419 return wrap(unsafeUnwrap(this).canvas); | |
6420 }, | |
6421 | |
6422 texImage2D: function() { | |
6423 arguments[5] = unwrapIfNeeded(arguments[5]); | |
6424 unsafeUnwrap(this).texImage2D.apply(unsafeUnwrap(this), arguments); | |
6425 }, | |
6426 | |
6427 texSubImage2D: function() { | |
6428 arguments[6] = unwrapIfNeeded(arguments[6]); | |
6429 unsafeUnwrap(this).texSubImage2D.apply(unsafeUnwrap(this), arguments); | |
6430 } | |
6431 }); | |
6432 | |
6433 // Blink/WebKit has broken DOM bindings. Usually we would create an instance | |
6434 // of the object and pass it into registerWrapper as a "blueprint" but | |
6435 // creating WebGL contexts is expensive and might fail so we use a dummy | |
6436 // object with dummy instance properties for these broken browsers. | |
6437 var instanceProperties = /WebKit/.test(navigator.userAgent) ? | |
6438 {drawingBufferHeight: null, drawingBufferWidth: null} : {}; | |
6439 | |
6440 registerWrapper(OriginalWebGLRenderingContext, WebGLRenderingContext, | |
6441 instanceProperties); | |
6442 | |
6443 scope.wrappers.WebGLRenderingContext = WebGLRenderingContext; | |
6444 })(window.ShadowDOMPolyfill); | |
6445 | |
6446 // Copyright 2013 The Polymer Authors. All rights reserved. | |
6447 // Use of this source code is goverened by a BSD-style | |
6448 // license that can be found in the LICENSE file. | |
6449 | |
6450 (function(scope) { | |
6451 'use strict'; | |
6452 | |
6453 var registerWrapper = scope.registerWrapper; | |
6454 var setWrapper = scope.setWrapper; | |
6455 var unsafeUnwrap = scope.unsafeUnwrap; | |
6456 var unwrap = scope.unwrap; | |
6457 var unwrapIfNeeded = scope.unwrapIfNeeded; | |
6458 var wrap = scope.wrap; | |
6459 | |
6460 var OriginalRange = window.Range; | |
6461 | |
6462 function Range(impl) { | |
6463 setWrapper(impl, this); | |
6464 } | |
6465 Range.prototype = { | |
6466 get startContainer() { | |
6467 return wrap(unsafeUnwrap(this).startContainer); | |
6468 }, | |
6469 get endContainer() { | |
6470 return wrap(unsafeUnwrap(this).endContainer); | |
6471 }, | |
6472 get commonAncestorContainer() { | |
6473 return wrap(unsafeUnwrap(this).commonAncestorContainer); | |
6474 }, | |
6475 setStart: function(refNode,offset) { | |
6476 unsafeUnwrap(this).setStart(unwrapIfNeeded(refNode), offset); | |
6477 }, | |
6478 setEnd: function(refNode,offset) { | |
6479 unsafeUnwrap(this).setEnd(unwrapIfNeeded(refNode), offset); | |
6480 }, | |
6481 setStartBefore: function(refNode) { | |
6482 unsafeUnwrap(this).setStartBefore(unwrapIfNeeded(refNode)); | |
6483 }, | |
6484 setStartAfter: function(refNode) { | |
6485 unsafeUnwrap(this).setStartAfter(unwrapIfNeeded(refNode)); | |
6486 }, | |
6487 setEndBefore: function(refNode) { | |
6488 unsafeUnwrap(this).setEndBefore(unwrapIfNeeded(refNode)); | |
6489 }, | |
6490 setEndAfter: function(refNode) { | |
6491 unsafeUnwrap(this).setEndAfter(unwrapIfNeeded(refNode)); | |
6492 }, | |
6493 selectNode: function(refNode) { | |
6494 unsafeUnwrap(this).selectNode(unwrapIfNeeded(refNode)); | |
6495 }, | |
6496 selectNodeContents: function(refNode) { | |
6497 unsafeUnwrap(this).selectNodeContents(unwrapIfNeeded(refNode)); | |
6498 }, | |
6499 compareBoundaryPoints: function(how, sourceRange) { | |
6500 return unsafeUnwrap(this).compareBoundaryPoints(how, unwrap(sourceRange)); | |
6501 }, | |
6502 extractContents: function() { | |
6503 return wrap(unsafeUnwrap(this).extractContents()); | |
6504 }, | |
6505 cloneContents: function() { | |
6506 return wrap(unsafeUnwrap(this).cloneContents()); | |
6507 }, | |
6508 insertNode: function(node) { | |
6509 unsafeUnwrap(this).insertNode(unwrapIfNeeded(node)); | |
6510 }, | |
6511 surroundContents: function(newParent) { | |
6512 unsafeUnwrap(this).surroundContents(unwrapIfNeeded(newParent)); | |
6513 }, | |
6514 cloneRange: function() { | |
6515 return wrap(unsafeUnwrap(this).cloneRange()); | |
6516 }, | |
6517 isPointInRange: function(node, offset) { | |
6518 return unsafeUnwrap(this).isPointInRange(unwrapIfNeeded(node), offset); | |
6519 }, | |
6520 comparePoint: function(node, offset) { | |
6521 return unsafeUnwrap(this).comparePoint(unwrapIfNeeded(node), offset); | |
6522 }, | |
6523 intersectsNode: function(node) { | |
6524 return unsafeUnwrap(this).intersectsNode(unwrapIfNeeded(node)); | |
6525 }, | |
6526 toString: function() { | |
6527 return unsafeUnwrap(this).toString(); | |
6528 } | |
6529 }; | |
6530 | |
6531 // IE9 does not have createContextualFragment. | |
6532 if (OriginalRange.prototype.createContextualFragment) { | |
6533 Range.prototype.createContextualFragment = function(html) { | |
6534 return wrap(unsafeUnwrap(this).createContextualFragment(html)); | |
6535 }; | |
6536 } | |
6537 | |
6538 registerWrapper(window.Range, Range, document.createRange()); | |
6539 | |
6540 scope.wrappers.Range = Range; | |
6541 | |
6542 })(window.ShadowDOMPolyfill); | |
6543 | |
6544 // Copyright 2013 The Polymer Authors. All rights reserved. | |
6545 // Use of this source code is goverened by a BSD-style | |
6546 // license that can be found in the LICENSE file. | |
6547 | |
6548 (function(scope) { | |
6549 'use strict'; | |
6550 | |
6551 var GetElementsByInterface = scope.GetElementsByInterface; | |
6552 var ParentNodeInterface = scope.ParentNodeInterface; | |
6553 var SelectorsInterface = scope.SelectorsInterface; | |
6554 var mixin = scope.mixin; | |
6555 var registerObject = scope.registerObject; | |
6556 | |
6557 var DocumentFragment = registerObject(document.createDocumentFragment()); | |
6558 mixin(DocumentFragment.prototype, ParentNodeInterface); | |
6559 mixin(DocumentFragment.prototype, SelectorsInterface); | |
6560 mixin(DocumentFragment.prototype, GetElementsByInterface); | |
6561 | |
6562 var Comment = registerObject(document.createComment('')); | |
6563 | |
6564 scope.wrappers.Comment = Comment; | |
6565 scope.wrappers.DocumentFragment = DocumentFragment; | |
6566 | |
6567 })(window.ShadowDOMPolyfill); | |
6568 | |
6569 // Copyright 2013 The Polymer Authors. All rights reserved. | |
6570 // Use of this source code is goverened by a BSD-style | |
6571 // license that can be found in the LICENSE file. | |
6572 | |
6573 (function(scope) { | |
6574 'use strict'; | |
6575 | |
6576 var DocumentFragment = scope.wrappers.DocumentFragment; | |
6577 var TreeScope = scope.TreeScope; | |
6578 var elementFromPoint = scope.elementFromPoint; | |
6579 var getInnerHTML = scope.getInnerHTML; | |
6580 var getTreeScope = scope.getTreeScope; | |
6581 var mixin = scope.mixin; | |
6582 var rewrap = scope.rewrap; | |
6583 var setInnerHTML = scope.setInnerHTML; | |
6584 var unsafeUnwrap = scope.unsafeUnwrap; | |
6585 var unwrap = scope.unwrap; | |
6586 | |
6587 var shadowHostTable = new WeakMap(); | |
6588 var nextOlderShadowTreeTable = new WeakMap(); | |
6589 | |
6590 var spaceCharRe = /[ \t\n\r\f]/; | |
6591 | |
6592 function ShadowRoot(hostWrapper) { | |
6593 var node = unwrap(unsafeUnwrap(hostWrapper).ownerDocument.createDocumentFrag
ment()); | |
6594 DocumentFragment.call(this, node); | |
6595 | |
6596 // createDocumentFragment associates the node with a wrapper | |
6597 // DocumentFragment instance. Override that. | |
6598 rewrap(node, this); | |
6599 | |
6600 var oldShadowRoot = hostWrapper.shadowRoot; | |
6601 nextOlderShadowTreeTable.set(this, oldShadowRoot); | |
6602 | |
6603 this.treeScope_ = | |
6604 new TreeScope(this, getTreeScope(oldShadowRoot || hostWrapper)); | |
6605 | |
6606 shadowHostTable.set(this, hostWrapper); | |
6607 } | |
6608 ShadowRoot.prototype = Object.create(DocumentFragment.prototype); | |
6609 mixin(ShadowRoot.prototype, { | |
6610 constructor: ShadowRoot, | |
6611 | |
6612 get innerHTML() { | |
6613 return getInnerHTML(this); | |
6614 }, | |
6615 set innerHTML(value) { | |
6616 setInnerHTML(this, value); | |
6617 this.invalidateShadowRenderer(); | |
6618 }, | |
6619 | |
6620 get olderShadowRoot() { | |
6621 return nextOlderShadowTreeTable.get(this) || null; | |
6622 }, | |
6623 | |
6624 get host() { | |
6625 return shadowHostTable.get(this) || null; | |
6626 }, | |
6627 | |
6628 invalidateShadowRenderer: function() { | |
6629 return shadowHostTable.get(this).invalidateShadowRenderer(); | |
6630 }, | |
6631 | |
6632 elementFromPoint: function(x, y) { | |
6633 return elementFromPoint(this, this.ownerDocument, x, y); | |
6634 }, | |
6635 | |
6636 getElementById: function(id) { | |
6637 if (spaceCharRe.test(id)) | |
6638 return null; | |
6639 return this.querySelector('[id="' + id + '"]'); | |
6640 } | |
6641 }); | |
6642 | |
6643 scope.wrappers.ShadowRoot = ShadowRoot; | |
6644 | |
6645 })(window.ShadowDOMPolyfill); | |
6646 | |
6647 // Copyright 2013 The Polymer Authors. All rights reserved. | |
6648 // Use of this source code is governed by a BSD-style | |
6649 // license that can be found in the LICENSE file. | |
6650 | |
6651 (function(scope) { | |
6652 'use strict'; | |
6653 | |
6654 var Element = scope.wrappers.Element; | |
6655 var HTMLContentElement = scope.wrappers.HTMLContentElement; | |
6656 var HTMLShadowElement = scope.wrappers.HTMLShadowElement; | |
6657 var Node = scope.wrappers.Node; | |
6658 var ShadowRoot = scope.wrappers.ShadowRoot; | |
6659 var assert = scope.assert; | |
6660 var getTreeScope = scope.getTreeScope; | |
6661 var mixin = scope.mixin; | |
6662 var oneOf = scope.oneOf; | |
6663 var unsafeUnwrap = scope.unsafeUnwrap; | |
6664 var unwrap = scope.unwrap; | |
6665 var wrap = scope.wrap; | |
6666 | |
6667 /** | |
6668 * Updates the fields of a wrapper to a snapshot of the logical DOM as needed. | |
6669 * Up means parentNode | |
6670 * Sideways means previous and next sibling. | |
6671 * @param {!Node} wrapper | |
6672 */ | |
6673 function updateWrapperUpAndSideways(wrapper) { | |
6674 wrapper.previousSibling_ = wrapper.previousSibling; | |
6675 wrapper.nextSibling_ = wrapper.nextSibling; | |
6676 wrapper.parentNode_ = wrapper.parentNode; | |
6677 } | |
6678 | |
6679 /** | |
6680 * Updates the fields of a wrapper to a snapshot of the logical DOM as needed. | |
6681 * Down means first and last child | |
6682 * @param {!Node} wrapper | |
6683 */ | |
6684 function updateWrapperDown(wrapper) { | |
6685 wrapper.firstChild_ = wrapper.firstChild; | |
6686 wrapper.lastChild_ = wrapper.lastChild; | |
6687 } | |
6688 | |
6689 function updateAllChildNodes(parentNodeWrapper) { | |
6690 assert(parentNodeWrapper instanceof Node); | |
6691 for (var childWrapper = parentNodeWrapper.firstChild; | |
6692 childWrapper; | |
6693 childWrapper = childWrapper.nextSibling) { | |
6694 updateWrapperUpAndSideways(childWrapper); | |
6695 } | |
6696 updateWrapperDown(parentNodeWrapper); | |
6697 } | |
6698 | |
6699 function insertBefore(parentNodeWrapper, newChildWrapper, refChildWrapper) { | |
6700 var parentNode = unwrap(parentNodeWrapper); | |
6701 var newChild = unwrap(newChildWrapper); | |
6702 var refChild = refChildWrapper ? unwrap(refChildWrapper) : null; | |
6703 | |
6704 remove(newChildWrapper); | |
6705 updateWrapperUpAndSideways(newChildWrapper); | |
6706 | |
6707 if (!refChildWrapper) { | |
6708 parentNodeWrapper.lastChild_ = parentNodeWrapper.lastChild; | |
6709 if (parentNodeWrapper.lastChild === parentNodeWrapper.firstChild) | |
6710 parentNodeWrapper.firstChild_ = parentNodeWrapper.firstChild; | |
6711 | |
6712 var lastChildWrapper = wrap(parentNode.lastChild); | |
6713 if (lastChildWrapper) | |
6714 lastChildWrapper.nextSibling_ = lastChildWrapper.nextSibling; | |
6715 } else { | |
6716 if (parentNodeWrapper.firstChild === refChildWrapper) | |
6717 parentNodeWrapper.firstChild_ = refChildWrapper; | |
6718 | |
6719 refChildWrapper.previousSibling_ = refChildWrapper.previousSibling; | |
6720 } | |
6721 | |
6722 scope.originalInsertBefore.call(parentNode, newChild, refChild); | |
6723 } | |
6724 | |
6725 function remove(nodeWrapper) { | |
6726 var node = unwrap(nodeWrapper) | |
6727 var parentNode = node.parentNode; | |
6728 if (!parentNode) | |
6729 return; | |
6730 | |
6731 var parentNodeWrapper = wrap(parentNode); | |
6732 updateWrapperUpAndSideways(nodeWrapper); | |
6733 | |
6734 if (nodeWrapper.previousSibling) | |
6735 nodeWrapper.previousSibling.nextSibling_ = nodeWrapper; | |
6736 if (nodeWrapper.nextSibling) | |
6737 nodeWrapper.nextSibling.previousSibling_ = nodeWrapper; | |
6738 | |
6739 if (parentNodeWrapper.lastChild === nodeWrapper) | |
6740 parentNodeWrapper.lastChild_ = nodeWrapper; | |
6741 if (parentNodeWrapper.firstChild === nodeWrapper) | |
6742 parentNodeWrapper.firstChild_ = nodeWrapper; | |
6743 | |
6744 scope.originalRemoveChild.call(parentNode, node); | |
6745 } | |
6746 | |
6747 var distributedNodesTable = new WeakMap(); | |
6748 var destinationInsertionPointsTable = new WeakMap(); | |
6749 var rendererForHostTable = new WeakMap(); | |
6750 | |
6751 function resetDistributedNodes(insertionPoint) { | |
6752 distributedNodesTable.set(insertionPoint, []); | |
6753 } | |
6754 | |
6755 function getDistributedNodes(insertionPoint) { | |
6756 var rv = distributedNodesTable.get(insertionPoint); | |
6757 if (!rv) | |
6758 distributedNodesTable.set(insertionPoint, rv = []); | |
6759 return rv; | |
6760 } | |
6761 | |
6762 function getChildNodesSnapshot(node) { | |
6763 var result = [], i = 0; | |
6764 for (var child = node.firstChild; child; child = child.nextSibling) { | |
6765 result[i++] = child; | |
6766 } | |
6767 return result; | |
6768 } | |
6769 | |
6770 var request = oneOf(window, [ | |
6771 'requestAnimationFrame', | |
6772 'mozRequestAnimationFrame', | |
6773 'webkitRequestAnimationFrame', | |
6774 'setTimeout' | |
6775 ]); | |
6776 | |
6777 var pendingDirtyRenderers = []; | |
6778 var renderTimer; | |
6779 | |
6780 function renderAllPending() { | |
6781 // TODO(arv): Order these in document order. That way we do not have to | |
6782 // render something twice. | |
6783 for (var i = 0; i < pendingDirtyRenderers.length; i++) { | |
6784 var renderer = pendingDirtyRenderers[i]; | |
6785 var parentRenderer = renderer.parentRenderer; | |
6786 if (parentRenderer && parentRenderer.dirty) | |
6787 continue; | |
6788 renderer.render(); | |
6789 } | |
6790 | |
6791 pendingDirtyRenderers = []; | |
6792 } | |
6793 | |
6794 function handleRequestAnimationFrame() { | |
6795 renderTimer = null; | |
6796 renderAllPending(); | |
6797 } | |
6798 | |
6799 /** | |
6800 * Returns existing shadow renderer for a host or creates it if it is needed. | |
6801 * @params {!Element} host | |
6802 * @return {!ShadowRenderer} | |
6803 */ | |
6804 function getRendererForHost(host) { | |
6805 var renderer = rendererForHostTable.get(host); | |
6806 if (!renderer) { | |
6807 renderer = new ShadowRenderer(host); | |
6808 rendererForHostTable.set(host, renderer); | |
6809 } | |
6810 return renderer; | |
6811 } | |
6812 | |
6813 function getShadowRootAncestor(node) { | |
6814 var root = getTreeScope(node).root; | |
6815 if (root instanceof ShadowRoot) | |
6816 return root; | |
6817 return null; | |
6818 } | |
6819 | |
6820 function getRendererForShadowRoot(shadowRoot) { | |
6821 return getRendererForHost(shadowRoot.host); | |
6822 } | |
6823 | |
6824 var spliceDiff = new ArraySplice(); | |
6825 spliceDiff.equals = function(renderNode, rawNode) { | |
6826 return unwrap(renderNode.node) === rawNode; | |
6827 }; | |
6828 | |
6829 /** | |
6830 * RenderNode is used as an in memory "render tree". When we render the | |
6831 * composed tree we create a tree of RenderNodes, then we diff this against | |
6832 * the real DOM tree and make minimal changes as needed. | |
6833 */ | |
6834 function RenderNode(node) { | |
6835 this.skip = false; | |
6836 this.node = node; | |
6837 this.childNodes = []; | |
6838 } | |
6839 | |
6840 RenderNode.prototype = { | |
6841 append: function(node) { | |
6842 var rv = new RenderNode(node); | |
6843 this.childNodes.push(rv); | |
6844 return rv; | |
6845 }, | |
6846 | |
6847 sync: function(opt_added) { | |
6848 if (this.skip) | |
6849 return; | |
6850 | |
6851 var nodeWrapper = this.node; | |
6852 // plain array of RenderNodes | |
6853 var newChildren = this.childNodes; | |
6854 // plain array of real nodes. | |
6855 var oldChildren = getChildNodesSnapshot(unwrap(nodeWrapper)); | |
6856 var added = opt_added || new WeakMap(); | |
6857 | |
6858 var splices = spliceDiff.calculateSplices(newChildren, oldChildren); | |
6859 | |
6860 var newIndex = 0, oldIndex = 0; | |
6861 var lastIndex = 0; | |
6862 for (var i = 0; i < splices.length; i++) { | |
6863 var splice = splices[i]; | |
6864 for (; lastIndex < splice.index; lastIndex++) { | |
6865 oldIndex++; | |
6866 newChildren[newIndex++].sync(added); | |
6867 } | |
6868 | |
6869 var removedCount = splice.removed.length; | |
6870 for (var j = 0; j < removedCount; j++) { | |
6871 var wrapper = wrap(oldChildren[oldIndex++]); | |
6872 if (!added.get(wrapper)) | |
6873 remove(wrapper); | |
6874 } | |
6875 | |
6876 var addedCount = splice.addedCount; | |
6877 var refNode = oldChildren[oldIndex] && wrap(oldChildren[oldIndex]); | |
6878 for (var j = 0; j < addedCount; j++) { | |
6879 var newChildRenderNode = newChildren[newIndex++]; | |
6880 var newChildWrapper = newChildRenderNode.node; | |
6881 insertBefore(nodeWrapper, newChildWrapper, refNode); | |
6882 | |
6883 // Keep track of added so that we do not remove the node after it | |
6884 // has been added. | |
6885 added.set(newChildWrapper, true); | |
6886 | |
6887 newChildRenderNode.sync(added); | |
6888 } | |
6889 | |
6890 lastIndex += addedCount; | |
6891 } | |
6892 | |
6893 for (var i = lastIndex; i < newChildren.length; i++) { | |
6894 newChildren[i].sync(added); | |
6895 } | |
6896 } | |
6897 }; | |
6898 | |
6899 function ShadowRenderer(host) { | |
6900 this.host = host; | |
6901 this.dirty = false; | |
6902 this.invalidateAttributes(); | |
6903 this.associateNode(host); | |
6904 } | |
6905 | |
6906 ShadowRenderer.prototype = { | |
6907 | |
6908 // http://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/shadow/index.html#r
endering-shadow-trees | |
6909 render: function(opt_renderNode) { | |
6910 if (!this.dirty) | |
6911 return; | |
6912 | |
6913 this.invalidateAttributes(); | |
6914 | |
6915 var host = this.host; | |
6916 | |
6917 this.distribution(host); | |
6918 var renderNode = opt_renderNode || new RenderNode(host); | |
6919 this.buildRenderTree(renderNode, host); | |
6920 | |
6921 var topMostRenderer = !opt_renderNode; | |
6922 if (topMostRenderer) | |
6923 renderNode.sync(); | |
6924 | |
6925 this.dirty = false; | |
6926 }, | |
6927 | |
6928 get parentRenderer() { | |
6929 return getTreeScope(this.host).renderer; | |
6930 }, | |
6931 | |
6932 invalidate: function() { | |
6933 if (!this.dirty) { | |
6934 this.dirty = true; | |
6935 var parentRenderer = this.parentRenderer; | |
6936 if (parentRenderer) | |
6937 parentRenderer.invalidate(); | |
6938 pendingDirtyRenderers.push(this); | |
6939 if (renderTimer) | |
6940 return; | |
6941 renderTimer = window[request](handleRequestAnimationFrame, 0); | |
6942 } | |
6943 }, | |
6944 | |
6945 // http://w3c.github.io/webcomponents/spec/shadow/#distribution-algorithms | |
6946 distribution: function(root) { | |
6947 this.resetAllSubtrees(root); | |
6948 this.distributionResolution(root); | |
6949 }, | |
6950 | |
6951 resetAll: function(node) { | |
6952 if (isInsertionPoint(node)) | |
6953 resetDistributedNodes(node); | |
6954 else | |
6955 resetDestinationInsertionPoints(node); | |
6956 | |
6957 this.resetAllSubtrees(node); | |
6958 }, | |
6959 | |
6960 resetAllSubtrees: function(node) { | |
6961 for (var child = node.firstChild; child; child = child.nextSibling) { | |
6962 this.resetAll(child); | |
6963 } | |
6964 | |
6965 if (node.shadowRoot) | |
6966 this.resetAll(node.shadowRoot); | |
6967 | |
6968 if (node.olderShadowRoot) | |
6969 this.resetAll(node.olderShadowRoot); | |
6970 }, | |
6971 | |
6972 // http://w3c.github.io/webcomponents/spec/shadow/#distribution-results | |
6973 distributionResolution: function(node) { | |
6974 if (isShadowHost(node)) { | |
6975 var shadowHost = node; | |
6976 // 1.1 | |
6977 var pool = poolPopulation(shadowHost); | |
6978 | |
6979 var shadowTrees = getShadowTrees(shadowHost); | |
6980 | |
6981 // 1.2 | |
6982 for (var i = 0; i < shadowTrees.length; i++) { | |
6983 // 1.2.1 | |
6984 this.poolDistribution(shadowTrees[i], pool); | |
6985 } | |
6986 | |
6987 // 1.3 | |
6988 for (var i = shadowTrees.length - 1; i >= 0; i--) { | |
6989 var shadowTree = shadowTrees[i]; | |
6990 | |
6991 // 1.3.1 | |
6992 // TODO(arv): We should keep the shadow insertion points on the | |
6993 // shadow root (or renderer) so we don't have to search the tree | |
6994 // every time. | |
6995 var shadow = getShadowInsertionPoint(shadowTree); | |
6996 | |
6997 // 1.3.2 | |
6998 if (shadow) { | |
6999 | |
7000 // 1.3.2.1 | |
7001 var olderShadowRoot = shadowTree.olderShadowRoot; | |
7002 if (olderShadowRoot) { | |
7003 // 1.3.2.1.1 | |
7004 pool = poolPopulation(olderShadowRoot); | |
7005 } | |
7006 | |
7007 // 1.3.2.2 | |
7008 for (var j = 0; j < pool.length; j++) { | |
7009 // 1.3.2.2.1 | |
7010 destributeNodeInto(pool[j], shadow); | |
7011 } | |
7012 } | |
7013 | |
7014 // 1.3.3 | |
7015 this.distributionResolution(shadowTree); | |
7016 } | |
7017 } | |
7018 | |
7019 for (var child = node.firstChild; child; child = child.nextSibling) { | |
7020 this.distributionResolution(child); | |
7021 } | |
7022 }, | |
7023 | |
7024 // http://w3c.github.io/webcomponents/spec/shadow/#dfn-pool-distribution-alg
orithm | |
7025 poolDistribution: function (node, pool) { | |
7026 if (node instanceof HTMLShadowElement) | |
7027 return; | |
7028 | |
7029 if (node instanceof HTMLContentElement) { | |
7030 var content = node; | |
7031 this.updateDependentAttributes(content.getAttribute('select')); | |
7032 | |
7033 var anyDistributed = false; | |
7034 | |
7035 // 1.1 | |
7036 for (var i = 0; i < pool.length; i++) { | |
7037 var node = pool[i]; | |
7038 if (!node) | |
7039 continue; | |
7040 if (matches(node, content)) { | |
7041 destributeNodeInto(node, content); | |
7042 pool[i] = undefined; | |
7043 anyDistributed = true; | |
7044 } | |
7045 } | |
7046 | |
7047 // 1.2 | |
7048 // Fallback content | |
7049 if (!anyDistributed) { | |
7050 for (var child = content.firstChild; | |
7051 child; | |
7052 child = child.nextSibling) { | |
7053 destributeNodeInto(child, content); | |
7054 } | |
7055 } | |
7056 | |
7057 return; | |
7058 } | |
7059 | |
7060 for (var child = node.firstChild; child; child = child.nextSibling) { | |
7061 this.poolDistribution(child, pool); | |
7062 } | |
7063 }, | |
7064 | |
7065 buildRenderTree: function(renderNode, node) { | |
7066 var children = this.compose(node); | |
7067 for (var i = 0; i < children.length; i++) { | |
7068 var child = children[i]; | |
7069 var childRenderNode = renderNode.append(child); | |
7070 this.buildRenderTree(childRenderNode, child); | |
7071 } | |
7072 | |
7073 if (isShadowHost(node)) { | |
7074 var renderer = getRendererForHost(node); | |
7075 renderer.dirty = false; | |
7076 } | |
7077 | |
7078 }, | |
7079 | |
7080 compose: function(node) { | |
7081 var children = []; | |
7082 var p = node.shadowRoot || node; | |
7083 for (var child = p.firstChild; child; child = child.nextSibling) { | |
7084 if (isInsertionPoint(child)) { | |
7085 this.associateNode(p); | |
7086 var distributedNodes = getDistributedNodes(child); | |
7087 for (var j = 0; j < distributedNodes.length; j++) { | |
7088 var distributedNode = distributedNodes[j]; | |
7089 if (isFinalDestination(child, distributedNode)) | |
7090 children.push(distributedNode); | |
7091 } | |
7092 } else { | |
7093 children.push(child); | |
7094 } | |
7095 } | |
7096 return children; | |
7097 }, | |
7098 | |
7099 /** | |
7100 * Invalidates the attributes used to keep track of which attributes may | |
7101 * cause the renderer to be invalidated. | |
7102 */ | |
7103 invalidateAttributes: function() { | |
7104 this.attributes = Object.create(null); | |
7105 }, | |
7106 | |
7107 /** | |
7108 * Parses the selector and makes this renderer dependent on the attribute | |
7109 * being used in the selector. | |
7110 * @param {string} selector | |
7111 */ | |
7112 updateDependentAttributes: function(selector) { | |
7113 if (!selector) | |
7114 return; | |
7115 | |
7116 var attributes = this.attributes; | |
7117 | |
7118 // .class | |
7119 if (/\.\w+/.test(selector)) | |
7120 attributes['class'] = true; | |
7121 | |
7122 // #id | |
7123 if (/#\w+/.test(selector)) | |
7124 attributes['id'] = true; | |
7125 | |
7126 selector.replace(/\[\s*([^\s=\|~\]]+)/g, function(_, name) { | |
7127 attributes[name] = true; | |
7128 }); | |
7129 | |
7130 // Pseudo selectors have been removed from the spec. | |
7131 }, | |
7132 | |
7133 dependsOnAttribute: function(name) { | |
7134 return this.attributes[name]; | |
7135 }, | |
7136 | |
7137 associateNode: function(node) { | |
7138 unsafeUnwrap(node).polymerShadowRenderer_ = this; | |
7139 } | |
7140 }; | |
7141 | |
7142 // http://w3c.github.io/webcomponents/spec/shadow/#dfn-pool-population-algorit
hm | |
7143 function poolPopulation(node) { | |
7144 var pool = []; | |
7145 for (var child = node.firstChild; child; child = child.nextSibling) { | |
7146 if (isInsertionPoint(child)) { | |
7147 pool.push.apply(pool, getDistributedNodes(child)); | |
7148 } else { | |
7149 pool.push(child); | |
7150 } | |
7151 } | |
7152 return pool; | |
7153 } | |
7154 | |
7155 function getShadowInsertionPoint(node) { | |
7156 if (node instanceof HTMLShadowElement) | |
7157 return node; | |
7158 if (node instanceof HTMLContentElement) | |
7159 return null; | |
7160 for (var child = node.firstChild; child; child = child.nextSibling) { | |
7161 var res = getShadowInsertionPoint(child); | |
7162 if (res) | |
7163 return res; | |
7164 } | |
7165 return null; | |
7166 } | |
7167 | |
7168 function destributeNodeInto(child, insertionPoint) { | |
7169 getDistributedNodes(insertionPoint).push(child); | |
7170 var points = destinationInsertionPointsTable.get(child); | |
7171 if (!points) | |
7172 destinationInsertionPointsTable.set(child, [insertionPoint]); | |
7173 else | |
7174 points.push(insertionPoint); | |
7175 } | |
7176 | |
7177 function getDestinationInsertionPoints(node) { | |
7178 return destinationInsertionPointsTable.get(node); | |
7179 } | |
7180 | |
7181 function resetDestinationInsertionPoints(node) { | |
7182 // IE11 crashes when delete is used. | |
7183 destinationInsertionPointsTable.set(node, undefined); | |
7184 } | |
7185 | |
7186 // AllowedSelectors : | |
7187 // TypeSelector | |
7188 // * | |
7189 // ClassSelector | |
7190 // IDSelector | |
7191 // AttributeSelector | |
7192 // negation | |
7193 var selectorStartCharRe = /^(:not\()?[*.#[a-zA-Z_|]/; | |
7194 | |
7195 function matches(node, contentElement) { | |
7196 var select = contentElement.getAttribute('select'); | |
7197 if (!select) | |
7198 return true; | |
7199 | |
7200 // Here we know the select attribute is a non empty string. | |
7201 select = select.trim(); | |
7202 if (!select) | |
7203 return true; | |
7204 | |
7205 if (!(node instanceof Element)) | |
7206 return false; | |
7207 | |
7208 if (!selectorStartCharRe.test(select)) | |
7209 return false; | |
7210 | |
7211 try { | |
7212 return node.matches(select); | |
7213 } catch (ex) { | |
7214 // Invalid selector. | |
7215 return false; | |
7216 } | |
7217 } | |
7218 | |
7219 function isFinalDestination(insertionPoint, node) { | |
7220 var points = getDestinationInsertionPoints(node); | |
7221 return points && points[points.length - 1] === insertionPoint; | |
7222 } | |
7223 | |
7224 function isInsertionPoint(node) { | |
7225 return node instanceof HTMLContentElement || | |
7226 node instanceof HTMLShadowElement; | |
7227 } | |
7228 | |
7229 function isShadowHost(shadowHost) { | |
7230 return shadowHost.shadowRoot; | |
7231 } | |
7232 | |
7233 // Returns the shadow trees as an array, with the youngest tree at the | |
7234 // beginning of the array. | |
7235 function getShadowTrees(host) { | |
7236 var trees = []; | |
7237 | |
7238 for (var tree = host.shadowRoot; tree; tree = tree.olderShadowRoot) { | |
7239 trees.push(tree); | |
7240 } | |
7241 return trees; | |
7242 } | |
7243 | |
7244 function render(host) { | |
7245 new ShadowRenderer(host).render(); | |
7246 }; | |
7247 | |
7248 // Need to rerender shadow host when: | |
7249 // | |
7250 // - a direct child to the ShadowRoot is added or removed | |
7251 // - a direct child to the host is added or removed | |
7252 // - a new shadow root is created | |
7253 // - a direct child to a content/shadow element is added or removed | |
7254 // - a sibling to a content/shadow element is added or removed | |
7255 // - content[select] is changed | |
7256 // - an attribute in a direct child to a host is modified | |
7257 | |
7258 /** | |
7259 * This gets called when a node was added or removed to it. | |
7260 */ | |
7261 Node.prototype.invalidateShadowRenderer = function(force) { | |
7262 var renderer = unsafeUnwrap(this).polymerShadowRenderer_; | |
7263 if (renderer) { | |
7264 renderer.invalidate(); | |
7265 return true; | |
7266 } | |
7267 | |
7268 return false; | |
7269 }; | |
7270 | |
7271 HTMLContentElement.prototype.getDistributedNodes = | |
7272 HTMLShadowElement.prototype.getDistributedNodes = function() { | |
7273 // TODO(arv): We should only rerender the dirty ancestor renderers (from | |
7274 // the root and down). | |
7275 renderAllPending(); | |
7276 return getDistributedNodes(this); | |
7277 }; | |
7278 | |
7279 Element.prototype.getDestinationInsertionPoints = function() { | |
7280 renderAllPending(); | |
7281 return getDestinationInsertionPoints(this) || []; | |
7282 }; | |
7283 | |
7284 HTMLContentElement.prototype.nodeIsInserted_ = | |
7285 HTMLShadowElement.prototype.nodeIsInserted_ = function() { | |
7286 // Invalidate old renderer if any. | |
7287 this.invalidateShadowRenderer(); | |
7288 | |
7289 var shadowRoot = getShadowRootAncestor(this); | |
7290 var renderer; | |
7291 if (shadowRoot) | |
7292 renderer = getRendererForShadowRoot(shadowRoot); | |
7293 unsafeUnwrap(this).polymerShadowRenderer_ = renderer; | |
7294 if (renderer) | |
7295 renderer.invalidate(); | |
7296 }; | |
7297 | |
7298 scope.getRendererForHost = getRendererForHost; | |
7299 scope.getShadowTrees = getShadowTrees; | |
7300 scope.renderAllPending = renderAllPending; | |
7301 | |
7302 scope.getDestinationInsertionPoints = getDestinationInsertionPoints; | |
7303 | |
7304 // Exposed for testing | |
7305 scope.visual = { | |
7306 insertBefore: insertBefore, | |
7307 remove: remove, | |
7308 }; | |
7309 | |
7310 })(window.ShadowDOMPolyfill); | |
7311 | |
7312 // Copyright 2013 The Polymer Authors. All rights reserved. | |
7313 // Use of this source code is goverened by a BSD-style | |
7314 // license that can be found in the LICENSE file. | |
7315 | |
7316 (function(scope) { | |
7317 'use strict'; | |
7318 | |
7319 var HTMLElement = scope.wrappers.HTMLElement; | |
7320 var assert = scope.assert; | |
7321 var mixin = scope.mixin; | |
7322 var registerWrapper = scope.registerWrapper; | |
7323 var unwrap = scope.unwrap; | |
7324 var wrap = scope.wrap; | |
7325 | |
7326 var elementsWithFormProperty = [ | |
7327 'HTMLButtonElement', | |
7328 'HTMLFieldSetElement', | |
7329 'HTMLInputElement', | |
7330 'HTMLKeygenElement', | |
7331 'HTMLLabelElement', | |
7332 'HTMLLegendElement', | |
7333 'HTMLObjectElement', | |
7334 // HTMLOptionElement is handled in HTMLOptionElement.js | |
7335 'HTMLOutputElement', | |
7336 // HTMLSelectElement is handled in HTMLSelectElement.js | |
7337 'HTMLTextAreaElement', | |
7338 ]; | |
7339 | |
7340 function createWrapperConstructor(name) { | |
7341 if (!window[name]) | |
7342 return; | |
7343 | |
7344 // Ensure we are not overriding an already existing constructor. | |
7345 assert(!scope.wrappers[name]); | |
7346 | |
7347 var GeneratedWrapper = function(node) { | |
7348 // At this point all of them extend HTMLElement. | |
7349 HTMLElement.call(this, node); | |
7350 } | |
7351 GeneratedWrapper.prototype = Object.create(HTMLElement.prototype); | |
7352 mixin(GeneratedWrapper.prototype, { | |
7353 get form() { | |
7354 return wrap(unwrap(this).form); | |
7355 }, | |
7356 }); | |
7357 | |
7358 registerWrapper(window[name], GeneratedWrapper, | |
7359 document.createElement(name.slice(4, -7))); | |
7360 scope.wrappers[name] = GeneratedWrapper; | |
7361 } | |
7362 | |
7363 elementsWithFormProperty.forEach(createWrapperConstructor); | |
7364 | |
7365 })(window.ShadowDOMPolyfill); | |
7366 | |
7367 // Copyright 2014 The Polymer Authors. All rights reserved. | |
7368 // Use of this source code is goverened by a BSD-style | |
7369 // license that can be found in the LICENSE file. | |
7370 | |
7371 (function(scope) { | |
7372 'use strict'; | |
7373 | |
7374 var registerWrapper = scope.registerWrapper; | |
7375 var setWrapper = scope.setWrapper; | |
7376 var unsafeUnwrap = scope.unsafeUnwrap; | |
7377 var unwrap = scope.unwrap; | |
7378 var unwrapIfNeeded = scope.unwrapIfNeeded; | |
7379 var wrap = scope.wrap; | |
7380 | |
7381 var OriginalSelection = window.Selection; | |
7382 | |
7383 function Selection(impl) { | |
7384 setWrapper(impl, this); | |
7385 } | |
7386 Selection.prototype = { | |
7387 get anchorNode() { | |
7388 return wrap(unsafeUnwrap(this).anchorNode); | |
7389 }, | |
7390 get focusNode() { | |
7391 return wrap(unsafeUnwrap(this).focusNode); | |
7392 }, | |
7393 addRange: function(range) { | |
7394 unsafeUnwrap(this).addRange(unwrap(range)); | |
7395 }, | |
7396 collapse: function(node, index) { | |
7397 unsafeUnwrap(this).collapse(unwrapIfNeeded(node), index); | |
7398 }, | |
7399 containsNode: function(node, allowPartial) { | |
7400 return unsafeUnwrap(this).containsNode(unwrapIfNeeded(node), allowPartial)
; | |
7401 }, | |
7402 extend: function(node, offset) { | |
7403 unsafeUnwrap(this).extend(unwrapIfNeeded(node), offset); | |
7404 }, | |
7405 getRangeAt: function(index) { | |
7406 return wrap(unsafeUnwrap(this).getRangeAt(index)); | |
7407 }, | |
7408 removeRange: function(range) { | |
7409 unsafeUnwrap(this).removeRange(unwrap(range)); | |
7410 }, | |
7411 selectAllChildren: function(node) { | |
7412 unsafeUnwrap(this).selectAllChildren(unwrapIfNeeded(node)); | |
7413 }, | |
7414 toString: function() { | |
7415 return unsafeUnwrap(this).toString(); | |
7416 } | |
7417 }; | |
7418 | |
7419 // WebKit extensions. Not implemented. | |
7420 // readonly attribute Node baseNode; | |
7421 // readonly attribute long baseOffset; | |
7422 // readonly attribute Node extentNode; | |
7423 // readonly attribute long extentOffset; | |
7424 // [RaisesException] void setBaseAndExtent([Default=Undefined] optional Node b
aseNode, | |
7425 // [Default=Undefined] optional long baseOffset, | |
7426 // [Default=Undefined] optional Node extentNode, | |
7427 // [Default=Undefined] optional long extentOffset); | |
7428 // [RaisesException, ImplementedAs=collapse] void setPosition([Default=Undefin
ed] optional Node node, | |
7429 // [Default=Undefined] optional long offset); | |
7430 | |
7431 registerWrapper(window.Selection, Selection, window.getSelection()); | |
7432 | |
7433 scope.wrappers.Selection = Selection; | |
7434 | |
7435 })(window.ShadowDOMPolyfill); | |
7436 | |
7437 // Copyright 2013 The Polymer Authors. All rights reserved. | |
7438 // Use of this source code is goverened by a BSD-style | |
7439 // license that can be found in the LICENSE file. | |
7440 | |
7441 (function(scope) { | |
7442 'use strict'; | |
7443 | |
7444 var GetElementsByInterface = scope.GetElementsByInterface; | |
7445 var Node = scope.wrappers.Node; | |
7446 var ParentNodeInterface = scope.ParentNodeInterface; | |
7447 var Selection = scope.wrappers.Selection; | |
7448 var SelectorsInterface = scope.SelectorsInterface; | |
7449 var ShadowRoot = scope.wrappers.ShadowRoot; | |
7450 var TreeScope = scope.TreeScope; | |
7451 var cloneNode = scope.cloneNode; | |
7452 var defineWrapGetter = scope.defineWrapGetter; | |
7453 var elementFromPoint = scope.elementFromPoint; | |
7454 var forwardMethodsToWrapper = scope.forwardMethodsToWrapper; | |
7455 var matchesNames = scope.matchesNames; | |
7456 var mixin = scope.mixin; | |
7457 var registerWrapper = scope.registerWrapper; | |
7458 var renderAllPending = scope.renderAllPending; | |
7459 var rewrap = scope.rewrap; | |
7460 var setWrapper = scope.setWrapper; | |
7461 var unsafeUnwrap = scope.unsafeUnwrap; | |
7462 var unwrap = scope.unwrap; | |
7463 var wrap = scope.wrap; | |
7464 var wrapEventTargetMethods = scope.wrapEventTargetMethods; | |
7465 var wrapNodeList = scope.wrapNodeList; | |
7466 | |
7467 var implementationTable = new WeakMap(); | |
7468 | |
7469 function Document(node) { | |
7470 Node.call(this, node); | |
7471 this.treeScope_ = new TreeScope(this, null); | |
7472 } | |
7473 Document.prototype = Object.create(Node.prototype); | |
7474 | |
7475 defineWrapGetter(Document, 'documentElement'); | |
7476 | |
7477 // Conceptually both body and head can be in a shadow but suporting that seems | |
7478 // overkill at this point. | |
7479 defineWrapGetter(Document, 'body'); | |
7480 defineWrapGetter(Document, 'head'); | |
7481 | |
7482 // document cannot be overridden so we override a bunch of its methods | |
7483 // directly on the instance. | |
7484 | |
7485 function wrapMethod(name) { | |
7486 var original = document[name]; | |
7487 Document.prototype[name] = function() { | |
7488 return wrap(original.apply(unsafeUnwrap(this), arguments)); | |
7489 }; | |
7490 } | |
7491 | |
7492 [ | |
7493 'createComment', | |
7494 'createDocumentFragment', | |
7495 'createElement', | |
7496 'createElementNS', | |
7497 'createEvent', | |
7498 'createEventNS', | |
7499 'createRange', | |
7500 'createTextNode', | |
7501 'getElementById' | |
7502 ].forEach(wrapMethod); | |
7503 | |
7504 var originalAdoptNode = document.adoptNode; | |
7505 | |
7506 function adoptNodeNoRemove(node, doc) { | |
7507 originalAdoptNode.call(unsafeUnwrap(doc), unwrap(node)); | |
7508 adoptSubtree(node, doc); | |
7509 } | |
7510 | |
7511 function adoptSubtree(node, doc) { | |
7512 if (node.shadowRoot) | |
7513 doc.adoptNode(node.shadowRoot); | |
7514 if (node instanceof ShadowRoot) | |
7515 adoptOlderShadowRoots(node, doc); | |
7516 for (var child = node.firstChild; child; child = child.nextSibling) { | |
7517 adoptSubtree(child, doc); | |
7518 } | |
7519 } | |
7520 | |
7521 function adoptOlderShadowRoots(shadowRoot, doc) { | |
7522 var oldShadowRoot = shadowRoot.olderShadowRoot; | |
7523 if (oldShadowRoot) | |
7524 doc.adoptNode(oldShadowRoot); | |
7525 } | |
7526 | |
7527 var originalGetSelection = document.getSelection; | |
7528 | |
7529 mixin(Document.prototype, { | |
7530 adoptNode: function(node) { | |
7531 if (node.parentNode) | |
7532 node.parentNode.removeChild(node); | |
7533 adoptNodeNoRemove(node, this); | |
7534 return node; | |
7535 }, | |
7536 elementFromPoint: function(x, y) { | |
7537 return elementFromPoint(this, this, x, y); | |
7538 }, | |
7539 importNode: function(node, deep) { | |
7540 return cloneNode(node, deep, unsafeUnwrap(this)); | |
7541 }, | |
7542 getSelection: function() { | |
7543 renderAllPending(); | |
7544 return new Selection(originalGetSelection.call(unwrap(this))); | |
7545 }, | |
7546 getElementsByName: function(name) { | |
7547 return SelectorsInterface.querySelectorAll.call(this, | |
7548 '[name=' + JSON.stringify(String(name)) + ']'); | |
7549 } | |
7550 }); | |
7551 | |
7552 if (document.registerElement) { | |
7553 var originalRegisterElement = document.registerElement; | |
7554 Document.prototype.registerElement = function(tagName, object) { | |
7555 var prototype, extendsOption; | |
7556 if (object !== undefined) { | |
7557 prototype = object.prototype; | |
7558 extendsOption = object.extends; | |
7559 } | |
7560 | |
7561 if (!prototype) | |
7562 prototype = Object.create(HTMLElement.prototype); | |
7563 | |
7564 | |
7565 // If we already used the object as a prototype for another custom | |
7566 // element. | |
7567 if (scope.nativePrototypeTable.get(prototype)) { | |
7568 // TODO(arv): DOMException | |
7569 throw new Error('NotSupportedError'); | |
7570 } | |
7571 | |
7572 // Find first object on the prototype chain that already have a native | |
7573 // prototype. Keep track of all the objects before that so we can create | |
7574 // a similar structure for the native case. | |
7575 var proto = Object.getPrototypeOf(prototype); | |
7576 var nativePrototype; | |
7577 var prototypes = []; | |
7578 while (proto) { | |
7579 nativePrototype = scope.nativePrototypeTable.get(proto); | |
7580 if (nativePrototype) | |
7581 break; | |
7582 prototypes.push(proto); | |
7583 proto = Object.getPrototypeOf(proto); | |
7584 } | |
7585 | |
7586 if (!nativePrototype) { | |
7587 // TODO(arv): DOMException | |
7588 throw new Error('NotSupportedError'); | |
7589 } | |
7590 | |
7591 // This works by creating a new prototype object that is empty, but has | |
7592 // the native prototype as its proto. The original prototype object | |
7593 // passed into register is used as the wrapper prototype. | |
7594 | |
7595 var newPrototype = Object.create(nativePrototype); | |
7596 for (var i = prototypes.length - 1; i >= 0; i--) { | |
7597 newPrototype = Object.create(newPrototype); | |
7598 } | |
7599 | |
7600 // Add callbacks if present. | |
7601 // Names are taken from: | |
7602 // https://code.google.com/p/chromium/codesearch#chromium/src/third_part
y/WebKit/Source/bindings/v8/CustomElementConstructorBuilder.cpp&sq=package:chrom
ium&type=cs&l=156 | |
7603 // and not from the spec since the spec is out of date. | |
7604 [ | |
7605 'createdCallback', | |
7606 'attachedCallback', | |
7607 'detachedCallback', | |
7608 'attributeChangedCallback', | |
7609 ].forEach(function(name) { | |
7610 var f = prototype[name]; | |
7611 if (!f) | |
7612 return; | |
7613 newPrototype[name] = function() { | |
7614 // if this element has been wrapped prior to registration, | |
7615 // the wrapper is stale; in this case rewrap | |
7616 if (!(wrap(this) instanceof CustomElementConstructor)) { | |
7617 rewrap(this); | |
7618 } | |
7619 f.apply(wrap(this), arguments); | |
7620 }; | |
7621 }); | |
7622 | |
7623 var p = {prototype: newPrototype}; | |
7624 if (extendsOption) | |
7625 p.extends = extendsOption; | |
7626 | |
7627 function CustomElementConstructor(node) { | |
7628 if (!node) { | |
7629 if (extendsOption) { | |
7630 return document.createElement(extendsOption, tagName); | |
7631 } else { | |
7632 return document.createElement(tagName); | |
7633 } | |
7634 } | |
7635 setWrapper(node, this); | |
7636 } | |
7637 CustomElementConstructor.prototype = prototype; | |
7638 CustomElementConstructor.prototype.constructor = CustomElementConstructor; | |
7639 | |
7640 scope.constructorTable.set(newPrototype, CustomElementConstructor); | |
7641 scope.nativePrototypeTable.set(prototype, newPrototype); | |
7642 | |
7643 // registration is synchronous so do it last | |
7644 var nativeConstructor = originalRegisterElement.call(unwrap(this), | |
7645 tagName, p); | |
7646 return CustomElementConstructor; | |
7647 }; | |
7648 | |
7649 forwardMethodsToWrapper([ | |
7650 window.HTMLDocument || window.Document, // Gecko adds these to HTMLDocume
nt | |
7651 ], [ | |
7652 'registerElement', | |
7653 ]); | |
7654 } | |
7655 | |
7656 // We also override some of the methods on document.body and document.head | |
7657 // for convenience. | |
7658 forwardMethodsToWrapper([ | |
7659 window.HTMLBodyElement, | |
7660 window.HTMLDocument || window.Document, // Gecko adds these to HTMLDocument | |
7661 window.HTMLHeadElement, | |
7662 window.HTMLHtmlElement, | |
7663 ], [ | |
7664 'appendChild', | |
7665 'compareDocumentPosition', | |
7666 'contains', | |
7667 'getElementsByClassName', | |
7668 'getElementsByTagName', | |
7669 'getElementsByTagNameNS', | |
7670 'insertBefore', | |
7671 'querySelector', | |
7672 'querySelectorAll', | |
7673 'removeChild', | |
7674 'replaceChild', | |
7675 ].concat(matchesNames)); | |
7676 | |
7677 forwardMethodsToWrapper([ | |
7678 window.HTMLDocument || window.Document, // Gecko adds these to HTMLDocument | |
7679 ], [ | |
7680 'adoptNode', | |
7681 'importNode', | |
7682 'contains', | |
7683 'createComment', | |
7684 'createDocumentFragment', | |
7685 'createElement', | |
7686 'createElementNS', | |
7687 'createEvent', | |
7688 'createEventNS', | |
7689 'createRange', | |
7690 'createTextNode', | |
7691 'elementFromPoint', | |
7692 'getElementById', | |
7693 'getElementsByName', | |
7694 'getSelection', | |
7695 ]); | |
7696 | |
7697 mixin(Document.prototype, GetElementsByInterface); | |
7698 mixin(Document.prototype, ParentNodeInterface); | |
7699 mixin(Document.prototype, SelectorsInterface); | |
7700 | |
7701 mixin(Document.prototype, { | |
7702 get implementation() { | |
7703 var implementation = implementationTable.get(this); | |
7704 if (implementation) | |
7705 return implementation; | |
7706 implementation = | |
7707 new DOMImplementation(unwrap(this).implementation); | |
7708 implementationTable.set(this, implementation); | |
7709 return implementation; | |
7710 }, | |
7711 | |
7712 get defaultView() { | |
7713 return wrap(unwrap(this).defaultView); | |
7714 } | |
7715 }); | |
7716 | |
7717 registerWrapper(window.Document, Document, | |
7718 document.implementation.createHTMLDocument('')); | |
7719 | |
7720 // Both WebKit and Gecko uses HTMLDocument for document. HTML5/DOM only has | |
7721 // one Document interface and IE implements the standard correctly. | |
7722 if (window.HTMLDocument) | |
7723 registerWrapper(window.HTMLDocument, Document); | |
7724 | |
7725 wrapEventTargetMethods([ | |
7726 window.HTMLBodyElement, | |
7727 window.HTMLDocument || window.Document, // Gecko adds these to HTMLDocument | |
7728 window.HTMLHeadElement, | |
7729 ]); | |
7730 | |
7731 function DOMImplementation(impl) { | |
7732 setWrapper(impl, this); | |
7733 } | |
7734 | |
7735 function wrapImplMethod(constructor, name) { | |
7736 var original = document.implementation[name]; | |
7737 constructor.prototype[name] = function() { | |
7738 return wrap(original.apply(unsafeUnwrap(this), arguments)); | |
7739 }; | |
7740 } | |
7741 | |
7742 function forwardImplMethod(constructor, name) { | |
7743 var original = document.implementation[name]; | |
7744 constructor.prototype[name] = function() { | |
7745 return original.apply(unsafeUnwrap(this), arguments); | |
7746 }; | |
7747 } | |
7748 | |
7749 wrapImplMethod(DOMImplementation, 'createDocumentType'); | |
7750 wrapImplMethod(DOMImplementation, 'createDocument'); | |
7751 wrapImplMethod(DOMImplementation, 'createHTMLDocument'); | |
7752 forwardImplMethod(DOMImplementation, 'hasFeature'); | |
7753 | |
7754 registerWrapper(window.DOMImplementation, DOMImplementation); | |
7755 | |
7756 forwardMethodsToWrapper([ | |
7757 window.DOMImplementation, | |
7758 ], [ | |
7759 'createDocumentType', | |
7760 'createDocument', | |
7761 'createHTMLDocument', | |
7762 'hasFeature', | |
7763 ]); | |
7764 | |
7765 scope.adoptNodeNoRemove = adoptNodeNoRemove; | |
7766 scope.wrappers.DOMImplementation = DOMImplementation; | |
7767 scope.wrappers.Document = Document; | |
7768 | |
7769 })(window.ShadowDOMPolyfill); | |
7770 | |
7771 // Copyright 2013 The Polymer Authors. All rights reserved. | |
7772 // Use of this source code is goverened by a BSD-style | |
7773 // license that can be found in the LICENSE file. | |
7774 | |
7775 (function(scope) { | |
7776 'use strict'; | |
7777 | |
7778 var EventTarget = scope.wrappers.EventTarget; | |
7779 var Selection = scope.wrappers.Selection; | |
7780 var mixin = scope.mixin; | |
7781 var registerWrapper = scope.registerWrapper; | |
7782 var renderAllPending = scope.renderAllPending; | |
7783 var unwrap = scope.unwrap; | |
7784 var unwrapIfNeeded = scope.unwrapIfNeeded; | |
7785 var wrap = scope.wrap; | |
7786 | |
7787 var OriginalWindow = window.Window; | |
7788 var originalGetComputedStyle = window.getComputedStyle; | |
7789 var originalGetDefaultComputedStyle = window.getDefaultComputedStyle; | |
7790 var originalGetSelection = window.getSelection; | |
7791 | |
7792 function Window(impl) { | |
7793 EventTarget.call(this, impl); | |
7794 } | |
7795 Window.prototype = Object.create(EventTarget.prototype); | |
7796 | |
7797 OriginalWindow.prototype.getComputedStyle = function(el, pseudo) { | |
7798 return wrap(this || window).getComputedStyle(unwrapIfNeeded(el), pseudo); | |
7799 }; | |
7800 | |
7801 // Mozilla proprietary extension. | |
7802 if (originalGetDefaultComputedStyle) { | |
7803 OriginalWindow.prototype.getDefaultComputedStyle = function(el, pseudo) { | |
7804 return wrap(this || window).getDefaultComputedStyle( | |
7805 unwrapIfNeeded(el), pseudo); | |
7806 }; | |
7807 } | |
7808 | |
7809 OriginalWindow.prototype.getSelection = function() { | |
7810 return wrap(this || window).getSelection(); | |
7811 }; | |
7812 | |
7813 // Work around for https://bugzilla.mozilla.org/show_bug.cgi?id=943065 | |
7814 delete window.getComputedStyle; | |
7815 delete window.getDefaultComputedStyle; | |
7816 delete window.getSelection; | |
7817 | |
7818 ['addEventListener', 'removeEventListener', 'dispatchEvent'].forEach( | |
7819 function(name) { | |
7820 OriginalWindow.prototype[name] = function() { | |
7821 var w = wrap(this || window); | |
7822 return w[name].apply(w, arguments); | |
7823 }; | |
7824 | |
7825 // Work around for https://bugzilla.mozilla.org/show_bug.cgi?id=943065 | |
7826 delete window[name]; | |
7827 }); | |
7828 | |
7829 mixin(Window.prototype, { | |
7830 getComputedStyle: function(el, pseudo) { | |
7831 renderAllPending(); | |
7832 return originalGetComputedStyle.call(unwrap(this), unwrapIfNeeded(el), | |
7833 pseudo); | |
7834 }, | |
7835 getSelection: function() { | |
7836 renderAllPending(); | |
7837 return new Selection(originalGetSelection.call(unwrap(this))); | |
7838 }, | |
7839 | |
7840 get document() { | |
7841 return wrap(unwrap(this).document); | |
7842 } | |
7843 }); | |
7844 | |
7845 // Mozilla proprietary extension. | |
7846 if (originalGetDefaultComputedStyle) { | |
7847 Window.prototype.getDefaultComputedStyle = function(el, pseudo) { | |
7848 renderAllPending(); | |
7849 return originalGetDefaultComputedStyle.call(unwrap(this), | |
7850 unwrapIfNeeded(el),pseudo); | |
7851 }; | |
7852 } | |
7853 | |
7854 registerWrapper(OriginalWindow, Window, window); | |
7855 | |
7856 scope.wrappers.Window = Window; | |
7857 | |
7858 })(window.ShadowDOMPolyfill); | |
7859 | |
7860 /** | |
7861 * Copyright 2014 The Polymer Authors. All rights reserved. | |
7862 * Use of this source code is goverened by a BSD-style | |
7863 * license that can be found in the LICENSE file. | |
7864 */ | |
7865 | |
7866 (function(scope) { | |
7867 'use strict'; | |
7868 | |
7869 var unwrap = scope.unwrap; | |
7870 | |
7871 // DataTransfer (Clipboard in old Blink/WebKit) has a single method that | |
7872 // requires wrapping. Since it is only a method we do not need a real wrapper, | |
7873 // we can just override the method. | |
7874 | |
7875 var OriginalDataTransfer = window.DataTransfer || window.Clipboard; | |
7876 var OriginalDataTransferSetDragImage = | |
7877 OriginalDataTransfer.prototype.setDragImage; | |
7878 | |
7879 if (OriginalDataTransferSetDragImage) { | |
7880 OriginalDataTransfer.prototype.setDragImage = function(image, x, y) { | |
7881 OriginalDataTransferSetDragImage.call(this, unwrap(image), x, y); | |
7882 }; | |
7883 } | |
7884 | |
7885 })(window.ShadowDOMPolyfill); | |
7886 | |
7887 /** | |
7888 * Copyright 2014 The Polymer Authors. All rights reserved. | |
7889 * Use of this source code is goverened by a BSD-style | |
7890 * license that can be found in the LICENSE file. | |
7891 */ | |
7892 | |
7893 (function(scope) { | |
7894 'use strict'; | |
7895 | |
7896 var registerWrapper = scope.registerWrapper; | |
7897 var setWrapper = scope.setWrapper; | |
7898 var unwrap = scope.unwrap; | |
7899 | |
7900 var OriginalFormData = window.FormData; | |
7901 if (!OriginalFormData) return; | |
7902 | |
7903 function FormData(formElement) { | |
7904 var impl; | |
7905 if (formElement instanceof OriginalFormData) { | |
7906 impl = formElement; | |
7907 } else { | |
7908 impl = new OriginalFormData(formElement && unwrap(formElement)); | |
7909 } | |
7910 setWrapper(impl, this); | |
7911 } | |
7912 | |
7913 registerWrapper(OriginalFormData, FormData, new OriginalFormData()); | |
7914 | |
7915 scope.wrappers.FormData = FormData; | |
7916 | |
7917 })(window.ShadowDOMPolyfill); | |
7918 | |
7919 /* | |
7920 * Copyright 2014 The Polymer Authors. All rights reserved. | |
7921 * Use of this source code is goverened by a BSD-style | |
7922 * license that can be found in the LICENSE file. | |
7923 */ | |
7924 | |
7925 (function(scope) { | |
7926 'use strict'; | |
7927 | |
7928 var unwrapIfNeeded = scope.unwrapIfNeeded; | |
7929 var originalSend = XMLHttpRequest.prototype.send; | |
7930 | |
7931 // Since we only need to adjust XHR.send, we just patch it instead of wrapping | |
7932 // the entire object. This happens when FormData is passed. | |
7933 XMLHttpRequest.prototype.send = function(obj) { | |
7934 return originalSend.call(this, unwrapIfNeeded(obj)); | |
7935 }; | |
7936 | |
7937 })(window.ShadowDOMPolyfill); | |
7938 | |
7939 // Copyright 2013 The Polymer Authors. All rights reserved. | |
7940 // Use of this source code is goverened by a BSD-style | |
7941 // license that can be found in the LICENSE file. | |
7942 | |
7943 (function(scope) { | |
7944 'use strict'; | |
7945 | |
7946 var isWrapperFor = scope.isWrapperFor; | |
7947 | |
7948 // This is a list of the elements we currently override the global constructor | |
7949 // for. | |
7950 var elements = { | |
7951 'a': 'HTMLAnchorElement', | |
7952 // Do not create an applet element by default since it shows a warning in | |
7953 // IE. | |
7954 // https://github.com/Polymer/polymer/issues/217 | |
7955 // 'applet': 'HTMLAppletElement', | |
7956 'area': 'HTMLAreaElement', | |
7957 'audio': 'HTMLAudioElement', | |
7958 'base': 'HTMLBaseElement', | |
7959 'body': 'HTMLBodyElement', | |
7960 'br': 'HTMLBRElement', | |
7961 'button': 'HTMLButtonElement', | |
7962 'canvas': 'HTMLCanvasElement', | |
7963 'caption': 'HTMLTableCaptionElement', | |
7964 'col': 'HTMLTableColElement', | |
7965 // 'command': 'HTMLCommandElement', // Not fully implemented in Gecko. | |
7966 'content': 'HTMLContentElement', | |
7967 'data': 'HTMLDataElement', | |
7968 'datalist': 'HTMLDataListElement', | |
7969 'del': 'HTMLModElement', | |
7970 'dir': 'HTMLDirectoryElement', | |
7971 'div': 'HTMLDivElement', | |
7972 'dl': 'HTMLDListElement', | |
7973 'embed': 'HTMLEmbedElement', | |
7974 'fieldset': 'HTMLFieldSetElement', | |
7975 'font': 'HTMLFontElement', | |
7976 'form': 'HTMLFormElement', | |
7977 'frame': 'HTMLFrameElement', | |
7978 'frameset': 'HTMLFrameSetElement', | |
7979 'h1': 'HTMLHeadingElement', | |
7980 'head': 'HTMLHeadElement', | |
7981 'hr': 'HTMLHRElement', | |
7982 'html': 'HTMLHtmlElement', | |
7983 'iframe': 'HTMLIFrameElement', | |
7984 'img': 'HTMLImageElement', | |
7985 'input': 'HTMLInputElement', | |
7986 'keygen': 'HTMLKeygenElement', | |
7987 'label': 'HTMLLabelElement', | |
7988 'legend': 'HTMLLegendElement', | |
7989 'li': 'HTMLLIElement', | |
7990 'link': 'HTMLLinkElement', | |
7991 'map': 'HTMLMapElement', | |
7992 'marquee': 'HTMLMarqueeElement', | |
7993 'menu': 'HTMLMenuElement', | |
7994 'menuitem': 'HTMLMenuItemElement', | |
7995 'meta': 'HTMLMetaElement', | |
7996 'meter': 'HTMLMeterElement', | |
7997 'object': 'HTMLObjectElement', | |
7998 'ol': 'HTMLOListElement', | |
7999 'optgroup': 'HTMLOptGroupElement', | |
8000 'option': 'HTMLOptionElement', | |
8001 'output': 'HTMLOutputElement', | |
8002 'p': 'HTMLParagraphElement', | |
8003 'param': 'HTMLParamElement', | |
8004 'pre': 'HTMLPreElement', | |
8005 'progress': 'HTMLProgressElement', | |
8006 'q': 'HTMLQuoteElement', | |
8007 'script': 'HTMLScriptElement', | |
8008 'select': 'HTMLSelectElement', | |
8009 'shadow': 'HTMLShadowElement', | |
8010 'source': 'HTMLSourceElement', | |
8011 'span': 'HTMLSpanElement', | |
8012 'style': 'HTMLStyleElement', | |
8013 'table': 'HTMLTableElement', | |
8014 'tbody': 'HTMLTableSectionElement', | |
8015 // WebKit and Moz are wrong: | |
8016 // https://bugs.webkit.org/show_bug.cgi?id=111469 | |
8017 // https://bugzilla.mozilla.org/show_bug.cgi?id=848096 | |
8018 // 'td': 'HTMLTableCellElement', | |
8019 'template': 'HTMLTemplateElement', | |
8020 'textarea': 'HTMLTextAreaElement', | |
8021 'thead': 'HTMLTableSectionElement', | |
8022 'time': 'HTMLTimeElement', | |
8023 'title': 'HTMLTitleElement', | |
8024 'tr': 'HTMLTableRowElement', | |
8025 'track': 'HTMLTrackElement', | |
8026 'ul': 'HTMLUListElement', | |
8027 'video': 'HTMLVideoElement', | |
8028 }; | |
8029 | |
8030 function overrideConstructor(tagName) { | |
8031 var nativeConstructorName = elements[tagName]; | |
8032 var nativeConstructor = window[nativeConstructorName]; | |
8033 if (!nativeConstructor) | |
8034 return; | |
8035 var element = document.createElement(tagName); | |
8036 var wrapperConstructor = element.constructor; | |
8037 window[nativeConstructorName] = wrapperConstructor; | |
8038 } | |
8039 | |
8040 Object.keys(elements).forEach(overrideConstructor); | |
8041 | |
8042 Object.getOwnPropertyNames(scope.wrappers).forEach(function(name) { | |
8043 window[name] = scope.wrappers[name] | |
8044 }); | |
8045 | |
8046 })(window.ShadowDOMPolyfill); | |
8047 | |
8048 /* | |
8049 * Copyright (c) 2014 The Polymer Project Authors. All rights reserved. | |
8050 * This code may only be used under the BSD style license found at http://polyme
r.github.io/LICENSE.txt | |
8051 * The complete set of authors may be found at http://polymer.github.io/AUTHORS.
txt | |
8052 * The complete set of contributors may be found at http://polymer.github.io/CON
TRIBUTORS.txt | |
8053 * Code distributed by Google as part of the polymer project is also | |
8054 * subject to an additional IP rights grant found at http://polymer.github.io/PA
TENTS.txt | |
8055 */ | |
8056 | |
8057 (function(scope) { | |
8058 | |
8059 // convenient global | |
8060 window.wrap = ShadowDOMPolyfill.wrapIfNeeded; | |
8061 window.unwrap = ShadowDOMPolyfill.unwrapIfNeeded; | |
8062 | |
8063 // users may want to customize other types | |
8064 // TODO(sjmiles): 'button' is now supported by ShadowDOMPolyfill, but | |
8065 // I've left this code here in case we need to temporarily patch another | |
8066 // type | |
8067 /* | |
8068 (function() { | |
8069 var elts = {HTMLButtonElement: 'button'}; | |
8070 for (var c in elts) { | |
8071 window[c] = function() { throw 'Patched Constructor'; }; | |
8072 window[c].prototype = Object.getPrototypeOf( | |
8073 document.createElement(elts[c])); | |
8074 } | |
8075 })(); | |
8076 */ | |
8077 | |
8078 // patch in prefixed name | |
8079 Object.defineProperty(Element.prototype, 'webkitShadowRoot', | |
8080 Object.getOwnPropertyDescriptor(Element.prototype, 'shadowRoot')); | |
8081 | |
8082 var originalCreateShadowRoot = Element.prototype.createShadowRoot; | |
8083 Element.prototype.createShadowRoot = function() { | |
8084 var root = originalCreateShadowRoot.call(this); | |
8085 CustomElements.watchShadow(this); | |
8086 return root; | |
8087 }; | |
8088 | |
8089 Element.prototype.webkitCreateShadowRoot = Element.prototype.createShadowRoot; | |
8090 | |
8091 function queryShadow(node, selector) { | |
8092 var m, el = node.firstElementChild; | |
8093 var shadows, sr, i; | |
8094 shadows = []; | |
8095 sr = node.shadowRoot; | |
8096 while(sr) { | |
8097 shadows.push(sr); | |
8098 sr = sr.olderShadowRoot; | |
8099 } | |
8100 for(i = shadows.length - 1; i >= 0; i--) { | |
8101 m = shadows[i].querySelector(selector); | |
8102 if (m) { | |
8103 return m; | |
8104 } | |
8105 } | |
8106 while(el) { | |
8107 m = queryShadow(el, selector); | |
8108 if (m) { | |
8109 return m; | |
8110 } | |
8111 el = el.nextElementSibling; | |
8112 } | |
8113 return null; | |
8114 } | |
8115 | |
8116 function queryAllShadows(node, selector, results) { | |
8117 var el = node.firstElementChild; | |
8118 var temp, sr, shadows, i, j; | |
8119 shadows = []; | |
8120 sr = node.shadowRoot; | |
8121 while(sr) { | |
8122 shadows.push(sr); | |
8123 sr = sr.olderShadowRoot; | |
8124 } | |
8125 for (i = shadows.length - 1; i >= 0; i--) { | |
8126 temp = shadows[i].querySelectorAll(selector); | |
8127 for(j = 0; j < temp.length; j++) { | |
8128 results.push(temp[j]); | |
8129 } | |
8130 } | |
8131 while (el) { | |
8132 queryAllShadows(el, selector, results); | |
8133 el = el.nextElementSibling; | |
8134 } | |
8135 return results; | |
8136 } | |
8137 | |
8138 scope.queryAllShadows = function(node, selector, all) { | |
8139 if (all) { | |
8140 return queryAllShadows(node, selector, []); | |
8141 } else { | |
8142 return queryShadow(node, selector); | |
8143 } | |
8144 }; | |
8145 })(window.Platform); | |
8146 | |
8147 /* | |
8148 * Copyright (c) 2014 The Polymer Project Authors. All rights reserved. | |
8149 * This code may only be used under the BSD style license found at http://polyme
r.github.io/LICENSE.txt | |
8150 * The complete set of authors may be found at http://polymer.github.io/AUTHORS.
txt | |
8151 * The complete set of contributors may be found at http://polymer.github.io/CON
TRIBUTORS.txt | |
8152 * Code distributed by Google as part of the polymer project is also | |
8153 * subject to an additional IP rights grant found at http://polymer.github.io/PA
TENTS.txt | |
8154 */ | |
8155 | |
8156 /* | |
8157 This is a limited shim for ShadowDOM css styling. | |
8158 https://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/shadow/index.html#style
s | |
8159 | |
8160 The intention here is to support only the styling features which can be | |
8161 relatively simply implemented. The goal is to allow users to avoid the | |
8162 most obvious pitfalls and do so without compromising performance significantly
. | |
8163 For ShadowDOM styling that's not covered here, a set of best practices | |
8164 can be provided that should allow users to accomplish more complex styling. | |
8165 | |
8166 The following is a list of specific ShadowDOM styling features and a brief | |
8167 discussion of the approach used to shim. | |
8168 | |
8169 Shimmed features: | |
8170 | |
8171 * :host, :host-context: ShadowDOM allows styling of the shadowRoot's host | |
8172 element using the :host rule. To shim this feature, the :host styles are | |
8173 reformatted and prefixed with a given scope name and promoted to a | |
8174 document level stylesheet. | |
8175 For example, given a scope name of .foo, a rule like this: | |
8176 | |
8177 :host { | |
8178 background: red; | |
8179 } | |
8180 } | |
8181 | |
8182 becomes: | |
8183 | |
8184 .foo { | |
8185 background: red; | |
8186 } | |
8187 | |
8188 * encapsultion: Styles defined within ShadowDOM, apply only to | |
8189 dom inside the ShadowDOM. Polymer uses one of two techniques to imlement | |
8190 this feature. | |
8191 | |
8192 By default, rules are prefixed with the host element tag name | |
8193 as a descendant selector. This ensures styling does not leak out of the 'top' | |
8194 of the element's ShadowDOM. For example, | |
8195 | |
8196 div { | |
8197 font-weight: bold; | |
8198 } | |
8199 | |
8200 becomes: | |
8201 | |
8202 x-foo div { | |
8203 font-weight: bold; | |
8204 } | |
8205 | |
8206 becomes: | |
8207 | |
8208 | |
8209 Alternatively, if Platform.ShadowCSS.strictStyling is set to true then | |
8210 selectors are scoped by adding an attribute selector suffix to each | |
8211 simple selector that contains the host element tag name. Each element | |
8212 in the element's ShadowDOM template is also given the scope attribute. | |
8213 Thus, these rules match only elements that have the scope attribute. | |
8214 For example, given a scope name of x-foo, a rule like this: | |
8215 | |
8216 div { | |
8217 font-weight: bold; | |
8218 } | |
8219 | |
8220 becomes: | |
8221 | |
8222 div[x-foo] { | |
8223 font-weight: bold; | |
8224 } | |
8225 | |
8226 Note that elements that are dynamically added to a scope must have the scope | |
8227 selector added to them manually. | |
8228 | |
8229 * upper/lower bound encapsulation: Styles which are defined outside a | |
8230 shadowRoot should not cross the ShadowDOM boundary and should not apply | |
8231 inside a shadowRoot. | |
8232 | |
8233 This styling behavior is not emulated. Some possible ways to do this that | |
8234 were rejected due to complexity and/or performance concerns include: (1) reset | |
8235 every possible property for every possible selector for a given scope name; | |
8236 (2) re-implement css in javascript. | |
8237 | |
8238 As an alternative, users should make sure to use selectors | |
8239 specific to the scope in which they are working. | |
8240 | |
8241 * ::distributed: This behavior is not emulated. It's often not necessary | |
8242 to style the contents of a specific insertion point and instead, descendants | |
8243 of the host element can be styled selectively. Users can also create an | |
8244 extra node around an insertion point and style that node's contents | |
8245 via descendent selectors. For example, with a shadowRoot like this: | |
8246 | |
8247 <style> | |
8248 ::content(div) { | |
8249 background: red; | |
8250 } | |
8251 </style> | |
8252 <content></content> | |
8253 | |
8254 could become: | |
8255 | |
8256 <style> | |
8257 / *@polyfill .content-container div * / | |
8258 ::content(div) { | |
8259 background: red; | |
8260 } | |
8261 </style> | |
8262 <div class="content-container"> | |
8263 <content></content> | |
8264 </div> | |
8265 | |
8266 Note the use of @polyfill in the comment above a ShadowDOM specific style | |
8267 declaration. This is a directive to the styling shim to use the selector | |
8268 in comments in lieu of the next selector when running under polyfill. | |
8269 */ | |
8270 (function(scope) { | |
8271 | |
8272 var ShadowCSS = { | |
8273 strictStyling: false, | |
8274 registry: {}, | |
8275 // Shim styles for a given root associated with a name and extendsName | |
8276 // 1. cache root styles by name | |
8277 // 2. optionally tag root nodes with scope name | |
8278 // 3. shim polyfill directives /* @polyfill */ and /* @polyfill-rule */ | |
8279 // 4. shim :host and scoping | |
8280 shimStyling: function(root, name, extendsName) { | |
8281 var scopeStyles = this.prepareRoot(root, name, extendsName); | |
8282 var typeExtension = this.isTypeExtension(extendsName); | |
8283 var scopeSelector = this.makeScopeSelector(name, typeExtension); | |
8284 // use caching to make working with styles nodes easier and to facilitate | |
8285 // lookup of extendee | |
8286 var cssText = stylesToCssText(scopeStyles, true); | |
8287 cssText = this.scopeCssText(cssText, scopeSelector); | |
8288 // cache shimmed css on root for user extensibility | |
8289 if (root) { | |
8290 root.shimmedStyle = cssText; | |
8291 } | |
8292 // add style to document | |
8293 this.addCssToDocument(cssText, name); | |
8294 }, | |
8295 /* | |
8296 * Shim a style element with the given selector. Returns cssText that can | |
8297 * be included in the document via Platform.ShadowCSS.addCssToDocument(css). | |
8298 */ | |
8299 shimStyle: function(style, selector) { | |
8300 return this.shimCssText(style.textContent, selector); | |
8301 }, | |
8302 /* | |
8303 * Shim some cssText with the given selector. Returns cssText that can | |
8304 * be included in the document via Platform.ShadowCSS.addCssToDocument(css). | |
8305 */ | |
8306 shimCssText: function(cssText, selector) { | |
8307 cssText = this.insertDirectives(cssText); | |
8308 return this.scopeCssText(cssText, selector); | |
8309 }, | |
8310 makeScopeSelector: function(name, typeExtension) { | |
8311 if (name) { | |
8312 return typeExtension ? '[is=' + name + ']' : name; | |
8313 } | |
8314 return ''; | |
8315 }, | |
8316 isTypeExtension: function(extendsName) { | |
8317 return extendsName && extendsName.indexOf('-') < 0; | |
8318 }, | |
8319 prepareRoot: function(root, name, extendsName) { | |
8320 var def = this.registerRoot(root, name, extendsName); | |
8321 this.replaceTextInStyles(def.rootStyles, this.insertDirectives); | |
8322 // remove existing style elements | |
8323 this.removeStyles(root, def.rootStyles); | |
8324 // apply strict attr | |
8325 if (this.strictStyling) { | |
8326 this.applyScopeToContent(root, name); | |
8327 } | |
8328 return def.scopeStyles; | |
8329 }, | |
8330 removeStyles: function(root, styles) { | |
8331 for (var i=0, l=styles.length, s; (i<l) && (s=styles[i]); i++) { | |
8332 s.parentNode.removeChild(s); | |
8333 } | |
8334 }, | |
8335 registerRoot: function(root, name, extendsName) { | |
8336 var def = this.registry[name] = { | |
8337 root: root, | |
8338 name: name, | |
8339 extendsName: extendsName | |
8340 } | |
8341 var styles = this.findStyles(root); | |
8342 def.rootStyles = styles; | |
8343 def.scopeStyles = def.rootStyles; | |
8344 var extendee = this.registry[def.extendsName]; | |
8345 if (extendee) { | |
8346 def.scopeStyles = extendee.scopeStyles.concat(def.scopeStyles); | |
8347 } | |
8348 return def; | |
8349 }, | |
8350 findStyles: function(root) { | |
8351 if (!root) { | |
8352 return []; | |
8353 } | |
8354 var styles = root.querySelectorAll('style'); | |
8355 return Array.prototype.filter.call(styles, function(s) { | |
8356 return !s.hasAttribute(NO_SHIM_ATTRIBUTE); | |
8357 }); | |
8358 }, | |
8359 applyScopeToContent: function(root, name) { | |
8360 if (root) { | |
8361 // add the name attribute to each node in root. | |
8362 Array.prototype.forEach.call(root.querySelectorAll('*'), | |
8363 function(node) { | |
8364 node.setAttribute(name, ''); | |
8365 }); | |
8366 // and template contents too | |
8367 Array.prototype.forEach.call(root.querySelectorAll('template'), | |
8368 function(template) { | |
8369 this.applyScopeToContent(template.content, name); | |
8370 }, | |
8371 this); | |
8372 } | |
8373 }, | |
8374 insertDirectives: function(cssText) { | |
8375 cssText = this.insertPolyfillDirectivesInCssText(cssText); | |
8376 return this.insertPolyfillRulesInCssText(cssText); | |
8377 }, | |
8378 /* | |
8379 * Process styles to convert native ShadowDOM rules that will trip | |
8380 * up the css parser; we rely on decorating the stylesheet with inert rules. | |
8381 * | |
8382 * For example, we convert this rule: | |
8383 * | |
8384 * polyfill-next-selector { content: ':host menu-item'; } | |
8385 * ::content menu-item { | |
8386 * | |
8387 * to this: | |
8388 * | |
8389 * scopeName menu-item { | |
8390 * | |
8391 **/ | |
8392 insertPolyfillDirectivesInCssText: function(cssText) { | |
8393 // TODO(sorvell): remove either content or comment | |
8394 cssText = cssText.replace(cssCommentNextSelectorRe, function(match, p1) { | |
8395 // remove end comment delimiter and add block start | |
8396 return p1.slice(0, -2) + '{'; | |
8397 }); | |
8398 return cssText.replace(cssContentNextSelectorRe, function(match, p1) { | |
8399 return p1 + ' {'; | |
8400 }); | |
8401 }, | |
8402 /* | |
8403 * Process styles to add rules which will only apply under the polyfill | |
8404 * | |
8405 * For example, we convert this rule: | |
8406 * | |
8407 * polyfill-rule { | |
8408 * content: ':host menu-item'; | |
8409 * ... | |
8410 * } | |
8411 * | |
8412 * to this: | |
8413 * | |
8414 * scopeName menu-item {...} | |
8415 * | |
8416 **/ | |
8417 insertPolyfillRulesInCssText: function(cssText) { | |
8418 // TODO(sorvell): remove either content or comment | |
8419 cssText = cssText.replace(cssCommentRuleRe, function(match, p1) { | |
8420 // remove end comment delimiter | |
8421 return p1.slice(0, -1); | |
8422 }); | |
8423 return cssText.replace(cssContentRuleRe, function(match, p1, p2, p3) { | |
8424 var rule = match.replace(p1, '').replace(p2, ''); | |
8425 return p3 + rule; | |
8426 }); | |
8427 }, | |
8428 /* Ensure styles are scoped. Pseudo-scoping takes a rule like: | |
8429 * | |
8430 * .foo {... } | |
8431 * | |
8432 * and converts this to | |
8433 * | |
8434 * scopeName .foo { ... } | |
8435 */ | |
8436 scopeCssText: function(cssText, scopeSelector) { | |
8437 var unscoped = this.extractUnscopedRulesFromCssText(cssText); | |
8438 cssText = this.insertPolyfillHostInCssText(cssText); | |
8439 cssText = this.convertColonHost(cssText); | |
8440 cssText = this.convertColonHostContext(cssText); | |
8441 cssText = this.convertShadowDOMSelectors(cssText); | |
8442 if (scopeSelector) { | |
8443 var self = this, cssText; | |
8444 withCssRules(cssText, function(rules) { | |
8445 cssText = self.scopeRules(rules, scopeSelector); | |
8446 }); | |
8447 | |
8448 } | |
8449 cssText = cssText + '\n' + unscoped; | |
8450 return cssText.trim(); | |
8451 }, | |
8452 /* | |
8453 * Process styles to add rules which will only apply under the polyfill | |
8454 * and do not process via CSSOM. (CSSOM is destructive to rules on rare | |
8455 * occasions, e.g. -webkit-calc on Safari.) | |
8456 * For example, we convert this rule: | |
8457 * | |
8458 * (comment start) @polyfill-unscoped-rule menu-item { | |
8459 * ... } (comment end) | |
8460 * | |
8461 * to this: | |
8462 * | |
8463 * menu-item {...} | |
8464 * | |
8465 **/ | |
8466 extractUnscopedRulesFromCssText: function(cssText) { | |
8467 // TODO(sorvell): remove either content or comment | |
8468 var r = '', m; | |
8469 while (m = cssCommentUnscopedRuleRe.exec(cssText)) { | |
8470 r += m[1].slice(0, -1) + '\n\n'; | |
8471 } | |
8472 while (m = cssContentUnscopedRuleRe.exec(cssText)) { | |
8473 r += m[0].replace(m[2], '').replace(m[1], m[3]) + '\n\n'; | |
8474 } | |
8475 return r; | |
8476 }, | |
8477 /* | |
8478 * convert a rule like :host(.foo) > .bar { } | |
8479 * | |
8480 * to | |
8481 * | |
8482 * scopeName.foo > .bar | |
8483 */ | |
8484 convertColonHost: function(cssText) { | |
8485 return this.convertColonRule(cssText, cssColonHostRe, | |
8486 this.colonHostPartReplacer); | |
8487 }, | |
8488 /* | |
8489 * convert a rule like :host-context(.foo) > .bar { } | |
8490 * | |
8491 * to | |
8492 * | |
8493 * scopeName.foo > .bar, .foo scopeName > .bar { } | |
8494 * | |
8495 * and | |
8496 * | |
8497 * :host-context(.foo:host) .bar { ... } | |
8498 * | |
8499 * to | |
8500 * | |
8501 * scopeName.foo .bar { ... } | |
8502 */ | |
8503 convertColonHostContext: function(cssText) { | |
8504 return this.convertColonRule(cssText, cssColonHostContextRe, | |
8505 this.colonHostContextPartReplacer); | |
8506 }, | |
8507 convertColonRule: function(cssText, regExp, partReplacer) { | |
8508 // p1 = :host, p2 = contents of (), p3 rest of rule | |
8509 return cssText.replace(regExp, function(m, p1, p2, p3) { | |
8510 p1 = polyfillHostNoCombinator; | |
8511 if (p2) { | |
8512 var parts = p2.split(','), r = []; | |
8513 for (var i=0, l=parts.length, p; (i<l) && (p=parts[i]); i++) { | |
8514 p = p.trim(); | |
8515 r.push(partReplacer(p1, p, p3)); | |
8516 } | |
8517 return r.join(','); | |
8518 } else { | |
8519 return p1 + p3; | |
8520 } | |
8521 }); | |
8522 }, | |
8523 colonHostContextPartReplacer: function(host, part, suffix) { | |
8524 if (part.match(polyfillHost)) { | |
8525 return this.colonHostPartReplacer(host, part, suffix); | |
8526 } else { | |
8527 return host + part + suffix + ', ' + part + ' ' + host + suffix; | |
8528 } | |
8529 }, | |
8530 colonHostPartReplacer: function(host, part, suffix) { | |
8531 return host + part.replace(polyfillHost, '') + suffix; | |
8532 }, | |
8533 /* | |
8534 * Convert combinators like ::shadow and pseudo-elements like ::content | |
8535 * by replacing with space. | |
8536 */ | |
8537 convertShadowDOMSelectors: function(cssText) { | |
8538 for (var i=0; i < shadowDOMSelectorsRe.length; i++) { | |
8539 cssText = cssText.replace(shadowDOMSelectorsRe[i], ' '); | |
8540 } | |
8541 return cssText; | |
8542 }, | |
8543 // change a selector like 'div' to 'name div' | |
8544 scopeRules: function(cssRules, scopeSelector) { | |
8545 var cssText = ''; | |
8546 if (cssRules) { | |
8547 Array.prototype.forEach.call(cssRules, function(rule) { | |
8548 if (rule.selectorText && (rule.style && rule.style.cssText !== undefined
)) { | |
8549 cssText += this.scopeSelector(rule.selectorText, scopeSelector, | |
8550 this.strictStyling) + ' {\n\t'; | |
8551 cssText += this.propertiesFromRule(rule) + '\n}\n\n'; | |
8552 } else if (rule.type === CSSRule.MEDIA_RULE) { | |
8553 cssText += '@media ' + rule.media.mediaText + ' {\n'; | |
8554 cssText += this.scopeRules(rule.cssRules, scopeSelector); | |
8555 cssText += '\n}\n\n'; | |
8556 } else { | |
8557 // KEYFRAMES_RULE in IE throws when we query cssText | |
8558 // when it contains a -webkit- property. | |
8559 // if this happens, we fallback to constructing the rule | |
8560 // from the CSSRuleSet | |
8561 // https://connect.microsoft.com/IE/feedbackdetail/view/955703/accessi
ng-csstext-of-a-keyframe-rule-that-contains-a-webkit-property-via-cssom-generate
s-exception | |
8562 try { | |
8563 if (rule.cssText) { | |
8564 cssText += rule.cssText + '\n\n'; | |
8565 } | |
8566 } catch(x) { | |
8567 if (rule.type === CSSRule.KEYFRAMES_RULE && rule.cssRules) { | |
8568 cssText += this.ieSafeCssTextFromKeyFrameRule(rule); | |
8569 } | |
8570 } | |
8571 } | |
8572 }, this); | |
8573 } | |
8574 return cssText; | |
8575 }, | |
8576 ieSafeCssTextFromKeyFrameRule: function(rule) { | |
8577 var cssText = '@keyframes ' + rule.name + ' {'; | |
8578 Array.prototype.forEach.call(rule.cssRules, function(rule) { | |
8579 cssText += ' ' + rule.keyText + ' {' + rule.style.cssText + '}'; | |
8580 }); | |
8581 cssText += ' }'; | |
8582 return cssText; | |
8583 }, | |
8584 scopeSelector: function(selector, scopeSelector, strict) { | |
8585 var r = [], parts = selector.split(','); | |
8586 parts.forEach(function(p) { | |
8587 p = p.trim(); | |
8588 if (this.selectorNeedsScoping(p, scopeSelector)) { | |
8589 p = (strict && !p.match(polyfillHostNoCombinator)) ? | |
8590 this.applyStrictSelectorScope(p, scopeSelector) : | |
8591 this.applySelectorScope(p, scopeSelector); | |
8592 } | |
8593 r.push(p); | |
8594 }, this); | |
8595 return r.join(', '); | |
8596 }, | |
8597 selectorNeedsScoping: function(selector, scopeSelector) { | |
8598 if (Array.isArray(scopeSelector)) { | |
8599 return true; | |
8600 } | |
8601 var re = this.makeScopeMatcher(scopeSelector); | |
8602 return !selector.match(re); | |
8603 }, | |
8604 makeScopeMatcher: function(scopeSelector) { | |
8605 scopeSelector = scopeSelector.replace(/\[/g, '\\[').replace(/\[/g, '\\]'); | |
8606 return new RegExp('^(' + scopeSelector + ')' + selectorReSuffix, 'm'); | |
8607 }, | |
8608 applySelectorScope: function(selector, selectorScope) { | |
8609 return Array.isArray(selectorScope) ? | |
8610 this.applySelectorScopeList(selector, selectorScope) : | |
8611 this.applySimpleSelectorScope(selector, selectorScope); | |
8612 }, | |
8613 // apply an array of selectors | |
8614 applySelectorScopeList: function(selector, scopeSelectorList) { | |
8615 var r = []; | |
8616 for (var i=0, s; (s=scopeSelectorList[i]); i++) { | |
8617 r.push(this.applySimpleSelectorScope(selector, s)); | |
8618 } | |
8619 return r.join(', '); | |
8620 }, | |
8621 // scope via name and [is=name] | |
8622 applySimpleSelectorScope: function(selector, scopeSelector) { | |
8623 if (selector.match(polyfillHostRe)) { | |
8624 selector = selector.replace(polyfillHostNoCombinator, scopeSelector); | |
8625 return selector.replace(polyfillHostRe, scopeSelector + ' '); | |
8626 } else { | |
8627 return scopeSelector + ' ' + selector; | |
8628 } | |
8629 }, | |
8630 // return a selector with [name] suffix on each simple selector | |
8631 // e.g. .foo.bar > .zot becomes .foo[name].bar[name] > .zot[name] | |
8632 applyStrictSelectorScope: function(selector, scopeSelector) { | |
8633 scopeSelector = scopeSelector.replace(/\[is=([^\]]*)\]/g, '$1'); | |
8634 var splits = [' ', '>', '+', '~'], | |
8635 scoped = selector, | |
8636 attrName = '[' + scopeSelector + ']'; | |
8637 splits.forEach(function(sep) { | |
8638 var parts = scoped.split(sep); | |
8639 scoped = parts.map(function(p) { | |
8640 // remove :host since it should be unnecessary | |
8641 var t = p.trim().replace(polyfillHostRe, ''); | |
8642 if (t && (splits.indexOf(t) < 0) && (t.indexOf(attrName) < 0)) { | |
8643 p = t.replace(/([^:]*)(:*)(.*)/, '$1' + attrName + '$2$3') | |
8644 } | |
8645 return p; | |
8646 }).join(sep); | |
8647 }); | |
8648 return scoped; | |
8649 }, | |
8650 insertPolyfillHostInCssText: function(selector) { | |
8651 return selector.replace(colonHostContextRe, polyfillHostContext).replace( | |
8652 colonHostRe, polyfillHost); | |
8653 }, | |
8654 propertiesFromRule: function(rule) { | |
8655 var cssText = rule.style.cssText; | |
8656 // TODO(sorvell): Safari cssom incorrectly removes quotes from the content | |
8657 // property. (https://bugs.webkit.org/show_bug.cgi?id=118045) | |
8658 // don't replace attr rules | |
8659 if (rule.style.content && !rule.style.content.match(/['"]+|attr/)) { | |
8660 cssText = cssText.replace(/content:[^;]*;/g, 'content: \'' + | |
8661 rule.style.content + '\';'); | |
8662 } | |
8663 // TODO(sorvell): we can workaround this issue here, but we need a list | |
8664 // of troublesome properties to fix https://github.com/Polymer/platform/issu
es/53 | |
8665 // | |
8666 // inherit rules can be omitted from cssText | |
8667 // TODO(sorvell): remove when Blink bug is fixed: | |
8668 // https://code.google.com/p/chromium/issues/detail?id=358273 | |
8669 var style = rule.style; | |
8670 for (var i in style) { | |
8671 if (style[i] === 'initial') { | |
8672 cssText += i + ': initial; '; | |
8673 } | |
8674 } | |
8675 return cssText; | |
8676 }, | |
8677 replaceTextInStyles: function(styles, action) { | |
8678 if (styles && action) { | |
8679 if (!(styles instanceof Array)) { | |
8680 styles = [styles]; | |
8681 } | |
8682 Array.prototype.forEach.call(styles, function(s) { | |
8683 s.textContent = action.call(this, s.textContent); | |
8684 }, this); | |
8685 } | |
8686 }, | |
8687 addCssToDocument: function(cssText, name) { | |
8688 if (cssText.match('@import')) { | |
8689 addOwnSheet(cssText, name); | |
8690 } else { | |
8691 addCssToDocument(cssText); | |
8692 } | |
8693 } | |
8694 }; | |
8695 | |
8696 var selectorRe = /([^{]*)({[\s\S]*?})/gim, | |
8697 cssCommentRe = /\/\*[^*]*\*+([^/*][^*]*\*+)*\//gim, | |
8698 // TODO(sorvell): remove either content or comment | |
8699 cssCommentNextSelectorRe = /\/\*\s*@polyfill ([^*]*\*+([^/*][^*]*\*+)*\/)([^
{]*?){/gim, | |
8700 cssContentNextSelectorRe = /polyfill-next-selector[^}]*content\:[\s]*?['"](.
*?)['"][;\s]*}([^{]*?){/gim, | |
8701 // TODO(sorvell): remove either content or comment | |
8702 cssCommentRuleRe = /\/\*\s@polyfill-rule([^*]*\*+([^/*][^*]*\*+)*)\//gim, | |
8703 cssContentRuleRe = /(polyfill-rule)[^}]*(content\:[\s]*['"](.*?)['"])[;\s]*[
^}]*}/gim, | |
8704 // TODO(sorvell): remove either content or comment | |
8705 cssCommentUnscopedRuleRe = /\/\*\s@polyfill-unscoped-rule([^*]*\*+([^/*][^*]
*\*+)*)\//gim, | |
8706 cssContentUnscopedRuleRe = /(polyfill-unscoped-rule)[^}]*(content\:[\s]*['"]
(.*?)['"])[;\s]*[^}]*}/gim, | |
8707 cssPseudoRe = /::(x-[^\s{,(]*)/gim, | |
8708 cssPartRe = /::part\(([^)]*)\)/gim, | |
8709 // note: :host pre-processed to -shadowcsshost. | |
8710 polyfillHost = '-shadowcsshost', | |
8711 // note: :host-context pre-processed to -shadowcsshostcontext. | |
8712 polyfillHostContext = '-shadowcsscontext', | |
8713 parenSuffix = ')(?:\\((' + | |
8714 '(?:\\([^)(]*\\)|[^)(]*)+?' + | |
8715 ')\\))?([^,{]*)'; | |
8716 cssColonHostRe = new RegExp('(' + polyfillHost + parenSuffix, 'gim'), | |
8717 cssColonHostContextRe = new RegExp('(' + polyfillHostContext + parenSuffix,
'gim'), | |
8718 selectorReSuffix = '([>\\s~+\[.,{:][\\s\\S]*)?$', | |
8719 colonHostRe = /\:host/gim, | |
8720 colonHostContextRe = /\:host-context/gim, | |
8721 /* host name without combinator */ | |
8722 polyfillHostNoCombinator = polyfillHost + '-no-combinator', | |
8723 polyfillHostRe = new RegExp(polyfillHost, 'gim'), | |
8724 polyfillHostContextRe = new RegExp(polyfillHostContext, 'gim'), | |
8725 shadowDOMSelectorsRe = [ | |
8726 /\^\^/g, | |
8727 /\^/g, | |
8728 /\/shadow\//g, | |
8729 /\/shadow-deep\//g, | |
8730 /::shadow/g, | |
8731 /\/deep\//g, | |
8732 /::content/g | |
8733 ]; | |
8734 | |
8735 function stylesToCssText(styles, preserveComments) { | |
8736 var cssText = ''; | |
8737 Array.prototype.forEach.call(styles, function(s) { | |
8738 cssText += s.textContent + '\n\n'; | |
8739 }); | |
8740 // strip comments for easier processing | |
8741 if (!preserveComments) { | |
8742 cssText = cssText.replace(cssCommentRe, ''); | |
8743 } | |
8744 return cssText; | |
8745 } | |
8746 | |
8747 function cssTextToStyle(cssText) { | |
8748 var style = document.createElement('style'); | |
8749 style.textContent = cssText; | |
8750 return style; | |
8751 } | |
8752 | |
8753 function cssToRules(cssText) { | |
8754 var style = cssTextToStyle(cssText); | |
8755 document.head.appendChild(style); | |
8756 var rules = []; | |
8757 if (style.sheet) { | |
8758 // TODO(sorvell): Firefox throws when accessing the rules of a stylesheet | |
8759 // with an @import | |
8760 // https://bugzilla.mozilla.org/show_bug.cgi?id=625013 | |
8761 try { | |
8762 rules = style.sheet.cssRules; | |
8763 } catch(e) { | |
8764 // | |
8765 } | |
8766 } else { | |
8767 console.warn('sheet not found', style); | |
8768 } | |
8769 style.parentNode.removeChild(style); | |
8770 return rules; | |
8771 } | |
8772 | |
8773 var frame = document.createElement('iframe'); | |
8774 frame.style.display = 'none'; | |
8775 | |
8776 function initFrame() { | |
8777 frame.initialized = true; | |
8778 document.body.appendChild(frame); | |
8779 var doc = frame.contentDocument; | |
8780 var base = doc.createElement('base'); | |
8781 base.href = document.baseURI; | |
8782 doc.head.appendChild(base); | |
8783 } | |
8784 | |
8785 function inFrame(fn) { | |
8786 if (!frame.initialized) { | |
8787 initFrame(); | |
8788 } | |
8789 document.body.appendChild(frame); | |
8790 fn(frame.contentDocument); | |
8791 document.body.removeChild(frame); | |
8792 } | |
8793 | |
8794 // TODO(sorvell): use an iframe if the cssText contains an @import to workaround | |
8795 // https://code.google.com/p/chromium/issues/detail?id=345114 | |
8796 var isChrome = navigator.userAgent.match('Chrome'); | |
8797 function withCssRules(cssText, callback) { | |
8798 if (!callback) { | |
8799 return; | |
8800 } | |
8801 var rules; | |
8802 if (cssText.match('@import') && isChrome) { | |
8803 var style = cssTextToStyle(cssText); | |
8804 inFrame(function(doc) { | |
8805 doc.head.appendChild(style.impl); | |
8806 rules = Array.prototype.slice.call(style.sheet.cssRules, 0); | |
8807 callback(rules); | |
8808 }); | |
8809 } else { | |
8810 rules = cssToRules(cssText); | |
8811 callback(rules); | |
8812 } | |
8813 } | |
8814 | |
8815 function rulesToCss(cssRules) { | |
8816 for (var i=0, css=[]; i < cssRules.length; i++) { | |
8817 css.push(cssRules[i].cssText); | |
8818 } | |
8819 return css.join('\n\n'); | |
8820 } | |
8821 | |
8822 function addCssToDocument(cssText) { | |
8823 if (cssText) { | |
8824 getSheet().appendChild(document.createTextNode(cssText)); | |
8825 } | |
8826 } | |
8827 | |
8828 function addOwnSheet(cssText, name) { | |
8829 var style = cssTextToStyle(cssText); | |
8830 style.setAttribute(name, ''); | |
8831 style.setAttribute(SHIMMED_ATTRIBUTE, ''); | |
8832 document.head.appendChild(style); | |
8833 } | |
8834 | |
8835 var SHIM_ATTRIBUTE = 'shim-shadowdom'; | |
8836 var SHIMMED_ATTRIBUTE = 'shim-shadowdom-css'; | |
8837 var NO_SHIM_ATTRIBUTE = 'no-shim'; | |
8838 | |
8839 var sheet; | |
8840 function getSheet() { | |
8841 if (!sheet) { | |
8842 sheet = document.createElement("style"); | |
8843 sheet.setAttribute(SHIMMED_ATTRIBUTE, ''); | |
8844 sheet[SHIMMED_ATTRIBUTE] = true; | |
8845 } | |
8846 return sheet; | |
8847 } | |
8848 | |
8849 // add polyfill stylesheet to document | |
8850 if (window.ShadowDOMPolyfill) { | |
8851 addCssToDocument('style { display: none !important; }\n'); | |
8852 var doc = wrap(document); | |
8853 var head = doc.querySelector('head'); | |
8854 head.insertBefore(getSheet(), head.childNodes[0]); | |
8855 | |
8856 // TODO(sorvell): monkey-patching HTMLImports is abusive; | |
8857 // consider a better solution. | |
8858 document.addEventListener('DOMContentLoaded', function() { | |
8859 var urlResolver = scope.urlResolver; | |
8860 | |
8861 if (window.HTMLImports && !HTMLImports.useNative) { | |
8862 var SHIM_SHEET_SELECTOR = 'link[rel=stylesheet]' + | |
8863 '[' + SHIM_ATTRIBUTE + ']'; | |
8864 var SHIM_STYLE_SELECTOR = 'style[' + SHIM_ATTRIBUTE + ']'; | |
8865 HTMLImports.importer.documentPreloadSelectors += ',' + SHIM_SHEET_SELECTOR
; | |
8866 HTMLImports.importer.importsPreloadSelectors += ',' + SHIM_SHEET_SELECTOR; | |
8867 | |
8868 HTMLImports.parser.documentSelectors = [ | |
8869 HTMLImports.parser.documentSelectors, | |
8870 SHIM_SHEET_SELECTOR, | |
8871 SHIM_STYLE_SELECTOR | |
8872 ].join(','); | |
8873 | |
8874 var originalParseGeneric = HTMLImports.parser.parseGeneric; | |
8875 | |
8876 HTMLImports.parser.parseGeneric = function(elt) { | |
8877 if (elt[SHIMMED_ATTRIBUTE]) { | |
8878 return; | |
8879 } | |
8880 var style = elt.__importElement || elt; | |
8881 if (!style.hasAttribute(SHIM_ATTRIBUTE)) { | |
8882 originalParseGeneric.call(this, elt); | |
8883 return; | |
8884 } | |
8885 if (elt.__resource) { | |
8886 style = elt.ownerDocument.createElement('style'); | |
8887 style.textContent = elt.__resource; | |
8888 } | |
8889 // relay on HTMLImports for path fixup | |
8890 HTMLImports.path.resolveUrlsInStyle(style); | |
8891 style.textContent = ShadowCSS.shimStyle(style); | |
8892 style.removeAttribute(SHIM_ATTRIBUTE, ''); | |
8893 style.setAttribute(SHIMMED_ATTRIBUTE, ''); | |
8894 style[SHIMMED_ATTRIBUTE] = true; | |
8895 // place in document | |
8896 if (style.parentNode !== head) { | |
8897 // replace links in head | |
8898 if (elt.parentNode === head) { | |
8899 head.replaceChild(style, elt); | |
8900 } else { | |
8901 this.addElementToDocument(style); | |
8902 } | |
8903 } | |
8904 style.__importParsed = true; | |
8905 this.markParsingComplete(elt); | |
8906 this.parseNext(); | |
8907 } | |
8908 | |
8909 var hasResource = HTMLImports.parser.hasResource; | |
8910 HTMLImports.parser.hasResource = function(node) { | |
8911 if (node.localName === 'link' && node.rel === 'stylesheet' && | |
8912 node.hasAttribute(SHIM_ATTRIBUTE)) { | |
8913 return (node.__resource); | |
8914 } else { | |
8915 return hasResource.call(this, node); | |
8916 } | |
8917 } | |
8918 | |
8919 } | |
8920 }); | |
8921 } | |
8922 | |
8923 // exports | |
8924 scope.ShadowCSS = ShadowCSS; | |
8925 | |
8926 })(window.Platform); | |
8927 | |
8928 } else { | |
8929 /* | |
8930 * Copyright (c) 2014 The Polymer Project Authors. All rights reserved. | |
8931 * This code may only be used under the BSD style license found at http://polyme
r.github.io/LICENSE.txt | |
8932 * The complete set of authors may be found at http://polymer.github.io/AUTHORS.
txt | |
8933 * The complete set of contributors may be found at http://polymer.github.io/CON
TRIBUTORS.txt | |
8934 * Code distributed by Google as part of the polymer project is also | |
8935 * subject to an additional IP rights grant found at http://polymer.github.io/PA
TENTS.txt | |
8936 */ | |
8937 | |
8938 (function(scope) { | |
8939 | |
8940 // so we can call wrap/unwrap without testing for ShadowDOMPolyfill | |
8941 window.wrap = window.unwrap = function(n){ | |
8942 return n; | |
8943 } | |
8944 | |
8945 addEventListener('DOMContentLoaded', function() { | |
8946 if (CustomElements.useNative === false) { | |
8947 var originalCreateShadowRoot = Element.prototype.createShadowRoot; | |
8948 Element.prototype.createShadowRoot = function() { | |
8949 var root = originalCreateShadowRoot.call(this); | |
8950 CustomElements.watchShadow(this); | |
8951 return root; | |
8952 }; | |
8953 } | |
8954 }); | |
8955 | |
8956 })(window.Platform); | |
8957 | |
8958 } | |
8959 /* Any copyright is dedicated to the Public Domain. | |
8960 * http://creativecommons.org/publicdomain/zero/1.0/ */ | |
8961 | |
8962 (function(scope) { | |
8963 'use strict'; | |
8964 | |
8965 // feature detect for URL constructor | |
8966 var hasWorkingUrl = false; | |
8967 if (!scope.forceJURL) { | |
8968 try { | |
8969 var u = new URL('b', 'http://a'); | |
8970 hasWorkingUrl = u.href === 'http://a/b'; | |
8971 } catch(e) {} | |
8972 } | |
8973 | |
8974 if (hasWorkingUrl) | |
8975 return; | |
8976 | |
8977 var relative = Object.create(null); | |
8978 relative['ftp'] = 21; | |
8979 relative['file'] = 0; | |
8980 relative['gopher'] = 70; | |
8981 relative['http'] = 80; | |
8982 relative['https'] = 443; | |
8983 relative['ws'] = 80; | |
8984 relative['wss'] = 443; | |
8985 | |
8986 var relativePathDotMapping = Object.create(null); | |
8987 relativePathDotMapping['%2e'] = '.'; | |
8988 relativePathDotMapping['.%2e'] = '..'; | |
8989 relativePathDotMapping['%2e.'] = '..'; | |
8990 relativePathDotMapping['%2e%2e'] = '..'; | |
8991 | |
8992 function isRelativeScheme(scheme) { | |
8993 return relative[scheme] !== undefined; | |
8994 } | |
8995 | |
8996 function invalid() { | |
8997 clear.call(this); | |
8998 this._isInvalid = true; | |
8999 } | |
9000 | |
9001 function IDNAToASCII(h) { | |
9002 if ('' == h) { | |
9003 invalid.call(this) | |
9004 } | |
9005 // XXX | |
9006 return h.toLowerCase() | |
9007 } | |
9008 | |
9009 function percentEscape(c) { | |
9010 var unicode = c.charCodeAt(0); | |
9011 if (unicode > 0x20 && | |
9012 unicode < 0x7F && | |
9013 // " # < > ? ` | |
9014 [0x22, 0x23, 0x3C, 0x3E, 0x3F, 0x60].indexOf(unicode) == -1 | |
9015 ) { | |
9016 return c; | |
9017 } | |
9018 return encodeURIComponent(c); | |
9019 } | |
9020 | |
9021 function percentEscapeQuery(c) { | |
9022 // XXX This actually needs to encode c using encoding and then | |
9023 // convert the bytes one-by-one. | |
9024 | |
9025 var unicode = c.charCodeAt(0); | |
9026 if (unicode > 0x20 && | |
9027 unicode < 0x7F && | |
9028 // " # < > ` (do not escape '?') | |
9029 [0x22, 0x23, 0x3C, 0x3E, 0x60].indexOf(unicode) == -1 | |
9030 ) { | |
9031 return c; | |
9032 } | |
9033 return encodeURIComponent(c); | |
9034 } | |
9035 | |
9036 var EOF = undefined, | |
9037 ALPHA = /[a-zA-Z]/, | |
9038 ALPHANUMERIC = /[a-zA-Z0-9\+\-\.]/; | |
9039 | |
9040 function parse(input, stateOverride, base) { | |
9041 function err(message) { | |
9042 errors.push(message) | |
9043 } | |
9044 | |
9045 var state = stateOverride || 'scheme start', | |
9046 cursor = 0, | |
9047 buffer = '', | |
9048 seenAt = false, | |
9049 seenBracket = false, | |
9050 errors = []; | |
9051 | |
9052 loop: while ((input[cursor - 1] != EOF || cursor == 0) && !this._isInvalid)
{ | |
9053 var c = input[cursor]; | |
9054 switch (state) { | |
9055 case 'scheme start': | |
9056 if (c && ALPHA.test(c)) { | |
9057 buffer += c.toLowerCase(); // ASCII-safe | |
9058 state = 'scheme'; | |
9059 } else if (!stateOverride) { | |
9060 buffer = ''; | |
9061 state = 'no scheme'; | |
9062 continue; | |
9063 } else { | |
9064 err('Invalid scheme.'); | |
9065 break loop; | |
9066 } | |
9067 break; | |
9068 | |
9069 case 'scheme': | |
9070 if (c && ALPHANUMERIC.test(c)) { | |
9071 buffer += c.toLowerCase(); // ASCII-safe | |
9072 } else if (':' == c) { | |
9073 this._scheme = buffer; | |
9074 buffer = ''; | |
9075 if (stateOverride) { | |
9076 break loop; | |
9077 } | |
9078 if (isRelativeScheme(this._scheme)) { | |
9079 this._isRelative = true; | |
9080 } | |
9081 if ('file' == this._scheme) { | |
9082 state = 'relative'; | |
9083 } else if (this._isRelative && base && base._scheme == this._scheme)
{ | |
9084 state = 'relative or authority'; | |
9085 } else if (this._isRelative) { | |
9086 state = 'authority first slash'; | |
9087 } else { | |
9088 state = 'scheme data'; | |
9089 } | |
9090 } else if (!stateOverride) { | |
9091 buffer = ''; | |
9092 cursor = 0; | |
9093 state = 'no scheme'; | |
9094 continue; | |
9095 } else if (EOF == c) { | |
9096 break loop; | |
9097 } else { | |
9098 err('Code point not allowed in scheme: ' + c) | |
9099 break loop; | |
9100 } | |
9101 break; | |
9102 | |
9103 case 'scheme data': | |
9104 if ('?' == c) { | |
9105 query = '?'; | |
9106 state = 'query'; | |
9107 } else if ('#' == c) { | |
9108 this._fragment = '#'; | |
9109 state = 'fragment'; | |
9110 } else { | |
9111 // XXX error handling | |
9112 if (EOF != c && '\t' != c && '\n' != c && '\r' != c) { | |
9113 this._schemeData += percentEscape(c); | |
9114 } | |
9115 } | |
9116 break; | |
9117 | |
9118 case 'no scheme': | |
9119 if (!base || !(isRelativeScheme(base._scheme))) { | |
9120 err('Missing scheme.'); | |
9121 invalid.call(this); | |
9122 } else { | |
9123 state = 'relative'; | |
9124 continue; | |
9125 } | |
9126 break; | |
9127 | |
9128 case 'relative or authority': | |
9129 if ('/' == c && '/' == input[cursor+1]) { | |
9130 state = 'authority ignore slashes'; | |
9131 } else { | |
9132 err('Expected /, got: ' + c); | |
9133 state = 'relative'; | |
9134 continue | |
9135 } | |
9136 break; | |
9137 | |
9138 case 'relative': | |
9139 this._isRelative = true; | |
9140 if ('file' != this._scheme) | |
9141 this._scheme = base._scheme; | |
9142 if (EOF == c) { | |
9143 this._host = base._host; | |
9144 this._port = base._port; | |
9145 this._path = base._path.slice(); | |
9146 this._query = base._query; | |
9147 break loop; | |
9148 } else if ('/' == c || '\\' == c) { | |
9149 if ('\\' == c) | |
9150 err('\\ is an invalid code point.'); | |
9151 state = 'relative slash'; | |
9152 } else if ('?' == c) { | |
9153 this._host = base._host; | |
9154 this._port = base._port; | |
9155 this._path = base._path.slice(); | |
9156 this._query = '?'; | |
9157 state = 'query'; | |
9158 } else if ('#' == c) { | |
9159 this._host = base._host; | |
9160 this._port = base._port; | |
9161 this._path = base._path.slice(); | |
9162 this._query = base._query; | |
9163 this._fragment = '#'; | |
9164 state = 'fragment'; | |
9165 } else { | |
9166 var nextC = input[cursor+1] | |
9167 var nextNextC = input[cursor+2] | |
9168 if ( | |
9169 'file' != this._scheme || !ALPHA.test(c) || | |
9170 (nextC != ':' && nextC != '|') || | |
9171 (EOF != nextNextC && '/' != nextNextC && '\\' != nextNextC && '?'
!= nextNextC && '#' != nextNextC)) { | |
9172 this._host = base._host; | |
9173 this._port = base._port; | |
9174 this._path = base._path.slice(); | |
9175 this._path.pop(); | |
9176 } | |
9177 state = 'relative path'; | |
9178 continue; | |
9179 } | |
9180 break; | |
9181 | |
9182 case 'relative slash': | |
9183 if ('/' == c || '\\' == c) { | |
9184 if ('\\' == c) { | |
9185 err('\\ is an invalid code point.'); | |
9186 } | |
9187 if ('file' == this._scheme) { | |
9188 state = 'file host'; | |
9189 } else { | |
9190 state = 'authority ignore slashes'; | |
9191 } | |
9192 } else { | |
9193 if ('file' != this._scheme) { | |
9194 this._host = base._host; | |
9195 this._port = base._port; | |
9196 } | |
9197 state = 'relative path'; | |
9198 continue; | |
9199 } | |
9200 break; | |
9201 | |
9202 case 'authority first slash': | |
9203 if ('/' == c) { | |
9204 state = 'authority second slash'; | |
9205 } else { | |
9206 err("Expected '/', got: " + c); | |
9207 state = 'authority ignore slashes'; | |
9208 continue; | |
9209 } | |
9210 break; | |
9211 | |
9212 case 'authority second slash': | |
9213 state = 'authority ignore slashes'; | |
9214 if ('/' != c) { | |
9215 err("Expected '/', got: " + c); | |
9216 continue; | |
9217 } | |
9218 break; | |
9219 | |
9220 case 'authority ignore slashes': | |
9221 if ('/' != c && '\\' != c) { | |
9222 state = 'authority'; | |
9223 continue; | |
9224 } else { | |
9225 err('Expected authority, got: ' + c); | |
9226 } | |
9227 break; | |
9228 | |
9229 case 'authority': | |
9230 if ('@' == c) { | |
9231 if (seenAt) { | |
9232 err('@ already seen.'); | |
9233 buffer += '%40'; | |
9234 } | |
9235 seenAt = true; | |
9236 for (var i = 0; i < buffer.length; i++) { | |
9237 var cp = buffer[i]; | |
9238 if ('\t' == cp || '\n' == cp || '\r' == cp) { | |
9239 err('Invalid whitespace in authority.'); | |
9240 continue; | |
9241 } | |
9242 // XXX check URL code points | |
9243 if (':' == cp && null === this._password) { | |
9244 this._password = ''; | |
9245 continue; | |
9246 } | |
9247 var tempC = percentEscape(cp); | |
9248 (null !== this._password) ? this._password += tempC : this._userna
me += tempC; | |
9249 } | |
9250 buffer = ''; | |
9251 } else if (EOF == c || '/' == c || '\\' == c || '?' == c || '#' == c)
{ | |
9252 cursor -= buffer.length; | |
9253 buffer = ''; | |
9254 state = 'host'; | |
9255 continue; | |
9256 } else { | |
9257 buffer += c; | |
9258 } | |
9259 break; | |
9260 | |
9261 case 'file host': | |
9262 if (EOF == c || '/' == c || '\\' == c || '?' == c || '#' == c) { | |
9263 if (buffer.length == 2 && ALPHA.test(buffer[0]) && (buffer[1] == ':'
|| buffer[1] == '|')) { | |
9264 state = 'relative path'; | |
9265 } else if (buffer.length == 0) { | |
9266 state = 'relative path start'; | |
9267 } else { | |
9268 this._host = IDNAToASCII.call(this, buffer); | |
9269 buffer = ''; | |
9270 state = 'relative path start'; | |
9271 } | |
9272 continue; | |
9273 } else if ('\t' == c || '\n' == c || '\r' == c) { | |
9274 err('Invalid whitespace in file host.'); | |
9275 } else { | |
9276 buffer += c; | |
9277 } | |
9278 break; | |
9279 | |
9280 case 'host': | |
9281 case 'hostname': | |
9282 if (':' == c && !seenBracket) { | |
9283 // XXX host parsing | |
9284 this._host = IDNAToASCII.call(this, buffer); | |
9285 buffer = ''; | |
9286 state = 'port'; | |
9287 if ('hostname' == stateOverride) { | |
9288 break loop; | |
9289 } | |
9290 } else if (EOF == c || '/' == c || '\\' == c || '?' == c || '#' == c)
{ | |
9291 this._host = IDNAToASCII.call(this, buffer); | |
9292 buffer = ''; | |
9293 state = 'relative path start'; | |
9294 if (stateOverride) { | |
9295 break loop; | |
9296 } | |
9297 continue; | |
9298 } else if ('\t' != c && '\n' != c && '\r' != c) { | |
9299 if ('[' == c) { | |
9300 seenBracket = true; | |
9301 } else if (']' == c) { | |
9302 seenBracket = false; | |
9303 } | |
9304 buffer += c; | |
9305 } else { | |
9306 err('Invalid code point in host/hostname: ' + c); | |
9307 } | |
9308 break; | |
9309 | |
9310 case 'port': | |
9311 if (/[0-9]/.test(c)) { | |
9312 buffer += c; | |
9313 } else if (EOF == c || '/' == c || '\\' == c || '?' == c || '#' == c |
| stateOverride) { | |
9314 if ('' != buffer) { | |
9315 var temp = parseInt(buffer, 10); | |
9316 if (temp != relative[this._scheme]) { | |
9317 this._port = temp + ''; | |
9318 } | |
9319 buffer = ''; | |
9320 } | |
9321 if (stateOverride) { | |
9322 break loop; | |
9323 } | |
9324 state = 'relative path start'; | |
9325 continue; | |
9326 } else if ('\t' == c || '\n' == c || '\r' == c) { | |
9327 err('Invalid code point in port: ' + c); | |
9328 } else { | |
9329 invalid.call(this); | |
9330 } | |
9331 break; | |
9332 | |
9333 case 'relative path start': | |
9334 if ('\\' == c) | |
9335 err("'\\' not allowed in path."); | |
9336 state = 'relative path'; | |
9337 if ('/' != c && '\\' != c) { | |
9338 continue; | |
9339 } | |
9340 break; | |
9341 | |
9342 case 'relative path': | |
9343 if (EOF == c || '/' == c || '\\' == c || (!stateOverride && ('?' == c
|| '#' == c))) { | |
9344 if ('\\' == c) { | |
9345 err('\\ not allowed in relative path.'); | |
9346 } | |
9347 var tmp; | |
9348 if (tmp = relativePathDotMapping[buffer.toLowerCase()]) { | |
9349 buffer = tmp; | |
9350 } | |
9351 if ('..' == buffer) { | |
9352 this._path.pop(); | |
9353 if ('/' != c && '\\' != c) { | |
9354 this._path.push(''); | |
9355 } | |
9356 } else if ('.' == buffer && '/' != c && '\\' != c) { | |
9357 this._path.push(''); | |
9358 } else if ('.' != buffer) { | |
9359 if ('file' == this._scheme && this._path.length == 0 && buffer.len
gth == 2 && ALPHA.test(buffer[0]) && buffer[1] == '|') { | |
9360 buffer = buffer[0] + ':'; | |
9361 } | |
9362 this._path.push(buffer); | |
9363 } | |
9364 buffer = ''; | |
9365 if ('?' == c) { | |
9366 this._query = '?'; | |
9367 state = 'query'; | |
9368 } else if ('#' == c) { | |
9369 this._fragment = '#'; | |
9370 state = 'fragment'; | |
9371 } | |
9372 } else if ('\t' != c && '\n' != c && '\r' != c) { | |
9373 buffer += percentEscape(c); | |
9374 } | |
9375 break; | |
9376 | |
9377 case 'query': | |
9378 if (!stateOverride && '#' == c) { | |
9379 this._fragment = '#'; | |
9380 state = 'fragment'; | |
9381 } else if (EOF != c && '\t' != c && '\n' != c && '\r' != c) { | |
9382 this._query += percentEscapeQuery(c); | |
9383 } | |
9384 break; | |
9385 | |
9386 case 'fragment': | |
9387 if (EOF != c && '\t' != c && '\n' != c && '\r' != c) { | |
9388 this._fragment += c; | |
9389 } | |
9390 break; | |
9391 } | |
9392 | |
9393 cursor++; | |
9394 } | |
9395 } | |
9396 | |
9397 function clear() { | |
9398 this._scheme = ''; | |
9399 this._schemeData = ''; | |
9400 this._username = ''; | |
9401 this._password = null; | |
9402 this._host = ''; | |
9403 this._port = ''; | |
9404 this._path = []; | |
9405 this._query = ''; | |
9406 this._fragment = ''; | |
9407 this._isInvalid = false; | |
9408 this._isRelative = false; | |
9409 } | |
9410 | |
9411 // Does not process domain names or IP addresses. | |
9412 // Does not handle encoding for the query parameter. | |
9413 function jURL(url, base /* , encoding */) { | |
9414 if (base !== undefined && !(base instanceof jURL)) | |
9415 base = new jURL(String(base)); | |
9416 | |
9417 this._url = url; | |
9418 clear.call(this); | |
9419 | |
9420 var input = url.replace(/^[ \t\r\n\f]+|[ \t\r\n\f]+$/g, ''); | |
9421 // encoding = encoding || 'utf-8' | |
9422 | |
9423 parse.call(this, input, null, base); | |
9424 } | |
9425 | |
9426 jURL.prototype = { | |
9427 get href() { | |
9428 if (this._isInvalid) | |
9429 return this._url; | |
9430 | |
9431 var authority = ''; | |
9432 if ('' != this._username || null != this._password) { | |
9433 authority = this._username + | |
9434 (null != this._password ? ':' + this._password : '') + '@'; | |
9435 } | |
9436 | |
9437 return this.protocol + | |
9438 (this._isRelative ? '//' + authority + this.host : '') + | |
9439 this.pathname + this._query + this._fragment; | |
9440 }, | |
9441 set href(href) { | |
9442 clear.call(this); | |
9443 parse.call(this, href); | |
9444 }, | |
9445 | |
9446 get protocol() { | |
9447 return this._scheme + ':'; | |
9448 }, | |
9449 set protocol(protocol) { | |
9450 if (this._isInvalid) | |
9451 return; | |
9452 parse.call(this, protocol + ':', 'scheme start'); | |
9453 }, | |
9454 | |
9455 get host() { | |
9456 return this._isInvalid ? '' : this._port ? | |
9457 this._host + ':' + this._port : this._host; | |
9458 }, | |
9459 set host(host) { | |
9460 if (this._isInvalid || !this._isRelative) | |
9461 return; | |
9462 parse.call(this, host, 'host'); | |
9463 }, | |
9464 | |
9465 get hostname() { | |
9466 return this._host; | |
9467 }, | |
9468 set hostname(hostname) { | |
9469 if (this._isInvalid || !this._isRelative) | |
9470 return; | |
9471 parse.call(this, hostname, 'hostname'); | |
9472 }, | |
9473 | |
9474 get port() { | |
9475 return this._port; | |
9476 }, | |
9477 set port(port) { | |
9478 if (this._isInvalid || !this._isRelative) | |
9479 return; | |
9480 parse.call(this, port, 'port'); | |
9481 }, | |
9482 | |
9483 get pathname() { | |
9484 return this._isInvalid ? '' : this._isRelative ? | |
9485 '/' + this._path.join('/') : this._schemeData; | |
9486 }, | |
9487 set pathname(pathname) { | |
9488 if (this._isInvalid || !this._isRelative) | |
9489 return; | |
9490 this._path = []; | |
9491 parse.call(this, pathname, 'relative path start'); | |
9492 }, | |
9493 | |
9494 get search() { | |
9495 return this._isInvalid || !this._query || '?' == this._query ? | |
9496 '' : this._query; | |
9497 }, | |
9498 set search(search) { | |
9499 if (this._isInvalid || !this._isRelative) | |
9500 return; | |
9501 this._query = '?'; | |
9502 if ('?' == search[0]) | |
9503 search = search.slice(1); | |
9504 parse.call(this, search, 'query'); | |
9505 }, | |
9506 | |
9507 get hash() { | |
9508 return this._isInvalid || !this._fragment || '#' == this._fragment ? | |
9509 '' : this._fragment; | |
9510 }, | |
9511 set hash(hash) { | |
9512 if (this._isInvalid) | |
9513 return; | |
9514 this._fragment = '#'; | |
9515 if ('#' == hash[0]) | |
9516 hash = hash.slice(1); | |
9517 parse.call(this, hash, 'fragment'); | |
9518 }, | |
9519 | |
9520 get origin() { | |
9521 var host; | |
9522 if (this._isInvalid || !this._scheme) { | |
9523 return ''; | |
9524 } | |
9525 // javascript: Gecko returns String(""), WebKit/Blink String("null") | |
9526 // Gecko throws error for "data://" | |
9527 // data: Gecko returns "", Blink returns "data://", WebKit returns "null" | |
9528 // Gecko returns String("") for file: mailto: | |
9529 // WebKit/Blink returns String("SCHEME://") for file: mailto: | |
9530 switch (this._scheme) { | |
9531 case 'data': | |
9532 case 'file': | |
9533 case 'javascript': | |
9534 case 'mailto': | |
9535 return 'null'; | |
9536 } | |
9537 host = this.host; | |
9538 if (!host) { | |
9539 return ''; | |
9540 } | |
9541 return this._scheme + '://' + host; | |
9542 } | |
9543 }; | |
9544 | |
9545 // Copy over the static methods | |
9546 var OriginalURL = scope.URL; | |
9547 if (OriginalURL) { | |
9548 jURL.createObjectURL = function(blob) { | |
9549 // IE extension allows a second optional options argument. | |
9550 // http://msdn.microsoft.com/en-us/library/ie/hh772302(v=vs.85).aspx | |
9551 return OriginalURL.createObjectURL.apply(OriginalURL, arguments); | |
9552 }; | |
9553 jURL.revokeObjectURL = function(url) { | |
9554 OriginalURL.revokeObjectURL(url); | |
9555 }; | |
9556 } | |
9557 | |
9558 scope.URL = jURL; | |
9559 | |
9560 })(this); | |
9561 | |
9562 /* | |
9563 * Copyright (c) 2014 The Polymer Project Authors. All rights reserved. | |
9564 * This code may only be used under the BSD style license found at http://polyme
r.github.io/LICENSE.txt | |
9565 * The complete set of authors may be found at http://polymer.github.io/AUTHORS.
txt | |
9566 * The complete set of contributors may be found at http://polymer.github.io/CON
TRIBUTORS.txt | |
9567 * Code distributed by Google as part of the polymer project is also | |
9568 * subject to an additional IP rights grant found at http://polymer.github.io/PA
TENTS.txt | |
9569 */ | |
9570 | |
9571 (function(scope) { | |
9572 | |
9573 // Old versions of iOS do not have bind. | |
9574 | |
9575 if (!Function.prototype.bind) { | |
9576 Function.prototype.bind = function(scope) { | |
9577 var self = this; | |
9578 var args = Array.prototype.slice.call(arguments, 1); | |
9579 return function() { | |
9580 var args2 = args.slice(); | |
9581 args2.push.apply(args2, arguments); | |
9582 return self.apply(scope, args2); | |
9583 }; | |
9584 }; | |
9585 } | |
9586 | |
9587 })(window.Platform); | |
9588 | |
9589 /* | |
9590 * Copyright 2012 The Polymer Authors. All rights reserved. | |
9591 * Use of this source code is goverened by a BSD-style | |
9592 * license that can be found in the LICENSE file. | |
9593 */ | |
9594 | |
9595 (function(global) { | |
9596 | |
9597 var registrationsTable = new WeakMap(); | |
9598 | |
9599 // We use setImmediate or postMessage for our future callback. | |
9600 var setImmediate = window.msSetImmediate; | |
9601 | |
9602 // Use post message to emulate setImmediate. | |
9603 if (!setImmediate) { | |
9604 var setImmediateQueue = []; | |
9605 var sentinel = String(Math.random()); | |
9606 window.addEventListener('message', function(e) { | |
9607 if (e.data === sentinel) { | |
9608 var queue = setImmediateQueue; | |
9609 setImmediateQueue = []; | |
9610 queue.forEach(function(func) { | |
9611 func(); | |
9612 }); | |
9613 } | |
9614 }); | |
9615 setImmediate = function(func) { | |
9616 setImmediateQueue.push(func); | |
9617 window.postMessage(sentinel, '*'); | |
9618 }; | |
9619 } | |
9620 | |
9621 // This is used to ensure that we never schedule 2 callas to setImmediate | |
9622 var isScheduled = false; | |
9623 | |
9624 // Keep track of observers that needs to be notified next time. | |
9625 var scheduledObservers = []; | |
9626 | |
9627 /** | |
9628 * Schedules |dispatchCallback| to be called in the future. | |
9629 * @param {MutationObserver} observer | |
9630 */ | |
9631 function scheduleCallback(observer) { | |
9632 scheduledObservers.push(observer); | |
9633 if (!isScheduled) { | |
9634 isScheduled = true; | |
9635 setImmediate(dispatchCallbacks); | |
9636 } | |
9637 } | |
9638 | |
9639 function wrapIfNeeded(node) { | |
9640 return window.ShadowDOMPolyfill && | |
9641 window.ShadowDOMPolyfill.wrapIfNeeded(node) || | |
9642 node; | |
9643 } | |
9644 | |
9645 function dispatchCallbacks() { | |
9646 // http://dom.spec.whatwg.org/#mutation-observers | |
9647 | |
9648 isScheduled = false; // Used to allow a new setImmediate call above. | |
9649 | |
9650 var observers = scheduledObservers; | |
9651 scheduledObservers = []; | |
9652 // Sort observers based on their creation UID (incremental). | |
9653 observers.sort(function(o1, o2) { | |
9654 return o1.uid_ - o2.uid_; | |
9655 }); | |
9656 | |
9657 var anyNonEmpty = false; | |
9658 observers.forEach(function(observer) { | |
9659 | |
9660 // 2.1, 2.2 | |
9661 var queue = observer.takeRecords(); | |
9662 // 2.3. Remove all transient registered observers whose observer is mo. | |
9663 removeTransientObserversFor(observer); | |
9664 | |
9665 // 2.4 | |
9666 if (queue.length) { | |
9667 observer.callback_(queue, observer); | |
9668 anyNonEmpty = true; | |
9669 } | |
9670 }); | |
9671 | |
9672 // 3. | |
9673 if (anyNonEmpty) | |
9674 dispatchCallbacks(); | |
9675 } | |
9676 | |
9677 function removeTransientObserversFor(observer) { | |
9678 observer.nodes_.forEach(function(node) { | |
9679 var registrations = registrationsTable.get(node); | |
9680 if (!registrations) | |
9681 return; | |
9682 registrations.forEach(function(registration) { | |
9683 if (registration.observer === observer) | |
9684 registration.removeTransientObservers(); | |
9685 }); | |
9686 }); | |
9687 } | |
9688 | |
9689 /** | |
9690 * This function is used for the "For each registered observer observer (with | |
9691 * observer's options as options) in target's list of registered observers, | |
9692 * run these substeps:" and the "For each ancestor ancestor of target, and for | |
9693 * each registered observer observer (with options options) in ancestor's list | |
9694 * of registered observers, run these substeps:" part of the algorithms. The | |
9695 * |options.subtree| is checked to ensure that the callback is called | |
9696 * correctly. | |
9697 * | |
9698 * @param {Node} target | |
9699 * @param {function(MutationObserverInit):MutationRecord} callback | |
9700 */ | |
9701 function forEachAncestorAndObserverEnqueueRecord(target, callback) { | |
9702 for (var node = target; node; node = node.parentNode) { | |
9703 var registrations = registrationsTable.get(node); | |
9704 | |
9705 if (registrations) { | |
9706 for (var j = 0; j < registrations.length; j++) { | |
9707 var registration = registrations[j]; | |
9708 var options = registration.options; | |
9709 | |
9710 // Only target ignores subtree. | |
9711 if (node !== target && !options.subtree) | |
9712 continue; | |
9713 | |
9714 var record = callback(options); | |
9715 if (record) | |
9716 registration.enqueue(record); | |
9717 } | |
9718 } | |
9719 } | |
9720 } | |
9721 | |
9722 var uidCounter = 0; | |
9723 | |
9724 /** | |
9725 * The class that maps to the DOM MutationObserver interface. | |
9726 * @param {Function} callback. | |
9727 * @constructor | |
9728 */ | |
9729 function JsMutationObserver(callback) { | |
9730 this.callback_ = callback; | |
9731 this.nodes_ = []; | |
9732 this.records_ = []; | |
9733 this.uid_ = ++uidCounter; | |
9734 } | |
9735 | |
9736 JsMutationObserver.prototype = { | |
9737 observe: function(target, options) { | |
9738 target = wrapIfNeeded(target); | |
9739 | |
9740 // 1.1 | |
9741 if (!options.childList && !options.attributes && !options.characterData || | |
9742 | |
9743 // 1.2 | |
9744 options.attributeOldValue && !options.attributes || | |
9745 | |
9746 // 1.3 | |
9747 options.attributeFilter && options.attributeFilter.length && | |
9748 !options.attributes || | |
9749 | |
9750 // 1.4 | |
9751 options.characterDataOldValue && !options.characterData) { | |
9752 | |
9753 throw new SyntaxError(); | |
9754 } | |
9755 | |
9756 var registrations = registrationsTable.get(target); | |
9757 if (!registrations) | |
9758 registrationsTable.set(target, registrations = []); | |
9759 | |
9760 // 2 | |
9761 // If target's list of registered observers already includes a registered | |
9762 // observer associated with the context object, replace that registered | |
9763 // observer's options with options. | |
9764 var registration; | |
9765 for (var i = 0; i < registrations.length; i++) { | |
9766 if (registrations[i].observer === this) { | |
9767 registration = registrations[i]; | |
9768 registration.removeListeners(); | |
9769 registration.options = options; | |
9770 break; | |
9771 } | |
9772 } | |
9773 | |
9774 // 3. | |
9775 // Otherwise, add a new registered observer to target's list of registered | |
9776 // observers with the context object as the observer and options as the | |
9777 // options, and add target to context object's list of nodes on which it | |
9778 // is registered. | |
9779 if (!registration) { | |
9780 registration = new Registration(this, target, options); | |
9781 registrations.push(registration); | |
9782 this.nodes_.push(target); | |
9783 } | |
9784 | |
9785 registration.addListeners(); | |
9786 }, | |
9787 | |
9788 disconnect: function() { | |
9789 this.nodes_.forEach(function(node) { | |
9790 var registrations = registrationsTable.get(node); | |
9791 for (var i = 0; i < registrations.length; i++) { | |
9792 var registration = registrations[i]; | |
9793 if (registration.observer === this) { | |
9794 registration.removeListeners(); | |
9795 registrations.splice(i, 1); | |
9796 // Each node can only have one registered observer associated with | |
9797 // this observer. | |
9798 break; | |
9799 } | |
9800 } | |
9801 }, this); | |
9802 this.records_ = []; | |
9803 }, | |
9804 | |
9805 takeRecords: function() { | |
9806 var copyOfRecords = this.records_; | |
9807 this.records_ = []; | |
9808 return copyOfRecords; | |
9809 } | |
9810 }; | |
9811 | |
9812 /** | |
9813 * @param {string} type | |
9814 * @param {Node} target | |
9815 * @constructor | |
9816 */ | |
9817 function MutationRecord(type, target) { | |
9818 this.type = type; | |
9819 this.target = target; | |
9820 this.addedNodes = []; | |
9821 this.removedNodes = []; | |
9822 this.previousSibling = null; | |
9823 this.nextSibling = null; | |
9824 this.attributeName = null; | |
9825 this.attributeNamespace = null; | |
9826 this.oldValue = null; | |
9827 } | |
9828 | |
9829 function copyMutationRecord(original) { | |
9830 var record = new MutationRecord(original.type, original.target); | |
9831 record.addedNodes = original.addedNodes.slice(); | |
9832 record.removedNodes = original.removedNodes.slice(); | |
9833 record.previousSibling = original.previousSibling; | |
9834 record.nextSibling = original.nextSibling; | |
9835 record.attributeName = original.attributeName; | |
9836 record.attributeNamespace = original.attributeNamespace; | |
9837 record.oldValue = original.oldValue; | |
9838 return record; | |
9839 }; | |
9840 | |
9841 // We keep track of the two (possibly one) records used in a single mutation. | |
9842 var currentRecord, recordWithOldValue; | |
9843 | |
9844 /** | |
9845 * Creates a record without |oldValue| and caches it as |currentRecord| for | |
9846 * later use. | |
9847 * @param {string} oldValue | |
9848 * @return {MutationRecord} | |
9849 */ | |
9850 function getRecord(type, target) { | |
9851 return currentRecord = new MutationRecord(type, target); | |
9852 } | |
9853 | |
9854 /** | |
9855 * Gets or creates a record with |oldValue| based in the |currentRecord| | |
9856 * @param {string} oldValue | |
9857 * @return {MutationRecord} | |
9858 */ | |
9859 function getRecordWithOldValue(oldValue) { | |
9860 if (recordWithOldValue) | |
9861 return recordWithOldValue; | |
9862 recordWithOldValue = copyMutationRecord(currentRecord); | |
9863 recordWithOldValue.oldValue = oldValue; | |
9864 return recordWithOldValue; | |
9865 } | |
9866 | |
9867 function clearRecords() { | |
9868 currentRecord = recordWithOldValue = undefined; | |
9869 } | |
9870 | |
9871 /** | |
9872 * @param {MutationRecord} record | |
9873 * @return {boolean} Whether the record represents a record from the current | |
9874 * mutation event. | |
9875 */ | |
9876 function recordRepresentsCurrentMutation(record) { | |
9877 return record === recordWithOldValue || record === currentRecord; | |
9878 } | |
9879 | |
9880 /** | |
9881 * Selects which record, if any, to replace the last record in the queue. | |
9882 * This returns |null| if no record should be replaced. | |
9883 * | |
9884 * @param {MutationRecord} lastRecord | |
9885 * @param {MutationRecord} newRecord | |
9886 * @param {MutationRecord} | |
9887 */ | |
9888 function selectRecord(lastRecord, newRecord) { | |
9889 if (lastRecord === newRecord) | |
9890 return lastRecord; | |
9891 | |
9892 // Check if the the record we are adding represents the same record. If | |
9893 // so, we keep the one with the oldValue in it. | |
9894 if (recordWithOldValue && recordRepresentsCurrentMutation(lastRecord)) | |
9895 return recordWithOldValue; | |
9896 | |
9897 return null; | |
9898 } | |
9899 | |
9900 /** | |
9901 * Class used to represent a registered observer. | |
9902 * @param {MutationObserver} observer | |
9903 * @param {Node} target | |
9904 * @param {MutationObserverInit} options | |
9905 * @constructor | |
9906 */ | |
9907 function Registration(observer, target, options) { | |
9908 this.observer = observer; | |
9909 this.target = target; | |
9910 this.options = options; | |
9911 this.transientObservedNodes = []; | |
9912 } | |
9913 | |
9914 Registration.prototype = { | |
9915 enqueue: function(record) { | |
9916 var records = this.observer.records_; | |
9917 var length = records.length; | |
9918 | |
9919 // There are cases where we replace the last record with the new record. | |
9920 // For example if the record represents the same mutation we need to use | |
9921 // the one with the oldValue. If we get same record (this can happen as we | |
9922 // walk up the tree) we ignore the new record. | |
9923 if (records.length > 0) { | |
9924 var lastRecord = records[length - 1]; | |
9925 var recordToReplaceLast = selectRecord(lastRecord, record); | |
9926 if (recordToReplaceLast) { | |
9927 records[length - 1] = recordToReplaceLast; | |
9928 return; | |
9929 } | |
9930 } else { | |
9931 scheduleCallback(this.observer); | |
9932 } | |
9933 | |
9934 records[length] = record; | |
9935 }, | |
9936 | |
9937 addListeners: function() { | |
9938 this.addListeners_(this.target); | |
9939 }, | |
9940 | |
9941 addListeners_: function(node) { | |
9942 var options = this.options; | |
9943 if (options.attributes) | |
9944 node.addEventListener('DOMAttrModified', this, true); | |
9945 | |
9946 if (options.characterData) | |
9947 node.addEventListener('DOMCharacterDataModified', this, true); | |
9948 | |
9949 if (options.childList) | |
9950 node.addEventListener('DOMNodeInserted', this, true); | |
9951 | |
9952 if (options.childList || options.subtree) | |
9953 node.addEventListener('DOMNodeRemoved', this, true); | |
9954 }, | |
9955 | |
9956 removeListeners: function() { | |
9957 this.removeListeners_(this.target); | |
9958 }, | |
9959 | |
9960 removeListeners_: function(node) { | |
9961 var options = this.options; | |
9962 if (options.attributes) | |
9963 node.removeEventListener('DOMAttrModified', this, true); | |
9964 | |
9965 if (options.characterData) | |
9966 node.removeEventListener('DOMCharacterDataModified', this, true); | |
9967 | |
9968 if (options.childList) | |
9969 node.removeEventListener('DOMNodeInserted', this, true); | |
9970 | |
9971 if (options.childList || options.subtree) | |
9972 node.removeEventListener('DOMNodeRemoved', this, true); | |
9973 }, | |
9974 | |
9975 /** | |
9976 * Adds a transient observer on node. The transient observer gets removed | |
9977 * next time we deliver the change records. | |
9978 * @param {Node} node | |
9979 */ | |
9980 addTransientObserver: function(node) { | |
9981 // Don't add transient observers on the target itself. We already have all | |
9982 // the required listeners set up on the target. | |
9983 if (node === this.target) | |
9984 return; | |
9985 | |
9986 this.addListeners_(node); | |
9987 this.transientObservedNodes.push(node); | |
9988 var registrations = registrationsTable.get(node); | |
9989 if (!registrations) | |
9990 registrationsTable.set(node, registrations = []); | |
9991 | |
9992 // We know that registrations does not contain this because we already | |
9993 // checked if node === this.target. | |
9994 registrations.push(this); | |
9995 }, | |
9996 | |
9997 removeTransientObservers: function() { | |
9998 var transientObservedNodes = this.transientObservedNodes; | |
9999 this.transientObservedNodes = []; | |
10000 | |
10001 transientObservedNodes.forEach(function(node) { | |
10002 // Transient observers are never added to the target. | |
10003 this.removeListeners_(node); | |
10004 | |
10005 var registrations = registrationsTable.get(node); | |
10006 for (var i = 0; i < registrations.length; i++) { | |
10007 if (registrations[i] === this) { | |
10008 registrations.splice(i, 1); | |
10009 // Each node can only have one registered observer associated with | |
10010 // this observer. | |
10011 break; | |
10012 } | |
10013 } | |
10014 }, this); | |
10015 }, | |
10016 | |
10017 handleEvent: function(e) { | |
10018 // Stop propagation since we are managing the propagation manually. | |
10019 // This means that other mutation events on the page will not work | |
10020 // correctly but that is by design. | |
10021 e.stopImmediatePropagation(); | |
10022 | |
10023 switch (e.type) { | |
10024 case 'DOMAttrModified': | |
10025 // http://dom.spec.whatwg.org/#concept-mo-queue-attributes | |
10026 | |
10027 var name = e.attrName; | |
10028 var namespace = e.relatedNode.namespaceURI; | |
10029 var target = e.target; | |
10030 | |
10031 // 1. | |
10032 var record = new getRecord('attributes', target); | |
10033 record.attributeName = name; | |
10034 record.attributeNamespace = namespace; | |
10035 | |
10036 // 2. | |
10037 var oldValue = | |
10038 e.attrChange === MutationEvent.ADDITION ? null : e.prevValue; | |
10039 | |
10040 forEachAncestorAndObserverEnqueueRecord(target, function(options) { | |
10041 // 3.1, 4.2 | |
10042 if (!options.attributes) | |
10043 return; | |
10044 | |
10045 // 3.2, 4.3 | |
10046 if (options.attributeFilter && options.attributeFilter.length && | |
10047 options.attributeFilter.indexOf(name) === -1 && | |
10048 options.attributeFilter.indexOf(namespace) === -1) { | |
10049 return; | |
10050 } | |
10051 // 3.3, 4.4 | |
10052 if (options.attributeOldValue) | |
10053 return getRecordWithOldValue(oldValue); | |
10054 | |
10055 // 3.4, 4.5 | |
10056 return record; | |
10057 }); | |
10058 | |
10059 break; | |
10060 | |
10061 case 'DOMCharacterDataModified': | |
10062 // http://dom.spec.whatwg.org/#concept-mo-queue-characterdata | |
10063 var target = e.target; | |
10064 | |
10065 // 1. | |
10066 var record = getRecord('characterData', target); | |
10067 | |
10068 // 2. | |
10069 var oldValue = e.prevValue; | |
10070 | |
10071 | |
10072 forEachAncestorAndObserverEnqueueRecord(target, function(options) { | |
10073 // 3.1, 4.2 | |
10074 if (!options.characterData) | |
10075 return; | |
10076 | |
10077 // 3.2, 4.3 | |
10078 if (options.characterDataOldValue) | |
10079 return getRecordWithOldValue(oldValue); | |
10080 | |
10081 // 3.3, 4.4 | |
10082 return record; | |
10083 }); | |
10084 | |
10085 break; | |
10086 | |
10087 case 'DOMNodeRemoved': | |
10088 this.addTransientObserver(e.target); | |
10089 // Fall through. | |
10090 case 'DOMNodeInserted': | |
10091 // http://dom.spec.whatwg.org/#concept-mo-queue-childlist | |
10092 var target = e.relatedNode; | |
10093 var changedNode = e.target; | |
10094 var addedNodes, removedNodes; | |
10095 if (e.type === 'DOMNodeInserted') { | |
10096 addedNodes = [changedNode]; | |
10097 removedNodes = []; | |
10098 } else { | |
10099 | |
10100 addedNodes = []; | |
10101 removedNodes = [changedNode]; | |
10102 } | |
10103 var previousSibling = changedNode.previousSibling; | |
10104 var nextSibling = changedNode.nextSibling; | |
10105 | |
10106 // 1. | |
10107 var record = getRecord('childList', target); | |
10108 record.addedNodes = addedNodes; | |
10109 record.removedNodes = removedNodes; | |
10110 record.previousSibling = previousSibling; | |
10111 record.nextSibling = nextSibling; | |
10112 | |
10113 forEachAncestorAndObserverEnqueueRecord(target, function(options) { | |
10114 // 2.1, 3.2 | |
10115 if (!options.childList) | |
10116 return; | |
10117 | |
10118 // 2.2, 3.3 | |
10119 return record; | |
10120 }); | |
10121 | |
10122 } | |
10123 | |
10124 clearRecords(); | |
10125 } | |
10126 }; | |
10127 | |
10128 global.JsMutationObserver = JsMutationObserver; | |
10129 | |
10130 if (!global.MutationObserver) | |
10131 global.MutationObserver = JsMutationObserver; | |
10132 | |
10133 | |
10134 })(this); | |
10135 | |
10136 /* | |
10137 * Copyright (c) 2014 The Polymer Project Authors. All rights reserved. | |
10138 * This code may only be used under the BSD style license found at http://polyme
r.github.io/LICENSE.txt | |
10139 * The complete set of authors may be found at http://polymer.github.io/AUTHORS.
txt | |
10140 * The complete set of contributors may be found at http://polymer.github.io/CON
TRIBUTORS.txt | |
10141 * Code distributed by Google as part of the polymer project is also | |
10142 * subject to an additional IP rights grant found at http://polymer.github.io/PA
TENTS.txt | |
10143 */ | |
10144 window.HTMLImports = window.HTMLImports || {flags:{}}; | |
10145 /* | |
10146 * Copyright (c) 2014 The Polymer Project Authors. All rights reserved. | |
10147 * This code may only be used under the BSD style license found at http://polyme
r.github.io/LICENSE.txt | |
10148 * The complete set of authors may be found at http://polymer.github.io/AUTHORS.
txt | |
10149 * The complete set of contributors may be found at http://polymer.github.io/CON
TRIBUTORS.txt | |
10150 * Code distributed by Google as part of the polymer project is also | |
10151 * subject to an additional IP rights grant found at http://polymer.github.io/PA
TENTS.txt | |
10152 */ | |
10153 | |
10154 (function(scope) { | |
10155 | |
10156 var IMPORT_LINK_TYPE = 'import'; | |
10157 var hasNative = (IMPORT_LINK_TYPE in document.createElement('link')); | |
10158 var useNative = hasNative; | |
10159 var isIE = /Trident/.test(navigator.userAgent); | |
10160 | |
10161 // TODO(sorvell): SD polyfill intrusion | |
10162 var hasShadowDOMPolyfill = Boolean(window.ShadowDOMPolyfill); | |
10163 var wrap = function(node) { | |
10164 return hasShadowDOMPolyfill ? ShadowDOMPolyfill.wrapIfNeeded(node) : node; | |
10165 }; | |
10166 | |
10167 var rootDocument = wrap(document); | |
10168 | |
10169 // NOTE: We cannot polyfill document.currentScript because it's not possible | |
10170 // both to override and maintain the ability to capture the native value; | |
10171 // therefore we choose to expose _currentScript both when native imports | |
10172 // and the polyfill are in use. | |
10173 var currentScriptDescriptor = { | |
10174 get: function() { | |
10175 var script = HTMLImports.currentScript || document.currentScript || | |
10176 // NOTE: only works when called in synchronously executing code. | |
10177 // readyState should check if `loading` but IE10 is | |
10178 // interactive when scripts run so we cheat. | |
10179 (document.readyState !== 'complete' ? | |
10180 document.scripts[document.scripts.length - 1] : null); | |
10181 return wrap(script); | |
10182 }, | |
10183 configurable: true | |
10184 }; | |
10185 | |
10186 Object.defineProperty(document, '_currentScript', currentScriptDescriptor); | |
10187 Object.defineProperty(rootDocument, '_currentScript', currentScriptDescriptor); | |
10188 | |
10189 // call a callback when all HTMLImports in the document at call (or at least | |
10190 // document ready) time have loaded. | |
10191 // 1. ensure the document is in a ready state (has dom), then | |
10192 // 2. watch for loading of imports and call callback when done | |
10193 function whenReady(callback, doc) { | |
10194 doc = doc || rootDocument; | |
10195 // if document is loading, wait and try again | |
10196 whenDocumentReady(function() { | |
10197 watchImportsLoad(callback, doc); | |
10198 }, doc); | |
10199 } | |
10200 | |
10201 // call the callback when the document is in a ready state (has dom) | |
10202 var requiredReadyState = isIE ? 'complete' : 'interactive'; | |
10203 var READY_EVENT = 'readystatechange'; | |
10204 function isDocumentReady(doc) { | |
10205 return (doc.readyState === 'complete' || | |
10206 doc.readyState === requiredReadyState); | |
10207 } | |
10208 | |
10209 // call <callback> when we ensure the document is in a ready state | |
10210 function whenDocumentReady(callback, doc) { | |
10211 if (!isDocumentReady(doc)) { | |
10212 var checkReady = function() { | |
10213 if (doc.readyState === 'complete' || | |
10214 doc.readyState === requiredReadyState) { | |
10215 doc.removeEventListener(READY_EVENT, checkReady); | |
10216 whenDocumentReady(callback, doc); | |
10217 } | |
10218 }; | |
10219 doc.addEventListener(READY_EVENT, checkReady); | |
10220 } else if (callback) { | |
10221 callback(); | |
10222 } | |
10223 } | |
10224 | |
10225 function markTargetLoaded(event) { | |
10226 event.target.__loaded = true; | |
10227 } | |
10228 | |
10229 // call <callback> when we ensure all imports have loaded | |
10230 function watchImportsLoad(callback, doc) { | |
10231 var imports = doc.querySelectorAll('link[rel=import]'); | |
10232 var loaded = 0, l = imports.length; | |
10233 function checkDone(d) { | |
10234 if ((loaded == l) && callback) { | |
10235 callback(); | |
10236 } | |
10237 } | |
10238 function loadedImport(e) { | |
10239 markTargetLoaded(e); | |
10240 loaded++; | |
10241 checkDone(); | |
10242 } | |
10243 if (l) { | |
10244 for (var i=0, imp; (i<l) && (imp=imports[i]); i++) { | |
10245 if (isImportLoaded(imp)) { | |
10246 loadedImport.call(imp, {target: imp}); | |
10247 } else { | |
10248 imp.addEventListener('load', loadedImport); | |
10249 imp.addEventListener('error', loadedImport); | |
10250 } | |
10251 } | |
10252 } else { | |
10253 checkDone(); | |
10254 } | |
10255 } | |
10256 | |
10257 // NOTE: test for native imports loading is based on explicitly watching | |
10258 // all imports (see below). | |
10259 // We cannot rely on this entirely without watching the entire document | |
10260 // for import links. For perf reasons, currently only head is watched. | |
10261 // Instead, we fallback to checking if the import property is available | |
10262 // and the document is not itself loading. | |
10263 function isImportLoaded(link) { | |
10264 return useNative ? link.__loaded || | |
10265 (link.import && link.import.readyState !== 'loading') : | |
10266 link.__importParsed; | |
10267 } | |
10268 | |
10269 // TODO(sorvell): Workaround for | |
10270 // https://www.w3.org/Bugs/Public/show_bug.cgi?id=25007, should be removed when | |
10271 // this bug is addressed. | |
10272 // (1) Install a mutation observer to see when HTMLImports have loaded | |
10273 // (2) if this script is run during document load it will watch any existing | |
10274 // imports for loading. | |
10275 // | |
10276 // NOTE: The workaround has restricted functionality: (1) it's only compatible | |
10277 // with imports that are added to document.head since the mutation observer | |
10278 // watches only head for perf reasons, (2) it requires this script | |
10279 // to run before any imports have completed loading. | |
10280 if (useNative) { | |
10281 new MutationObserver(function(mxns) { | |
10282 for (var i=0, l=mxns.length, m; (i < l) && (m=mxns[i]); i++) { | |
10283 if (m.addedNodes) { | |
10284 handleImports(m.addedNodes); | |
10285 } | |
10286 } | |
10287 }).observe(document.head, {childList: true}); | |
10288 | |
10289 function handleImports(nodes) { | |
10290 for (var i=0, l=nodes.length, n; (i<l) && (n=nodes[i]); i++) { | |
10291 if (isImport(n)) { | |
10292 handleImport(n); | |
10293 } | |
10294 } | |
10295 } | |
10296 | |
10297 function isImport(element) { | |
10298 return element.localName === 'link' && element.rel === 'import'; | |
10299 } | |
10300 | |
10301 function handleImport(element) { | |
10302 var loaded = element.import; | |
10303 if (loaded) { | |
10304 markTargetLoaded({target: element}); | |
10305 } else { | |
10306 element.addEventListener('load', markTargetLoaded); | |
10307 element.addEventListener('error', markTargetLoaded); | |
10308 } | |
10309 } | |
10310 | |
10311 // make sure to catch any imports that are in the process of loading | |
10312 // when this script is run. | |
10313 (function() { | |
10314 if (document.readyState === 'loading') { | |
10315 var imports = document.querySelectorAll('link[rel=import]'); | |
10316 for (var i=0, l=imports.length, imp; (i<l) && (imp=imports[i]); i++) { | |
10317 handleImport(imp); | |
10318 } | |
10319 } | |
10320 })(); | |
10321 | |
10322 } | |
10323 | |
10324 // Fire the 'HTMLImportsLoaded' event when imports in document at load time | |
10325 // have loaded. This event is required to simulate the script blocking | |
10326 // behavior of native imports. A main document script that needs to be sure | |
10327 // imports have loaded should wait for this event. | |
10328 whenReady(function() { | |
10329 HTMLImports.ready = true; | |
10330 HTMLImports.readyTime = new Date().getTime(); | |
10331 rootDocument.dispatchEvent( | |
10332 new CustomEvent('HTMLImportsLoaded', {bubbles: true}) | |
10333 ); | |
10334 }); | |
10335 | |
10336 // exports | |
10337 scope.useNative = useNative; | |
10338 scope.isImportLoaded = isImportLoaded; | |
10339 scope.whenReady = whenReady; | |
10340 scope.rootDocument = rootDocument; | |
10341 scope.IMPORT_LINK_TYPE = IMPORT_LINK_TYPE; | |
10342 scope.isIE = isIE; | |
10343 | |
10344 })(window.HTMLImports); | |
10345 /* | |
10346 * Copyright (c) 2014 The Polymer Project Authors. All rights reserved. | |
10347 * This code may only be used under the BSD style license found at http://polyme
r.github.io/LICENSE.txt | |
10348 * The complete set of authors may be found at http://polymer.github.io/AUTHORS.
txt | |
10349 * The complete set of contributors may be found at http://polymer.github.io/CON
TRIBUTORS.txt | |
10350 * Code distributed by Google as part of the polymer project is also | |
10351 * subject to an additional IP rights grant found at http://polymer.github.io/PA
TENTS.txt | |
10352 */ | |
10353 (function(scope) { | |
10354 | |
10355 // imports | |
10356 var path = scope.path; | |
10357 var xhr = scope.xhr; | |
10358 var flags = scope.flags; | |
10359 | |
10360 // TODO(sorvell): this loader supports a dynamic list of urls | |
10361 // and an oncomplete callback that is called when the loader is done. | |
10362 // The polyfill currently does *not* need this dynamism or the onComplete | |
10363 // concept. Because of this, the loader could be simplified quite a bit. | |
10364 var Loader = function(onLoad, onComplete) { | |
10365 this.cache = {}; | |
10366 this.onload = onLoad; | |
10367 this.oncomplete = onComplete; | |
10368 this.inflight = 0; | |
10369 this.pending = {}; | |
10370 }; | |
10371 | |
10372 Loader.prototype = { | |
10373 | |
10374 addNodes: function(nodes) { | |
10375 // number of transactions to complete | |
10376 this.inflight += nodes.length; | |
10377 // commence transactions | |
10378 for (var i=0, l=nodes.length, n; (i<l) && (n=nodes[i]); i++) { | |
10379 this.require(n); | |
10380 } | |
10381 // anything to do? | |
10382 this.checkDone(); | |
10383 }, | |
10384 | |
10385 addNode: function(node) { | |
10386 // number of transactions to complete | |
10387 this.inflight++; | |
10388 // commence transactions | |
10389 this.require(node); | |
10390 // anything to do? | |
10391 this.checkDone(); | |
10392 }, | |
10393 | |
10394 require: function(elt) { | |
10395 var url = elt.src || elt.href; | |
10396 // ensure we have a standard url that can be used | |
10397 // reliably for deduping. | |
10398 // TODO(sjmiles): ad-hoc | |
10399 elt.__nodeUrl = url; | |
10400 // deduplication | |
10401 if (!this.dedupe(url, elt)) { | |
10402 // fetch this resource | |
10403 this.fetch(url, elt); | |
10404 } | |
10405 }, | |
10406 | |
10407 dedupe: function(url, elt) { | |
10408 if (this.pending[url]) { | |
10409 // add to list of nodes waiting for inUrl | |
10410 this.pending[url].push(elt); | |
10411 // don't need fetch | |
10412 return true; | |
10413 } | |
10414 var resource; | |
10415 if (this.cache[url]) { | |
10416 this.onload(url, elt, this.cache[url]); | |
10417 // finished this transaction | |
10418 this.tail(); | |
10419 // don't need fetch | |
10420 return true; | |
10421 } | |
10422 // first node waiting for inUrl | |
10423 this.pending[url] = [elt]; | |
10424 // need fetch (not a dupe) | |
10425 return false; | |
10426 }, | |
10427 | |
10428 fetch: function(url, elt) { | |
10429 flags.load && console.log('fetch', url, elt); | |
10430 if (url.match(/^data:/)) { | |
10431 // Handle Data URI Scheme | |
10432 var pieces = url.split(','); | |
10433 var header = pieces[0]; | |
10434 var body = pieces[1]; | |
10435 if(header.indexOf(';base64') > -1) { | |
10436 body = atob(body); | |
10437 } else { | |
10438 body = decodeURIComponent(body); | |
10439 } | |
10440 setTimeout(function() { | |
10441 this.receive(url, elt, null, body); | |
10442 }.bind(this), 0); | |
10443 } else { | |
10444 var receiveXhr = function(err, resource, redirectedUrl) { | |
10445 this.receive(url, elt, err, resource, redirectedUrl); | |
10446 }.bind(this); | |
10447 xhr.load(url, receiveXhr); | |
10448 // TODO(sorvell): blocked on) | |
10449 // https://code.google.com/p/chromium/issues/detail?id=257221 | |
10450 // xhr'ing for a document makes scripts in imports runnable; otherwise | |
10451 // they are not; however, it requires that we have doctype=html in | |
10452 // the import which is unacceptable. This is only needed on Chrome | |
10453 // to avoid the bug above. | |
10454 /* | |
10455 if (isDocumentLink(elt)) { | |
10456 xhr.loadDocument(url, receiveXhr); | |
10457 } else { | |
10458 xhr.load(url, receiveXhr); | |
10459 } | |
10460 */ | |
10461 } | |
10462 }, | |
10463 | |
10464 receive: function(url, elt, err, resource, redirectedUrl) { | |
10465 this.cache[url] = resource; | |
10466 var $p = this.pending[url]; | |
10467 for (var i=0, l=$p.length, p; (i<l) && (p=$p[i]); i++) { | |
10468 // If url was redirected, use the redirected location so paths are | |
10469 // calculated relative to that. | |
10470 this.onload(url, p, resource, err, redirectedUrl); | |
10471 this.tail(); | |
10472 } | |
10473 this.pending[url] = null; | |
10474 }, | |
10475 | |
10476 tail: function() { | |
10477 --this.inflight; | |
10478 this.checkDone(); | |
10479 }, | |
10480 | |
10481 checkDone: function() { | |
10482 if (!this.inflight) { | |
10483 this.oncomplete(); | |
10484 } | |
10485 } | |
10486 | |
10487 }; | |
10488 | |
10489 xhr = xhr || { | |
10490 async: true, | |
10491 | |
10492 ok: function(request) { | |
10493 return (request.status >= 200 && request.status < 300) | |
10494 || (request.status === 304) | |
10495 || (request.status === 0); | |
10496 }, | |
10497 | |
10498 load: function(url, next, nextContext) { | |
10499 var request = new XMLHttpRequest(); | |
10500 if (scope.flags.debug || scope.flags.bust) { | |
10501 url += '?' + Math.random(); | |
10502 } | |
10503 request.open('GET', url, xhr.async); | |
10504 request.addEventListener('readystatechange', function(e) { | |
10505 if (request.readyState === 4) { | |
10506 // Servers redirecting an import can add a Location header to help us | |
10507 // polyfill correctly. | |
10508 var locationHeader = request.getResponseHeader("Location"); | |
10509 var redirectedUrl = null; | |
10510 if (locationHeader) { | |
10511 var redirectedUrl = (locationHeader.substr( 0, 1 ) === "/") | |
10512 ? location.origin + locationHeader // Location is a relative path | |
10513 : locationHeader; // Full path | |
10514 } | |
10515 next.call(nextContext, !xhr.ok(request) && request, | |
10516 request.response || request.responseText, redirectedUrl); | |
10517 } | |
10518 }); | |
10519 request.send(); | |
10520 return request; | |
10521 }, | |
10522 | |
10523 loadDocument: function(url, next, nextContext) { | |
10524 this.load(url, next, nextContext).responseType = 'document'; | |
10525 } | |
10526 | |
10527 }; | |
10528 | |
10529 // exports | |
10530 scope.xhr = xhr; | |
10531 scope.Loader = Loader; | |
10532 | |
10533 })(window.HTMLImports); | |
10534 | |
10535 /* | |
10536 * Copyright (c) 2014 The Polymer Project Authors. All rights reserved. | |
10537 * This code may only be used under the BSD style license found at http://polyme
r.github.io/LICENSE.txt | |
10538 * The complete set of authors may be found at http://polymer.github.io/AUTHORS.
txt | |
10539 * The complete set of contributors may be found at http://polymer.github.io/CON
TRIBUTORS.txt | |
10540 * Code distributed by Google as part of the polymer project is also | |
10541 * subject to an additional IP rights grant found at http://polymer.github.io/PA
TENTS.txt | |
10542 */ | |
10543 (function(scope) { | |
10544 | |
10545 // imports | |
10546 var rootDocument = scope.rootDocument; | |
10547 var flags = scope.flags; | |
10548 var isIE = scope.isIE; | |
10549 var IMPORT_LINK_TYPE = scope.IMPORT_LINK_TYPE; | |
10550 | |
10551 // importParser | |
10552 // highlander object to manage parsing of imports | |
10553 // parses import related elements | |
10554 // and ensures proper parse order | |
10555 // parse order is enforced by crawling the tree and monitoring which elements | |
10556 // have been parsed; async parsing is also supported. | |
10557 | |
10558 // highlander object for parsing a document tree | |
10559 var importParser = { | |
10560 | |
10561 // parse selectors for main document elements | |
10562 documentSelectors: 'link[rel=' + IMPORT_LINK_TYPE + ']', | |
10563 | |
10564 // parse selectors for import document elements | |
10565 importsSelectors: [ | |
10566 'link[rel=' + IMPORT_LINK_TYPE + ']', | |
10567 'link[rel=stylesheet]', | |
10568 'style', | |
10569 'script:not([type])', | |
10570 'script[type="text/javascript"]' | |
10571 ].join(','), | |
10572 | |
10573 map: { | |
10574 link: 'parseLink', | |
10575 script: 'parseScript', | |
10576 style: 'parseStyle' | |
10577 }, | |
10578 | |
10579 dynamicElements: [], | |
10580 | |
10581 // try to parse the next import in the tree | |
10582 parseNext: function() { | |
10583 var next = this.nextToParse(); | |
10584 if (next) { | |
10585 this.parse(next); | |
10586 } | |
10587 }, | |
10588 | |
10589 parse: function(elt) { | |
10590 if (this.isParsed(elt)) { | |
10591 flags.parse && console.log('[%s] is already parsed', elt.localName); | |
10592 return; | |
10593 } | |
10594 var fn = this[this.map[elt.localName]]; | |
10595 if (fn) { | |
10596 this.markParsing(elt); | |
10597 fn.call(this, elt); | |
10598 } | |
10599 }, | |
10600 | |
10601 parseDynamic: function(elt, quiet) { | |
10602 this.dynamicElements.push(elt); | |
10603 if (!quiet) { | |
10604 this.parseNext(); | |
10605 } | |
10606 }, | |
10607 | |
10608 // only 1 element may be parsed at a time; parsing is async so each | |
10609 // parsing implementation must inform the system that parsing is complete | |
10610 // via markParsingComplete. | |
10611 // To prompt the system to parse the next element, parseNext should then be | |
10612 // called. | |
10613 // Note, parseNext used to be included at the end of markParsingComplete, but | |
10614 // we must not do this so that, for example, we can (1) mark parsing complete | |
10615 // then (2) fire an import load event, and then (3) parse the next resource. | |
10616 markParsing: function(elt) { | |
10617 flags.parse && console.log('parsing', elt); | |
10618 this.parsingElement = elt; | |
10619 }, | |
10620 | |
10621 markParsingComplete: function(elt) { | |
10622 elt.__importParsed = true; | |
10623 this.markDynamicParsingComplete(elt); | |
10624 if (elt.__importElement) { | |
10625 elt.__importElement.__importParsed = true; | |
10626 this.markDynamicParsingComplete(elt.__importElement); | |
10627 } | |
10628 this.parsingElement = null; | |
10629 flags.parse && console.log('completed', elt); | |
10630 }, | |
10631 | |
10632 markDynamicParsingComplete: function(elt) { | |
10633 var i = this.dynamicElements.indexOf(elt); | |
10634 if (i >= 0) { | |
10635 this.dynamicElements.splice(i, 1); | |
10636 } | |
10637 }, | |
10638 | |
10639 parseImport: function(elt) { | |
10640 // TODO(sorvell): consider if there's a better way to do this; | |
10641 // expose an imports parsing hook; this is needed, for example, by the | |
10642 // CustomElements polyfill. | |
10643 if (HTMLImports.__importsParsingHook) { | |
10644 HTMLImports.__importsParsingHook(elt); | |
10645 } | |
10646 if (elt.import) { | |
10647 elt.import.__importParsed = true; | |
10648 } | |
10649 this.markParsingComplete(elt); | |
10650 // fire load event | |
10651 if (elt.__resource && !elt.__error) { | |
10652 elt.dispatchEvent(new CustomEvent('load', {bubbles: false})); | |
10653 } else { | |
10654 elt.dispatchEvent(new CustomEvent('error', {bubbles: false})); | |
10655 } | |
10656 // TODO(sorvell): workaround for Safari addEventListener not working | |
10657 // for elements not in the main document. | |
10658 if (elt.__pending) { | |
10659 var fn; | |
10660 while (elt.__pending.length) { | |
10661 fn = elt.__pending.shift(); | |
10662 if (fn) { | |
10663 fn({target: elt}); | |
10664 } | |
10665 } | |
10666 } | |
10667 this.parseNext(); | |
10668 }, | |
10669 | |
10670 parseLink: function(linkElt) { | |
10671 if (nodeIsImport(linkElt)) { | |
10672 this.parseImport(linkElt); | |
10673 } else { | |
10674 // make href absolute | |
10675 linkElt.href = linkElt.href; | |
10676 this.parseGeneric(linkElt); | |
10677 } | |
10678 }, | |
10679 | |
10680 parseStyle: function(elt) { | |
10681 // TODO(sorvell): style element load event can just not fire so clone styles | |
10682 var src = elt; | |
10683 elt = cloneStyle(elt); | |
10684 elt.__importElement = src; | |
10685 this.parseGeneric(elt); | |
10686 }, | |
10687 | |
10688 parseGeneric: function(elt) { | |
10689 this.trackElement(elt); | |
10690 this.addElementToDocument(elt); | |
10691 }, | |
10692 | |
10693 rootImportForElement: function(elt) { | |
10694 var n = elt; | |
10695 while (n.ownerDocument.__importLink) { | |
10696 n = n.ownerDocument.__importLink; | |
10697 } | |
10698 return n; | |
10699 }, | |
10700 | |
10701 addElementToDocument: function(elt) { | |
10702 var port = this.rootImportForElement(elt.__importElement || elt); | |
10703 var l = port.__insertedElements = port.__insertedElements || 0; | |
10704 var refNode = port.nextElementSibling; | |
10705 for (var i=0; i < l; i++) { | |
10706 refNode = refNode && refNode.nextElementSibling; | |
10707 } | |
10708 port.parentNode.insertBefore(elt, refNode); | |
10709 }, | |
10710 | |
10711 // tracks when a loadable element has loaded | |
10712 trackElement: function(elt, callback) { | |
10713 var self = this; | |
10714 var done = function(e) { | |
10715 if (callback) { | |
10716 callback(e); | |
10717 } | |
10718 self.markParsingComplete(elt); | |
10719 self.parseNext(); | |
10720 }; | |
10721 elt.addEventListener('load', done); | |
10722 elt.addEventListener('error', done); | |
10723 | |
10724 // NOTE: IE does not fire "load" event for styles that have already loaded | |
10725 // This is in violation of the spec, so we try our hardest to work around it | |
10726 if (isIE && elt.localName === 'style') { | |
10727 var fakeLoad = false; | |
10728 // If there's not @import in the textContent, assume it has loaded | |
10729 if (elt.textContent.indexOf('@import') == -1) { | |
10730 fakeLoad = true; | |
10731 // if we have a sheet, we have been parsed | |
10732 } else if (elt.sheet) { | |
10733 fakeLoad = true; | |
10734 var csr = elt.sheet.cssRules; | |
10735 var len = csr ? csr.length : 0; | |
10736 // search the rules for @import's | |
10737 for (var i = 0, r; (i < len) && (r = csr[i]); i++) { | |
10738 if (r.type === CSSRule.IMPORT_RULE) { | |
10739 // if every @import has resolved, fake the load | |
10740 fakeLoad = fakeLoad && Boolean(r.styleSheet); | |
10741 } | |
10742 } | |
10743 } | |
10744 // dispatch a fake load event and continue parsing | |
10745 if (fakeLoad) { | |
10746 elt.dispatchEvent(new CustomEvent('load', {bubbles: false})); | |
10747 } | |
10748 } | |
10749 }, | |
10750 | |
10751 // NOTE: execute scripts by injecting them and watching for the load/error | |
10752 // event. Inline scripts are handled via dataURL's because browsers tend to | |
10753 // provide correct parsing errors in this case. If this has any compatibility | |
10754 // issues, we can switch to injecting the inline script with textContent. | |
10755 // Scripts with dataURL's do not appear to generate load events and therefore | |
10756 // we assume they execute synchronously. | |
10757 parseScript: function(scriptElt) { | |
10758 var script = document.createElement('script'); | |
10759 script.__importElement = scriptElt; | |
10760 script.src = scriptElt.src ? scriptElt.src : | |
10761 generateScriptDataUrl(scriptElt); | |
10762 scope.currentScript = scriptElt; | |
10763 this.trackElement(script, function(e) { | |
10764 script.parentNode.removeChild(script); | |
10765 scope.currentScript = null; | |
10766 }); | |
10767 this.addElementToDocument(script); | |
10768 }, | |
10769 | |
10770 // determine the next element in the tree which should be parsed | |
10771 nextToParse: function() { | |
10772 this._mayParse = []; | |
10773 return !this.parsingElement && (this.nextToParseInDoc(rootDocument) || | |
10774 this.nextToParseDynamic()); | |
10775 }, | |
10776 | |
10777 nextToParseInDoc: function(doc, link) { | |
10778 // use `marParse` list to avoid looping into the same document again | |
10779 // since it could cause an iloop. | |
10780 if (doc && this._mayParse.indexOf(doc) < 0) { | |
10781 this._mayParse.push(doc); | |
10782 var nodes = doc.querySelectorAll(this.parseSelectorsForNode(doc)); | |
10783 for (var i=0, l=nodes.length, p=0, n; (i<l) && (n=nodes[i]); i++) { | |
10784 if (!this.isParsed(n)) { | |
10785 if (this.hasResource(n)) { | |
10786 return nodeIsImport(n) ? this.nextToParseInDoc(n.import, n) : n; | |
10787 } else { | |
10788 return; | |
10789 } | |
10790 } | |
10791 } | |
10792 } | |
10793 // all nodes have been parsed, ready to parse import, if any | |
10794 return link; | |
10795 }, | |
10796 | |
10797 nextToParseDynamic: function() { | |
10798 return this.dynamicElements[0]; | |
10799 }, | |
10800 | |
10801 // return the set of parse selectors relevant for this node. | |
10802 parseSelectorsForNode: function(node) { | |
10803 var doc = node.ownerDocument || node; | |
10804 return doc === rootDocument ? this.documentSelectors : | |
10805 this.importsSelectors; | |
10806 }, | |
10807 | |
10808 isParsed: function(node) { | |
10809 return node.__importParsed; | |
10810 }, | |
10811 | |
10812 needsDynamicParsing: function(elt) { | |
10813 return (this.dynamicElements.indexOf(elt) >= 0); | |
10814 }, | |
10815 | |
10816 hasResource: function(node) { | |
10817 if (nodeIsImport(node) && (node.import === undefined)) { | |
10818 return false; | |
10819 } | |
10820 return true; | |
10821 } | |
10822 | |
10823 }; | |
10824 | |
10825 function nodeIsImport(elt) { | |
10826 return (elt.localName === 'link') && (elt.rel === IMPORT_LINK_TYPE); | |
10827 } | |
10828 | |
10829 function generateScriptDataUrl(script) { | |
10830 var scriptContent = generateScriptContent(script); | |
10831 return 'data:text/javascript;charset=utf-8,' + encodeURIComponent(scriptConten
t); | |
10832 } | |
10833 | |
10834 function generateScriptContent(script) { | |
10835 return script.textContent + generateSourceMapHint(script); | |
10836 } | |
10837 | |
10838 // calculate source map hint | |
10839 function generateSourceMapHint(script) { | |
10840 var moniker = script.__nodeUrl; | |
10841 if (!moniker) { | |
10842 moniker = script.ownerDocument.baseURI; | |
10843 // there could be more than one script this url | |
10844 var tag = '[' + Math.floor((Math.random()+1)*1000) + ']'; | |
10845 // TODO(sjmiles): Polymer hack, should be pluggable if we need to allow | |
10846 // this sort of thing | |
10847 var matches = script.textContent.match(/Polymer\(['"]([^'"]*)/); | |
10848 tag = matches && matches[1] || tag; | |
10849 // tag the moniker | |
10850 moniker += '/' + tag + '.js'; | |
10851 } | |
10852 return '\n//# sourceURL=' + moniker + '\n'; | |
10853 } | |
10854 | |
10855 // style/stylesheet handling | |
10856 | |
10857 // clone style with proper path resolution for main document | |
10858 // NOTE: styles are the only elements that require direct path fixup. | |
10859 function cloneStyle(style) { | |
10860 var clone = style.ownerDocument.createElement('style'); | |
10861 clone.textContent = style.textContent; | |
10862 path.resolveUrlsInStyle(clone); | |
10863 return clone; | |
10864 } | |
10865 | |
10866 // path fixup: style elements in imports must be made relative to the main | |
10867 // document. We fixup url's in url() and @import. | |
10868 var CSS_URL_REGEXP = /(url\()([^)]*)(\))/g; | |
10869 var CSS_IMPORT_REGEXP = /(@import[\s]+(?!url\())([^;]*)(;)/g; | |
10870 | |
10871 var path = { | |
10872 | |
10873 resolveUrlsInStyle: function(style) { | |
10874 var doc = style.ownerDocument; | |
10875 var resolver = doc.createElement('a'); | |
10876 style.textContent = this.resolveUrlsInCssText(style.textContent, resolver); | |
10877 return style; | |
10878 }, | |
10879 | |
10880 resolveUrlsInCssText: function(cssText, urlObj) { | |
10881 var r = this.replaceUrls(cssText, urlObj, CSS_URL_REGEXP); | |
10882 r = this.replaceUrls(r, urlObj, CSS_IMPORT_REGEXP); | |
10883 return r; | |
10884 }, | |
10885 | |
10886 replaceUrls: function(text, urlObj, regexp) { | |
10887 return text.replace(regexp, function(m, pre, url, post) { | |
10888 var urlPath = url.replace(/["']/g, ''); | |
10889 urlObj.href = urlPath; | |
10890 urlPath = urlObj.href; | |
10891 return pre + '\'' + urlPath + '\'' + post; | |
10892 }); | |
10893 } | |
10894 | |
10895 }; | |
10896 | |
10897 // exports | |
10898 scope.parser = importParser; | |
10899 scope.path = path; | |
10900 | |
10901 })(HTMLImports); | |
10902 | |
10903 /* | |
10904 * Copyright (c) 2014 The Polymer Project Authors. All rights reserved. | |
10905 * This code may only be used under the BSD style license found at http://polyme
r.github.io/LICENSE.txt | |
10906 * The complete set of authors may be found at http://polymer.github.io/AUTHORS.
txt | |
10907 * The complete set of contributors may be found at http://polymer.github.io/CON
TRIBUTORS.txt | |
10908 * Code distributed by Google as part of the polymer project is also | |
10909 * subject to an additional IP rights grant found at http://polymer.github.io/PA
TENTS.txt | |
10910 */ | |
10911 (function(scope) { | |
10912 | |
10913 // imports | |
10914 var useNative = scope.useNative; | |
10915 var flags = scope.flags; | |
10916 var IMPORT_LINK_TYPE = scope.IMPORT_LINK_TYPE; | |
10917 | |
10918 if (!useNative) { | |
10919 | |
10920 // imports | |
10921 var rootDocument = scope.rootDocument; | |
10922 var xhr = scope.xhr; | |
10923 var Loader = scope.Loader; | |
10924 var parser = scope.parser; | |
10925 | |
10926 // importer | |
10927 // highlander object to manage loading of imports | |
10928 | |
10929 // for any document, importer: | |
10930 // - loads any linked import documents (with deduping) | |
10931 | |
10932 var importer = { | |
10933 | |
10934 documents: {}, | |
10935 | |
10936 // nodes to load in the mian document | |
10937 documentPreloadSelectors: 'link[rel=' + IMPORT_LINK_TYPE + ']', | |
10938 | |
10939 // nodes to load in imports | |
10940 importsPreloadSelectors: [ | |
10941 'link[rel=' + IMPORT_LINK_TYPE + ']' | |
10942 ].join(','), | |
10943 | |
10944 loadNode: function(node) { | |
10945 importLoader.addNode(node); | |
10946 }, | |
10947 | |
10948 // load all loadable elements within the parent element | |
10949 loadSubtree: function(parent) { | |
10950 var nodes = this.marshalNodes(parent); | |
10951 // add these nodes to loader's queue | |
10952 importLoader.addNodes(nodes); | |
10953 }, | |
10954 | |
10955 marshalNodes: function(parent) { | |
10956 // all preloadable nodes in inDocument | |
10957 return parent.querySelectorAll(this.loadSelectorsForNode(parent)); | |
10958 }, | |
10959 | |
10960 // find the proper set of load selectors for a given node | |
10961 loadSelectorsForNode: function(node) { | |
10962 var doc = node.ownerDocument || node; | |
10963 return doc === rootDocument ? this.documentPreloadSelectors : | |
10964 this.importsPreloadSelectors; | |
10965 }, | |
10966 | |
10967 loaded: function(url, elt, resource, err, redirectedUrl) { | |
10968 flags.load && console.log('loaded', url, elt); | |
10969 // store generic resource | |
10970 // TODO(sorvell): fails for nodes inside <template>.content | |
10971 // see https://code.google.com/p/chromium/issues/detail?id=249381. | |
10972 elt.__resource = resource; | |
10973 elt.__error = err; | |
10974 if (isDocumentLink(elt)) { | |
10975 var doc = this.documents[url]; | |
10976 // if we've never seen a document at this url | |
10977 if (doc === undefined) { | |
10978 // generate an HTMLDocument from data | |
10979 doc = err ? null : makeDocument(resource, redirectedUrl || url); | |
10980 if (doc) { | |
10981 doc.__importLink = elt; | |
10982 // note, we cannot use MO to detect parsed nodes because | |
10983 // SD polyfill does not report these as mutations. | |
10984 this.bootDocument(doc); | |
10985 } | |
10986 // cache document | |
10987 this.documents[url] = doc; | |
10988 } | |
10989 // don't store import record until we're actually loaded | |
10990 // store document resource | |
10991 elt.import = doc; | |
10992 } | |
10993 parser.parseNext(); | |
10994 }, | |
10995 | |
10996 bootDocument: function(doc) { | |
10997 this.loadSubtree(doc); | |
10998 this.observe(doc); | |
10999 parser.parseNext(); | |
11000 }, | |
11001 | |
11002 loadedAll: function() { | |
11003 parser.parseNext(); | |
11004 } | |
11005 | |
11006 }; | |
11007 | |
11008 // loader singleton | |
11009 var importLoader = new Loader(importer.loaded.bind(importer), | |
11010 importer.loadedAll.bind(importer)); | |
11011 | |
11012 function isDocumentLink(elt) { | |
11013 return isLinkRel(elt, IMPORT_LINK_TYPE); | |
11014 } | |
11015 | |
11016 function isLinkRel(elt, rel) { | |
11017 return elt.localName === 'link' && elt.getAttribute('rel') === rel; | |
11018 } | |
11019 | |
11020 function isScript(elt) { | |
11021 return elt.localName === 'script'; | |
11022 } | |
11023 | |
11024 function makeDocument(resource, url) { | |
11025 // create a new HTML document | |
11026 var doc = resource; | |
11027 if (!(doc instanceof Document)) { | |
11028 doc = document.implementation.createHTMLDocument(IMPORT_LINK_TYPE); | |
11029 } | |
11030 // cache the new document's source url | |
11031 doc._URL = url; | |
11032 // establish a relative path via <base> | |
11033 var base = doc.createElement('base'); | |
11034 base.setAttribute('href', url); | |
11035 // add baseURI support to browsers (IE) that lack it. | |
11036 if (!doc.baseURI) { | |
11037 doc.baseURI = url; | |
11038 } | |
11039 // ensure UTF-8 charset | |
11040 var meta = doc.createElement('meta'); | |
11041 meta.setAttribute('charset', 'utf-8'); | |
11042 | |
11043 doc.head.appendChild(meta); | |
11044 doc.head.appendChild(base); | |
11045 // install HTML last as it may trigger CustomElement upgrades | |
11046 // TODO(sjmiles): problem wrt to template boostrapping below, | |
11047 // template bootstrapping must (?) come before element upgrade | |
11048 // but we cannot bootstrap templates until they are in a document | |
11049 // which is too late | |
11050 if (!(resource instanceof Document)) { | |
11051 // install html | |
11052 doc.body.innerHTML = resource; | |
11053 } | |
11054 // TODO(sorvell): ideally this code is not aware of Template polyfill, | |
11055 // but for now the polyfill needs help to bootstrap these templates | |
11056 if (window.HTMLTemplateElement && HTMLTemplateElement.bootstrap) { | |
11057 HTMLTemplateElement.bootstrap(doc); | |
11058 } | |
11059 return doc; | |
11060 } | |
11061 | |
11062 // Polyfill document.baseURI for browsers without it. | |
11063 if (!document.baseURI) { | |
11064 var baseURIDescriptor = { | |
11065 get: function() { | |
11066 var base = document.querySelector('base'); | |
11067 return base ? base.href : window.location.href; | |
11068 }, | |
11069 configurable: true | |
11070 }; | |
11071 | |
11072 Object.defineProperty(document, 'baseURI', baseURIDescriptor); | |
11073 Object.defineProperty(rootDocument, 'baseURI', baseURIDescriptor); | |
11074 } | |
11075 | |
11076 // IE shim for CustomEvent | |
11077 if (typeof window.CustomEvent !== 'function') { | |
11078 window.CustomEvent = function(inType, dictionary) { | |
11079 var e = document.createEvent('HTMLEvents'); | |
11080 e.initEvent(inType, | |
11081 dictionary.bubbles === false ? false : true, | |
11082 dictionary.cancelable === false ? false : true, | |
11083 dictionary.detail); | |
11084 return e; | |
11085 }; | |
11086 } | |
11087 | |
11088 } else { | |
11089 // do nothing if using native imports | |
11090 var importer = {}; | |
11091 } | |
11092 | |
11093 // exports | |
11094 scope.importer = importer; | |
11095 scope.IMPORT_LINK_TYPE = IMPORT_LINK_TYPE; | |
11096 scope.importLoader = importLoader; | |
11097 | |
11098 })(window.HTMLImports); | |
11099 | |
11100 /* | |
11101 * Copyright (c) 2014 The Polymer Project Authors. All rights reserved. | |
11102 * This code may only be used under the BSD style license found at http://polyme
r.github.io/LICENSE.txt | |
11103 * The complete set of authors may be found at http://polymer.github.io/AUTHORS.
txt | |
11104 * The complete set of contributors may be found at http://polymer.github.io/CON
TRIBUTORS.txt | |
11105 * Code distributed by Google as part of the polymer project is also | |
11106 * subject to an additional IP rights grant found at http://polymer.github.io/PA
TENTS.txt | |
11107 */ | |
11108 (function(scope){ | |
11109 | |
11110 // imports | |
11111 var IMPORT_LINK_TYPE = scope.IMPORT_LINK_TYPE; | |
11112 var importer = scope.importer; | |
11113 var parser = scope.parser; | |
11114 | |
11115 var importSelector = 'link[rel=' + IMPORT_LINK_TYPE + ']'; | |
11116 | |
11117 | |
11118 // we track mutations for addedNodes, looking for imports | |
11119 function handler(mutations) { | |
11120 for (var i=0, l=mutations.length, m; (i<l) && (m=mutations[i]); i++) { | |
11121 if (m.type === 'childList' && m.addedNodes.length) { | |
11122 addedNodes(m.addedNodes); | |
11123 } | |
11124 } | |
11125 } | |
11126 | |
11127 // find loadable elements and add them to the importer | |
11128 // IFF the owning document has already parsed, then parsable elements | |
11129 // need to be marked for dynamic parsing. | |
11130 function addedNodes(nodes) { | |
11131 var owner, parsed; | |
11132 for (var i=0, l=nodes.length, n, loading; (i<l) && (n=nodes[i]); i++) { | |
11133 if (!owner) { | |
11134 owner = n.ownerDocument; | |
11135 parsed = parser.isParsed(owner); | |
11136 } | |
11137 // note: the act of loading kicks the parser, so we use parseDynamic's | |
11138 // 2nd argument to control if this added node needs to kick the parser. | |
11139 loading = shouldLoadNode(n); | |
11140 if (loading) { | |
11141 importer.loadNode(n); | |
11142 } | |
11143 if (shouldParseNode(n) && parsed) { | |
11144 parser.parseDynamic(n, loading); | |
11145 } | |
11146 if (n.children && n.children.length) { | |
11147 addedNodes(n.children); | |
11148 } | |
11149 } | |
11150 } | |
11151 | |
11152 function shouldLoadNode(node) { | |
11153 return (node.nodeType === 1) && matches.call(node, | |
11154 importer.loadSelectorsForNode(node)); | |
11155 } | |
11156 | |
11157 function shouldParseNode(node) { | |
11158 return (node.nodeType === 1) && matches.call(node, | |
11159 parser.parseSelectorsForNode(node)); | |
11160 } | |
11161 | |
11162 // x-plat matches | |
11163 var matches = HTMLElement.prototype.matches || | |
11164 HTMLElement.prototype.matchesSelector || | |
11165 HTMLElement.prototype.webkitMatchesSelector || | |
11166 HTMLElement.prototype.mozMatchesSelector || | |
11167 HTMLElement.prototype.msMatchesSelector; | |
11168 | |
11169 var observer = new MutationObserver(handler); | |
11170 | |
11171 // observe the given root for loadable elements | |
11172 function observe(root) { | |
11173 observer.observe(root, {childList: true, subtree: true}); | |
11174 } | |
11175 | |
11176 // exports | |
11177 // TODO(sorvell): factor so can put on scope | |
11178 scope.observe = observe; | |
11179 importer.observe = observe; | |
11180 | |
11181 })(HTMLImports); | |
11182 | |
11183 /* | |
11184 * Copyright (c) 2014 The Polymer Project Authors. All rights reserved. | |
11185 * This code may only be used under the BSD style license found at http://polyme
r.github.io/LICENSE.txt | |
11186 * The complete set of authors may be found at http://polymer.github.io/AUTHORS.
txt | |
11187 * The complete set of contributors may be found at http://polymer.github.io/CON
TRIBUTORS.txt | |
11188 * Code distributed by Google as part of the polymer project is also | |
11189 * subject to an additional IP rights grant found at http://polymer.github.io/PA
TENTS.txt | |
11190 */ | |
11191 (function(){ | |
11192 | |
11193 // bootstrap | |
11194 | |
11195 // TODO(sorvell): SD polyfill intrusion | |
11196 var doc = window.ShadowDOMPolyfill ? | |
11197 window.ShadowDOMPolyfill.wrapIfNeeded(document) : document; | |
11198 | |
11199 // no need to bootstrap the polyfill when native imports is available. | |
11200 if (!HTMLImports.useNative) { | |
11201 function bootstrap() { | |
11202 HTMLImports.importer.bootDocument(doc); | |
11203 } | |
11204 | |
11205 // TODO(sorvell): SD polyfill does *not* generate mutations for nodes added | |
11206 // by the parser. For this reason, we must wait until the dom exists to | |
11207 // bootstrap. | |
11208 if (document.readyState === 'complete' || | |
11209 (document.readyState === 'interactive' && !window.attachEvent)) { | |
11210 bootstrap(); | |
11211 } else { | |
11212 document.addEventListener('DOMContentLoaded', bootstrap); | |
11213 } | |
11214 } | |
11215 | |
11216 })(); | |
11217 | |
11218 /* | |
11219 * Copyright (c) 2014 The Polymer Project Authors. All rights reserved. | |
11220 * This code may only be used under the BSD style license found at http://polyme
r.github.io/LICENSE.txt | |
11221 * The complete set of authors may be found at http://polymer.github.io/AUTHORS.
txt | |
11222 * The complete set of contributors may be found at http://polymer.github.io/CON
TRIBUTORS.txt | |
11223 * Code distributed by Google as part of the polymer project is also | |
11224 * subject to an additional IP rights grant found at http://polymer.github.io/PA
TENTS.txt | |
11225 */ | |
11226 window.CustomElements = window.CustomElements || {flags:{}}; | |
11227 /* | |
11228 * Copyright (c) 2014 The Polymer Project Authors. All rights reserved. | |
11229 * This code may only be used under the BSD style license found at http://polyme
r.github.io/LICENSE.txt | |
11230 * The complete set of authors may be found at http://polymer.github.io/AUTHORS.
txt | |
11231 * The complete set of contributors may be found at http://polymer.github.io/CON
TRIBUTORS.txt | |
11232 * Code distributed by Google as part of the polymer project is also | |
11233 * subject to an additional IP rights grant found at http://polymer.github.io/PA
TENTS.txt | |
11234 */ | |
11235 | |
11236 (function(scope){ | |
11237 | |
11238 var logFlags = window.logFlags || {}; | |
11239 var IMPORT_LINK_TYPE = window.HTMLImports ? HTMLImports.IMPORT_LINK_TYPE : 'none
'; | |
11240 | |
11241 // walk the subtree rooted at node, applying 'find(element, data)' function | |
11242 // to each element | |
11243 // if 'find' returns true for 'element', do not search element's subtree | |
11244 function findAll(node, find, data) { | |
11245 var e = node.firstElementChild; | |
11246 if (!e) { | |
11247 e = node.firstChild; | |
11248 while (e && e.nodeType !== Node.ELEMENT_NODE) { | |
11249 e = e.nextSibling; | |
11250 } | |
11251 } | |
11252 while (e) { | |
11253 if (find(e, data) !== true) { | |
11254 findAll(e, find, data); | |
11255 } | |
11256 e = e.nextElementSibling; | |
11257 } | |
11258 return null; | |
11259 } | |
11260 | |
11261 // walk all shadowRoots on a given node. | |
11262 function forRoots(node, cb) { | |
11263 var root = node.shadowRoot; | |
11264 while(root) { | |
11265 forSubtree(root, cb); | |
11266 root = root.olderShadowRoot; | |
11267 } | |
11268 } | |
11269 | |
11270 // walk the subtree rooted at node, including descent into shadow-roots, | |
11271 // applying 'cb' to each element | |
11272 function forSubtree(node, cb) { | |
11273 //logFlags.dom && node.childNodes && node.childNodes.length && console.group('
subTree: ', node); | |
11274 findAll(node, function(e) { | |
11275 if (cb(e)) { | |
11276 return true; | |
11277 } | |
11278 forRoots(e, cb); | |
11279 }); | |
11280 forRoots(node, cb); | |
11281 //logFlags.dom && node.childNodes && node.childNodes.length && console.groupEn
d(); | |
11282 } | |
11283 | |
11284 // manage lifecycle on added node | |
11285 function added(node) { | |
11286 if (upgrade(node)) { | |
11287 insertedNode(node); | |
11288 return true; | |
11289 } | |
11290 inserted(node); | |
11291 } | |
11292 | |
11293 // manage lifecycle on added node's subtree only | |
11294 function addedSubtree(node) { | |
11295 forSubtree(node, function(e) { | |
11296 if (added(e)) { | |
11297 return true; | |
11298 } | |
11299 }); | |
11300 } | |
11301 | |
11302 // manage lifecycle on added node and it's subtree | |
11303 function addedNode(node) { | |
11304 return added(node) || addedSubtree(node); | |
11305 } | |
11306 | |
11307 // upgrade custom elements at node, if applicable | |
11308 function upgrade(node) { | |
11309 if (!node.__upgraded__ && node.nodeType === Node.ELEMENT_NODE) { | |
11310 var type = node.getAttribute('is') || node.localName; | |
11311 var definition = scope.registry[type]; | |
11312 if (definition) { | |
11313 logFlags.dom && console.group('upgrade:', node.localName); | |
11314 scope.upgrade(node); | |
11315 logFlags.dom && console.groupEnd(); | |
11316 return true; | |
11317 } | |
11318 } | |
11319 } | |
11320 | |
11321 function insertedNode(node) { | |
11322 inserted(node); | |
11323 if (inDocument(node)) { | |
11324 forSubtree(node, function(e) { | |
11325 inserted(e); | |
11326 }); | |
11327 } | |
11328 } | |
11329 | |
11330 // TODO(sorvell): on platforms without MutationObserver, mutations may not be | |
11331 // reliable and therefore attached/detached are not reliable. | |
11332 // To make these callbacks less likely to fail, we defer all inserts and removes | |
11333 // to give a chance for elements to be inserted into dom. | |
11334 // This ensures attachedCallback fires for elements that are created and | |
11335 // immediately added to dom. | |
11336 var hasPolyfillMutations = (!window.MutationObserver || | |
11337 (window.MutationObserver === window.JsMutationObserver)); | |
11338 scope.hasPolyfillMutations = hasPolyfillMutations; | |
11339 | |
11340 var isPendingMutations = false; | |
11341 var pendingMutations = []; | |
11342 function deferMutation(fn) { | |
11343 pendingMutations.push(fn); | |
11344 if (!isPendingMutations) { | |
11345 isPendingMutations = true; | |
11346 var async = (window.Platform && window.Platform.endOfMicrotask) || | |
11347 setTimeout; | |
11348 async(takeMutations); | |
11349 } | |
11350 } | |
11351 | |
11352 function takeMutations() { | |
11353 isPendingMutations = false; | |
11354 var $p = pendingMutations; | |
11355 for (var i=0, l=$p.length, p; (i<l) && (p=$p[i]); i++) { | |
11356 p(); | |
11357 } | |
11358 pendingMutations = []; | |
11359 } | |
11360 | |
11361 function inserted(element) { | |
11362 if (hasPolyfillMutations) { | |
11363 deferMutation(function() { | |
11364 _inserted(element); | |
11365 }); | |
11366 } else { | |
11367 _inserted(element); | |
11368 } | |
11369 } | |
11370 | |
11371 // TODO(sjmiles): if there are descents into trees that can never have inDocumen
t(*) true, fix this | |
11372 function _inserted(element) { | |
11373 // TODO(sjmiles): it's possible we were inserted and removed in the space | |
11374 // of one microtask, in which case we won't be 'inDocument' here | |
11375 // But there are other cases where we are testing for inserted without | |
11376 // specific knowledge of mutations, and must test 'inDocument' to determine | |
11377 // whether to call inserted | |
11378 // If we can factor these cases into separate code paths we can have | |
11379 // better diagnostics. | |
11380 // TODO(sjmiles): when logging, do work on all custom elements so we can | |
11381 // track behavior even when callbacks not defined | |
11382 //console.log('inserted: ', element.localName); | |
11383 if (element.attachedCallback || element.detachedCallback || (element.__upgrade
d__ && logFlags.dom)) { | |
11384 logFlags.dom && console.group('inserted:', element.localName); | |
11385 if (inDocument(element)) { | |
11386 element.__inserted = (element.__inserted || 0) + 1; | |
11387 // if we are in a 'removed' state, bluntly adjust to an 'inserted' state | |
11388 if (element.__inserted < 1) { | |
11389 element.__inserted = 1; | |
11390 } | |
11391 // if we are 'over inserted', squelch the callback | |
11392 if (element.__inserted > 1) { | |
11393 logFlags.dom && console.warn('inserted:', element.localName, | |
11394 'insert/remove count:', element.__inserted) | |
11395 } else if (element.attachedCallback) { | |
11396 logFlags.dom && console.log('inserted:', element.localName); | |
11397 element.attachedCallback(); | |
11398 } | |
11399 } | |
11400 logFlags.dom && console.groupEnd(); | |
11401 } | |
11402 } | |
11403 | |
11404 function removedNode(node) { | |
11405 removed(node); | |
11406 forSubtree(node, function(e) { | |
11407 removed(e); | |
11408 }); | |
11409 } | |
11410 | |
11411 function removed(element) { | |
11412 if (hasPolyfillMutations) { | |
11413 deferMutation(function() { | |
11414 _removed(element); | |
11415 }); | |
11416 } else { | |
11417 _removed(element); | |
11418 } | |
11419 } | |
11420 | |
11421 function _removed(element) { | |
11422 // TODO(sjmiles): temporary: do work on all custom elements so we can track | |
11423 // behavior even when callbacks not defined | |
11424 if (element.attachedCallback || element.detachedCallback || (element.__upgrade
d__ && logFlags.dom)) { | |
11425 logFlags.dom && console.group('removed:', element.localName); | |
11426 if (!inDocument(element)) { | |
11427 element.__inserted = (element.__inserted || 0) - 1; | |
11428 // if we are in a 'inserted' state, bluntly adjust to an 'removed' state | |
11429 if (element.__inserted > 0) { | |
11430 element.__inserted = 0; | |
11431 } | |
11432 // if we are 'over removed', squelch the callback | |
11433 if (element.__inserted < 0) { | |
11434 logFlags.dom && console.warn('removed:', element.localName, | |
11435 'insert/remove count:', element.__inserted) | |
11436 } else if (element.detachedCallback) { | |
11437 element.detachedCallback(); | |
11438 } | |
11439 } | |
11440 logFlags.dom && console.groupEnd(); | |
11441 } | |
11442 } | |
11443 | |
11444 // SD polyfill intrustion due mainly to the fact that 'document' | |
11445 // is not entirely wrapped | |
11446 function wrapIfNeeded(node) { | |
11447 return window.ShadowDOMPolyfill ? ShadowDOMPolyfill.wrapIfNeeded(node) | |
11448 : node; | |
11449 } | |
11450 | |
11451 function inDocument(element) { | |
11452 var p = element; | |
11453 var doc = wrapIfNeeded(document); | |
11454 while (p) { | |
11455 if (p == doc) { | |
11456 return true; | |
11457 } | |
11458 p = p.parentNode || p.host; | |
11459 } | |
11460 } | |
11461 | |
11462 function watchShadow(node) { | |
11463 if (node.shadowRoot && !node.shadowRoot.__watched) { | |
11464 logFlags.dom && console.log('watching shadow-root for: ', node.localName); | |
11465 // watch all unwatched roots... | |
11466 var root = node.shadowRoot; | |
11467 while (root) { | |
11468 watchRoot(root); | |
11469 root = root.olderShadowRoot; | |
11470 } | |
11471 } | |
11472 } | |
11473 | |
11474 function watchRoot(root) { | |
11475 observe(root); | |
11476 } | |
11477 | |
11478 function handler(mutations) { | |
11479 // | |
11480 if (logFlags.dom) { | |
11481 var mx = mutations[0]; | |
11482 if (mx && mx.type === 'childList' && mx.addedNodes) { | |
11483 if (mx.addedNodes) { | |
11484 var d = mx.addedNodes[0]; | |
11485 while (d && d !== document && !d.host) { | |
11486 d = d.parentNode; | |
11487 } | |
11488 var u = d && (d.URL || d._URL || (d.host && d.host.localName)) || ''; | |
11489 u = u.split('/?').shift().split('/').pop(); | |
11490 } | |
11491 } | |
11492 console.group('mutations (%d) [%s]', mutations.length, u || ''); | |
11493 } | |
11494 // | |
11495 mutations.forEach(function(mx) { | |
11496 //logFlags.dom && console.group('mutation'); | |
11497 if (mx.type === 'childList') { | |
11498 forEach(mx.addedNodes, function(n) { | |
11499 //logFlags.dom && console.log(n.localName); | |
11500 if (!n.localName) { | |
11501 return; | |
11502 } | |
11503 // nodes added may need lifecycle management | |
11504 addedNode(n); | |
11505 }); | |
11506 // removed nodes may need lifecycle management | |
11507 forEach(mx.removedNodes, function(n) { | |
11508 //logFlags.dom && console.log(n.localName); | |
11509 if (!n.localName) { | |
11510 return; | |
11511 } | |
11512 removedNode(n); | |
11513 }); | |
11514 } | |
11515 //logFlags.dom && console.groupEnd(); | |
11516 }); | |
11517 logFlags.dom && console.groupEnd(); | |
11518 }; | |
11519 | |
11520 function takeRecords(node) { | |
11521 // If the optional node is not supplied, assume we mean the whole document. | |
11522 if (!node) node = wrapIfNeeded(document); | |
11523 | |
11524 // Find the root of the tree, which will be an Document or ShadowRoot. | |
11525 while (node.parentNode) { | |
11526 node = node.parentNode; | |
11527 } | |
11528 | |
11529 var observer = node.__observer; | |
11530 if (observer) { | |
11531 handler(observer.takeRecords()); | |
11532 takeMutations(); | |
11533 } | |
11534 } | |
11535 | |
11536 var forEach = Array.prototype.forEach.call.bind(Array.prototype.forEach); | |
11537 | |
11538 function observe(inRoot) { | |
11539 if (inRoot.__observer) return; | |
11540 | |
11541 // For each ShadowRoot, we create a new MutationObserver, so the root can be | |
11542 // garbage collected once all references to the `inRoot` node are gone. | |
11543 var observer = new MutationObserver(handler); | |
11544 observer.observe(inRoot, {childList: true, subtree: true}); | |
11545 inRoot.__observer = observer; | |
11546 } | |
11547 | |
11548 function observeDocument(doc) { | |
11549 observe(doc); | |
11550 } | |
11551 | |
11552 function upgradeDocument(doc) { | |
11553 logFlags.dom && console.group('upgradeDocument: ', (doc.baseURI).split('/').po
p()); | |
11554 addedNode(doc); | |
11555 logFlags.dom && console.groupEnd(); | |
11556 } | |
11557 | |
11558 /* | |
11559 This method is intended to be called when the document tree (including imports) | |
11560 has pending custom elements to upgrade. It can be called multiple times and | |
11561 should do nothing if no elements are in need of upgrade. | |
11562 | |
11563 Note that the import tree can consume itself and therefore special care | |
11564 must be taken to avoid recursion. | |
11565 */ | |
11566 var upgradedDocuments; | |
11567 function upgradeDocumentTree(doc) { | |
11568 upgradedDocuments = []; | |
11569 _upgradeDocumentTree(doc); | |
11570 upgradedDocuments = null; | |
11571 } | |
11572 | |
11573 | |
11574 function _upgradeDocumentTree(doc) { | |
11575 doc = wrapIfNeeded(doc); | |
11576 if (upgradedDocuments.indexOf(doc) >= 0) { | |
11577 return; | |
11578 } | |
11579 upgradedDocuments.push(doc); | |
11580 //console.log('upgradeDocumentTree: ', (doc.baseURI).split('/').pop()); | |
11581 // upgrade contained imported documents | |
11582 var imports = doc.querySelectorAll('link[rel=' + IMPORT_LINK_TYPE + ']'); | |
11583 for (var i=0, l=imports.length, n; (i<l) && (n=imports[i]); i++) { | |
11584 if (n.import && n.import.__parsed) { | |
11585 _upgradeDocumentTree(n.import); | |
11586 } | |
11587 } | |
11588 upgradeDocument(doc); | |
11589 } | |
11590 | |
11591 // exports | |
11592 scope.IMPORT_LINK_TYPE = IMPORT_LINK_TYPE; | |
11593 scope.watchShadow = watchShadow; | |
11594 scope.upgradeDocumentTree = upgradeDocumentTree; | |
11595 scope.upgradeAll = addedNode; | |
11596 scope.upgradeSubtree = addedSubtree; | |
11597 scope.insertedNode = insertedNode; | |
11598 | |
11599 scope.observeDocument = observeDocument; | |
11600 scope.upgradeDocument = upgradeDocument; | |
11601 | |
11602 scope.takeRecords = takeRecords; | |
11603 | |
11604 })(window.CustomElements); | |
11605 | |
11606 /* | |
11607 * Copyright (c) 2014 The Polymer Project Authors. All rights reserved. | |
11608 * This code may only be used under the BSD style license found at http://polyme
r.github.io/LICENSE.txt | |
11609 * The complete set of authors may be found at http://polymer.github.io/AUTHORS.
txt | |
11610 * The complete set of contributors may be found at http://polymer.github.io/CON
TRIBUTORS.txt | |
11611 * Code distributed by Google as part of the polymer project is also | |
11612 * subject to an additional IP rights grant found at http://polymer.github.io/PA
TENTS.txt | |
11613 */ | |
11614 | |
11615 /** | |
11616 * Implements `document.registerElement` | |
11617 * @module CustomElements | |
11618 */ | |
11619 | |
11620 /** | |
11621 * Polyfilled extensions to the `document` object. | |
11622 * @class Document | |
11623 */ | |
11624 | |
11625 (function(scope) { | |
11626 | |
11627 // imports | |
11628 | |
11629 if (!scope) { | |
11630 scope = window.CustomElements = {flags:{}}; | |
11631 } | |
11632 var flags = scope.flags; | |
11633 | |
11634 // native document.registerElement? | |
11635 | |
11636 var hasNative = Boolean(document.registerElement); | |
11637 // For consistent timing, use native custom elements only when not polyfilling | |
11638 // other key related web components features. | |
11639 var useNative = !flags.register && hasNative && !window.ShadowDOMPolyfill && (!w
indow.HTMLImports || HTMLImports.useNative); | |
11640 | |
11641 if (useNative) { | |
11642 | |
11643 // stub | |
11644 var nop = function() {}; | |
11645 | |
11646 // exports | |
11647 scope.registry = {}; | |
11648 scope.upgradeElement = nop; | |
11649 | |
11650 scope.watchShadow = nop; | |
11651 scope.upgrade = nop; | |
11652 scope.upgradeAll = nop; | |
11653 scope.upgradeSubtree = nop; | |
11654 scope.observeDocument = nop; | |
11655 scope.upgradeDocument = nop; | |
11656 scope.upgradeDocumentTree = nop; | |
11657 scope.takeRecords = nop; | |
11658 scope.reservedTagList = []; | |
11659 | |
11660 } else { | |
11661 | |
11662 /** | |
11663 * Registers a custom tag name with the document. | |
11664 * | |
11665 * When a registered element is created, a `readyCallback` method is called | |
11666 * in the scope of the element. The `readyCallback` method can be specified on | |
11667 * either `options.prototype` or `options.lifecycle` with the latter taking | |
11668 * precedence. | |
11669 * | |
11670 * @method register | |
11671 * @param {String} name The tag name to register. Must include a dash ('-'), | |
11672 * for example 'x-component'. | |
11673 * @param {Object} options | |
11674 * @param {String} [options.extends] | |
11675 * (_off spec_) Tag name of an element to extend (or blank for a new | |
11676 * element). This parameter is not part of the specification, but instead | |
11677 * is a hint for the polyfill because the extendee is difficult to infer. | |
11678 * Remember that the input prototype must chain to the extended element's | |
11679 * prototype (or HTMLElement.prototype) regardless of the value of | |
11680 * `extends`. | |
11681 * @param {Object} options.prototype The prototype to use for the new | |
11682 * element. The prototype must inherit from HTMLElement. | |
11683 * @param {Object} [options.lifecycle] | |
11684 * Callbacks that fire at important phases in the life of the custom | |
11685 * element. | |
11686 * | |
11687 * @example | |
11688 * FancyButton = document.registerElement("fancy-button", { | |
11689 * extends: 'button', | |
11690 * prototype: Object.create(HTMLButtonElement.prototype, { | |
11691 * readyCallback: { | |
11692 * value: function() { | |
11693 * console.log("a fancy-button was created", | |
11694 * } | |
11695 * } | |
11696 * }) | |
11697 * }); | |
11698 * @return {Function} Constructor for the newly registered type. | |
11699 */ | |
11700 function register(name, options) { | |
11701 //console.warn('document.registerElement("' + name + '", ', options, ')'); | |
11702 // construct a defintion out of options | |
11703 // TODO(sjmiles): probably should clone options instead of mutating it | |
11704 var definition = options || {}; | |
11705 if (!name) { | |
11706 // TODO(sjmiles): replace with more appropriate error (EricB can probably | |
11707 // offer guidance) | |
11708 throw new Error('document.registerElement: first argument `name` must not
be empty'); | |
11709 } | |
11710 if (name.indexOf('-') < 0) { | |
11711 // TODO(sjmiles): replace with more appropriate error (EricB can probably | |
11712 // offer guidance) | |
11713 throw new Error('document.registerElement: first argument (\'name\') must
contain a dash (\'-\'). Argument provided was \'' + String(name) + '\'.'); | |
11714 } | |
11715 // prevent registering reserved names | |
11716 if (isReservedTag(name)) { | |
11717 throw new Error('Failed to execute \'registerElement\' on \'Document\': Re
gistration failed for type \'' + String(name) + '\'. The type name is invalid.')
; | |
11718 } | |
11719 // elements may only be registered once | |
11720 if (getRegisteredDefinition(name)) { | |
11721 throw new Error('DuplicateDefinitionError: a type with name \'' + String(n
ame) + '\' is already registered'); | |
11722 } | |
11723 // must have a prototype, default to an extension of HTMLElement | |
11724 // TODO(sjmiles): probably should throw if no prototype, check spec | |
11725 if (!definition.prototype) { | |
11726 // TODO(sjmiles): replace with more appropriate error (EricB can probably | |
11727 // offer guidance) | |
11728 throw new Error('Options missing required prototype property'); | |
11729 } | |
11730 // record name | |
11731 definition.__name = name.toLowerCase(); | |
11732 // ensure a lifecycle object so we don't have to null test it | |
11733 definition.lifecycle = definition.lifecycle || {}; | |
11734 // build a list of ancestral custom elements (for native base detection) | |
11735 // TODO(sjmiles): we used to need to store this, but current code only | |
11736 // uses it in 'resolveTagName': it should probably be inlined | |
11737 definition.ancestry = ancestry(definition.extends); | |
11738 // extensions of native specializations of HTMLElement require localName | |
11739 // to remain native, and use secondary 'is' specifier for extension type | |
11740 resolveTagName(definition); | |
11741 // some platforms require modifications to the user-supplied prototype | |
11742 // chain | |
11743 resolvePrototypeChain(definition); | |
11744 // overrides to implement attributeChanged callback | |
11745 overrideAttributeApi(definition.prototype); | |
11746 // 7.1.5: Register the DEFINITION with DOCUMENT | |
11747 registerDefinition(definition.__name, definition); | |
11748 // 7.1.7. Run custom element constructor generation algorithm with PROTOTYPE | |
11749 // 7.1.8. Return the output of the previous step. | |
11750 definition.ctor = generateConstructor(definition); | |
11751 definition.ctor.prototype = definition.prototype; | |
11752 // force our .constructor to be our actual constructor | |
11753 definition.prototype.constructor = definition.ctor; | |
11754 // if initial parsing is complete | |
11755 if (scope.ready) { | |
11756 // upgrade any pre-existing nodes of this type | |
11757 scope.upgradeDocumentTree(document); | |
11758 } | |
11759 return definition.ctor; | |
11760 } | |
11761 | |
11762 function isReservedTag(name) { | |
11763 for (var i = 0; i < reservedTagList.length; i++) { | |
11764 if (name === reservedTagList[i]) { | |
11765 return true; | |
11766 } | |
11767 } | |
11768 } | |
11769 | |
11770 var reservedTagList = [ | |
11771 'annotation-xml', 'color-profile', 'font-face', 'font-face-src', | |
11772 'font-face-uri', 'font-face-format', 'font-face-name', 'missing-glyph' | |
11773 ]; | |
11774 | |
11775 function ancestry(extnds) { | |
11776 var extendee = getRegisteredDefinition(extnds); | |
11777 if (extendee) { | |
11778 return ancestry(extendee.extends).concat([extendee]); | |
11779 } | |
11780 return []; | |
11781 } | |
11782 | |
11783 function resolveTagName(definition) { | |
11784 // if we are explicitly extending something, that thing is our | |
11785 // baseTag, unless it represents a custom component | |
11786 var baseTag = definition.extends; | |
11787 // if our ancestry includes custom components, we only have a | |
11788 // baseTag if one of them does | |
11789 for (var i=0, a; (a=definition.ancestry[i]); i++) { | |
11790 baseTag = a.is && a.tag; | |
11791 } | |
11792 // our tag is our baseTag, if it exists, and otherwise just our name | |
11793 definition.tag = baseTag || definition.__name; | |
11794 if (baseTag) { | |
11795 // if there is a base tag, use secondary 'is' specifier | |
11796 definition.is = definition.__name; | |
11797 } | |
11798 } | |
11799 | |
11800 function resolvePrototypeChain(definition) { | |
11801 // if we don't support __proto__ we need to locate the native level | |
11802 // prototype for precise mixing in | |
11803 if (!Object.__proto__) { | |
11804 // default prototype | |
11805 var nativePrototype = HTMLElement.prototype; | |
11806 // work out prototype when using type-extension | |
11807 if (definition.is) { | |
11808 var inst = document.createElement(definition.tag); | |
11809 var expectedPrototype = Object.getPrototypeOf(inst); | |
11810 // only set nativePrototype if it will actually appear in the definition
's chain | |
11811 if (expectedPrototype === definition.prototype) { | |
11812 nativePrototype = expectedPrototype; | |
11813 } | |
11814 } | |
11815 // ensure __proto__ reference is installed at each point on the prototype | |
11816 // chain. | |
11817 // NOTE: On platforms without __proto__, a mixin strategy is used instead | |
11818 // of prototype swizzling. In this case, this generated __proto__ provides | |
11819 // limited support for prototype traversal. | |
11820 var proto = definition.prototype, ancestor; | |
11821 while (proto && (proto !== nativePrototype)) { | |
11822 ancestor = Object.getPrototypeOf(proto); | |
11823 proto.__proto__ = ancestor; | |
11824 proto = ancestor; | |
11825 } | |
11826 // cache this in case of mixin | |
11827 definition.native = nativePrototype; | |
11828 } | |
11829 } | |
11830 | |
11831 // SECTION 4 | |
11832 | |
11833 function instantiate(definition) { | |
11834 // 4.a.1. Create a new object that implements PROTOTYPE | |
11835 // 4.a.2. Let ELEMENT by this new object | |
11836 // | |
11837 // the custom element instantiation algorithm must also ensure that the | |
11838 // output is a valid DOM element with the proper wrapper in place. | |
11839 // | |
11840 return upgrade(domCreateElement(definition.tag), definition); | |
11841 } | |
11842 | |
11843 function upgrade(element, definition) { | |
11844 // some definitions specify an 'is' attribute | |
11845 if (definition.is) { | |
11846 element.setAttribute('is', definition.is); | |
11847 } | |
11848 // make 'element' implement definition.prototype | |
11849 implement(element, definition); | |
11850 // flag as upgraded | |
11851 element.__upgraded__ = true; | |
11852 // lifecycle management | |
11853 created(element); | |
11854 // attachedCallback fires in tree order, call before recursing | |
11855 scope.insertedNode(element); | |
11856 // there should never be a shadow root on element at this point | |
11857 scope.upgradeSubtree(element); | |
11858 // OUTPUT | |
11859 return element; | |
11860 } | |
11861 | |
11862 function implement(element, definition) { | |
11863 // prototype swizzling is best | |
11864 if (Object.__proto__) { | |
11865 element.__proto__ = definition.prototype; | |
11866 } else { | |
11867 // where above we can re-acquire inPrototype via | |
11868 // getPrototypeOf(Element), we cannot do so when | |
11869 // we use mixin, so we install a magic reference | |
11870 customMixin(element, definition.prototype, definition.native); | |
11871 element.__proto__ = definition.prototype; | |
11872 } | |
11873 } | |
11874 | |
11875 function customMixin(inTarget, inSrc, inNative) { | |
11876 // TODO(sjmiles): 'used' allows us to only copy the 'youngest' version of | |
11877 // any property. This set should be precalculated. We also need to | |
11878 // consider this for supporting 'super'. | |
11879 var used = {}; | |
11880 // start with inSrc | |
11881 var p = inSrc; | |
11882 // The default is HTMLElement.prototype, so we add a test to avoid mixing in | |
11883 // native prototypes | |
11884 while (p !== inNative && p !== HTMLElement.prototype) { | |
11885 var keys = Object.getOwnPropertyNames(p); | |
11886 for (var i=0, k; k=keys[i]; i++) { | |
11887 if (!used[k]) { | |
11888 Object.defineProperty(inTarget, k, | |
11889 Object.getOwnPropertyDescriptor(p, k)); | |
11890 used[k] = 1; | |
11891 } | |
11892 } | |
11893 p = Object.getPrototypeOf(p); | |
11894 } | |
11895 } | |
11896 | |
11897 function created(element) { | |
11898 // invoke createdCallback | |
11899 if (element.createdCallback) { | |
11900 element.createdCallback(); | |
11901 } | |
11902 } | |
11903 | |
11904 // attribute watching | |
11905 | |
11906 function overrideAttributeApi(prototype) { | |
11907 // overrides to implement callbacks | |
11908 // TODO(sjmiles): should support access via .attributes NamedNodeMap | |
11909 // TODO(sjmiles): preserves user defined overrides, if any | |
11910 if (prototype.setAttribute._polyfilled) { | |
11911 return; | |
11912 } | |
11913 var setAttribute = prototype.setAttribute; | |
11914 prototype.setAttribute = function(name, value) { | |
11915 changeAttribute.call(this, name, value, setAttribute); | |
11916 } | |
11917 var removeAttribute = prototype.removeAttribute; | |
11918 prototype.removeAttribute = function(name) { | |
11919 changeAttribute.call(this, name, null, removeAttribute); | |
11920 } | |
11921 prototype.setAttribute._polyfilled = true; | |
11922 } | |
11923 | |
11924 // https://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/custom/ | |
11925 // index.html#dfn-attribute-changed-callback | |
11926 function changeAttribute(name, value, operation) { | |
11927 name = name.toLowerCase(); | |
11928 var oldValue = this.getAttribute(name); | |
11929 operation.apply(this, arguments); | |
11930 var newValue = this.getAttribute(name); | |
11931 if (this.attributeChangedCallback | |
11932 && (newValue !== oldValue)) { | |
11933 this.attributeChangedCallback(name, oldValue, newValue); | |
11934 } | |
11935 } | |
11936 | |
11937 // element registry (maps tag names to definitions) | |
11938 | |
11939 var registry = {}; | |
11940 | |
11941 function getRegisteredDefinition(name) { | |
11942 if (name) { | |
11943 return registry[name.toLowerCase()]; | |
11944 } | |
11945 } | |
11946 | |
11947 function registerDefinition(name, definition) { | |
11948 registry[name] = definition; | |
11949 } | |
11950 | |
11951 function generateConstructor(definition) { | |
11952 return function() { | |
11953 return instantiate(definition); | |
11954 }; | |
11955 } | |
11956 | |
11957 var HTML_NAMESPACE = 'http://www.w3.org/1999/xhtml'; | |
11958 function createElementNS(namespace, tag, typeExtension) { | |
11959 // NOTE: we do not support non-HTML elements, | |
11960 // just call createElementNS for non HTML Elements | |
11961 if (namespace === HTML_NAMESPACE) { | |
11962 return createElement(tag, typeExtension); | |
11963 } else { | |
11964 return domCreateElementNS(namespace, tag); | |
11965 } | |
11966 } | |
11967 | |
11968 function createElement(tag, typeExtension) { | |
11969 // TODO(sjmiles): ignore 'tag' when using 'typeExtension', we could | |
11970 // error check it, or perhaps there should only ever be one argument | |
11971 var definition = getRegisteredDefinition(typeExtension || tag); | |
11972 if (definition) { | |
11973 if (tag == definition.tag && typeExtension == definition.is) { | |
11974 return new definition.ctor(); | |
11975 } | |
11976 // Handle empty string for type extension. | |
11977 if (!typeExtension && !definition.is) { | |
11978 return new definition.ctor(); | |
11979 } | |
11980 } | |
11981 | |
11982 if (typeExtension) { | |
11983 var element = createElement(tag); | |
11984 element.setAttribute('is', typeExtension); | |
11985 return element; | |
11986 } | |
11987 var element = domCreateElement(tag); | |
11988 // Custom tags should be HTMLElements even if not upgraded. | |
11989 if (tag.indexOf('-') >= 0) { | |
11990 implement(element, HTMLElement); | |
11991 } | |
11992 return element; | |
11993 } | |
11994 | |
11995 function upgradeElement(element) { | |
11996 if (!element.__upgraded__ && (element.nodeType === Node.ELEMENT_NODE)) { | |
11997 var is = element.getAttribute('is'); | |
11998 var definition = getRegisteredDefinition(is || element.localName); | |
11999 if (definition) { | |
12000 if (is && definition.tag == element.localName) { | |
12001 return upgrade(element, definition); | |
12002 } else if (!is && !definition.extends) { | |
12003 return upgrade(element, definition); | |
12004 } | |
12005 } | |
12006 } | |
12007 } | |
12008 | |
12009 function cloneNode(deep) { | |
12010 // call original clone | |
12011 var n = domCloneNode.call(this, deep); | |
12012 // upgrade the element and subtree | |
12013 scope.upgradeAll(n); | |
12014 // return the clone | |
12015 return n; | |
12016 } | |
12017 // capture native createElement before we override it | |
12018 | |
12019 var domCreateElement = document.createElement.bind(document); | |
12020 var domCreateElementNS = document.createElementNS.bind(document); | |
12021 | |
12022 // capture native cloneNode before we override it | |
12023 | |
12024 var domCloneNode = Node.prototype.cloneNode; | |
12025 | |
12026 // exports | |
12027 | |
12028 document.registerElement = register; | |
12029 document.createElement = createElement; // override | |
12030 document.createElementNS = createElementNS; // override | |
12031 Node.prototype.cloneNode = cloneNode; // override | |
12032 | |
12033 scope.registry = registry; | |
12034 | |
12035 /** | |
12036 * Upgrade an element to a custom element. Upgrading an element | |
12037 * causes the custom prototype to be applied, an `is` attribute | |
12038 * to be attached (as needed), and invocation of the `readyCallback`. | |
12039 * `upgrade` does nothing if the element is already upgraded, or | |
12040 * if it matches no registered custom tag name. | |
12041 * | |
12042 * @method ugprade | |
12043 * @param {Element} element The element to upgrade. | |
12044 * @return {Element} The upgraded element. | |
12045 */ | |
12046 scope.upgrade = upgradeElement; | |
12047 } | |
12048 | |
12049 // Create a custom 'instanceof'. This is necessary when CustomElements | |
12050 // are implemented via a mixin strategy, as for example on IE10. | |
12051 var isInstance; | |
12052 if (!Object.__proto__ && !useNative) { | |
12053 isInstance = function(obj, ctor) { | |
12054 var p = obj; | |
12055 while (p) { | |
12056 // NOTE: this is not technically correct since we're not checking if | |
12057 // an object is an instance of a constructor; however, this should | |
12058 // be good enough for the mixin strategy. | |
12059 if (p === ctor.prototype) { | |
12060 return true; | |
12061 } | |
12062 p = p.__proto__; | |
12063 } | |
12064 return false; | |
12065 } | |
12066 } else { | |
12067 isInstance = function(obj, base) { | |
12068 return obj instanceof base; | |
12069 } | |
12070 } | |
12071 | |
12072 // exports | |
12073 scope.instanceof = isInstance; | |
12074 scope.reservedTagList = reservedTagList; | |
12075 | |
12076 // bc | |
12077 document.register = document.registerElement; | |
12078 | |
12079 scope.hasNative = hasNative; | |
12080 scope.useNative = useNative; | |
12081 | |
12082 })(window.CustomElements); | |
12083 | |
12084 /* | |
12085 * Copyright (c) 2014 The Polymer Project Authors. All rights reserved. | |
12086 * This code may only be used under the BSD style license found at http://polyme
r.github.io/LICENSE.txt | |
12087 * The complete set of authors may be found at http://polymer.github.io/AUTHORS.
txt | |
12088 * The complete set of contributors may be found at http://polymer.github.io/CON
TRIBUTORS.txt | |
12089 * Code distributed by Google as part of the polymer project is also | |
12090 * subject to an additional IP rights grant found at http://polymer.github.io/PA
TENTS.txt | |
12091 */ | |
12092 | |
12093 (function(scope) { | |
12094 | |
12095 // import | |
12096 | |
12097 var IMPORT_LINK_TYPE = scope.IMPORT_LINK_TYPE; | |
12098 | |
12099 // highlander object for parsing a document tree | |
12100 | |
12101 var parser = { | |
12102 selectors: [ | |
12103 'link[rel=' + IMPORT_LINK_TYPE + ']' | |
12104 ], | |
12105 map: { | |
12106 link: 'parseLink' | |
12107 }, | |
12108 parse: function(inDocument) { | |
12109 if (!inDocument.__parsed) { | |
12110 // only parse once | |
12111 inDocument.__parsed = true; | |
12112 // all parsable elements in inDocument (depth-first pre-order traversal) | |
12113 var elts = inDocument.querySelectorAll(parser.selectors); | |
12114 // for each parsable node type, call the mapped parsing method | |
12115 forEach(elts, function(e) { | |
12116 parser[parser.map[e.localName]](e); | |
12117 }); | |
12118 // upgrade all upgradeable static elements, anything dynamically | |
12119 // created should be caught by observer | |
12120 CustomElements.upgradeDocument(inDocument); | |
12121 // observe document for dom changes | |
12122 CustomElements.observeDocument(inDocument); | |
12123 } | |
12124 }, | |
12125 parseLink: function(linkElt) { | |
12126 // imports | |
12127 if (isDocumentLink(linkElt)) { | |
12128 this.parseImport(linkElt); | |
12129 } | |
12130 }, | |
12131 parseImport: function(linkElt) { | |
12132 if (linkElt.import) { | |
12133 parser.parse(linkElt.import); | |
12134 } | |
12135 } | |
12136 }; | |
12137 | |
12138 function isDocumentLink(inElt) { | |
12139 return (inElt.localName === 'link' | |
12140 && inElt.getAttribute('rel') === IMPORT_LINK_TYPE); | |
12141 } | |
12142 | |
12143 var forEach = Array.prototype.forEach.call.bind(Array.prototype.forEach); | |
12144 | |
12145 // exports | |
12146 | |
12147 scope.parser = parser; | |
12148 scope.IMPORT_LINK_TYPE = IMPORT_LINK_TYPE; | |
12149 | |
12150 })(window.CustomElements); | |
12151 /* | |
12152 * Copyright (c) 2014 The Polymer Project Authors. All rights reserved. | |
12153 * This code may only be used under the BSD style license found at http://polyme
r.github.io/LICENSE.txt | |
12154 * The complete set of authors may be found at http://polymer.github.io/AUTHORS.
txt | |
12155 * The complete set of contributors may be found at http://polymer.github.io/CON
TRIBUTORS.txt | |
12156 * Code distributed by Google as part of the polymer project is also | |
12157 * subject to an additional IP rights grant found at http://polymer.github.io/PA
TENTS.txt | |
12158 */ | |
12159 (function(scope){ | |
12160 | |
12161 // bootstrap parsing | |
12162 function bootstrap() { | |
12163 // parse document | |
12164 CustomElements.parser.parse(document); | |
12165 // one more pass before register is 'live' | |
12166 CustomElements.upgradeDocument(document); | |
12167 // install upgrade hook if HTMLImports are available | |
12168 if (window.HTMLImports) { | |
12169 HTMLImports.__importsParsingHook = function(elt) { | |
12170 CustomElements.parser.parse(elt.import); | |
12171 } | |
12172 } | |
12173 // set internal 'ready' flag, now document.registerElement will trigger | |
12174 // synchronous upgrades | |
12175 CustomElements.ready = true; | |
12176 // async to ensure *native* custom elements upgrade prior to this | |
12177 // DOMContentLoaded can fire before elements upgrade (e.g. when there's | |
12178 // an external script) | |
12179 setTimeout(function() { | |
12180 // capture blunt profiling data | |
12181 CustomElements.readyTime = Date.now(); | |
12182 if (window.HTMLImports) { | |
12183 CustomElements.elapsed = CustomElements.readyTime - HTMLImports.readyTime; | |
12184 } | |
12185 // notify the system that we are bootstrapped | |
12186 document.dispatchEvent( | |
12187 new CustomEvent('WebComponentsReady', {bubbles: true}) | |
12188 ); | |
12189 }); | |
12190 } | |
12191 | |
12192 // CustomEvent shim for IE | |
12193 if (typeof window.CustomEvent !== 'function') { | |
12194 window.CustomEvent = function(inType, params) { | |
12195 params = params || {}; | |
12196 var e = document.createEvent('CustomEvent'); | |
12197 e.initCustomEvent(inType, Boolean(params.bubbles), Boolean(params.cancelable
), params.detail); | |
12198 return e; | |
12199 }; | |
12200 window.CustomEvent.prototype = window.Event.prototype; | |
12201 } | |
12202 | |
12203 // When loading at readyState complete time (or via flag), boot custom elements | |
12204 // immediately. | |
12205 // If relevant, HTMLImports must already be loaded. | |
12206 if (document.readyState === 'complete' || scope.flags.eager) { | |
12207 bootstrap(); | |
12208 // When loading at readyState interactive time, bootstrap only if HTMLImports | |
12209 // are not pending. Also avoid IE as the semantics of this state are unreliable. | |
12210 } else if (document.readyState === 'interactive' && !window.attachEvent && | |
12211 (!window.HTMLImports || window.HTMLImports.ready)) { | |
12212 bootstrap(); | |
12213 // When loading at other readyStates, wait for the appropriate DOM event to | |
12214 // bootstrap. | |
12215 } else { | |
12216 var loadEvent = window.HTMLImports && !HTMLImports.ready ? | |
12217 'HTMLImportsLoaded' : 'DOMContentLoaded'; | |
12218 window.addEventListener(loadEvent, bootstrap); | |
12219 } | |
12220 | |
12221 })(window.CustomElements); | |
12222 | |
12223 /* | |
12224 * Copyright (c) 2014 The Polymer Project Authors. All rights reserved. | |
12225 * This code may only be used under the BSD style license found at http://polyme
r.github.io/LICENSE.txt | |
12226 * The complete set of authors may be found at http://polymer.github.io/AUTHORS.
txt | |
12227 * The complete set of contributors may be found at http://polymer.github.io/CON
TRIBUTORS.txt | |
12228 * Code distributed by Google as part of the polymer project is also | |
12229 * subject to an additional IP rights grant found at http://polymer.github.io/PA
TENTS.txt | |
12230 */ | |
12231 | |
12232 (function() { | |
12233 | |
12234 if (window.ShadowDOMPolyfill) { | |
12235 | |
12236 // ensure wrapped inputs for these functions | |
12237 var fns = ['upgradeAll', 'upgradeSubtree', 'observeDocument', | |
12238 'upgradeDocument']; | |
12239 | |
12240 // cache originals | |
12241 var original = {}; | |
12242 fns.forEach(function(fn) { | |
12243 original[fn] = CustomElements[fn]; | |
12244 }); | |
12245 | |
12246 // override | |
12247 fns.forEach(function(fn) { | |
12248 CustomElements[fn] = function(inNode) { | |
12249 return original[fn](wrap(inNode)); | |
12250 }; | |
12251 }); | |
12252 | |
12253 } | |
12254 | |
12255 })(); | |
12256 | |
12257 /* | |
12258 * Copyright (c) 2014 The Polymer Project Authors. All rights reserved. | |
12259 * This code may only be used under the BSD style license found at http://polyme
r.github.io/LICENSE.txt | |
12260 * The complete set of authors may be found at http://polymer.github.io/AUTHORS.
txt | |
12261 * The complete set of contributors may be found at http://polymer.github.io/CON
TRIBUTORS.txt | |
12262 * Code distributed by Google as part of the polymer project is also | |
12263 * subject to an additional IP rights grant found at http://polymer.github.io/PA
TENTS.txt | |
12264 */ | |
12265 | |
12266 (function(scope) { | |
12267 | |
12268 'use strict'; | |
12269 | |
12270 // polyfill performance.now | |
12271 | |
12272 if (!window.performance) { | |
12273 var start = Date.now(); | |
12274 // only at millisecond precision | |
12275 window.performance = {now: function(){ return Date.now() - start }}; | |
12276 } | |
12277 | |
12278 // polyfill for requestAnimationFrame | |
12279 | |
12280 if (!window.requestAnimationFrame) { | |
12281 window.requestAnimationFrame = (function() { | |
12282 var nativeRaf = window.webkitRequestAnimationFrame || | |
12283 window.mozRequestAnimationFrame; | |
12284 | |
12285 return nativeRaf ? | |
12286 function(callback) { | |
12287 return nativeRaf(function() { | |
12288 callback(performance.now()); | |
12289 }); | |
12290 } : | |
12291 function( callback ){ | |
12292 return window.setTimeout(callback, 1000 / 60); | |
12293 }; | |
12294 })(); | |
12295 } | |
12296 | |
12297 if (!window.cancelAnimationFrame) { | |
12298 window.cancelAnimationFrame = (function() { | |
12299 return window.webkitCancelAnimationFrame || | |
12300 window.mozCancelAnimationFrame || | |
12301 function(id) { | |
12302 clearTimeout(id); | |
12303 }; | |
12304 })(); | |
12305 } | |
12306 | |
12307 // Make a stub for Polymer() for polyfill purposes; under the HTMLImports | |
12308 // polyfill, scripts in the main document run before imports. That means | |
12309 // if (1) polymer is imported and (2) Polymer() is called in the main document | |
12310 // in a script after the import, 2 occurs before 1. We correct this here | |
12311 // by specfiically patching Polymer(); this is not necessary under native | |
12312 // HTMLImports. | |
12313 var elementDeclarations = []; | |
12314 | |
12315 var polymerStub = function(name, dictionary) { | |
12316 if ((typeof name !== 'string') && (arguments.length === 1)) { | |
12317 Array.prototype.push.call(arguments, document._currentScript); | |
12318 } | |
12319 elementDeclarations.push(arguments); | |
12320 }; | |
12321 window.Polymer = polymerStub; | |
12322 | |
12323 // deliver queued delcarations | |
12324 scope.consumeDeclarations = function(callback) { | |
12325 scope.consumeDeclarations = function() { | |
12326 throw 'Possible attempt to load Polymer twice'; | |
12327 }; | |
12328 if (callback) { | |
12329 callback(elementDeclarations); | |
12330 } | |
12331 elementDeclarations = null; | |
12332 }; | |
12333 | |
12334 function installPolymerWarning() { | |
12335 if (window.Polymer === polymerStub) { | |
12336 window.Polymer = function() { | |
12337 throw new Error('You tried to use polymer without loading it first. To '
+ | |
12338 'load polymer, <link rel="import" href="' + | |
12339 'components/polymer/polymer.html">'); | |
12340 }; | |
12341 } | |
12342 } | |
12343 | |
12344 // Once DOMContent has loaded, any main document scripts that depend on | |
12345 // Polymer() should have run. Calling Polymer() now is an error until | |
12346 // polymer is imported. | |
12347 if (HTMLImports.useNative) { | |
12348 installPolymerWarning(); | |
12349 } else { | |
12350 addEventListener('DOMContentLoaded', installPolymerWarning); | |
12351 } | |
12352 | |
12353 })(window.Platform); | |
12354 | |
12355 /* | |
12356 * Copyright (c) 2014 The Polymer Project Authors. All rights reserved. | |
12357 * This code may only be used under the BSD style license found at http://polyme
r.github.io/LICENSE.txt | |
12358 * The complete set of authors may be found at http://polymer.github.io/AUTHORS.
txt | |
12359 * The complete set of contributors may be found at http://polymer.github.io/CON
TRIBUTORS.txt | |
12360 * Code distributed by Google as part of the polymer project is also | |
12361 * subject to an additional IP rights grant found at http://polymer.github.io/PA
TENTS.txt | |
12362 */ | |
12363 | |
12364 (function(scope) { | |
12365 | |
12366 // It's desireable to provide a default stylesheet | |
12367 // that's convenient for styling unresolved elements, but | |
12368 // it's cumbersome to have to include this manually in every page. | |
12369 // It would make sense to put inside some HTMLImport but | |
12370 // the HTMLImports polyfill does not allow loading of stylesheets | |
12371 // that block rendering. Therefore this injection is tolerated here. | |
12372 // | |
12373 // NOTE: position: relative fixes IE's failure to inherit opacity | |
12374 // when a child is not statically positioned. | |
12375 var style = document.createElement('style'); | |
12376 style.textContent = '' | |
12377 + 'body {' | |
12378 + 'transition: opacity ease-in 0.2s;' | |
12379 + ' } \n' | |
12380 + 'body[unresolved] {' | |
12381 + 'opacity: 0; display: block; overflow: hidden; position: relative;' | |
12382 + ' } \n' | |
12383 ; | |
12384 var head = document.querySelector('head'); | |
12385 head.insertBefore(style, head.firstChild); | |
12386 | |
12387 })(Platform); | |
12388 | |
12389 /* | |
12390 * Copyright (c) 2014 The Polymer Project Authors. All rights reserved. | |
12391 * This code may only be used under the BSD style license found at http://polyme
r.github.io/LICENSE.txt | |
12392 * The complete set of authors may be found at http://polymer.github.io/AUTHORS.
txt | |
12393 * The complete set of contributors may be found at http://polymer.github.io/CON
TRIBUTORS.txt | |
12394 * Code distributed by Google as part of the polymer project is also | |
12395 * subject to an additional IP rights grant found at http://polymer.github.io/PA
TENTS.txt | |
12396 */ | |
12397 | |
12398 (function(scope) { | |
12399 | |
12400 function withDependencies(task, depends) { | |
12401 depends = depends || []; | |
12402 if (!depends.map) { | |
12403 depends = [depends]; | |
12404 } | |
12405 return task.apply(this, depends.map(marshal)); | |
12406 } | |
12407 | |
12408 function module(name, dependsOrFactory, moduleFactory) { | |
12409 var module; | |
12410 switch (arguments.length) { | |
12411 case 0: | |
12412 return; | |
12413 case 1: | |
12414 module = null; | |
12415 break; | |
12416 case 2: | |
12417 // dependsOrFactory is `factory` in this case | |
12418 module = dependsOrFactory.apply(this); | |
12419 break; | |
12420 default: | |
12421 // dependsOrFactory is `depends` in this case | |
12422 module = withDependencies(moduleFactory, dependsOrFactory); | |
12423 break; | |
12424 } | |
12425 modules[name] = module; | |
12426 }; | |
12427 | |
12428 function marshal(name) { | |
12429 return modules[name]; | |
12430 } | |
12431 | |
12432 var modules = {}; | |
12433 | |
12434 function using(depends, task) { | |
12435 HTMLImports.whenImportsReady(function() { | |
12436 withDependencies(task, depends); | |
12437 }); | |
12438 }; | |
12439 | |
12440 // exports | |
12441 | |
12442 scope.marshal = marshal; | |
12443 // `module` confuses commonjs detectors | |
12444 scope.modularize = module; | |
12445 scope.using = using; | |
12446 | |
12447 })(window); | |
12448 | |
12449 //# sourceMappingURL=platform.concat.js.map | |
OLD | NEW |