Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(9)

Side by Side Diff: pkg/shadow_dom/lib/shadow_dom.debug.js

Issue 24129004: fix shadowdom polyfill (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 7 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « pkg/pkg.status ('k') | pkg/shadow_dom/lib/shadow_dom.min.js » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 if ((!HTMLElement.prototype.createShadowRoot && 1 if ((!HTMLElement.prototype.createShadowRoot &&
2 !HTMLElement.prototype.webkitCreateShadowRoot) || 2 !HTMLElement.prototype.webkitCreateShadowRoot) ||
3 window.__forceShadowDomPolyfill) { 3 window.__forceShadowDomPolyfill) {
4 4
5 /* 5 /*
6 * Copyright 2013 The Polymer Authors. All rights reserved. 6 * Copyright 2013 The Polymer Authors. All rights reserved.
7 * Use of this source code is governed by a BSD-style 7 * Use of this source code is governed by a BSD-style
8 * license that can be found in the LICENSE file. 8 * license that can be found in the LICENSE file.
9 */ 9 */
10 (function() { 10 (function() {
11 // TODO(jmesserly): fix dart:html to use unprefixed name 11 // TODO(jmesserly): fix dart:html to use unprefixed name
12 if (Element.prototype.webkitCreateShadowRoot) { 12 if (Element.prototype.webkitCreateShadowRoot) {
13 Element.prototype.webkitCreateShadowRoot = function() { 13 Element.prototype.webkitCreateShadowRoot = function() {
14 return window.ShadowDOMPolyfill.wrapIfNeeded(this).createShadowRoot(); 14 return window.ShadowDOMPolyfill.wrapIfNeeded(this).createShadowRoot();
15 }; 15 };
16 } 16 }
17 })(); 17 })();
18 18
19 // Copyright 2012 Google Inc.
20 //
21 // Licensed under the Apache License, Version 2.0 (the "License");
22 // you may not use this file except in compliance with the License.
23 // You may obtain a copy of the License at
24 //
25 // http://www.apache.org/licenses/LICENSE-2.0
26 //
27 // Unless required by applicable law or agreed to in writing, software
28 // distributed under the License is distributed on an "AS IS" BASIS,
29 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
30 // See the License for the specific language governing permissions and
31 // limitations under the License.
32
33 (function(global) {
34 'use strict';
35
36 function detectObjectObserve() {
37 if (typeof Object.observe !== 'function' ||
38 typeof Array.observe !== 'function') {
39 return false;
40 }
41
42 var gotSplice = false;
43 function callback(records) {
44 if (records[0].type === 'splice' && records[1].type === 'splice')
45 gotSplice = true;
46 }
47
48 var test = [0];
49 Array.observe(test, callback);
50 test[1] = 1;
51 test.length = 0;
52 Object.deliverChangeRecords(callback);
53 return gotSplice;
54 }
55
56 var hasObserve = detectObjectObserve();
57
58 function detectEval() {
59 // don't test for eval if document has CSP securityPolicy object and we can see that
60 // eval is not supported. This avoids an error message in console even when the exception
61 // is caught
62 if (global.document &&
63 'securityPolicy' in global.document &&
64 !global.document.securityPolicy.allowsEval) {
65 return false;
66 }
67
68 try {
69 var f = new Function('', 'return true;');
70 return f();
71 } catch (ex) {
72 return false;
73 }
74 }
75
76 var hasEval = detectEval();
77
78 function isIndex(s) {
79 return +s === s >>> 0;
80 }
81
82 function toNumber(s) {
83 return +s;
84 }
85
86 function isObject(obj) {
87 return obj === Object(obj);
88 }
89
90 var numberIsNaN = global.Number.isNaN || function isNaN(value) {
91 return typeof value === 'number' && global.isNaN(value);
92 }
93
94 function areSameValue(left, right) {
95 if (left === right)
96 return left !== 0 || 1 / left === 1 / right;
97 if (numberIsNaN(left) && numberIsNaN(right))
98 return true;
99
100 return left !== left && right !== right;
101 }
102
103 var createObject = ('__proto__' in {}) ?
104 function(obj) { return obj; } :
105 function(obj) {
106 var proto = obj.__proto__;
107 if (!proto)
108 return obj;
109 var newObject = Object.create(proto);
110 Object.getOwnPropertyNames(obj).forEach(function(name) {
111 Object.defineProperty(newObject, name,
112 Object.getOwnPropertyDescriptor(obj, name));
113 });
114 return newObject;
115 };
116
117 var identStart = '[\$_a-zA-Z]';
118 var identPart = '[\$_a-zA-Z0-9]';
119 var ident = identStart + '+' + identPart + '*';
120 var elementIndex = '(?:[0-9]|[1-9]+[0-9]+)';
121 var identOrElementIndex = '(?:' + ident + '|' + elementIndex + ')';
122 var path = '(?:' + identOrElementIndex + ')(?:\\s*\\.\\s*' + identOrElementInd ex + ')*';
123 var pathRegExp = new RegExp('^' + path + '$');
124
125 function isPathValid(s) {
126 if (typeof s != 'string')
127 return false;
128 s = s.trim();
129
130 if (s == '')
131 return true;
132
133 if (s[0] == '.')
134 return false;
135
136 return pathRegExp.test(s);
137 }
138
139 var constructorIsPrivate = {};
140
141 function Path(s, privateToken) {
142 if (privateToken !== constructorIsPrivate)
143 throw Error('Use Path.get to retrieve path objects');
144
145 if (s.trim() == '')
146 return this;
147
148 if (isIndex(s)) {
149 this.push(s);
150 return this;
151 }
152
153 s.split(/\s*\.\s*/).filter(function(part) {
154 return part;
155 }).forEach(function(part) {
156 this.push(part);
157 }, this);
158
159 if (hasEval && !hasObserve && this.length) {
160 this.getValueFrom = this.compiledGetValueFromFn();
161 }
162 }
163
164 // TODO(rafaelw): Make simple LRU cache
165 var pathCache = {};
166
167 function getPath(pathString) {
168 if (pathString instanceof Path)
169 return pathString;
170
171 if (pathString == null)
172 pathString = '';
173
174 if (typeof pathString !== 'string')
175 pathString = String(pathString);
176
177 var path = pathCache[pathString];
178 if (path)
179 return path;
180 if (!isPathValid(pathString))
181 return invalidPath;
182 var path = new Path(pathString, constructorIsPrivate);
183 pathCache[pathString] = path;
184 return path;
185 }
186
187 Path.get = getPath;
188
189 Path.prototype = createObject({
190 __proto__: [],
191 valid: true,
192
193 toString: function() {
194 return this.join('.');
195 },
196
197 getValueFrom: function(obj, observedSet) {
198 for (var i = 0; i < this.length; i++) {
199 if (obj == null)
200 return;
201 if (observedSet)
202 observedSet.observe(obj);
203 obj = obj[this[i]];
204 }
205 return obj;
206 },
207
208 compiledGetValueFromFn: function() {
209 var accessors = this.map(function(ident) {
210 return isIndex(ident) ? '["' + ident + '"]' : '.' + ident;
211 });
212
213 var str = '';
214 var pathString = 'obj';
215 str += 'if (obj != null';
216 var i = 0;
217 for (; i < (this.length - 1); i++) {
218 var ident = this[i];
219 pathString += accessors[i];
220 str += ' &&\n ' + pathString + ' != null';
221 }
222 str += ')\n';
223
224 pathString += accessors[i];
225
226 str += ' return ' + pathString + ';\nelse\n return undefined;';
227 return new Function('obj', str);
228 },
229
230 setValueFrom: function(obj, value) {
231 if (!this.length)
232 return false;
233
234 for (var i = 0; i < this.length - 1; i++) {
235 if (!isObject(obj))
236 return false;
237 obj = obj[this[i]];
238 }
239
240 if (!isObject(obj))
241 return false;
242
243 obj[this[i]] = value;
244 return true;
245 }
246 });
247
248 var invalidPath = new Path('', constructorIsPrivate);
249 invalidPath.valid = false;
250 invalidPath.getValueFrom = invalidPath.setValueFrom = function() {};
251
252 var MAX_DIRTY_CHECK_CYCLES = 1000;
253
254 function dirtyCheck(observer) {
255 var cycles = 0;
256 while (cycles < MAX_DIRTY_CHECK_CYCLES && observer.check()) {
257 observer.report();
258 cycles++;
259 }
260 if (global.testingExposeCycleCount)
261 global.dirtyCheckCycleCount = cycles;
262 }
263
264 function objectIsEmpty(object) {
265 for (var prop in object)
266 return false;
267 return true;
268 }
269
270 function diffIsEmpty(diff) {
271 return objectIsEmpty(diff.added) &&
272 objectIsEmpty(diff.removed) &&
273 objectIsEmpty(diff.changed);
274 }
275
276 function diffObjectFromOldObject(object, oldObject) {
277 var added = {};
278 var removed = {};
279 var changed = {};
280 var oldObjectHas = {};
281
282 for (var prop in oldObject) {
283 var newValue = object[prop];
284
285 if (newValue !== undefined && newValue === oldObject[prop])
286 continue;
287
288 if (!(prop in object)) {
289 removed[prop] = undefined;
290 continue;
291 }
292
293 if (newValue !== oldObject[prop])
294 changed[prop] = newValue;
295 }
296
297 for (var prop in object) {
298 if (prop in oldObject)
299 continue;
300
301 added[prop] = object[prop];
302 }
303
304 if (Array.isArray(object) && object.length !== oldObject.length)
305 changed.length = object.length;
306
307 return {
308 added: added,
309 removed: removed,
310 changed: changed
311 };
312 }
313
314 function copyObject(object, opt_copy) {
315 var copy = opt_copy || (Array.isArray(object) ? [] : {});
316 for (var prop in object) {
317 copy[prop] = object[prop];
318 };
319 if (Array.isArray(object))
320 copy.length = object.length;
321 return copy;
322 }
323
324 function Observer(object, callback, target, token) {
325 this.closed = false;
326 this.object = object;
327 this.callback = callback;
328 // TODO(rafaelw): Hold this.target weakly when WeakRef is available.
329 this.target = target;
330 this.token = token;
331 this.reporting = true;
332 if (hasObserve) {
333 var self = this;
334 this.boundInternalCallback = function(records) {
335 self.internalCallback(records);
336 };
337 }
338
339 addToAll(this);
340 }
341
342 Observer.prototype = {
343 internalCallback: function(records) {
344 if (this.closed)
345 return;
346 if (this.reporting && this.check(records)) {
347 this.report();
348 if (this.testingResults)
349 this.testingResults.anyChanged = true;
350 }
351 },
352
353 close: function() {
354 if (this.closed)
355 return;
356 if (this.object && typeof this.object.close === 'function')
357 this.object.close();
358
359 this.disconnect();
360 this.object = undefined;
361 this.closed = true;
362 },
363
364 deliver: function(testingResults) {
365 if (this.closed)
366 return;
367 if (hasObserve) {
368 this.testingResults = testingResults;
369 Object.deliverChangeRecords(this.boundInternalCallback);
370 this.testingResults = undefined;
371 } else {
372 dirtyCheck(this);
373 }
374 },
375
376 report: function() {
377 if (!this.reporting)
378 return;
379
380 this.sync(false);
381 if (this.callback) {
382 this.reportArgs.push(this.token);
383 this.invokeCallback(this.reportArgs);
384 }
385 this.reportArgs = undefined;
386 },
387
388 invokeCallback: function(args) {
389 try {
390 this.callback.apply(this.target, args);
391 } catch (ex) {
392 Observer._errorThrownDuringCallback = true;
393 console.error('Exception caught during observer callback: ' + (ex.stack || ex));
394 }
395 },
396
397 reset: function() {
398 if (this.closed)
399 return;
400
401 if (hasObserve) {
402 this.reporting = false;
403 Object.deliverChangeRecords(this.boundInternalCallback);
404 this.reporting = true;
405 }
406
407 this.sync(true);
408 }
409 }
410
411 var collectObservers = !hasObserve || global.forceCollectObservers;
412 var allObservers;
413 Observer._allObserversCount = 0;
414
415 if (collectObservers) {
416 allObservers = [];
417 }
418
419 function addToAll(observer) {
420 if (!collectObservers)
421 return;
422
423 allObservers.push(observer);
424 Observer._allObserversCount++;
425 }
426
427 var runningMicrotaskCheckpoint = false;
428
429 var hasDebugForceFullDelivery = typeof Object.deliverAllChangeRecords == 'func tion';
430
431 global.Platform = global.Platform || {};
432
433 global.Platform.performMicrotaskCheckpoint = function() {
434 if (runningMicrotaskCheckpoint)
435 return;
436
437 if (hasDebugForceFullDelivery) {
438 Object.deliverAllChangeRecords();
439 return;
440 }
441
442 if (!collectObservers)
443 return;
444
445 runningMicrotaskCheckpoint = true;
446
447 var cycles = 0;
448 var results = {};
449
450 do {
451 cycles++;
452 var toCheck = allObservers;
453 allObservers = [];
454 results.anyChanged = false;
455
456 for (var i = 0; i < toCheck.length; i++) {
457 var observer = toCheck[i];
458 if (observer.closed)
459 continue;
460
461 if (hasObserve) {
462 observer.deliver(results);
463 } else if (observer.check()) {
464 results.anyChanged = true;
465 observer.report();
466 }
467
468 allObservers.push(observer);
469 }
470 } while (cycles < MAX_DIRTY_CHECK_CYCLES && results.anyChanged);
471
472 if (global.testingExposeCycleCount)
473 global.dirtyCheckCycleCount = cycles;
474
475 Observer._allObserversCount = allObservers.length;
476 runningMicrotaskCheckpoint = false;
477 };
478
479 if (collectObservers) {
480 global.Platform.clearObservers = function() {
481 allObservers = [];
482 };
483 }
484
485 function ObjectObserver(object, callback, target, token) {
486 Observer.call(this, object, callback, target, token);
487 this.connect();
488 this.sync(true);
489 }
490
491 ObjectObserver.prototype = createObject({
492 __proto__: Observer.prototype,
493
494 connect: function() {
495 if (hasObserve)
496 Object.observe(this.object, this.boundInternalCallback);
497 },
498
499 sync: function(hard) {
500 if (!hasObserve)
501 this.oldObject = copyObject(this.object);
502 },
503
504 check: function(changeRecords) {
505 var diff;
506 var oldValues;
507 if (hasObserve) {
508 if (!changeRecords)
509 return false;
510
511 oldValues = {};
512 diff = diffObjectFromChangeRecords(this.object, changeRecords,
513 oldValues);
514 } else {
515 oldValues = this.oldObject;
516 diff = diffObjectFromOldObject(this.object, this.oldObject);
517 }
518
519 if (diffIsEmpty(diff))
520 return false;
521
522 this.reportArgs =
523 [diff.added || {}, diff.removed || {}, diff.changed || {}];
524 this.reportArgs.push(function(property) {
525 return oldValues[property];
526 });
527
528 return true;
529 },
530
531 disconnect: function() {
532 if (!hasObserve)
533 this.oldObject = undefined;
534 else if (this.object)
535 Object.unobserve(this.object, this.boundInternalCallback);
536 }
537 });
538
539 function ArrayObserver(array, callback, target, token) {
540 if (!Array.isArray(array))
541 throw Error('Provided object is not an Array');
542 ObjectObserver.call(this, array, callback, target, token);
543 }
544
545 ArrayObserver.prototype = createObject({
546 __proto__: ObjectObserver.prototype,
547
548 connect: function() {
549 if (hasObserve)
550 Array.observe(this.object, this.boundInternalCallback);
551 },
552
553 sync: function() {
554 if (!hasObserve)
555 this.oldObject = this.object.slice();
556 },
557
558 check: function(changeRecords) {
559 var splices;
560 if (hasObserve) {
561 if (!changeRecords)
562 return false;
563 splices = projectArraySplices(this.object, changeRecords);
564 } else {
565 splices = calcSplices(this.object, 0, this.object.length,
566 this.oldObject, 0, this.oldObject.length);
567 }
568
569 if (!splices || !splices.length)
570 return false;
571
572 this.reportArgs = [splices];
573 return true;
574 }
575 });
576
577 ArrayObserver.applySplices = function(previous, current, splices) {
578 splices.forEach(function(splice) {
579 var spliceArgs = [splice.index, splice.removed.length];
580 var addIndex = splice.index;
581 while (addIndex < splice.index + splice.addedCount) {
582 spliceArgs.push(current[addIndex]);
583 addIndex++;
584 }
585
586 Array.prototype.splice.apply(previous, spliceArgs);
587 });
588 };
589
590 function ObservedSet(callback) {
591 this.arr = [];
592 this.callback = callback;
593 this.isObserved = true;
594 }
595
596 var objProto = Object.getPrototypeOf({});
597 var arrayProto = Object.getPrototypeOf([]);
598 ObservedSet.prototype = {
599 reset: function() {
600 this.isObserved = !this.isObserved;
601 },
602
603 observe: function(obj) {
604 if (!isObject(obj) || obj === objProto || obj === arrayProto)
605 return;
606 var i = this.arr.indexOf(obj);
607 if (i >= 0 && this.arr[i+1] === this.isObserved)
608 return;
609
610 if (i < 0) {
611 i = this.arr.length;
612 this.arr[i] = obj;
613 Object.observe(obj, this.callback);
614 }
615
616 this.arr[i+1] = this.isObserved;
617 this.observe(Object.getPrototypeOf(obj));
618 },
619
620 cleanup: function() {
621 var i = 0, j = 0;
622 var isObserved = this.isObserved;
623 while(j < this.arr.length) {
624 var obj = this.arr[j];
625 if (this.arr[j + 1] == isObserved) {
626 if (i < j) {
627 this.arr[i] = obj;
628 this.arr[i + 1] = isObserved;
629 }
630 i += 2;
631 } else {
632 Object.unobserve(obj, this.callback);
633 }
634 j += 2;
635 }
636
637 this.arr.length = i;
638 }
639 };
640
641 function PathObserver(object, path, callback, target, token, valueFn,
642 setValueFn) {
643 var path = path instanceof Path ? path : getPath(path);
644 if (!path || !path.length || !isObject(object)) {
645 this.value_ = path ? path.getValueFrom(object) : undefined;
646 this.value = valueFn ? valueFn(this.value_) : this.value_;
647 this.closed = true;
648 return;
649 }
650
651 Observer.call(this, object, callback, target, token);
652 this.valueFn = valueFn;
653 this.setValueFn = setValueFn;
654 this.path = path;
655
656 this.connect();
657 this.sync(true);
658 }
659
660 PathObserver.prototype = createObject({
661 __proto__: Observer.prototype,
662
663 connect: function() {
664 if (hasObserve)
665 this.observedSet = new ObservedSet(this.boundInternalCallback);
666 },
667
668 disconnect: function() {
669 this.value = undefined;
670 this.value_ = undefined;
671 if (this.observedSet) {
672 this.observedSet.reset();
673 this.observedSet.cleanup();
674 this.observedSet = undefined;
675 }
676 },
677
678 check: function() {
679 // Note: Extracting this to a member function for use here and below
680 // regresses dirty-checking path perf by about 25% =-(.
681 if (this.observedSet)
682 this.observedSet.reset();
683
684 this.value_ = this.path.getValueFrom(this.object, this.observedSet);
685
686 if (this.observedSet)
687 this.observedSet.cleanup();
688
689 if (areSameValue(this.value_, this.oldValue_))
690 return false;
691
692 this.value = this.valueFn ? this.valueFn(this.value_) : this.value_;
693 this.reportArgs = [this.value, this.oldValue];
694 return true;
695 },
696
697 sync: function(hard) {
698 if (hard) {
699 if (this.observedSet)
700 this.observedSet.reset();
701
702 this.value_ = this.path.getValueFrom(this.object, this.observedSet);
703 this.value = this.valueFn ? this.valueFn(this.value_) : this.value_;
704
705 if (this.observedSet)
706 this.observedSet.cleanup();
707 }
708
709 this.oldValue_ = this.value_;
710 this.oldValue = this.value;
711 },
712
713 setValue: function(newValue) {
714 if (!this.path)
715 return;
716 if (typeof this.setValueFn === 'function')
717 newValue = this.setValueFn(newValue);
718 this.path.setValueFrom(this.object, newValue);
719 }
720 });
721
722 function CompoundPathObserver(callback, target, token, valueFn) {
723 Observer.call(this, undefined, callback, target, token);
724 this.valueFn = valueFn;
725
726 this.observed = [];
727 this.values = [];
728 this.started = false;
729 }
730
731 CompoundPathObserver.prototype = createObject({
732 __proto__: PathObserver.prototype,
733
734 addPath: function(object, path) {
735 if (this.started)
736 throw Error('Cannot add more paths once started.');
737
738 var path = path instanceof Path ? path : getPath(path);
739 var value = path ? path.getValueFrom(object) : undefined;
740
741 this.observed.push(object, path);
742 this.values.push(value);
743 },
744
745 start: function() {
746 this.connect();
747 this.sync(true);
748 },
749
750 getValues: function() {
751 if (this.observedSet)
752 this.observedSet.reset();
753
754 var anyChanged = false;
755 for (var i = 0; i < this.observed.length; i = i+2) {
756 var path = this.observed[i+1];
757 if (!path)
758 continue;
759 var object = this.observed[i];
760 var value = path.getValueFrom(object, this.observedSet);
761 var oldValue = this.values[i/2];
762 if (!areSameValue(value, oldValue)) {
763 this.values[i/2] = value;
764 anyChanged = true;
765 }
766 }
767
768 if (this.observedSet)
769 this.observedSet.cleanup();
770
771 return anyChanged;
772 },
773
774 check: function() {
775 if (!this.getValues())
776 return;
777
778 this.value = this.valueFn(this.values);
779
780 if (areSameValue(this.value, this.oldValue))
781 return false;
782
783 this.reportArgs = [this.value, this.oldValue];
784 return true;
785 },
786
787 sync: function(hard) {
788 if (hard) {
789 this.getValues();
790 this.value = this.valueFn(this.values);
791 }
792
793 this.oldValue = this.value;
794 },
795
796 close: function() {
797 if (this.observed) {
798 for (var i = 0; i < this.observed.length; i = i + 2) {
799 var object = this.observed[i];
800 if (object && typeof object.close === 'function')
801 object.close();
802 }
803 this.observed = undefined;
804 this.values = undefined;
805 }
806
807 Observer.prototype.close.call(this);
808 }
809 });
810
811 var knownRecordTypes = {
812 'new': true,
813 'updated': true,
814 'deleted': true
815 };
816
817 function notifyFunction(object, name) {
818 if (typeof Object.observe !== 'function')
819 return;
820
821 var notifier = Object.getNotifier(object);
822 return function(type, oldValue) {
823 var changeRecord = {
824 object: object,
825 type: type,
826 name: name
827 };
828 if (arguments.length === 2)
829 changeRecord.oldValue = oldValue;
830 notifier.notify(changeRecord);
831 }
832 }
833
834 // TODO(rafaelw): It should be possible for the Object.observe case to have
835 // every PathObserver used by defineProperty share a single Object.observe
836 // callback, and thus get() can simply call observer.deliver() and any changes
837 // to any dependent value will be observed.
838 PathObserver.defineProperty = function(object, name, descriptor) {
839 // TODO(rafaelw): Validate errors
840 var obj = descriptor.object;
841 var path = getPath(descriptor.path);
842 var notify = notifyFunction(object, name);
843
844 var observer = new PathObserver(obj, descriptor.path,
845 function(newValue, oldValue) {
846 if (notify)
847 notify('updated', oldValue);
848 }
849 );
850
851 Object.defineProperty(object, name, {
852 get: function() {
853 return path.getValueFrom(obj);
854 },
855 set: function(newValue) {
856 path.setValueFrom(obj, newValue);
857 },
858 configurable: true
859 });
860
861 return {
862 close: function() {
863 var oldValue = path.getValueFrom(obj);
864 if (notify)
865 observer.deliver();
866 observer.close();
867 Object.defineProperty(object, name, {
868 value: oldValue,
869 writable: true,
870 configurable: true
871 });
872 }
873 };
874 }
875
876 function diffObjectFromChangeRecords(object, changeRecords, oldValues) {
877 var added = {};
878 var removed = {};
879
880 for (var i = 0; i < changeRecords.length; i++) {
881 var record = changeRecords[i];
882 if (!knownRecordTypes[record.type]) {
883 console.error('Unknown changeRecord type: ' + record.type);
884 console.error(record);
885 continue;
886 }
887
888 if (!(record.name in oldValues))
889 oldValues[record.name] = record.oldValue;
890
891 if (record.type == 'updated')
892 continue;
893
894 if (record.type == 'new') {
895 if (record.name in removed)
896 delete removed[record.name];
897 else
898 added[record.name] = true;
899
900 continue;
901 }
902
903 // type = 'deleted'
904 if (record.name in added) {
905 delete added[record.name];
906 delete oldValues[record.name];
907 } else {
908 removed[record.name] = true;
909 }
910 }
911
912 for (var prop in added)
913 added[prop] = object[prop];
914
915 for (var prop in removed)
916 removed[prop] = undefined;
917
918 var changed = {};
919 for (var prop in oldValues) {
920 if (prop in added || prop in removed)
921 continue;
922
923 var newValue = object[prop];
924 if (oldValues[prop] !== newValue)
925 changed[prop] = newValue;
926 }
927
928 return {
929 added: added,
930 removed: removed,
931 changed: changed
932 };
933 }
934
935 function newSplice(index, removed, addedCount) {
936 return {
937 index: index,
938 removed: removed,
939 addedCount: addedCount
940 };
941 }
942
943 var EDIT_LEAVE = 0;
944 var EDIT_UPDATE = 1;
945 var EDIT_ADD = 2;
946 var EDIT_DELETE = 3;
947
948 function ArraySplice() {}
949
950 ArraySplice.prototype = {
951
952 // Note: This function is *based* on the computation of the Levenshtein
953 // "edit" distance. The one change is that "updates" are treated as two
954 // edits - not one. With Array splices, an update is really a delete
955 // followed by an add. By retaining this, we optimize for "keeping" the
956 // maximum array items in the original array. For example:
957 //
958 // 'xxxx123' -> '123yyyy'
959 //
960 // With 1-edit updates, the shortest path would be just to update all seven
961 // characters. With 2-edit updates, we delete 4, leave 3, and add 4. This
962 // leaves the substring '123' intact.
963 calcEditDistances: function(current, currentStart, currentEnd,
964 old, oldStart, oldEnd) {
965 // "Deletion" columns
966 var rowCount = oldEnd - oldStart + 1;
967 var columnCount = currentEnd - currentStart + 1;
968 var distances = new Array(rowCount);
969
970 // "Addition" rows. Initialize null column.
971 for (var i = 0; i < rowCount; i++) {
972 distances[i] = new Array(columnCount);
973 distances[i][0] = i;
974 }
975
976 // Initialize null row
977 for (var j = 0; j < columnCount; j++)
978 distances[0][j] = j;
979
980 for (var i = 1; i < rowCount; i++) {
981 for (var j = 1; j < columnCount; j++) {
982 if (this.equals(current[currentStart + j - 1], old[oldStart + i - 1]))
983 distances[i][j] = distances[i - 1][j - 1];
984 else {
985 var north = distances[i - 1][j] + 1;
986 var west = distances[i][j - 1] + 1;
987 distances[i][j] = north < west ? north : west;
988 }
989 }
990 }
991
992 return distances;
993 },
994
995 // This starts at the final weight, and walks "backward" by finding
996 // the minimum previous weight recursively until the origin of the weight
997 // matrix.
998 spliceOperationsFromEditDistances: function(distances) {
999 var i = distances.length - 1;
1000 var j = distances[0].length - 1;
1001 var current = distances[i][j];
1002 var edits = [];
1003 while (i > 0 || j > 0) {
1004 if (i == 0) {
1005 edits.push(EDIT_ADD);
1006 j--;
1007 continue;
1008 }
1009 if (j == 0) {
1010 edits.push(EDIT_DELETE);
1011 i--;
1012 continue;
1013 }
1014 var northWest = distances[i - 1][j - 1];
1015 var west = distances[i - 1][j];
1016 var north = distances[i][j - 1];
1017
1018 var min;
1019 if (west < north)
1020 min = west < northWest ? west : northWest;
1021 else
1022 min = north < northWest ? north : northWest;
1023
1024 if (min == northWest) {
1025 if (northWest == current) {
1026 edits.push(EDIT_LEAVE);
1027 } else {
1028 edits.push(EDIT_UPDATE);
1029 current = northWest;
1030 }
1031 i--;
1032 j--;
1033 } else if (min == west) {
1034 edits.push(EDIT_DELETE);
1035 i--;
1036 current = west;
1037 } else {
1038 edits.push(EDIT_ADD);
1039 j--;
1040 current = north;
1041 }
1042 }
1043
1044 edits.reverse();
1045 return edits;
1046 },
1047
1048 /**
1049 * Splice Projection functions:
1050 *
1051 * A splice map is a representation of how a previous array of items
1052 * was transformed into a new array of items. Conceptually it is a list of
1053 * tuples of
1054 *
1055 * <index, removed, addedCount>
1056 *
1057 * which are kept in ascending index order of. The tuple represents that at
1058 * the |index|, |removed| sequence of items were removed, and counting forwa rd
1059 * from |index|, |addedCount| items were added.
1060 */
1061
1062 /**
1063 * Lacking individual splice mutation information, the minimal set of
1064 * splices can be synthesized given the previous state and final state of an
1065 * array. The basic approach is to calculate the edit distance matrix and
1066 * choose the shortest path through it.
1067 *
1068 * Complexity: O(l * p)
1069 * l: The length of the current array
1070 * p: The length of the old array
1071 */
1072 calcSplices: function(current, currentStart, currentEnd,
1073 old, oldStart, oldEnd) {
1074 var prefixCount = 0;
1075 var suffixCount = 0;
1076
1077 var minLength = Math.min(currentEnd - currentStart, oldEnd - oldStart);
1078 if (currentStart == 0 && oldStart == 0)
1079 prefixCount = this.sharedPrefix(current, old, minLength);
1080
1081 if (currentEnd == current.length && oldEnd == old.length)
1082 suffixCount = this.sharedSuffix(current, old, minLength - prefixCount);
1083
1084 currentStart += prefixCount;
1085 oldStart += prefixCount;
1086 currentEnd -= suffixCount;
1087 oldEnd -= suffixCount;
1088
1089 if (currentEnd - currentStart == 0 && oldEnd - oldStart == 0)
1090 return [];
1091
1092 if (currentStart == currentEnd) {
1093 var splice = newSplice(currentStart, [], 0);
1094 while (oldStart < oldEnd)
1095 splice.removed.push(old[oldStart++]);
1096
1097 return [ splice ];
1098 } else if (oldStart == oldEnd)
1099 return [ newSplice(currentStart, [], currentEnd - currentStart) ];
1100
1101 var ops = this.spliceOperationsFromEditDistances(
1102 this.calcEditDistances(current, currentStart, currentEnd,
1103 old, oldStart, oldEnd));
1104
1105 var splice = undefined;
1106 var splices = [];
1107 var index = currentStart;
1108 var oldIndex = oldStart;
1109 for (var i = 0; i < ops.length; i++) {
1110 switch(ops[i]) {
1111 case EDIT_LEAVE:
1112 if (splice) {
1113 splices.push(splice);
1114 splice = undefined;
1115 }
1116
1117 index++;
1118 oldIndex++;
1119 break;
1120 case EDIT_UPDATE:
1121 if (!splice)
1122 splice = newSplice(index, [], 0);
1123
1124 splice.addedCount++;
1125 index++;
1126
1127 splice.removed.push(old[oldIndex]);
1128 oldIndex++;
1129 break;
1130 case EDIT_ADD:
1131 if (!splice)
1132 splice = newSplice(index, [], 0);
1133
1134 splice.addedCount++;
1135 index++;
1136 break;
1137 case EDIT_DELETE:
1138 if (!splice)
1139 splice = newSplice(index, [], 0);
1140
1141 splice.removed.push(old[oldIndex]);
1142 oldIndex++;
1143 break;
1144 }
1145 }
1146
1147 if (splice) {
1148 splices.push(splice);
1149 }
1150 return splices;
1151 },
1152
1153 sharedPrefix: function(current, old, searchLength) {
1154 for (var i = 0; i < searchLength; i++)
1155 if (!this.equals(current[i], old[i]))
1156 return i;
1157 return searchLength;
1158 },
1159
1160 sharedSuffix: function(current, old, searchLength) {
1161 var index1 = current.length;
1162 var index2 = old.length;
1163 var count = 0;
1164 while (count < searchLength && this.equals(current[--index1], old[--index2 ]))
1165 count++;
1166
1167 return count;
1168 },
1169
1170 calculateSplices: function(current, previous) {
1171 return this.calcSplices(current, 0, current.length, previous, 0,
1172 previous.length);
1173 },
1174
1175 equals: function(currentValue, previousValue) {
1176 return currentValue === previousValue;
1177 }
1178 };
1179
1180 var arraySplice = new ArraySplice();
1181
1182 function calcSplices(current, currentStart, currentEnd,
1183 old, oldStart, oldEnd) {
1184 return arraySplice.calcSplices(current, currentStart, currentEnd,
1185 old, oldStart, oldEnd);
1186 }
1187
1188 function intersect(start1, end1, start2, end2) {
1189 // Disjoint
1190 if (end1 < start2 || end2 < start1)
1191 return -1;
1192
1193 // Adjacent
1194 if (end1 == start2 || end2 == start1)
1195 return 0;
1196
1197 // Non-zero intersect, span1 first
1198 if (start1 < start2) {
1199 if (end1 < end2)
1200 return end1 - start2; // Overlap
1201 else
1202 return end2 - start2; // Contained
1203 } else {
1204 // Non-zero intersect, span2 first
1205 if (end2 < end1)
1206 return end2 - start1; // Overlap
1207 else
1208 return end1 - start1; // Contained
1209 }
1210 }
1211
1212 function mergeSplice(splices, index, removed, addedCount) {
1213
1214 var splice = newSplice(index, removed, addedCount);
1215
1216 var inserted = false;
1217 var insertionOffset = 0;
1218
1219 for (var i = 0; i < splices.length; i++) {
1220 var current = splices[i];
1221 current.index += insertionOffset;
1222
1223 if (inserted)
1224 continue;
1225
1226 var intersectCount = intersect(splice.index,
1227 splice.index + splice.removed.length,
1228 current.index,
1229 current.index + current.addedCount);
1230
1231 if (intersectCount >= 0) {
1232 // Merge the two splices
1233
1234 splices.splice(i, 1);
1235 i--;
1236
1237 insertionOffset -= current.addedCount - current.removed.length;
1238
1239 splice.addedCount += current.addedCount - intersectCount;
1240 var deleteCount = splice.removed.length +
1241 current.removed.length - intersectCount;
1242
1243 if (!splice.addedCount && !deleteCount) {
1244 // merged splice is a noop. discard.
1245 inserted = true;
1246 } else {
1247 var removed = current.removed;
1248
1249 if (splice.index < current.index) {
1250 // some prefix of splice.removed is prepended to current.removed.
1251 var prepend = splice.removed.slice(0, current.index - splice.index);
1252 Array.prototype.push.apply(prepend, removed);
1253 removed = prepend;
1254 }
1255
1256 if (splice.index + splice.removed.length > current.index + current.add edCount) {
1257 // some suffix of splice.removed is appended to current.removed.
1258 var append = splice.removed.slice(current.index + current.addedCount - splice.index);
1259 Array.prototype.push.apply(removed, append);
1260 }
1261
1262 splice.removed = removed;
1263 if (current.index < splice.index) {
1264 splice.index = current.index;
1265 }
1266 }
1267 } else if (splice.index < current.index) {
1268 // Insert splice here.
1269
1270 inserted = true;
1271
1272 splices.splice(i, 0, splice);
1273 i++;
1274
1275 var offset = splice.addedCount - splice.removed.length
1276 current.index += offset;
1277 insertionOffset += offset;
1278 }
1279 }
1280
1281 if (!inserted)
1282 splices.push(splice);
1283 }
1284
1285 function createInitialSplices(array, changeRecords) {
1286 var splices = [];
1287
1288 for (var i = 0; i < changeRecords.length; i++) {
1289 var record = changeRecords[i];
1290 switch(record.type) {
1291 case 'splice':
1292 mergeSplice(splices, record.index, record.removed.slice(), record.adde dCount);
1293 break;
1294 case 'new':
1295 case 'updated':
1296 case 'deleted':
1297 if (!isIndex(record.name))
1298 continue;
1299 var index = toNumber(record.name);
1300 if (index < 0)
1301 continue;
1302 mergeSplice(splices, index, [record.oldValue], 1);
1303 break;
1304 default:
1305 console.error('Unexpected record type: ' + JSON.stringify(record));
1306 break;
1307 }
1308 }
1309
1310 return splices;
1311 }
1312
1313 function projectArraySplices(array, changeRecords) {
1314 var splices = [];
1315
1316 createInitialSplices(array, changeRecords).forEach(function(splice) {
1317 if (splice.addedCount == 1 && splice.removed.length == 1) {
1318 if (splice.removed[0] !== array[splice.index])
1319 splices.push(splice);
1320
1321 return
1322 };
1323
1324 splices = splices.concat(calcSplices(array, splice.index, splice.index + s plice.addedCount,
1325 splice.removed, 0, splice.removed.len gth));
1326 });
1327
1328 return splices;
1329 }
1330
1331 global.Observer = Observer;
1332 global.Observer.hasObjectObserve = hasObserve;
1333 global.ArrayObserver = ArrayObserver;
1334 global.ArrayObserver.calculateSplices = function(current, previous) {
1335 return arraySplice.calculateSplices(current, previous);
1336 };
1337
1338 global.ArraySplice = ArraySplice;
1339 global.ObjectObserver = ObjectObserver;
1340 global.PathObserver = PathObserver;
1341 global.CompoundPathObserver = CompoundPathObserver;
1342 global.Path = Path;
1343 })(typeof global !== 'undefined' && global ? global : this);
1344
19 /* 1345 /*
20 * Copyright 2012 The Polymer Authors. All rights reserved. 1346 * Copyright 2012 The Polymer Authors. All rights reserved.
21 * Use of this source code is goverened by a BSD-style 1347 * Use of this source code is governed by a BSD-style
22 * license that can be found in the LICENSE file. 1348 * license that can be found in the LICENSE file.
23 */ 1349 */
24 1350
25 // SideTable is a weak map where possible. If WeakMap is not available the 1351 // If WeakMap is not available, the association is stored as an expando property on the "key".
26 // association is stored as an expando property.
27 var SideTable;
28 // TODO(arv): WeakMap does not allow for Node etc to be keys in Firefox 1352 // TODO(arv): WeakMap does not allow for Node etc to be keys in Firefox
29 if (typeof WeakMap !== 'undefined' && navigator.userAgent.indexOf('Firefox/') < 0) { 1353 if (typeof WeakMap === 'undefined' || navigator.userAgent.indexOf('Firefox/') > -1) {
30 SideTable = WeakMap;
31 } else {
32 (function() { 1354 (function() {
33 var defineProperty = Object.defineProperty; 1355 var defineProperty = Object.defineProperty;
34 var counter = Date.now() % 1e9; 1356 var counter = Date.now() % 1e9;
35 1357
36 SideTable = function() { 1358 var WeakMap = function() {
37 this.name = '__st' + (Math.random() * 1e9 >>> 0) + (counter++ + '__'); 1359 this.name = '__st' + (Math.random() * 1e9 >>> 0) + (counter++ + '__');
38 }; 1360 };
39 1361
40 SideTable.prototype = { 1362 WeakMap.prototype = {
41 set: function(key, value) { 1363 set: function(key, value) {
42 var entry = key[this.name]; 1364 var entry = key[this.name];
43 if (entry && entry[0] === key) 1365 if (entry && entry[0] === key)
44 entry[1] = value; 1366 entry[1] = value;
45 else 1367 else
46 defineProperty(key, this.name, {value: [key, value], writable: true}); 1368 defineProperty(key, this.name, {value: [key, value], writable: true});
47 }, 1369 },
48 get: function(key) { 1370 get: function(key) {
49 var entry; 1371 var entry;
50 return (entry = key[this.name]) && entry[0] === key ? 1372 return (entry = key[this.name]) && entry[0] === key ?
51 entry[1] : undefined; 1373 entry[1] : undefined;
52 }, 1374 },
53 delete: function(key) { 1375 delete: function(key) {
54 this.set(key, undefined); 1376 this.set(key, undefined);
55 } 1377 }
56 } 1378 };
1379
1380 window.WeakMap = WeakMap;
57 })(); 1381 })();
58 } 1382 }
59 1383
60 // Copyright 2012 The Polymer Authors. All rights reserved. 1384 // Copyright 2012 The Polymer Authors. All rights reserved.
61 // Use of this source code is goverened by a BSD-style 1385 // Use of this source code is goverened by a BSD-style
62 // license that can be found in the LICENSE file. 1386 // license that can be found in the LICENSE file.
63 1387
64 var ShadowDOMPolyfill = {}; 1388 var ShadowDOMPolyfill = {};
65 1389
66 (function(scope) { 1390 (function(scope) {
67 'use strict'; 1391 'use strict';
68 1392
69 var wrapperTable = new SideTable(); 1393 var constructorTable = new WeakMap();
70 var constructorTable = new SideTable(); 1394 var nativePrototypeTable = new WeakMap();
71 var nativePrototypeTable = new SideTable();
72 var wrappers = Object.create(null); 1395 var wrappers = Object.create(null);
73 1396
1397 // Don't test for eval if document has CSP securityPolicy object and we can
1398 // see that eval is not supported. This avoids an error message in console
1399 // even when the exception is caught
1400 var hasEval = !('securityPolicy' in document) ||
1401 document.securityPolicy.allowsEval;
1402 if (hasEval) {
1403 try {
1404 var f = new Function('', 'return true;');
1405 hasEval = f();
1406 } catch (ex) {
1407 }
1408 }
1409
74 function assert(b) { 1410 function assert(b) {
75 if (!b) 1411 if (!b)
76 throw new Error('Assertion failed'); 1412 throw new Error('Assertion failed');
77 }; 1413 };
78 1414
79 function mixin(to, from) { 1415 function mixin(to, from) {
80 Object.getOwnPropertyNames(from).forEach(function(name) { 1416 Object.getOwnPropertyNames(from).forEach(function(name) {
81 Object.defineProperty(to, name, 1417 Object.defineProperty(to, name,
82 Object.getOwnPropertyDescriptor(from, name)); 1418 Object.getOwnPropertyDescriptor(from, name));
83 }); 1419 });
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after
143 get: function() {}, 1479 get: function() {},
144 set: function(v) {}, 1480 set: function(v) {},
145 configurable: true, 1481 configurable: true,
146 enumerable: true 1482 enumerable: true
147 }; 1483 };
148 1484
149 function isEventHandlerName(name) { 1485 function isEventHandlerName(name) {
150 return /^on[a-z]+$/.test(name); 1486 return /^on[a-z]+$/.test(name);
151 } 1487 }
152 1488
1489 function getGetter(name) {
1490 return hasEval ?
1491 new Function('return this.impl.' + name) :
1492 function() { return this.impl[name]; };
1493 }
1494
1495 function getSetter(name) {
1496 return hasEval ?
1497 new Function('v', 'this.impl.' + name + ' = v') :
1498 function(v) { this.impl[name] = v; };
1499 }
1500
1501 function getMethod(name) {
1502 return hasEval ?
1503 new Function('return this.impl.' + name +
1504 '.apply(this.impl, arguments)') :
1505 function() { return this.impl[name].apply(this.impl, arguments); };
1506 }
1507
153 function installProperty(source, target, allowMethod) { 1508 function installProperty(source, target, allowMethod) {
154 Object.getOwnPropertyNames(source).forEach(function(name) { 1509 Object.getOwnPropertyNames(source).forEach(function(name) {
155 if (name in target) 1510 if (name in target)
156 return; 1511 return;
157 1512
158 if (isFirefox) { 1513 if (isFirefox) {
159 // Tickle Firefox's old bindings. 1514 // Tickle Firefox's old bindings.
160 source.__lookupGetter__(name); 1515 source.__lookupGetter__(name);
161 } 1516 }
162 var descriptor; 1517 var descriptor;
163 try { 1518 try {
164 descriptor = Object.getOwnPropertyDescriptor(source, name); 1519 descriptor = Object.getOwnPropertyDescriptor(source, name);
165 } catch (ex) { 1520 } catch (ex) {
166 // JSC and V8 both use data properties instead of accessors which can 1521 // JSC and V8 both use data properties instead of accessors which can
167 // cause getting the property desciptor to throw an exception. 1522 // cause getting the property desciptor to throw an exception.
168 // https://bugs.webkit.org/show_bug.cgi?id=49739 1523 // https://bugs.webkit.org/show_bug.cgi?id=49739
169 descriptor = dummyDescriptor; 1524 descriptor = dummyDescriptor;
170 } 1525 }
171 var getter, setter; 1526 var getter, setter;
172 if (allowMethod && typeof descriptor.value === 'function') { 1527 if (allowMethod && typeof descriptor.value === 'function') {
173 target[name] = function() { 1528 target[name] = getMethod(name);
174 return this.impl[name].apply(this.impl, arguments);
175 };
176 return; 1529 return;
177 } 1530 }
178 1531
179 var isEvent = isEventHandlerName(name); 1532 var isEvent = isEventHandlerName(name);
180 if (isEvent) { 1533 if (isEvent)
181 getter = scope.getEventHandlerGetter(name); 1534 getter = scope.getEventHandlerGetter(name);
182 } else { 1535 else
183 getter = function() { 1536 getter = getGetter(name);
184 return this.impl[name];
185 };
186 }
187 1537
188 if (descriptor.writable || descriptor.set) { 1538 if (descriptor.writable || descriptor.set) {
189 if (isEvent) { 1539 if (isEvent)
190 setter = scope.getEventHandlerSetter(name); 1540 setter = scope.getEventHandlerSetter(name);
191 } else { 1541 else
192 setter = function(value) { 1542 setter = getSetter(name);
193 this.impl[name] = value;
194 };
195 }
196 } 1543 }
197 1544
198 Object.defineProperty(target, name, { 1545 Object.defineProperty(target, name, {
199 get: getter, 1546 get: getter,
200 set: setter, 1547 set: setter,
201 configurable: descriptor.configurable, 1548 configurable: descriptor.configurable,
202 enumerable: descriptor.enumerable 1549 enumerable: descriptor.enumerable
203 }); 1550 });
204 }); 1551 });
205 } 1552 }
(...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after
289 * Wraps a node in a WrapperNode. If there already exists a wrapper for the 1636 * Wraps a node in a WrapperNode. If there already exists a wrapper for the
290 * |node| that wrapper is returned instead. 1637 * |node| that wrapper is returned instead.
291 * @param {Node} node 1638 * @param {Node} node
292 * @return {WrapperNode} 1639 * @return {WrapperNode}
293 */ 1640 */
294 function wrap(impl) { 1641 function wrap(impl) {
295 if (impl === null) 1642 if (impl === null)
296 return null; 1643 return null;
297 1644
298 assert(isNative(impl)); 1645 assert(isNative(impl));
299 var wrapper = wrapperTable.get(impl); 1646 return impl.polymerWrapper_ ||
300 if (!wrapper) 1647 (impl.polymerWrapper_ = new (getWrapperConstructor(impl))(impl));
301 wrapperTable.set(impl, wrapper = new (getWrapperConstructor(impl))(impl));
302 return wrapper;
303 } 1648 }
304 1649
305 /** 1650 /**
306 * Unwraps a wrapper and returns the node it is wrapping. 1651 * Unwraps a wrapper and returns the node it is wrapping.
307 * @param {WrapperNode} wrapper 1652 * @param {WrapperNode} wrapper
308 * @return {Node} 1653 * @return {Node}
309 */ 1654 */
310 function unwrap(wrapper) { 1655 function unwrap(wrapper) {
311 if (wrapper === null) 1656 if (wrapper === null)
312 return null; 1657 return null;
(...skipping 23 matching lines...) Expand all
336 * Overrides the current wrapper (if any) for node. 1681 * Overrides the current wrapper (if any) for node.
337 * @param {Node} node 1682 * @param {Node} node
338 * @param {WrapperNode=} wrapper If left out the wrapper will be created as 1683 * @param {WrapperNode=} wrapper If left out the wrapper will be created as
339 * needed next time someone wraps the node. 1684 * needed next time someone wraps the node.
340 */ 1685 */
341 function rewrap(node, wrapper) { 1686 function rewrap(node, wrapper) {
342 if (wrapper === null) 1687 if (wrapper === null)
343 return; 1688 return;
344 assert(isNative(node)); 1689 assert(isNative(node));
345 assert(wrapper === undefined || isWrapper(wrapper)); 1690 assert(wrapper === undefined || isWrapper(wrapper));
346 wrapperTable.set(node, wrapper); 1691 node.polymerWrapper_ = wrapper;
347 } 1692 }
348 1693
349 function defineGetter(constructor, name, getter) { 1694 function defineGetter(constructor, name, getter) {
350 Object.defineProperty(constructor.prototype, name, { 1695 Object.defineProperty(constructor.prototype, name, {
351 get: getter, 1696 get: getter,
352 configurable: true, 1697 configurable: true,
353 enumerable: true 1698 enumerable: true
354 }); 1699 });
355 } 1700 }
356 1701
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after
404 (function(scope) { 1749 (function(scope) {
405 'use strict'; 1750 'use strict';
406 1751
407 var forwardMethodsToWrapper = scope.forwardMethodsToWrapper; 1752 var forwardMethodsToWrapper = scope.forwardMethodsToWrapper;
408 var mixin = scope.mixin; 1753 var mixin = scope.mixin;
409 var registerWrapper = scope.registerWrapper; 1754 var registerWrapper = scope.registerWrapper;
410 var unwrap = scope.unwrap; 1755 var unwrap = scope.unwrap;
411 var wrap = scope.wrap; 1756 var wrap = scope.wrap;
412 var wrappers = scope.wrappers; 1757 var wrappers = scope.wrappers;
413 1758
414 var wrappedFuns = new SideTable(); 1759 var wrappedFuns = new WeakMap();
415 var listenersTable = new SideTable(); 1760 var listenersTable = new WeakMap();
416 var handledEventsTable = new SideTable(); 1761 var handledEventsTable = new WeakMap();
417 var targetTable = new SideTable(); 1762 var targetTable = new WeakMap();
418 var currentTargetTable = new SideTable(); 1763 var currentTargetTable = new WeakMap();
419 var relatedTargetTable = new SideTable(); 1764 var relatedTargetTable = new WeakMap();
420 var eventPhaseTable = new SideTable(); 1765 var eventPhaseTable = new WeakMap();
421 var stopPropagationTable = new SideTable(); 1766 var stopPropagationTable = new WeakMap();
422 var stopImmediatePropagationTable = new SideTable(); 1767 var stopImmediatePropagationTable = new WeakMap();
423 var eventHandlersTable = new SideTable(); 1768 var eventHandlersTable = new WeakMap();
424 var eventPathTable = new SideTable(); 1769 var eventPathTable = new WeakMap();
425 1770
426 function isShadowRoot(node) { 1771 function isShadowRoot(node) {
427 return node instanceof wrappers.ShadowRoot; 1772 return node instanceof wrappers.ShadowRoot;
428 } 1773 }
429 1774
430 function isInsertionPoint(node) { 1775 function isInsertionPoint(node) {
431 var localName = node.localName; 1776 var localName = node.localName;
432 return localName === 'content' || localName === 'shadow'; 1777 return localName === 'content' || localName === 'shadow';
433 } 1778 }
434 1779
(...skipping 136 matching lines...) Expand 10 before | Expand all | Expand 10 after
571 } 1916 }
572 1917
573 function enclosedBy(a, b) { 1918 function enclosedBy(a, b) {
574 if (a === b) 1919 if (a === b)
575 return true; 1920 return true;
576 if (a instanceof wrappers.ShadowRoot) { 1921 if (a instanceof wrappers.ShadowRoot) {
577 var host = scope.getHostForShadowRoot(a); 1922 var host = scope.getHostForShadowRoot(a);
578 return enclosedBy(rootOfNode(host), b); 1923 return enclosedBy(rootOfNode(host), b);
579 } 1924 }
580 return false; 1925 return false;
581
582 } 1926 }
583 1927
584 function isMutationEvent(type) { 1928 function isMutationEvent(type) {
585 switch (type) { 1929 switch (type) {
586 case 'DOMAttrModified': 1930 case 'DOMAttrModified':
587 case 'DOMAttributeNameChanged': 1931 case 'DOMAttributeNameChanged':
588 case 'DOMCharacterDataModified': 1932 case 'DOMCharacterDataModified':
589 case 'DOMElementNameChanged': 1933 case 'DOMElementNameChanged':
590 case 'DOMNodeInserted': 1934 case 'DOMNodeInserted':
591 case 'DOMNodeInsertedIntoDocument': 1935 case 'DOMNodeInsertedIntoDocument':
(...skipping 443 matching lines...) Expand 10 before | Expand all | Expand 10 after
1035 } 2379 }
1036 } 2380 }
1037 } 2381 }
1038 2382
1039 if (found && count === 1) { 2383 if (found && count === 1) {
1040 var target = getTargetToListenAt(this); 2384 var target = getTargetToListenAt(this);
1041 target.removeEventListener_(type, dispatchOriginalEvent, true); 2385 target.removeEventListener_(type, dispatchOriginalEvent, true);
1042 } 2386 }
1043 }, 2387 },
1044 dispatchEvent: function(event) { 2388 dispatchEvent: function(event) {
1045 scope.renderAllPending();
1046 var target = getTargetToListenAt(this); 2389 var target = getTargetToListenAt(this);
1047 return target.dispatchEvent_(unwrap(event)); 2390 return target.dispatchEvent_(unwrap(event));
1048 } 2391 }
1049 }; 2392 };
1050 2393
1051 if (OriginalEventTarget) 2394 if (OriginalEventTarget)
1052 registerWrapper(OriginalEventTarget, EventTarget); 2395 registerWrapper(OriginalEventTarget, EventTarget);
1053 2396
1054 function wrapEventTargetMethods(constructors) { 2397 function wrapEventTargetMethods(constructors) {
1055 forwardMethodsToWrapper(constructors, methodNames); 2398 forwardMethodsToWrapper(constructors, methodNames);
(...skipping 148 matching lines...) Expand 10 before | Expand all | Expand 10 after
1204 assert(node instanceof Node); 2547 assert(node instanceof Node);
1205 } 2548 }
1206 2549
1207 /** 2550 /**
1208 * Collects nodes from a DocumentFragment or a Node for removal followed 2551 * Collects nodes from a DocumentFragment or a Node for removal followed
1209 * by an insertion. 2552 * by an insertion.
1210 * 2553 *
1211 * This updates the internal pointers for node, previousNode and nextNode. 2554 * This updates the internal pointers for node, previousNode and nextNode.
1212 */ 2555 */
1213 function collectNodes(node, parentNode, previousNode, nextNode) { 2556 function collectNodes(node, parentNode, previousNode, nextNode) {
1214 if (node.nodeType !== Node.DOCUMENT_FRAGMENT_NODE) { 2557 if (!(node instanceof DocumentFragment)) {
1215 if (node.parentNode) 2558 if (node.parentNode)
1216 node.parentNode.removeChild(node); 2559 node.parentNode.removeChild(node);
1217 node.parentNode_ = parentNode; 2560 node.parentNode_ = parentNode;
1218 node.previousSibling_ = previousNode; 2561 node.previousSibling_ = previousNode;
1219 node.nextSibling_ = nextNode; 2562 node.nextSibling_ = nextNode;
1220 if (previousNode) 2563 if (previousNode)
1221 previousNode.nextSibling_ = node; 2564 previousNode.nextSibling_ = node;
1222 if (nextNode) 2565 if (nextNode)
1223 nextNode.previousSibling_ = node; 2566 nextNode.previousSibling_ = node;
1224 return [node]; 2567 return [node];
(...skipping 13 matching lines...) Expand all
1238 } 2581 }
1239 2582
1240 if (previousNode) 2583 if (previousNode)
1241 previousNode.nextSibling_ = nodes[0]; 2584 previousNode.nextSibling_ = nodes[0];
1242 if (nextNode) 2585 if (nextNode)
1243 nextNode.previousSibling_ = nodes[nodes.length - 1]; 2586 nextNode.previousSibling_ = nodes[nodes.length - 1];
1244 2587
1245 return nodes; 2588 return nodes;
1246 } 2589 }
1247 2590
2591 function collectNodesNoNeedToUpdatePointers(node) {
2592 if (node instanceof DocumentFragment) {
2593 var nodes = [];
2594 var i = 0;
2595 for (var child = node.firstChild; child; child = child.nextSibling) {
2596 nodes[i++] = child;
2597 }
2598 return nodes;
2599 }
2600 return [node];
2601 }
2602
2603 function nodesWereAdded(nodes) {
2604 for (var i = 0; i < nodes.length; i++) {
2605 nodes[i].nodeWasAdded_();
2606 }
2607 }
2608
1248 function ensureSameOwnerDocument(parent, child) { 2609 function ensureSameOwnerDocument(parent, child) {
1249 var ownerDoc = parent.nodeType === Node.DOCUMENT_NODE ? 2610 var ownerDoc = parent.nodeType === Node.DOCUMENT_NODE ?
1250 parent : parent.ownerDocument; 2611 parent : parent.ownerDocument;
1251 if (ownerDoc !== child.ownerDocument) 2612 if (ownerDoc !== child.ownerDocument)
1252 ownerDoc.adoptNode(child); 2613 ownerDoc.adoptNode(child);
1253 } 2614 }
1254 2615
1255 function adoptNodesIfNeeded(owner, nodes) { 2616 function adoptNodesIfNeeded(owner, nodes) {
1256 if (!nodes.length) 2617 if (!nodes.length)
1257 return; 2618 return;
(...skipping 108 matching lines...) Expand 10 before | Expand all | Expand 10 after
1366 var originalReplaceChild = OriginalNode.prototype.replaceChild; 2727 var originalReplaceChild = OriginalNode.prototype.replaceChild;
1367 var originalRemoveChild = OriginalNode.prototype.removeChild; 2728 var originalRemoveChild = OriginalNode.prototype.removeChild;
1368 var originalCompareDocumentPosition = 2729 var originalCompareDocumentPosition =
1369 OriginalNode.prototype.compareDocumentPosition; 2730 OriginalNode.prototype.compareDocumentPosition;
1370 2731
1371 Node.prototype = Object.create(EventTarget.prototype); 2732 Node.prototype = Object.create(EventTarget.prototype);
1372 mixin(Node.prototype, { 2733 mixin(Node.prototype, {
1373 appendChild: function(childWrapper) { 2734 appendChild: function(childWrapper) {
1374 assertIsNodeWrapper(childWrapper); 2735 assertIsNodeWrapper(childWrapper);
1375 2736
2737 var nodes;
2738
1376 if (this.invalidateShadowRenderer() || invalidateParent(childWrapper)) { 2739 if (this.invalidateShadowRenderer() || invalidateParent(childWrapper)) {
1377 var previousNode = this.lastChild; 2740 var previousNode = this.lastChild;
1378 var nextNode = null; 2741 var nextNode = null;
1379 var nodes = collectNodes(childWrapper, this, previousNode, nextNode); 2742 nodes = collectNodes(childWrapper, this, previousNode, nextNode);
1380 2743
1381 this.lastChild_ = nodes[nodes.length - 1]; 2744 this.lastChild_ = nodes[nodes.length - 1];
1382 if (!previousNode) 2745 if (!previousNode)
1383 this.firstChild_ = nodes[0]; 2746 this.firstChild_ = nodes[0];
1384 2747
1385 originalAppendChild.call(this.impl, unwrapNodesForInsertion(this, nodes) ); 2748 originalAppendChild.call(this.impl, unwrapNodesForInsertion(this, nodes) );
1386 } else { 2749 } else {
2750 nodes = collectNodesNoNeedToUpdatePointers(childWrapper)
1387 ensureSameOwnerDocument(this, childWrapper); 2751 ensureSameOwnerDocument(this, childWrapper);
1388 originalAppendChild.call(this.impl, unwrap(childWrapper)); 2752 originalAppendChild.call(this.impl, unwrap(childWrapper));
1389 } 2753 }
1390 2754
1391 childWrapper.nodeWasAdded_(); 2755 nodesWereAdded(nodes);
1392 2756
1393 return childWrapper; 2757 return childWrapper;
1394 }, 2758 },
1395 2759
1396 insertBefore: function(childWrapper, refWrapper) { 2760 insertBefore: function(childWrapper, refWrapper) {
1397 // TODO(arv): Unify with appendChild 2761 // TODO(arv): Unify with appendChild
1398 if (!refWrapper) 2762 if (!refWrapper)
1399 return this.appendChild(childWrapper); 2763 return this.appendChild(childWrapper);
1400 2764
1401 assertIsNodeWrapper(childWrapper); 2765 assertIsNodeWrapper(childWrapper);
1402 assertIsNodeWrapper(refWrapper); 2766 assertIsNodeWrapper(refWrapper);
1403 assert(refWrapper.parentNode === this); 2767 assert(refWrapper.parentNode === this);
1404 2768
2769 var nodes;
2770
1405 if (this.invalidateShadowRenderer() || invalidateParent(childWrapper)) { 2771 if (this.invalidateShadowRenderer() || invalidateParent(childWrapper)) {
1406 var previousNode = refWrapper.previousSibling; 2772 var previousNode = refWrapper.previousSibling;
1407 var nextNode = refWrapper; 2773 var nextNode = refWrapper;
1408 var nodes = collectNodes(childWrapper, this, previousNode, nextNode); 2774 nodes = collectNodes(childWrapper, this, previousNode, nextNode);
1409 2775
1410 if (this.firstChild === refWrapper) 2776 if (this.firstChild === refWrapper)
1411 this.firstChild_ = nodes[0]; 2777 this.firstChild_ = nodes[0];
1412 2778
1413 // insertBefore refWrapper no matter what the parent is? 2779 // insertBefore refWrapper no matter what the parent is?
1414 var refNode = unwrap(refWrapper); 2780 var refNode = unwrap(refWrapper);
1415 var parentNode = refNode.parentNode; 2781 var parentNode = refNode.parentNode;
1416 2782
1417 if (parentNode) { 2783 if (parentNode) {
1418 originalInsertBefore.call( 2784 originalInsertBefore.call(
1419 parentNode, 2785 parentNode,
1420 unwrapNodesForInsertion(this, nodes), 2786 unwrapNodesForInsertion(this, nodes),
1421 refNode); 2787 refNode);
1422 } else { 2788 } else {
1423 adoptNodesIfNeeded(this, nodes); 2789 adoptNodesIfNeeded(this, nodes);
1424 } 2790 }
1425 } else { 2791 } else {
2792 nodes = collectNodesNoNeedToUpdatePointers(childWrapper);
1426 ensureSameOwnerDocument(this, childWrapper); 2793 ensureSameOwnerDocument(this, childWrapper);
1427 originalInsertBefore.call(this.impl, unwrap(childWrapper), 2794 originalInsertBefore.call(this.impl, unwrap(childWrapper),
1428 unwrap(refWrapper)); 2795 unwrap(refWrapper));
1429 } 2796 }
1430 2797
1431 childWrapper.nodeWasAdded_(); 2798 nodesWereAdded(nodes);
1432 2799
1433 return childWrapper; 2800 return childWrapper;
1434 }, 2801 },
1435 2802
1436 removeChild: function(childWrapper) { 2803 removeChild: function(childWrapper) {
1437 assertIsNodeWrapper(childWrapper); 2804 assertIsNodeWrapper(childWrapper);
1438 if (childWrapper.parentNode !== this) { 2805 if (childWrapper.parentNode !== this) {
1439 // TODO(arv): DOMException 2806 // TODO(arv): DOMException
1440 throw new Error('NotFoundError'); 2807 throw new Error('NotFoundError');
1441 } 2808 }
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
1478 replaceChild: function(newChildWrapper, oldChildWrapper) { 2845 replaceChild: function(newChildWrapper, oldChildWrapper) {
1479 assertIsNodeWrapper(newChildWrapper); 2846 assertIsNodeWrapper(newChildWrapper);
1480 assertIsNodeWrapper(oldChildWrapper); 2847 assertIsNodeWrapper(oldChildWrapper);
1481 2848
1482 if (oldChildWrapper.parentNode !== this) { 2849 if (oldChildWrapper.parentNode !== this) {
1483 // TODO(arv): DOMException 2850 // TODO(arv): DOMException
1484 throw new Error('NotFoundError'); 2851 throw new Error('NotFoundError');
1485 } 2852 }
1486 2853
1487 var oldChildNode = unwrap(oldChildWrapper); 2854 var oldChildNode = unwrap(oldChildWrapper);
2855 var nodes;
1488 2856
1489 if (this.invalidateShadowRenderer() || 2857 if (this.invalidateShadowRenderer() ||
1490 invalidateParent(newChildWrapper)) { 2858 invalidateParent(newChildWrapper)) {
1491 var previousNode = oldChildWrapper.previousSibling; 2859 var previousNode = oldChildWrapper.previousSibling;
1492 var nextNode = oldChildWrapper.nextSibling; 2860 var nextNode = oldChildWrapper.nextSibling;
1493 if (nextNode === newChildWrapper) 2861 if (nextNode === newChildWrapper)
1494 nextNode = newChildWrapper.nextSibling; 2862 nextNode = newChildWrapper.nextSibling;
1495 var nodes = collectNodes(newChildWrapper, this, 2863 nodes = collectNodes(newChildWrapper, this, previousNode, nextNode);
1496 previousNode, nextNode);
1497 2864
1498 if (this.firstChild === oldChildWrapper) 2865 if (this.firstChild === oldChildWrapper)
1499 this.firstChild_ = nodes[0]; 2866 this.firstChild_ = nodes[0];
1500 if (this.lastChild === oldChildWrapper) 2867 if (this.lastChild === oldChildWrapper)
1501 this.lastChild_ = nodes[nodes.length - 1]; 2868 this.lastChild_ = nodes[nodes.length - 1];
1502 2869
1503 oldChildWrapper.previousSibling_ = oldChildWrapper.nextSibling_ = 2870 oldChildWrapper.previousSibling_ = oldChildWrapper.nextSibling_ =
1504 oldChildWrapper.parentNode_ = undefined; 2871 oldChildWrapper.parentNode_ = undefined;
1505 2872
1506 // replaceChild no matter what the parent is? 2873 // replaceChild no matter what the parent is?
1507 if (oldChildNode.parentNode) { 2874 if (oldChildNode.parentNode) {
1508 originalReplaceChild.call( 2875 originalReplaceChild.call(
1509 oldChildNode.parentNode, 2876 oldChildNode.parentNode,
1510 unwrapNodesForInsertion(this, nodes), 2877 unwrapNodesForInsertion(this, nodes),
1511 oldChildNode); 2878 oldChildNode);
1512 } 2879 }
1513 } else { 2880 } else {
2881 nodes = collectNodesNoNeedToUpdatePointers(newChildWrapper);
1514 ensureSameOwnerDocument(this, newChildWrapper); 2882 ensureSameOwnerDocument(this, newChildWrapper);
1515 originalReplaceChild.call(this.impl, unwrap(newChildWrapper), 2883 originalReplaceChild.call(this.impl, unwrap(newChildWrapper),
1516 oldChildNode); 2884 oldChildNode);
1517 } 2885 }
1518 2886
1519 newChildWrapper.nodeWasAdded_(); 2887 nodesWereAdded(nodes);
1520 2888
1521 return oldChildWrapper; 2889 return oldChildWrapper;
1522 }, 2890 },
1523 2891
1524 /** 2892 /**
1525 * Called after a node was added. Subclasses override this to invalidate 2893 * Called after a node was added. Subclasses override this to invalidate
1526 * the renderer as needed. 2894 * the renderer as needed.
1527 * @private 2895 * @private
1528 */ 2896 */
1529 nodeWasAdded_: function() {}, 2897 nodeWasAdded_: function() {},
(...skipping 98 matching lines...) Expand 10 before | Expand all | Expand 10 after
1628 var parentNode = child.parentNode; 2996 var parentNode = child.parentNode;
1629 if (!parentNode) 2997 if (!parentNode)
1630 return false; 2998 return false;
1631 return this.contains(parentNode); 2999 return this.contains(parentNode);
1632 }, 3000 },
1633 3001
1634 compareDocumentPosition: function(otherNode) { 3002 compareDocumentPosition: function(otherNode) {
1635 // This only wraps, it therefore only operates on the composed DOM and not 3003 // This only wraps, it therefore only operates on the composed DOM and not
1636 // the logical DOM. 3004 // the logical DOM.
1637 return originalCompareDocumentPosition.call(this.impl, unwrap(otherNode)); 3005 return originalCompareDocumentPosition.call(this.impl, unwrap(otherNode));
1638 },
1639
1640 // TODO(jmesserly): this is a workaround for
1641 // https://github.com/Polymer/ShadowDOM/issues/200
1642 get ownerDocument() {
1643 scope.renderAllPending();
1644 return wrap(this.impl.ownerDocument);
1645 } 3006 }
1646 }); 3007 });
1647 3008
1648 // TODO(jmesserly): this is commented out to workaround: 3009 defineWrapGetter(Node, 'ownerDocument');
1649 // https://github.com/Polymer/ShadowDOM/issues/200
1650 //defineWrapGetter(Node, 'ownerDocument');
1651 3010
1652 // We use a DocumentFragment as a base and then delete the properties of 3011 // We use a DocumentFragment as a base and then delete the properties of
1653 // DocumentFragment.prototype from the wrapper Node. Since delete makes 3012 // DocumentFragment.prototype from the wrapper Node. Since delete makes
1654 // objects slow in some JS engines we recreate the prototype object. 3013 // objects slow in some JS engines we recreate the prototype object.
1655 registerWrapper(OriginalNode, Node, document.createDocumentFragment()); 3014 registerWrapper(OriginalNode, Node, document.createDocumentFragment());
1656 delete Node.prototype.querySelector; 3015 delete Node.prototype.querySelector;
1657 delete Node.prototype.querySelectorAll; 3016 delete Node.prototype.querySelectorAll;
1658 Node.prototype = mixin(Object.create(EventTarget.prototype), Node.prototype); 3017 Node.prototype = mixin(Object.create(EventTarget.prototype), Node.prototype);
1659 3018
1660 scope.wrappers.Node = Node; 3019 scope.wrappers.Node = Node;
(...skipping 190 matching lines...) Expand 10 before | Expand all | Expand 10 after
1851 var GetElementsByInterface = scope.GetElementsByInterface; 3210 var GetElementsByInterface = scope.GetElementsByInterface;
1852 var Node = scope.wrappers.Node; 3211 var Node = scope.wrappers.Node;
1853 var ParentNodeInterface = scope.ParentNodeInterface; 3212 var ParentNodeInterface = scope.ParentNodeInterface;
1854 var SelectorsInterface = scope.SelectorsInterface; 3213 var SelectorsInterface = scope.SelectorsInterface;
1855 var addWrapNodeListMethod = scope.addWrapNodeListMethod; 3214 var addWrapNodeListMethod = scope.addWrapNodeListMethod;
1856 var mixin = scope.mixin; 3215 var mixin = scope.mixin;
1857 var oneOf = scope.oneOf; 3216 var oneOf = scope.oneOf;
1858 var registerWrapper = scope.registerWrapper; 3217 var registerWrapper = scope.registerWrapper;
1859 var wrappers = scope.wrappers; 3218 var wrappers = scope.wrappers;
1860 3219
1861 var shadowRootTable = new SideTable(); 3220 var shadowRootTable = new WeakMap();
1862 var OriginalElement = window.Element; 3221 var OriginalElement = window.Element;
1863 3222
1864 3223
1865 var matchesName = oneOf(OriginalElement.prototype, [ 3224 var matchesName = oneOf(OriginalElement.prototype, [
1866 'matches', 3225 'matches',
1867 'mozMatchesSelector', 3226 'mozMatchesSelector',
1868 'msMatchesSelector', 3227 'msMatchesSelector',
1869 'webkitMatchesSelector', 3228 'webkitMatchesSelector',
1870 ]); 3229 ]);
1871 3230
(...skipping 345 matching lines...) Expand 10 before | Expand all | Expand 10 after
2217 'use strict'; 3576 'use strict';
2218 3577
2219 var HTMLElement = scope.wrappers.HTMLElement; 3578 var HTMLElement = scope.wrappers.HTMLElement;
2220 var getInnerHTML = scope.getInnerHTML; 3579 var getInnerHTML = scope.getInnerHTML;
2221 var mixin = scope.mixin; 3580 var mixin = scope.mixin;
2222 var registerWrapper = scope.registerWrapper; 3581 var registerWrapper = scope.registerWrapper;
2223 var setInnerHTML = scope.setInnerHTML; 3582 var setInnerHTML = scope.setInnerHTML;
2224 var unwrap = scope.unwrap; 3583 var unwrap = scope.unwrap;
2225 var wrap = scope.wrap; 3584 var wrap = scope.wrap;
2226 3585
2227 var contentTable = new SideTable(); 3586 var contentTable = new WeakMap();
2228 var templateContentsOwnerTable = new SideTable(); 3587 var templateContentsOwnerTable = new WeakMap();
2229 3588
2230 // http://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/templates/index.html# dfn-template-contents-owner 3589 // http://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/templates/index.html# dfn-template-contents-owner
2231 function getTemplateContentsOwner(doc) { 3590 function getTemplateContentsOwner(doc) {
2232 if (!doc.defaultView) 3591 if (!doc.defaultView)
2233 return doc; 3592 return doc;
2234 var d = templateContentsOwnerTable.get(doc); 3593 var d = templateContentsOwnerTable.get(doc);
2235 if (!d) { 3594 if (!d) {
2236 // TODO(arv): This should either be a Document or HTMLDocument depending 3595 // TODO(arv): This should either be a Document or HTMLDocument depending
2237 // on doc. 3596 // on doc.
2238 d = doc.implementation.createHTMLDocument(''); 3597 d = doc.implementation.createHTMLDocument('');
(...skipping 116 matching lines...) Expand 10 before | Expand all | Expand 10 after
2355 'use strict'; 3714 'use strict';
2356 3715
2357 var DocumentFragment = scope.wrappers.DocumentFragment; 3716 var DocumentFragment = scope.wrappers.DocumentFragment;
2358 var elementFromPoint = scope.elementFromPoint; 3717 var elementFromPoint = scope.elementFromPoint;
2359 var getInnerHTML = scope.getInnerHTML; 3718 var getInnerHTML = scope.getInnerHTML;
2360 var mixin = scope.mixin; 3719 var mixin = scope.mixin;
2361 var rewrap = scope.rewrap; 3720 var rewrap = scope.rewrap;
2362 var setInnerHTML = scope.setInnerHTML; 3721 var setInnerHTML = scope.setInnerHTML;
2363 var unwrap = scope.unwrap; 3722 var unwrap = scope.unwrap;
2364 3723
2365 var shadowHostTable = new SideTable(); 3724 var shadowHostTable = new WeakMap();
2366 var nextOlderShadowTreeTable = new SideTable(); 3725 var nextOlderShadowTreeTable = new WeakMap();
2367 3726
2368 function ShadowRoot(hostWrapper) { 3727 function ShadowRoot(hostWrapper) {
2369 var node = unwrap(hostWrapper.impl.ownerDocument.createDocumentFragment()); 3728 var node = unwrap(hostWrapper.impl.ownerDocument.createDocumentFragment());
2370 DocumentFragment.call(this, node); 3729 DocumentFragment.call(this, node);
2371 3730
2372 // createDocumentFragment associates the node with a wrapper 3731 // createDocumentFragment associates the node with a wrapper
2373 // DocumentFragment instance. Override that. 3732 // DocumentFragment instance. Override that.
2374 rewrap(node, this); 3733 rewrap(node, this);
2375 3734
2376 var oldShadowRoot = hostWrapper.shadowRoot; 3735 var oldShadowRoot = hostWrapper.shadowRoot;
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
2410 return shadowHostTable.get(node); 3769 return shadowHostTable.get(node);
2411 }; 3770 };
2412 })(this.ShadowDOMPolyfill); 3771 })(this.ShadowDOMPolyfill);
2413 // Copyright 2013 The Polymer Authors. All rights reserved. 3772 // Copyright 2013 The Polymer Authors. All rights reserved.
2414 // Use of this source code is governed by a BSD-style 3773 // Use of this source code is governed by a BSD-style
2415 // license that can be found in the LICENSE file. 3774 // license that can be found in the LICENSE file.
2416 3775
2417 (function(scope) { 3776 (function(scope) {
2418 'use strict'; 3777 'use strict';
2419 3778
3779 var Element = scope.wrappers.Element;
2420 var HTMLContentElement = scope.wrappers.HTMLContentElement; 3780 var HTMLContentElement = scope.wrappers.HTMLContentElement;
2421 var HTMLShadowElement = scope.wrappers.HTMLShadowElement; 3781 var HTMLShadowElement = scope.wrappers.HTMLShadowElement;
2422 var Node = scope.wrappers.Node; 3782 var Node = scope.wrappers.Node;
2423 var ShadowRoot = scope.wrappers.ShadowRoot; 3783 var ShadowRoot = scope.wrappers.ShadowRoot;
2424 var assert = scope.assert; 3784 var assert = scope.assert;
2425 var getHostForShadowRoot = scope.getHostForShadowRoot; 3785 var getHostForShadowRoot = scope.getHostForShadowRoot;
2426 var mixin = scope.mixin; 3786 var mixin = scope.mixin;
2427 var oneOf = scope.oneOf; 3787 var oneOf = scope.oneOf;
2428 var unwrap = scope.unwrap; 3788 var unwrap = scope.unwrap;
2429 var wrap = scope.wrap; 3789 var wrap = scope.wrap;
(...skipping 23 matching lines...) Expand all
2453 function updateAllChildNodes(parentNodeWrapper) { 3813 function updateAllChildNodes(parentNodeWrapper) {
2454 assert(parentNodeWrapper instanceof Node); 3814 assert(parentNodeWrapper instanceof Node);
2455 for (var childWrapper = parentNodeWrapper.firstChild; 3815 for (var childWrapper = parentNodeWrapper.firstChild;
2456 childWrapper; 3816 childWrapper;
2457 childWrapper = childWrapper.nextSibling) { 3817 childWrapper = childWrapper.nextSibling) {
2458 updateWrapperUpAndSideways(childWrapper); 3818 updateWrapperUpAndSideways(childWrapper);
2459 } 3819 }
2460 updateWrapperDown(parentNodeWrapper); 3820 updateWrapperDown(parentNodeWrapper);
2461 } 3821 }
2462 3822
2463 // This object groups DOM operations. This is supposed to be the DOM as the 3823 function insertBefore(parentNodeWrapper, newChildWrapper, refChildWrapper) {
2464 // browser/render tree sees it.
2465 // When changes are done to the visual DOM the logical DOM needs to be updated
2466 // to reflect the correct tree.
2467 function removeAllChildNodes(parentNodeWrapper) {
2468 var parentNode = unwrap(parentNodeWrapper); 3824 var parentNode = unwrap(parentNodeWrapper);
2469 updateAllChildNodes(parentNodeWrapper); 3825 var newChild = unwrap(newChildWrapper);
2470 if (parentNode.firstChild) 3826 var refChild = refChildWrapper ? unwrap(refChildWrapper) : null;
2471 parentNode.textContent = '';
2472 }
2473 3827
2474 function appendChild(parentNodeWrapper, childWrapper) { 3828 remove(newChildWrapper);
2475 var parentNode = unwrap(parentNodeWrapper); 3829 updateWrapperUpAndSideways(newChildWrapper);
2476 var child = unwrap(childWrapper);
2477 if (child.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
2478 updateAllChildNodes(childWrapper);
2479 3830
3831 if (!refChildWrapper) {
3832 parentNodeWrapper.lastChild_ = parentNodeWrapper.lastChild;
3833 if (parentNodeWrapper.lastChild === parentNodeWrapper.firstChild)
3834 parentNodeWrapper.firstChild_ = parentNodeWrapper.firstChild;
3835
3836 var lastChildWrapper = wrap(parentNode.lastChild);
3837 if (lastChildWrapper)
3838 lastChildWrapper.nextSibling_ = lastChildWrapper.nextSibling;
2480 } else { 3839 } else {
2481 remove(childWrapper); 3840 if (parentNodeWrapper.firstChild === refChildWrapper)
2482 updateWrapperUpAndSideways(childWrapper); 3841 parentNodeWrapper.firstChild_ = refChildWrapper;
3842
3843 refChildWrapper.previousSibling_ = refChildWrapper.previousSibling;
2483 } 3844 }
2484 3845
2485 parentNodeWrapper.lastChild_ = parentNodeWrapper.lastChild; 3846 parentNode.insertBefore(newChild, refChild);
2486 if (parentNodeWrapper.lastChild === parentNodeWrapper.firstChild)
2487 parentNodeWrapper.firstChild_ = parentNodeWrapper.firstChild;
2488
2489 var lastChildWrapper = wrap(parentNode.lastChild);
2490 if (lastChildWrapper) {
2491 lastChildWrapper.nextSibling_ = lastChildWrapper.nextSibling;
2492 }
2493
2494 parentNode.appendChild(child);
2495 }
2496
2497 function removeChild(parentNodeWrapper, childWrapper) {
2498 var parentNode = unwrap(parentNodeWrapper);
2499 var child = unwrap(childWrapper);
2500
2501 updateWrapperUpAndSideways(childWrapper);
2502
2503 if (childWrapper.previousSibling)
2504 childWrapper.previousSibling.nextSibling_ = childWrapper;
2505 if (childWrapper.nextSibling)
2506 childWrapper.nextSibling.previousSibling_ = childWrapper;
2507
2508 if (parentNodeWrapper.lastChild === childWrapper)
2509 parentNodeWrapper.lastChild_ = childWrapper;
2510 if (parentNodeWrapper.firstChild === childWrapper)
2511 parentNodeWrapper.firstChild_ = childWrapper;
2512
2513 parentNode.removeChild(child);
2514 } 3847 }
2515 3848
2516 function remove(nodeWrapper) { 3849 function remove(nodeWrapper) {
2517 var node = unwrap(nodeWrapper) 3850 var node = unwrap(nodeWrapper)
2518 var parentNode = node.parentNode; 3851 var parentNode = node.parentNode;
2519 if (parentNode) 3852 if (!parentNode)
2520 removeChild(wrap(parentNode), nodeWrapper); 3853 return;
3854
3855 var parentNodeWrapper = wrap(parentNode);
3856 updateWrapperUpAndSideways(nodeWrapper);
3857
3858 if (nodeWrapper.previousSibling)
3859 nodeWrapper.previousSibling.nextSibling_ = nodeWrapper;
3860 if (nodeWrapper.nextSibling)
3861 nodeWrapper.nextSibling.previousSibling_ = nodeWrapper;
3862
3863 if (parentNodeWrapper.lastChild === nodeWrapper)
3864 parentNodeWrapper.lastChild_ = nodeWrapper;
3865 if (parentNodeWrapper.firstChild === nodeWrapper)
3866 parentNodeWrapper.firstChild_ = nodeWrapper;
3867
3868 parentNode.removeChild(node);
2521 } 3869 }
2522 3870
2523 var distributedChildNodesTable = new SideTable(); 3871 var distributedChildNodesTable = new WeakMap();
2524 var eventParentsTable = new SideTable(); 3872 var eventParentsTable = new WeakMap();
2525 var insertionParentTable = new SideTable(); 3873 var insertionParentTable = new WeakMap();
2526 var rendererForHostTable = new SideTable(); 3874 var rendererForHostTable = new WeakMap();
2527 var shadowDOMRendererTable = new SideTable(); 3875 var shadowDOMRendererTable = new WeakMap();
2528
2529 var reprCounter = 0;
2530
2531 function repr(node) {
2532 if (!node.displayName)
2533 node.displayName = node.nodeName + '-' + ++reprCounter;
2534 return node.displayName;
2535 }
2536 3876
2537 function distributeChildToInsertionPoint(child, insertionPoint) { 3877 function distributeChildToInsertionPoint(child, insertionPoint) {
2538 getDistributedChildNodes(insertionPoint).push(child); 3878 getDistributedChildNodes(insertionPoint).push(child);
2539 assignToInsertionPoint(child, insertionPoint); 3879 assignToInsertionPoint(child, insertionPoint);
2540 3880
2541 var eventParents = eventParentsTable.get(child); 3881 var eventParents = eventParentsTable.get(child);
2542 if (!eventParents) 3882 if (!eventParents)
2543 eventParentsTable.set(child, eventParents = []); 3883 eventParentsTable.set(child, eventParents = []);
2544 eventParents.push(insertionPoint); 3884 eventParents.push(insertionPoint);
2545 } 3885 }
(...skipping 16 matching lines...) Expand all
2562 3902
2563 /** 3903 /**
2564 * Visits all nodes in the tree that fulfils the |predicate|. If the |visitor| 3904 * Visits all nodes in the tree that fulfils the |predicate|. If the |visitor|
2565 * function returns |false| the traversal is aborted. 3905 * function returns |false| the traversal is aborted.
2566 * @param {!Node} tree 3906 * @param {!Node} tree
2567 * @param {function(!Node) : boolean} predicate 3907 * @param {function(!Node) : boolean} predicate
2568 * @param {function(!Node) : *} visitor 3908 * @param {function(!Node) : *} visitor
2569 */ 3909 */
2570 function visit(tree, predicate, visitor) { 3910 function visit(tree, predicate, visitor) {
2571 // This operates on logical DOM. 3911 // This operates on logical DOM.
2572 var nodes = getChildNodesSnapshot(tree); 3912 for (var node = tree.firstChild; node; node = node.nextSibling) {
2573 for (var i = 0; i < nodes.length; i++) {
2574 var node = nodes[i];
2575 if (predicate(node)) { 3913 if (predicate(node)) {
2576 if (visitor(node) === false) 3914 if (visitor(node) === false)
2577 return; 3915 return;
2578 } else { 3916 } else {
2579 visit(node, predicate, visitor); 3917 visit(node, predicate, visitor);
2580 } 3918 }
2581 } 3919 }
2582 } 3920 }
2583 3921
2584 // Matching Insertion Points 3922 // Matching Insertion Points
(...skipping 30 matching lines...) Expand all
2615 function matchesCriteria(node, point) { 3953 function matchesCriteria(node, point) {
2616 var select = point.getAttribute('select'); 3954 var select = point.getAttribute('select');
2617 if (!select) 3955 if (!select)
2618 return true; 3956 return true;
2619 3957
2620 // Here we know the select attribute is a non empty string. 3958 // Here we know the select attribute is a non empty string.
2621 select = select.trim(); 3959 select = select.trim();
2622 if (!select) 3960 if (!select)
2623 return true; 3961 return true;
2624 3962
2625 if (node.nodeType !== Node.ELEMENT_NODE) 3963 if (!(node instanceof Element))
2626 return false; 3964 return false;
2627 3965
2628 // TODO(arv): This does not seem right. Need to check for a simple selector. 3966 // TODO(arv): This does not seem right. Need to check for a simple selector.
2629 if (!selectorMatchRegExp.test(select)) 3967 if (!selectorMatchRegExp.test(select))
2630 return false; 3968 return false;
2631 3969
2632 if (select[0] === ':' &&!allowedPseudoRegExp.test(select)) 3970 if (select[0] === ':' && !allowedPseudoRegExp.test(select))
2633 return false; 3971 return false;
2634 3972
2635 try { 3973 try {
2636 return node.matches(select); 3974 return node.matches(select);
2637 } catch (ex) { 3975 } catch (ex) {
2638 // Invalid selector. 3976 // Invalid selector.
2639 return false; 3977 return false;
2640 } 3978 }
2641 } 3979 }
2642 3980
2643 var request = oneOf(window, [ 3981 var request = oneOf(window, [
2644 'requestAnimationFrame', 3982 'requestAnimationFrame',
2645 'mozRequestAnimationFrame', 3983 'mozRequestAnimationFrame',
2646 'webkitRequestAnimationFrame', 3984 'webkitRequestAnimationFrame',
2647 'setTimeout' 3985 'setTimeout'
2648 ]); 3986 ]);
2649 3987
2650 var pendingDirtyRenderers = []; 3988 var pendingDirtyRenderers = [];
2651 var renderTimer; 3989 var renderTimer;
2652 3990
2653 function renderAllPending() { 3991 function renderAllPending() {
2654 renderTimer = null; 3992 for (var i = 0; i < pendingDirtyRenderers.length; i++) {
2655 pendingDirtyRenderers.forEach(function(owner) { 3993 pendingDirtyRenderers[i].render();
2656 owner.render(); 3994 }
2657 });
2658 pendingDirtyRenderers = []; 3995 pendingDirtyRenderers = [];
2659 } 3996 }
2660 3997
2661 function ShadowRenderer(host) { 3998 function handleRequestAnimationFrame() {
2662 this.host = host; 3999 renderTimer = null;
2663 this.dirty = false; 4000 renderAllPending();
2664 this.invalidateAttributes();
2665 this.associateNode(host);
2666 } 4001 }
2667 4002
2668 /** 4003 /**
2669 * Returns existing shadow renderer for a host or creates it if it is needed. 4004 * Returns existing shadow renderer for a host or creates it if it is needed.
2670 * @params {!Element} host 4005 * @params {!Element} host
2671 * @return {!ShadowRenderer} 4006 * @return {!ShadowRenderer}
2672 */ 4007 */
2673 function getRendererForHost(host) { 4008 function getRendererForHost(host) {
2674 var renderer = rendererForHostTable.get(host); 4009 var renderer = rendererForHostTable.get(host);
2675 if (!renderer) { 4010 if (!renderer) {
2676 renderer = new ShadowRenderer(host); 4011 renderer = new ShadowRenderer(host);
2677 rendererForHostTable.set(host, renderer); 4012 rendererForHostTable.set(host, renderer);
2678 } 4013 }
2679 return renderer; 4014 return renderer;
2680 } 4015 }
2681 4016
2682 function getShadowRootAncestor(node) { 4017 function getShadowRootAncestor(node) {
2683 for (; node; node = node.parentNode) { 4018 for (; node; node = node.parentNode) {
2684 if (node instanceof ShadowRoot) 4019 if (node instanceof ShadowRoot)
2685 return node; 4020 return node;
2686 } 4021 }
2687 return null; 4022 return null;
2688 } 4023 }
2689 4024
2690 function getRendererForShadowRoot(shadowRoot) { 4025 function getRendererForShadowRoot(shadowRoot) {
2691 return getRendererForHost(getHostForShadowRoot(shadowRoot)); 4026 return getRendererForHost(getHostForShadowRoot(shadowRoot));
2692 } 4027 }
2693 4028
4029 var spliceDiff = new ArraySplice();
4030 spliceDiff.equals = function(renderNode, rawNode) {
4031 return unwrap(renderNode.node) === rawNode;
4032 };
4033
4034 /**
4035 * RenderNode is used as an in memory "render tree". When we render the
4036 * composed tree we create a tree of RenderNodes, then we diff this against
4037 * the real DOM tree and make minimal changes as needed.
4038 */
4039 function RenderNode(node) {
4040 this.skip = false;
4041 this.node = node;
4042 this.childNodes = [];
4043 }
4044
4045 RenderNode.prototype = {
4046 append: function(node) {
4047 var rv = new RenderNode(node);
4048 this.childNodes.push(rv);
4049 return rv;
4050 },
4051
4052 sync: function(opt_added) {
4053 if (this.skip)
4054 return;
4055
4056 var nodeWrapper = this.node;
4057 // plain array of RenderNodes
4058 var newChildren = this.childNodes;
4059 // plain array of real nodes.
4060 var oldChildren = getChildNodesSnapshot(unwrap(nodeWrapper));
4061 var added = opt_added || new WeakMap();
4062
4063 var splices = spliceDiff.calculateSplices(newChildren, oldChildren);
4064
4065 var newIndex = 0, oldIndex = 0;
4066 var lastIndex = 0;
4067 for (var i = 0; i < splices.length; i++) {
4068 var splice = splices[i];
4069 for (; lastIndex < splice.index; lastIndex++) {
4070 oldIndex++;
4071 newChildren[newIndex++].sync(added);
4072 }
4073
4074 var removedCount = splice.removed.length;
4075 for (var j = 0; j < removedCount; j++) {
4076 var wrapper = wrap(oldChildren[oldIndex++]);
4077 if (!added.get(wrapper))
4078 remove(wrapper);
4079 }
4080
4081 var addedCount = splice.addedCount;
4082 var refNode = oldChildren[oldIndex] && wrap(oldChildren[oldIndex]);
4083 for (var j = 0; j < addedCount; j++) {
4084 var newChildRenderNode = newChildren[newIndex++];
4085 var newChildWrapper = newChildRenderNode.node;
4086 insertBefore(nodeWrapper, newChildWrapper, refNode);
4087
4088 // Keep track of added so that we do not remove the node after it
4089 // has been added.
4090 added.set(newChildWrapper, true);
4091
4092 newChildRenderNode.sync(added);
4093 }
4094
4095 lastIndex += addedCount;
4096 }
4097
4098 for (var i = lastIndex; i < newChildren.length; i++) {
4099 newChildren[i++].sync(added);
4100 }
4101 }
4102 };
4103
4104 function ShadowRenderer(host) {
4105 this.host = host;
4106 this.dirty = false;
4107 this.invalidateAttributes();
4108 this.associateNode(host);
4109 }
4110
2694 ShadowRenderer.prototype = { 4111 ShadowRenderer.prototype = {
2695 4112
2696 // http://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/shadow/index.html#r endering-shadow-trees 4113 // http://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/shadow/index.html#r endering-shadow-trees
2697 render: function() { 4114 render: function(opt_renderNode) {
2698 if (!this.dirty) 4115 if (!this.dirty)
2699 return; 4116 return;
2700 4117
2701 this.invalidateAttributes(); 4118 this.invalidateAttributes();
2702 this.treeComposition(); 4119 this.treeComposition();
2703 4120
2704 var host = this.host; 4121 var host = this.host;
2705 var shadowDOM = host.shadowRoot; 4122 var shadowRoot = host.shadowRoot;
2706 4123
2707 this.removeAllChildNodes(this.host); 4124 this.associateNode(host);
4125 var topMostRenderer = !renderNode;
4126 var renderNode = opt_renderNode || new RenderNode(host);
2708 4127
2709 var shadowDOMChildNodes = getChildNodesSnapshot(shadowDOM); 4128 for (var node = shadowRoot.firstChild; node; node = node.nextSibling) {
2710 shadowDOMChildNodes.forEach(function(node) { 4129 this.renderNode(shadowRoot, renderNode, node, false);
2711 this.renderNode(host, shadowDOM, node, false); 4130 }
2712 }, this); 4131
4132 if (topMostRenderer)
4133 renderNode.sync();
2713 4134
2714 this.dirty = false; 4135 this.dirty = false;
2715 }, 4136 },
2716 4137
2717 invalidate: function() { 4138 invalidate: function() {
2718 if (!this.dirty) { 4139 if (!this.dirty) {
2719 this.dirty = true; 4140 this.dirty = true;
2720 pendingDirtyRenderers.push(this); 4141 pendingDirtyRenderers.push(this);
2721 if (renderTimer) 4142 if (renderTimer)
2722 return; 4143 return;
2723 renderTimer = window[request](renderAllPending, 0); 4144 renderTimer = window[request](handleRequestAnimationFrame, 0);
2724 } 4145 }
2725 }, 4146 },
2726 4147
2727 renderNode: function(visualParent, tree, node, isNested) { 4148 renderNode: function(shadowRoot, renderNode, node, isNested) {
2728 if (isShadowHost(node)) { 4149 if (isShadowHost(node)) {
2729 this.appendChild(visualParent, node); 4150 renderNode = renderNode.append(node);
2730 var renderer = getRendererForHost(node); 4151 var renderer = getRendererForHost(node);
2731 renderer.dirty = true; // Need to rerender due to reprojection. 4152 renderer.dirty = true; // Need to rerender due to reprojection.
2732 renderer.render(); 4153 renderer.render(renderNode);
2733 } else if (isInsertionPoint(node)) { 4154 } else if (isInsertionPoint(node)) {
2734 this.renderInsertionPoint(visualParent, tree, node, isNested); 4155 this.renderInsertionPoint(shadowRoot, renderNode, node, isNested);
2735 } else if (isShadowInsertionPoint(node)) { 4156 } else if (isShadowInsertionPoint(node)) {
2736 this.renderShadowInsertionPoint(visualParent, tree, node); 4157 this.renderShadowInsertionPoint(shadowRoot, renderNode, node);
2737 } else { 4158 } else {
2738 this.renderAsAnyDomTree(visualParent, tree, node, isNested); 4159 this.renderAsAnyDomTree(shadowRoot, renderNode, node, isNested);
2739 } 4160 }
2740 }, 4161 },
2741 4162
2742 renderAsAnyDomTree: function(visualParent, tree, node, isNested) { 4163 renderAsAnyDomTree: function(shadowRoot, renderNode, node, isNested) {
2743 this.appendChild(visualParent, node); 4164 renderNode = renderNode.append(node);
2744 4165
2745 if (isShadowHost(node)) { 4166 if (isShadowHost(node)) {
2746 render(node); 4167 var renderer = getRendererForHost(node);
4168 renderNode.skip = !renderer.dirty;
4169 renderer.render(renderNode);
2747 } else { 4170 } else {
2748 var parent = node; 4171 for (var child = node.firstChild; child; child = child.nextSibling) {
2749 var logicalChildNodes = getChildNodesSnapshot(parent); 4172 this.renderNode(shadowRoot, renderNode, child, isNested);
2750 // We associate the parent of a content/shadow with the renderer 4173 }
2751 // because we may need to remove stale childNodes.
2752 if (shadowDOMRendererTable.get(parent))
2753 this.removeAllChildNodes(parent);
2754 logicalChildNodes.forEach(function(node) {
2755 this.renderNode(parent, tree, node, isNested);
2756 }, this);
2757 } 4174 }
2758 }, 4175 },
2759 4176
2760 renderInsertionPoint: function(visualParent, tree, insertionPoint, isNested) { 4177 renderInsertionPoint: function(shadowRoot, renderNode, insertionPoint,
4178 isNested) {
2761 var distributedChildNodes = getDistributedChildNodes(insertionPoint); 4179 var distributedChildNodes = getDistributedChildNodes(insertionPoint);
2762 if (distributedChildNodes.length) { 4180 if (distributedChildNodes.length) {
2763 this.removeAllChildNodes(insertionPoint); 4181 this.associateNode(insertionPoint);
2764 4182
2765 distributedChildNodes.forEach(function(child) { 4183 for (var i = 0; i < distributedChildNodes.length; i++) {
4184 var child = distributedChildNodes[i];
2766 if (isInsertionPoint(child) && isNested) 4185 if (isInsertionPoint(child) && isNested)
2767 this.renderInsertionPoint(visualParent, tree, child, isNested); 4186 this.renderInsertionPoint(shadowRoot, renderNode, child, isNested);
2768 else 4187 else
2769 this.renderAsAnyDomTree(visualParent, tree, child, isNested); 4188 this.renderAsAnyDomTree(shadowRoot, renderNode, child, isNested);
2770 }, this); 4189 }
2771 } else { 4190 } else {
2772 this.renderFallbackContent(visualParent, insertionPoint); 4191 this.renderFallbackContent(shadowRoot, renderNode, insertionPoint);
2773 } 4192 }
2774 this.remove(insertionPoint); 4193 this.associateNode(insertionPoint.parentNode);
2775 }, 4194 },
2776 4195
2777 renderShadowInsertionPoint: function(visualParent, tree, shadowInsertionPoin t) { 4196 renderShadowInsertionPoint: function(shadowRoot, renderNode,
2778 var nextOlderTree = tree.olderShadowRoot; 4197 shadowInsertionPoint) {
4198 var nextOlderTree = shadowRoot.olderShadowRoot;
2779 if (nextOlderTree) { 4199 if (nextOlderTree) {
2780 assignToInsertionPoint(nextOlderTree, shadowInsertionPoint); 4200 assignToInsertionPoint(nextOlderTree, shadowInsertionPoint);
2781 this.remove(shadowInsertionPoint); 4201 this.associateNode(shadowInsertionPoint.parentNode);
2782 var shadowDOMChildNodes = getChildNodesSnapshot(nextOlderTree); 4202 for (var node = nextOlderTree.firstChild;
2783 shadowDOMChildNodes.forEach(function(node) { 4203 node;
2784 this.renderNode(visualParent, nextOlderTree, node, true); 4204 node = node.nextSibling) {
2785 }, this); 4205 this.renderNode(nextOlderTree, renderNode, node, true);
4206 }
2786 } else { 4207 } else {
2787 this.renderFallbackContent(visualParent, shadowInsertionPoint); 4208 this.renderFallbackContent(shadowRoot, renderNode,
4209 shadowInsertionPoint);
2788 } 4210 }
2789 }, 4211 },
2790 4212
2791 renderFallbackContent: function (visualParent, fallbackHost) { 4213 renderFallbackContent: function(shadowRoot, renderNode, fallbackHost) {
2792 var logicalChildNodes = getChildNodesSnapshot(fallbackHost);
2793 this.associateNode(fallbackHost); 4214 this.associateNode(fallbackHost);
2794 this.remove(fallbackHost); 4215 this.associateNode(fallbackHost.parentNode);
2795 logicalChildNodes.forEach(function(node) { 4216 for (var node = fallbackHost.firstChild; node; node = node.nextSibling) {
2796 this.appendChild(visualParent, node); 4217 this.renderAsAnyDomTree(shadowRoot, renderNode, node, false);
2797 }, this); 4218 }
2798 }, 4219 },
2799 4220
2800 /** 4221 /**
2801 * Invalidates the attributes used to keep track of which attributes may 4222 * Invalidates the attributes used to keep track of which attributes may
2802 * cause the renderer to be invalidated. 4223 * cause the renderer to be invalidated.
2803 */ 4224 */
2804 invalidateAttributes: function() { 4225 invalidateAttributes: function() {
2805 this.attributes = Object.create(null); 4226 this.attributes = Object.create(null);
2806 }, 4227 },
2807 4228
(...skipping 22 matching lines...) Expand all
2830 4251
2831 // Pseudo selectors have been removed from the spec. 4252 // Pseudo selectors have been removed from the spec.
2832 }, 4253 },
2833 4254
2834 dependsOnAttribute: function(name) { 4255 dependsOnAttribute: function(name) {
2835 return this.attributes[name]; 4256 return this.attributes[name];
2836 }, 4257 },
2837 4258
2838 // http://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/shadow/index.html#d fn-distribution-algorithm 4259 // http://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/shadow/index.html#d fn-distribution-algorithm
2839 distribute: function(tree, pool) { 4260 distribute: function(tree, pool) {
2840 var anyRemoved = false;
2841 var self = this; 4261 var self = this;
2842 4262
2843 visit(tree, isActiveInsertionPoint, 4263 visit(tree, isActiveInsertionPoint,
2844 function(insertionPoint) { 4264 function(insertionPoint) {
2845 resetDistributedChildNodes(insertionPoint); 4265 resetDistributedChildNodes(insertionPoint);
2846 self.updateDependentAttributes( 4266 self.updateDependentAttributes(
2847 insertionPoint.getAttribute('select')); 4267 insertionPoint.getAttribute('select'));
2848 4268
2849 for (var i = 0; i < pool.length; i++) { // 1.2 4269 for (var i = 0; i < pool.length; i++) { // 1.2
2850 var node = pool[i]; // 1.2.1 4270 var node = pool[i]; // 1.2.1
2851 if (node === undefined) // removed 4271 if (node === undefined) // removed
2852 continue; 4272 continue;
2853 if (matchesCriteria(node, insertionPoint)) { // 1.2.2 4273 if (matchesCriteria(node, insertionPoint)) { // 1.2.2
2854 distributeChildToInsertionPoint(node, insertionPoint); // 1.2.2 .1 4274 distributeChildToInsertionPoint(node, insertionPoint); // 1.2.2 .1
2855 pool[i] = undefined; // 1.2.2.2 4275 pool[i] = undefined; // 1.2.2.2
2856 anyRemoved = true;
2857 } 4276 }
2858 } 4277 }
2859 }); 4278 });
2860
2861 if (!anyRemoved)
2862 return pool;
2863
2864 return pool.filter(function(item) {
2865 return item !== undefined;
2866 });
2867 }, 4279 },
2868 4280
2869 // http://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/shadow/index.html#d fn-tree-composition 4281 // http://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/shadow/index.html#d fn-tree-composition
2870 treeComposition: function () { 4282 treeComposition: function () {
2871 var shadowHost = this.host; 4283 var shadowHost = this.host;
2872 var tree = shadowHost.shadowRoot; // 1. 4284 var tree = shadowHost.shadowRoot; // 1.
2873 var pool = []; // 2. 4285 var pool = []; // 2.
2874 var shadowHostChildNodes = getChildNodesSnapshot(shadowHost); 4286
2875 shadowHostChildNodes.forEach(function(child) { // 3. 4287 for (var child = shadowHost.firstChild;
4288 child;
4289 child = child.nextSibling) { // 3.
2876 if (isInsertionPoint(child)) { // 3.2. 4290 if (isInsertionPoint(child)) { // 3.2.
2877 var reprojected = getDistributedChildNodes(child); // 3.2.1. 4291 var reprojected = getDistributedChildNodes(child); // 3.2.1.
2878 // if reprojected is undef... reset it? 4292 // if reprojected is undef... reset it?
2879 if (!reprojected || !reprojected.length) // 3.2.2. 4293 if (!reprojected || !reprojected.length) // 3.2.2.
2880 reprojected = getChildNodesSnapshot(child); 4294 reprojected = getChildNodesSnapshot(child);
2881 pool.push.apply(pool, reprojected); // 3.2.3. 4295 pool.push.apply(pool, reprojected); // 3.2.3.
2882 } else { 4296 } else {
2883 pool.push(child); // 3.3. 4297 pool.push(child); // 3.3.
2884 } 4298 }
2885 }); 4299 }
2886 4300
2887 var shadowInsertionPoint, point; 4301 var shadowInsertionPoint, point;
2888 while (tree) { // 4. 4302 while (tree) { // 4.
2889 // 4.1. 4303 // 4.1.
2890 shadowInsertionPoint = undefined; // Reset every iteration. 4304 shadowInsertionPoint = undefined; // Reset every iteration.
2891 visit(tree, isActiveShadowInsertionPoint, function(point) { 4305 visit(tree, isActiveShadowInsertionPoint, function(point) {
2892 shadowInsertionPoint = point; 4306 shadowInsertionPoint = point;
2893 return false; 4307 return false;
2894 }); 4308 });
2895 point = shadowInsertionPoint; 4309 point = shadowInsertionPoint;
2896 4310
2897 pool = this.distribute(tree, pool); // 4.2. 4311 this.distribute(tree, pool); // 4.2.
2898 if (point) { // 4.3. 4312 if (point) { // 4.3.
2899 var nextOlderTree = tree.olderShadowRoot; // 4.3.1. 4313 var nextOlderTree = tree.olderShadowRoot; // 4.3.1.
2900 if (!nextOlderTree) { 4314 if (!nextOlderTree) {
2901 break; // 4.3.1.1. 4315 break; // 4.3.1.1.
2902 } else { 4316 } else {
2903 tree = nextOlderTree; // 4.3.2.2. 4317 tree = nextOlderTree; // 4.3.2.2.
2904 assignToInsertionPoint(tree, point); // 4.3.2.2. 4318 assignToInsertionPoint(tree, point); // 4.3.2.2.
2905 continue; // 4.3.2.3. 4319 continue; // 4.3.2.3.
2906 } 4320 }
2907 } else { 4321 } else {
2908 break; // 4.4. 4322 break; // 4.4.
2909 } 4323 }
2910 } 4324 }
2911 }, 4325 },
2912 4326
2913 appendChild: function(parent, child) {
2914 // this.associateNode(child);
2915 this.associateNode(parent);
2916 appendChild(parent, child);
2917 },
2918
2919 remove: function(node) {
2920 // this.associateNode(node);
2921 this.associateNode(node.parentNode);
2922 remove(node);
2923 },
2924
2925 removeAllChildNodes: function(parent) {
2926 this.associateNode(parent);
2927 removeAllChildNodes(parent);
2928 },
2929
2930 associateNode: function(node) { 4327 associateNode: function(node) {
2931 shadowDOMRendererTable.set(node, this); 4328 shadowDOMRendererTable.set(node, this);
2932 } 4329 }
2933 }; 4330 };
2934 4331
2935 function isInsertionPoint(node) { 4332 function isInsertionPoint(node) {
2936 // Should this include <shadow>? 4333 // Should this include <shadow>?
2937 return node.localName === 'content'; 4334 return node instanceof HTMLContentElement;
2938 } 4335 }
2939 4336
2940 function isActiveInsertionPoint(node) { 4337 function isActiveInsertionPoint(node) {
2941 // <content> inside another <content> or <shadow> is considered inactive. 4338 // <content> inside another <content> or <shadow> is considered inactive.
2942 return node.localName === 'content'; 4339 return node instanceof HTMLContentElement;
2943 } 4340 }
2944 4341
2945 function isShadowInsertionPoint(node) { 4342 function isShadowInsertionPoint(node) {
2946 return node.localName === 'shadow'; 4343 return node instanceof HTMLShadowElement;
2947 } 4344 }
2948 4345
2949 function isActiveShadowInsertionPoint(node) { 4346 function isActiveShadowInsertionPoint(node) {
2950 // <shadow> inside another <content> or <shadow> is considered inactive. 4347 // <shadow> inside another <content> or <shadow> is considered inactive.
2951 return node.localName === 'shadow'; 4348 return node instanceof HTMLShadowElement;
2952 } 4349 }
2953 4350
2954 function isShadowHost(shadowHost) { 4351 function isShadowHost(shadowHost) {
2955 return shadowHost.shadowRoot; 4352 return shadowHost.shadowRoot;
2956 } 4353 }
2957 4354
2958 function getShadowTrees(host) { 4355 function getShadowTrees(host) {
2959 var trees = []; 4356 var trees = [];
2960 4357
2961 for (var tree = host.shadowRoot; tree; tree = tree.olderShadowRoot) { 4358 for (var tree = host.shadowRoot; tree; tree = tree.olderShadowRoot) {
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after
3018 }; 4415 };
3019 4416
3020 scope.eventParentsTable = eventParentsTable; 4417 scope.eventParentsTable = eventParentsTable;
3021 scope.getRendererForHost = getRendererForHost; 4418 scope.getRendererForHost = getRendererForHost;
3022 scope.getShadowTrees = getShadowTrees; 4419 scope.getShadowTrees = getShadowTrees;
3023 scope.insertionParentTable = insertionParentTable; 4420 scope.insertionParentTable = insertionParentTable;
3024 scope.renderAllPending = renderAllPending; 4421 scope.renderAllPending = renderAllPending;
3025 4422
3026 // Exposed for testing 4423 // Exposed for testing
3027 scope.visual = { 4424 scope.visual = {
3028 removeAllChildNodes: removeAllChildNodes, 4425 insertBefore: insertBefore,
3029 appendChild: appendChild, 4426 remove: remove,
3030 removeChild: removeChild
3031 }; 4427 };
3032 4428
3033 })(this.ShadowDOMPolyfill); 4429 })(this.ShadowDOMPolyfill);
3034 // Copyright 2013 The Polymer Authors. All rights reserved. 4430 // Copyright 2013 The Polymer Authors. All rights reserved.
3035 // Use of this source code is goverened by a BSD-style 4431 // Use of this source code is goverened by a BSD-style
3036 // license that can be found in the LICENSE file. 4432 // license that can be found in the LICENSE file.
3037 4433
3038 (function(scope) { 4434 (function(scope) {
3039 'use strict'; 4435 'use strict';
3040 4436
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after
3102 var elementFromPoint = scope.elementFromPoint; 4498 var elementFromPoint = scope.elementFromPoint;
3103 var forwardMethodsToWrapper = scope.forwardMethodsToWrapper; 4499 var forwardMethodsToWrapper = scope.forwardMethodsToWrapper;
3104 var matchesName = scope.matchesName; 4500 var matchesName = scope.matchesName;
3105 var mixin = scope.mixin; 4501 var mixin = scope.mixin;
3106 var registerWrapper = scope.registerWrapper; 4502 var registerWrapper = scope.registerWrapper;
3107 var unwrap = scope.unwrap; 4503 var unwrap = scope.unwrap;
3108 var wrap = scope.wrap; 4504 var wrap = scope.wrap;
3109 var wrapEventTargetMethods = scope.wrapEventTargetMethods; 4505 var wrapEventTargetMethods = scope.wrapEventTargetMethods;
3110 var wrapNodeList = scope.wrapNodeList; 4506 var wrapNodeList = scope.wrapNodeList;
3111 4507
3112 var implementationTable = new SideTable(); 4508 var implementationTable = new WeakMap();
3113 4509
3114 function Document(node) { 4510 function Document(node) {
3115 Node.call(this, node); 4511 Node.call(this, node);
3116 } 4512 }
3117 Document.prototype = Object.create(Node.prototype); 4513 Document.prototype = Object.create(Node.prototype);
3118 4514
3119 defineWrapGetter(Document, 'documentElement'); 4515 defineWrapGetter(Document, 'documentElement');
3120 4516
3121 // Conceptually both body and head can be in a shadow but suporting that seems 4517 // Conceptually both body and head can be in a shadow but suporting that seems
3122 // overkill at this point. 4518 // overkill at this point.
(...skipping 254 matching lines...) Expand 10 before | Expand all | Expand 10 after
3377 4773
3378 (function(scope) { 4774 (function(scope) {
3379 'use strict'; 4775 'use strict';
3380 4776
3381 var EventTarget = scope.wrappers.EventTarget; 4777 var EventTarget = scope.wrappers.EventTarget;
3382 var mixin = scope.mixin; 4778 var mixin = scope.mixin;
3383 var registerWrapper = scope.registerWrapper; 4779 var registerWrapper = scope.registerWrapper;
3384 var unwrap = scope.unwrap; 4780 var unwrap = scope.unwrap;
3385 var unwrapIfNeeded = scope.unwrapIfNeeded; 4781 var unwrapIfNeeded = scope.unwrapIfNeeded;
3386 var wrap = scope.wrap; 4782 var wrap = scope.wrap;
4783 var renderAllPending = scope.renderAllPending;
3387 4784
3388 var OriginalWindow = window.Window; 4785 var OriginalWindow = window.Window;
3389 4786
3390 function Window(impl) { 4787 function Window(impl) {
3391 EventTarget.call(this, impl); 4788 EventTarget.call(this, impl);
3392 } 4789 }
3393 Window.prototype = Object.create(EventTarget.prototype); 4790 Window.prototype = Object.create(EventTarget.prototype);
3394 4791
3395 var originalGetComputedStyle = window.getComputedStyle; 4792 var originalGetComputedStyle = window.getComputedStyle;
3396 OriginalWindow.prototype.getComputedStyle = function(el, pseudo) { 4793 OriginalWindow.prototype.getComputedStyle = function(el, pseudo) {
4794 renderAllPending();
3397 return originalGetComputedStyle.call(this || window, unwrapIfNeeded(el), 4795 return originalGetComputedStyle.call(this || window, unwrapIfNeeded(el),
3398 pseudo); 4796 pseudo);
3399 }; 4797 };
3400 4798
3401 ['addEventListener', 'removeEventListener', 'dispatchEvent'].forEach( 4799 ['addEventListener', 'removeEventListener', 'dispatchEvent'].forEach(
3402 function(name) { 4800 function(name) {
3403 OriginalWindow.prototype[name] = function() { 4801 OriginalWindow.prototype[name] = function() {
3404 var w = wrap(this || window); 4802 var w = wrap(this || window);
3405 return w[name].apply(w, arguments); 4803 return w[name].apply(w, arguments);
3406 }; 4804 };
(...skipping 331 matching lines...) Expand 10 before | Expand all | Expand 10 after
3738 if (obj instanceof NodeList) return 'NodeList'; 5136 if (obj instanceof NodeList) return 'NodeList';
3739 if (obj instanceof ShadowRoot) return 'ShadowRoot'; 5137 if (obj instanceof ShadowRoot) return 'ShadowRoot';
3740 if (obj instanceof MutationRecord) return 'MutationRecord'; 5138 if (obj instanceof MutationRecord) return 'MutationRecord';
3741 if (obj instanceof MutationObserver) return 'MutationObserver'; 5139 if (obj instanceof MutationObserver) return 'MutationObserver';
3742 5140
3743 var unwrapped = unwrapIfNeeded(obj); 5141 var unwrapped = unwrapIfNeeded(obj);
3744 if (obj !== unwrapped) { 5142 if (obj !== unwrapped) {
3745 // Fix up class names for Firefox. 5143 // Fix up class names for Firefox.
3746 // For some of them (like HTMLFormElement and HTMLInputElement), 5144 // For some of them (like HTMLFormElement and HTMLInputElement),
3747 // the "constructor" property of the unwrapped nodes points at the 5145 // the "constructor" property of the unwrapped nodes points at the
3748 // wrapper. 5146 // same constructor as the wrapper.
3749 // Note: it is safe to check for the GeneratedWrapper string because 5147 var ctor = obj.constructor
3750 // we know it is some kind of Shadow DOM wrapper object. 5148 if (ctor === unwrapped.constructor) {
3751 var ctor = obj.constructor;
3752 if (ctor && ctor.name == 'GeneratedWrapper') {
3753 var name = ctor._ShadowDOMPolyfill$cacheTag_; 5149 var name = ctor._ShadowDOMPolyfill$cacheTag_;
3754 if (!name) { 5150 if (!name) {
3755 name = Object.prototype.toString.call(unwrapped); 5151 name = Object.prototype.toString.call(unwrapped);
3756 name = name.substring(8, name.length - 1); 5152 name = name.substring(8, name.length - 1);
3757 ctor._ShadowDOMPolyfill$cacheTag_ = name; 5153 ctor._ShadowDOMPolyfill$cacheTag_ = name;
3758 } 5154 }
3759 return name; 5155 return name;
3760 } 5156 }
3761 5157
3762 obj = unwrapped; 5158 obj = unwrapped;
(...skipping 523 matching lines...) Expand 10 before | Expand all | Expand 10 after
4286 css.push(cssRules[i].cssText); 5682 css.push(cssRules[i].cssText);
4287 } 5683 }
4288 return css.join('\n\n'); 5684 return css.join('\n\n');
4289 } 5685 }
4290 5686
4291 // exports 5687 // exports
4292 scope.ShadowCSS.shimShadowDOMStyling2 = shimShadowDOMStyling2; 5688 scope.ShadowCSS.shimShadowDOMStyling2 = shimShadowDOMStyling2;
4293 })(window.Platform); 5689 })(window.Platform);
4294 5690
4295 } 5691 }
OLDNEW
« no previous file with comments | « pkg/pkg.status ('k') | pkg/shadow_dom/lib/shadow_dom.min.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698