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

Side by Side Diff: runtime/bin/vmservice/client/deployed/web/packages/web_components/platform.concat.js

Issue 342513004: Visual refresh of allocation profile page (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 6 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
OLDNEW
(Empty)
1 /*
2 * Copyright 2012 The Polymer Authors. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7 if (typeof WeakMap === 'undefined') {
8 (function() {
9 var defineProperty = Object.defineProperty;
10 var counter = Date.now() % 1e9;
11
12 var WeakMap = function() {
13 this.name = '__st' + (Math.random() * 1e9 >>> 0) + (counter++ + '__');
14 };
15
16 WeakMap.prototype = {
17 set: function(key, value) {
18 var entry = key[this.name];
19 if (entry && entry[0] === key)
20 entry[1] = value;
21 else
22 defineProperty(key, this.name, {value: [key, value], writable: true});
23 },
24 get: function(key) {
25 var entry;
26 return (entry = key[this.name]) && entry[0] === key ?
27 entry[1] : undefined;
28 },
29 delete: function(key) {
30 this.set(key, undefined);
31 }
32 };
33
34 window.WeakMap = WeakMap;
35 })();
36 }
37
38 // Copyright 2012 Google Inc.
39 //
40 // Licensed under the Apache License, Version 2.0 (the "License");
41 // you may not use this file except in compliance with the License.
42 // You may obtain a copy of the License at
43 //
44 // http://www.apache.org/licenses/LICENSE-2.0
45 //
46 // Unless required by applicable law or agreed to in writing, software
47 // distributed under the License is distributed on an "AS IS" BASIS,
48 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
49 // See the License for the specific language governing permissions and
50 // limitations under the License.
51
52 (function(global) {
53 'use strict';
54
55 // Detect and do basic sanity checking on Object/Array.observe.
56 function detectObjectObserve() {
57 if (typeof Object.observe !== 'function' ||
58 typeof Array.observe !== 'function') {
59 return false;
60 }
61
62 var records = [];
63
64 function callback(recs) {
65 records = recs;
66 }
67
68 var test = {};
69 var arr = [];
70 Object.observe(test, callback);
71 Array.observe(arr, callback);
72 test.id = 1;
73 test.id = 2;
74 delete test.id;
75 arr.push(1, 2);
76 arr.length = 0;
77
78 Object.deliverChangeRecords(callback);
79 if (records.length !== 5)
80 return false;
81
82 if (records[0].type != 'add' ||
83 records[1].type != 'update' ||
84 records[2].type != 'delete' ||
85 records[3].type != 'splice' ||
86 records[4].type != 'splice') {
87 return false;
88 }
89
90 Object.unobserve(test, callback);
91 Array.unobserve(arr, callback);
92
93 return true;
94 }
95
96 var hasObserve = detectObjectObserve();
97
98 function detectEval() {
99 // don't test for eval if document has CSP securityPolicy object and we can see that
100 // eval is not supported. This avoids an error message in console even when the exception
101 // is caught
102 if (global.document &&
103 'securityPolicy' in global.document &&
104 !global.document.securityPolicy.allowsEval) {
105 return false;
106 }
107
108 try {
109 var f = new Function('', 'return true;');
110 return f();
111 } catch (ex) {
112 return false;
113 }
114 }
115
116 var hasEval = detectEval();
117
118 function isIndex(s) {
119 return +s === s >>> 0;
120 }
121
122 function toNumber(s) {
123 return +s;
124 }
125
126 function isObject(obj) {
127 return obj === Object(obj);
128 }
129
130 var numberIsNaN = global.Number.isNaN || function isNaN(value) {
131 return typeof value === 'number' && global.isNaN(value);
132 }
133
134 function areSameValue(left, right) {
135 if (left === right)
136 return left !== 0 || 1 / left === 1 / right;
137 if (numberIsNaN(left) && numberIsNaN(right))
138 return true;
139
140 return left !== left && right !== right;
141 }
142
143 var createObject = ('__proto__' in {}) ?
144 function(obj) { return obj; } :
145 function(obj) {
146 var proto = obj.__proto__;
147 if (!proto)
148 return obj;
149 var newObject = Object.create(proto);
150 Object.getOwnPropertyNames(obj).forEach(function(name) {
151 Object.defineProperty(newObject, name,
152 Object.getOwnPropertyDescriptor(obj, name));
153 });
154 return newObject;
155 };
156
157 var identStart = '[\$_a-zA-Z]';
158 var identPart = '[\$_a-zA-Z0-9]';
159 var ident = identStart + '+' + identPart + '*';
160 var elementIndex = '(?:[0-9]|[1-9]+[0-9]+)';
161 var identOrElementIndex = '(?:' + ident + '|' + elementIndex + ')';
162 var path = '(?:' + identOrElementIndex + ')(?:\\s*\\.\\s*' + identOrElementInd ex + ')*';
163 var pathRegExp = new RegExp('^' + path + '$');
164
165 function isPathValid(s) {
166 if (typeof s != 'string')
167 return false;
168 s = s.trim();
169
170 if (s == '')
171 return true;
172
173 if (s[0] == '.')
174 return false;
175
176 return pathRegExp.test(s);
177 }
178
179 var constructorIsPrivate = {};
180
181 function Path(s, privateToken) {
182 if (privateToken !== constructorIsPrivate)
183 throw Error('Use Path.get to retrieve path objects');
184
185 if (s.trim() == '')
186 return this;
187
188 if (isIndex(s)) {
189 this.push(s);
190 return this;
191 }
192
193 s.split(/\s*\.\s*/).filter(function(part) {
194 return part;
195 }).forEach(function(part) {
196 this.push(part);
197 }, this);
198
199 if (hasEval && this.length) {
200 this.getValueFrom = this.compiledGetValueFromFn();
201 }
202 }
203
204 // TODO(rafaelw): Make simple LRU cache
205 var pathCache = {};
206
207 function getPath(pathString) {
208 if (pathString instanceof Path)
209 return pathString;
210
211 if (pathString == null)
212 pathString = '';
213
214 if (typeof pathString !== 'string')
215 pathString = String(pathString);
216
217 var path = pathCache[pathString];
218 if (path)
219 return path;
220 if (!isPathValid(pathString))
221 return invalidPath;
222 var path = new Path(pathString, constructorIsPrivate);
223 pathCache[pathString] = path;
224 return path;
225 }
226
227 Path.get = getPath;
228
229 Path.prototype = createObject({
230 __proto__: [],
231 valid: true,
232
233 toString: function() {
234 return this.join('.');
235 },
236
237 getValueFrom: function(obj, directObserver) {
238 for (var i = 0; i < this.length; i++) {
239 if (obj == null)
240 return;
241 obj = obj[this[i]];
242 }
243 return obj;
244 },
245
246 iterateObjects: function(obj, observe) {
247 for (var i = 0; i < this.length; i++) {
248 if (i)
249 obj = obj[this[i - 1]];
250 if (!isObject(obj))
251 return;
252 observe(obj);
253 }
254 },
255
256 compiledGetValueFromFn: function() {
257 var accessors = this.map(function(ident) {
258 return isIndex(ident) ? '["' + ident + '"]' : '.' + ident;
259 });
260
261 var str = '';
262 var pathString = 'obj';
263 str += 'if (obj != null';
264 var i = 0;
265 for (; i < (this.length - 1); i++) {
266 var ident = this[i];
267 pathString += accessors[i];
268 str += ' &&\n ' + pathString + ' != null';
269 }
270 str += ')\n';
271
272 pathString += accessors[i];
273
274 str += ' return ' + pathString + ';\nelse\n return undefined;';
275 return new Function('obj', str);
276 },
277
278 setValueFrom: function(obj, value) {
279 if (!this.length)
280 return false;
281
282 for (var i = 0; i < this.length - 1; i++) {
283 if (!isObject(obj))
284 return false;
285 obj = obj[this[i]];
286 }
287
288 if (!isObject(obj))
289 return false;
290
291 obj[this[i]] = value;
292 return true;
293 }
294 });
295
296 var invalidPath = new Path('', constructorIsPrivate);
297 invalidPath.valid = false;
298 invalidPath.getValueFrom = invalidPath.setValueFrom = function() {};
299
300 var MAX_DIRTY_CHECK_CYCLES = 1000;
301
302 function dirtyCheck(observer) {
303 var cycles = 0;
304 while (cycles < MAX_DIRTY_CHECK_CYCLES && observer.check_()) {
305 cycles++;
306 }
307 if (global.testingExposeCycleCount)
308 global.dirtyCheckCycleCount = cycles;
309
310 return cycles > 0;
311 }
312
313 function objectIsEmpty(object) {
314 for (var prop in object)
315 return false;
316 return true;
317 }
318
319 function diffIsEmpty(diff) {
320 return objectIsEmpty(diff.added) &&
321 objectIsEmpty(diff.removed) &&
322 objectIsEmpty(diff.changed);
323 }
324
325 function diffObjectFromOldObject(object, oldObject) {
326 var added = {};
327 var removed = {};
328 var changed = {};
329
330 for (var prop in oldObject) {
331 var newValue = object[prop];
332
333 if (newValue !== undefined && newValue === oldObject[prop])
334 continue;
335
336 if (!(prop in object)) {
337 removed[prop] = undefined;
338 continue;
339 }
340
341 if (newValue !== oldObject[prop])
342 changed[prop] = newValue;
343 }
344
345 for (var prop in object) {
346 if (prop in oldObject)
347 continue;
348
349 added[prop] = object[prop];
350 }
351
352 if (Array.isArray(object) && object.length !== oldObject.length)
353 changed.length = object.length;
354
355 return {
356 added: added,
357 removed: removed,
358 changed: changed
359 };
360 }
361
362 var eomTasks = [];
363 function runEOMTasks() {
364 if (!eomTasks.length)
365 return false;
366
367 for (var i = 0; i < eomTasks.length; i++) {
368 eomTasks[i]();
369 }
370 eomTasks.length = 0;
371 return true;
372 }
373
374 var runEOM = hasObserve ? (function(){
375 var eomObj = { pingPong: true };
376 var eomRunScheduled = false;
377
378 Object.observe(eomObj, function() {
379 runEOMTasks();
380 eomRunScheduled = false;
381 });
382
383 return function(fn) {
384 eomTasks.push(fn);
385 if (!eomRunScheduled) {
386 eomRunScheduled = true;
387 eomObj.pingPong = !eomObj.pingPong;
388 }
389 };
390 })() :
391 (function() {
392 return function(fn) {
393 eomTasks.push(fn);
394 };
395 })();
396
397 var observedObjectCache = [];
398
399 function newObservedObject() {
400 var observer;
401 var object;
402 var discardRecords = false;
403 var first = true;
404
405 function callback(records) {
406 if (observer && observer.state_ === OPENED && !discardRecords)
407 observer.check_(records);
408 }
409
410 return {
411 open: function(obs) {
412 if (observer)
413 throw Error('ObservedObject in use');
414
415 if (!first)
416 Object.deliverChangeRecords(callback);
417
418 observer = obs;
419 first = false;
420 },
421 observe: function(obj, arrayObserve) {
422 object = obj;
423 if (arrayObserve)
424 Array.observe(object, callback);
425 else
426 Object.observe(object, callback);
427 },
428 deliver: function(discard) {
429 discardRecords = discard;
430 Object.deliverChangeRecords(callback);
431 discardRecords = false;
432 },
433 close: function() {
434 observer = undefined;
435 Object.unobserve(object, callback);
436 observedObjectCache.push(this);
437 }
438 };
439 }
440
441 function getObservedObject(observer, object, arrayObserve) {
442 var dir = observedObjectCache.pop() || newObservedObject();
443 dir.open(observer);
444 dir.observe(object, arrayObserve);
445 return dir;
446 }
447
448 var emptyArray = [];
449 var observedSetCache = [];
450
451 function newObservedSet() {
452 var observers = [];
453 var observerCount = 0;
454 var objects = [];
455 var toRemove = emptyArray;
456 var resetNeeded = false;
457 var resetScheduled = false;
458
459 function observe(obj) {
460 if (!obj)
461 return;
462
463 var index = toRemove.indexOf(obj);
464 if (index >= 0) {
465 toRemove[index] = undefined;
466 objects.push(obj);
467 } else if (objects.indexOf(obj) < 0) {
468 objects.push(obj);
469 Object.observe(obj, callback);
470 }
471
472 observe(Object.getPrototypeOf(obj));
473 }
474
475 function reset() {
476 var objs = toRemove === emptyArray ? [] : toRemove;
477 toRemove = objects;
478 objects = objs;
479
480 var observer;
481 for (var id in observers) {
482 observer = observers[id];
483 if (!observer || observer.state_ != OPENED)
484 continue;
485
486 observer.iterateObjects_(observe);
487 }
488
489 for (var i = 0; i < toRemove.length; i++) {
490 var obj = toRemove[i];
491 if (obj)
492 Object.unobserve(obj, callback);
493 }
494
495 toRemove.length = 0;
496 }
497
498 function scheduledReset() {
499 resetScheduled = false;
500 if (!resetNeeded)
501 return;
502
503 reset();
504 }
505
506 function scheduleReset() {
507 if (resetScheduled)
508 return;
509
510 resetNeeded = true;
511 resetScheduled = true;
512 runEOM(scheduledReset);
513 }
514
515 function callback() {
516 reset();
517
518 var observer;
519
520 for (var id in observers) {
521 observer = observers[id];
522 if (!observer || observer.state_ != OPENED)
523 continue;
524
525 observer.check_();
526 }
527 }
528
529 var record = {
530 object: undefined,
531 objects: objects,
532 open: function(obs) {
533 observers[obs.id_] = obs;
534 observerCount++;
535 obs.iterateObjects_(observe);
536 },
537 close: function(obs) {
538 var anyLeft = false;
539
540 observers[obs.id_] = undefined;
541 observerCount--;
542
543 if (observerCount) {
544 scheduleReset();
545 return;
546 }
547 resetNeeded = false;
548
549 for (var i = 0; i < objects.length; i++) {
550 Object.unobserve(objects[i], callback);
551 Observer.unobservedCount++;
552 }
553
554 observers.length = 0;
555 objects.length = 0;
556 observedSetCache.push(this);
557 },
558 reset: scheduleReset
559 };
560
561 return record;
562 }
563
564 var lastObservedSet;
565
566 function getObservedSet(observer, obj) {
567 if (!lastObservedSet || lastObservedSet.object !== obj) {
568 lastObservedSet = observedSetCache.pop() || newObservedSet();
569 lastObservedSet.object = obj;
570 }
571 lastObservedSet.open(observer);
572 return lastObservedSet;
573 }
574
575 var UNOPENED = 0;
576 var OPENED = 1;
577 var CLOSED = 2;
578 var RESETTING = 3;
579
580 var nextObserverId = 1;
581
582 function Observer() {
583 this.state_ = UNOPENED;
584 this.callback_ = undefined;
585 this.target_ = undefined; // TODO(rafaelw): Should be WeakRef
586 this.directObserver_ = undefined;
587 this.value_ = undefined;
588 this.id_ = nextObserverId++;
589 }
590
591 Observer.prototype = {
592 open: function(callback, target) {
593 if (this.state_ != UNOPENED)
594 throw Error('Observer has already been opened.');
595
596 addToAll(this);
597 this.callback_ = callback;
598 this.target_ = target;
599 this.state_ = OPENED;
600 this.connect_();
601 return this.value_;
602 },
603
604 close: function() {
605 if (this.state_ != OPENED)
606 return;
607
608 removeFromAll(this);
609 this.state_ = CLOSED;
610 this.disconnect_();
611 this.value_ = undefined;
612 this.callback_ = undefined;
613 this.target_ = undefined;
614 },
615
616 deliver: function() {
617 if (this.state_ != OPENED)
618 return;
619
620 dirtyCheck(this);
621 },
622
623 report_: function(changes) {
624 try {
625 this.callback_.apply(this.target_, changes);
626 } catch (ex) {
627 Observer._errorThrownDuringCallback = true;
628 console.error('Exception caught during observer callback: ' +
629 (ex.stack || ex));
630 }
631 },
632
633 discardChanges: function() {
634 this.check_(undefined, true);
635 return this.value_;
636 }
637 }
638
639 var collectObservers = !hasObserve;
640 var allObservers;
641 Observer._allObserversCount = 0;
642
643 if (collectObservers) {
644 allObservers = [];
645 }
646
647 function addToAll(observer) {
648 Observer._allObserversCount++;
649 if (!collectObservers)
650 return;
651
652 allObservers.push(observer);
653 }
654
655 function removeFromAll(observer) {
656 Observer._allObserversCount--;
657 }
658
659 var runningMicrotaskCheckpoint = false;
660
661 var hasDebugForceFullDelivery = hasObserve && (function() {
662 try {
663 eval('%RunMicrotasks()');
664 return true;
665 } catch (ex) {
666 return false;
667 }
668 })();
669
670 global.Platform = global.Platform || {};
671
672 global.Platform.performMicrotaskCheckpoint = function() {
673 if (runningMicrotaskCheckpoint)
674 return;
675
676 if (hasDebugForceFullDelivery) {
677 eval('%RunMicrotasks()');
678 return;
679 }
680
681 if (!collectObservers)
682 return;
683
684 runningMicrotaskCheckpoint = true;
685
686 var cycles = 0;
687 var anyChanged, toCheck;
688
689 do {
690 cycles++;
691 toCheck = allObservers;
692 allObservers = [];
693 anyChanged = false;
694
695 for (var i = 0; i < toCheck.length; i++) {
696 var observer = toCheck[i];
697 if (observer.state_ != OPENED)
698 continue;
699
700 if (observer.check_())
701 anyChanged = true;
702
703 allObservers.push(observer);
704 }
705 if (runEOMTasks())
706 anyChanged = true;
707 } while (cycles < MAX_DIRTY_CHECK_CYCLES && anyChanged);
708
709 if (global.testingExposeCycleCount)
710 global.dirtyCheckCycleCount = cycles;
711
712 runningMicrotaskCheckpoint = false;
713 };
714
715 if (collectObservers) {
716 global.Platform.clearObservers = function() {
717 allObservers = [];
718 };
719 }
720
721 function ObjectObserver(object) {
722 Observer.call(this);
723 this.value_ = object;
724 this.oldObject_ = undefined;
725 }
726
727 ObjectObserver.prototype = createObject({
728 __proto__: Observer.prototype,
729
730 arrayObserve: false,
731
732 connect_: function(callback, target) {
733 if (hasObserve) {
734 this.directObserver_ = getObservedObject(this, this.value_,
735 this.arrayObserve);
736 } else {
737 this.oldObject_ = this.copyObject(this.value_);
738 }
739
740 },
741
742 copyObject: function(object) {
743 var copy = Array.isArray(object) ? [] : {};
744 for (var prop in object) {
745 copy[prop] = object[prop];
746 };
747 if (Array.isArray(object))
748 copy.length = object.length;
749 return copy;
750 },
751
752 check_: function(changeRecords, skipChanges) {
753 var diff;
754 var oldValues;
755 if (hasObserve) {
756 if (!changeRecords)
757 return false;
758
759 oldValues = {};
760 diff = diffObjectFromChangeRecords(this.value_, changeRecords,
761 oldValues);
762 } else {
763 oldValues = this.oldObject_;
764 diff = diffObjectFromOldObject(this.value_, this.oldObject_);
765 }
766
767 if (diffIsEmpty(diff))
768 return false;
769
770 if (!hasObserve)
771 this.oldObject_ = this.copyObject(this.value_);
772
773 this.report_([
774 diff.added || {},
775 diff.removed || {},
776 diff.changed || {},
777 function(property) {
778 return oldValues[property];
779 }
780 ]);
781
782 return true;
783 },
784
785 disconnect_: function() {
786 if (hasObserve) {
787 this.directObserver_.close();
788 this.directObserver_ = undefined;
789 } else {
790 this.oldObject_ = undefined;
791 }
792 },
793
794 deliver: function() {
795 if (this.state_ != OPENED)
796 return;
797
798 if (hasObserve)
799 this.directObserver_.deliver(false);
800 else
801 dirtyCheck(this);
802 },
803
804 discardChanges: function() {
805 if (this.directObserver_)
806 this.directObserver_.deliver(true);
807 else
808 this.oldObject_ = this.copyObject(this.value_);
809
810 return this.value_;
811 }
812 });
813
814 function ArrayObserver(array) {
815 if (!Array.isArray(array))
816 throw Error('Provided object is not an Array');
817 ObjectObserver.call(this, array);
818 }
819
820 ArrayObserver.prototype = createObject({
821
822 __proto__: ObjectObserver.prototype,
823
824 arrayObserve: true,
825
826 copyObject: function(arr) {
827 return arr.slice();
828 },
829
830 check_: function(changeRecords) {
831 var splices;
832 if (hasObserve) {
833 if (!changeRecords)
834 return false;
835 splices = projectArraySplices(this.value_, changeRecords);
836 } else {
837 splices = calcSplices(this.value_, 0, this.value_.length,
838 this.oldObject_, 0, this.oldObject_.length);
839 }
840
841 if (!splices || !splices.length)
842 return false;
843
844 if (!hasObserve)
845 this.oldObject_ = this.copyObject(this.value_);
846
847 this.report_([splices]);
848 return true;
849 }
850 });
851
852 ArrayObserver.applySplices = function(previous, current, splices) {
853 splices.forEach(function(splice) {
854 var spliceArgs = [splice.index, splice.removed.length];
855 var addIndex = splice.index;
856 while (addIndex < splice.index + splice.addedCount) {
857 spliceArgs.push(current[addIndex]);
858 addIndex++;
859 }
860
861 Array.prototype.splice.apply(previous, spliceArgs);
862 });
863 };
864
865 function PathObserver(object, path) {
866 Observer.call(this);
867
868 this.object_ = object;
869 this.path_ = path instanceof Path ? path : getPath(path);
870 this.directObserver_ = undefined;
871 }
872
873 PathObserver.prototype = createObject({
874 __proto__: Observer.prototype,
875
876 connect_: function() {
877 if (hasObserve)
878 this.directObserver_ = getObservedSet(this, this.object_);
879
880 this.check_(undefined, true);
881 },
882
883 disconnect_: function() {
884 this.value_ = undefined;
885
886 if (this.directObserver_) {
887 this.directObserver_.close(this);
888 this.directObserver_ = undefined;
889 }
890 },
891
892 iterateObjects_: function(observe) {
893 this.path_.iterateObjects(this.object_, observe);
894 },
895
896 check_: function(changeRecords, skipChanges) {
897 var oldValue = this.value_;
898 this.value_ = this.path_.getValueFrom(this.object_);
899 if (skipChanges || areSameValue(this.value_, oldValue))
900 return false;
901
902 this.report_([this.value_, oldValue]);
903 return true;
904 },
905
906 setValue: function(newValue) {
907 if (this.path_)
908 this.path_.setValueFrom(this.object_, newValue);
909 }
910 });
911
912 function CompoundObserver() {
913 Observer.call(this);
914
915 this.value_ = [];
916 this.directObserver_ = undefined;
917 this.observed_ = [];
918 }
919
920 var observerSentinel = {};
921
922 CompoundObserver.prototype = createObject({
923 __proto__: Observer.prototype,
924
925 connect_: function() {
926 this.check_(undefined, true);
927
928 if (!hasObserve)
929 return;
930
931 var object;
932 var needsDirectObserver = false;
933 for (var i = 0; i < this.observed_.length; i += 2) {
934 object = this.observed_[i]
935 if (object !== observerSentinel) {
936 needsDirectObserver = true;
937 break;
938 }
939 }
940
941 if (this.directObserver_) {
942 if (needsDirectObserver) {
943 this.directObserver_.reset();
944 return;
945 }
946 this.directObserver_.close();
947 this.directObserver_ = undefined;
948 return;
949 }
950
951 if (needsDirectObserver)
952 this.directObserver_ = getObservedSet(this, object);
953 },
954
955 closeObservers_: function() {
956 for (var i = 0; i < this.observed_.length; i += 2) {
957 if (this.observed_[i] === observerSentinel)
958 this.observed_[i + 1].close();
959 }
960 this.observed_.length = 0;
961 },
962
963 disconnect_: function() {
964 this.value_ = undefined;
965
966 if (this.directObserver_) {
967 this.directObserver_.close(this);
968 this.directObserver_ = undefined;
969 }
970
971 this.closeObservers_();
972 },
973
974 addPath: function(object, path) {
975 if (this.state_ != UNOPENED && this.state_ != RESETTING)
976 throw Error('Cannot add paths once started.');
977
978 this.observed_.push(object, path instanceof Path ? path : getPath(path));
979 },
980
981 addObserver: function(observer) {
982 if (this.state_ != UNOPENED && this.state_ != RESETTING)
983 throw Error('Cannot add observers once started.');
984
985 observer.open(this.deliver, this);
986 this.observed_.push(observerSentinel, observer);
987 },
988
989 startReset: function() {
990 if (this.state_ != OPENED)
991 throw Error('Can only reset while open');
992
993 this.state_ = RESETTING;
994 this.closeObservers_();
995 },
996
997 finishReset: function() {
998 if (this.state_ != RESETTING)
999 throw Error('Can only finishReset after startReset');
1000 this.state_ = OPENED;
1001 this.connect_();
1002
1003 return this.value_;
1004 },
1005
1006 iterateObjects_: function(observe) {
1007 var object;
1008 for (var i = 0; i < this.observed_.length; i += 2) {
1009 object = this.observed_[i]
1010 if (object !== observerSentinel)
1011 this.observed_[i + 1].iterateObjects(object, observe)
1012 }
1013 },
1014
1015 check_: function(changeRecords, skipChanges) {
1016 var oldValues;
1017 for (var i = 0; i < this.observed_.length; i += 2) {
1018 var pathOrObserver = this.observed_[i+1];
1019 var object = this.observed_[i];
1020 var value = object === observerSentinel ?
1021 pathOrObserver.discardChanges() :
1022 pathOrObserver.getValueFrom(object)
1023
1024 if (skipChanges) {
1025 this.value_[i / 2] = value;
1026 continue;
1027 }
1028
1029 if (areSameValue(value, this.value_[i / 2]))
1030 continue;
1031
1032 oldValues = oldValues || [];
1033 oldValues[i / 2] = this.value_[i / 2];
1034 this.value_[i / 2] = value;
1035 }
1036
1037 if (!oldValues)
1038 return false;
1039
1040 // TODO(rafaelw): Having observed_ as the third callback arg here is
1041 // pretty lame API. Fix.
1042 this.report_([this.value_, oldValues, this.observed_]);
1043 return true;
1044 }
1045 });
1046
1047 function identFn(value) { return value; }
1048
1049 function ObserverTransform(observable, getValueFn, setValueFn,
1050 dontPassThroughSet) {
1051 this.callback_ = undefined;
1052 this.target_ = undefined;
1053 this.value_ = undefined;
1054 this.observable_ = observable;
1055 this.getValueFn_ = getValueFn || identFn;
1056 this.setValueFn_ = setValueFn || identFn;
1057 // TODO(rafaelw): This is a temporary hack. PolymerExpressions needs this
1058 // at the moment because of a bug in it's dependency tracking.
1059 this.dontPassThroughSet_ = dontPassThroughSet;
1060 }
1061
1062 ObserverTransform.prototype = {
1063 open: function(callback, target) {
1064 this.callback_ = callback;
1065 this.target_ = target;
1066 this.value_ =
1067 this.getValueFn_(this.observable_.open(this.observedCallback_, this));
1068 return this.value_;
1069 },
1070
1071 observedCallback_: function(value) {
1072 value = this.getValueFn_(value);
1073 if (areSameValue(value, this.value_))
1074 return;
1075 var oldValue = this.value_;
1076 this.value_ = value;
1077 this.callback_.call(this.target_, this.value_, oldValue);
1078 },
1079
1080 discardChanges: function() {
1081 this.value_ = this.getValueFn_(this.observable_.discardChanges());
1082 return this.value_;
1083 },
1084
1085 deliver: function() {
1086 return this.observable_.deliver();
1087 },
1088
1089 setValue: function(value) {
1090 value = this.setValueFn_(value);
1091 if (!this.dontPassThroughSet_ && this.observable_.setValue)
1092 return this.observable_.setValue(value);
1093 },
1094
1095 close: function() {
1096 if (this.observable_)
1097 this.observable_.close();
1098 this.callback_ = undefined;
1099 this.target_ = undefined;
1100 this.observable_ = undefined;
1101 this.value_ = undefined;
1102 this.getValueFn_ = undefined;
1103 this.setValueFn_ = undefined;
1104 }
1105 }
1106
1107 var expectedRecordTypes = {
1108 add: true,
1109 update: true,
1110 delete: true
1111 };
1112
1113 function notifyFunction(object, name) {
1114 if (typeof Object.observe !== 'function')
1115 return;
1116
1117 var notifier = Object.getNotifier(object);
1118 return function(type, oldValue) {
1119 var changeRecord = {
1120 object: object,
1121 type: type,
1122 name: name
1123 };
1124 if (arguments.length === 2)
1125 changeRecord.oldValue = oldValue;
1126 notifier.notify(changeRecord);
1127 }
1128 }
1129
1130 Observer.defineComputedProperty = function(target, name, observable) {
1131 var notify = notifyFunction(target, name);
1132 var value = observable.open(function(newValue, oldValue) {
1133 value = newValue;
1134 if (notify)
1135 notify('update', oldValue);
1136 });
1137
1138 Object.defineProperty(target, name, {
1139 get: function() {
1140 observable.deliver();
1141 return value;
1142 },
1143 set: function(newValue) {
1144 observable.setValue(newValue);
1145 return newValue;
1146 },
1147 configurable: true
1148 });
1149
1150 return {
1151 close: function() {
1152 observable.close();
1153 Object.defineProperty(target, name, {
1154 value: value,
1155 writable: true,
1156 configurable: true
1157 });
1158 }
1159 };
1160 }
1161
1162 function diffObjectFromChangeRecords(object, changeRecords, oldValues) {
1163 var added = {};
1164 var removed = {};
1165
1166 for (var i = 0; i < changeRecords.length; i++) {
1167 var record = changeRecords[i];
1168 if (!expectedRecordTypes[record.type]) {
1169 console.error('Unknown changeRecord type: ' + record.type);
1170 console.error(record);
1171 continue;
1172 }
1173
1174 if (!(record.name in oldValues))
1175 oldValues[record.name] = record.oldValue;
1176
1177 if (record.type == 'update')
1178 continue;
1179
1180 if (record.type == 'add') {
1181 if (record.name in removed)
1182 delete removed[record.name];
1183 else
1184 added[record.name] = true;
1185
1186 continue;
1187 }
1188
1189 // type = 'delete'
1190 if (record.name in added) {
1191 delete added[record.name];
1192 delete oldValues[record.name];
1193 } else {
1194 removed[record.name] = true;
1195 }
1196 }
1197
1198 for (var prop in added)
1199 added[prop] = object[prop];
1200
1201 for (var prop in removed)
1202 removed[prop] = undefined;
1203
1204 var changed = {};
1205 for (var prop in oldValues) {
1206 if (prop in added || prop in removed)
1207 continue;
1208
1209 var newValue = object[prop];
1210 if (oldValues[prop] !== newValue)
1211 changed[prop] = newValue;
1212 }
1213
1214 return {
1215 added: added,
1216 removed: removed,
1217 changed: changed
1218 };
1219 }
1220
1221 function newSplice(index, removed, addedCount) {
1222 return {
1223 index: index,
1224 removed: removed,
1225 addedCount: addedCount
1226 };
1227 }
1228
1229 var EDIT_LEAVE = 0;
1230 var EDIT_UPDATE = 1;
1231 var EDIT_ADD = 2;
1232 var EDIT_DELETE = 3;
1233
1234 function ArraySplice() {}
1235
1236 ArraySplice.prototype = {
1237
1238 // Note: This function is *based* on the computation of the Levenshtein
1239 // "edit" distance. The one change is that "updates" are treated as two
1240 // edits - not one. With Array splices, an update is really a delete
1241 // followed by an add. By retaining this, we optimize for "keeping" the
1242 // maximum array items in the original array. For example:
1243 //
1244 // 'xxxx123' -> '123yyyy'
1245 //
1246 // With 1-edit updates, the shortest path would be just to update all seven
1247 // characters. With 2-edit updates, we delete 4, leave 3, and add 4. This
1248 // leaves the substring '123' intact.
1249 calcEditDistances: function(current, currentStart, currentEnd,
1250 old, oldStart, oldEnd) {
1251 // "Deletion" columns
1252 var rowCount = oldEnd - oldStart + 1;
1253 var columnCount = currentEnd - currentStart + 1;
1254 var distances = new Array(rowCount);
1255
1256 // "Addition" rows. Initialize null column.
1257 for (var i = 0; i < rowCount; i++) {
1258 distances[i] = new Array(columnCount);
1259 distances[i][0] = i;
1260 }
1261
1262 // Initialize null row
1263 for (var j = 0; j < columnCount; j++)
1264 distances[0][j] = j;
1265
1266 for (var i = 1; i < rowCount; i++) {
1267 for (var j = 1; j < columnCount; j++) {
1268 if (this.equals(current[currentStart + j - 1], old[oldStart + i - 1]))
1269 distances[i][j] = distances[i - 1][j - 1];
1270 else {
1271 var north = distances[i - 1][j] + 1;
1272 var west = distances[i][j - 1] + 1;
1273 distances[i][j] = north < west ? north : west;
1274 }
1275 }
1276 }
1277
1278 return distances;
1279 },
1280
1281 // This starts at the final weight, and walks "backward" by finding
1282 // the minimum previous weight recursively until the origin of the weight
1283 // matrix.
1284 spliceOperationsFromEditDistances: function(distances) {
1285 var i = distances.length - 1;
1286 var j = distances[0].length - 1;
1287 var current = distances[i][j];
1288 var edits = [];
1289 while (i > 0 || j > 0) {
1290 if (i == 0) {
1291 edits.push(EDIT_ADD);
1292 j--;
1293 continue;
1294 }
1295 if (j == 0) {
1296 edits.push(EDIT_DELETE);
1297 i--;
1298 continue;
1299 }
1300 var northWest = distances[i - 1][j - 1];
1301 var west = distances[i - 1][j];
1302 var north = distances[i][j - 1];
1303
1304 var min;
1305 if (west < north)
1306 min = west < northWest ? west : northWest;
1307 else
1308 min = north < northWest ? north : northWest;
1309
1310 if (min == northWest) {
1311 if (northWest == current) {
1312 edits.push(EDIT_LEAVE);
1313 } else {
1314 edits.push(EDIT_UPDATE);
1315 current = northWest;
1316 }
1317 i--;
1318 j--;
1319 } else if (min == west) {
1320 edits.push(EDIT_DELETE);
1321 i--;
1322 current = west;
1323 } else {
1324 edits.push(EDIT_ADD);
1325 j--;
1326 current = north;
1327 }
1328 }
1329
1330 edits.reverse();
1331 return edits;
1332 },
1333
1334 /**
1335 * Splice Projection functions:
1336 *
1337 * A splice map is a representation of how a previous array of items
1338 * was transformed into a new array of items. Conceptually it is a list of
1339 * tuples of
1340 *
1341 * <index, removed, addedCount>
1342 *
1343 * which are kept in ascending index order of. The tuple represents that at
1344 * the |index|, |removed| sequence of items were removed, and counting forwa rd
1345 * from |index|, |addedCount| items were added.
1346 */
1347
1348 /**
1349 * Lacking individual splice mutation information, the minimal set of
1350 * splices can be synthesized given the previous state and final state of an
1351 * array. The basic approach is to calculate the edit distance matrix and
1352 * choose the shortest path through it.
1353 *
1354 * Complexity: O(l * p)
1355 * l: The length of the current array
1356 * p: The length of the old array
1357 */
1358 calcSplices: function(current, currentStart, currentEnd,
1359 old, oldStart, oldEnd) {
1360 var prefixCount = 0;
1361 var suffixCount = 0;
1362
1363 var minLength = Math.min(currentEnd - currentStart, oldEnd - oldStart);
1364 if (currentStart == 0 && oldStart == 0)
1365 prefixCount = this.sharedPrefix(current, old, minLength);
1366
1367 if (currentEnd == current.length && oldEnd == old.length)
1368 suffixCount = this.sharedSuffix(current, old, minLength - prefixCount);
1369
1370 currentStart += prefixCount;
1371 oldStart += prefixCount;
1372 currentEnd -= suffixCount;
1373 oldEnd -= suffixCount;
1374
1375 if (currentEnd - currentStart == 0 && oldEnd - oldStart == 0)
1376 return [];
1377
1378 if (currentStart == currentEnd) {
1379 var splice = newSplice(currentStart, [], 0);
1380 while (oldStart < oldEnd)
1381 splice.removed.push(old[oldStart++]);
1382
1383 return [ splice ];
1384 } else if (oldStart == oldEnd)
1385 return [ newSplice(currentStart, [], currentEnd - currentStart) ];
1386
1387 var ops = this.spliceOperationsFromEditDistances(
1388 this.calcEditDistances(current, currentStart, currentEnd,
1389 old, oldStart, oldEnd));
1390
1391 var splice = undefined;
1392 var splices = [];
1393 var index = currentStart;
1394 var oldIndex = oldStart;
1395 for (var i = 0; i < ops.length; i++) {
1396 switch(ops[i]) {
1397 case EDIT_LEAVE:
1398 if (splice) {
1399 splices.push(splice);
1400 splice = undefined;
1401 }
1402
1403 index++;
1404 oldIndex++;
1405 break;
1406 case EDIT_UPDATE:
1407 if (!splice)
1408 splice = newSplice(index, [], 0);
1409
1410 splice.addedCount++;
1411 index++;
1412
1413 splice.removed.push(old[oldIndex]);
1414 oldIndex++;
1415 break;
1416 case EDIT_ADD:
1417 if (!splice)
1418 splice = newSplice(index, [], 0);
1419
1420 splice.addedCount++;
1421 index++;
1422 break;
1423 case EDIT_DELETE:
1424 if (!splice)
1425 splice = newSplice(index, [], 0);
1426
1427 splice.removed.push(old[oldIndex]);
1428 oldIndex++;
1429 break;
1430 }
1431 }
1432
1433 if (splice) {
1434 splices.push(splice);
1435 }
1436 return splices;
1437 },
1438
1439 sharedPrefix: function(current, old, searchLength) {
1440 for (var i = 0; i < searchLength; i++)
1441 if (!this.equals(current[i], old[i]))
1442 return i;
1443 return searchLength;
1444 },
1445
1446 sharedSuffix: function(current, old, searchLength) {
1447 var index1 = current.length;
1448 var index2 = old.length;
1449 var count = 0;
1450 while (count < searchLength && this.equals(current[--index1], old[--index2 ]))
1451 count++;
1452
1453 return count;
1454 },
1455
1456 calculateSplices: function(current, previous) {
1457 return this.calcSplices(current, 0, current.length, previous, 0,
1458 previous.length);
1459 },
1460
1461 equals: function(currentValue, previousValue) {
1462 return currentValue === previousValue;
1463 }
1464 };
1465
1466 var arraySplice = new ArraySplice();
1467
1468 function calcSplices(current, currentStart, currentEnd,
1469 old, oldStart, oldEnd) {
1470 return arraySplice.calcSplices(current, currentStart, currentEnd,
1471 old, oldStart, oldEnd);
1472 }
1473
1474 function intersect(start1, end1, start2, end2) {
1475 // Disjoint
1476 if (end1 < start2 || end2 < start1)
1477 return -1;
1478
1479 // Adjacent
1480 if (end1 == start2 || end2 == start1)
1481 return 0;
1482
1483 // Non-zero intersect, span1 first
1484 if (start1 < start2) {
1485 if (end1 < end2)
1486 return end1 - start2; // Overlap
1487 else
1488 return end2 - start2; // Contained
1489 } else {
1490 // Non-zero intersect, span2 first
1491 if (end2 < end1)
1492 return end2 - start1; // Overlap
1493 else
1494 return end1 - start1; // Contained
1495 }
1496 }
1497
1498 function mergeSplice(splices, index, removed, addedCount) {
1499
1500 var splice = newSplice(index, removed, addedCount);
1501
1502 var inserted = false;
1503 var insertionOffset = 0;
1504
1505 for (var i = 0; i < splices.length; i++) {
1506 var current = splices[i];
1507 current.index += insertionOffset;
1508
1509 if (inserted)
1510 continue;
1511
1512 var intersectCount = intersect(splice.index,
1513 splice.index + splice.removed.length,
1514 current.index,
1515 current.index + current.addedCount);
1516
1517 if (intersectCount >= 0) {
1518 // Merge the two splices
1519
1520 splices.splice(i, 1);
1521 i--;
1522
1523 insertionOffset -= current.addedCount - current.removed.length;
1524
1525 splice.addedCount += current.addedCount - intersectCount;
1526 var deleteCount = splice.removed.length +
1527 current.removed.length - intersectCount;
1528
1529 if (!splice.addedCount && !deleteCount) {
1530 // merged splice is a noop. discard.
1531 inserted = true;
1532 } else {
1533 var removed = current.removed;
1534
1535 if (splice.index < current.index) {
1536 // some prefix of splice.removed is prepended to current.removed.
1537 var prepend = splice.removed.slice(0, current.index - splice.index);
1538 Array.prototype.push.apply(prepend, removed);
1539 removed = prepend;
1540 }
1541
1542 if (splice.index + splice.removed.length > current.index + current.add edCount) {
1543 // some suffix of splice.removed is appended to current.removed.
1544 var append = splice.removed.slice(current.index + current.addedCount - splice.index);
1545 Array.prototype.push.apply(removed, append);
1546 }
1547
1548 splice.removed = removed;
1549 if (current.index < splice.index) {
1550 splice.index = current.index;
1551 }
1552 }
1553 } else if (splice.index < current.index) {
1554 // Insert splice here.
1555
1556 inserted = true;
1557
1558 splices.splice(i, 0, splice);
1559 i++;
1560
1561 var offset = splice.addedCount - splice.removed.length
1562 current.index += offset;
1563 insertionOffset += offset;
1564 }
1565 }
1566
1567 if (!inserted)
1568 splices.push(splice);
1569 }
1570
1571 function createInitialSplices(array, changeRecords) {
1572 var splices = [];
1573
1574 for (var i = 0; i < changeRecords.length; i++) {
1575 var record = changeRecords[i];
1576 switch(record.type) {
1577 case 'splice':
1578 mergeSplice(splices, record.index, record.removed.slice(), record.adde dCount);
1579 break;
1580 case 'add':
1581 case 'update':
1582 case 'delete':
1583 if (!isIndex(record.name))
1584 continue;
1585 var index = toNumber(record.name);
1586 if (index < 0)
1587 continue;
1588 mergeSplice(splices, index, [record.oldValue], 1);
1589 break;
1590 default:
1591 console.error('Unexpected record type: ' + JSON.stringify(record));
1592 break;
1593 }
1594 }
1595
1596 return splices;
1597 }
1598
1599 function projectArraySplices(array, changeRecords) {
1600 var splices = [];
1601
1602 createInitialSplices(array, changeRecords).forEach(function(splice) {
1603 if (splice.addedCount == 1 && splice.removed.length == 1) {
1604 if (splice.removed[0] !== array[splice.index])
1605 splices.push(splice);
1606
1607 return
1608 };
1609
1610 splices = splices.concat(calcSplices(array, splice.index, splice.index + s plice.addedCount,
1611 splice.removed, 0, splice.removed.len gth));
1612 });
1613
1614 return splices;
1615 }
1616
1617 global.Observer = Observer;
1618 global.Observer.runEOM_ = runEOM;
1619 global.Observer.hasObjectObserve = hasObserve;
1620 global.ArrayObserver = ArrayObserver;
1621 global.ArrayObserver.calculateSplices = function(current, previous) {
1622 return arraySplice.calculateSplices(current, previous);
1623 };
1624
1625 global.ArraySplice = ArraySplice;
1626 global.ObjectObserver = ObjectObserver;
1627 global.PathObserver = PathObserver;
1628 global.CompoundObserver = CompoundObserver;
1629 global.Path = Path;
1630 global.ObserverTransform = ObserverTransform;
1631 })(typeof global !== 'undefined' && global && typeof module !== 'undefined' && m odule ? global : this || window);
1632
1633 // prepoulate window.Platform.flags for default controls
1634 window.Platform = window.Platform || {};
1635 // prepopulate window.logFlags if necessary
1636 window.logFlags = window.logFlags || {};
1637 // process flags
1638 (function(scope){
1639 // import
1640 var flags = scope.flags || {};
1641 // populate flags from location
1642 location.search.slice(1).split('&').forEach(function(o) {
1643 o = o.split('=');
1644 o[0] && (flags[o[0]] = o[1] || true);
1645 });
1646 var entryPoint = document.currentScript ||
1647 document.querySelector('script[src*="platform.js"]');
1648 if (entryPoint) {
1649 var a = entryPoint.attributes;
1650 for (var i = 0, n; i < a.length; i++) {
1651 n = a[i];
1652 if (n.name !== 'src') {
1653 flags[n.name] = n.value || true;
1654 }
1655 }
1656 }
1657 if (flags.log) {
1658 flags.log.split(',').forEach(function(f) {
1659 window.logFlags[f] = true;
1660 });
1661 }
1662 // If any of these flags match 'native', then force native ShadowDOM; any
1663 // other truthy value, or failure to detect native
1664 // ShadowDOM, results in polyfill
1665 flags.shadow = flags.shadow || flags.shadowdom || flags.polyfill;
1666 if (flags.shadow === 'native') {
1667 flags.shadow = false;
1668 } else {
1669 flags.shadow = flags.shadow || !HTMLElement.prototype.createShadowRoot;
1670 }
1671
1672 if (flags.shadow && document.querySelectorAll('script').length > 1) {
1673 console.warn('platform.js is not the first script on the page. ' +
1674 'See http://www.polymer-project.org/docs/start/platform.html#setup ' +
1675 'for details.');
1676 }
1677
1678 // CustomElements polyfill flag
1679 if (flags.register) {
1680 window.CustomElements = window.CustomElements || {flags: {}};
1681 window.CustomElements.flags.register = flags.register;
1682 }
1683
1684 if (flags.imports) {
1685 window.HTMLImports = window.HTMLImports || {flags: {}};
1686 window.HTMLImports.flags.imports = flags.imports;
1687 }
1688
1689 // export
1690 scope.flags = flags;
1691 })(Platform);
1692
1693 // select ShadowDOM impl
1694 if (Platform.flags.shadow) {
1695
1696 // Copyright 2012 The Polymer Authors. All rights reserved.
1697 // Use of this source code is goverened by a BSD-style
1698 // license that can be found in the LICENSE file.
1699
1700 window.ShadowDOMPolyfill = {};
1701
1702 (function(scope) {
1703 'use strict';
1704
1705 var constructorTable = new WeakMap();
1706 var nativePrototypeTable = new WeakMap();
1707 var wrappers = Object.create(null);
1708
1709 // Don't test for eval if document has CSP securityPolicy object and we can
1710 // see that eval is not supported. This avoids an error message in console
1711 // even when the exception is caught
1712 var hasEval = !('securityPolicy' in document) ||
1713 document.securityPolicy.allowsEval;
1714 if (hasEval) {
1715 try {
1716 var f = new Function('', 'return true;');
1717 hasEval = f();
1718 } catch (ex) {
1719 hasEval = false;
1720 }
1721 }
1722
1723 function assert(b) {
1724 if (!b)
1725 throw new Error('Assertion failed');
1726 };
1727
1728 var defineProperty = Object.defineProperty;
1729 var getOwnPropertyNames = Object.getOwnPropertyNames;
1730 var getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
1731
1732 function mixin(to, from) {
1733 getOwnPropertyNames(from).forEach(function(name) {
1734 defineProperty(to, name, getOwnPropertyDescriptor(from, name));
1735 });
1736 return to;
1737 };
1738
1739 function mixinStatics(to, from) {
1740 getOwnPropertyNames(from).forEach(function(name) {
1741 switch (name) {
1742 case 'arguments':
1743 case 'caller':
1744 case 'length':
1745 case 'name':
1746 case 'prototype':
1747 case 'toString':
1748 return;
1749 }
1750 defineProperty(to, name, getOwnPropertyDescriptor(from, name));
1751 });
1752 return to;
1753 };
1754
1755 function oneOf(object, propertyNames) {
1756 for (var i = 0; i < propertyNames.length; i++) {
1757 if (propertyNames[i] in object)
1758 return propertyNames[i];
1759 }
1760 }
1761
1762 // Mozilla's old DOM bindings are bretty busted:
1763 // https://bugzilla.mozilla.org/show_bug.cgi?id=855844
1764 // Make sure they are create before we start modifying things.
1765 getOwnPropertyNames(window);
1766
1767 function getWrapperConstructor(node) {
1768 var nativePrototype = node.__proto__ || Object.getPrototypeOf(node);
1769 var wrapperConstructor = constructorTable.get(nativePrototype);
1770 if (wrapperConstructor)
1771 return wrapperConstructor;
1772
1773 var parentWrapperConstructor = getWrapperConstructor(nativePrototype);
1774
1775 var GeneratedWrapper = createWrapperConstructor(parentWrapperConstructor);
1776 registerInternal(nativePrototype, GeneratedWrapper, node);
1777
1778 return GeneratedWrapper;
1779 }
1780
1781 function addForwardingProperties(nativePrototype, wrapperPrototype) {
1782 installProperty(nativePrototype, wrapperPrototype, true);
1783 }
1784
1785 function registerInstanceProperties(wrapperPrototype, instanceObject) {
1786 installProperty(instanceObject, wrapperPrototype, false);
1787 }
1788
1789 var isFirefox = /Firefox/.test(navigator.userAgent);
1790
1791 // This is used as a fallback when getting the descriptor fails in
1792 // installProperty.
1793 var dummyDescriptor = {
1794 get: function() {},
1795 set: function(v) {},
1796 configurable: true,
1797 enumerable: true
1798 };
1799
1800 function isEventHandlerName(name) {
1801 return /^on[a-z]+$/.test(name);
1802 }
1803
1804 function isIdentifierName(name) {
1805 return /^\w[a-zA-Z_0-9]*$/.test(name);
1806 }
1807
1808 function getGetter(name) {
1809 return hasEval && isIdentifierName(name) ?
1810 new Function('return this.impl.' + name) :
1811 function() { return this.impl[name]; };
1812 }
1813
1814 function getSetter(name) {
1815 return hasEval && isIdentifierName(name) ?
1816 new Function('v', 'this.impl.' + name + ' = v') :
1817 function(v) { this.impl[name] = v; };
1818 }
1819
1820 function getMethod(name) {
1821 return hasEval && isIdentifierName(name) ?
1822 new Function('return this.impl.' + name +
1823 '.apply(this.impl, arguments)') :
1824 function() { return this.impl[name].apply(this.impl, arguments); };
1825 }
1826
1827 function getDescriptor(source, name) {
1828 try {
1829 return Object.getOwnPropertyDescriptor(source, name);
1830 } catch (ex) {
1831 // JSC and V8 both use data properties instead of accessors which can
1832 // cause getting the property desciptor to throw an exception.
1833 // https://bugs.webkit.org/show_bug.cgi?id=49739
1834 return dummyDescriptor;
1835 }
1836 }
1837
1838 function installProperty(source, target, allowMethod, opt_blacklist) {
1839 var names = getOwnPropertyNames(source);
1840 for (var i = 0; i < names.length; i++) {
1841 var name = names[i];
1842 if (name === 'polymerBlackList_')
1843 continue;
1844
1845 if (name in target)
1846 continue;
1847
1848 if (source.polymerBlackList_ && source.polymerBlackList_[name])
1849 continue;
1850
1851 if (isFirefox) {
1852 // Tickle Firefox's old bindings.
1853 source.__lookupGetter__(name);
1854 }
1855 var descriptor = getDescriptor(source, name);
1856 var getter, setter;
1857 if (allowMethod && typeof descriptor.value === 'function') {
1858 target[name] = getMethod(name);
1859 continue;
1860 }
1861
1862 var isEvent = isEventHandlerName(name);
1863 if (isEvent)
1864 getter = scope.getEventHandlerGetter(name);
1865 else
1866 getter = getGetter(name);
1867
1868 if (descriptor.writable || descriptor.set) {
1869 if (isEvent)
1870 setter = scope.getEventHandlerSetter(name);
1871 else
1872 setter = getSetter(name);
1873 }
1874
1875 defineProperty(target, name, {
1876 get: getter,
1877 set: setter,
1878 configurable: descriptor.configurable,
1879 enumerable: descriptor.enumerable
1880 });
1881 }
1882 }
1883
1884 /**
1885 * @param {Function} nativeConstructor
1886 * @param {Function} wrapperConstructor
1887 * @param {Object=} opt_instance If present, this is used to extract
1888 * properties from an instance object.
1889 */
1890 function register(nativeConstructor, wrapperConstructor, opt_instance) {
1891 var nativePrototype = nativeConstructor.prototype;
1892 registerInternal(nativePrototype, wrapperConstructor, opt_instance);
1893 mixinStatics(wrapperConstructor, nativeConstructor);
1894 }
1895
1896 function registerInternal(nativePrototype, wrapperConstructor, opt_instance) {
1897 var wrapperPrototype = wrapperConstructor.prototype;
1898 assert(constructorTable.get(nativePrototype) === undefined);
1899
1900 constructorTable.set(nativePrototype, wrapperConstructor);
1901 nativePrototypeTable.set(wrapperPrototype, nativePrototype);
1902
1903 addForwardingProperties(nativePrototype, wrapperPrototype);
1904 if (opt_instance)
1905 registerInstanceProperties(wrapperPrototype, opt_instance);
1906 defineProperty(wrapperPrototype, 'constructor', {
1907 value: wrapperConstructor,
1908 configurable: true,
1909 enumerable: false,
1910 writable: true
1911 });
1912 // Set it again. Some VMs optimizes objects that are used as prototypes.
1913 wrapperConstructor.prototype = wrapperPrototype;
1914 }
1915
1916 function isWrapperFor(wrapperConstructor, nativeConstructor) {
1917 return constructorTable.get(nativeConstructor.prototype) ===
1918 wrapperConstructor;
1919 }
1920
1921 /**
1922 * Creates a generic wrapper constructor based on |object| and its
1923 * constructor.
1924 * @param {Node} object
1925 * @return {Function} The generated constructor.
1926 */
1927 function registerObject(object) {
1928 var nativePrototype = Object.getPrototypeOf(object);
1929
1930 var superWrapperConstructor = getWrapperConstructor(nativePrototype);
1931 var GeneratedWrapper = createWrapperConstructor(superWrapperConstructor);
1932 registerInternal(nativePrototype, GeneratedWrapper, object);
1933
1934 return GeneratedWrapper;
1935 }
1936
1937 function createWrapperConstructor(superWrapperConstructor) {
1938 function GeneratedWrapper(node) {
1939 superWrapperConstructor.call(this, node);
1940 }
1941 var p = Object.create(superWrapperConstructor.prototype);
1942 p.constructor = GeneratedWrapper;
1943 GeneratedWrapper.prototype = p;
1944
1945 return GeneratedWrapper;
1946 }
1947
1948 var OriginalDOMImplementation = window.DOMImplementation;
1949 var OriginalEventTarget = window.EventTarget;
1950 var OriginalEvent = window.Event;
1951 var OriginalNode = window.Node;
1952 var OriginalWindow = window.Window;
1953 var OriginalRange = window.Range;
1954 var OriginalCanvasRenderingContext2D = window.CanvasRenderingContext2D;
1955 var OriginalWebGLRenderingContext = window.WebGLRenderingContext;
1956 var OriginalSVGElementInstance = window.SVGElementInstance;
1957
1958 function isWrapper(object) {
1959 return object instanceof wrappers.EventTarget ||
1960 object instanceof wrappers.Event ||
1961 object instanceof wrappers.Range ||
1962 object instanceof wrappers.DOMImplementation ||
1963 object instanceof wrappers.CanvasRenderingContext2D ||
1964 wrappers.WebGLRenderingContext &&
1965 object instanceof wrappers.WebGLRenderingContext;
1966 }
1967
1968 function isNative(object) {
1969 return OriginalEventTarget && object instanceof OriginalEventTarget ||
1970 object instanceof OriginalNode ||
1971 object instanceof OriginalEvent ||
1972 object instanceof OriginalWindow ||
1973 object instanceof OriginalRange ||
1974 object instanceof OriginalDOMImplementation ||
1975 object instanceof OriginalCanvasRenderingContext2D ||
1976 OriginalWebGLRenderingContext &&
1977 object instanceof OriginalWebGLRenderingContext ||
1978 OriginalSVGElementInstance &&
1979 object instanceof OriginalSVGElementInstance;
1980 }
1981
1982 /**
1983 * Wraps a node in a WrapperNode. If there already exists a wrapper for the
1984 * |node| that wrapper is returned instead.
1985 * @param {Node} node
1986 * @return {WrapperNode}
1987 */
1988 function wrap(impl) {
1989 if (impl === null)
1990 return null;
1991
1992 assert(isNative(impl));
1993 return impl.polymerWrapper_ ||
1994 (impl.polymerWrapper_ = new (getWrapperConstructor(impl))(impl));
1995 }
1996
1997 /**
1998 * Unwraps a wrapper and returns the node it is wrapping.
1999 * @param {WrapperNode} wrapper
2000 * @return {Node}
2001 */
2002 function unwrap(wrapper) {
2003 if (wrapper === null)
2004 return null;
2005 assert(isWrapper(wrapper));
2006 return wrapper.impl;
2007 }
2008
2009 /**
2010 * Unwraps object if it is a wrapper.
2011 * @param {Object} object
2012 * @return {Object} The native implementation object.
2013 */
2014 function unwrapIfNeeded(object) {
2015 return object && isWrapper(object) ? unwrap(object) : object;
2016 }
2017
2018 /**
2019 * Wraps object if it is not a wrapper.
2020 * @param {Object} object
2021 * @return {Object} The wrapper for object.
2022 */
2023 function wrapIfNeeded(object) {
2024 return object && !isWrapper(object) ? wrap(object) : object;
2025 }
2026
2027 /**
2028 * Overrides the current wrapper (if any) for node.
2029 * @param {Node} node
2030 * @param {WrapperNode=} wrapper If left out the wrapper will be created as
2031 * needed next time someone wraps the node.
2032 */
2033 function rewrap(node, wrapper) {
2034 if (wrapper === null)
2035 return;
2036 assert(isNative(node));
2037 assert(wrapper === undefined || isWrapper(wrapper));
2038 node.polymerWrapper_ = wrapper;
2039 }
2040
2041 function defineGetter(constructor, name, getter) {
2042 defineProperty(constructor.prototype, name, {
2043 get: getter,
2044 configurable: true,
2045 enumerable: true
2046 });
2047 }
2048
2049 function defineWrapGetter(constructor, name) {
2050 defineGetter(constructor, name, function() {
2051 return wrap(this.impl[name]);
2052 });
2053 }
2054
2055 /**
2056 * Forwards existing methods on the native object to the wrapper methods.
2057 * This does not wrap any of the arguments or the return value since the
2058 * wrapper implementation already takes care of that.
2059 * @param {Array.<Function>} constructors
2060 * @parem {Array.<string>} names
2061 */
2062 function forwardMethodsToWrapper(constructors, names) {
2063 constructors.forEach(function(constructor) {
2064 names.forEach(function(name) {
2065 constructor.prototype[name] = function() {
2066 var w = wrapIfNeeded(this);
2067 return w[name].apply(w, arguments);
2068 };
2069 });
2070 });
2071 }
2072
2073 scope.assert = assert;
2074 scope.constructorTable = constructorTable;
2075 scope.defineGetter = defineGetter;
2076 scope.defineWrapGetter = defineWrapGetter;
2077 scope.forwardMethodsToWrapper = forwardMethodsToWrapper;
2078 scope.isWrapper = isWrapper;
2079 scope.isWrapperFor = isWrapperFor;
2080 scope.mixin = mixin;
2081 scope.nativePrototypeTable = nativePrototypeTable;
2082 scope.oneOf = oneOf;
2083 scope.registerObject = registerObject;
2084 scope.registerWrapper = register;
2085 scope.rewrap = rewrap;
2086 scope.unwrap = unwrap;
2087 scope.unwrapIfNeeded = unwrapIfNeeded;
2088 scope.wrap = wrap;
2089 scope.wrapIfNeeded = wrapIfNeeded;
2090 scope.wrappers = wrappers;
2091
2092 })(window.ShadowDOMPolyfill);
2093
2094 /*
2095 * Copyright 2013 The Polymer Authors. All rights reserved.
2096 * Use of this source code is goverened by a BSD-style
2097 * license that can be found in the LICENSE file.
2098 */
2099
2100 (function(context) {
2101 'use strict';
2102
2103 var OriginalMutationObserver = window.MutationObserver;
2104 var callbacks = [];
2105 var pending = false;
2106 var timerFunc;
2107
2108 function handle() {
2109 pending = false;
2110 var copies = callbacks.slice(0);
2111 callbacks = [];
2112 for (var i = 0; i < copies.length; i++) {
2113 (0, copies[i])();
2114 }
2115 }
2116
2117 if (OriginalMutationObserver) {
2118 var counter = 1;
2119 var observer = new OriginalMutationObserver(handle);
2120 var textNode = document.createTextNode(counter);
2121 observer.observe(textNode, {characterData: true});
2122
2123 timerFunc = function() {
2124 counter = (counter + 1) % 2;
2125 textNode.data = counter;
2126 };
2127
2128 } else {
2129 timerFunc = window.setImmediate || window.setTimeout;
2130 }
2131
2132 function setEndOfMicrotask(func) {
2133 callbacks.push(func);
2134 if (pending)
2135 return;
2136 pending = true;
2137 timerFunc(handle, 0);
2138 }
2139
2140 context.setEndOfMicrotask = setEndOfMicrotask;
2141
2142 })(window.ShadowDOMPolyfill);
2143
2144 /*
2145 * Copyright 2013 The Polymer Authors. All rights reserved.
2146 * Use of this source code is goverened by a BSD-style
2147 * license that can be found in the LICENSE file.
2148 */
2149
2150 (function(scope) {
2151 'use strict';
2152
2153 var setEndOfMicrotask = scope.setEndOfMicrotask
2154 var wrapIfNeeded = scope.wrapIfNeeded
2155 var wrappers = scope.wrappers;
2156
2157 var registrationsTable = new WeakMap();
2158 var globalMutationObservers = [];
2159 var isScheduled = false;
2160
2161 function scheduleCallback(observer) {
2162 if (isScheduled)
2163 return;
2164 setEndOfMicrotask(notifyObservers);
2165 isScheduled = true;
2166 }
2167
2168 // http://dom.spec.whatwg.org/#mutation-observers
2169 function notifyObservers() {
2170 isScheduled = false;
2171
2172 do {
2173 var notifyList = globalMutationObservers.slice();
2174 var anyNonEmpty = false;
2175 for (var i = 0; i < notifyList.length; i++) {
2176 var mo = notifyList[i];
2177 var queue = mo.takeRecords();
2178 removeTransientObserversFor(mo);
2179 if (queue.length) {
2180 mo.callback_(queue, mo);
2181 anyNonEmpty = true;
2182 }
2183 }
2184 } while (anyNonEmpty);
2185 }
2186
2187 /**
2188 * @param {string} type
2189 * @param {Node} target
2190 * @constructor
2191 */
2192 function MutationRecord(type, target) {
2193 this.type = type;
2194 this.target = target;
2195 this.addedNodes = new wrappers.NodeList();
2196 this.removedNodes = new wrappers.NodeList();
2197 this.previousSibling = null;
2198 this.nextSibling = null;
2199 this.attributeName = null;
2200 this.attributeNamespace = null;
2201 this.oldValue = null;
2202 }
2203
2204 /**
2205 * Registers transient observers to ancestor and its ancesors for the node
2206 * which was removed.
2207 * @param {!Node} ancestor
2208 * @param {!Node} node
2209 */
2210 function registerTransientObservers(ancestor, node) {
2211 for (; ancestor; ancestor = ancestor.parentNode) {
2212 var registrations = registrationsTable.get(ancestor);
2213 if (!registrations)
2214 continue;
2215 for (var i = 0; i < registrations.length; i++) {
2216 var registration = registrations[i];
2217 if (registration.options.subtree)
2218 registration.addTransientObserver(node);
2219 }
2220 }
2221 }
2222
2223 function removeTransientObserversFor(observer) {
2224 for (var i = 0; i < observer.nodes_.length; i++) {
2225 var node = observer.nodes_[i];
2226 var registrations = registrationsTable.get(node);
2227 if (!registrations)
2228 return;
2229 for (var j = 0; j < registrations.length; j++) {
2230 var registration = registrations[j];
2231 if (registration.observer === observer)
2232 registration.removeTransientObservers();
2233 }
2234 }
2235 }
2236
2237 // http://dom.spec.whatwg.org/#queue-a-mutation-record
2238 function enqueueMutation(target, type, data) {
2239 // 1.
2240 var interestedObservers = Object.create(null);
2241 var associatedStrings = Object.create(null);
2242
2243 // 2.
2244 for (var node = target; node; node = node.parentNode) {
2245 // 3.
2246 var registrations = registrationsTable.get(node);
2247 if (!registrations)
2248 continue;
2249 for (var j = 0; j < registrations.length; j++) {
2250 var registration = registrations[j];
2251 var options = registration.options;
2252 // 1.
2253 if (node !== target && !options.subtree)
2254 continue;
2255
2256 // 2.
2257 if (type === 'attributes' && !options.attributes)
2258 continue;
2259
2260 // 3. If type is "attributes", options's attributeFilter is present, and
2261 // either options's attributeFilter does not contain name or namespace
2262 // is non-null, continue.
2263 if (type === 'attributes' && options.attributeFilter &&
2264 (data.namespace !== null ||
2265 options.attributeFilter.indexOf(data.name) === -1)) {
2266 continue;
2267 }
2268
2269 // 4.
2270 if (type === 'characterData' && !options.characterData)
2271 continue;
2272
2273 // 5.
2274 if (type === 'childList' && !options.childList)
2275 continue;
2276
2277 // 6.
2278 var observer = registration.observer;
2279 interestedObservers[observer.uid_] = observer;
2280
2281 // 7. If either type is "attributes" and options's attributeOldValue is
2282 // true, or type is "characterData" and options's characterDataOldValue
2283 // is true, set the paired string of registered observer's observer in
2284 // interested observers to oldValue.
2285 if (type === 'attributes' && options.attributeOldValue ||
2286 type === 'characterData' && options.characterDataOldValue) {
2287 associatedStrings[observer.uid_] = data.oldValue;
2288 }
2289 }
2290 }
2291
2292 var anyRecordsEnqueued = false;
2293
2294 // 4.
2295 for (var uid in interestedObservers) {
2296 var observer = interestedObservers[uid];
2297 var record = new MutationRecord(type, target);
2298
2299 // 2.
2300 if ('name' in data && 'namespace' in data) {
2301 record.attributeName = data.name;
2302 record.attributeNamespace = data.namespace;
2303 }
2304
2305 // 3.
2306 if (data.addedNodes)
2307 record.addedNodes = data.addedNodes;
2308
2309 // 4.
2310 if (data.removedNodes)
2311 record.removedNodes = data.removedNodes;
2312
2313 // 5.
2314 if (data.previousSibling)
2315 record.previousSibling = data.previousSibling;
2316
2317 // 6.
2318 if (data.nextSibling)
2319 record.nextSibling = data.nextSibling;
2320
2321 // 7.
2322 if (associatedStrings[uid] !== undefined)
2323 record.oldValue = associatedStrings[uid];
2324
2325 // 8.
2326 observer.records_.push(record);
2327
2328 anyRecordsEnqueued = true;
2329 }
2330
2331 if (anyRecordsEnqueued)
2332 scheduleCallback();
2333 }
2334
2335 var slice = Array.prototype.slice;
2336
2337 /**
2338 * @param {!Object} options
2339 * @constructor
2340 */
2341 function MutationObserverOptions(options) {
2342 this.childList = !!options.childList;
2343 this.subtree = !!options.subtree;
2344
2345 // 1. If either options' attributeOldValue or attributeFilter is present
2346 // and options' attributes is omitted, set options' attributes to true.
2347 if (!('attributes' in options) &&
2348 ('attributeOldValue' in options || 'attributeFilter' in options)) {
2349 this.attributes = true;
2350 } else {
2351 this.attributes = !!options.attributes;
2352 }
2353
2354 // 2. If options' characterDataOldValue is present and options'
2355 // characterData is omitted, set options' characterData to true.
2356 if ('characterDataOldValue' in options && !('characterData' in options))
2357 this.characterData = true;
2358 else
2359 this.characterData = !!options.characterData;
2360
2361 // 3. & 4.
2362 if (!this.attributes &&
2363 (options.attributeOldValue || 'attributeFilter' in options) ||
2364 // 5.
2365 !this.characterData && options.characterDataOldValue) {
2366 throw new TypeError();
2367 }
2368
2369 this.characterData = !!options.characterData;
2370 this.attributeOldValue = !!options.attributeOldValue;
2371 this.characterDataOldValue = !!options.characterDataOldValue;
2372 if ('attributeFilter' in options) {
2373 if (options.attributeFilter == null ||
2374 typeof options.attributeFilter !== 'object') {
2375 throw new TypeError();
2376 }
2377 this.attributeFilter = slice.call(options.attributeFilter);
2378 } else {
2379 this.attributeFilter = null;
2380 }
2381 }
2382
2383 var uidCounter = 0;
2384
2385 /**
2386 * The class that maps to the DOM MutationObserver interface.
2387 * @param {Function} callback.
2388 * @constructor
2389 */
2390 function MutationObserver(callback) {
2391 this.callback_ = callback;
2392 this.nodes_ = [];
2393 this.records_ = [];
2394 this.uid_ = ++uidCounter;
2395
2396 // This will leak. There is no way to implement this without WeakRefs :'(
2397 globalMutationObservers.push(this);
2398 }
2399
2400 MutationObserver.prototype = {
2401 // http://dom.spec.whatwg.org/#dom-mutationobserver-observe
2402 observe: function(target, options) {
2403 target = wrapIfNeeded(target);
2404
2405 var newOptions = new MutationObserverOptions(options);
2406
2407 // 6.
2408 var registration;
2409 var registrations = registrationsTable.get(target);
2410 if (!registrations)
2411 registrationsTable.set(target, registrations = []);
2412
2413 for (var i = 0; i < registrations.length; i++) {
2414 if (registrations[i].observer === this) {
2415 registration = registrations[i];
2416 // 6.1.
2417 registration.removeTransientObservers();
2418 // 6.2.
2419 registration.options = newOptions;
2420 }
2421 }
2422
2423 // 7.
2424 if (!registration) {
2425 registration = new Registration(this, target, newOptions);
2426 registrations.push(registration);
2427 this.nodes_.push(target);
2428 }
2429 },
2430
2431 // http://dom.spec.whatwg.org/#dom-mutationobserver-disconnect
2432 disconnect: function() {
2433 this.nodes_.forEach(function(node) {
2434 var registrations = registrationsTable.get(node);
2435 for (var i = 0; i < registrations.length; i++) {
2436 var registration = registrations[i];
2437 if (registration.observer === this) {
2438 registrations.splice(i, 1);
2439 // Each node can only have one registered observer associated with
2440 // this observer.
2441 break;
2442 }
2443 }
2444 }, this);
2445 this.records_ = [];
2446 },
2447
2448 takeRecords: function() {
2449 var copyOfRecords = this.records_;
2450 this.records_ = [];
2451 return copyOfRecords;
2452 }
2453 };
2454
2455 /**
2456 * Class used to represent a registered observer.
2457 * @param {MutationObserver} observer
2458 * @param {Node} target
2459 * @param {MutationObserverOptions} options
2460 * @constructor
2461 */
2462 function Registration(observer, target, options) {
2463 this.observer = observer;
2464 this.target = target;
2465 this.options = options;
2466 this.transientObservedNodes = [];
2467 }
2468
2469 Registration.prototype = {
2470 /**
2471 * Adds a transient observer on node. The transient observer gets removed
2472 * next time we deliver the change records.
2473 * @param {Node} node
2474 */
2475 addTransientObserver: function(node) {
2476 // Don't add transient observers on the target itself. We already have all
2477 // the required listeners set up on the target.
2478 if (node === this.target)
2479 return;
2480
2481 this.transientObservedNodes.push(node);
2482 var registrations = registrationsTable.get(node);
2483 if (!registrations)
2484 registrationsTable.set(node, registrations = []);
2485
2486 // We know that registrations does not contain this because we already
2487 // checked if node === this.target.
2488 registrations.push(this);
2489 },
2490
2491 removeTransientObservers: function() {
2492 var transientObservedNodes = this.transientObservedNodes;
2493 this.transientObservedNodes = [];
2494
2495 for (var i = 0; i < transientObservedNodes.length; i++) {
2496 var node = transientObservedNodes[i];
2497 var registrations = registrationsTable.get(node);
2498 for (var j = 0; j < registrations.length; j++) {
2499 if (registrations[j] === this) {
2500 registrations.splice(j, 1);
2501 // Each node can only have one registered observer associated with
2502 // this observer.
2503 break;
2504 }
2505 }
2506 }
2507 }
2508 };
2509
2510 scope.enqueueMutation = enqueueMutation;
2511 scope.registerTransientObservers = registerTransientObservers;
2512 scope.wrappers.MutationObserver = MutationObserver;
2513 scope.wrappers.MutationRecord = MutationRecord;
2514
2515 })(window.ShadowDOMPolyfill);
2516
2517 /**
2518 * Copyright 2014 The Polymer Authors. All rights reserved.
2519 * Use of this source code is goverened by a BSD-style
2520 * license that can be found in the LICENSE file.
2521 */
2522
2523 (function(scope) {
2524 'use strict';
2525
2526 /**
2527 * A tree scope represents the root of a tree. All nodes in a tree point to
2528 * the same TreeScope object. The tree scope of a node get set the first time
2529 * it is accessed or when a node is added or remove to a tree.
2530 * @constructor
2531 */
2532 function TreeScope(root, parent) {
2533 this.root = root;
2534 this.parent = parent;
2535 }
2536
2537 TreeScope.prototype = {
2538 get renderer() {
2539 if (this.root instanceof scope.wrappers.ShadowRoot) {
2540 return scope.getRendererForHost(this.root.host);
2541 }
2542 return null;
2543 },
2544
2545 contains: function(treeScope) {
2546 for (; treeScope; treeScope = treeScope.parent) {
2547 if (treeScope === this)
2548 return true;
2549 }
2550 return false;
2551 }
2552 };
2553
2554 function setTreeScope(node, treeScope) {
2555 if (node.treeScope_ !== treeScope) {
2556 node.treeScope_ = treeScope;
2557 for (var sr = node.shadowRoot; sr; sr = sr.olderShadowRoot) {
2558 sr.treeScope_.parent = treeScope;
2559 }
2560 for (var child = node.firstChild; child; child = child.nextSibling) {
2561 setTreeScope(child, treeScope);
2562 }
2563 }
2564 }
2565
2566 function getTreeScope(node) {
2567 if (node.treeScope_)
2568 return node.treeScope_;
2569 var parent = node.parentNode;
2570 var treeScope;
2571 if (parent)
2572 treeScope = getTreeScope(parent);
2573 else
2574 treeScope = new TreeScope(node, null);
2575 return node.treeScope_ = treeScope;
2576 }
2577
2578 scope.TreeScope = TreeScope;
2579 scope.getTreeScope = getTreeScope;
2580 scope.setTreeScope = setTreeScope;
2581
2582 })(window.ShadowDOMPolyfill);
2583
2584 // Copyright 2013 The Polymer Authors. All rights reserved.
2585 // Use of this source code is goverened by a BSD-style
2586 // license that can be found in the LICENSE file.
2587
2588 (function(scope) {
2589 'use strict';
2590
2591 var forwardMethodsToWrapper = scope.forwardMethodsToWrapper;
2592 var getTreeScope = scope.getTreeScope;
2593 var mixin = scope.mixin;
2594 var registerWrapper = scope.registerWrapper;
2595 var unwrap = scope.unwrap;
2596 var wrap = scope.wrap;
2597 var wrappers = scope.wrappers;
2598
2599 var wrappedFuns = new WeakMap();
2600 var listenersTable = new WeakMap();
2601 var handledEventsTable = new WeakMap();
2602 var currentlyDispatchingEvents = new WeakMap();
2603 var targetTable = new WeakMap();
2604 var currentTargetTable = new WeakMap();
2605 var relatedTargetTable = new WeakMap();
2606 var eventPhaseTable = new WeakMap();
2607 var stopPropagationTable = new WeakMap();
2608 var stopImmediatePropagationTable = new WeakMap();
2609 var eventHandlersTable = new WeakMap();
2610 var eventPathTable = new WeakMap();
2611
2612 function isShadowRoot(node) {
2613 return node instanceof wrappers.ShadowRoot;
2614 }
2615
2616 function isInsertionPoint(node) {
2617 var localName = node.localName;
2618 return localName === 'content' || localName === 'shadow';
2619 }
2620
2621 function isShadowHost(node) {
2622 return !!node.shadowRoot;
2623 }
2624
2625 function getEventParent(node) {
2626 var dv;
2627 return node.parentNode || (dv = node.defaultView) && wrap(dv) || null;
2628 }
2629
2630 // https://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/shadow/index.html#df n-adjusted-parent
2631 function calculateParents(node, context, ancestors) {
2632 if (ancestors.length)
2633 return ancestors.shift();
2634
2635 // 1.
2636 if (isShadowRoot(node))
2637 return getInsertionParent(node) || node.host;
2638
2639 // 2.
2640 var eventParents = scope.eventParentsTable.get(node);
2641 if (eventParents) {
2642 // Copy over the remaining event parents for next iteration.
2643 for (var i = 1; i < eventParents.length; i++) {
2644 ancestors[i - 1] = eventParents[i];
2645 }
2646 return eventParents[0];
2647 }
2648
2649 // 3.
2650 if (context && isInsertionPoint(node)) {
2651 var parentNode = node.parentNode;
2652 if (parentNode && isShadowHost(parentNode)) {
2653 var trees = scope.getShadowTrees(parentNode);
2654 var p = getInsertionParent(context);
2655 for (var i = 0; i < trees.length; i++) {
2656 if (trees[i].contains(p))
2657 return p;
2658 }
2659 }
2660 }
2661
2662 return getEventParent(node);
2663 }
2664
2665 // https://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/shadow/index.html#ev ent-retargeting
2666 function retarget(node) {
2667 var stack = []; // 1.
2668 var ancestor = node; // 2.
2669 var targets = [];
2670 var ancestors = [];
2671 while (ancestor) { // 3.
2672 var context = null; // 3.2.
2673 // TODO(arv): Change order of these. If the stack is empty we always end
2674 // up pushing ancestor, no matter what.
2675 if (isInsertionPoint(ancestor)) { // 3.1.
2676 context = topMostNotInsertionPoint(stack); // 3.1.1.
2677 var top = stack[stack.length - 1] || ancestor; // 3.1.2.
2678 stack.push(top);
2679 } else if (!stack.length) {
2680 stack.push(ancestor); // 3.3.
2681 }
2682 var target = stack[stack.length - 1]; // 3.4.
2683 targets.push({target: target, currentTarget: ancestor}); // 3.5.
2684 if (isShadowRoot(ancestor)) // 3.6.
2685 stack.pop(); // 3.6.1.
2686
2687 ancestor = calculateParents(ancestor, context, ancestors); // 3.7.
2688 }
2689 return targets;
2690 }
2691
2692 function topMostNotInsertionPoint(stack) {
2693 for (var i = stack.length - 1; i >= 0; i--) {
2694 if (!isInsertionPoint(stack[i]))
2695 return stack[i];
2696 }
2697 return null;
2698 }
2699
2700 // https://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/shadow/index.html#df n-adjusted-related-target
2701 function adjustRelatedTarget(target, related) {
2702 var ancestors = [];
2703 while (target) { // 3.
2704 var stack = []; // 3.1.
2705 var ancestor = related; // 3.2.
2706 var last = undefined; // 3.3. Needs to be reset every iteration.
2707 while (ancestor) {
2708 var context = null;
2709 if (!stack.length) {
2710 stack.push(ancestor);
2711 } else {
2712 if (isInsertionPoint(ancestor)) { // 3.4.3.
2713 context = topMostNotInsertionPoint(stack);
2714 // isDistributed is more general than checking whether last is
2715 // assigned into ancestor.
2716 if (isDistributed(last)) { // 3.4.3.2.
2717 var head = stack[stack.length - 1];
2718 stack.push(head);
2719 }
2720 }
2721 }
2722
2723 if (inSameTree(ancestor, target)) // 3.4.4.
2724 return stack[stack.length - 1];
2725
2726 if (isShadowRoot(ancestor)) // 3.4.5.
2727 stack.pop();
2728
2729 last = ancestor; // 3.4.6.
2730 ancestor = calculateParents(ancestor, context, ancestors); // 3.4.7.
2731 }
2732 if (isShadowRoot(target)) // 3.5.
2733 target = target.host;
2734 else
2735 target = target.parentNode; // 3.6.
2736 }
2737 }
2738
2739 function getInsertionParent(node) {
2740 return scope.insertionParentTable.get(node);
2741 }
2742
2743 function isDistributed(node) {
2744 return getInsertionParent(node);
2745 }
2746
2747 function inSameTree(a, b) {
2748 return getTreeScope(a) === getTreeScope(b);
2749 }
2750
2751 function dispatchOriginalEvent(originalEvent) {
2752 // Make sure this event is only dispatched once.
2753 if (handledEventsTable.get(originalEvent))
2754 return;
2755 handledEventsTable.set(originalEvent, true);
2756 dispatchEvent(wrap(originalEvent), wrap(originalEvent.target));
2757 }
2758
2759 function isLoadLikeEvent(event) {
2760 switch (event.type) {
2761 case 'beforeunload':
2762 case 'load':
2763 case 'unload':
2764 return true;
2765 }
2766 return false;
2767 }
2768
2769 function dispatchEvent(event, originalWrapperTarget) {
2770 if (currentlyDispatchingEvents.get(event))
2771 throw new Error('InvalidStateError')
2772 currentlyDispatchingEvents.set(event, true);
2773
2774 // Render to ensure that the event path is correct.
2775 scope.renderAllPending();
2776 var eventPath = retarget(originalWrapperTarget);
2777
2778 // For window "load" events the "load" event is dispatched at the window but
2779 // the target is set to the document.
2780 //
2781 // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-end.html# the-end
2782 //
2783 // TODO(arv): Find a less hacky way to do this.
2784 if (eventPath.length === 2 &&
2785 eventPath[0].target instanceof wrappers.Document &&
2786 isLoadLikeEvent(event)) {
2787 eventPath.shift();
2788 }
2789
2790 eventPathTable.set(event, eventPath);
2791
2792 if (dispatchCapturing(event, eventPath)) {
2793 if (dispatchAtTarget(event, eventPath)) {
2794 dispatchBubbling(event, eventPath);
2795 }
2796 }
2797
2798 eventPhaseTable.set(event, Event.NONE);
2799 currentTargetTable.delete(event, null);
2800 currentlyDispatchingEvents.delete(event);
2801
2802 return event.defaultPrevented;
2803 }
2804
2805 function dispatchCapturing(event, eventPath) {
2806 var phase;
2807
2808 for (var i = eventPath.length - 1; i > 0; i--) {
2809 var target = eventPath[i].target;
2810 var currentTarget = eventPath[i].currentTarget;
2811 if (target === currentTarget)
2812 continue;
2813
2814 phase = Event.CAPTURING_PHASE;
2815 if (!invoke(eventPath[i], event, phase))
2816 return false;
2817 }
2818
2819 return true;
2820 }
2821
2822 function dispatchAtTarget(event, eventPath) {
2823 var phase = Event.AT_TARGET;
2824 return invoke(eventPath[0], event, phase);
2825 }
2826
2827 function dispatchBubbling(event, eventPath) {
2828 var bubbles = event.bubbles;
2829 var phase;
2830
2831 for (var i = 1; i < eventPath.length; i++) {
2832 var target = eventPath[i].target;
2833 var currentTarget = eventPath[i].currentTarget;
2834 if (target === currentTarget)
2835 phase = Event.AT_TARGET;
2836 else if (bubbles && !stopImmediatePropagationTable.get(event))
2837 phase = Event.BUBBLING_PHASE;
2838 else
2839 continue;
2840
2841 if (!invoke(eventPath[i], event, phase))
2842 return;
2843 }
2844 }
2845
2846 function invoke(tuple, event, phase) {
2847 var target = tuple.target;
2848 var currentTarget = tuple.currentTarget;
2849
2850 var listeners = listenersTable.get(currentTarget);
2851 if (!listeners)
2852 return true;
2853
2854 if ('relatedTarget' in event) {
2855 var originalEvent = unwrap(event);
2856 var unwrappedRelatedTarget = originalEvent.relatedTarget;
2857
2858 // X-Tag sets relatedTarget on a CustomEvent. If they do that there is no
2859 // way to have relatedTarget return the adjusted target but worse is that
2860 // the originalEvent might not have a relatedTarget so we hit an assert
2861 // when we try to wrap it.
2862 if (unwrappedRelatedTarget) {
2863 // In IE we can get objects that are not EventTargets at this point.
2864 // Safari does not have an EventTarget interface so revert to checking
2865 // for addEventListener as an approximation.
2866 if (unwrappedRelatedTarget instanceof Object &&
2867 unwrappedRelatedTarget.addEventListener) {
2868 var relatedTarget = wrap(unwrappedRelatedTarget);
2869
2870 var adjusted = adjustRelatedTarget(currentTarget, relatedTarget);
2871 if (adjusted === target)
2872 return true;
2873 } else {
2874 adjusted = null;
2875 }
2876 relatedTargetTable.set(event, adjusted);
2877 }
2878 }
2879
2880 eventPhaseTable.set(event, phase);
2881 var type = event.type;
2882
2883 var anyRemoved = false;
2884 targetTable.set(event, target);
2885 currentTargetTable.set(event, currentTarget);
2886
2887 for (var i = 0; i < listeners.length; i++) {
2888 var listener = listeners[i];
2889 if (listener.removed) {
2890 anyRemoved = true;
2891 continue;
2892 }
2893
2894 if (listener.type !== type ||
2895 !listener.capture && phase === Event.CAPTURING_PHASE ||
2896 listener.capture && phase === Event.BUBBLING_PHASE) {
2897 continue;
2898 }
2899
2900 try {
2901 if (typeof listener.handler === 'function')
2902 listener.handler.call(currentTarget, event);
2903 else
2904 listener.handler.handleEvent(event);
2905
2906 if (stopImmediatePropagationTable.get(event))
2907 return false;
2908
2909 } catch (ex) {
2910 if (window.onerror)
2911 window.onerror(ex.message);
2912 else
2913 console.error(ex, ex.stack);
2914 }
2915 }
2916
2917 if (anyRemoved) {
2918 var copy = listeners.slice();
2919 listeners.length = 0;
2920 for (var i = 0; i < copy.length; i++) {
2921 if (!copy[i].removed)
2922 listeners.push(copy[i]);
2923 }
2924 }
2925
2926 return !stopPropagationTable.get(event);
2927 }
2928
2929 function Listener(type, handler, capture) {
2930 this.type = type;
2931 this.handler = handler;
2932 this.capture = Boolean(capture);
2933 }
2934 Listener.prototype = {
2935 equals: function(that) {
2936 return this.handler === that.handler && this.type === that.type &&
2937 this.capture === that.capture;
2938 },
2939 get removed() {
2940 return this.handler === null;
2941 },
2942 remove: function() {
2943 this.handler = null;
2944 }
2945 };
2946
2947 var OriginalEvent = window.Event;
2948 OriginalEvent.prototype.polymerBlackList_ = {
2949 returnValue: true,
2950 // TODO(arv): keyLocation is part of KeyboardEvent but Firefox does not
2951 // support constructable KeyboardEvent so we keep it here for now.
2952 keyLocation: true
2953 };
2954
2955 /**
2956 * Creates a new Event wrapper or wraps an existin native Event object.
2957 * @param {string|Event} type
2958 * @param {Object=} options
2959 * @constructor
2960 */
2961 function Event(type, options) {
2962 if (type instanceof OriginalEvent) {
2963 var impl = type;
2964 if (!OriginalBeforeUnloadEvent && impl.type === 'beforeunload')
2965 return new BeforeUnloadEvent(impl);
2966 this.impl = impl;
2967 } else {
2968 return wrap(constructEvent(OriginalEvent, 'Event', type, options));
2969 }
2970 }
2971 Event.prototype = {
2972 get target() {
2973 return targetTable.get(this);
2974 },
2975 get currentTarget() {
2976 return currentTargetTable.get(this);
2977 },
2978 get eventPhase() {
2979 return eventPhaseTable.get(this);
2980 },
2981 get path() {
2982 var nodeList = new wrappers.NodeList();
2983 var eventPath = eventPathTable.get(this);
2984 if (eventPath) {
2985 var index = 0;
2986 var lastIndex = eventPath.length - 1;
2987 var baseRoot = getTreeScope(currentTargetTable.get(this));
2988
2989 for (var i = 0; i <= lastIndex; i++) {
2990 var currentTarget = eventPath[i].currentTarget;
2991 var currentRoot = getTreeScope(currentTarget);
2992 if (currentRoot.contains(baseRoot) &&
2993 // Make sure we do not add Window to the path.
2994 (i !== lastIndex || currentTarget instanceof wrappers.Node)) {
2995 nodeList[index++] = currentTarget;
2996 }
2997 }
2998 nodeList.length = index;
2999 }
3000 return nodeList;
3001 },
3002 stopPropagation: function() {
3003 stopPropagationTable.set(this, true);
3004 },
3005 stopImmediatePropagation: function() {
3006 stopPropagationTable.set(this, true);
3007 stopImmediatePropagationTable.set(this, true);
3008 }
3009 };
3010 registerWrapper(OriginalEvent, Event, document.createEvent('Event'));
3011
3012 function unwrapOptions(options) {
3013 if (!options || !options.relatedTarget)
3014 return options;
3015 return Object.create(options, {
3016 relatedTarget: {value: unwrap(options.relatedTarget)}
3017 });
3018 }
3019
3020 function registerGenericEvent(name, SuperEvent, prototype) {
3021 var OriginalEvent = window[name];
3022 var GenericEvent = function(type, options) {
3023 if (type instanceof OriginalEvent)
3024 this.impl = type;
3025 else
3026 return wrap(constructEvent(OriginalEvent, name, type, options));
3027 };
3028 GenericEvent.prototype = Object.create(SuperEvent.prototype);
3029 if (prototype)
3030 mixin(GenericEvent.prototype, prototype);
3031 if (OriginalEvent) {
3032 // - Old versions of Safari fails on new FocusEvent (and others?).
3033 // - IE does not support event constructors.
3034 // - createEvent('FocusEvent') throws in Firefox.
3035 // => Try the best practice solution first and fallback to the old way
3036 // if needed.
3037 try {
3038 registerWrapper(OriginalEvent, GenericEvent, new OriginalEvent('temp'));
3039 } catch (ex) {
3040 registerWrapper(OriginalEvent, GenericEvent,
3041 document.createEvent(name));
3042 }
3043 }
3044 return GenericEvent;
3045 }
3046
3047 var UIEvent = registerGenericEvent('UIEvent', Event);
3048 var CustomEvent = registerGenericEvent('CustomEvent', Event);
3049
3050 var relatedTargetProto = {
3051 get relatedTarget() {
3052 var relatedTarget = relatedTargetTable.get(this);
3053 // relatedTarget can be null.
3054 if (relatedTarget !== undefined)
3055 return relatedTarget;
3056 return wrap(unwrap(this).relatedTarget);
3057 }
3058 };
3059
3060 function getInitFunction(name, relatedTargetIndex) {
3061 return function() {
3062 arguments[relatedTargetIndex] = unwrap(arguments[relatedTargetIndex]);
3063 var impl = unwrap(this);
3064 impl[name].apply(impl, arguments);
3065 };
3066 }
3067
3068 var mouseEventProto = mixin({
3069 initMouseEvent: getInitFunction('initMouseEvent', 14)
3070 }, relatedTargetProto);
3071
3072 var focusEventProto = mixin({
3073 initFocusEvent: getInitFunction('initFocusEvent', 5)
3074 }, relatedTargetProto);
3075
3076 var MouseEvent = registerGenericEvent('MouseEvent', UIEvent, mouseEventProto);
3077 var FocusEvent = registerGenericEvent('FocusEvent', UIEvent, focusEventProto);
3078
3079 // In case the browser does not support event constructors we polyfill that
3080 // by calling `createEvent('Foo')` and `initFooEvent` where the arguments to
3081 // `initFooEvent` are derived from the registered default event init dict.
3082 var defaultInitDicts = Object.create(null);
3083
3084 var supportsEventConstructors = (function() {
3085 try {
3086 new window.FocusEvent('focus');
3087 } catch (ex) {
3088 return false;
3089 }
3090 return true;
3091 })();
3092
3093 /**
3094 * Constructs a new native event.
3095 */
3096 function constructEvent(OriginalEvent, name, type, options) {
3097 if (supportsEventConstructors)
3098 return new OriginalEvent(type, unwrapOptions(options));
3099
3100 // Create the arguments from the default dictionary.
3101 var event = unwrap(document.createEvent(name));
3102 var defaultDict = defaultInitDicts[name];
3103 var args = [type];
3104 Object.keys(defaultDict).forEach(function(key) {
3105 var v = options != null && key in options ?
3106 options[key] : defaultDict[key];
3107 if (key === 'relatedTarget')
3108 v = unwrap(v);
3109 args.push(v);
3110 });
3111 event['init' + name].apply(event, args);
3112 return event;
3113 }
3114
3115 if (!supportsEventConstructors) {
3116 var configureEventConstructor = function(name, initDict, superName) {
3117 if (superName) {
3118 var superDict = defaultInitDicts[superName];
3119 initDict = mixin(mixin({}, superDict), initDict);
3120 }
3121
3122 defaultInitDicts[name] = initDict;
3123 };
3124
3125 // The order of the default event init dictionary keys is important, the
3126 // arguments to initFooEvent is derived from that.
3127 configureEventConstructor('Event', {bubbles: false, cancelable: false});
3128 configureEventConstructor('CustomEvent', {detail: null}, 'Event');
3129 configureEventConstructor('UIEvent', {view: null, detail: 0}, 'Event');
3130 configureEventConstructor('MouseEvent', {
3131 screenX: 0,
3132 screenY: 0,
3133 clientX: 0,
3134 clientY: 0,
3135 ctrlKey: false,
3136 altKey: false,
3137 shiftKey: false,
3138 metaKey: false,
3139 button: 0,
3140 relatedTarget: null
3141 }, 'UIEvent');
3142 configureEventConstructor('FocusEvent', {relatedTarget: null}, 'UIEvent');
3143 }
3144
3145 // Safari 7 does not yet have BeforeUnloadEvent.
3146 // https://bugs.webkit.org/show_bug.cgi?id=120849
3147 var OriginalBeforeUnloadEvent = window.BeforeUnloadEvent;
3148
3149 function BeforeUnloadEvent(impl) {
3150 Event.call(this, impl);
3151 }
3152 BeforeUnloadEvent.prototype = Object.create(Event.prototype);
3153 mixin(BeforeUnloadEvent.prototype, {
3154 get returnValue() {
3155 return this.impl.returnValue;
3156 },
3157 set returnValue(v) {
3158 this.impl.returnValue = v;
3159 }
3160 });
3161
3162 if (OriginalBeforeUnloadEvent)
3163 registerWrapper(OriginalBeforeUnloadEvent, BeforeUnloadEvent);
3164
3165 function isValidListener(fun) {
3166 if (typeof fun === 'function')
3167 return true;
3168 return fun && fun.handleEvent;
3169 }
3170
3171 function isMutationEvent(type) {
3172 switch (type) {
3173 case 'DOMAttrModified':
3174 case 'DOMAttributeNameChanged':
3175 case 'DOMCharacterDataModified':
3176 case 'DOMElementNameChanged':
3177 case 'DOMNodeInserted':
3178 case 'DOMNodeInsertedIntoDocument':
3179 case 'DOMNodeRemoved':
3180 case 'DOMNodeRemovedFromDocument':
3181 case 'DOMSubtreeModified':
3182 return true;
3183 }
3184 return false;
3185 }
3186
3187 var OriginalEventTarget = window.EventTarget;
3188
3189 /**
3190 * This represents a wrapper for an EventTarget.
3191 * @param {!EventTarget} impl The original event target.
3192 * @constructor
3193 */
3194 function EventTarget(impl) {
3195 this.impl = impl;
3196 }
3197
3198 // Node and Window have different internal type checks in WebKit so we cannot
3199 // use the same method as the original function.
3200 var methodNames = [
3201 'addEventListener',
3202 'removeEventListener',
3203 'dispatchEvent'
3204 ];
3205
3206 [Node, Window].forEach(function(constructor) {
3207 var p = constructor.prototype;
3208 methodNames.forEach(function(name) {
3209 Object.defineProperty(p, name + '_', {value: p[name]});
3210 });
3211 });
3212
3213 function getTargetToListenAt(wrapper) {
3214 if (wrapper instanceof wrappers.ShadowRoot)
3215 wrapper = wrapper.host;
3216 return unwrap(wrapper);
3217 }
3218
3219 EventTarget.prototype = {
3220 addEventListener: function(type, fun, capture) {
3221 if (!isValidListener(fun) || isMutationEvent(type))
3222 return;
3223
3224 var listener = new Listener(type, fun, capture);
3225 var listeners = listenersTable.get(this);
3226 if (!listeners) {
3227 listeners = [];
3228 listenersTable.set(this, listeners);
3229 } else {
3230 // Might have a duplicate.
3231 for (var i = 0; i < listeners.length; i++) {
3232 if (listener.equals(listeners[i]))
3233 return;
3234 }
3235 }
3236
3237 listeners.push(listener);
3238
3239 var target = getTargetToListenAt(this);
3240 target.addEventListener_(type, dispatchOriginalEvent, true);
3241 },
3242 removeEventListener: function(type, fun, capture) {
3243 capture = Boolean(capture);
3244 var listeners = listenersTable.get(this);
3245 if (!listeners)
3246 return;
3247 var count = 0, found = false;
3248 for (var i = 0; i < listeners.length; i++) {
3249 if (listeners[i].type === type && listeners[i].capture === capture) {
3250 count++;
3251 if (listeners[i].handler === fun) {
3252 found = true;
3253 listeners[i].remove();
3254 }
3255 }
3256 }
3257
3258 if (found && count === 1) {
3259 var target = getTargetToListenAt(this);
3260 target.removeEventListener_(type, dispatchOriginalEvent, true);
3261 }
3262 },
3263 dispatchEvent: function(event) {
3264 // We want to use the native dispatchEvent because it triggers the default
3265 // actions (like checking a checkbox). However, if there are no listeners
3266 // in the composed tree then there are no events that will trigger and
3267 // listeners in the non composed tree that are part of the event path are
3268 // not notified.
3269 //
3270 // If we find out that there are no listeners in the composed tree we add
3271 // a temporary listener to the target which makes us get called back even
3272 // in that case.
3273
3274 var nativeEvent = unwrap(event);
3275 var eventType = nativeEvent.type;
3276
3277 // Allow dispatching the same event again. This is safe because if user
3278 // code calls this during an existing dispatch of the same event the
3279 // native dispatchEvent throws (that is required by the spec).
3280 handledEventsTable.set(nativeEvent, false);
3281
3282 // Force rendering since we prefer native dispatch and that works on the
3283 // composed tree.
3284 scope.renderAllPending();
3285
3286 var tempListener;
3287 if (!hasListenerInAncestors(this, eventType)) {
3288 tempListener = function() {};
3289 this.addEventListener(eventType, tempListener, true);
3290 }
3291
3292 try {
3293 return unwrap(this).dispatchEvent_(nativeEvent);
3294 } finally {
3295 if (tempListener)
3296 this.removeEventListener(eventType, tempListener, true);
3297 }
3298 }
3299 };
3300
3301 function hasListener(node, type) {
3302 var listeners = listenersTable.get(node);
3303 if (listeners) {
3304 for (var i = 0; i < listeners.length; i++) {
3305 if (!listeners[i].removed && listeners[i].type === type)
3306 return true;
3307 }
3308 }
3309 return false;
3310 }
3311
3312 function hasListenerInAncestors(target, type) {
3313 for (var node = unwrap(target); node; node = node.parentNode) {
3314 if (hasListener(wrap(node), type))
3315 return true;
3316 }
3317 return false;
3318 }
3319
3320 if (OriginalEventTarget)
3321 registerWrapper(OriginalEventTarget, EventTarget);
3322
3323 function wrapEventTargetMethods(constructors) {
3324 forwardMethodsToWrapper(constructors, methodNames);
3325 }
3326
3327 var originalElementFromPoint = document.elementFromPoint;
3328
3329 function elementFromPoint(self, document, x, y) {
3330 scope.renderAllPending();
3331
3332 var element = wrap(originalElementFromPoint.call(document.impl, x, y));
3333 var targets = retarget(element, this)
3334 for (var i = 0; i < targets.length; i++) {
3335 var target = targets[i];
3336 if (target.currentTarget === self)
3337 return target.target;
3338 }
3339 return null;
3340 }
3341
3342 /**
3343 * Returns a function that is to be used as a getter for `onfoo` properties.
3344 * @param {string} name
3345 * @return {Function}
3346 */
3347 function getEventHandlerGetter(name) {
3348 return function() {
3349 var inlineEventHandlers = eventHandlersTable.get(this);
3350 return inlineEventHandlers && inlineEventHandlers[name] &&
3351 inlineEventHandlers[name].value || null;
3352 };
3353 }
3354
3355 /**
3356 * Returns a function that is to be used as a setter for `onfoo` properties.
3357 * @param {string} name
3358 * @return {Function}
3359 */
3360 function getEventHandlerSetter(name) {
3361 var eventType = name.slice(2);
3362 return function(value) {
3363 var inlineEventHandlers = eventHandlersTable.get(this);
3364 if (!inlineEventHandlers) {
3365 inlineEventHandlers = Object.create(null);
3366 eventHandlersTable.set(this, inlineEventHandlers);
3367 }
3368
3369 var old = inlineEventHandlers[name];
3370 if (old)
3371 this.removeEventListener(eventType, old.wrapped, false);
3372
3373 if (typeof value === 'function') {
3374 var wrapped = function(e) {
3375 var rv = value.call(this, e);
3376 if (rv === false)
3377 e.preventDefault();
3378 else if (name === 'onbeforeunload' && typeof rv === 'string')
3379 e.returnValue = rv;
3380 // mouseover uses true for preventDefault but preventDefault for
3381 // mouseover is ignored by browsers these day.
3382 };
3383
3384 this.addEventListener(eventType, wrapped, false);
3385 inlineEventHandlers[name] = {
3386 value: value,
3387 wrapped: wrapped
3388 };
3389 }
3390 };
3391 }
3392
3393 scope.adjustRelatedTarget = adjustRelatedTarget;
3394 scope.elementFromPoint = elementFromPoint;
3395 scope.getEventHandlerGetter = getEventHandlerGetter;
3396 scope.getEventHandlerSetter = getEventHandlerSetter;
3397 scope.wrapEventTargetMethods = wrapEventTargetMethods;
3398 scope.wrappers.BeforeUnloadEvent = BeforeUnloadEvent;
3399 scope.wrappers.CustomEvent = CustomEvent;
3400 scope.wrappers.Event = Event;
3401 scope.wrappers.EventTarget = EventTarget;
3402 scope.wrappers.FocusEvent = FocusEvent;
3403 scope.wrappers.MouseEvent = MouseEvent;
3404 scope.wrappers.UIEvent = UIEvent;
3405
3406 })(window.ShadowDOMPolyfill);
3407
3408 // Copyright 2012 The Polymer Authors. All rights reserved.
3409 // Use of this source code is goverened by a BSD-style
3410 // license that can be found in the LICENSE file.
3411
3412 (function(scope) {
3413 'use strict';
3414
3415 var wrap = scope.wrap;
3416
3417 function nonEnum(obj, prop) {
3418 Object.defineProperty(obj, prop, {enumerable: false});
3419 }
3420
3421 function NodeList() {
3422 this.length = 0;
3423 nonEnum(this, 'length');
3424 }
3425 NodeList.prototype = {
3426 item: function(index) {
3427 return this[index];
3428 }
3429 };
3430 nonEnum(NodeList.prototype, 'item');
3431
3432 function wrapNodeList(list) {
3433 if (list == null)
3434 return list;
3435 var wrapperList = new NodeList();
3436 for (var i = 0, length = list.length; i < length; i++) {
3437 wrapperList[i] = wrap(list[i]);
3438 }
3439 wrapperList.length = length;
3440 return wrapperList;
3441 }
3442
3443 function addWrapNodeListMethod(wrapperConstructor, name) {
3444 wrapperConstructor.prototype[name] = function() {
3445 return wrapNodeList(this.impl[name].apply(this.impl, arguments));
3446 };
3447 }
3448
3449 scope.wrappers.NodeList = NodeList;
3450 scope.addWrapNodeListMethod = addWrapNodeListMethod;
3451 scope.wrapNodeList = wrapNodeList;
3452
3453 })(window.ShadowDOMPolyfill);
3454
3455 /*
3456 * Copyright 2014 The Polymer Authors. All rights reserved.
3457 * Use of this source code is goverened by a BSD-style
3458 * license that can be found in the LICENSE file.
3459 */
3460
3461 (function(scope) {
3462 'use strict';
3463
3464 // TODO(arv): Implement.
3465
3466 scope.wrapHTMLCollection = scope.wrapNodeList;
3467 scope.wrappers.HTMLCollection = scope.wrappers.NodeList;
3468
3469 })(window.ShadowDOMPolyfill);
3470
3471 /**
3472 * Copyright 2012 The Polymer Authors. All rights reserved.
3473 * Use of this source code is goverened by a BSD-style
3474 * license that can be found in the LICENSE file.
3475 */
3476
3477 (function(scope) {
3478 'use strict';
3479
3480 var EventTarget = scope.wrappers.EventTarget;
3481 var NodeList = scope.wrappers.NodeList;
3482 var TreeScope = scope.TreeScope;
3483 var assert = scope.assert;
3484 var defineWrapGetter = scope.defineWrapGetter;
3485 var enqueueMutation = scope.enqueueMutation;
3486 var getTreeScope = scope.getTreeScope;
3487 var isWrapper = scope.isWrapper;
3488 var mixin = scope.mixin;
3489 var registerTransientObservers = scope.registerTransientObservers;
3490 var registerWrapper = scope.registerWrapper;
3491 var setTreeScope = scope.setTreeScope;
3492 var unwrap = scope.unwrap;
3493 var unwrapIfNeeded = scope.unwrapIfNeeded;
3494 var wrap = scope.wrap;
3495 var wrapIfNeeded = scope.wrapIfNeeded;
3496 var wrappers = scope.wrappers;
3497
3498 function assertIsNodeWrapper(node) {
3499 assert(node instanceof Node);
3500 }
3501
3502 function createOneElementNodeList(node) {
3503 var nodes = new NodeList();
3504 nodes[0] = node;
3505 nodes.length = 1;
3506 return nodes;
3507 }
3508
3509 var surpressMutations = false;
3510
3511 /**
3512 * Called before node is inserted into a node to enqueue its removal from its
3513 * old parent.
3514 * @param {!Node} node The node that is about to be removed.
3515 * @param {!Node} parent The parent node that the node is being removed from.
3516 * @param {!NodeList} nodes The collected nodes.
3517 */
3518 function enqueueRemovalForInsertedNodes(node, parent, nodes) {
3519 enqueueMutation(parent, 'childList', {
3520 removedNodes: nodes,
3521 previousSibling: node.previousSibling,
3522 nextSibling: node.nextSibling
3523 });
3524 }
3525
3526 function enqueueRemovalForInsertedDocumentFragment(df, nodes) {
3527 enqueueMutation(df, 'childList', {
3528 removedNodes: nodes
3529 });
3530 }
3531
3532 /**
3533 * Collects nodes from a DocumentFragment or a Node for removal followed
3534 * by an insertion.
3535 *
3536 * This updates the internal pointers for node, previousNode and nextNode.
3537 */
3538 function collectNodes(node, parentNode, previousNode, nextNode) {
3539 if (node instanceof DocumentFragment) {
3540 var nodes = collectNodesForDocumentFragment(node);
3541
3542 // The extra loop is to work around bugs with DocumentFragments in IE.
3543 surpressMutations = true;
3544 for (var i = nodes.length - 1; i >= 0; i--) {
3545 node.removeChild(nodes[i]);
3546 nodes[i].parentNode_ = parentNode;
3547 }
3548 surpressMutations = false;
3549
3550 for (var i = 0; i < nodes.length; i++) {
3551 nodes[i].previousSibling_ = nodes[i - 1] || previousNode;
3552 nodes[i].nextSibling_ = nodes[i + 1] || nextNode;
3553 }
3554
3555 if (previousNode)
3556 previousNode.nextSibling_ = nodes[0];
3557 if (nextNode)
3558 nextNode.previousSibling_ = nodes[nodes.length - 1];
3559
3560 return nodes;
3561 }
3562
3563 var nodes = createOneElementNodeList(node);
3564 var oldParent = node.parentNode;
3565 if (oldParent) {
3566 // This will enqueue the mutation record for the removal as needed.
3567 oldParent.removeChild(node);
3568 }
3569
3570 node.parentNode_ = parentNode;
3571 node.previousSibling_ = previousNode;
3572 node.nextSibling_ = nextNode;
3573 if (previousNode)
3574 previousNode.nextSibling_ = node;
3575 if (nextNode)
3576 nextNode.previousSibling_ = node;
3577
3578 return nodes;
3579 }
3580
3581 function collectNodesNative(node) {
3582 if (node instanceof DocumentFragment)
3583 return collectNodesForDocumentFragment(node);
3584
3585 var nodes = createOneElementNodeList(node);
3586 var oldParent = node.parentNode;
3587 if (oldParent)
3588 enqueueRemovalForInsertedNodes(node, oldParent, nodes);
3589 return nodes;
3590 }
3591
3592 function collectNodesForDocumentFragment(node) {
3593 var nodes = new NodeList();
3594 var i = 0;
3595 for (var child = node.firstChild; child; child = child.nextSibling) {
3596 nodes[i++] = child;
3597 }
3598 nodes.length = i;
3599 enqueueRemovalForInsertedDocumentFragment(node, nodes);
3600 return nodes;
3601 }
3602
3603 function snapshotNodeList(nodeList) {
3604 // NodeLists are not live at the moment so just return the same object.
3605 return nodeList;
3606 }
3607
3608 // http://dom.spec.whatwg.org/#node-is-inserted
3609 function nodeWasAdded(node, treeScope) {
3610 setTreeScope(node, treeScope);
3611 node.nodeIsInserted_();
3612 }
3613
3614 function nodesWereAdded(nodes, parent) {
3615 var treeScope = getTreeScope(parent);
3616 for (var i = 0; i < nodes.length; i++) {
3617 nodeWasAdded(nodes[i], treeScope);
3618 }
3619 }
3620
3621 // http://dom.spec.whatwg.org/#node-is-removed
3622 function nodeWasRemoved(node) {
3623 setTreeScope(node, new TreeScope(node, null));
3624 }
3625
3626 function nodesWereRemoved(nodes) {
3627 for (var i = 0; i < nodes.length; i++) {
3628 nodeWasRemoved(nodes[i]);
3629 }
3630 }
3631
3632 function ensureSameOwnerDocument(parent, child) {
3633 var ownerDoc = parent.nodeType === Node.DOCUMENT_NODE ?
3634 parent : parent.ownerDocument;
3635 if (ownerDoc !== child.ownerDocument)
3636 ownerDoc.adoptNode(child);
3637 }
3638
3639 function adoptNodesIfNeeded(owner, nodes) {
3640 if (!nodes.length)
3641 return;
3642
3643 var ownerDoc = owner.ownerDocument;
3644
3645 // All nodes have the same ownerDocument when we get here.
3646 if (ownerDoc === nodes[0].ownerDocument)
3647 return;
3648
3649 for (var i = 0; i < nodes.length; i++) {
3650 scope.adoptNodeNoRemove(nodes[i], ownerDoc);
3651 }
3652 }
3653
3654 function unwrapNodesForInsertion(owner, nodes) {
3655 adoptNodesIfNeeded(owner, nodes);
3656 var length = nodes.length;
3657
3658 if (length === 1)
3659 return unwrap(nodes[0]);
3660
3661 var df = unwrap(owner.ownerDocument.createDocumentFragment());
3662 for (var i = 0; i < length; i++) {
3663 df.appendChild(unwrap(nodes[i]));
3664 }
3665 return df;
3666 }
3667
3668 function clearChildNodes(wrapper) {
3669 if (wrapper.firstChild_ !== undefined) {
3670 var child = wrapper.firstChild_;
3671 while (child) {
3672 var tmp = child;
3673 child = child.nextSibling_;
3674 tmp.parentNode_ = tmp.previousSibling_ = tmp.nextSibling_ = undefined;
3675 }
3676 }
3677 wrapper.firstChild_ = wrapper.lastChild_ = undefined;
3678 }
3679
3680 function removeAllChildNodes(wrapper) {
3681 if (wrapper.invalidateShadowRenderer()) {
3682 var childWrapper = wrapper.firstChild;
3683 while (childWrapper) {
3684 assert(childWrapper.parentNode === wrapper);
3685 var nextSibling = childWrapper.nextSibling;
3686 var childNode = unwrap(childWrapper);
3687 var parentNode = childNode.parentNode;
3688 if (parentNode)
3689 originalRemoveChild.call(parentNode, childNode);
3690 childWrapper.previousSibling_ = childWrapper.nextSibling_ =
3691 childWrapper.parentNode_ = null;
3692 childWrapper = nextSibling;
3693 }
3694 wrapper.firstChild_ = wrapper.lastChild_ = null;
3695 } else {
3696 var node = unwrap(wrapper);
3697 var child = node.firstChild;
3698 var nextSibling;
3699 while (child) {
3700 nextSibling = child.nextSibling;
3701 originalRemoveChild.call(node, child);
3702 child = nextSibling;
3703 }
3704 }
3705 }
3706
3707 function invalidateParent(node) {
3708 var p = node.parentNode;
3709 return p && p.invalidateShadowRenderer();
3710 }
3711
3712 function cleanupNodes(nodes) {
3713 for (var i = 0, n; i < nodes.length; i++) {
3714 n = nodes[i];
3715 n.parentNode.removeChild(n);
3716 }
3717 }
3718
3719 var originalImportNode = document.importNode;
3720 var originalCloneNode = window.Node.prototype.cloneNode;
3721
3722 function cloneNode(node, deep, opt_doc) {
3723 var clone;
3724 if (opt_doc)
3725 clone = wrap(originalImportNode.call(opt_doc, node.impl, false));
3726 else
3727 clone = wrap(originalCloneNode.call(node.impl, false));
3728
3729 if (deep) {
3730 for (var child = node.firstChild; child; child = child.nextSibling) {
3731 clone.appendChild(cloneNode(child, true, opt_doc));
3732 }
3733
3734 if (node instanceof wrappers.HTMLTemplateElement) {
3735 var cloneContent = clone.content;
3736 for (var child = node.content.firstChild;
3737 child;
3738 child = child.nextSibling) {
3739 cloneContent.appendChild(cloneNode(child, true, opt_doc));
3740 }
3741 }
3742 }
3743 // TODO(arv): Some HTML elements also clone other data like value.
3744 return clone;
3745 }
3746
3747 function contains(self, child) {
3748 if (!child || getTreeScope(self) !== getTreeScope(child))
3749 return false;
3750
3751 for (var node = child; node; node = node.parentNode) {
3752 if (node === self)
3753 return true;
3754 }
3755 return false;
3756 }
3757
3758 var OriginalNode = window.Node;
3759
3760 /**
3761 * This represents a wrapper of a native DOM node.
3762 * @param {!Node} original The original DOM node, aka, the visual DOM node.
3763 * @constructor
3764 * @extends {EventTarget}
3765 */
3766 function Node(original) {
3767 assert(original instanceof OriginalNode);
3768
3769 EventTarget.call(this, original);
3770
3771 // These properties are used to override the visual references with the
3772 // logical ones. If the value is undefined it means that the logical is the
3773 // same as the visual.
3774
3775 /**
3776 * @type {Node|undefined}
3777 * @private
3778 */
3779 this.parentNode_ = undefined;
3780
3781 /**
3782 * @type {Node|undefined}
3783 * @private
3784 */
3785 this.firstChild_ = undefined;
3786
3787 /**
3788 * @type {Node|undefined}
3789 * @private
3790 */
3791 this.lastChild_ = undefined;
3792
3793 /**
3794 * @type {Node|undefined}
3795 * @private
3796 */
3797 this.nextSibling_ = undefined;
3798
3799 /**
3800 * @type {Node|undefined}
3801 * @private
3802 */
3803 this.previousSibling_ = undefined;
3804
3805 this.treeScope_ = undefined;
3806 }
3807
3808 var OriginalDocumentFragment = window.DocumentFragment;
3809 var originalAppendChild = OriginalNode.prototype.appendChild;
3810 var originalCompareDocumentPosition =
3811 OriginalNode.prototype.compareDocumentPosition;
3812 var originalInsertBefore = OriginalNode.prototype.insertBefore;
3813 var originalRemoveChild = OriginalNode.prototype.removeChild;
3814 var originalReplaceChild = OriginalNode.prototype.replaceChild;
3815
3816 var isIe = /Trident/.test(navigator.userAgent);
3817
3818 var removeChildOriginalHelper = isIe ?
3819 function(parent, child) {
3820 try {
3821 originalRemoveChild.call(parent, child);
3822 } catch (ex) {
3823 if (!(parent instanceof OriginalDocumentFragment))
3824 throw ex;
3825 }
3826 } :
3827 function(parent, child) {
3828 originalRemoveChild.call(parent, child);
3829 };
3830
3831 Node.prototype = Object.create(EventTarget.prototype);
3832 mixin(Node.prototype, {
3833 appendChild: function(childWrapper) {
3834 return this.insertBefore(childWrapper, null);
3835 },
3836
3837 insertBefore: function(childWrapper, refWrapper) {
3838 assertIsNodeWrapper(childWrapper);
3839
3840 var refNode;
3841 if (refWrapper) {
3842 if (isWrapper(refWrapper)) {
3843 refNode = unwrap(refWrapper);
3844 } else {
3845 refNode = refWrapper;
3846 refWrapper = wrap(refNode);
3847 }
3848 } else {
3849 refWrapper = null;
3850 refNode = null;
3851 }
3852
3853 refWrapper && assert(refWrapper.parentNode === this);
3854
3855 var nodes;
3856 var previousNode =
3857 refWrapper ? refWrapper.previousSibling : this.lastChild;
3858
3859 var useNative = !this.invalidateShadowRenderer() &&
3860 !invalidateParent(childWrapper);
3861
3862 if (useNative)
3863 nodes = collectNodesNative(childWrapper);
3864 else
3865 nodes = collectNodes(childWrapper, this, previousNode, refWrapper);
3866
3867 if (useNative) {
3868 ensureSameOwnerDocument(this, childWrapper);
3869 clearChildNodes(this);
3870 originalInsertBefore.call(this.impl, unwrap(childWrapper), refNode);
3871 } else {
3872 if (!previousNode)
3873 this.firstChild_ = nodes[0];
3874 if (!refWrapper)
3875 this.lastChild_ = nodes[nodes.length - 1];
3876
3877 var parentNode = refNode ? refNode.parentNode : this.impl;
3878
3879 // insertBefore refWrapper no matter what the parent is?
3880 if (parentNode) {
3881 originalInsertBefore.call(parentNode,
3882 unwrapNodesForInsertion(this, nodes), refNode);
3883 } else {
3884 adoptNodesIfNeeded(this, nodes);
3885 }
3886 }
3887
3888 enqueueMutation(this, 'childList', {
3889 addedNodes: nodes,
3890 nextSibling: refWrapper,
3891 previousSibling: previousNode
3892 });
3893
3894 nodesWereAdded(nodes, this);
3895
3896 return childWrapper;
3897 },
3898
3899 removeChild: function(childWrapper) {
3900 assertIsNodeWrapper(childWrapper);
3901 if (childWrapper.parentNode !== this) {
3902 // IE has invalid DOM trees at times.
3903 var found = false;
3904 var childNodes = this.childNodes;
3905 for (var ieChild = this.firstChild; ieChild;
3906 ieChild = ieChild.nextSibling) {
3907 if (ieChild === childWrapper) {
3908 found = true;
3909 break;
3910 }
3911 }
3912 if (!found) {
3913 // TODO(arv): DOMException
3914 throw new Error('NotFoundError');
3915 }
3916 }
3917
3918 var childNode = unwrap(childWrapper);
3919 var childWrapperNextSibling = childWrapper.nextSibling;
3920 var childWrapperPreviousSibling = childWrapper.previousSibling;
3921
3922 if (this.invalidateShadowRenderer()) {
3923 // We need to remove the real node from the DOM before updating the
3924 // pointers. This is so that that mutation event is dispatched before
3925 // the pointers have changed.
3926 var thisFirstChild = this.firstChild;
3927 var thisLastChild = this.lastChild;
3928
3929 var parentNode = childNode.parentNode;
3930 if (parentNode)
3931 removeChildOriginalHelper(parentNode, childNode);
3932
3933 if (thisFirstChild === childWrapper)
3934 this.firstChild_ = childWrapperNextSibling;
3935 if (thisLastChild === childWrapper)
3936 this.lastChild_ = childWrapperPreviousSibling;
3937 if (childWrapperPreviousSibling)
3938 childWrapperPreviousSibling.nextSibling_ = childWrapperNextSibling;
3939 if (childWrapperNextSibling) {
3940 childWrapperNextSibling.previousSibling_ =
3941 childWrapperPreviousSibling;
3942 }
3943
3944 childWrapper.previousSibling_ = childWrapper.nextSibling_ =
3945 childWrapper.parentNode_ = undefined;
3946 } else {
3947 clearChildNodes(this);
3948 removeChildOriginalHelper(this.impl, childNode);
3949 }
3950
3951 if (!surpressMutations) {
3952 enqueueMutation(this, 'childList', {
3953 removedNodes: createOneElementNodeList(childWrapper),
3954 nextSibling: childWrapperNextSibling,
3955 previousSibling: childWrapperPreviousSibling
3956 });
3957 }
3958
3959 registerTransientObservers(this, childWrapper);
3960
3961 return childWrapper;
3962 },
3963
3964 replaceChild: function(newChildWrapper, oldChildWrapper) {
3965 assertIsNodeWrapper(newChildWrapper);
3966
3967 var oldChildNode;
3968 if (isWrapper(oldChildWrapper)) {
3969 oldChildNode = unwrap(oldChildWrapper);
3970 } else {
3971 oldChildNode = oldChildWrapper;
3972 oldChildWrapper = wrap(oldChildNode);
3973 }
3974
3975 if (oldChildWrapper.parentNode !== this) {
3976 // TODO(arv): DOMException
3977 throw new Error('NotFoundError');
3978 }
3979
3980 var nextNode = oldChildWrapper.nextSibling;
3981 var previousNode = oldChildWrapper.previousSibling;
3982 var nodes;
3983
3984 var useNative = !this.invalidateShadowRenderer() &&
3985 !invalidateParent(newChildWrapper);
3986
3987 if (useNative) {
3988 nodes = collectNodesNative(newChildWrapper);
3989 } else {
3990 if (nextNode === newChildWrapper)
3991 nextNode = newChildWrapper.nextSibling;
3992 nodes = collectNodes(newChildWrapper, this, previousNode, nextNode);
3993 }
3994
3995 if (!useNative) {
3996 if (this.firstChild === oldChildWrapper)
3997 this.firstChild_ = nodes[0];
3998 if (this.lastChild === oldChildWrapper)
3999 this.lastChild_ = nodes[nodes.length - 1];
4000
4001 oldChildWrapper.previousSibling_ = oldChildWrapper.nextSibling_ =
4002 oldChildWrapper.parentNode_ = undefined;
4003
4004 // replaceChild no matter what the parent is?
4005 if (oldChildNode.parentNode) {
4006 originalReplaceChild.call(
4007 oldChildNode.parentNode,
4008 unwrapNodesForInsertion(this, nodes),
4009 oldChildNode);
4010 }
4011 } else {
4012 ensureSameOwnerDocument(this, newChildWrapper);
4013 clearChildNodes(this);
4014 originalReplaceChild.call(this.impl, unwrap(newChildWrapper),
4015 oldChildNode);
4016 }
4017
4018 enqueueMutation(this, 'childList', {
4019 addedNodes: nodes,
4020 removedNodes: createOneElementNodeList(oldChildWrapper),
4021 nextSibling: nextNode,
4022 previousSibling: previousNode
4023 });
4024
4025 nodeWasRemoved(oldChildWrapper);
4026 nodesWereAdded(nodes, this);
4027
4028 return oldChildWrapper;
4029 },
4030
4031 /**
4032 * Called after a node was inserted. Subclasses override this to invalidate
4033 * the renderer as needed.
4034 * @private
4035 */
4036 nodeIsInserted_: function() {
4037 for (var child = this.firstChild; child; child = child.nextSibling) {
4038 child.nodeIsInserted_();
4039 }
4040 },
4041
4042 hasChildNodes: function() {
4043 return this.firstChild !== null;
4044 },
4045
4046 /** @type {Node} */
4047 get parentNode() {
4048 // If the parentNode has not been overridden, use the original parentNode.
4049 return this.parentNode_ !== undefined ?
4050 this.parentNode_ : wrap(this.impl.parentNode);
4051 },
4052
4053 /** @type {Node} */
4054 get firstChild() {
4055 return this.firstChild_ !== undefined ?
4056 this.firstChild_ : wrap(this.impl.firstChild);
4057 },
4058
4059 /** @type {Node} */
4060 get lastChild() {
4061 return this.lastChild_ !== undefined ?
4062 this.lastChild_ : wrap(this.impl.lastChild);
4063 },
4064
4065 /** @type {Node} */
4066 get nextSibling() {
4067 return this.nextSibling_ !== undefined ?
4068 this.nextSibling_ : wrap(this.impl.nextSibling);
4069 },
4070
4071 /** @type {Node} */
4072 get previousSibling() {
4073 return this.previousSibling_ !== undefined ?
4074 this.previousSibling_ : wrap(this.impl.previousSibling);
4075 },
4076
4077 get parentElement() {
4078 var p = this.parentNode;
4079 while (p && p.nodeType !== Node.ELEMENT_NODE) {
4080 p = p.parentNode;
4081 }
4082 return p;
4083 },
4084
4085 get textContent() {
4086 // TODO(arv): This should fallback to this.impl.textContent if there
4087 // are no shadow trees below or above the context node.
4088 var s = '';
4089 for (var child = this.firstChild; child; child = child.nextSibling) {
4090 if (child.nodeType != Node.COMMENT_NODE) {
4091 s += child.textContent;
4092 }
4093 }
4094 return s;
4095 },
4096 set textContent(textContent) {
4097 var removedNodes = snapshotNodeList(this.childNodes);
4098
4099 if (this.invalidateShadowRenderer()) {
4100 removeAllChildNodes(this);
4101 if (textContent !== '') {
4102 var textNode = this.impl.ownerDocument.createTextNode(textContent);
4103 this.appendChild(textNode);
4104 }
4105 } else {
4106 clearChildNodes(this);
4107 this.impl.textContent = textContent;
4108 }
4109
4110 var addedNodes = snapshotNodeList(this.childNodes);
4111
4112 enqueueMutation(this, 'childList', {
4113 addedNodes: addedNodes,
4114 removedNodes: removedNodes
4115 });
4116
4117 nodesWereRemoved(removedNodes);
4118 nodesWereAdded(addedNodes, this);
4119 },
4120
4121 get childNodes() {
4122 var wrapperList = new NodeList();
4123 var i = 0;
4124 for (var child = this.firstChild; child; child = child.nextSibling) {
4125 wrapperList[i++] = child;
4126 }
4127 wrapperList.length = i;
4128 return wrapperList;
4129 },
4130
4131 cloneNode: function(deep) {
4132 return cloneNode(this, deep);
4133 },
4134
4135 contains: function(child) {
4136 return contains(this, wrapIfNeeded(child));
4137 },
4138
4139 compareDocumentPosition: function(otherNode) {
4140 // This only wraps, it therefore only operates on the composed DOM and not
4141 // the logical DOM.
4142 return originalCompareDocumentPosition.call(this.impl,
4143 unwrapIfNeeded(otherNode));
4144 },
4145
4146 normalize: function() {
4147 var nodes = snapshotNodeList(this.childNodes);
4148 var remNodes = [];
4149 var s = '';
4150 var modNode;
4151
4152 for (var i = 0, n; i < nodes.length; i++) {
4153 n = nodes[i];
4154 if (n.nodeType === Node.TEXT_NODE) {
4155 if (!modNode && !n.data.length)
4156 this.removeNode(n);
4157 else if (!modNode)
4158 modNode = n;
4159 else {
4160 s += n.data;
4161 remNodes.push(n);
4162 }
4163 } else {
4164 if (modNode && remNodes.length) {
4165 modNode.data += s;
4166 cleanUpNodes(remNodes);
4167 }
4168 remNodes = [];
4169 s = '';
4170 modNode = null;
4171 if (n.childNodes.length)
4172 n.normalize();
4173 }
4174 }
4175
4176 // handle case where >1 text nodes are the last children
4177 if (modNode && remNodes.length) {
4178 modNode.data += s;
4179 cleanupNodes(remNodes);
4180 }
4181 }
4182 });
4183
4184 defineWrapGetter(Node, 'ownerDocument');
4185
4186 // We use a DocumentFragment as a base and then delete the properties of
4187 // DocumentFragment.prototype from the wrapper Node. Since delete makes
4188 // objects slow in some JS engines we recreate the prototype object.
4189 registerWrapper(OriginalNode, Node, document.createDocumentFragment());
4190 delete Node.prototype.querySelector;
4191 delete Node.prototype.querySelectorAll;
4192 Node.prototype = mixin(Object.create(EventTarget.prototype), Node.prototype);
4193
4194 scope.cloneNode = cloneNode;
4195 scope.nodeWasAdded = nodeWasAdded;
4196 scope.nodeWasRemoved = nodeWasRemoved;
4197 scope.nodesWereAdded = nodesWereAdded;
4198 scope.nodesWereRemoved = nodesWereRemoved;
4199 scope.snapshotNodeList = snapshotNodeList;
4200 scope.wrappers.Node = Node;
4201
4202 })(window.ShadowDOMPolyfill);
4203
4204 // Copyright 2013 The Polymer Authors. All rights reserved.
4205 // Use of this source code is governed by a BSD-style
4206 // license that can be found in the LICENSE file.
4207
4208 (function(scope) {
4209 'use strict';
4210
4211 function findOne(node, selector) {
4212 var m, el = node.firstElementChild;
4213 while (el) {
4214 if (el.matches(selector))
4215 return el;
4216 m = findOne(el, selector);
4217 if (m)
4218 return m;
4219 el = el.nextElementSibling;
4220 }
4221 return null;
4222 }
4223
4224 function findAll(node, selector, results) {
4225 var el = node.firstElementChild;
4226 while (el) {
4227 if (el.matches(selector))
4228 results[results.length++] = el;
4229 findAll(el, selector, results);
4230 el = el.nextElementSibling;
4231 }
4232 return results;
4233 }
4234
4235 // find and findAll will only match Simple Selectors,
4236 // Structural Pseudo Classes are not guarenteed to be correct
4237 // http://www.w3.org/TR/css3-selectors/#simple-selectors
4238
4239 var SelectorsInterface = {
4240 querySelector: function(selector) {
4241 return findOne(this, selector);
4242 },
4243 querySelectorAll: function(selector) {
4244 return findAll(this, selector, new NodeList())
4245 }
4246 };
4247
4248 var GetElementsByInterface = {
4249 getElementsByTagName: function(tagName) {
4250 // TODO(arv): Check tagName?
4251 return this.querySelectorAll(tagName);
4252 },
4253 getElementsByClassName: function(className) {
4254 // TODO(arv): Check className?
4255 return this.querySelectorAll('.' + className);
4256 },
4257 getElementsByTagNameNS: function(ns, tagName) {
4258 if (ns === '*')
4259 return this.getElementsByTagName(tagName);
4260
4261 // TODO(arv): Check tagName?
4262 var result = new NodeList;
4263 var els = this.getElementsByTagName(tagName);
4264 for (var i = 0, j = 0; i < els.length; i++) {
4265 if (els[i].namespaceURI === ns)
4266 result[j++] = els[i];
4267 }
4268 result.length = j;
4269 return result;
4270 }
4271 };
4272
4273 scope.GetElementsByInterface = GetElementsByInterface;
4274 scope.SelectorsInterface = SelectorsInterface;
4275
4276 })(window.ShadowDOMPolyfill);
4277
4278 // Copyright 2013 The Polymer Authors. All rights reserved.
4279 // Use of this source code is goverened by a BSD-style
4280 // license that can be found in the LICENSE file.
4281
4282 (function(scope) {
4283 'use strict';
4284
4285 var NodeList = scope.wrappers.NodeList;
4286
4287 function forwardElement(node) {
4288 while (node && node.nodeType !== Node.ELEMENT_NODE) {
4289 node = node.nextSibling;
4290 }
4291 return node;
4292 }
4293
4294 function backwardsElement(node) {
4295 while (node && node.nodeType !== Node.ELEMENT_NODE) {
4296 node = node.previousSibling;
4297 }
4298 return node;
4299 }
4300
4301 var ParentNodeInterface = {
4302 get firstElementChild() {
4303 return forwardElement(this.firstChild);
4304 },
4305
4306 get lastElementChild() {
4307 return backwardsElement(this.lastChild);
4308 },
4309
4310 get childElementCount() {
4311 var count = 0;
4312 for (var child = this.firstElementChild;
4313 child;
4314 child = child.nextElementSibling) {
4315 count++;
4316 }
4317 return count;
4318 },
4319
4320 get children() {
4321 var wrapperList = new NodeList();
4322 var i = 0;
4323 for (var child = this.firstElementChild;
4324 child;
4325 child = child.nextElementSibling) {
4326 wrapperList[i++] = child;
4327 }
4328 wrapperList.length = i;
4329 return wrapperList;
4330 },
4331
4332 remove: function() {
4333 var p = this.parentNode;
4334 if (p)
4335 p.removeChild(this);
4336 }
4337 };
4338
4339 var ChildNodeInterface = {
4340 get nextElementSibling() {
4341 return forwardElement(this.nextSibling);
4342 },
4343
4344 get previousElementSibling() {
4345 return backwardsElement(this.previousSibling);
4346 }
4347 };
4348
4349 scope.ChildNodeInterface = ChildNodeInterface;
4350 scope.ParentNodeInterface = ParentNodeInterface;
4351
4352 })(window.ShadowDOMPolyfill);
4353
4354 // Copyright 2013 The Polymer Authors. All rights reserved.
4355 // Use of this source code is goverened by a BSD-style
4356 // license that can be found in the LICENSE file.
4357
4358 (function(scope) {
4359 'use strict';
4360
4361 var ChildNodeInterface = scope.ChildNodeInterface;
4362 var Node = scope.wrappers.Node;
4363 var enqueueMutation = scope.enqueueMutation;
4364 var mixin = scope.mixin;
4365 var registerWrapper = scope.registerWrapper;
4366
4367 var OriginalCharacterData = window.CharacterData;
4368
4369 function CharacterData(node) {
4370 Node.call(this, node);
4371 }
4372 CharacterData.prototype = Object.create(Node.prototype);
4373 mixin(CharacterData.prototype, {
4374 get textContent() {
4375 return this.data;
4376 },
4377 set textContent(value) {
4378 this.data = value;
4379 },
4380 get data() {
4381 return this.impl.data;
4382 },
4383 set data(value) {
4384 var oldValue = this.impl.data;
4385 enqueueMutation(this, 'characterData', {
4386 oldValue: oldValue
4387 });
4388 this.impl.data = value;
4389 }
4390 });
4391
4392 mixin(CharacterData.prototype, ChildNodeInterface);
4393
4394 registerWrapper(OriginalCharacterData, CharacterData,
4395 document.createTextNode(''));
4396
4397 scope.wrappers.CharacterData = CharacterData;
4398 })(window.ShadowDOMPolyfill);
4399
4400 // Copyright 2014 The Polymer Authors. All rights reserved.
4401 // Use of this source code is goverened by a BSD-style
4402 // license that can be found in the LICENSE file.
4403
4404 (function(scope) {
4405 'use strict';
4406
4407 var CharacterData = scope.wrappers.CharacterData;
4408 var enqueueMutation = scope.enqueueMutation;
4409 var mixin = scope.mixin;
4410 var registerWrapper = scope.registerWrapper;
4411
4412 function toUInt32(x) {
4413 return x >>> 0;
4414 }
4415
4416 var OriginalText = window.Text;
4417
4418 function Text(node) {
4419 CharacterData.call(this, node);
4420 }
4421 Text.prototype = Object.create(CharacterData.prototype);
4422 mixin(Text.prototype, {
4423 splitText: function(offset) {
4424 offset = toUInt32(offset);
4425 var s = this.data;
4426 if (offset > s.length)
4427 throw new Error('IndexSizeError');
4428 var head = s.slice(0, offset);
4429 var tail = s.slice(offset);
4430 this.data = head;
4431 var newTextNode = this.ownerDocument.createTextNode(tail);
4432 if (this.parentNode)
4433 this.parentNode.insertBefore(newTextNode, this.nextSibling);
4434 return newTextNode;
4435 }
4436 });
4437
4438 registerWrapper(OriginalText, Text, document.createTextNode(''));
4439
4440 scope.wrappers.Text = Text;
4441 })(window.ShadowDOMPolyfill);
4442
4443 // Copyright 2013 The Polymer Authors. All rights reserved.
4444 // Use of this source code is goverened by a BSD-style
4445 // license that can be found in the LICENSE file.
4446
4447 (function(scope) {
4448 'use strict';
4449
4450 var ChildNodeInterface = scope.ChildNodeInterface;
4451 var GetElementsByInterface = scope.GetElementsByInterface;
4452 var Node = scope.wrappers.Node;
4453 var ParentNodeInterface = scope.ParentNodeInterface;
4454 var SelectorsInterface = scope.SelectorsInterface;
4455 var addWrapNodeListMethod = scope.addWrapNodeListMethod;
4456 var enqueueMutation = scope.enqueueMutation;
4457 var mixin = scope.mixin;
4458 var oneOf = scope.oneOf;
4459 var registerWrapper = scope.registerWrapper;
4460 var wrappers = scope.wrappers;
4461
4462 var OriginalElement = window.Element;
4463
4464 var matchesNames = [
4465 'matches', // needs to come first.
4466 'mozMatchesSelector',
4467 'msMatchesSelector',
4468 'webkitMatchesSelector',
4469 ].filter(function(name) {
4470 return OriginalElement.prototype[name];
4471 });
4472
4473 var matchesName = matchesNames[0];
4474
4475 var originalMatches = OriginalElement.prototype[matchesName];
4476
4477 function invalidateRendererBasedOnAttribute(element, name) {
4478 // Only invalidate if parent node is a shadow host.
4479 var p = element.parentNode;
4480 if (!p || !p.shadowRoot)
4481 return;
4482
4483 var renderer = scope.getRendererForHost(p);
4484 if (renderer.dependsOnAttribute(name))
4485 renderer.invalidate();
4486 }
4487
4488 function enqueAttributeChange(element, name, oldValue) {
4489 // This is not fully spec compliant. We should use localName (which might
4490 // have a different case than name) and the namespace (which requires us
4491 // to get the Attr object).
4492 enqueueMutation(element, 'attributes', {
4493 name: name,
4494 namespace: null,
4495 oldValue: oldValue
4496 });
4497 }
4498
4499 function Element(node) {
4500 Node.call(this, node);
4501 }
4502 Element.prototype = Object.create(Node.prototype);
4503 mixin(Element.prototype, {
4504 createShadowRoot: function() {
4505 var newShadowRoot = new wrappers.ShadowRoot(this);
4506 this.impl.polymerShadowRoot_ = newShadowRoot;
4507
4508 var renderer = scope.getRendererForHost(this);
4509 renderer.invalidate();
4510
4511 return newShadowRoot;
4512 },
4513
4514 get shadowRoot() {
4515 return this.impl.polymerShadowRoot_ || null;
4516 },
4517
4518 setAttribute: function(name, value) {
4519 var oldValue = this.impl.getAttribute(name);
4520 this.impl.setAttribute(name, value);
4521 enqueAttributeChange(this, name, oldValue);
4522 invalidateRendererBasedOnAttribute(this, name);
4523 },
4524
4525 removeAttribute: function(name) {
4526 var oldValue = this.impl.getAttribute(name);
4527 this.impl.removeAttribute(name);
4528 enqueAttributeChange(this, name, oldValue);
4529 invalidateRendererBasedOnAttribute(this, name);
4530 },
4531
4532 matches: function(selector) {
4533 return originalMatches.call(this.impl, selector);
4534 }
4535 });
4536
4537 matchesNames.forEach(function(name) {
4538 if (name !== 'matches') {
4539 Element.prototype[name] = function(selector) {
4540 return this.matches(selector);
4541 };
4542 }
4543 });
4544
4545 if (OriginalElement.prototype.webkitCreateShadowRoot) {
4546 Element.prototype.webkitCreateShadowRoot =
4547 Element.prototype.createShadowRoot;
4548 }
4549
4550 /**
4551 * Useful for generating the accessor pair for a property that reflects an
4552 * attribute.
4553 */
4554 function setterDirtiesAttribute(prototype, propertyName, opt_attrName) {
4555 var attrName = opt_attrName || propertyName;
4556 Object.defineProperty(prototype, propertyName, {
4557 get: function() {
4558 return this.impl[propertyName];
4559 },
4560 set: function(v) {
4561 this.impl[propertyName] = v;
4562 invalidateRendererBasedOnAttribute(this, attrName);
4563 },
4564 configurable: true,
4565 enumerable: true
4566 });
4567 }
4568
4569 setterDirtiesAttribute(Element.prototype, 'id');
4570 setterDirtiesAttribute(Element.prototype, 'className', 'class');
4571
4572 mixin(Element.prototype, ChildNodeInterface);
4573 mixin(Element.prototype, GetElementsByInterface);
4574 mixin(Element.prototype, ParentNodeInterface);
4575 mixin(Element.prototype, SelectorsInterface);
4576
4577 registerWrapper(OriginalElement, Element,
4578 document.createElementNS(null, 'x'));
4579
4580 // TODO(arv): Export setterDirtiesAttribute and apply it to more bindings
4581 // that reflect attributes.
4582 scope.matchesNames = matchesNames;
4583 scope.wrappers.Element = Element;
4584 })(window.ShadowDOMPolyfill);
4585
4586 // Copyright 2013 The Polymer Authors. All rights reserved.
4587 // Use of this source code is goverened by a BSD-style
4588 // license that can be found in the LICENSE file.
4589
4590 (function(scope) {
4591 'use strict';
4592
4593 var Element = scope.wrappers.Element;
4594 var defineGetter = scope.defineGetter;
4595 var enqueueMutation = scope.enqueueMutation;
4596 var mixin = scope.mixin;
4597 var nodesWereAdded = scope.nodesWereAdded;
4598 var nodesWereRemoved = scope.nodesWereRemoved;
4599 var registerWrapper = scope.registerWrapper;
4600 var snapshotNodeList = scope.snapshotNodeList;
4601 var unwrap = scope.unwrap;
4602 var wrap = scope.wrap;
4603 var wrappers = scope.wrappers;
4604
4605 /////////////////////////////////////////////////////////////////////////////
4606 // innerHTML and outerHTML
4607
4608 // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-end.html#es capingString
4609 var escapeAttrRegExp = /[&\u00A0"]/g;
4610 var escapeDataRegExp = /[&\u00A0<>]/g;
4611
4612 function escapeReplace(c) {
4613 switch (c) {
4614 case '&':
4615 return '&amp;';
4616 case '<':
4617 return '&lt;';
4618 case '>':
4619 return '&gt;';
4620 case '"':
4621 return '&quot;'
4622 case '\u00A0':
4623 return '&nbsp;';
4624 }
4625 }
4626
4627 function escapeAttr(s) {
4628 return s.replace(escapeAttrRegExp, escapeReplace);
4629 }
4630
4631 function escapeData(s) {
4632 return s.replace(escapeDataRegExp, escapeReplace);
4633 }
4634
4635 function makeSet(arr) {
4636 var set = {};
4637 for (var i = 0; i < arr.length; i++) {
4638 set[arr[i]] = true;
4639 }
4640 return set;
4641 }
4642
4643 // http://www.whatwg.org/specs/web-apps/current-work/#void-elements
4644 var voidElements = makeSet([
4645 'area',
4646 'base',
4647 'br',
4648 'col',
4649 'command',
4650 'embed',
4651 'hr',
4652 'img',
4653 'input',
4654 'keygen',
4655 'link',
4656 'meta',
4657 'param',
4658 'source',
4659 'track',
4660 'wbr'
4661 ]);
4662
4663 var plaintextParents = makeSet([
4664 'style',
4665 'script',
4666 'xmp',
4667 'iframe',
4668 'noembed',
4669 'noframes',
4670 'plaintext',
4671 'noscript'
4672 ]);
4673
4674 function getOuterHTML(node, parentNode) {
4675 switch (node.nodeType) {
4676 case Node.ELEMENT_NODE:
4677 var tagName = node.tagName.toLowerCase();
4678 var s = '<' + tagName;
4679 var attrs = node.attributes;
4680 for (var i = 0, attr; attr = attrs[i]; i++) {
4681 s += ' ' + attr.name + '="' + escapeAttr(attr.value) + '"';
4682 }
4683 s += '>';
4684 if (voidElements[tagName])
4685 return s;
4686
4687 return s + getInnerHTML(node) + '</' + tagName + '>';
4688
4689 case Node.TEXT_NODE:
4690 var data = node.data;
4691 if (parentNode && plaintextParents[parentNode.localName])
4692 return data;
4693 return escapeData(data);
4694
4695 case Node.COMMENT_NODE:
4696 return '<!--' + node.data + '-->';
4697
4698 default:
4699 console.error(node);
4700 throw new Error('not implemented');
4701 }
4702 }
4703
4704 function getInnerHTML(node) {
4705 if (node instanceof wrappers.HTMLTemplateElement)
4706 node = node.content;
4707
4708 var s = '';
4709 for (var child = node.firstChild; child; child = child.nextSibling) {
4710 s += getOuterHTML(child, node);
4711 }
4712 return s;
4713 }
4714
4715 function setInnerHTML(node, value, opt_tagName) {
4716 var tagName = opt_tagName || 'div';
4717 node.textContent = '';
4718 var tempElement = unwrap(node.ownerDocument.createElement(tagName));
4719 tempElement.innerHTML = value;
4720 var firstChild;
4721 while (firstChild = tempElement.firstChild) {
4722 node.appendChild(wrap(firstChild));
4723 }
4724 }
4725
4726 // IE11 does not have MSIE in the user agent string.
4727 var oldIe = /MSIE/.test(navigator.userAgent);
4728
4729 var OriginalHTMLElement = window.HTMLElement;
4730 var OriginalHTMLTemplateElement = window.HTMLTemplateElement;
4731
4732 function HTMLElement(node) {
4733 Element.call(this, node);
4734 }
4735 HTMLElement.prototype = Object.create(Element.prototype);
4736 mixin(HTMLElement.prototype, {
4737 get innerHTML() {
4738 return getInnerHTML(this);
4739 },
4740 set innerHTML(value) {
4741 // IE9 does not handle set innerHTML correctly on plaintextParents. It
4742 // creates element children. For example
4743 //
4744 // scriptElement.innerHTML = '<a>test</a>'
4745 //
4746 // Creates a single HTMLAnchorElement child.
4747 if (oldIe && plaintextParents[this.localName]) {
4748 this.textContent = value;
4749 return;
4750 }
4751
4752 var removedNodes = snapshotNodeList(this.childNodes);
4753
4754 if (this.invalidateShadowRenderer()) {
4755 if (this instanceof wrappers.HTMLTemplateElement)
4756 setInnerHTML(this.content, value);
4757 else
4758 setInnerHTML(this, value, this.tagName);
4759
4760 // If we have a non native template element we need to handle this
4761 // manually since setting impl.innerHTML would add the html as direct
4762 // children and not be moved over to the content fragment.
4763 } else if (!OriginalHTMLTemplateElement &&
4764 this instanceof wrappers.HTMLTemplateElement) {
4765 setInnerHTML(this.content, value);
4766 } else {
4767 this.impl.innerHTML = value;
4768 }
4769
4770 var addedNodes = snapshotNodeList(this.childNodes);
4771
4772 enqueueMutation(this, 'childList', {
4773 addedNodes: addedNodes,
4774 removedNodes: removedNodes
4775 });
4776
4777 nodesWereRemoved(removedNodes);
4778 nodesWereAdded(addedNodes, this);
4779 },
4780
4781 get outerHTML() {
4782 return getOuterHTML(this, this.parentNode);
4783 },
4784 set outerHTML(value) {
4785 var p = this.parentNode;
4786 if (p) {
4787 p.invalidateShadowRenderer();
4788 var df = frag(p, value);
4789 p.replaceChild(df, this);
4790 }
4791 },
4792
4793 insertAdjacentHTML: function(position, text) {
4794 var contextElement, refNode;
4795 switch (String(position).toLowerCase()) {
4796 case 'beforebegin':
4797 contextElement = this.parentNode;
4798 refNode = this;
4799 break;
4800 case 'afterend':
4801 contextElement = this.parentNode;
4802 refNode = this.nextSibling;
4803 break;
4804 case 'afterbegin':
4805 contextElement = this;
4806 refNode = this.firstChild;
4807 break;
4808 case 'beforeend':
4809 contextElement = this;
4810 refNode = null;
4811 break;
4812 default:
4813 return;
4814 }
4815
4816 var df = frag(contextElement, text);
4817 contextElement.insertBefore(df, refNode);
4818 }
4819 });
4820
4821 function frag(contextElement, html) {
4822 // TODO(arv): This does not work with SVG and other non HTML elements.
4823 var p = unwrap(contextElement.cloneNode(false));
4824 p.innerHTML = html;
4825 var df = unwrap(document.createDocumentFragment());
4826 var c;
4827 while (c = p.firstChild) {
4828 df.appendChild(c);
4829 }
4830 return wrap(df);
4831 }
4832
4833 function getter(name) {
4834 return function() {
4835 scope.renderAllPending();
4836 return this.impl[name];
4837 };
4838 }
4839
4840 function getterRequiresRendering(name) {
4841 defineGetter(HTMLElement, name, getter(name));
4842 }
4843
4844 [
4845 'clientHeight',
4846 'clientLeft',
4847 'clientTop',
4848 'clientWidth',
4849 'offsetHeight',
4850 'offsetLeft',
4851 'offsetTop',
4852 'offsetWidth',
4853 'scrollHeight',
4854 'scrollWidth',
4855 ].forEach(getterRequiresRendering);
4856
4857 function getterAndSetterRequiresRendering(name) {
4858 Object.defineProperty(HTMLElement.prototype, name, {
4859 get: getter(name),
4860 set: function(v) {
4861 scope.renderAllPending();
4862 this.impl[name] = v;
4863 },
4864 configurable: true,
4865 enumerable: true
4866 });
4867 }
4868
4869 [
4870 'scrollLeft',
4871 'scrollTop',
4872 ].forEach(getterAndSetterRequiresRendering);
4873
4874 function methodRequiresRendering(name) {
4875 Object.defineProperty(HTMLElement.prototype, name, {
4876 value: function() {
4877 scope.renderAllPending();
4878 return this.impl[name].apply(this.impl, arguments);
4879 },
4880 configurable: true,
4881 enumerable: true
4882 });
4883 }
4884
4885 [
4886 'getBoundingClientRect',
4887 'getClientRects',
4888 'scrollIntoView'
4889 ].forEach(methodRequiresRendering);
4890
4891 // HTMLElement is abstract so we use a subclass that has no members.
4892 registerWrapper(OriginalHTMLElement, HTMLElement,
4893 document.createElement('b'));
4894
4895 scope.wrappers.HTMLElement = HTMLElement;
4896
4897 // TODO: Find a better way to share these two with WrapperShadowRoot.
4898 scope.getInnerHTML = getInnerHTML;
4899 scope.setInnerHTML = setInnerHTML
4900 })(window.ShadowDOMPolyfill);
4901
4902 // Copyright 2013 The Polymer Authors. All rights reserved.
4903 // Use of this source code is goverened by a BSD-style
4904 // license that can be found in the LICENSE file.
4905
4906 (function(scope) {
4907 'use strict';
4908
4909 var HTMLElement = scope.wrappers.HTMLElement;
4910 var mixin = scope.mixin;
4911 var registerWrapper = scope.registerWrapper;
4912 var wrap = scope.wrap;
4913
4914 var OriginalHTMLCanvasElement = window.HTMLCanvasElement;
4915
4916 function HTMLCanvasElement(node) {
4917 HTMLElement.call(this, node);
4918 }
4919 HTMLCanvasElement.prototype = Object.create(HTMLElement.prototype);
4920
4921 mixin(HTMLCanvasElement.prototype, {
4922 getContext: function() {
4923 var context = this.impl.getContext.apply(this.impl, arguments);
4924 return context && wrap(context);
4925 }
4926 });
4927
4928 registerWrapper(OriginalHTMLCanvasElement, HTMLCanvasElement,
4929 document.createElement('canvas'));
4930
4931 scope.wrappers.HTMLCanvasElement = HTMLCanvasElement;
4932 })(window.ShadowDOMPolyfill);
4933
4934 // Copyright 2013 The Polymer Authors. All rights reserved.
4935 // Use of this source code is goverened by a BSD-style
4936 // license that can be found in the LICENSE file.
4937
4938 (function(scope) {
4939 'use strict';
4940
4941 var HTMLElement = scope.wrappers.HTMLElement;
4942 var mixin = scope.mixin;
4943 var registerWrapper = scope.registerWrapper;
4944
4945 var OriginalHTMLContentElement = window.HTMLContentElement;
4946
4947 function HTMLContentElement(node) {
4948 HTMLElement.call(this, node);
4949 }
4950 HTMLContentElement.prototype = Object.create(HTMLElement.prototype);
4951 mixin(HTMLContentElement.prototype, {
4952 get select() {
4953 return this.getAttribute('select');
4954 },
4955 set select(value) {
4956 this.setAttribute('select', value);
4957 },
4958
4959 setAttribute: function(n, v) {
4960 HTMLElement.prototype.setAttribute.call(this, n, v);
4961 if (String(n).toLowerCase() === 'select')
4962 this.invalidateShadowRenderer(true);
4963 }
4964
4965 // getDistributedNodes is added in ShadowRenderer
4966
4967 // TODO: attribute boolean resetStyleInheritance;
4968 });
4969
4970 if (OriginalHTMLContentElement)
4971 registerWrapper(OriginalHTMLContentElement, HTMLContentElement);
4972
4973 scope.wrappers.HTMLContentElement = HTMLContentElement;
4974 })(window.ShadowDOMPolyfill);
4975
4976 // Copyright 2013 The Polymer Authors. All rights reserved.
4977 // Use of this source code is goverened by a BSD-style
4978 // license that can be found in the LICENSE file.
4979
4980 (function(scope) {
4981 'use strict';
4982
4983 var HTMLElement = scope.wrappers.HTMLElement;
4984 var registerWrapper = scope.registerWrapper;
4985 var unwrap = scope.unwrap;
4986 var rewrap = scope.rewrap;
4987
4988 var OriginalHTMLImageElement = window.HTMLImageElement;
4989
4990 function HTMLImageElement(node) {
4991 HTMLElement.call(this, node);
4992 }
4993 HTMLImageElement.prototype = Object.create(HTMLElement.prototype);
4994
4995 registerWrapper(OriginalHTMLImageElement, HTMLImageElement,
4996 document.createElement('img'));
4997
4998 function Image(width, height) {
4999 if (!(this instanceof Image)) {
5000 throw new TypeError(
5001 'DOM object constructor cannot be called as a function.');
5002 }
5003
5004 var node = unwrap(document.createElement('img'));
5005 HTMLElement.call(this, node);
5006 rewrap(node, this);
5007
5008 if (width !== undefined)
5009 node.width = width;
5010 if (height !== undefined)
5011 node.height = height;
5012 }
5013
5014 Image.prototype = HTMLImageElement.prototype;
5015
5016 scope.wrappers.HTMLImageElement = HTMLImageElement;
5017 scope.wrappers.Image = Image;
5018 })(window.ShadowDOMPolyfill);
5019
5020 // Copyright 2013 The Polymer Authors. All rights reserved.
5021 // Use of this source code is goverened by a BSD-style
5022 // license that can be found in the LICENSE file.
5023
5024 (function(scope) {
5025 'use strict';
5026
5027 var HTMLElement = scope.wrappers.HTMLElement;
5028 var mixin = scope.mixin;
5029 var registerWrapper = scope.registerWrapper;
5030
5031 var OriginalHTMLShadowElement = window.HTMLShadowElement;
5032
5033 function HTMLShadowElement(node) {
5034 HTMLElement.call(this, node);
5035 }
5036 HTMLShadowElement.prototype = Object.create(HTMLElement.prototype);
5037 mixin(HTMLShadowElement.prototype, {
5038 // TODO: attribute boolean resetStyleInheritance;
5039 });
5040
5041 if (OriginalHTMLShadowElement)
5042 registerWrapper(OriginalHTMLShadowElement, HTMLShadowElement);
5043
5044 scope.wrappers.HTMLShadowElement = HTMLShadowElement;
5045 })(window.ShadowDOMPolyfill);
5046
5047 // Copyright 2013 The Polymer Authors. All rights reserved.
5048 // Use of this source code is goverened by a BSD-style
5049 // license that can be found in the LICENSE file.
5050
5051 (function(scope) {
5052 'use strict';
5053
5054 var HTMLElement = scope.wrappers.HTMLElement;
5055 var mixin = scope.mixin;
5056 var registerWrapper = scope.registerWrapper;
5057 var unwrap = scope.unwrap;
5058 var wrap = scope.wrap;
5059
5060 var contentTable = new WeakMap();
5061 var templateContentsOwnerTable = new WeakMap();
5062
5063 // http://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/templates/index.html# dfn-template-contents-owner
5064 function getTemplateContentsOwner(doc) {
5065 if (!doc.defaultView)
5066 return doc;
5067 var d = templateContentsOwnerTable.get(doc);
5068 if (!d) {
5069 // TODO(arv): This should either be a Document or HTMLDocument depending
5070 // on doc.
5071 d = doc.implementation.createHTMLDocument('');
5072 while (d.lastChild) {
5073 d.removeChild(d.lastChild);
5074 }
5075 templateContentsOwnerTable.set(doc, d);
5076 }
5077 return d;
5078 }
5079
5080 function extractContent(templateElement) {
5081 // templateElement is not a wrapper here.
5082 var doc = getTemplateContentsOwner(templateElement.ownerDocument);
5083 var df = unwrap(doc.createDocumentFragment());
5084 var child;
5085 while (child = templateElement.firstChild) {
5086 df.appendChild(child);
5087 }
5088 return df;
5089 }
5090
5091 var OriginalHTMLTemplateElement = window.HTMLTemplateElement;
5092
5093 function HTMLTemplateElement(node) {
5094 HTMLElement.call(this, node);
5095 if (!OriginalHTMLTemplateElement) {
5096 var content = extractContent(node);
5097 contentTable.set(this, wrap(content));
5098 }
5099 }
5100 HTMLTemplateElement.prototype = Object.create(HTMLElement.prototype);
5101
5102 mixin(HTMLTemplateElement.prototype, {
5103 get content() {
5104 if (OriginalHTMLTemplateElement)
5105 return wrap(this.impl.content);
5106 return contentTable.get(this);
5107 },
5108
5109 // TODO(arv): cloneNode needs to clone content.
5110
5111 });
5112
5113 if (OriginalHTMLTemplateElement)
5114 registerWrapper(OriginalHTMLTemplateElement, HTMLTemplateElement);
5115
5116 scope.wrappers.HTMLTemplateElement = HTMLTemplateElement;
5117 })(window.ShadowDOMPolyfill);
5118
5119 // Copyright 2013 The Polymer Authors. All rights reserved.
5120 // Use of this source code is goverened by a BSD-style
5121 // license that can be found in the LICENSE file.
5122
5123 (function(scope) {
5124 'use strict';
5125
5126 var HTMLElement = scope.wrappers.HTMLElement;
5127 var registerWrapper = scope.registerWrapper;
5128
5129 var OriginalHTMLMediaElement = window.HTMLMediaElement;
5130
5131 function HTMLMediaElement(node) {
5132 HTMLElement.call(this, node);
5133 }
5134 HTMLMediaElement.prototype = Object.create(HTMLElement.prototype);
5135
5136 registerWrapper(OriginalHTMLMediaElement, HTMLMediaElement,
5137 document.createElement('audio'));
5138
5139 scope.wrappers.HTMLMediaElement = HTMLMediaElement;
5140 })(window.ShadowDOMPolyfill);
5141
5142 // Copyright 2013 The Polymer Authors. All rights reserved.
5143 // Use of this source code is goverened by a BSD-style
5144 // license that can be found in the LICENSE file.
5145
5146 (function(scope) {
5147 'use strict';
5148
5149 var HTMLMediaElement = scope.wrappers.HTMLMediaElement;
5150 var registerWrapper = scope.registerWrapper;
5151 var unwrap = scope.unwrap;
5152 var rewrap = scope.rewrap;
5153
5154 var OriginalHTMLAudioElement = window.HTMLAudioElement;
5155
5156 function HTMLAudioElement(node) {
5157 HTMLMediaElement.call(this, node);
5158 }
5159 HTMLAudioElement.prototype = Object.create(HTMLMediaElement.prototype);
5160
5161 registerWrapper(OriginalHTMLAudioElement, HTMLAudioElement,
5162 document.createElement('audio'));
5163
5164 function Audio(src) {
5165 if (!(this instanceof Audio)) {
5166 throw new TypeError(
5167 'DOM object constructor cannot be called as a function.');
5168 }
5169
5170 var node = unwrap(document.createElement('audio'));
5171 HTMLMediaElement.call(this, node);
5172 rewrap(node, this);
5173
5174 node.setAttribute('preload', 'auto');
5175 if (src !== undefined)
5176 node.setAttribute('src', src);
5177 }
5178
5179 Audio.prototype = HTMLAudioElement.prototype;
5180
5181 scope.wrappers.HTMLAudioElement = HTMLAudioElement;
5182 scope.wrappers.Audio = Audio;
5183 })(window.ShadowDOMPolyfill);
5184
5185 // Copyright 2013 The Polymer Authors. All rights reserved.
5186 // Use of this source code is goverened by a BSD-style
5187 // license that can be found in the LICENSE file.
5188
5189 (function(scope) {
5190 'use strict';
5191
5192 var HTMLElement = scope.wrappers.HTMLElement;
5193 var mixin = scope.mixin;
5194 var registerWrapper = scope.registerWrapper;
5195 var rewrap = scope.rewrap;
5196 var unwrap = scope.unwrap;
5197 var wrap = scope.wrap;
5198
5199 var OriginalHTMLOptionElement = window.HTMLOptionElement;
5200
5201 function trimText(s) {
5202 return s.replace(/\s+/g, ' ').trim();
5203 }
5204
5205 function HTMLOptionElement(node) {
5206 HTMLElement.call(this, node);
5207 }
5208 HTMLOptionElement.prototype = Object.create(HTMLElement.prototype);
5209 mixin(HTMLOptionElement.prototype, {
5210 get text() {
5211 return trimText(this.textContent);
5212 },
5213 set text(value) {
5214 this.textContent = trimText(String(value));
5215 },
5216 get form() {
5217 return wrap(unwrap(this).form);
5218 }
5219 });
5220
5221 registerWrapper(OriginalHTMLOptionElement, HTMLOptionElement,
5222 document.createElement('option'));
5223
5224 function Option(text, value, defaultSelected, selected) {
5225 if (!(this instanceof Option)) {
5226 throw new TypeError(
5227 'DOM object constructor cannot be called as a function.');
5228 }
5229
5230 var node = unwrap(document.createElement('option'));
5231 HTMLElement.call(this, node);
5232 rewrap(node, this);
5233
5234 if (text !== undefined)
5235 node.text = text;
5236 if (value !== undefined)
5237 node.setAttribute('value', value);
5238 if (defaultSelected === true)
5239 node.setAttribute('selected', '');
5240 node.selected = selected === true;
5241 }
5242
5243 Option.prototype = HTMLOptionElement.prototype;
5244
5245 scope.wrappers.HTMLOptionElement = HTMLOptionElement;
5246 scope.wrappers.Option = Option;
5247 })(window.ShadowDOMPolyfill);
5248
5249 // Copyright 2014 The Polymer Authors. All rights reserved.
5250 // Use of this source code is goverened by a BSD-style
5251 // license that can be found in the LICENSE file.
5252
5253 (function(scope) {
5254 'use strict';
5255
5256 var HTMLElement = scope.wrappers.HTMLElement;
5257 var mixin = scope.mixin;
5258 var registerWrapper = scope.registerWrapper;
5259 var unwrap = scope.unwrap;
5260 var wrap = scope.wrap;
5261
5262 var OriginalHTMLSelectElement = window.HTMLSelectElement;
5263
5264 function HTMLSelectElement(node) {
5265 HTMLElement.call(this, node);
5266 }
5267 HTMLSelectElement.prototype = Object.create(HTMLElement.prototype);
5268 mixin(HTMLSelectElement.prototype, {
5269 add: function(element, before) {
5270 if (typeof before === 'object') // also includes null
5271 before = unwrap(before);
5272 unwrap(this).add(unwrap(element), before);
5273 },
5274
5275 remove: function(indexOrNode) {
5276 // Spec only allows index but implementations allow index or node.
5277 // remove() is also allowed which is same as remove(undefined)
5278 if (indexOrNode === undefined) {
5279 HTMLElement.prototype.remove.call(this);
5280 return;
5281 }
5282
5283 if (typeof indexOrNode === 'object')
5284 indexOrNode = unwrap(indexOrNode);
5285
5286 unwrap(this).remove(indexOrNode);
5287 },
5288
5289 get form() {
5290 return wrap(unwrap(this).form);
5291 }
5292 });
5293
5294 registerWrapper(OriginalHTMLSelectElement, HTMLSelectElement,
5295 document.createElement('select'));
5296
5297 scope.wrappers.HTMLSelectElement = HTMLSelectElement;
5298 })(window.ShadowDOMPolyfill);
5299
5300 /*
5301 * Copyright 2014 The Polymer Authors. All rights reserved.
5302 * Use of this source code is goverened by a BSD-style
5303 * license that can be found in the LICENSE file.
5304 */
5305
5306 (function(scope) {
5307 'use strict';
5308
5309 var HTMLElement = scope.wrappers.HTMLElement;
5310 var mixin = scope.mixin;
5311 var registerWrapper = scope.registerWrapper;
5312 var unwrap = scope.unwrap;
5313 var wrap = scope.wrap;
5314 var wrapHTMLCollection = scope.wrapHTMLCollection;
5315
5316 var OriginalHTMLTableElement = window.HTMLTableElement;
5317
5318 function HTMLTableElement(node) {
5319 HTMLElement.call(this, node);
5320 }
5321 HTMLTableElement.prototype = Object.create(HTMLElement.prototype);
5322 mixin(HTMLTableElement.prototype, {
5323 get caption() {
5324 return wrap(unwrap(this).caption);
5325 },
5326 createCaption: function() {
5327 return wrap(unwrap(this).createCaption());
5328 },
5329
5330 get tHead() {
5331 return wrap(unwrap(this).tHead);
5332 },
5333 createTHead: function() {
5334 return wrap(unwrap(this).createTHead());
5335 },
5336
5337 createTFoot: function() {
5338 return wrap(unwrap(this).createTFoot());
5339 },
5340 get tFoot() {
5341 return wrap(unwrap(this).tFoot);
5342 },
5343
5344 get tBodies() {
5345 return wrapHTMLCollection(unwrap(this).tBodies);
5346 },
5347 createTBody: function() {
5348 return wrap(unwrap(this).createTBody());
5349 },
5350
5351 get rows() {
5352 return wrapHTMLCollection(unwrap(this).rows);
5353 },
5354 insertRow: function(index) {
5355 return wrap(unwrap(this).insertRow(index));
5356 }
5357 });
5358
5359 registerWrapper(OriginalHTMLTableElement, HTMLTableElement,
5360 document.createElement('table'));
5361
5362 scope.wrappers.HTMLTableElement = HTMLTableElement;
5363 })(window.ShadowDOMPolyfill);
5364
5365 /*
5366 * Copyright 2014 The Polymer Authors. All rights reserved.
5367 * Use of this source code is goverened by a BSD-style
5368 * license that can be found in the LICENSE file.
5369 */
5370
5371 (function(scope) {
5372 'use strict';
5373
5374 var HTMLElement = scope.wrappers.HTMLElement;
5375 var mixin = scope.mixin;
5376 var registerWrapper = scope.registerWrapper;
5377 var wrapHTMLCollection = scope.wrapHTMLCollection;
5378 var unwrap = scope.unwrap;
5379 var wrap = scope.wrap;
5380
5381 var OriginalHTMLTableSectionElement = window.HTMLTableSectionElement;
5382
5383 function HTMLTableSectionElement(node) {
5384 HTMLElement.call(this, node);
5385 }
5386 HTMLTableSectionElement.prototype = Object.create(HTMLElement.prototype);
5387 mixin(HTMLTableSectionElement.prototype, {
5388 get rows() {
5389 return wrapHTMLCollection(unwrap(this).rows);
5390 },
5391 insertRow: function(index) {
5392 return wrap(unwrap(this).insertRow(index));
5393 }
5394 });
5395
5396 registerWrapper(OriginalHTMLTableSectionElement, HTMLTableSectionElement,
5397 document.createElement('thead'));
5398
5399 scope.wrappers.HTMLTableSectionElement = HTMLTableSectionElement;
5400 })(window.ShadowDOMPolyfill);
5401
5402 /*
5403 * Copyright 2014 The Polymer Authors. All rights reserved.
5404 * Use of this source code is goverened by a BSD-style
5405 * license that can be found in the LICENSE file.
5406 */
5407
5408 (function(scope) {
5409 'use strict';
5410
5411 var HTMLElement = scope.wrappers.HTMLElement;
5412 var mixin = scope.mixin;
5413 var registerWrapper = scope.registerWrapper;
5414 var wrapHTMLCollection = scope.wrapHTMLCollection;
5415 var unwrap = scope.unwrap;
5416 var wrap = scope.wrap;
5417
5418 var OriginalHTMLTableRowElement = window.HTMLTableRowElement;
5419
5420 function HTMLTableRowElement(node) {
5421 HTMLElement.call(this, node);
5422 }
5423 HTMLTableRowElement.prototype = Object.create(HTMLElement.prototype);
5424 mixin(HTMLTableRowElement.prototype, {
5425 get cells() {
5426 return wrapHTMLCollection(unwrap(this).cells);
5427 },
5428
5429 insertCell: function(index) {
5430 return wrap(unwrap(this).insertCell(index));
5431 }
5432 });
5433
5434 registerWrapper(OriginalHTMLTableRowElement, HTMLTableRowElement,
5435 document.createElement('tr'));
5436
5437 scope.wrappers.HTMLTableRowElement = HTMLTableRowElement;
5438 })(window.ShadowDOMPolyfill);
5439
5440 // Copyright 2013 The Polymer Authors. All rights reserved.
5441 // Use of this source code is goverened by a BSD-style
5442 // license that can be found in the LICENSE file.
5443
5444 (function(scope) {
5445 'use strict';
5446
5447 var HTMLContentElement = scope.wrappers.HTMLContentElement;
5448 var HTMLElement = scope.wrappers.HTMLElement;
5449 var HTMLShadowElement = scope.wrappers.HTMLShadowElement;
5450 var HTMLTemplateElement = scope.wrappers.HTMLTemplateElement;
5451 var mixin = scope.mixin;
5452 var registerWrapper = scope.registerWrapper;
5453
5454 var OriginalHTMLUnknownElement = window.HTMLUnknownElement;
5455
5456 function HTMLUnknownElement(node) {
5457 switch (node.localName) {
5458 case 'content':
5459 return new HTMLContentElement(node);
5460 case 'shadow':
5461 return new HTMLShadowElement(node);
5462 case 'template':
5463 return new HTMLTemplateElement(node);
5464 }
5465 HTMLElement.call(this, node);
5466 }
5467 HTMLUnknownElement.prototype = Object.create(HTMLElement.prototype);
5468 registerWrapper(OriginalHTMLUnknownElement, HTMLUnknownElement);
5469 scope.wrappers.HTMLUnknownElement = HTMLUnknownElement;
5470 })(window.ShadowDOMPolyfill);
5471
5472 // Copyright 2014 The Polymer Authors. All rights reserved.
5473 // Use of this source code is goverened by a BSD-style
5474 // license that can be found in the LICENSE file.
5475
5476 (function(scope) {
5477 'use strict';
5478
5479 var registerObject = scope.registerObject;
5480
5481 var SVG_NS = 'http://www.w3.org/2000/svg';
5482 var svgTitleElement = document.createElementNS(SVG_NS, 'title');
5483 var SVGTitleElement = registerObject(svgTitleElement);
5484 var SVGElement = Object.getPrototypeOf(SVGTitleElement.prototype).constructor;
5485
5486 scope.wrappers.SVGElement = SVGElement;
5487 })(window.ShadowDOMPolyfill);
5488
5489 // Copyright 2014 The Polymer Authors. All rights reserved.
5490 // Use of this source code is goverened by a BSD-style
5491 // license that can be found in the LICENSE file.
5492
5493 (function(scope) {
5494 'use strict';
5495
5496 var mixin = scope.mixin;
5497 var registerWrapper = scope.registerWrapper;
5498 var unwrap = scope.unwrap;
5499 var wrap = scope.wrap;
5500
5501 var OriginalSVGUseElement = window.SVGUseElement;
5502
5503 // IE uses SVGElement as parent interface, SVG2 (Blink & Gecko) uses
5504 // SVGGraphicsElement. Use the <g> element to get the right prototype.
5505
5506 var SVG_NS = 'http://www.w3.org/2000/svg';
5507 var gWrapper = wrap(document.createElementNS(SVG_NS, 'g'));
5508 var useElement = document.createElementNS(SVG_NS, 'use');
5509 var SVGGElement = gWrapper.constructor;
5510 var parentInterfacePrototype = Object.getPrototypeOf(SVGGElement.prototype);
5511 var parentInterface = parentInterfacePrototype.constructor;
5512
5513 function SVGUseElement(impl) {
5514 parentInterface.call(this, impl);
5515 }
5516
5517 SVGUseElement.prototype = Object.create(parentInterfacePrototype);
5518
5519 // Firefox does not expose instanceRoot.
5520 if ('instanceRoot' in useElement) {
5521 mixin(SVGUseElement.prototype, {
5522 get instanceRoot() {
5523 return wrap(unwrap(this).instanceRoot);
5524 },
5525 get animatedInstanceRoot() {
5526 return wrap(unwrap(this).animatedInstanceRoot);
5527 },
5528 });
5529 }
5530
5531 registerWrapper(OriginalSVGUseElement, SVGUseElement, useElement);
5532
5533 scope.wrappers.SVGUseElement = SVGUseElement;
5534 })(window.ShadowDOMPolyfill);
5535
5536 // Copyright 2014 The Polymer Authors. All rights reserved.
5537 // Use of this source code is goverened by a BSD-style
5538 // license that can be found in the LICENSE file.
5539
5540 (function(scope) {
5541 'use strict';
5542
5543 var EventTarget = scope.wrappers.EventTarget;
5544 var mixin = scope.mixin;
5545 var registerWrapper = scope.registerWrapper;
5546 var wrap = scope.wrap;
5547
5548 var OriginalSVGElementInstance = window.SVGElementInstance;
5549 if (!OriginalSVGElementInstance)
5550 return;
5551
5552 function SVGElementInstance(impl) {
5553 EventTarget.call(this, impl);
5554 }
5555
5556 SVGElementInstance.prototype = Object.create(EventTarget.prototype);
5557 mixin(SVGElementInstance.prototype, {
5558 /** @type {SVGElement} */
5559 get correspondingElement() {
5560 return wrap(this.impl.correspondingElement);
5561 },
5562
5563 /** @type {SVGUseElement} */
5564 get correspondingUseElement() {
5565 return wrap(this.impl.correspondingUseElement);
5566 },
5567
5568 /** @type {SVGElementInstance} */
5569 get parentNode() {
5570 return wrap(this.impl.parentNode);
5571 },
5572
5573 /** @type {SVGElementInstanceList} */
5574 get childNodes() {
5575 throw new Error('Not implemented');
5576 },
5577
5578 /** @type {SVGElementInstance} */
5579 get firstChild() {
5580 return wrap(this.impl.firstChild);
5581 },
5582
5583 /** @type {SVGElementInstance} */
5584 get lastChild() {
5585 return wrap(this.impl.lastChild);
5586 },
5587
5588 /** @type {SVGElementInstance} */
5589 get previousSibling() {
5590 return wrap(this.impl.previousSibling);
5591 },
5592
5593 /** @type {SVGElementInstance} */
5594 get nextSibling() {
5595 return wrap(this.impl.nextSibling);
5596 }
5597 });
5598
5599 registerWrapper(OriginalSVGElementInstance, SVGElementInstance);
5600
5601 scope.wrappers.SVGElementInstance = SVGElementInstance;
5602 })(window.ShadowDOMPolyfill);
5603
5604 // Copyright 2013 The Polymer Authors. All rights reserved.
5605 // Use of this source code is goverened by a BSD-style
5606 // license that can be found in the LICENSE file.
5607
5608 (function(scope) {
5609 'use strict';
5610
5611 var mixin = scope.mixin;
5612 var registerWrapper = scope.registerWrapper;
5613 var unwrap = scope.unwrap;
5614 var unwrapIfNeeded = scope.unwrapIfNeeded;
5615 var wrap = scope.wrap;
5616
5617 var OriginalCanvasRenderingContext2D = window.CanvasRenderingContext2D;
5618
5619 function CanvasRenderingContext2D(impl) {
5620 this.impl = impl;
5621 }
5622
5623 mixin(CanvasRenderingContext2D.prototype, {
5624 get canvas() {
5625 return wrap(this.impl.canvas);
5626 },
5627
5628 drawImage: function() {
5629 arguments[0] = unwrapIfNeeded(arguments[0]);
5630 this.impl.drawImage.apply(this.impl, arguments);
5631 },
5632
5633 createPattern: function() {
5634 arguments[0] = unwrap(arguments[0]);
5635 return this.impl.createPattern.apply(this.impl, arguments);
5636 }
5637 });
5638
5639 registerWrapper(OriginalCanvasRenderingContext2D, CanvasRenderingContext2D,
5640 document.createElement('canvas').getContext('2d'));
5641
5642 scope.wrappers.CanvasRenderingContext2D = CanvasRenderingContext2D;
5643 })(window.ShadowDOMPolyfill);
5644
5645 // Copyright 2013 The Polymer Authors. All rights reserved.
5646 // Use of this source code is goverened by a BSD-style
5647 // license that can be found in the LICENSE file.
5648
5649 (function(scope) {
5650 'use strict';
5651
5652 var mixin = scope.mixin;
5653 var registerWrapper = scope.registerWrapper;
5654 var unwrapIfNeeded = scope.unwrapIfNeeded;
5655 var wrap = scope.wrap;
5656
5657 var OriginalWebGLRenderingContext = window.WebGLRenderingContext;
5658
5659 // IE10 does not have WebGL.
5660 if (!OriginalWebGLRenderingContext)
5661 return;
5662
5663 function WebGLRenderingContext(impl) {
5664 this.impl = impl;
5665 }
5666
5667 mixin(WebGLRenderingContext.prototype, {
5668 get canvas() {
5669 return wrap(this.impl.canvas);
5670 },
5671
5672 texImage2D: function() {
5673 arguments[5] = unwrapIfNeeded(arguments[5]);
5674 this.impl.texImage2D.apply(this.impl, arguments);
5675 },
5676
5677 texSubImage2D: function() {
5678 arguments[6] = unwrapIfNeeded(arguments[6]);
5679 this.impl.texSubImage2D.apply(this.impl, arguments);
5680 }
5681 });
5682
5683 // Blink/WebKit has broken DOM bindings. Usually we would create an instance
5684 // of the object and pass it into registerWrapper as a "blueprint" but
5685 // creating WebGL contexts is expensive and might fail so we use a dummy
5686 // object with dummy instance properties for these broken browsers.
5687 var instanceProperties = /WebKit/.test(navigator.userAgent) ?
5688 {drawingBufferHeight: null, drawingBufferWidth: null} : {};
5689
5690 registerWrapper(OriginalWebGLRenderingContext, WebGLRenderingContext,
5691 instanceProperties);
5692
5693 scope.wrappers.WebGLRenderingContext = WebGLRenderingContext;
5694 })(window.ShadowDOMPolyfill);
5695
5696 // Copyright 2013 The Polymer Authors. All rights reserved.
5697 // Use of this source code is goverened by a BSD-style
5698 // license that can be found in the LICENSE file.
5699
5700 (function(scope) {
5701 'use strict';
5702
5703 var registerWrapper = scope.registerWrapper;
5704 var unwrap = scope.unwrap;
5705 var unwrapIfNeeded = scope.unwrapIfNeeded;
5706 var wrap = scope.wrap;
5707
5708 var OriginalRange = window.Range;
5709
5710 function Range(impl) {
5711 this.impl = impl;
5712 }
5713 Range.prototype = {
5714 get startContainer() {
5715 return wrap(this.impl.startContainer);
5716 },
5717 get endContainer() {
5718 return wrap(this.impl.endContainer);
5719 },
5720 get commonAncestorContainer() {
5721 return wrap(this.impl.commonAncestorContainer);
5722 },
5723 setStart: function(refNode,offset) {
5724 this.impl.setStart(unwrapIfNeeded(refNode), offset);
5725 },
5726 setEnd: function(refNode,offset) {
5727 this.impl.setEnd(unwrapIfNeeded(refNode), offset);
5728 },
5729 setStartBefore: function(refNode) {
5730 this.impl.setStartBefore(unwrapIfNeeded(refNode));
5731 },
5732 setStartAfter: function(refNode) {
5733 this.impl.setStartAfter(unwrapIfNeeded(refNode));
5734 },
5735 setEndBefore: function(refNode) {
5736 this.impl.setEndBefore(unwrapIfNeeded(refNode));
5737 },
5738 setEndAfter: function(refNode) {
5739 this.impl.setEndAfter(unwrapIfNeeded(refNode));
5740 },
5741 selectNode: function(refNode) {
5742 this.impl.selectNode(unwrapIfNeeded(refNode));
5743 },
5744 selectNodeContents: function(refNode) {
5745 this.impl.selectNodeContents(unwrapIfNeeded(refNode));
5746 },
5747 compareBoundaryPoints: function(how, sourceRange) {
5748 return this.impl.compareBoundaryPoints(how, unwrap(sourceRange));
5749 },
5750 extractContents: function() {
5751 return wrap(this.impl.extractContents());
5752 },
5753 cloneContents: function() {
5754 return wrap(this.impl.cloneContents());
5755 },
5756 insertNode: function(node) {
5757 this.impl.insertNode(unwrapIfNeeded(node));
5758 },
5759 surroundContents: function(newParent) {
5760 this.impl.surroundContents(unwrapIfNeeded(newParent));
5761 },
5762 cloneRange: function() {
5763 return wrap(this.impl.cloneRange());
5764 },
5765 isPointInRange: function(node, offset) {
5766 return this.impl.isPointInRange(unwrapIfNeeded(node), offset);
5767 },
5768 comparePoint: function(node, offset) {
5769 return this.impl.comparePoint(unwrapIfNeeded(node), offset);
5770 },
5771 intersectsNode: function(node) {
5772 return this.impl.intersectsNode(unwrapIfNeeded(node));
5773 },
5774 toString: function() {
5775 return this.impl.toString();
5776 }
5777 };
5778
5779 // IE9 does not have createContextualFragment.
5780 if (OriginalRange.prototype.createContextualFragment) {
5781 Range.prototype.createContextualFragment = function(html) {
5782 return wrap(this.impl.createContextualFragment(html));
5783 };
5784 }
5785
5786 registerWrapper(window.Range, Range, document.createRange());
5787
5788 scope.wrappers.Range = Range;
5789
5790 })(window.ShadowDOMPolyfill);
5791
5792 // Copyright 2013 The Polymer Authors. All rights reserved.
5793 // Use of this source code is goverened by a BSD-style
5794 // license that can be found in the LICENSE file.
5795
5796 (function(scope) {
5797 'use strict';
5798
5799 var GetElementsByInterface = scope.GetElementsByInterface;
5800 var ParentNodeInterface = scope.ParentNodeInterface;
5801 var SelectorsInterface = scope.SelectorsInterface;
5802 var mixin = scope.mixin;
5803 var registerObject = scope.registerObject;
5804
5805 var DocumentFragment = registerObject(document.createDocumentFragment());
5806 mixin(DocumentFragment.prototype, ParentNodeInterface);
5807 mixin(DocumentFragment.prototype, SelectorsInterface);
5808 mixin(DocumentFragment.prototype, GetElementsByInterface);
5809
5810 var Comment = registerObject(document.createComment(''));
5811
5812 scope.wrappers.Comment = Comment;
5813 scope.wrappers.DocumentFragment = DocumentFragment;
5814
5815 })(window.ShadowDOMPolyfill);
5816
5817 // Copyright 2013 The Polymer Authors. All rights reserved.
5818 // Use of this source code is goverened by a BSD-style
5819 // license that can be found in the LICENSE file.
5820
5821 (function(scope) {
5822 'use strict';
5823
5824 var DocumentFragment = scope.wrappers.DocumentFragment;
5825 var TreeScope = scope.TreeScope;
5826 var elementFromPoint = scope.elementFromPoint;
5827 var getInnerHTML = scope.getInnerHTML;
5828 var getTreeScope = scope.getTreeScope;
5829 var mixin = scope.mixin;
5830 var rewrap = scope.rewrap;
5831 var setInnerHTML = scope.setInnerHTML;
5832 var unwrap = scope.unwrap;
5833
5834 var shadowHostTable = new WeakMap();
5835 var nextOlderShadowTreeTable = new WeakMap();
5836
5837 var spaceCharRe = /[ \t\n\r\f]/;
5838
5839 function ShadowRoot(hostWrapper) {
5840 var node = unwrap(hostWrapper.impl.ownerDocument.createDocumentFragment());
5841 DocumentFragment.call(this, node);
5842
5843 // createDocumentFragment associates the node with a wrapper
5844 // DocumentFragment instance. Override that.
5845 rewrap(node, this);
5846
5847 this.treeScope_ = new TreeScope(this, getTreeScope(hostWrapper));
5848
5849 var oldShadowRoot = hostWrapper.shadowRoot;
5850 nextOlderShadowTreeTable.set(this, oldShadowRoot);
5851
5852 shadowHostTable.set(this, hostWrapper);
5853 }
5854 ShadowRoot.prototype = Object.create(DocumentFragment.prototype);
5855 mixin(ShadowRoot.prototype, {
5856 get innerHTML() {
5857 return getInnerHTML(this);
5858 },
5859 set innerHTML(value) {
5860 setInnerHTML(this, value);
5861 this.invalidateShadowRenderer();
5862 },
5863
5864 get olderShadowRoot() {
5865 return nextOlderShadowTreeTable.get(this) || null;
5866 },
5867
5868 get host() {
5869 return shadowHostTable.get(this) || null;
5870 },
5871
5872 invalidateShadowRenderer: function() {
5873 return shadowHostTable.get(this).invalidateShadowRenderer();
5874 },
5875
5876 elementFromPoint: function(x, y) {
5877 return elementFromPoint(this, this.ownerDocument, x, y);
5878 },
5879
5880 getElementById: function(id) {
5881 if (spaceCharRe.test(id))
5882 return null;
5883 return this.querySelector('[id="' + id + '"]');
5884 }
5885 });
5886
5887 scope.wrappers.ShadowRoot = ShadowRoot;
5888
5889 })(window.ShadowDOMPolyfill);
5890
5891 // Copyright 2013 The Polymer Authors. All rights reserved.
5892 // Use of this source code is governed by a BSD-style
5893 // license that can be found in the LICENSE file.
5894
5895 (function(scope) {
5896 'use strict';
5897
5898 var Element = scope.wrappers.Element;
5899 var HTMLContentElement = scope.wrappers.HTMLContentElement;
5900 var HTMLShadowElement = scope.wrappers.HTMLShadowElement;
5901 var Node = scope.wrappers.Node;
5902 var ShadowRoot = scope.wrappers.ShadowRoot;
5903 var assert = scope.assert;
5904 var getTreeScope = scope.getTreeScope;
5905 var mixin = scope.mixin;
5906 var oneOf = scope.oneOf;
5907 var unwrap = scope.unwrap;
5908 var wrap = scope.wrap;
5909
5910 /**
5911 * Updates the fields of a wrapper to a snapshot of the logical DOM as needed.
5912 * Up means parentNode
5913 * Sideways means previous and next sibling.
5914 * @param {!Node} wrapper
5915 */
5916 function updateWrapperUpAndSideways(wrapper) {
5917 wrapper.previousSibling_ = wrapper.previousSibling;
5918 wrapper.nextSibling_ = wrapper.nextSibling;
5919 wrapper.parentNode_ = wrapper.parentNode;
5920 }
5921
5922 /**
5923 * Updates the fields of a wrapper to a snapshot of the logical DOM as needed.
5924 * Down means first and last child
5925 * @param {!Node} wrapper
5926 */
5927 function updateWrapperDown(wrapper) {
5928 wrapper.firstChild_ = wrapper.firstChild;
5929 wrapper.lastChild_ = wrapper.lastChild;
5930 }
5931
5932 function updateAllChildNodes(parentNodeWrapper) {
5933 assert(parentNodeWrapper instanceof Node);
5934 for (var childWrapper = parentNodeWrapper.firstChild;
5935 childWrapper;
5936 childWrapper = childWrapper.nextSibling) {
5937 updateWrapperUpAndSideways(childWrapper);
5938 }
5939 updateWrapperDown(parentNodeWrapper);
5940 }
5941
5942 function insertBefore(parentNodeWrapper, newChildWrapper, refChildWrapper) {
5943 var parentNode = unwrap(parentNodeWrapper);
5944 var newChild = unwrap(newChildWrapper);
5945 var refChild = refChildWrapper ? unwrap(refChildWrapper) : null;
5946
5947 remove(newChildWrapper);
5948 updateWrapperUpAndSideways(newChildWrapper);
5949
5950 if (!refChildWrapper) {
5951 parentNodeWrapper.lastChild_ = parentNodeWrapper.lastChild;
5952 if (parentNodeWrapper.lastChild === parentNodeWrapper.firstChild)
5953 parentNodeWrapper.firstChild_ = parentNodeWrapper.firstChild;
5954
5955 var lastChildWrapper = wrap(parentNode.lastChild);
5956 if (lastChildWrapper)
5957 lastChildWrapper.nextSibling_ = lastChildWrapper.nextSibling;
5958 } else {
5959 if (parentNodeWrapper.firstChild === refChildWrapper)
5960 parentNodeWrapper.firstChild_ = refChildWrapper;
5961
5962 refChildWrapper.previousSibling_ = refChildWrapper.previousSibling;
5963 }
5964
5965 parentNode.insertBefore(newChild, refChild);
5966 }
5967
5968 function remove(nodeWrapper) {
5969 var node = unwrap(nodeWrapper)
5970 var parentNode = node.parentNode;
5971 if (!parentNode)
5972 return;
5973
5974 var parentNodeWrapper = wrap(parentNode);
5975 updateWrapperUpAndSideways(nodeWrapper);
5976
5977 if (nodeWrapper.previousSibling)
5978 nodeWrapper.previousSibling.nextSibling_ = nodeWrapper;
5979 if (nodeWrapper.nextSibling)
5980 nodeWrapper.nextSibling.previousSibling_ = nodeWrapper;
5981
5982 if (parentNodeWrapper.lastChild === nodeWrapper)
5983 parentNodeWrapper.lastChild_ = nodeWrapper;
5984 if (parentNodeWrapper.firstChild === nodeWrapper)
5985 parentNodeWrapper.firstChild_ = nodeWrapper;
5986
5987 parentNode.removeChild(node);
5988 }
5989
5990 var distributedChildNodesTable = new WeakMap();
5991 var eventParentsTable = new WeakMap();
5992 var insertionParentTable = new WeakMap();
5993 var rendererForHostTable = new WeakMap();
5994
5995 function distributeChildToInsertionPoint(child, insertionPoint) {
5996 getDistributedChildNodes(insertionPoint).push(child);
5997 assignToInsertionPoint(child, insertionPoint);
5998
5999 var eventParents = eventParentsTable.get(child);
6000 if (!eventParents)
6001 eventParentsTable.set(child, eventParents = []);
6002 eventParents.push(insertionPoint);
6003 }
6004
6005 function resetDistributedChildNodes(insertionPoint) {
6006 distributedChildNodesTable.set(insertionPoint, []);
6007 }
6008
6009 function getDistributedChildNodes(insertionPoint) {
6010 var rv = distributedChildNodesTable.get(insertionPoint);
6011 if (!rv)
6012 distributedChildNodesTable.set(insertionPoint, rv = []);
6013 return rv;
6014 }
6015
6016 function getChildNodesSnapshot(node) {
6017 var result = [], i = 0;
6018 for (var child = node.firstChild; child; child = child.nextSibling) {
6019 result[i++] = child;
6020 }
6021 return result;
6022 }
6023
6024 /**
6025 * Visits all nodes in the tree that fulfils the |predicate|. If the |visitor|
6026 * function returns |false| the traversal is aborted.
6027 * @param {!Node} tree
6028 * @param {function(!Node) : boolean} predicate
6029 * @param {function(!Node) : *} visitor
6030 */
6031 function visit(tree, predicate, visitor) {
6032 // This operates on logical DOM.
6033 for (var node = tree.firstChild; node; node = node.nextSibling) {
6034 if (predicate(node)) {
6035 if (visitor(node) === false)
6036 return;
6037 } else {
6038 visit(node, predicate, visitor);
6039 }
6040 }
6041 }
6042
6043 // Matching Insertion Points
6044 // http://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/shadow/index.html#mat ching-insertion-points
6045
6046 // TODO(arv): Verify this... I don't remember why I picked this regexp.
6047 var selectorMatchRegExp = /^[*.:#[a-zA-Z_|]/;
6048
6049 var allowedPseudoRegExp = new RegExp('^:(' + [
6050 'link',
6051 'visited',
6052 'target',
6053 'enabled',
6054 'disabled',
6055 'checked',
6056 'indeterminate',
6057 'nth-child',
6058 'nth-last-child',
6059 'nth-of-type',
6060 'nth-last-of-type',
6061 'first-child',
6062 'last-child',
6063 'first-of-type',
6064 'last-of-type',
6065 'only-of-type',
6066 ].join('|') + ')');
6067
6068
6069 /**
6070 * @param {Element} node
6071 * @oaram {Element} point The insertion point element.
6072 * @return {boolean} Whether the node matches the insertion point.
6073 */
6074 function matchesCriteria(node, point) {
6075 var select = point.getAttribute('select');
6076 if (!select)
6077 return true;
6078
6079 // Here we know the select attribute is a non empty string.
6080 select = select.trim();
6081 if (!select)
6082 return true;
6083
6084 if (!(node instanceof Element))
6085 return false;
6086
6087 // The native matches function in IE9 does not correctly work with elements
6088 // that are not in the document.
6089 // TODO(arv): Implement matching in JS.
6090 // https://github.com/Polymer/ShadowDOM/issues/361
6091 if (select === '*' || select === node.localName)
6092 return true;
6093
6094 // TODO(arv): This does not seem right. Need to check for a simple selector.
6095 if (!selectorMatchRegExp.test(select))
6096 return false;
6097
6098 // TODO(arv): This no longer matches the spec.
6099 if (select[0] === ':' && !allowedPseudoRegExp.test(select))
6100 return false;
6101
6102 try {
6103 return node.matches(select);
6104 } catch (ex) {
6105 // Invalid selector.
6106 return false;
6107 }
6108 }
6109
6110 var request = oneOf(window, [
6111 'requestAnimationFrame',
6112 'mozRequestAnimationFrame',
6113 'webkitRequestAnimationFrame',
6114 'setTimeout'
6115 ]);
6116
6117 var pendingDirtyRenderers = [];
6118 var renderTimer;
6119
6120 function renderAllPending() {
6121 // TODO(arv): Order these in document order. That way we do not have to
6122 // render something twice.
6123 for (var i = 0; i < pendingDirtyRenderers.length; i++) {
6124 var renderer = pendingDirtyRenderers[i];
6125 var parentRenderer = renderer.parentRenderer;
6126 if (parentRenderer && parentRenderer.dirty)
6127 continue;
6128 renderer.render();
6129 }
6130
6131 pendingDirtyRenderers = [];
6132 }
6133
6134 function handleRequestAnimationFrame() {
6135 renderTimer = null;
6136 renderAllPending();
6137 }
6138
6139 /**
6140 * Returns existing shadow renderer for a host or creates it if it is needed.
6141 * @params {!Element} host
6142 * @return {!ShadowRenderer}
6143 */
6144 function getRendererForHost(host) {
6145 var renderer = rendererForHostTable.get(host);
6146 if (!renderer) {
6147 renderer = new ShadowRenderer(host);
6148 rendererForHostTable.set(host, renderer);
6149 }
6150 return renderer;
6151 }
6152
6153 function getShadowRootAncestor(node) {
6154 var root = getTreeScope(node).root;
6155 if (root instanceof ShadowRoot)
6156 return root;
6157 return null;
6158 }
6159
6160 function getRendererForShadowRoot(shadowRoot) {
6161 return getRendererForHost(shadowRoot.host);
6162 }
6163
6164 var spliceDiff = new ArraySplice();
6165 spliceDiff.equals = function(renderNode, rawNode) {
6166 return unwrap(renderNode.node) === rawNode;
6167 };
6168
6169 /**
6170 * RenderNode is used as an in memory "render tree". When we render the
6171 * composed tree we create a tree of RenderNodes, then we diff this against
6172 * the real DOM tree and make minimal changes as needed.
6173 */
6174 function RenderNode(node) {
6175 this.skip = false;
6176 this.node = node;
6177 this.childNodes = [];
6178 }
6179
6180 RenderNode.prototype = {
6181 append: function(node) {
6182 var rv = new RenderNode(node);
6183 this.childNodes.push(rv);
6184 return rv;
6185 },
6186
6187 sync: function(opt_added) {
6188 if (this.skip)
6189 return;
6190
6191 var nodeWrapper = this.node;
6192 // plain array of RenderNodes
6193 var newChildren = this.childNodes;
6194 // plain array of real nodes.
6195 var oldChildren = getChildNodesSnapshot(unwrap(nodeWrapper));
6196 var added = opt_added || new WeakMap();
6197
6198 var splices = spliceDiff.calculateSplices(newChildren, oldChildren);
6199
6200 var newIndex = 0, oldIndex = 0;
6201 var lastIndex = 0;
6202 for (var i = 0; i < splices.length; i++) {
6203 var splice = splices[i];
6204 for (; lastIndex < splice.index; lastIndex++) {
6205 oldIndex++;
6206 newChildren[newIndex++].sync(added);
6207 }
6208
6209 var removedCount = splice.removed.length;
6210 for (var j = 0; j < removedCount; j++) {
6211 var wrapper = wrap(oldChildren[oldIndex++]);
6212 if (!added.get(wrapper))
6213 remove(wrapper);
6214 }
6215
6216 var addedCount = splice.addedCount;
6217 var refNode = oldChildren[oldIndex] && wrap(oldChildren[oldIndex]);
6218 for (var j = 0; j < addedCount; j++) {
6219 var newChildRenderNode = newChildren[newIndex++];
6220 var newChildWrapper = newChildRenderNode.node;
6221 insertBefore(nodeWrapper, newChildWrapper, refNode);
6222
6223 // Keep track of added so that we do not remove the node after it
6224 // has been added.
6225 added.set(newChildWrapper, true);
6226
6227 newChildRenderNode.sync(added);
6228 }
6229
6230 lastIndex += addedCount;
6231 }
6232
6233 for (var i = lastIndex; i < newChildren.length; i++) {
6234 newChildren[i].sync(added);
6235 }
6236 }
6237 };
6238
6239 function ShadowRenderer(host) {
6240 this.host = host;
6241 this.dirty = false;
6242 this.invalidateAttributes();
6243 this.associateNode(host);
6244 }
6245
6246 ShadowRenderer.prototype = {
6247
6248 // http://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/shadow/index.html#r endering-shadow-trees
6249 render: function(opt_renderNode) {
6250 if (!this.dirty)
6251 return;
6252
6253 this.invalidateAttributes();
6254 this.treeComposition();
6255
6256 var host = this.host;
6257 var shadowRoot = host.shadowRoot;
6258
6259 this.associateNode(host);
6260 var topMostRenderer = !renderNode;
6261 var renderNode = opt_renderNode || new RenderNode(host);
6262
6263 for (var node = shadowRoot.firstChild; node; node = node.nextSibling) {
6264 this.renderNode(shadowRoot, renderNode, node, false);
6265 }
6266
6267 if (topMostRenderer)
6268 renderNode.sync();
6269
6270 this.dirty = false;
6271 },
6272
6273 get parentRenderer() {
6274 return getTreeScope(this.host).renderer;
6275 },
6276
6277 invalidate: function() {
6278 if (!this.dirty) {
6279 this.dirty = true;
6280 pendingDirtyRenderers.push(this);
6281 if (renderTimer)
6282 return;
6283 renderTimer = window[request](handleRequestAnimationFrame, 0);
6284 }
6285 },
6286
6287 renderNode: function(shadowRoot, renderNode, node, isNested) {
6288 if (isShadowHost(node)) {
6289 renderNode = renderNode.append(node);
6290 var renderer = getRendererForHost(node);
6291 renderer.dirty = true; // Need to rerender due to reprojection.
6292 renderer.render(renderNode);
6293 } else if (isInsertionPoint(node)) {
6294 this.renderInsertionPoint(shadowRoot, renderNode, node, isNested);
6295 } else if (isShadowInsertionPoint(node)) {
6296 this.renderShadowInsertionPoint(shadowRoot, renderNode, node);
6297 } else {
6298 this.renderAsAnyDomTree(shadowRoot, renderNode, node, isNested);
6299 }
6300 },
6301
6302 renderAsAnyDomTree: function(shadowRoot, renderNode, node, isNested) {
6303 renderNode = renderNode.append(node);
6304
6305 if (isShadowHost(node)) {
6306 var renderer = getRendererForHost(node);
6307 renderNode.skip = !renderer.dirty;
6308 renderer.render(renderNode);
6309 } else {
6310 for (var child = node.firstChild; child; child = child.nextSibling) {
6311 this.renderNode(shadowRoot, renderNode, child, isNested);
6312 }
6313 }
6314 },
6315
6316 renderInsertionPoint: function(shadowRoot, renderNode, insertionPoint,
6317 isNested) {
6318 var distributedChildNodes = getDistributedChildNodes(insertionPoint);
6319 if (distributedChildNodes.length) {
6320 this.associateNode(insertionPoint);
6321
6322 for (var i = 0; i < distributedChildNodes.length; i++) {
6323 var child = distributedChildNodes[i];
6324 if (isInsertionPoint(child) && isNested)
6325 this.renderInsertionPoint(shadowRoot, renderNode, child, isNested);
6326 else
6327 this.renderAsAnyDomTree(shadowRoot, renderNode, child, isNested);
6328 }
6329 } else {
6330 this.renderFallbackContent(shadowRoot, renderNode, insertionPoint);
6331 }
6332 this.associateNode(insertionPoint.parentNode);
6333 },
6334
6335 renderShadowInsertionPoint: function(shadowRoot, renderNode,
6336 shadowInsertionPoint) {
6337 var nextOlderTree = shadowRoot.olderShadowRoot;
6338 if (nextOlderTree) {
6339 assignToInsertionPoint(nextOlderTree, shadowInsertionPoint);
6340 this.associateNode(shadowInsertionPoint.parentNode);
6341 for (var node = nextOlderTree.firstChild;
6342 node;
6343 node = node.nextSibling) {
6344 this.renderNode(nextOlderTree, renderNode, node, true);
6345 }
6346 } else {
6347 this.renderFallbackContent(shadowRoot, renderNode,
6348 shadowInsertionPoint);
6349 }
6350 },
6351
6352 renderFallbackContent: function(shadowRoot, renderNode, fallbackHost) {
6353 this.associateNode(fallbackHost);
6354 this.associateNode(fallbackHost.parentNode);
6355 for (var node = fallbackHost.firstChild; node; node = node.nextSibling) {
6356 this.renderAsAnyDomTree(shadowRoot, renderNode, node, false);
6357 }
6358 },
6359
6360 /**
6361 * Invalidates the attributes used to keep track of which attributes may
6362 * cause the renderer to be invalidated.
6363 */
6364 invalidateAttributes: function() {
6365 this.attributes = Object.create(null);
6366 },
6367
6368 /**
6369 * Parses the selector and makes this renderer dependent on the attribute
6370 * being used in the selector.
6371 * @param {string} selector
6372 */
6373 updateDependentAttributes: function(selector) {
6374 if (!selector)
6375 return;
6376
6377 var attributes = this.attributes;
6378
6379 // .class
6380 if (/\.\w+/.test(selector))
6381 attributes['class'] = true;
6382
6383 // #id
6384 if (/#\w+/.test(selector))
6385 attributes['id'] = true;
6386
6387 selector.replace(/\[\s*([^\s=\|~\]]+)/g, function(_, name) {
6388 attributes[name] = true;
6389 });
6390
6391 // Pseudo selectors have been removed from the spec.
6392 },
6393
6394 dependsOnAttribute: function(name) {
6395 return this.attributes[name];
6396 },
6397
6398 // http://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/shadow/index.html#d fn-distribution-algorithm
6399 distribute: function(tree, pool) {
6400 var self = this;
6401
6402 visit(tree, isActiveInsertionPoint,
6403 function(insertionPoint) {
6404 resetDistributedChildNodes(insertionPoint);
6405 self.updateDependentAttributes(
6406 insertionPoint.getAttribute('select'));
6407
6408 for (var i = 0; i < pool.length; i++) { // 1.2
6409 var node = pool[i]; // 1.2.1
6410 if (node === undefined) // removed
6411 continue;
6412 if (matchesCriteria(node, insertionPoint)) { // 1.2.2
6413 distributeChildToInsertionPoint(node, insertionPoint); // 1.2.2 .1
6414 pool[i] = undefined; // 1.2.2.2
6415 }
6416 }
6417 });
6418 },
6419
6420 // http://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/shadow/index.html#d fn-tree-composition
6421 treeComposition: function () {
6422 var shadowHost = this.host;
6423 var tree = shadowHost.shadowRoot; // 1.
6424 var pool = []; // 2.
6425
6426 for (var child = shadowHost.firstChild;
6427 child;
6428 child = child.nextSibling) { // 3.
6429 if (isInsertionPoint(child)) { // 3.2.
6430 var reprojected = getDistributedChildNodes(child); // 3.2.1.
6431 // if reprojected is undef... reset it?
6432 if (!reprojected || !reprojected.length) // 3.2.2.
6433 reprojected = getChildNodesSnapshot(child);
6434 pool.push.apply(pool, reprojected); // 3.2.3.
6435 } else {
6436 pool.push(child); // 3.3.
6437 }
6438 }
6439
6440 var shadowInsertionPoint, point;
6441 while (tree) { // 4.
6442 // 4.1.
6443 shadowInsertionPoint = undefined; // Reset every iteration.
6444 visit(tree, isActiveShadowInsertionPoint, function(point) {
6445 shadowInsertionPoint = point;
6446 return false;
6447 });
6448 point = shadowInsertionPoint;
6449
6450 this.distribute(tree, pool); // 4.2.
6451 if (point) { // 4.3.
6452 var nextOlderTree = tree.olderShadowRoot; // 4.3.1.
6453 if (!nextOlderTree) {
6454 break; // 4.3.1.1.
6455 } else {
6456 tree = nextOlderTree; // 4.3.2.2.
6457 assignToInsertionPoint(tree, point); // 4.3.2.2.
6458 continue; // 4.3.2.3.
6459 }
6460 } else {
6461 break; // 4.4.
6462 }
6463 }
6464 },
6465
6466 associateNode: function(node) {
6467 node.impl.polymerShadowRenderer_ = this;
6468 }
6469 };
6470
6471 function isInsertionPoint(node) {
6472 // Should this include <shadow>?
6473 return node instanceof HTMLContentElement;
6474 }
6475
6476 function isActiveInsertionPoint(node) {
6477 // <content> inside another <content> or <shadow> is considered inactive.
6478 return node instanceof HTMLContentElement;
6479 }
6480
6481 function isShadowInsertionPoint(node) {
6482 return node instanceof HTMLShadowElement;
6483 }
6484
6485 function isActiveShadowInsertionPoint(node) {
6486 // <shadow> inside another <content> or <shadow> is considered inactive.
6487 return node instanceof HTMLShadowElement;
6488 }
6489
6490 function isShadowHost(shadowHost) {
6491 return shadowHost.shadowRoot;
6492 }
6493
6494 function getShadowTrees(host) {
6495 var trees = [];
6496
6497 for (var tree = host.shadowRoot; tree; tree = tree.olderShadowRoot) {
6498 trees.push(tree);
6499 }
6500 return trees;
6501 }
6502
6503 function assignToInsertionPoint(tree, point) {
6504 insertionParentTable.set(tree, point);
6505 }
6506
6507 // http://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/shadow/index.html#ren dering-shadow-trees
6508 function render(host) {
6509 new ShadowRenderer(host).render();
6510 };
6511
6512 // Need to rerender shadow host when:
6513 //
6514 // - a direct child to the ShadowRoot is added or removed
6515 // - a direct child to the host is added or removed
6516 // - a new shadow root is created
6517 // - a direct child to a content/shadow element is added or removed
6518 // - a sibling to a content/shadow element is added or removed
6519 // - content[select] is changed
6520 // - an attribute in a direct child to a host is modified
6521
6522 /**
6523 * This gets called when a node was added or removed to it.
6524 */
6525 Node.prototype.invalidateShadowRenderer = function(force) {
6526 var renderer = this.impl.polymerShadowRenderer_;
6527 if (renderer) {
6528 renderer.invalidate();
6529 return true;
6530 }
6531
6532 return false;
6533 };
6534
6535 HTMLContentElement.prototype.getDistributedNodes = function() {
6536 // TODO(arv): We should only rerender the dirty ancestor renderers (from
6537 // the root and down).
6538 renderAllPending();
6539 return getDistributedChildNodes(this);
6540 };
6541
6542 HTMLShadowElement.prototype.nodeIsInserted_ =
6543 HTMLContentElement.prototype.nodeIsInserted_ = function() {
6544 // Invalidate old renderer if any.
6545 this.invalidateShadowRenderer();
6546
6547 var shadowRoot = getShadowRootAncestor(this);
6548 var renderer;
6549 if (shadowRoot)
6550 renderer = getRendererForShadowRoot(shadowRoot);
6551 this.impl.polymerShadowRenderer_ = renderer;
6552 if (renderer)
6553 renderer.invalidate();
6554 };
6555
6556 scope.eventParentsTable = eventParentsTable;
6557 scope.getRendererForHost = getRendererForHost;
6558 scope.getShadowTrees = getShadowTrees;
6559 scope.insertionParentTable = insertionParentTable;
6560 scope.renderAllPending = renderAllPending;
6561
6562 // Exposed for testing
6563 scope.visual = {
6564 insertBefore: insertBefore,
6565 remove: remove,
6566 };
6567
6568 })(window.ShadowDOMPolyfill);
6569
6570 // Copyright 2013 The Polymer Authors. All rights reserved.
6571 // Use of this source code is goverened by a BSD-style
6572 // license that can be found in the LICENSE file.
6573
6574 (function(scope) {
6575 'use strict';
6576
6577 var HTMLElement = scope.wrappers.HTMLElement;
6578 var assert = scope.assert;
6579 var mixin = scope.mixin;
6580 var registerWrapper = scope.registerWrapper;
6581 var unwrap = scope.unwrap;
6582 var wrap = scope.wrap;
6583
6584 var elementsWithFormProperty = [
6585 'HTMLButtonElement',
6586 'HTMLFieldSetElement',
6587 'HTMLInputElement',
6588 'HTMLKeygenElement',
6589 'HTMLLabelElement',
6590 'HTMLLegendElement',
6591 'HTMLObjectElement',
6592 // HTMLOptionElement is handled in HTMLOptionElement.js
6593 'HTMLOutputElement',
6594 // HTMLSelectElement is handled in HTMLSelectElement.js
6595 'HTMLTextAreaElement',
6596 ];
6597
6598 function createWrapperConstructor(name) {
6599 if (!window[name])
6600 return;
6601
6602 // Ensure we are not overriding an already existing constructor.
6603 assert(!scope.wrappers[name]);
6604
6605 var GeneratedWrapper = function(node) {
6606 // At this point all of them extend HTMLElement.
6607 HTMLElement.call(this, node);
6608 }
6609 GeneratedWrapper.prototype = Object.create(HTMLElement.prototype);
6610 mixin(GeneratedWrapper.prototype, {
6611 get form() {
6612 return wrap(unwrap(this).form);
6613 },
6614 });
6615
6616 registerWrapper(window[name], GeneratedWrapper,
6617 document.createElement(name.slice(4, -7)));
6618 scope.wrappers[name] = GeneratedWrapper;
6619 }
6620
6621 elementsWithFormProperty.forEach(createWrapperConstructor);
6622
6623 })(window.ShadowDOMPolyfill);
6624
6625 // Copyright 2014 The Polymer Authors. All rights reserved.
6626 // Use of this source code is goverened by a BSD-style
6627 // license that can be found in the LICENSE file.
6628
6629 (function(scope) {
6630 'use strict';
6631
6632 var registerWrapper = scope.registerWrapper;
6633 var unwrap = scope.unwrap;
6634 var unwrapIfNeeded = scope.unwrapIfNeeded;
6635 var wrap = scope.wrap;
6636
6637 var OriginalSelection = window.Selection;
6638
6639 function Selection(impl) {
6640 this.impl = impl;
6641 }
6642 Selection.prototype = {
6643 get anchorNode() {
6644 return wrap(this.impl.anchorNode);
6645 },
6646 get focusNode() {
6647 return wrap(this.impl.focusNode);
6648 },
6649 addRange: function(range) {
6650 this.impl.addRange(unwrap(range));
6651 },
6652 collapse: function(node, index) {
6653 this.impl.collapse(unwrapIfNeeded(node), index);
6654 },
6655 containsNode: function(node, allowPartial) {
6656 return this.impl.containsNode(unwrapIfNeeded(node), allowPartial);
6657 },
6658 extend: function(node, offset) {
6659 this.impl.extend(unwrapIfNeeded(node), offset);
6660 },
6661 getRangeAt: function(index) {
6662 return wrap(this.impl.getRangeAt(index));
6663 },
6664 removeRange: function(range) {
6665 this.impl.removeRange(unwrap(range));
6666 },
6667 selectAllChildren: function(node) {
6668 this.impl.selectAllChildren(unwrapIfNeeded(node));
6669 },
6670 toString: function() {
6671 return this.impl.toString();
6672 }
6673 };
6674
6675 // WebKit extensions. Not implemented.
6676 // readonly attribute Node baseNode;
6677 // readonly attribute long baseOffset;
6678 // readonly attribute Node extentNode;
6679 // readonly attribute long extentOffset;
6680 // [RaisesException] void setBaseAndExtent([Default=Undefined] optional Node b aseNode,
6681 // [Default=Undefined] optional long baseOffset,
6682 // [Default=Undefined] optional Node extentNode,
6683 // [Default=Undefined] optional long extentOffset);
6684 // [RaisesException, ImplementedAs=collapse] void setPosition([Default=Undefin ed] optional Node node,
6685 // [Default=Undefined] optional long offset);
6686
6687 registerWrapper(window.Selection, Selection, window.getSelection());
6688
6689 scope.wrappers.Selection = Selection;
6690
6691 })(window.ShadowDOMPolyfill);
6692
6693 // Copyright 2013 The Polymer Authors. All rights reserved.
6694 // Use of this source code is goverened by a BSD-style
6695 // license that can be found in the LICENSE file.
6696
6697 (function(scope) {
6698 'use strict';
6699
6700 var GetElementsByInterface = scope.GetElementsByInterface;
6701 var Node = scope.wrappers.Node;
6702 var ParentNodeInterface = scope.ParentNodeInterface;
6703 var Selection = scope.wrappers.Selection;
6704 var SelectorsInterface = scope.SelectorsInterface;
6705 var ShadowRoot = scope.wrappers.ShadowRoot;
6706 var TreeScope = scope.TreeScope;
6707 var cloneNode = scope.cloneNode;
6708 var defineWrapGetter = scope.defineWrapGetter;
6709 var elementFromPoint = scope.elementFromPoint;
6710 var forwardMethodsToWrapper = scope.forwardMethodsToWrapper;
6711 var matchesNames = scope.matchesNames;
6712 var mixin = scope.mixin;
6713 var registerWrapper = scope.registerWrapper;
6714 var renderAllPending = scope.renderAllPending;
6715 var rewrap = scope.rewrap;
6716 var unwrap = scope.unwrap;
6717 var wrap = scope.wrap;
6718 var wrapEventTargetMethods = scope.wrapEventTargetMethods;
6719 var wrapNodeList = scope.wrapNodeList;
6720
6721 var implementationTable = new WeakMap();
6722
6723 function Document(node) {
6724 Node.call(this, node);
6725 this.treeScope_ = new TreeScope(this, null);
6726 }
6727 Document.prototype = Object.create(Node.prototype);
6728
6729 defineWrapGetter(Document, 'documentElement');
6730
6731 // Conceptually both body and head can be in a shadow but suporting that seems
6732 // overkill at this point.
6733 defineWrapGetter(Document, 'body');
6734 defineWrapGetter(Document, 'head');
6735
6736 // document cannot be overridden so we override a bunch of its methods
6737 // directly on the instance.
6738
6739 function wrapMethod(name) {
6740 var original = document[name];
6741 Document.prototype[name] = function() {
6742 return wrap(original.apply(this.impl, arguments));
6743 };
6744 }
6745
6746 [
6747 'createComment',
6748 'createDocumentFragment',
6749 'createElement',
6750 'createElementNS',
6751 'createEvent',
6752 'createEventNS',
6753 'createRange',
6754 'createTextNode',
6755 'getElementById'
6756 ].forEach(wrapMethod);
6757
6758 var originalAdoptNode = document.adoptNode;
6759
6760 function adoptNodeNoRemove(node, doc) {
6761 originalAdoptNode.call(doc.impl, unwrap(node));
6762 adoptSubtree(node, doc);
6763 }
6764
6765 function adoptSubtree(node, doc) {
6766 if (node.shadowRoot)
6767 doc.adoptNode(node.shadowRoot);
6768 if (node instanceof ShadowRoot)
6769 adoptOlderShadowRoots(node, doc);
6770 for (var child = node.firstChild; child; child = child.nextSibling) {
6771 adoptSubtree(child, doc);
6772 }
6773 }
6774
6775 function adoptOlderShadowRoots(shadowRoot, doc) {
6776 var oldShadowRoot = shadowRoot.olderShadowRoot;
6777 if (oldShadowRoot)
6778 doc.adoptNode(oldShadowRoot);
6779 }
6780
6781 var originalGetSelection = document.getSelection;
6782
6783 mixin(Document.prototype, {
6784 adoptNode: function(node) {
6785 if (node.parentNode)
6786 node.parentNode.removeChild(node);
6787 adoptNodeNoRemove(node, this);
6788 return node;
6789 },
6790 elementFromPoint: function(x, y) {
6791 return elementFromPoint(this, this, x, y);
6792 },
6793 importNode: function(node, deep) {
6794 return cloneNode(node, deep, this.impl);
6795 },
6796 getSelection: function() {
6797 renderAllPending();
6798 return new Selection(originalGetSelection.call(unwrap(this)));
6799 }
6800 });
6801
6802 if (document.registerElement) {
6803 var originalRegisterElement = document.registerElement;
6804 Document.prototype.registerElement = function(tagName, object) {
6805 var prototype, extendsOption;
6806 if (object !== undefined) {
6807 prototype = object.prototype;
6808 extendsOption = object.extends;
6809 }
6810
6811 if (!prototype)
6812 prototype = Object.create(HTMLElement.prototype);
6813
6814
6815 // If we already used the object as a prototype for another custom
6816 // element.
6817 if (scope.nativePrototypeTable.get(prototype)) {
6818 // TODO(arv): DOMException
6819 throw new Error('NotSupportedError');
6820 }
6821
6822 // Find first object on the prototype chain that already have a native
6823 // prototype. Keep track of all the objects before that so we can create
6824 // a similar structure for the native case.
6825 var proto = Object.getPrototypeOf(prototype);
6826 var nativePrototype;
6827 var prototypes = [];
6828 while (proto) {
6829 nativePrototype = scope.nativePrototypeTable.get(proto);
6830 if (nativePrototype)
6831 break;
6832 prototypes.push(proto);
6833 proto = Object.getPrototypeOf(proto);
6834 }
6835
6836 if (!nativePrototype) {
6837 // TODO(arv): DOMException
6838 throw new Error('NotSupportedError');
6839 }
6840
6841 // This works by creating a new prototype object that is empty, but has
6842 // the native prototype as its proto. The original prototype object
6843 // passed into register is used as the wrapper prototype.
6844
6845 var newPrototype = Object.create(nativePrototype);
6846 for (var i = prototypes.length - 1; i >= 0; i--) {
6847 newPrototype = Object.create(newPrototype);
6848 }
6849
6850 // Add callbacks if present.
6851 // Names are taken from:
6852 // https://code.google.com/p/chromium/codesearch#chromium/src/third_part y/WebKit/Source/bindings/v8/CustomElementConstructorBuilder.cpp&sq=package:chrom ium&type=cs&l=156
6853 // and not from the spec since the spec is out of date.
6854 [
6855 'createdCallback',
6856 'attachedCallback',
6857 'detachedCallback',
6858 'attributeChangedCallback',
6859 ].forEach(function(name) {
6860 var f = prototype[name];
6861 if (!f)
6862 return;
6863 newPrototype[name] = function() {
6864 // if this element has been wrapped prior to registration,
6865 // the wrapper is stale; in this case rewrap
6866 if (!(wrap(this) instanceof CustomElementConstructor)) {
6867 rewrap(this);
6868 }
6869 f.apply(wrap(this), arguments);
6870 };
6871 });
6872
6873 var p = {prototype: newPrototype};
6874 if (extendsOption)
6875 p.extends = extendsOption;
6876
6877 function CustomElementConstructor(node) {
6878 if (!node) {
6879 if (extendsOption) {
6880 return document.createElement(extendsOption, tagName);
6881 } else {
6882 return document.createElement(tagName);
6883 }
6884 }
6885 this.impl = node;
6886 }
6887 CustomElementConstructor.prototype = prototype;
6888 CustomElementConstructor.prototype.constructor = CustomElementConstructor;
6889
6890 scope.constructorTable.set(newPrototype, CustomElementConstructor);
6891 scope.nativePrototypeTable.set(prototype, newPrototype);
6892
6893 // registration is synchronous so do it last
6894 var nativeConstructor = originalRegisterElement.call(unwrap(this),
6895 tagName, p);
6896 return CustomElementConstructor;
6897 };
6898
6899 forwardMethodsToWrapper([
6900 window.HTMLDocument || window.Document, // Gecko adds these to HTMLDocume nt
6901 ], [
6902 'registerElement',
6903 ]);
6904 }
6905
6906 // We also override some of the methods on document.body and document.head
6907 // for convenience.
6908 forwardMethodsToWrapper([
6909 window.HTMLBodyElement,
6910 window.HTMLDocument || window.Document, // Gecko adds these to HTMLDocument
6911 window.HTMLHeadElement,
6912 window.HTMLHtmlElement,
6913 ], [
6914 'appendChild',
6915 'compareDocumentPosition',
6916 'contains',
6917 'getElementsByClassName',
6918 'getElementsByTagName',
6919 'getElementsByTagNameNS',
6920 'insertBefore',
6921 'querySelector',
6922 'querySelectorAll',
6923 'removeChild',
6924 'replaceChild',
6925 ].concat(matchesNames));
6926
6927 forwardMethodsToWrapper([
6928 window.HTMLDocument || window.Document, // Gecko adds these to HTMLDocument
6929 ], [
6930 'adoptNode',
6931 'importNode',
6932 'contains',
6933 'createComment',
6934 'createDocumentFragment',
6935 'createElement',
6936 'createElementNS',
6937 'createEvent',
6938 'createEventNS',
6939 'createRange',
6940 'createTextNode',
6941 'elementFromPoint',
6942 'getElementById',
6943 'getSelection',
6944 ]);
6945
6946 mixin(Document.prototype, GetElementsByInterface);
6947 mixin(Document.prototype, ParentNodeInterface);
6948 mixin(Document.prototype, SelectorsInterface);
6949
6950 mixin(Document.prototype, {
6951 get implementation() {
6952 var implementation = implementationTable.get(this);
6953 if (implementation)
6954 return implementation;
6955 implementation =
6956 new DOMImplementation(unwrap(this).implementation);
6957 implementationTable.set(this, implementation);
6958 return implementation;
6959 }
6960 });
6961
6962 registerWrapper(window.Document, Document,
6963 document.implementation.createHTMLDocument(''));
6964
6965 // Both WebKit and Gecko uses HTMLDocument for document. HTML5/DOM only has
6966 // one Document interface and IE implements the standard correctly.
6967 if (window.HTMLDocument)
6968 registerWrapper(window.HTMLDocument, Document);
6969
6970 wrapEventTargetMethods([
6971 window.HTMLBodyElement,
6972 window.HTMLDocument || window.Document, // Gecko adds these to HTMLDocument
6973 window.HTMLHeadElement,
6974 ]);
6975
6976 function DOMImplementation(impl) {
6977 this.impl = impl;
6978 }
6979
6980 function wrapImplMethod(constructor, name) {
6981 var original = document.implementation[name];
6982 constructor.prototype[name] = function() {
6983 return wrap(original.apply(this.impl, arguments));
6984 };
6985 }
6986
6987 function forwardImplMethod(constructor, name) {
6988 var original = document.implementation[name];
6989 constructor.prototype[name] = function() {
6990 return original.apply(this.impl, arguments);
6991 };
6992 }
6993
6994 wrapImplMethod(DOMImplementation, 'createDocumentType');
6995 wrapImplMethod(DOMImplementation, 'createDocument');
6996 wrapImplMethod(DOMImplementation, 'createHTMLDocument');
6997 forwardImplMethod(DOMImplementation, 'hasFeature');
6998
6999 registerWrapper(window.DOMImplementation, DOMImplementation);
7000
7001 forwardMethodsToWrapper([
7002 window.DOMImplementation,
7003 ], [
7004 'createDocumentType',
7005 'createDocument',
7006 'createHTMLDocument',
7007 'hasFeature',
7008 ]);
7009
7010 scope.adoptNodeNoRemove = adoptNodeNoRemove;
7011 scope.wrappers.DOMImplementation = DOMImplementation;
7012 scope.wrappers.Document = Document;
7013
7014 })(window.ShadowDOMPolyfill);
7015
7016 // Copyright 2013 The Polymer Authors. All rights reserved.
7017 // Use of this source code is goverened by a BSD-style
7018 // license that can be found in the LICENSE file.
7019
7020 (function(scope) {
7021 'use strict';
7022
7023 var EventTarget = scope.wrappers.EventTarget;
7024 var Selection = scope.wrappers.Selection;
7025 var mixin = scope.mixin;
7026 var registerWrapper = scope.registerWrapper;
7027 var renderAllPending = scope.renderAllPending;
7028 var unwrap = scope.unwrap;
7029 var unwrapIfNeeded = scope.unwrapIfNeeded;
7030 var wrap = scope.wrap;
7031
7032 var OriginalWindow = window.Window;
7033 var originalGetComputedStyle = window.getComputedStyle;
7034 var originalGetSelection = window.getSelection;
7035
7036 function Window(impl) {
7037 EventTarget.call(this, impl);
7038 }
7039 Window.prototype = Object.create(EventTarget.prototype);
7040
7041 OriginalWindow.prototype.getComputedStyle = function(el, pseudo) {
7042 return wrap(this || window).getComputedStyle(unwrapIfNeeded(el), pseudo);
7043 };
7044
7045 OriginalWindow.prototype.getSelection = function() {
7046 return wrap(this || window).getSelection();
7047 };
7048
7049 // Work around for https://bugzilla.mozilla.org/show_bug.cgi?id=943065
7050 delete window.getComputedStyle;
7051 delete window.getSelection;
7052
7053 ['addEventListener', 'removeEventListener', 'dispatchEvent'].forEach(
7054 function(name) {
7055 OriginalWindow.prototype[name] = function() {
7056 var w = wrap(this || window);
7057 return w[name].apply(w, arguments);
7058 };
7059
7060 // Work around for https://bugzilla.mozilla.org/show_bug.cgi?id=943065
7061 delete window[name];
7062 });
7063
7064 mixin(Window.prototype, {
7065 getComputedStyle: function(el, pseudo) {
7066 renderAllPending();
7067 return originalGetComputedStyle.call(unwrap(this), unwrapIfNeeded(el),
7068 pseudo);
7069 },
7070 getSelection: function() {
7071 renderAllPending();
7072 return new Selection(originalGetSelection.call(unwrap(this)));
7073 },
7074 });
7075
7076 registerWrapper(OriginalWindow, Window);
7077
7078 scope.wrappers.Window = Window;
7079
7080 })(window.ShadowDOMPolyfill);
7081
7082 /**
7083 * Copyright 2014 The Polymer Authors. All rights reserved.
7084 * Use of this source code is goverened by a BSD-style
7085 * license that can be found in the LICENSE file.
7086 */
7087
7088 (function(scope) {
7089 'use strict';
7090
7091 var unwrap = scope.unwrap;
7092
7093 // DataTransfer (Clipboard in old Blink/WebKit) has a single method that
7094 // requires wrapping. Since it is only a method we do not need a real wrapper,
7095 // we can just override the method.
7096
7097 var OriginalDataTransfer = window.DataTransfer || window.Clipboard;
7098 var OriginalDataTransferSetDragImage =
7099 OriginalDataTransfer.prototype.setDragImage;
7100
7101 OriginalDataTransfer.prototype.setDragImage = function(image, x, y) {
7102 OriginalDataTransferSetDragImage.call(this, unwrap(image), x, y);
7103 };
7104
7105 })(window.ShadowDOMPolyfill);
7106
7107 // Copyright 2013 The Polymer Authors. All rights reserved.
7108 // Use of this source code is goverened by a BSD-style
7109 // license that can be found in the LICENSE file.
7110
7111 (function(scope) {
7112 'use strict';
7113
7114 var isWrapperFor = scope.isWrapperFor;
7115
7116 // This is a list of the elements we currently override the global constructor
7117 // for.
7118 var elements = {
7119 'a': 'HTMLAnchorElement',
7120 // Do not create an applet element by default since it shows a warning in
7121 // IE.
7122 // https://github.com/Polymer/polymer/issues/217
7123 // 'applet': 'HTMLAppletElement',
7124 'area': 'HTMLAreaElement',
7125 'audio': 'HTMLAudioElement',
7126 'base': 'HTMLBaseElement',
7127 'body': 'HTMLBodyElement',
7128 'br': 'HTMLBRElement',
7129 'button': 'HTMLButtonElement',
7130 'canvas': 'HTMLCanvasElement',
7131 'caption': 'HTMLTableCaptionElement',
7132 'col': 'HTMLTableColElement',
7133 // 'command': 'HTMLCommandElement', // Not fully implemented in Gecko.
7134 'content': 'HTMLContentElement',
7135 'data': 'HTMLDataElement',
7136 'datalist': 'HTMLDataListElement',
7137 'del': 'HTMLModElement',
7138 'dir': 'HTMLDirectoryElement',
7139 'div': 'HTMLDivElement',
7140 'dl': 'HTMLDListElement',
7141 'embed': 'HTMLEmbedElement',
7142 'fieldset': 'HTMLFieldSetElement',
7143 'font': 'HTMLFontElement',
7144 'form': 'HTMLFormElement',
7145 'frame': 'HTMLFrameElement',
7146 'frameset': 'HTMLFrameSetElement',
7147 'h1': 'HTMLHeadingElement',
7148 'head': 'HTMLHeadElement',
7149 'hr': 'HTMLHRElement',
7150 'html': 'HTMLHtmlElement',
7151 'iframe': 'HTMLIFrameElement',
7152 'img': 'HTMLImageElement',
7153 'input': 'HTMLInputElement',
7154 'keygen': 'HTMLKeygenElement',
7155 'label': 'HTMLLabelElement',
7156 'legend': 'HTMLLegendElement',
7157 'li': 'HTMLLIElement',
7158 'link': 'HTMLLinkElement',
7159 'map': 'HTMLMapElement',
7160 'marquee': 'HTMLMarqueeElement',
7161 'menu': 'HTMLMenuElement',
7162 'menuitem': 'HTMLMenuItemElement',
7163 'meta': 'HTMLMetaElement',
7164 'meter': 'HTMLMeterElement',
7165 'object': 'HTMLObjectElement',
7166 'ol': 'HTMLOListElement',
7167 'optgroup': 'HTMLOptGroupElement',
7168 'option': 'HTMLOptionElement',
7169 'output': 'HTMLOutputElement',
7170 'p': 'HTMLParagraphElement',
7171 'param': 'HTMLParamElement',
7172 'pre': 'HTMLPreElement',
7173 'progress': 'HTMLProgressElement',
7174 'q': 'HTMLQuoteElement',
7175 'script': 'HTMLScriptElement',
7176 'select': 'HTMLSelectElement',
7177 'shadow': 'HTMLShadowElement',
7178 'source': 'HTMLSourceElement',
7179 'span': 'HTMLSpanElement',
7180 'style': 'HTMLStyleElement',
7181 'table': 'HTMLTableElement',
7182 'tbody': 'HTMLTableSectionElement',
7183 // WebKit and Moz are wrong:
7184 // https://bugs.webkit.org/show_bug.cgi?id=111469
7185 // https://bugzilla.mozilla.org/show_bug.cgi?id=848096
7186 // 'td': 'HTMLTableCellElement',
7187 'template': 'HTMLTemplateElement',
7188 'textarea': 'HTMLTextAreaElement',
7189 'thead': 'HTMLTableSectionElement',
7190 'time': 'HTMLTimeElement',
7191 'title': 'HTMLTitleElement',
7192 'tr': 'HTMLTableRowElement',
7193 'track': 'HTMLTrackElement',
7194 'ul': 'HTMLUListElement',
7195 'video': 'HTMLVideoElement',
7196 };
7197
7198 function overrideConstructor(tagName) {
7199 var nativeConstructorName = elements[tagName];
7200 var nativeConstructor = window[nativeConstructorName];
7201 if (!nativeConstructor)
7202 return;
7203 var element = document.createElement(tagName);
7204 var wrapperConstructor = element.constructor;
7205 window[nativeConstructorName] = wrapperConstructor;
7206 }
7207
7208 Object.keys(elements).forEach(overrideConstructor);
7209
7210 Object.getOwnPropertyNames(scope.wrappers).forEach(function(name) {
7211 window[name] = scope.wrappers[name]
7212 });
7213
7214 })(window.ShadowDOMPolyfill);
7215
7216 /*
7217 * Copyright 2013 The Polymer Authors. All rights reserved.
7218 * Use of this source code is governed by a BSD-style
7219 * license that can be found in the LICENSE file.
7220 */
7221 (function() {
7222
7223 // convenient global
7224 window.wrap = ShadowDOMPolyfill.wrapIfNeeded;
7225 window.unwrap = ShadowDOMPolyfill.unwrapIfNeeded;
7226
7227 // users may want to customize other types
7228 // TODO(sjmiles): 'button' is now supported by ShadowDOMPolyfill, but
7229 // I've left this code here in case we need to temporarily patch another
7230 // type
7231 /*
7232 (function() {
7233 var elts = {HTMLButtonElement: 'button'};
7234 for (var c in elts) {
7235 window[c] = function() { throw 'Patched Constructor'; };
7236 window[c].prototype = Object.getPrototypeOf(
7237 document.createElement(elts[c]));
7238 }
7239 })();
7240 */
7241
7242 // patch in prefixed name
7243 Object.defineProperty(Element.prototype, 'webkitShadowRoot',
7244 Object.getOwnPropertyDescriptor(Element.prototype, 'shadowRoot'));
7245
7246 var originalCreateShadowRoot = Element.prototype.createShadowRoot;
7247 Element.prototype.createShadowRoot = function() {
7248 var root = originalCreateShadowRoot.call(this);
7249 CustomElements.watchShadow(this);
7250 return root;
7251 };
7252
7253 Element.prototype.webkitCreateShadowRoot = Element.prototype.createShadowRoot;
7254 })();
7255
7256 /*
7257 * Copyright 2012 The Polymer Authors. All rights reserved.
7258 * Use of this source code is governed by a BSD-style
7259 * license that can be found in the LICENSE file.
7260 */
7261
7262 /*
7263 This is a limited shim for ShadowDOM css styling.
7264 https://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/shadow/index.html#style s
7265
7266 The intention here is to support only the styling features which can be
7267 relatively simply implemented. The goal is to allow users to avoid the
7268 most obvious pitfalls and do so without compromising performance significantly .
7269 For ShadowDOM styling that's not covered here, a set of best practices
7270 can be provided that should allow users to accomplish more complex styling.
7271
7272 The following is a list of specific ShadowDOM styling features and a brief
7273 discussion of the approach used to shim.
7274
7275 Shimmed features:
7276
7277 * :host, :host-context: ShadowDOM allows styling of the shadowRoot's host
7278 element using the :host rule. To shim this feature, the :host styles are
7279 reformatted and prefixed with a given scope name and promoted to a
7280 document level stylesheet.
7281 For example, given a scope name of .foo, a rule like this:
7282
7283 :host {
7284 background: red;
7285 }
7286 }
7287
7288 becomes:
7289
7290 .foo {
7291 background: red;
7292 }
7293
7294 * encapsultion: Styles defined within ShadowDOM, apply only to
7295 dom inside the ShadowDOM. Polymer uses one of two techniques to imlement
7296 this feature.
7297
7298 By default, rules are prefixed with the host element tag name
7299 as a descendant selector. This ensures styling does not leak out of the 'top'
7300 of the element's ShadowDOM. For example,
7301
7302 div {
7303 font-weight: bold;
7304 }
7305
7306 becomes:
7307
7308 x-foo div {
7309 font-weight: bold;
7310 }
7311
7312 becomes:
7313
7314
7315 Alternatively, if Platform.ShadowCSS.strictStyling is set to true then
7316 selectors are scoped by adding an attribute selector suffix to each
7317 simple selector that contains the host element tag name. Each element
7318 in the element's ShadowDOM template is also given the scope attribute.
7319 Thus, these rules match only elements that have the scope attribute.
7320 For example, given a scope name of x-foo, a rule like this:
7321
7322 div {
7323 font-weight: bold;
7324 }
7325
7326 becomes:
7327
7328 div[x-foo] {
7329 font-weight: bold;
7330 }
7331
7332 Note that elements that are dynamically added to a scope must have the scope
7333 selector added to them manually.
7334
7335 * upper/lower bound encapsulation: Styles which are defined outside a
7336 shadowRoot should not cross the ShadowDOM boundary and should not apply
7337 inside a shadowRoot.
7338
7339 This styling behavior is not emulated. Some possible ways to do this that
7340 were rejected due to complexity and/or performance concerns include: (1) reset
7341 every possible property for every possible selector for a given scope name;
7342 (2) re-implement css in javascript.
7343
7344 As an alternative, users should make sure to use selectors
7345 specific to the scope in which they are working.
7346
7347 * ::distributed: This behavior is not emulated. It's often not necessary
7348 to style the contents of a specific insertion point and instead, descendants
7349 of the host element can be styled selectively. Users can also create an
7350 extra node around an insertion point and style that node's contents
7351 via descendent selectors. For example, with a shadowRoot like this:
7352
7353 <style>
7354 ::content(div) {
7355 background: red;
7356 }
7357 </style>
7358 <content></content>
7359
7360 could become:
7361
7362 <style>
7363 / *@polyfill .content-container div * /
7364 ::content(div) {
7365 background: red;
7366 }
7367 </style>
7368 <div class="content-container">
7369 <content></content>
7370 </div>
7371
7372 Note the use of @polyfill in the comment above a ShadowDOM specific style
7373 declaration. This is a directive to the styling shim to use the selector
7374 in comments in lieu of the next selector when running under polyfill.
7375 */
7376 (function(scope) {
7377
7378 var ShadowCSS = {
7379 strictStyling: false,
7380 registry: {},
7381 // Shim styles for a given root associated with a name and extendsName
7382 // 1. cache root styles by name
7383 // 2. optionally tag root nodes with scope name
7384 // 3. shim polyfill directives /* @polyfill */ and /* @polyfill-rule */
7385 // 4. shim :host and scoping
7386 shimStyling: function(root, name, extendsName) {
7387 var scopeStyles = this.prepareRoot(root, name, extendsName);
7388 var typeExtension = this.isTypeExtension(extendsName);
7389 var scopeSelector = this.makeScopeSelector(name, typeExtension);
7390 // use caching to make working with styles nodes easier and to facilitate
7391 // lookup of extendee
7392 var cssText = stylesToCssText(scopeStyles, true);
7393 cssText = this.scopeCssText(cssText, scopeSelector);
7394 // cache shimmed css on root for user extensibility
7395 if (root) {
7396 root.shimmedStyle = cssText;
7397 }
7398 // add style to document
7399 this.addCssToDocument(cssText, name);
7400 },
7401 /*
7402 * Shim a style element with the given selector. Returns cssText that can
7403 * be included in the document via Platform.ShadowCSS.addCssToDocument(css).
7404 */
7405 shimStyle: function(style, selector) {
7406 return this.shimCssText(style.textContent, selector);
7407 },
7408 /*
7409 * Shim some cssText with the given selector. Returns cssText that can
7410 * be included in the document via Platform.ShadowCSS.addCssToDocument(css).
7411 */
7412 shimCssText: function(cssText, selector) {
7413 cssText = this.insertDirectives(cssText);
7414 return this.scopeCssText(cssText, selector);
7415 },
7416 makeScopeSelector: function(name, typeExtension) {
7417 if (name) {
7418 return typeExtension ? '[is=' + name + ']' : name;
7419 }
7420 return '';
7421 },
7422 isTypeExtension: function(extendsName) {
7423 return extendsName && extendsName.indexOf('-') < 0;
7424 },
7425 prepareRoot: function(root, name, extendsName) {
7426 var def = this.registerRoot(root, name, extendsName);
7427 this.replaceTextInStyles(def.rootStyles, this.insertDirectives);
7428 // remove existing style elements
7429 this.removeStyles(root, def.rootStyles);
7430 // apply strict attr
7431 if (this.strictStyling) {
7432 this.applyScopeToContent(root, name);
7433 }
7434 return def.scopeStyles;
7435 },
7436 removeStyles: function(root, styles) {
7437 for (var i=0, l=styles.length, s; (i<l) && (s=styles[i]); i++) {
7438 s.parentNode.removeChild(s);
7439 }
7440 },
7441 registerRoot: function(root, name, extendsName) {
7442 var def = this.registry[name] = {
7443 root: root,
7444 name: name,
7445 extendsName: extendsName
7446 }
7447 var styles = this.findStyles(root);
7448 def.rootStyles = styles;
7449 def.scopeStyles = def.rootStyles;
7450 var extendee = this.registry[def.extendsName];
7451 if (extendee) {
7452 def.scopeStyles = extendee.scopeStyles.concat(def.scopeStyles);
7453 }
7454 return def;
7455 },
7456 findStyles: function(root) {
7457 if (!root) {
7458 return [];
7459 }
7460 var styles = root.querySelectorAll('style');
7461 return Array.prototype.filter.call(styles, function(s) {
7462 return !s.hasAttribute(NO_SHIM_ATTRIBUTE);
7463 });
7464 },
7465 applyScopeToContent: function(root, name) {
7466 if (root) {
7467 // add the name attribute to each node in root.
7468 Array.prototype.forEach.call(root.querySelectorAll('*'),
7469 function(node) {
7470 node.setAttribute(name, '');
7471 });
7472 // and template contents too
7473 Array.prototype.forEach.call(root.querySelectorAll('template'),
7474 function(template) {
7475 this.applyScopeToContent(template.content, name);
7476 },
7477 this);
7478 }
7479 },
7480 insertDirectives: function(cssText) {
7481 cssText = this.insertPolyfillDirectivesInCssText(cssText);
7482 return this.insertPolyfillRulesInCssText(cssText);
7483 },
7484 /*
7485 * Process styles to convert native ShadowDOM rules that will trip
7486 * up the css parser; we rely on decorating the stylesheet with inert rules.
7487 *
7488 * For example, we convert this rule:
7489 *
7490 * polyfill-next-selector { content: ':host menu-item'; }
7491 * ::content menu-item {
7492 *
7493 * to this:
7494 *
7495 * scopeName menu-item {
7496 *
7497 **/
7498 insertPolyfillDirectivesInCssText: function(cssText) {
7499 // TODO(sorvell): remove either content or comment
7500 cssText = cssText.replace(cssCommentNextSelectorRe, function(match, p1) {
7501 // remove end comment delimiter and add block start
7502 return p1.slice(0, -2) + '{';
7503 });
7504 return cssText.replace(cssContentNextSelectorRe, function(match, p1) {
7505 return p1 + ' {';
7506 });
7507 },
7508 /*
7509 * Process styles to add rules which will only apply under the polyfill
7510 *
7511 * For example, we convert this rule:
7512 *
7513 * polyfill-rule {
7514 * content: ':host menu-item';
7515 * ...
7516 * }
7517 *
7518 * to this:
7519 *
7520 * scopeName menu-item {...}
7521 *
7522 **/
7523 insertPolyfillRulesInCssText: function(cssText) {
7524 // TODO(sorvell): remove either content or comment
7525 cssText = cssText.replace(cssCommentRuleRe, function(match, p1) {
7526 // remove end comment delimiter
7527 return p1.slice(0, -1);
7528 });
7529 return cssText.replace(cssContentRuleRe, function(match, p1, p2, p3) {
7530 var rule = match.replace(p1, '').replace(p2, '');
7531 return p3 + rule;
7532 });
7533 },
7534 /* Ensure styles are scoped. Pseudo-scoping takes a rule like:
7535 *
7536 * .foo {... }
7537 *
7538 * and converts this to
7539 *
7540 * scopeName .foo { ... }
7541 */
7542 scopeCssText: function(cssText, scopeSelector) {
7543 var unscoped = this.extractUnscopedRulesFromCssText(cssText);
7544 cssText = this.insertPolyfillHostInCssText(cssText);
7545 cssText = this.convertColonHost(cssText);
7546 cssText = this.convertColonHostContext(cssText);
7547 cssText = this.convertCombinators(cssText);
7548 if (scopeSelector) {
7549 var self = this, cssText;
7550 withCssRules(cssText, function(rules) {
7551 cssText = self.scopeRules(rules, scopeSelector);
7552 });
7553
7554 }
7555 cssText = cssText + '\n' + unscoped;
7556 return cssText.trim();
7557 },
7558 /*
7559 * Process styles to add rules which will only apply under the polyfill
7560 * and do not process via CSSOM. (CSSOM is destructive to rules on rare
7561 * occasions, e.g. -webkit-calc on Safari.)
7562 * For example, we convert this rule:
7563 *
7564 * (comment start) @polyfill-unscoped-rule menu-item {
7565 * ... } (comment end)
7566 *
7567 * to this:
7568 *
7569 * menu-item {...}
7570 *
7571 **/
7572 extractUnscopedRulesFromCssText: function(cssText) {
7573 // TODO(sorvell): remove either content or comment
7574 var r = '', m;
7575 while (m = cssCommentUnscopedRuleRe.exec(cssText)) {
7576 r += m[1].slice(0, -1) + '\n\n';
7577 }
7578 while (m = cssContentUnscopedRuleRe.exec(cssText)) {
7579 r += m[0].replace(m[2], '').replace(m[1], m[3]) + '\n\n';
7580 }
7581 return r;
7582 },
7583 /*
7584 * convert a rule like :host(.foo) > .bar { }
7585 *
7586 * to
7587 *
7588 * scopeName.foo > .bar
7589 */
7590 convertColonHost: function(cssText) {
7591 return this.convertColonRule(cssText, cssColonHostRe,
7592 this.colonHostPartReplacer);
7593 },
7594 /*
7595 * convert a rule like :host-context(.foo) > .bar { }
7596 *
7597 * to
7598 *
7599 * scopeName.foo > .bar, .foo scopeName > .bar { }
7600 *
7601 * and
7602 *
7603 * :host-context(.foo:host) .bar { ... }
7604 *
7605 * to
7606 *
7607 * scopeName.foo .bar { ... }
7608 */
7609 convertColonHostContext: function(cssText) {
7610 return this.convertColonRule(cssText, cssColonHostContextRe,
7611 this.colonHostContextPartReplacer);
7612 },
7613 convertColonRule: function(cssText, regExp, partReplacer) {
7614 // p1 = :host, p2 = contents of (), p3 rest of rule
7615 return cssText.replace(regExp, function(m, p1, p2, p3) {
7616 p1 = polyfillHostNoCombinator;
7617 if (p2) {
7618 var parts = p2.split(','), r = [];
7619 for (var i=0, l=parts.length, p; (i<l) && (p=parts[i]); i++) {
7620 p = p.trim();
7621 r.push(partReplacer(p1, p, p3));
7622 }
7623 return r.join(',');
7624 } else {
7625 return p1 + p3;
7626 }
7627 });
7628 },
7629 colonHostContextPartReplacer: function(host, part, suffix) {
7630 if (part.match(polyfillHost)) {
7631 return this.colonHostPartReplacer(host, part, suffix);
7632 } else {
7633 return host + part + suffix + ', ' + part + ' ' + host + suffix;
7634 }
7635 },
7636 colonHostPartReplacer: function(host, part, suffix) {
7637 return host + part.replace(polyfillHost, '') + suffix;
7638 },
7639 /*
7640 * Convert ^ and ^^ combinators by replacing with space.
7641 */
7642 convertCombinators: function(cssText) {
7643 for (var i=0; i < combinatorsRe.length; i++) {
7644 cssText = cssText.replace(combinatorsRe[i], ' ');
7645 }
7646 return cssText;
7647 },
7648 // change a selector like 'div' to 'name div'
7649 scopeRules: function(cssRules, scopeSelector) {
7650 var cssText = '';
7651 if (cssRules) {
7652 Array.prototype.forEach.call(cssRules, function(rule) {
7653 if (rule.selectorText && (rule.style && rule.style.cssText)) {
7654 cssText += this.scopeSelector(rule.selectorText, scopeSelector,
7655 this.strictStyling) + ' {\n\t';
7656 cssText += this.propertiesFromRule(rule) + '\n}\n\n';
7657 } else if (rule.type === CSSRule.MEDIA_RULE) {
7658 cssText += '@media ' + rule.media.mediaText + ' {\n';
7659 cssText += this.scopeRules(rule.cssRules, scopeSelector);
7660 cssText += '\n}\n\n';
7661 } else if (rule.cssText) {
7662 cssText += rule.cssText + '\n\n';
7663 }
7664 }, this);
7665 }
7666 return cssText;
7667 },
7668 scopeSelector: function(selector, scopeSelector, strict) {
7669 var r = [], parts = selector.split(',');
7670 parts.forEach(function(p) {
7671 p = p.trim();
7672 if (this.selectorNeedsScoping(p, scopeSelector)) {
7673 p = (strict && !p.match(polyfillHostNoCombinator)) ?
7674 this.applyStrictSelectorScope(p, scopeSelector) :
7675 this.applySimpleSelectorScope(p, scopeSelector);
7676 }
7677 r.push(p);
7678 }, this);
7679 return r.join(', ');
7680 },
7681 selectorNeedsScoping: function(selector, scopeSelector) {
7682 var re = this.makeScopeMatcher(scopeSelector);
7683 return !selector.match(re);
7684 },
7685 makeScopeMatcher: function(scopeSelector) {
7686 scopeSelector = scopeSelector.replace(/\[/g, '\\[').replace(/\[/g, '\\]');
7687 return new RegExp('^(' + scopeSelector + ')' + selectorReSuffix, 'm');
7688 },
7689 // scope via name and [is=name]
7690 applySimpleSelectorScope: function(selector, scopeSelector) {
7691 if (selector.match(polyfillHostRe)) {
7692 selector = selector.replace(polyfillHostNoCombinator, scopeSelector);
7693 return selector.replace(polyfillHostRe, scopeSelector + ' ');
7694 } else {
7695 return scopeSelector + ' ' + selector;
7696 }
7697 },
7698 // return a selector with [name] suffix on each simple selector
7699 // e.g. .foo.bar > .zot becomes .foo[name].bar[name] > .zot[name]
7700 applyStrictSelectorScope: function(selector, scopeSelector) {
7701 scopeSelector = scopeSelector.replace(/\[is=([^\]]*)\]/g, '$1');
7702 var splits = [' ', '>', '+', '~'],
7703 scoped = selector,
7704 attrName = '[' + scopeSelector + ']';
7705 splits.forEach(function(sep) {
7706 var parts = scoped.split(sep);
7707 scoped = parts.map(function(p) {
7708 // remove :host since it should be unnecessary
7709 var t = p.trim().replace(polyfillHostRe, '');
7710 if (t && (splits.indexOf(t) < 0) && (t.indexOf(attrName) < 0)) {
7711 p = t.replace(/([^:]*)(:*)(.*)/, '$1' + attrName + '$2$3')
7712 }
7713 return p;
7714 }).join(sep);
7715 });
7716 return scoped;
7717 },
7718 insertPolyfillHostInCssText: function(selector) {
7719 return selector.replace(colonHostContextRe, polyfillHostContext).replace(
7720 colonHostRe, polyfillHost);
7721 },
7722 propertiesFromRule: function(rule) {
7723 var cssText = rule.style.cssText;
7724 // TODO(sorvell): Safari cssom incorrectly removes quotes from the content
7725 // property. (https://bugs.webkit.org/show_bug.cgi?id=118045)
7726 // don't replace attr rules
7727 if (rule.style.content && !rule.style.content.match(/['"]+|attr/)) {
7728 cssText = cssText.replace(/content:[^;]*;/g, 'content: \'' +
7729 rule.style.content + '\';');
7730 }
7731 // TODO(sorvell): we can workaround this issue here, but we need a list
7732 // of troublesome properties to fix https://github.com/Polymer/platform/issu es/53
7733 //
7734 // inherit rules can be omitted from cssText
7735 // TODO(sorvell): remove when Blink bug is fixed:
7736 // https://code.google.com/p/chromium/issues/detail?id=358273
7737 var style = rule.style;
7738 for (var i in style) {
7739 if (style[i] === 'initial') {
7740 cssText += i + ': initial; ';
7741 }
7742 }
7743 return cssText;
7744 },
7745 replaceTextInStyles: function(styles, action) {
7746 if (styles && action) {
7747 if (!(styles instanceof Array)) {
7748 styles = [styles];
7749 }
7750 Array.prototype.forEach.call(styles, function(s) {
7751 s.textContent = action.call(this, s.textContent);
7752 }, this);
7753 }
7754 },
7755 addCssToDocument: function(cssText, name) {
7756 if (cssText.match('@import')) {
7757 addOwnSheet(cssText, name);
7758 } else {
7759 addCssToDocument(cssText);
7760 }
7761 }
7762 };
7763
7764 var selectorRe = /([^{]*)({[\s\S]*?})/gim,
7765 cssCommentRe = /\/\*[^*]*\*+([^/*][^*]*\*+)*\//gim,
7766 // TODO(sorvell): remove either content or comment
7767 cssCommentNextSelectorRe = /\/\*\s*@polyfill ([^*]*\*+([^/*][^*]*\*+)*\/)([^ {]*?){/gim,
7768 cssContentNextSelectorRe = /polyfill-next-selector[^}]*content\:[\s]*'([^']* )'[^}]*}([^{]*?){/gim,
7769 // TODO(sorvell): remove either content or comment
7770 cssCommentRuleRe = /\/\*\s@polyfill-rule([^*]*\*+([^/*][^*]*\*+)*)\//gim,
7771 cssContentRuleRe = /(polyfill-rule)[^}]*(content\:[\s]*'([^']*)'[^;]*;)[^}]* }/gim,
7772 // TODO(sorvell): remove either content or comment
7773 cssCommentUnscopedRuleRe = /\/\*\s@polyfill-unscoped-rule([^*]*\*+([^/*][^*] *\*+)*)\//gim,
7774 cssContentUnscopedRuleRe = /(polyfill-unscoped-rule)[^}]*(content\:[\s]*'([^ ']*)'[^;]*;)[^}]*}/gim,
7775 cssPseudoRe = /::(x-[^\s{,(]*)/gim,
7776 cssPartRe = /::part\(([^)]*)\)/gim,
7777 // note: :host pre-processed to -shadowcsshost.
7778 polyfillHost = '-shadowcsshost',
7779 // note: :host-context pre-processed to -shadowcsshostcontext.
7780 polyfillHostContext = '-shadowcsscontext',
7781 parenSuffix = ')(?:\\((' +
7782 '(?:\\([^)(]*\\)|[^)(]*)+?' +
7783 ')\\))?([^,{]*)';
7784 cssColonHostRe = new RegExp('(' + polyfillHost + parenSuffix, 'gim'),
7785 cssColonHostContextRe = new RegExp('(' + polyfillHostContext + parenSuffix, 'gim'),
7786 selectorReSuffix = '([>\\s~+\[.,{:][\\s\\S]*)?$',
7787 colonHostRe = /\:host/gim,
7788 colonHostContextRe = /\:host-context/gim,
7789 /* host name without combinator */
7790 polyfillHostNoCombinator = polyfillHost + '-no-combinator',
7791 polyfillHostRe = new RegExp(polyfillHost, 'gim'),
7792 polyfillHostContextRe = new RegExp(polyfillHostContext, 'gim'),
7793 combinatorsRe = [
7794 /\^\^/g,
7795 /\^/g,
7796 /\/shadow\//g,
7797 /\/shadow-deep\//g,
7798 /::shadow/g,
7799 /\/deep\//g
7800 ];
7801
7802 function stylesToCssText(styles, preserveComments) {
7803 var cssText = '';
7804 Array.prototype.forEach.call(styles, function(s) {
7805 cssText += s.textContent + '\n\n';
7806 });
7807 // strip comments for easier processing
7808 if (!preserveComments) {
7809 cssText = cssText.replace(cssCommentRe, '');
7810 }
7811 return cssText;
7812 }
7813
7814 function cssTextToStyle(cssText) {
7815 var style = document.createElement('style');
7816 style.textContent = cssText;
7817 return style;
7818 }
7819
7820 function cssToRules(cssText) {
7821 var style = cssTextToStyle(cssText);
7822 document.head.appendChild(style);
7823 var rules = [];
7824 if (style.sheet) {
7825 // TODO(sorvell): Firefox throws when accessing the rules of a stylesheet
7826 // with an @import
7827 // https://bugzilla.mozilla.org/show_bug.cgi?id=625013
7828 try {
7829 rules = style.sheet.cssRules;
7830 } catch(e) {
7831 //
7832 }
7833 } else {
7834 console.warn('sheet not found', style);
7835 }
7836 style.parentNode.removeChild(style);
7837 return rules;
7838 }
7839
7840 var frame = document.createElement('iframe');
7841 frame.style.display = 'none';
7842
7843 function initFrame() {
7844 frame.initialized = true;
7845 document.body.appendChild(frame);
7846 var doc = frame.contentDocument;
7847 var base = doc.createElement('base');
7848 base.href = document.baseURI;
7849 doc.head.appendChild(base);
7850 }
7851
7852 function inFrame(fn) {
7853 if (!frame.initialized) {
7854 initFrame();
7855 }
7856 document.body.appendChild(frame);
7857 fn(frame.contentDocument);
7858 document.body.removeChild(frame);
7859 }
7860
7861 // TODO(sorvell): use an iframe if the cssText contains an @import to workaround
7862 // https://code.google.com/p/chromium/issues/detail?id=345114
7863 var isChrome = navigator.userAgent.match('Chrome');
7864 function withCssRules(cssText, callback) {
7865 if (!callback) {
7866 return;
7867 }
7868 var rules;
7869 if (cssText.match('@import') && isChrome) {
7870 var style = cssTextToStyle(cssText);
7871 inFrame(function(doc) {
7872 doc.head.appendChild(style.impl);
7873 rules = style.sheet.cssRules;
7874 callback(rules);
7875 });
7876 } else {
7877 rules = cssToRules(cssText);
7878 callback(rules);
7879 }
7880 }
7881
7882 function rulesToCss(cssRules) {
7883 for (var i=0, css=[]; i < cssRules.length; i++) {
7884 css.push(cssRules[i].cssText);
7885 }
7886 return css.join('\n\n');
7887 }
7888
7889 function addCssToDocument(cssText) {
7890 if (cssText) {
7891 getSheet().appendChild(document.createTextNode(cssText));
7892 }
7893 }
7894
7895 function addOwnSheet(cssText, name) {
7896 var style = cssTextToStyle(cssText);
7897 style.setAttribute(name, '');
7898 style.setAttribute(SHIMMED_ATTRIBUTE, '');
7899 document.head.appendChild(style);
7900 }
7901
7902 var SHIM_ATTRIBUTE = 'shim-shadowdom';
7903 var SHIMMED_ATTRIBUTE = 'shim-shadowdom-css';
7904 var NO_SHIM_ATTRIBUTE = 'no-shim';
7905
7906 var sheet;
7907 function getSheet() {
7908 if (!sheet) {
7909 sheet = document.createElement("style");
7910 sheet.setAttribute(SHIMMED_ATTRIBUTE, '');
7911 sheet[SHIMMED_ATTRIBUTE] = true;
7912 }
7913 return sheet;
7914 }
7915
7916 // add polyfill stylesheet to document
7917 if (window.ShadowDOMPolyfill) {
7918 addCssToDocument('style { display: none !important; }\n');
7919 var doc = wrap(document);
7920 var head = doc.querySelector('head');
7921 head.insertBefore(getSheet(), head.childNodes[0]);
7922
7923 // TODO(sorvell): monkey-patching HTMLImports is abusive;
7924 // consider a better solution.
7925 document.addEventListener('DOMContentLoaded', function() {
7926 var urlResolver = scope.urlResolver;
7927
7928 if (window.HTMLImports && !HTMLImports.useNative) {
7929 var SHIM_SHEET_SELECTOR = 'link[rel=stylesheet]' +
7930 '[' + SHIM_ATTRIBUTE + ']';
7931 var SHIM_STYLE_SELECTOR = 'style[' + SHIM_ATTRIBUTE + ']';
7932 HTMLImports.importer.documentPreloadSelectors += ',' + SHIM_SHEET_SELECTOR ;
7933 HTMLImports.importer.importsPreloadSelectors += ',' + SHIM_SHEET_SELECTOR;
7934
7935 HTMLImports.parser.documentSelectors = [
7936 HTMLImports.parser.documentSelectors,
7937 SHIM_SHEET_SELECTOR,
7938 SHIM_STYLE_SELECTOR
7939 ].join(',');
7940
7941 var originalParseGeneric = HTMLImports.parser.parseGeneric;
7942
7943 HTMLImports.parser.parseGeneric = function(elt) {
7944 if (elt[SHIMMED_ATTRIBUTE]) {
7945 return;
7946 }
7947 var style = elt.__importElement || elt;
7948 if (!style.hasAttribute(SHIM_ATTRIBUTE)) {
7949 originalParseGeneric.call(this, elt);
7950 return;
7951 }
7952 if (elt.__resource) {
7953 style = elt.ownerDocument.createElement('style');
7954 style.textContent = urlResolver.resolveCssText(
7955 elt.__resource, elt.href);
7956 } else {
7957 urlResolver.resolveStyle(style);
7958 }
7959 style.textContent = ShadowCSS.shimStyle(style);
7960 style.removeAttribute(SHIM_ATTRIBUTE, '');
7961 style.setAttribute(SHIMMED_ATTRIBUTE, '');
7962 style[SHIMMED_ATTRIBUTE] = true;
7963 // place in document
7964 if (style.parentNode !== head) {
7965 // replace links in head
7966 if (elt.parentNode === head) {
7967 head.replaceChild(style, elt);
7968 } else {
7969 head.appendChild(style);
7970 }
7971 }
7972 style.__importParsed = true;
7973 this.markParsingComplete(elt);
7974 }
7975
7976 var hasResource = HTMLImports.parser.hasResource;
7977 HTMLImports.parser.hasResource = function(node) {
7978 if (node.localName === 'link' && node.rel === 'stylesheet' &&
7979 node.hasAttribute(SHIM_ATTRIBUTE)) {
7980 return (node.__resource);
7981 } else {
7982 return hasResource.call(this, node);
7983 }
7984 }
7985
7986 }
7987 });
7988 }
7989
7990 // exports
7991 scope.ShadowCSS = ShadowCSS;
7992
7993 })(window.Platform);
7994 } else {
7995 /*
7996 * Copyright 2013 The Polymer Authors. All rights reserved.
7997 * Use of this source code is governed by a BSD-style
7998 * license that can be found in the LICENSE file.
7999 */
8000 (function() {
8001
8002 // poor man's adapter for template.content on various platform scenarios
8003 window.templateContent = window.templateContent || function(inTemplate) {
8004 return inTemplate.content;
8005 };
8006
8007 // so we can call wrap/unwrap without testing for ShadowDOMPolyfill
8008
8009 window.wrap = window.unwrap = function(n){
8010 return n;
8011 }
8012
8013 addEventListener('DOMContentLoaded', function() {
8014 if (CustomElements.useNative === false) {
8015 var originalCreateShadowRoot = Element.prototype.createShadowRoot;
8016 Element.prototype.createShadowRoot = function() {
8017 var root = originalCreateShadowRoot.call(this);
8018 CustomElements.watchShadow(this);
8019 return root;
8020 };
8021 }
8022 });
8023
8024 window.templateContent = function(inTemplate) {
8025 // if MDV exists, it may need to boostrap this template to reveal content
8026 if (window.HTMLTemplateElement && HTMLTemplateElement.bootstrap) {
8027 HTMLTemplateElement.bootstrap(inTemplate);
8028 }
8029 // fallback when there is no Shadow DOM polyfill, no MDV polyfill, and no
8030 // native template support
8031 if (!inTemplate.content && !inTemplate._content) {
8032 var frag = document.createDocumentFragment();
8033 while (inTemplate.firstChild) {
8034 frag.appendChild(inTemplate.firstChild);
8035 }
8036 inTemplate._content = frag;
8037 }
8038 return inTemplate.content || inTemplate._content;
8039 };
8040
8041 })();
8042 }
8043 /* Any copyright is dedicated to the Public Domain.
8044 * http://creativecommons.org/publicdomain/zero/1.0/ */
8045
8046 (function(scope) {
8047 'use strict';
8048
8049 // feature detect for URL constructor
8050 var hasWorkingUrl = false;
8051 if (!scope.forceJURL) {
8052 try {
8053 var u = new URL('b', 'http://a');
8054 hasWorkingUrl = u.href === 'http://a/b';
8055 } catch(e) {}
8056 }
8057
8058 if (hasWorkingUrl)
8059 return;
8060
8061 var relative = Object.create(null);
8062 relative['ftp'] = 21;
8063 relative['file'] = 0;
8064 relative['gopher'] = 70;
8065 relative['http'] = 80;
8066 relative['https'] = 443;
8067 relative['ws'] = 80;
8068 relative['wss'] = 443;
8069
8070 var relativePathDotMapping = Object.create(null);
8071 relativePathDotMapping['%2e'] = '.';
8072 relativePathDotMapping['.%2e'] = '..';
8073 relativePathDotMapping['%2e.'] = '..';
8074 relativePathDotMapping['%2e%2e'] = '..';
8075
8076 function isRelativeScheme(scheme) {
8077 return relative[scheme] !== undefined;
8078 }
8079
8080 function invalid() {
8081 clear.call(this);
8082 this._isInvalid = true;
8083 }
8084
8085 function IDNAToASCII(h) {
8086 if ('' == h) {
8087 invalid.call(this)
8088 }
8089 // XXX
8090 return h.toLowerCase()
8091 }
8092
8093 function percentEscape(c) {
8094 var unicode = c.charCodeAt(0);
8095 if (unicode > 0x20 &&
8096 unicode < 0x7F &&
8097 // " # < > ? `
8098 [0x22, 0x23, 0x3C, 0x3E, 0x3F, 0x60].indexOf(unicode) == -1
8099 ) {
8100 return c;
8101 }
8102 return encodeURIComponent(c);
8103 }
8104
8105 function percentEscapeQuery(c) {
8106 // XXX This actually needs to encode c using encoding and then
8107 // convert the bytes one-by-one.
8108
8109 var unicode = c.charCodeAt(0);
8110 if (unicode > 0x20 &&
8111 unicode < 0x7F &&
8112 // " # < > ` (do not escape '?')
8113 [0x22, 0x23, 0x3C, 0x3E, 0x60].indexOf(unicode) == -1
8114 ) {
8115 return c;
8116 }
8117 return encodeURIComponent(c);
8118 }
8119
8120 var EOF = undefined,
8121 ALPHA = /[a-zA-Z]/,
8122 ALPHANUMERIC = /[a-zA-Z0-9\+\-\.]/;
8123
8124 function parse(input, stateOverride, base) {
8125 function err(message) {
8126 errors.push(message)
8127 }
8128
8129 var state = stateOverride || 'scheme start',
8130 cursor = 0,
8131 buffer = '',
8132 seenAt = false,
8133 seenBracket = false,
8134 errors = [];
8135
8136 loop: while ((input[cursor - 1] != EOF || cursor == 0) && !this._isInvalid) {
8137 var c = input[cursor];
8138 switch (state) {
8139 case 'scheme start':
8140 if (c && ALPHA.test(c)) {
8141 buffer += c.toLowerCase(); // ASCII-safe
8142 state = 'scheme';
8143 } else if (!stateOverride) {
8144 buffer = '';
8145 state = 'no scheme';
8146 continue;
8147 } else {
8148 err('Invalid scheme.');
8149 break loop;
8150 }
8151 break;
8152
8153 case 'scheme':
8154 if (c && ALPHANUMERIC.test(c)) {
8155 buffer += c.toLowerCase(); // ASCII-safe
8156 } else if (':' == c) {
8157 this._scheme = buffer;
8158 buffer = '';
8159 if (stateOverride) {
8160 break loop;
8161 }
8162 if (isRelativeScheme(this._scheme)) {
8163 this._isRelative = true;
8164 }
8165 if ('file' == this._scheme) {
8166 state = 'relative';
8167 } else if (this._isRelative && base && base._scheme == this._scheme) {
8168 state = 'relative or authority';
8169 } else if (this._isRelative) {
8170 state = 'authority first slash';
8171 } else {
8172 state = 'scheme data';
8173 }
8174 } else if (!stateOverride) {
8175 buffer = '';
8176 cursor = 0;
8177 state = 'no scheme';
8178 continue;
8179 } else if (EOF == c) {
8180 break loop;
8181 } else {
8182 err('Code point not allowed in scheme: ' + c)
8183 break loop;
8184 }
8185 break;
8186
8187 case 'scheme data':
8188 if ('?' == c) {
8189 query = '?';
8190 state = 'query';
8191 } else if ('#' == c) {
8192 this._fragment = '#';
8193 state = 'fragment';
8194 } else {
8195 // XXX error handling
8196 if (EOF != c && '\t' != c && '\n' != c && '\r' != c) {
8197 this._schemeData += percentEscape(c);
8198 }
8199 }
8200 break;
8201
8202 case 'no scheme':
8203 if (!base || !(isRelativeScheme(base._scheme))) {
8204 err('Missing scheme.');
8205 invalid.call(this);
8206 } else {
8207 state = 'relative';
8208 continue;
8209 }
8210 break;
8211
8212 case 'relative or authority':
8213 if ('/' == c && '/' == input[cursor+1]) {
8214 state = 'authority ignore slashes';
8215 } else {
8216 err('Expected /, got: ' + c);
8217 state = 'relative';
8218 continue
8219 }
8220 break;
8221
8222 case 'relative':
8223 this._isRelative = true;
8224 if ('file' != this._scheme)
8225 this._scheme = base._scheme;
8226 if (EOF == c) {
8227 this._host = base._host;
8228 this._port = base._port;
8229 this._path = base._path.slice();
8230 this._query = base._query;
8231 break loop;
8232 } else if ('/' == c || '\\' == c) {
8233 if ('\\' == c)
8234 err('\\ is an invalid code point.');
8235 state = 'relative slash';
8236 } else if ('?' == c) {
8237 this._host = base._host;
8238 this._port = base._port;
8239 this._path = base._path.slice();
8240 this._query = '?';
8241 state = 'query';
8242 } else if ('#' == c) {
8243 this._host = base._host;
8244 this._port = base._port;
8245 this._path = base._path.slice();
8246 this._query = base._query;
8247 this._fragment = '#';
8248 state = 'fragment';
8249 } else {
8250 var nextC = input[cursor+1]
8251 var nextNextC = input[cursor+2]
8252 if (
8253 'file' != this._scheme || !ALPHA.test(c) ||
8254 (nextC != ':' && nextC != '|') ||
8255 (EOF != nextNextC && '/' != nextNextC && '\\' != nextNextC && '?' != nextNextC && '#' != nextNextC)) {
8256 this._host = base._host;
8257 this._port = base._port;
8258 this._path = base._path.slice();
8259 this._path.pop();
8260 }
8261 state = 'relative path';
8262 continue;
8263 }
8264 break;
8265
8266 case 'relative slash':
8267 if ('/' == c || '\\' == c) {
8268 if ('\\' == c) {
8269 err('\\ is an invalid code point.');
8270 }
8271 if ('file' == this._scheme) {
8272 state = 'file host';
8273 } else {
8274 state = 'authority ignore slashes';
8275 }
8276 } else {
8277 if ('file' != this._scheme) {
8278 this._host = base._host;
8279 this._port = base._port;
8280 }
8281 state = 'relative path';
8282 continue;
8283 }
8284 break;
8285
8286 case 'authority first slash':
8287 if ('/' == c) {
8288 state = 'authority second slash';
8289 } else {
8290 err("Expected '/', got: " + c);
8291 state = 'authority ignore slashes';
8292 continue;
8293 }
8294 break;
8295
8296 case 'authority second slash':
8297 state = 'authority ignore slashes';
8298 if ('/' != c) {
8299 err("Expected '/', got: " + c);
8300 continue;
8301 }
8302 break;
8303
8304 case 'authority ignore slashes':
8305 if ('/' != c && '\\' != c) {
8306 state = 'authority';
8307 continue;
8308 } else {
8309 err('Expected authority, got: ' + c);
8310 }
8311 break;
8312
8313 case 'authority':
8314 if ('@' == c) {
8315 if (seenAt) {
8316 err('@ already seen.');
8317 buffer += '%40';
8318 }
8319 seenAt = true;
8320 for (var i = 0; i < buffer.length; i++) {
8321 var cp = buffer[i];
8322 if ('\t' == cp || '\n' == cp || '\r' == cp) {
8323 err('Invalid whitespace in authority.');
8324 continue;
8325 }
8326 // XXX check URL code points
8327 if (':' == cp && null === this._password) {
8328 this._password = '';
8329 continue;
8330 }
8331 var tempC = percentEscape(cp);
8332 (null !== this._password) ? this._password += tempC : this._userna me += tempC;
8333 }
8334 buffer = '';
8335 } else if (EOF == c || '/' == c || '\\' == c || '?' == c || '#' == c) {
8336 cursor -= buffer.length;
8337 buffer = '';
8338 state = 'host';
8339 continue;
8340 } else {
8341 buffer += c;
8342 }
8343 break;
8344
8345 case 'file host':
8346 if (EOF == c || '/' == c || '\\' == c || '?' == c || '#' == c) {
8347 if (buffer.length == 2 && ALPHA.test(buffer[0]) && (buffer[1] == ':' || buffer[1] == '|')) {
8348 state = 'relative path';
8349 } else if (buffer.length == 0) {
8350 state = 'relative path start';
8351 } else {
8352 this._host = IDNAToASCII.call(this, buffer);
8353 buffer = '';
8354 state = 'relative path start';
8355 }
8356 continue;
8357 } else if ('\t' == c || '\n' == c || '\r' == c) {
8358 err('Invalid whitespace in file host.');
8359 } else {
8360 buffer += c;
8361 }
8362 break;
8363
8364 case 'host':
8365 case 'hostname':
8366 if (':' == c && !seenBracket) {
8367 // XXX host parsing
8368 this._host = IDNAToASCII.call(this, buffer);
8369 buffer = '';
8370 state = 'port';
8371 if ('hostname' == stateOverride) {
8372 break loop;
8373 }
8374 } else if (EOF == c || '/' == c || '\\' == c || '?' == c || '#' == c) {
8375 this._host = IDNAToASCII.call(this, buffer);
8376 buffer = '';
8377 state = 'relative path start';
8378 if (stateOverride) {
8379 break loop;
8380 }
8381 continue;
8382 } else if ('\t' != c && '\n' != c && '\r' != c) {
8383 if ('[' == c) {
8384 seenBracket = true;
8385 } else if (']' == c) {
8386 seenBracket = false;
8387 }
8388 buffer += c;
8389 } else {
8390 err('Invalid code point in host/hostname: ' + c);
8391 }
8392 break;
8393
8394 case 'port':
8395 if (/[0-9]/.test(c)) {
8396 buffer += c;
8397 } else if (EOF == c || '/' == c || '\\' == c || '?' == c || '#' == c | | stateOverride) {
8398 if ('' != buffer) {
8399 var temp = parseInt(buffer, 10);
8400 if (temp != relative[this._scheme]) {
8401 this._port = temp + '';
8402 }
8403 buffer = '';
8404 }
8405 if (stateOverride) {
8406 break loop;
8407 }
8408 state = 'relative path start';
8409 continue;
8410 } else if ('\t' == c || '\n' == c || '\r' == c) {
8411 err('Invalid code point in port: ' + c);
8412 } else {
8413 invalid.call(this);
8414 }
8415 break;
8416
8417 case 'relative path start':
8418 if ('\\' == c)
8419 err("'\\' not allowed in path.");
8420 state = 'relative path';
8421 if ('/' != c && '\\' != c) {
8422 continue;
8423 }
8424 break;
8425
8426 case 'relative path':
8427 if (EOF == c || '/' == c || '\\' == c || (!stateOverride && ('?' == c || '#' == c))) {
8428 if ('\\' == c) {
8429 err('\\ not allowed in relative path.');
8430 }
8431 var tmp;
8432 if (tmp = relativePathDotMapping[buffer.toLowerCase()]) {
8433 buffer = tmp;
8434 }
8435 if ('..' == buffer) {
8436 this._path.pop();
8437 if ('/' != c && '\\' != c) {
8438 this._path.push('');
8439 }
8440 } else if ('.' == buffer && '/' != c && '\\' != c) {
8441 this._path.push('');
8442 } else if ('.' != buffer) {
8443 if ('file' == this._scheme && this._path.length == 0 && buffer.len gth == 2 && ALPHA.test(buffer[0]) && buffer[1] == '|') {
8444 buffer = buffer[0] + ':';
8445 }
8446 this._path.push(buffer);
8447 }
8448 buffer = '';
8449 if ('?' == c) {
8450 this._query = '?';
8451 state = 'query';
8452 } else if ('#' == c) {
8453 this._fragment = '#';
8454 state = 'fragment';
8455 }
8456 } else if ('\t' != c && '\n' != c && '\r' != c) {
8457 buffer += percentEscape(c);
8458 }
8459 break;
8460
8461 case 'query':
8462 if (!stateOverride && '#' == c) {
8463 this._fragment = '#';
8464 state = 'fragment';
8465 } else if (EOF != c && '\t' != c && '\n' != c && '\r' != c) {
8466 this._query += percentEscapeQuery(c);
8467 }
8468 break;
8469
8470 case 'fragment':
8471 if (EOF != c && '\t' != c && '\n' != c && '\r' != c) {
8472 this._fragment += c;
8473 }
8474 break;
8475 }
8476
8477 cursor++;
8478 }
8479 }
8480
8481 function clear() {
8482 this._scheme = '';
8483 this._schemeData = '';
8484 this._username = '';
8485 this._password = null;
8486 this._host = '';
8487 this._port = '';
8488 this._path = [];
8489 this._query = '';
8490 this._fragment = '';
8491 this._isInvalid = false;
8492 this._isRelative = false;
8493 }
8494
8495 // Does not process domain names or IP addresses.
8496 // Does not handle encoding for the query parameter.
8497 function jURL(url, base /* , encoding */) {
8498 if (base !== undefined && !(base instanceof jURL))
8499 base = new jURL(String(base));
8500
8501 this._url = url;
8502 clear.call(this);
8503
8504 var input = url.replace(/^[ \t\r\n\f]+|[ \t\r\n\f]+$/g, '');
8505 // encoding = encoding || 'utf-8'
8506
8507 parse.call(this, input, null, base);
8508 }
8509
8510 jURL.prototype = {
8511 get href() {
8512 if (this._isInvalid)
8513 return this._url;
8514
8515 var authority = '';
8516 if ('' != this._username || null != this._password) {
8517 authority = this._username +
8518 (null != this._password ? ':' + this._password : '') + '@';
8519 }
8520
8521 return this.protocol +
8522 (this._isRelative ? '//' + authority + this.host : '') +
8523 this.pathname + this._query + this._fragment;
8524 },
8525 set href(href) {
8526 clear.call(this);
8527 parse.call(this, href);
8528 },
8529
8530 get protocol() {
8531 return this._scheme + ':';
8532 },
8533 set protocol(protocol) {
8534 if (this._isInvalid)
8535 return;
8536 parse.call(this, protocol + ':', 'scheme start');
8537 },
8538
8539 get host() {
8540 return this._isInvalid ? '' : this._port ?
8541 this._host + ':' + this._port : this._host;
8542 },
8543 set host(host) {
8544 if (this._isInvalid || !this._isRelative)
8545 return;
8546 parse.call(this, host, 'host');
8547 },
8548
8549 get hostname() {
8550 return this._host;
8551 },
8552 set hostname(hostname) {
8553 if (this._isInvalid || !this._isRelative)
8554 return;
8555 parse.call(this, hostname, 'hostname');
8556 },
8557
8558 get port() {
8559 return this._port;
8560 },
8561 set port(port) {
8562 if (this._isInvalid || !this._isRelative)
8563 return;
8564 parse.call(this, port, 'port');
8565 },
8566
8567 get pathname() {
8568 return this._isInvalid ? '' : this._isRelative ?
8569 '/' + this._path.join('/') : this._schemeData;
8570 },
8571 set pathname(pathname) {
8572 if (this._isInvalid || !this._isRelative)
8573 return;
8574 this._path = [];
8575 parse.call(this, pathname, 'relative path start');
8576 },
8577
8578 get search() {
8579 return this._isInvalid || !this._query || '?' == this._query ?
8580 '' : this._query;
8581 },
8582 set search(search) {
8583 if (this._isInvalid || !this._isRelative)
8584 return;
8585 this._query = '?';
8586 if ('?' == search[0])
8587 search = search.slice(1);
8588 parse.call(this, search, 'query');
8589 },
8590
8591 get hash() {
8592 return this._isInvalid || !this._fragment || '#' == this._fragment ?
8593 '' : this._fragment;
8594 },
8595 set hash(hash) {
8596 if (this._isInvalid)
8597 return;
8598 this._fragment = '#';
8599 if ('#' == hash[0])
8600 hash = hash.slice(1);
8601 parse.call(this, hash, 'fragment');
8602 }
8603 };
8604
8605 scope.URL = jURL;
8606
8607 })(window);
8608
8609 /*
8610 * Copyright 2013 The Polymer Authors. All rights reserved.
8611 * Use of this source code is governed by a BSD-style
8612 * license that can be found in the LICENSE file.
8613 */
8614
8615 (function(scope) {
8616
8617 // Old versions of iOS do not have bind.
8618
8619 if (!Function.prototype.bind) {
8620 Function.prototype.bind = function(scope) {
8621 var self = this;
8622 var args = Array.prototype.slice.call(arguments, 1);
8623 return function() {
8624 var args2 = args.slice();
8625 args2.push.apply(args2, arguments);
8626 return self.apply(scope, args2);
8627 };
8628 };
8629 }
8630
8631 // mixin
8632
8633 // copy all properties from inProps (et al) to inObj
8634 function mixin(inObj/*, inProps, inMoreProps, ...*/) {
8635 var obj = inObj || {};
8636 for (var i = 1; i < arguments.length; i++) {
8637 var p = arguments[i];
8638 try {
8639 for (var n in p) {
8640 copyProperty(n, p, obj);
8641 }
8642 } catch(x) {
8643 }
8644 }
8645 return obj;
8646 }
8647
8648 // copy property inName from inSource object to inTarget object
8649 function copyProperty(inName, inSource, inTarget) {
8650 var pd = getPropertyDescriptor(inSource, inName);
8651 Object.defineProperty(inTarget, inName, pd);
8652 }
8653
8654 // get property descriptor for inName on inObject, even if
8655 // inName exists on some link in inObject's prototype chain
8656 function getPropertyDescriptor(inObject, inName) {
8657 if (inObject) {
8658 var pd = Object.getOwnPropertyDescriptor(inObject, inName);
8659 return pd || getPropertyDescriptor(Object.getPrototypeOf(inObject), inName);
8660 }
8661 }
8662
8663 // export
8664
8665 scope.mixin = mixin;
8666
8667 })(window.Platform);
8668 // Copyright 2011 Google Inc.
8669 //
8670 // Licensed under the Apache License, Version 2.0 (the "License");
8671 // you may not use this file except in compliance with the License.
8672 // You may obtain a copy of the License at
8673 //
8674 // http://www.apache.org/licenses/LICENSE-2.0
8675 //
8676 // Unless required by applicable law or agreed to in writing, software
8677 // distributed under the License is distributed on an "AS IS" BASIS,
8678 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
8679 // See the License for the specific language governing permissions and
8680 // limitations under the License.
8681
8682 (function(scope) {
8683
8684 'use strict';
8685
8686 // polyfill DOMTokenList
8687 // * add/remove: allow these methods to take multiple classNames
8688 // * toggle: add a 2nd argument which forces the given state rather
8689 // than toggling.
8690
8691 var add = DOMTokenList.prototype.add;
8692 var remove = DOMTokenList.prototype.remove;
8693 DOMTokenList.prototype.add = function() {
8694 for (var i = 0; i < arguments.length; i++) {
8695 add.call(this, arguments[i]);
8696 }
8697 };
8698 DOMTokenList.prototype.remove = function() {
8699 for (var i = 0; i < arguments.length; i++) {
8700 remove.call(this, arguments[i]);
8701 }
8702 };
8703 DOMTokenList.prototype.toggle = function(name, bool) {
8704 if (arguments.length == 1) {
8705 bool = !this.contains(name);
8706 }
8707 bool ? this.add(name) : this.remove(name);
8708 };
8709 DOMTokenList.prototype.switch = function(oldName, newName) {
8710 oldName && this.remove(oldName);
8711 newName && this.add(newName);
8712 };
8713
8714 // add array() to NodeList, NamedNodeMap, HTMLCollection
8715
8716 var ArraySlice = function() {
8717 return Array.prototype.slice.call(this);
8718 };
8719
8720 var namedNodeMap = (window.NamedNodeMap || window.MozNamedAttrMap || {});
8721
8722 NodeList.prototype.array = ArraySlice;
8723 namedNodeMap.prototype.array = ArraySlice;
8724 HTMLCollection.prototype.array = ArraySlice;
8725
8726 // polyfill performance.now
8727
8728 if (!window.performance) {
8729 var start = Date.now();
8730 // only at millisecond precision
8731 window.performance = {now: function(){ return Date.now() - start }};
8732 }
8733
8734 // polyfill for requestAnimationFrame
8735
8736 if (!window.requestAnimationFrame) {
8737 window.requestAnimationFrame = (function() {
8738 var nativeRaf = window.webkitRequestAnimationFrame ||
8739 window.mozRequestAnimationFrame;
8740
8741 return nativeRaf ?
8742 function(callback) {
8743 return nativeRaf(function() {
8744 callback(performance.now());
8745 });
8746 } :
8747 function( callback ){
8748 return window.setTimeout(callback, 1000 / 60);
8749 };
8750 })();
8751 }
8752
8753 if (!window.cancelAnimationFrame) {
8754 window.cancelAnimationFrame = (function() {
8755 return window.webkitCancelAnimationFrame ||
8756 window.mozCancelAnimationFrame ||
8757 function(id) {
8758 clearTimeout(id);
8759 };
8760 })();
8761 }
8762
8763 // utility
8764
8765 function createDOM(inTagOrNode, inHTML, inAttrs) {
8766 var dom = typeof inTagOrNode == 'string' ?
8767 document.createElement(inTagOrNode) : inTagOrNode.cloneNode(true);
8768 dom.innerHTML = inHTML;
8769 if (inAttrs) {
8770 for (var n in inAttrs) {
8771 dom.setAttribute(n, inAttrs[n]);
8772 }
8773 }
8774 return dom;
8775 }
8776 // Make a stub for Polymer() for polyfill purposes; under the HTMLImports
8777 // polyfill, scripts in the main document run before imports. That means
8778 // if (1) polymer is imported and (2) Polymer() is called in the main document
8779 // in a script after the import, 2 occurs before 1. We correct this here
8780 // by specfiically patching Polymer(); this is not necessary under native
8781 // HTMLImports.
8782 var elementDeclarations = [];
8783
8784 var polymerStub = function(name, dictionary) {
8785 elementDeclarations.push(arguments);
8786 }
8787 window.Polymer = polymerStub;
8788
8789 // deliver queued delcarations
8790 scope.deliverDeclarations = function() {
8791 scope.deliverDeclarations = function() {
8792 throw 'Possible attempt to load Polymer twice';
8793 };
8794 return elementDeclarations;
8795 }
8796
8797 // Once DOMContent has loaded, any main document scripts that depend on
8798 // Polymer() should have run. Calling Polymer() now is an error until
8799 // polymer is imported.
8800 window.addEventListener('DOMContentLoaded', function() {
8801 if (window.Polymer === polymerStub) {
8802 window.Polymer = function() {
8803 console.error('You tried to use polymer without loading it first. To ' +
8804 'load polymer, <link rel="import" href="' +
8805 'components/polymer/polymer.html">');
8806 };
8807 }
8808 });
8809
8810 // exports
8811 scope.createDOM = createDOM;
8812
8813 })(window.Platform);
8814
8815 /*
8816 * Copyright 2013 The Polymer Authors. All rights reserved.
8817 * Use of this source code is governed by a BSD-style
8818 * license that can be found in the LICENSE file.
8819 */
8820
8821 // poor man's adapter for template.content on various platform scenarios
8822 window.templateContent = window.templateContent || function(inTemplate) {
8823 return inTemplate.content;
8824 };
8825 (function(scope) {
8826
8827 scope = scope || (window.Inspector = {});
8828
8829 var inspector;
8830
8831 window.sinspect = function(inNode, inProxy) {
8832 if (!inspector) {
8833 inspector = window.open('', 'ShadowDOM Inspector', null, true);
8834 inspector.document.write(inspectorHTML);
8835 //inspector.document.close();
8836 inspector.api = {
8837 shadowize: shadowize
8838 };
8839 }
8840 inspect(inNode || wrap(document.body), inProxy);
8841 };
8842
8843 var inspectorHTML = [
8844 '<!DOCTYPE html>',
8845 '<html>',
8846 ' <head>',
8847 ' <title>ShadowDOM Inspector</title>',
8848 ' <style>',
8849 ' body {',
8850 ' }',
8851 ' pre {',
8852 ' font: 9pt "Courier New", monospace;',
8853 ' line-height: 1.5em;',
8854 ' }',
8855 ' tag {',
8856 ' color: purple;',
8857 ' }',
8858 ' ul {',
8859 ' margin: 0;',
8860 ' padding: 0;',
8861 ' list-style: none;',
8862 ' }',
8863 ' li {',
8864 ' display: inline-block;',
8865 ' background-color: #f1f1f1;',
8866 ' padding: 4px 6px;',
8867 ' border-radius: 4px;',
8868 ' margin-right: 4px;',
8869 ' }',
8870 ' </style>',
8871 ' </head>',
8872 ' <body>',
8873 ' <ul id="crumbs">',
8874 ' </ul>',
8875 ' <div id="tree"></div>',
8876 ' </body>',
8877 '</html>'
8878 ].join('\n');
8879
8880 var crumbs = [];
8881
8882 var displayCrumbs = function() {
8883 // alias our document
8884 var d = inspector.document;
8885 // get crumbbar
8886 var cb = d.querySelector('#crumbs');
8887 // clear crumbs
8888 cb.textContent = '';
8889 // build new crumbs
8890 for (var i=0, c; c=crumbs[i]; i++) {
8891 var a = d.createElement('a');
8892 a.href = '#';
8893 a.textContent = c.localName;
8894 a.idx = i;
8895 a.onclick = function(event) {
8896 var c;
8897 while (crumbs.length > this.idx) {
8898 c = crumbs.pop();
8899 }
8900 inspect(c.shadow || c, c);
8901 event.preventDefault();
8902 };
8903 cb.appendChild(d.createElement('li')).appendChild(a);
8904 }
8905 };
8906
8907 var inspect = function(inNode, inProxy) {
8908 // alias our document
8909 var d = inspector.document;
8910 // reset list of drillable nodes
8911 drillable = [];
8912 // memoize our crumb proxy
8913 var proxy = inProxy || inNode;
8914 crumbs.push(proxy);
8915 // update crumbs
8916 displayCrumbs();
8917 // reflect local tree
8918 d.body.querySelector('#tree').innerHTML =
8919 '<pre>' + output(inNode, inNode.childNodes) + '</pre>';
8920 };
8921
8922 var forEach = Array.prototype.forEach.call.bind(Array.prototype.forEach);
8923
8924 var blacklisted = {STYLE:1, SCRIPT:1, "#comment": 1, TEMPLATE: 1};
8925 var blacklist = function(inNode) {
8926 return blacklisted[inNode.nodeName];
8927 };
8928
8929 var output = function(inNode, inChildNodes, inIndent) {
8930 if (blacklist(inNode)) {
8931 return '';
8932 }
8933 var indent = inIndent || '';
8934 if (inNode.localName || inNode.nodeType == 11) {
8935 var name = inNode.localName || 'shadow-root';
8936 //inChildNodes = ShadowDOM.localNodes(inNode);
8937 var info = indent + describe(inNode);
8938 // if only textNodes
8939 // TODO(sjmiles): make correct for ShadowDOM
8940 /*if (!inNode.children.length && inNode.localName !== 'content' && inNode. localName !== 'shadow') {
8941 info += catTextContent(inChildNodes);
8942 } else*/ {
8943 // TODO(sjmiles): native <shadow> has no reference to its projection
8944 if (name == 'content' /*|| name == 'shadow'*/) {
8945 inChildNodes = inNode.getDistributedNodes();
8946 }
8947 info += '<br/>';
8948 var ind = indent + '&nbsp;&nbsp;';
8949 forEach(inChildNodes, function(n) {
8950 info += output(n, n.childNodes, ind);
8951 });
8952 info += indent;
8953 }
8954 if (!({br:1}[name])) {
8955 info += '<tag>&lt;/' + name + '&gt;</tag>';
8956 info += '<br/>';
8957 }
8958 } else {
8959 var text = inNode.textContent.trim();
8960 info = text ? indent + '"' + text + '"' + '<br/>' : '';
8961 }
8962 return info;
8963 };
8964
8965 var catTextContent = function(inChildNodes) {
8966 var info = '';
8967 forEach(inChildNodes, function(n) {
8968 info += n.textContent.trim();
8969 });
8970 return info;
8971 };
8972
8973 var drillable = [];
8974
8975 var describe = function(inNode) {
8976 var tag = '<tag>' + '&lt;';
8977 var name = inNode.localName || 'shadow-root';
8978 if (inNode.webkitShadowRoot || inNode.shadowRoot) {
8979 tag += ' <button idx="' + drillable.length +
8980 '" onclick="api.shadowize.call(this)">' + name + '</button>';
8981 drillable.push(inNode);
8982 } else {
8983 tag += name || 'shadow-root';
8984 }
8985 if (inNode.attributes) {
8986 forEach(inNode.attributes, function(a) {
8987 tag += ' ' + a.name + (a.value ? '="' + a.value + '"' : '');
8988 });
8989 }
8990 tag += '&gt;'+ '</tag>';
8991 return tag;
8992 };
8993
8994 // remote api
8995
8996 shadowize = function() {
8997 var idx = Number(this.attributes.idx.value);
8998 //alert(idx);
8999 var node = drillable[idx];
9000 if (node) {
9001 inspect(node.webkitShadowRoot || node.shadowRoot, node)
9002 } else {
9003 console.log("bad shadowize node");
9004 console.dir(this);
9005 }
9006 };
9007
9008 // export
9009
9010 scope.output = output;
9011
9012 })(window.Inspector);
9013
9014
9015
9016 /*
9017 * Copyright 2013 The Polymer Authors. All rights reserved.
9018 * Use of this source code is governed by a BSD-style
9019 * license that can be found in the LICENSE file.
9020 */
9021 (function(scope) {
9022
9023 // TODO(sorvell): It's desireable to provide a default stylesheet
9024 // that's convenient for styling unresolved elements, but
9025 // it's cumbersome to have to include this manually in every page.
9026 // It would make sense to put inside some HTMLImport but
9027 // the HTMLImports polyfill does not allow loading of stylesheets
9028 // that block rendering. Therefore this injection is tolerated here.
9029
9030 var style = document.createElement('style');
9031 style.textContent = ''
9032 + 'body {'
9033 + 'transition: opacity ease-in 0.2s;'
9034 + ' } \n'
9035 + 'body[unresolved] {'
9036 + 'opacity: 0; display: block; overflow: hidden;'
9037 + ' } \n'
9038 ;
9039 var head = document.querySelector('head');
9040 head.insertBefore(style, head.firstChild);
9041
9042 })(Platform);
9043
9044 (function(scope) {
9045
9046 function withDependencies(task, depends) {
9047 depends = depends || [];
9048 if (!depends.map) {
9049 depends = [depends];
9050 }
9051 return task.apply(this, depends.map(marshal));
9052 }
9053
9054 function module(name, dependsOrFactory, moduleFactory) {
9055 var module;
9056 switch (arguments.length) {
9057 case 0:
9058 return;
9059 case 1:
9060 module = null;
9061 break;
9062 case 2:
9063 module = dependsOrFactory.apply(this);
9064 break;
9065 default:
9066 module = withDependencies(moduleFactory, dependsOrFactory);
9067 break;
9068 }
9069 modules[name] = module;
9070 };
9071
9072 function marshal(name) {
9073 return modules[name];
9074 }
9075
9076 var modules = {};
9077
9078 function using(depends, task) {
9079 HTMLImports.whenImportsReady(function() {
9080 withDependencies(task, depends);
9081 });
9082 };
9083
9084 // exports
9085
9086 scope.marshal = marshal;
9087 scope.module = module;
9088 scope.using = using;
9089
9090 })(window);
9091 /*
9092 * Copyright 2013 The Polymer Authors. All rights reserved.
9093 * Use of this source code is governed by a BSD-style
9094 * license that can be found in the LICENSE file.
9095 */
9096 (function(scope) {
9097
9098 var iterations = 0;
9099 var callbacks = [];
9100 var twiddle = document.createTextNode('');
9101
9102 function endOfMicrotask(callback) {
9103 twiddle.textContent = iterations++;
9104 callbacks.push(callback);
9105 }
9106
9107 function atEndOfMicrotask() {
9108 while (callbacks.length) {
9109 callbacks.shift()();
9110 }
9111 }
9112
9113 new (window.MutationObserver || JsMutationObserver)(atEndOfMicrotask)
9114 .observe(twiddle, {characterData: true})
9115 ;
9116
9117 // exports
9118
9119 scope.endOfMicrotask = endOfMicrotask;
9120
9121 })(Platform);
9122
9123
9124 /*
9125 * Copyright 2013 The Polymer Authors. All rights reserved.
9126 * Use of this source code is governed by a BSD-style
9127 * license that can be found in the LICENSE file.
9128 */
9129
9130 (function(scope) {
9131
9132 var urlResolver = {
9133 resolveDom: function(root, url) {
9134 url = url || root.ownerDocument.baseURI;
9135 this.resolveAttributes(root, url);
9136 this.resolveStyles(root, url);
9137 // handle template.content
9138 var templates = root.querySelectorAll('template');
9139 if (templates) {
9140 for (var i = 0, l = templates.length, t; (i < l) && (t = templates[i]); i+ +) {
9141 if (t.content) {
9142 this.resolveDom(t.content, url);
9143 }
9144 }
9145 }
9146 },
9147 resolveTemplate: function(template) {
9148 this.resolveDom(template.content, template.ownerDocument.baseURI);
9149 },
9150 resolveStyles: function(root, url) {
9151 var styles = root.querySelectorAll('style');
9152 if (styles) {
9153 for (var i = 0, l = styles.length, s; (i < l) && (s = styles[i]); i++) {
9154 this.resolveStyle(s, url);
9155 }
9156 }
9157 },
9158 resolveStyle: function(style, url) {
9159 url = url || style.ownerDocument.baseURI;
9160 style.textContent = this.resolveCssText(style.textContent, url);
9161 },
9162 resolveCssText: function(cssText, baseUrl) {
9163 cssText = replaceUrlsInCssText(cssText, baseUrl, CSS_URL_REGEXP);
9164 return replaceUrlsInCssText(cssText, baseUrl, CSS_IMPORT_REGEXP);
9165 },
9166 resolveAttributes: function(root, url) {
9167 if (root.hasAttributes && root.hasAttributes()) {
9168 this.resolveElementAttributes(root, url);
9169 }
9170 // search for attributes that host urls
9171 var nodes = root && root.querySelectorAll(URL_ATTRS_SELECTOR);
9172 if (nodes) {
9173 for (var i = 0, l = nodes.length, n; (i < l) && (n = nodes[i]); i++) {
9174 this.resolveElementAttributes(n, url);
9175 }
9176 }
9177 },
9178 resolveElementAttributes: function(node, url) {
9179 url = url || node.ownerDocument.baseURI;
9180 URL_ATTRS.forEach(function(v) {
9181 var attr = node.attributes[v];
9182 if (attr && attr.value &&
9183 (attr.value.search(URL_TEMPLATE_SEARCH) < 0)) {
9184 var urlPath = resolveRelativeUrl(url, attr.value);
9185 attr.value = urlPath;
9186 }
9187 });
9188 }
9189 };
9190
9191 var CSS_URL_REGEXP = /(url\()([^)]*)(\))/g;
9192 var CSS_IMPORT_REGEXP = /(@import[\s]+(?!url\())([^;]*)(;)/g;
9193 var URL_ATTRS = ['href', 'src', 'action'];
9194 var URL_ATTRS_SELECTOR = '[' + URL_ATTRS.join('],[') + ']';
9195 var URL_TEMPLATE_SEARCH = '{{.*}}';
9196
9197 function replaceUrlsInCssText(cssText, baseUrl, regexp) {
9198 return cssText.replace(regexp, function(m, pre, url, post) {
9199 var urlPath = url.replace(/["']/g, '');
9200 urlPath = resolveRelativeUrl(baseUrl, urlPath);
9201 return pre + '\'' + urlPath + '\'' + post;
9202 });
9203 }
9204
9205 function resolveRelativeUrl(baseUrl, url) {
9206 var u = new URL(url, baseUrl);
9207 return makeDocumentRelPath(u.href);
9208 }
9209
9210 function makeDocumentRelPath(url) {
9211 var root = document.baseURI;
9212 var u = new URL(url, root);
9213 if (u.host === root.host && u.port === root.port &&
9214 u.protocol === root.protocol) {
9215 return makeRelPath(root.pathname, u.pathname);
9216 } else {
9217 return url;
9218 }
9219 }
9220
9221 // make a relative path from source to target
9222 function makeRelPath(source, target) {
9223 var s = source.split('/');
9224 var t = target.split('/');
9225 while (s.length && s[0] === t[0]){
9226 s.shift();
9227 t.shift();
9228 }
9229 for (var i = 0, l = s.length - 1; i < l; i++) {
9230 t.unshift('..');
9231 }
9232 return t.join('/');
9233 }
9234
9235 // exports
9236 scope.urlResolver = urlResolver;
9237
9238 })(Platform);
9239
9240 /*
9241 * Copyright 2012 The Polymer Authors. All rights reserved.
9242 * Use of this source code is goverened by a BSD-style
9243 * license that can be found in the LICENSE file.
9244 */
9245
9246 (function(global) {
9247
9248 var registrationsTable = new WeakMap();
9249
9250 // We use setImmediate or postMessage for our future callback.
9251 var setImmediate = window.msSetImmediate;
9252
9253 // Use post message to emulate setImmediate.
9254 if (!setImmediate) {
9255 var setImmediateQueue = [];
9256 var sentinel = String(Math.random());
9257 window.addEventListener('message', function(e) {
9258 if (e.data === sentinel) {
9259 var queue = setImmediateQueue;
9260 setImmediateQueue = [];
9261 queue.forEach(function(func) {
9262 func();
9263 });
9264 }
9265 });
9266 setImmediate = function(func) {
9267 setImmediateQueue.push(func);
9268 window.postMessage(sentinel, '*');
9269 };
9270 }
9271
9272 // This is used to ensure that we never schedule 2 callas to setImmediate
9273 var isScheduled = false;
9274
9275 // Keep track of observers that needs to be notified next time.
9276 var scheduledObservers = [];
9277
9278 /**
9279 * Schedules |dispatchCallback| to be called in the future.
9280 * @param {MutationObserver} observer
9281 */
9282 function scheduleCallback(observer) {
9283 scheduledObservers.push(observer);
9284 if (!isScheduled) {
9285 isScheduled = true;
9286 setImmediate(dispatchCallbacks);
9287 }
9288 }
9289
9290 function wrapIfNeeded(node) {
9291 return window.ShadowDOMPolyfill &&
9292 window.ShadowDOMPolyfill.wrapIfNeeded(node) ||
9293 node;
9294 }
9295
9296 function dispatchCallbacks() {
9297 // http://dom.spec.whatwg.org/#mutation-observers
9298
9299 isScheduled = false; // Used to allow a new setImmediate call above.
9300
9301 var observers = scheduledObservers;
9302 scheduledObservers = [];
9303 // Sort observers based on their creation UID (incremental).
9304 observers.sort(function(o1, o2) {
9305 return o1.uid_ - o2.uid_;
9306 });
9307
9308 var anyNonEmpty = false;
9309 observers.forEach(function(observer) {
9310
9311 // 2.1, 2.2
9312 var queue = observer.takeRecords();
9313 // 2.3. Remove all transient registered observers whose observer is mo.
9314 removeTransientObserversFor(observer);
9315
9316 // 2.4
9317 if (queue.length) {
9318 observer.callback_(queue, observer);
9319 anyNonEmpty = true;
9320 }
9321 });
9322
9323 // 3.
9324 if (anyNonEmpty)
9325 dispatchCallbacks();
9326 }
9327
9328 function removeTransientObserversFor(observer) {
9329 observer.nodes_.forEach(function(node) {
9330 var registrations = registrationsTable.get(node);
9331 if (!registrations)
9332 return;
9333 registrations.forEach(function(registration) {
9334 if (registration.observer === observer)
9335 registration.removeTransientObservers();
9336 });
9337 });
9338 }
9339
9340 /**
9341 * This function is used for the "For each registered observer observer (with
9342 * observer's options as options) in target's list of registered observers,
9343 * run these substeps:" and the "For each ancestor ancestor of target, and for
9344 * each registered observer observer (with options options) in ancestor's list
9345 * of registered observers, run these substeps:" part of the algorithms. The
9346 * |options.subtree| is checked to ensure that the callback is called
9347 * correctly.
9348 *
9349 * @param {Node} target
9350 * @param {function(MutationObserverInit):MutationRecord} callback
9351 */
9352 function forEachAncestorAndObserverEnqueueRecord(target, callback) {
9353 for (var node = target; node; node = node.parentNode) {
9354 var registrations = registrationsTable.get(node);
9355
9356 if (registrations) {
9357 for (var j = 0; j < registrations.length; j++) {
9358 var registration = registrations[j];
9359 var options = registration.options;
9360
9361 // Only target ignores subtree.
9362 if (node !== target && !options.subtree)
9363 continue;
9364
9365 var record = callback(options);
9366 if (record)
9367 registration.enqueue(record);
9368 }
9369 }
9370 }
9371 }
9372
9373 var uidCounter = 0;
9374
9375 /**
9376 * The class that maps to the DOM MutationObserver interface.
9377 * @param {Function} callback.
9378 * @constructor
9379 */
9380 function JsMutationObserver(callback) {
9381 this.callback_ = callback;
9382 this.nodes_ = [];
9383 this.records_ = [];
9384 this.uid_ = ++uidCounter;
9385 }
9386
9387 JsMutationObserver.prototype = {
9388 observe: function(target, options) {
9389 target = wrapIfNeeded(target);
9390
9391 // 1.1
9392 if (!options.childList && !options.attributes && !options.characterData ||
9393
9394 // 1.2
9395 options.attributeOldValue && !options.attributes ||
9396
9397 // 1.3
9398 options.attributeFilter && options.attributeFilter.length &&
9399 !options.attributes ||
9400
9401 // 1.4
9402 options.characterDataOldValue && !options.characterData) {
9403
9404 throw new SyntaxError();
9405 }
9406
9407 var registrations = registrationsTable.get(target);
9408 if (!registrations)
9409 registrationsTable.set(target, registrations = []);
9410
9411 // 2
9412 // If target's list of registered observers already includes a registered
9413 // observer associated with the context object, replace that registered
9414 // observer's options with options.
9415 var registration;
9416 for (var i = 0; i < registrations.length; i++) {
9417 if (registrations[i].observer === this) {
9418 registration = registrations[i];
9419 registration.removeListeners();
9420 registration.options = options;
9421 break;
9422 }
9423 }
9424
9425 // 3.
9426 // Otherwise, add a new registered observer to target's list of registered
9427 // observers with the context object as the observer and options as the
9428 // options, and add target to context object's list of nodes on which it
9429 // is registered.
9430 if (!registration) {
9431 registration = new Registration(this, target, options);
9432 registrations.push(registration);
9433 this.nodes_.push(target);
9434 }
9435
9436 registration.addListeners();
9437 },
9438
9439 disconnect: function() {
9440 this.nodes_.forEach(function(node) {
9441 var registrations = registrationsTable.get(node);
9442 for (var i = 0; i < registrations.length; i++) {
9443 var registration = registrations[i];
9444 if (registration.observer === this) {
9445 registration.removeListeners();
9446 registrations.splice(i, 1);
9447 // Each node can only have one registered observer associated with
9448 // this observer.
9449 break;
9450 }
9451 }
9452 }, this);
9453 this.records_ = [];
9454 },
9455
9456 takeRecords: function() {
9457 var copyOfRecords = this.records_;
9458 this.records_ = [];
9459 return copyOfRecords;
9460 }
9461 };
9462
9463 /**
9464 * @param {string} type
9465 * @param {Node} target
9466 * @constructor
9467 */
9468 function MutationRecord(type, target) {
9469 this.type = type;
9470 this.target = target;
9471 this.addedNodes = [];
9472 this.removedNodes = [];
9473 this.previousSibling = null;
9474 this.nextSibling = null;
9475 this.attributeName = null;
9476 this.attributeNamespace = null;
9477 this.oldValue = null;
9478 }
9479
9480 function copyMutationRecord(original) {
9481 var record = new MutationRecord(original.type, original.target);
9482 record.addedNodes = original.addedNodes.slice();
9483 record.removedNodes = original.removedNodes.slice();
9484 record.previousSibling = original.previousSibling;
9485 record.nextSibling = original.nextSibling;
9486 record.attributeName = original.attributeName;
9487 record.attributeNamespace = original.attributeNamespace;
9488 record.oldValue = original.oldValue;
9489 return record;
9490 };
9491
9492 // We keep track of the two (possibly one) records used in a single mutation.
9493 var currentRecord, recordWithOldValue;
9494
9495 /**
9496 * Creates a record without |oldValue| and caches it as |currentRecord| for
9497 * later use.
9498 * @param {string} oldValue
9499 * @return {MutationRecord}
9500 */
9501 function getRecord(type, target) {
9502 return currentRecord = new MutationRecord(type, target);
9503 }
9504
9505 /**
9506 * Gets or creates a record with |oldValue| based in the |currentRecord|
9507 * @param {string} oldValue
9508 * @return {MutationRecord}
9509 */
9510 function getRecordWithOldValue(oldValue) {
9511 if (recordWithOldValue)
9512 return recordWithOldValue;
9513 recordWithOldValue = copyMutationRecord(currentRecord);
9514 recordWithOldValue.oldValue = oldValue;
9515 return recordWithOldValue;
9516 }
9517
9518 function clearRecords() {
9519 currentRecord = recordWithOldValue = undefined;
9520 }
9521
9522 /**
9523 * @param {MutationRecord} record
9524 * @return {boolean} Whether the record represents a record from the current
9525 * mutation event.
9526 */
9527 function recordRepresentsCurrentMutation(record) {
9528 return record === recordWithOldValue || record === currentRecord;
9529 }
9530
9531 /**
9532 * Selects which record, if any, to replace the last record in the queue.
9533 * This returns |null| if no record should be replaced.
9534 *
9535 * @param {MutationRecord} lastRecord
9536 * @param {MutationRecord} newRecord
9537 * @param {MutationRecord}
9538 */
9539 function selectRecord(lastRecord, newRecord) {
9540 if (lastRecord === newRecord)
9541 return lastRecord;
9542
9543 // Check if the the record we are adding represents the same record. If
9544 // so, we keep the one with the oldValue in it.
9545 if (recordWithOldValue && recordRepresentsCurrentMutation(lastRecord))
9546 return recordWithOldValue;
9547
9548 return null;
9549 }
9550
9551 /**
9552 * Class used to represent a registered observer.
9553 * @param {MutationObserver} observer
9554 * @param {Node} target
9555 * @param {MutationObserverInit} options
9556 * @constructor
9557 */
9558 function Registration(observer, target, options) {
9559 this.observer = observer;
9560 this.target = target;
9561 this.options = options;
9562 this.transientObservedNodes = [];
9563 }
9564
9565 Registration.prototype = {
9566 enqueue: function(record) {
9567 var records = this.observer.records_;
9568 var length = records.length;
9569
9570 // There are cases where we replace the last record with the new record.
9571 // For example if the record represents the same mutation we need to use
9572 // the one with the oldValue. If we get same record (this can happen as we
9573 // walk up the tree) we ignore the new record.
9574 if (records.length > 0) {
9575 var lastRecord = records[length - 1];
9576 var recordToReplaceLast = selectRecord(lastRecord, record);
9577 if (recordToReplaceLast) {
9578 records[length - 1] = recordToReplaceLast;
9579 return;
9580 }
9581 } else {
9582 scheduleCallback(this.observer);
9583 }
9584
9585 records[length] = record;
9586 },
9587
9588 addListeners: function() {
9589 this.addListeners_(this.target);
9590 },
9591
9592 addListeners_: function(node) {
9593 var options = this.options;
9594 if (options.attributes)
9595 node.addEventListener('DOMAttrModified', this, true);
9596
9597 if (options.characterData)
9598 node.addEventListener('DOMCharacterDataModified', this, true);
9599
9600 if (options.childList)
9601 node.addEventListener('DOMNodeInserted', this, true);
9602
9603 if (options.childList || options.subtree)
9604 node.addEventListener('DOMNodeRemoved', this, true);
9605 },
9606
9607 removeListeners: function() {
9608 this.removeListeners_(this.target);
9609 },
9610
9611 removeListeners_: function(node) {
9612 var options = this.options;
9613 if (options.attributes)
9614 node.removeEventListener('DOMAttrModified', this, true);
9615
9616 if (options.characterData)
9617 node.removeEventListener('DOMCharacterDataModified', this, true);
9618
9619 if (options.childList)
9620 node.removeEventListener('DOMNodeInserted', this, true);
9621
9622 if (options.childList || options.subtree)
9623 node.removeEventListener('DOMNodeRemoved', this, true);
9624 },
9625
9626 /**
9627 * Adds a transient observer on node. The transient observer gets removed
9628 * next time we deliver the change records.
9629 * @param {Node} node
9630 */
9631 addTransientObserver: function(node) {
9632 // Don't add transient observers on the target itself. We already have all
9633 // the required listeners set up on the target.
9634 if (node === this.target)
9635 return;
9636
9637 this.addListeners_(node);
9638 this.transientObservedNodes.push(node);
9639 var registrations = registrationsTable.get(node);
9640 if (!registrations)
9641 registrationsTable.set(node, registrations = []);
9642
9643 // We know that registrations does not contain this because we already
9644 // checked if node === this.target.
9645 registrations.push(this);
9646 },
9647
9648 removeTransientObservers: function() {
9649 var transientObservedNodes = this.transientObservedNodes;
9650 this.transientObservedNodes = [];
9651
9652 transientObservedNodes.forEach(function(node) {
9653 // Transient observers are never added to the target.
9654 this.removeListeners_(node);
9655
9656 var registrations = registrationsTable.get(node);
9657 for (var i = 0; i < registrations.length; i++) {
9658 if (registrations[i] === this) {
9659 registrations.splice(i, 1);
9660 // Each node can only have one registered observer associated with
9661 // this observer.
9662 break;
9663 }
9664 }
9665 }, this);
9666 },
9667
9668 handleEvent: function(e) {
9669 // Stop propagation since we are managing the propagation manually.
9670 // This means that other mutation events on the page will not work
9671 // correctly but that is by design.
9672 e.stopImmediatePropagation();
9673
9674 switch (e.type) {
9675 case 'DOMAttrModified':
9676 // http://dom.spec.whatwg.org/#concept-mo-queue-attributes
9677
9678 var name = e.attrName;
9679 var namespace = e.relatedNode.namespaceURI;
9680 var target = e.target;
9681
9682 // 1.
9683 var record = new getRecord('attributes', target);
9684 record.attributeName = name;
9685 record.attributeNamespace = namespace;
9686
9687 // 2.
9688 var oldValue =
9689 e.attrChange === MutationEvent.ADDITION ? null : e.prevValue;
9690
9691 forEachAncestorAndObserverEnqueueRecord(target, function(options) {
9692 // 3.1, 4.2
9693 if (!options.attributes)
9694 return;
9695
9696 // 3.2, 4.3
9697 if (options.attributeFilter && options.attributeFilter.length &&
9698 options.attributeFilter.indexOf(name) === -1 &&
9699 options.attributeFilter.indexOf(namespace) === -1) {
9700 return;
9701 }
9702 // 3.3, 4.4
9703 if (options.attributeOldValue)
9704 return getRecordWithOldValue(oldValue);
9705
9706 // 3.4, 4.5
9707 return record;
9708 });
9709
9710 break;
9711
9712 case 'DOMCharacterDataModified':
9713 // http://dom.spec.whatwg.org/#concept-mo-queue-characterdata
9714 var target = e.target;
9715
9716 // 1.
9717 var record = getRecord('characterData', target);
9718
9719 // 2.
9720 var oldValue = e.prevValue;
9721
9722
9723 forEachAncestorAndObserverEnqueueRecord(target, function(options) {
9724 // 3.1, 4.2
9725 if (!options.characterData)
9726 return;
9727
9728 // 3.2, 4.3
9729 if (options.characterDataOldValue)
9730 return getRecordWithOldValue(oldValue);
9731
9732 // 3.3, 4.4
9733 return record;
9734 });
9735
9736 break;
9737
9738 case 'DOMNodeRemoved':
9739 this.addTransientObserver(e.target);
9740 // Fall through.
9741 case 'DOMNodeInserted':
9742 // http://dom.spec.whatwg.org/#concept-mo-queue-childlist
9743 var target = e.relatedNode;
9744 var changedNode = e.target;
9745 var addedNodes, removedNodes;
9746 if (e.type === 'DOMNodeInserted') {
9747 addedNodes = [changedNode];
9748 removedNodes = [];
9749 } else {
9750
9751 addedNodes = [];
9752 removedNodes = [changedNode];
9753 }
9754 var previousSibling = changedNode.previousSibling;
9755 var nextSibling = changedNode.nextSibling;
9756
9757 // 1.
9758 var record = getRecord('childList', target);
9759 record.addedNodes = addedNodes;
9760 record.removedNodes = removedNodes;
9761 record.previousSibling = previousSibling;
9762 record.nextSibling = nextSibling;
9763
9764 forEachAncestorAndObserverEnqueueRecord(target, function(options) {
9765 // 2.1, 3.2
9766 if (!options.childList)
9767 return;
9768
9769 // 2.2, 3.3
9770 return record;
9771 });
9772
9773 }
9774
9775 clearRecords();
9776 }
9777 };
9778
9779 global.JsMutationObserver = JsMutationObserver;
9780
9781 if (!global.MutationObserver)
9782 global.MutationObserver = JsMutationObserver;
9783
9784
9785 })(this);
9786
9787 /*
9788 * Copyright 2013 The Polymer Authors. All rights reserved.
9789 * Use of this source code is governed by a BSD-style
9790 * license that can be found in the LICENSE file.
9791 */
9792 window.HTMLImports = window.HTMLImports || {flags:{}};
9793 /*
9794 * Copyright 2013 The Polymer Authors. All rights reserved.
9795 * Use of this source code is governed by a BSD-style
9796 * license that can be found in the LICENSE file.
9797 */
9798
9799 (function(scope) {
9800
9801 // imports
9802 var path = scope.path;
9803 var xhr = scope.xhr;
9804 var flags = scope.flags;
9805
9806 // TODO(sorvell): this loader supports a dynamic list of urls
9807 // and an oncomplete callback that is called when the loader is done.
9808 // The polyfill currently does *not* need this dynamism or the onComplete
9809 // concept. Because of this, the loader could be simplified quite a bit.
9810 var Loader = function(onLoad, onComplete) {
9811 this.cache = {};
9812 this.onload = onLoad;
9813 this.oncomplete = onComplete;
9814 this.inflight = 0;
9815 this.pending = {};
9816 };
9817
9818 Loader.prototype = {
9819 addNodes: function(nodes) {
9820 // number of transactions to complete
9821 this.inflight += nodes.length;
9822 // commence transactions
9823 for (var i=0, l=nodes.length, n; (i<l) && (n=nodes[i]); i++) {
9824 this.require(n);
9825 }
9826 // anything to do?
9827 this.checkDone();
9828 },
9829 addNode: function(node) {
9830 // number of transactions to complete
9831 this.inflight++;
9832 // commence transactions
9833 this.require(node);
9834 // anything to do?
9835 this.checkDone();
9836 },
9837 require: function(elt) {
9838 var url = elt.src || elt.href;
9839 // ensure we have a standard url that can be used
9840 // reliably for deduping.
9841 // TODO(sjmiles): ad-hoc
9842 elt.__nodeUrl = url;
9843 // deduplication
9844 if (!this.dedupe(url, elt)) {
9845 // fetch this resource
9846 this.fetch(url, elt);
9847 }
9848 },
9849 dedupe: function(url, elt) {
9850 if (this.pending[url]) {
9851 // add to list of nodes waiting for inUrl
9852 this.pending[url].push(elt);
9853 // don't need fetch
9854 return true;
9855 }
9856 var resource;
9857 if (this.cache[url]) {
9858 this.onload(url, elt, this.cache[url]);
9859 // finished this transaction
9860 this.tail();
9861 // don't need fetch
9862 return true;
9863 }
9864 // first node waiting for inUrl
9865 this.pending[url] = [elt];
9866 // need fetch (not a dupe)
9867 return false;
9868 },
9869 fetch: function(url, elt) {
9870 flags.load && console.log('fetch', url, elt);
9871 if (url.match(/^data:/)) {
9872 // Handle Data URI Scheme
9873 var pieces = url.split(',');
9874 var header = pieces[0];
9875 var body = pieces[1];
9876 if(header.indexOf(';base64') > -1) {
9877 body = atob(body);
9878 } else {
9879 body = decodeURIComponent(body);
9880 }
9881 setTimeout(function() {
9882 this.receive(url, elt, null, body);
9883 }.bind(this), 0);
9884 } else {
9885 var receiveXhr = function(err, resource) {
9886 this.receive(url, elt, err, resource);
9887 }.bind(this);
9888 xhr.load(url, receiveXhr);
9889 // TODO(sorvell): blocked on)
9890 // https://code.google.com/p/chromium/issues/detail?id=257221
9891 // xhr'ing for a document makes scripts in imports runnable; otherwise
9892 // they are not; however, it requires that we have doctype=html in
9893 // the import which is unacceptable. This is only needed on Chrome
9894 // to avoid the bug above.
9895 /*
9896 if (isDocumentLink(elt)) {
9897 xhr.loadDocument(url, receiveXhr);
9898 } else {
9899 xhr.load(url, receiveXhr);
9900 }
9901 */
9902 }
9903 },
9904 receive: function(url, elt, err, resource) {
9905 this.cache[url] = resource;
9906 var $p = this.pending[url];
9907 for (var i=0, l=$p.length, p; (i<l) && (p=$p[i]); i++) {
9908 //if (!err) {
9909 this.onload(url, p, resource);
9910 //}
9911 this.tail();
9912 }
9913 this.pending[url] = null;
9914 },
9915 tail: function() {
9916 --this.inflight;
9917 this.checkDone();
9918 },
9919 checkDone: function() {
9920 if (!this.inflight) {
9921 this.oncomplete();
9922 }
9923 }
9924 };
9925
9926 xhr = xhr || {
9927 async: true,
9928 ok: function(request) {
9929 return (request.status >= 200 && request.status < 300)
9930 || (request.status === 304)
9931 || (request.status === 0);
9932 },
9933 load: function(url, next, nextContext) {
9934 var request = new XMLHttpRequest();
9935 if (scope.flags.debug || scope.flags.bust) {
9936 url += '?' + Math.random();
9937 }
9938 request.open('GET', url, xhr.async);
9939 request.addEventListener('readystatechange', function(e) {
9940 if (request.readyState === 4) {
9941 next.call(nextContext, !xhr.ok(request) && request,
9942 request.response || request.responseText, url);
9943 }
9944 });
9945 request.send();
9946 return request;
9947 },
9948 loadDocument: function(url, next, nextContext) {
9949 this.load(url, next, nextContext).responseType = 'document';
9950 }
9951 };
9952
9953 // exports
9954 scope.xhr = xhr;
9955 scope.Loader = Loader;
9956
9957 })(window.HTMLImports);
9958
9959 /*
9960 * Copyright 2013 The Polymer Authors. All rights reserved.
9961 * Use of this source code is governed by a BSD-style
9962 * license that can be found in the LICENSE file.
9963 */
9964
9965 (function(scope) {
9966
9967 var IMPORT_LINK_TYPE = 'import';
9968 var flags = scope.flags;
9969 var isIe = /Trident/.test(navigator.userAgent);
9970 // TODO(sorvell): SD polyfill intrusion
9971 var mainDoc = window.ShadowDOMPolyfill ?
9972 window.ShadowDOMPolyfill.wrapIfNeeded(document) : document;
9973
9974 // importParser
9975 // highlander object to manage parsing of imports
9976 // parses import related elements
9977 // and ensures proper parse order
9978 // parse order is enforced by crawling the tree and monitoring which elements
9979 // have been parsed; async parsing is also supported.
9980
9981 // highlander object for parsing a document tree
9982 var importParser = {
9983 // parse selectors for main document elements
9984 documentSelectors: 'link[rel=' + IMPORT_LINK_TYPE + ']',
9985 // parse selectors for import document elements
9986 importsSelectors: [
9987 'link[rel=' + IMPORT_LINK_TYPE + ']',
9988 'link[rel=stylesheet]',
9989 'style',
9990 'script:not([type])',
9991 'script[type="text/javascript"]'
9992 ].join(','),
9993 map: {
9994 link: 'parseLink',
9995 script: 'parseScript',
9996 style: 'parseStyle'
9997 },
9998 // try to parse the next import in the tree
9999 parseNext: function() {
10000 var next = this.nextToParse();
10001 if (next) {
10002 this.parse(next);
10003 }
10004 },
10005 parse: function(elt) {
10006 if (this.isParsed(elt)) {
10007 flags.parse && console.log('[%s] is already parsed', elt.localName);
10008 return;
10009 }
10010 var fn = this[this.map[elt.localName]];
10011 if (fn) {
10012 this.markParsing(elt);
10013 fn.call(this, elt);
10014 }
10015 },
10016 // only 1 element may be parsed at a time; parsing is async so, each
10017 // parsing implementation must inform the system that parsing is complete
10018 // via markParsingComplete.
10019 markParsing: function(elt) {
10020 flags.parse && console.log('parsing', elt);
10021 this.parsingElement = elt;
10022 },
10023 markParsingComplete: function(elt) {
10024 elt.__importParsed = true;
10025 if (elt.__importElement) {
10026 elt.__importElement.__importParsed = true;
10027 }
10028 this.parsingElement = null;
10029 flags.parse && console.log('completed', elt);
10030 this.parseNext();
10031 },
10032 parseImport: function(elt) {
10033 elt.import.__importParsed = true;
10034 // TODO(sorvell): consider if there's a better way to do this;
10035 // expose an imports parsing hook; this is needed, for example, by the
10036 // CustomElements polyfill.
10037 if (HTMLImports.__importsParsingHook) {
10038 HTMLImports.__importsParsingHook(elt);
10039 }
10040 // fire load event
10041 if (elt.__resource) {
10042 elt.dispatchEvent(new CustomEvent('load', {bubbles: false}));
10043 } else {
10044 elt.dispatchEvent(new CustomEvent('error', {bubbles: false}));
10045 }
10046 // TODO(sorvell): workaround for Safari addEventListener not working
10047 // for elements not in the main document.
10048 if (elt.__pending) {
10049 var fn;
10050 while (elt.__pending.length) {
10051 fn = elt.__pending.shift();
10052 if (fn) {
10053 fn({target: elt});
10054 }
10055 }
10056 }
10057 this.markParsingComplete(elt);
10058 },
10059 parseLink: function(linkElt) {
10060 if (nodeIsImport(linkElt)) {
10061 this.parseImport(linkElt);
10062 } else {
10063 // make href absolute
10064 linkElt.href = linkElt.href;
10065 this.parseGeneric(linkElt);
10066 }
10067 },
10068 parseStyle: function(elt) {
10069 // TODO(sorvell): style element load event can just not fire so clone styles
10070 var src = elt;
10071 elt = cloneStyle(elt);
10072 elt.__importElement = src;
10073 this.parseGeneric(elt);
10074 },
10075 parseGeneric: function(elt) {
10076 this.trackElement(elt);
10077 document.head.appendChild(elt);
10078 },
10079 // tracks when a loadable element has loaded
10080 trackElement: function(elt, callback) {
10081 var self = this;
10082 var done = function(e) {
10083 if (callback) {
10084 callback(e);
10085 }
10086 self.markParsingComplete(elt);
10087 };
10088 elt.addEventListener('load', done);
10089 elt.addEventListener('error', done);
10090
10091 // NOTE: IE does not fire "load" event for styles that have already loaded
10092 // This is in violation of the spec, so we try our hardest to work around it
10093 if (isIe && elt.localName === 'style') {
10094 var fakeLoad = false;
10095 // If there's not @import in the textContent, assume it has loaded
10096 if (elt.textContent.indexOf('@import') == -1) {
10097 fakeLoad = true;
10098 // if we have a sheet, we have been parsed
10099 } else if (elt.sheet) {
10100 fakeLoad = true;
10101 var csr = elt.sheet.cssRules;
10102 var len = csr ? csr.length : 0;
10103 // search the rules for @import's
10104 for (var i = 0, r; (i < len) && (r = csr[i]); i++) {
10105 if (r.type === CSSRule.IMPORT_RULE) {
10106 // if every @import has resolved, fake the load
10107 fakeLoad = fakeLoad && Boolean(r.styleSheet);
10108 }
10109 }
10110 }
10111 // dispatch a fake load event and continue parsing
10112 if (fakeLoad) {
10113 elt.dispatchEvent(new CustomEvent('load', {bubbles: false}));
10114 }
10115 }
10116 },
10117 // NOTE: execute scripts by injecting them and watching for the load/error
10118 // event. Inline scripts are handled via dataURL's because browsers tend to
10119 // provide correct parsing errors in this case. If this has any compatibility
10120 // issues, we can switch to injecting the inline script with textContent.
10121 // Scripts with dataURL's do not appear to generate load events and therefore
10122 // we assume they execute synchronously.
10123 parseScript: function(scriptElt) {
10124 var script = document.createElement('script');
10125 script.__importElement = scriptElt;
10126 script.src = scriptElt.src ? scriptElt.src :
10127 generateScriptDataUrl(scriptElt);
10128 scope.currentScript = scriptElt;
10129 this.trackElement(script, function(e) {
10130 script.parentNode.removeChild(script);
10131 scope.currentScript = null;
10132 });
10133 document.head.appendChild(script);
10134 },
10135 // determine the next element in the tree which should be parsed
10136 nextToParse: function() {
10137 return !this.parsingElement && this.nextToParseInDoc(mainDoc);
10138 },
10139 nextToParseInDoc: function(doc, link) {
10140 var nodes = doc.querySelectorAll(this.parseSelectorsForNode(doc));
10141 for (var i=0, l=nodes.length, p=0, n; (i<l) && (n=nodes[i]); i++) {
10142 if (!this.isParsed(n)) {
10143 if (this.hasResource(n)) {
10144 return nodeIsImport(n) ? this.nextToParseInDoc(n.import, n) : n;
10145 } else {
10146 return;
10147 }
10148 }
10149 }
10150 // all nodes have been parsed, ready to parse import, if any
10151 return link;
10152 },
10153 // return the set of parse selectors relevant for this node.
10154 parseSelectorsForNode: function(node) {
10155 var doc = node.ownerDocument || node;
10156 return doc === mainDoc ? this.documentSelectors : this.importsSelectors;
10157 },
10158 isParsed: function(node) {
10159 return node.__importParsed;
10160 },
10161 hasResource: function(node) {
10162 if (nodeIsImport(node) && !node.import) {
10163 return false;
10164 }
10165 return true;
10166 }
10167 };
10168
10169 function nodeIsImport(elt) {
10170 return (elt.localName === 'link') && (elt.rel === IMPORT_LINK_TYPE);
10171 }
10172
10173 function generateScriptDataUrl(script) {
10174 var scriptContent = generateScriptContent(script), b64;
10175 try {
10176 b64 = btoa(scriptContent);
10177 } catch(e) {
10178 b64 = btoa(unescape(encodeURIComponent(scriptContent)));
10179 console.warn('Script contained non-latin characters that were forced ' +
10180 'to latin. Some characters may be wrong.', script);
10181 }
10182 return 'data:text/javascript;base64,' + b64;
10183 }
10184
10185 function generateScriptContent(script) {
10186 return script.textContent + generateSourceMapHint(script);
10187 }
10188
10189 // calculate source map hint
10190 function generateSourceMapHint(script) {
10191 var moniker = script.__nodeUrl;
10192 if (!moniker) {
10193 moniker = script.ownerDocument.baseURI;
10194 // there could be more than one script this url
10195 var tag = '[' + Math.floor((Math.random()+1)*1000) + ']';
10196 // TODO(sjmiles): Polymer hack, should be pluggable if we need to allow
10197 // this sort of thing
10198 var matches = script.textContent.match(/Polymer\(['"]([^'"]*)/);
10199 tag = matches && matches[1] || tag;
10200 // tag the moniker
10201 moniker += '/' + tag + '.js';
10202 }
10203 return '\n//# sourceURL=' + moniker + '\n';
10204 }
10205
10206 // style/stylesheet handling
10207
10208 // clone style with proper path resolution for main document
10209 // NOTE: styles are the only elements that require direct path fixup.
10210 function cloneStyle(style) {
10211 var clone = style.ownerDocument.createElement('style');
10212 clone.textContent = style.textContent;
10213 path.resolveUrlsInStyle(clone);
10214 return clone;
10215 }
10216
10217 // path fixup: style elements in imports must be made relative to the main
10218 // document. We fixup url's in url() and @import.
10219 var CSS_URL_REGEXP = /(url\()([^)]*)(\))/g;
10220 var CSS_IMPORT_REGEXP = /(@import[\s]+(?!url\())([^;]*)(;)/g;
10221
10222 var path = {
10223 resolveUrlsInStyle: function(style) {
10224 var doc = style.ownerDocument;
10225 var resolver = doc.createElement('a');
10226 style.textContent = this.resolveUrlsInCssText(style.textContent, resolver);
10227 return style;
10228 },
10229 resolveUrlsInCssText: function(cssText, urlObj) {
10230 var r = this.replaceUrls(cssText, urlObj, CSS_URL_REGEXP);
10231 r = this.replaceUrls(r, urlObj, CSS_IMPORT_REGEXP);
10232 return r;
10233 },
10234 replaceUrls: function(text, urlObj, regexp) {
10235 return text.replace(regexp, function(m, pre, url, post) {
10236 var urlPath = url.replace(/["']/g, '');
10237 urlObj.href = urlPath;
10238 urlPath = urlObj.href;
10239 return pre + '\'' + urlPath + '\'' + post;
10240 });
10241 }
10242 }
10243
10244 // exports
10245 scope.parser = importParser;
10246 scope.path = path;
10247 scope.isIE = isIe;
10248
10249 })(HTMLImports);
10250
10251 /*
10252 * Copyright 2013 The Polymer Authors. All rights reserved.
10253 * Use of this source code is governed by a BSD-style
10254 * license that can be found in the LICENSE file.
10255 */
10256
10257 (function(scope) {
10258
10259 var hasNative = ('import' in document.createElement('link'));
10260 var useNative = hasNative;
10261 var flags = scope.flags;
10262 var IMPORT_LINK_TYPE = 'import';
10263
10264 // TODO(sorvell): SD polyfill intrusion
10265 var mainDoc = window.ShadowDOMPolyfill ?
10266 ShadowDOMPolyfill.wrapIfNeeded(document) : document;
10267
10268 if (!useNative) {
10269
10270 // imports
10271 var xhr = scope.xhr;
10272 var Loader = scope.Loader;
10273 var parser = scope.parser;
10274
10275 // importer
10276 // highlander object to manage loading of imports
10277
10278 // for any document, importer:
10279 // - loads any linked import documents (with deduping)
10280
10281 var importer = {
10282 documents: {},
10283 // nodes to load in the mian document
10284 documentPreloadSelectors: 'link[rel=' + IMPORT_LINK_TYPE + ']',
10285 // nodes to load in imports
10286 importsPreloadSelectors: [
10287 'link[rel=' + IMPORT_LINK_TYPE + ']'
10288 ].join(','),
10289 loadNode: function(node) {
10290 importLoader.addNode(node);
10291 },
10292 // load all loadable elements within the parent element
10293 loadSubtree: function(parent) {
10294 var nodes = this.marshalNodes(parent);
10295 // add these nodes to loader's queue
10296 importLoader.addNodes(nodes);
10297 },
10298 marshalNodes: function(parent) {
10299 // all preloadable nodes in inDocument
10300 return parent.querySelectorAll(this.loadSelectorsForNode(parent));
10301 },
10302 // find the proper set of load selectors for a given node
10303 loadSelectorsForNode: function(node) {
10304 var doc = node.ownerDocument || node;
10305 return doc === mainDoc ? this.documentPreloadSelectors :
10306 this.importsPreloadSelectors;
10307 },
10308 loaded: function(url, elt, resource) {
10309 flags.load && console.log('loaded', url, elt);
10310 // store generic resource
10311 // TODO(sorvell): fails for nodes inside <template>.content
10312 // see https://code.google.com/p/chromium/issues/detail?id=249381.
10313 elt.__resource = resource;
10314 if (isDocumentLink(elt)) {
10315 var doc = this.documents[url];
10316 // if we've never seen a document at this url
10317 if (!doc) {
10318 // generate an HTMLDocument from data
10319 doc = makeDocument(resource, url);
10320 doc.__importLink = elt;
10321 // TODO(sorvell): we cannot use MO to detect parsed nodes because
10322 // SD polyfill does not report these as mutations.
10323 this.bootDocument(doc);
10324 // cache document
10325 this.documents[url] = doc;
10326 }
10327 // don't store import record until we're actually loaded
10328 // store document resource
10329 elt.import = doc;
10330 }
10331 parser.parseNext();
10332 },
10333 bootDocument: function(doc) {
10334 this.loadSubtree(doc);
10335 this.observe(doc);
10336 parser.parseNext();
10337 },
10338 loadedAll: function() {
10339 parser.parseNext();
10340 }
10341 };
10342
10343 // loader singleton
10344 var importLoader = new Loader(importer.loaded.bind(importer),
10345 importer.loadedAll.bind(importer));
10346
10347 function isDocumentLink(elt) {
10348 return isLinkRel(elt, IMPORT_LINK_TYPE);
10349 }
10350
10351 function isLinkRel(elt, rel) {
10352 return elt.localName === 'link' && elt.getAttribute('rel') === rel;
10353 }
10354
10355 function isScript(elt) {
10356 return elt.localName === 'script';
10357 }
10358
10359 function makeDocument(resource, url) {
10360 // create a new HTML document
10361 var doc = resource;
10362 if (!(doc instanceof Document)) {
10363 doc = document.implementation.createHTMLDocument(IMPORT_LINK_TYPE);
10364 }
10365 // cache the new document's source url
10366 doc._URL = url;
10367 // establish a relative path via <base>
10368 var base = doc.createElement('base');
10369 base.setAttribute('href', url);
10370 // add baseURI support to browsers (IE) that lack it.
10371 if (!doc.baseURI) {
10372 doc.baseURI = url;
10373 }
10374 // ensure UTF-8 charset
10375 var meta = doc.createElement('meta');
10376 meta.setAttribute('charset', 'utf-8');
10377
10378 doc.head.appendChild(meta);
10379 doc.head.appendChild(base);
10380 // install HTML last as it may trigger CustomElement upgrades
10381 // TODO(sjmiles): problem wrt to template boostrapping below,
10382 // template bootstrapping must (?) come before element upgrade
10383 // but we cannot bootstrap templates until they are in a document
10384 // which is too late
10385 if (!(resource instanceof Document)) {
10386 // install html
10387 doc.body.innerHTML = resource;
10388 }
10389 // TODO(sorvell): ideally this code is not aware of Template polyfill,
10390 // but for now the polyfill needs help to bootstrap these templates
10391 if (window.HTMLTemplateElement && HTMLTemplateElement.bootstrap) {
10392 HTMLTemplateElement.bootstrap(doc);
10393 }
10394 return doc;
10395 }
10396 } else {
10397 // do nothing if using native imports
10398 var importer = {};
10399 }
10400
10401 // NOTE: We cannot polyfill document.currentScript because it's not possible
10402 // both to override and maintain the ability to capture the native value;
10403 // therefore we choose to expose _currentScript both when native imports
10404 // and the polyfill are in use.
10405 var currentScriptDescriptor = {
10406 get: function() {
10407 return HTMLImports.currentScript || document.currentScript;
10408 },
10409 configurable: true
10410 };
10411
10412 Object.defineProperty(document, '_currentScript', currentScriptDescriptor);
10413 Object.defineProperty(mainDoc, '_currentScript', currentScriptDescriptor);
10414
10415 // Polyfill document.baseURI for browsers without it.
10416 if (!document.baseURI) {
10417 var baseURIDescriptor = {
10418 get: function() {
10419 return window.location.href;
10420 },
10421 configurable: true
10422 };
10423
10424 Object.defineProperty(document, 'baseURI', baseURIDescriptor);
10425 Object.defineProperty(mainDoc, 'baseURI', baseURIDescriptor);
10426 }
10427
10428 // call a callback when all HTMLImports in the document at call (or at least
10429 // document ready) time have loaded.
10430 // 1. ensure the document is in a ready state (has dom), then
10431 // 2. watch for loading of imports and call callback when done
10432 function whenImportsReady(callback, doc) {
10433 doc = doc || mainDoc;
10434 // if document is loading, wait and try again
10435 whenDocumentReady(function() {
10436 watchImportsLoad(callback, doc);
10437 }, doc);
10438 }
10439
10440 // call the callback when the document is in a ready state (has dom)
10441 var requiredReadyState = HTMLImports.isIE ? 'complete' : 'interactive';
10442 var READY_EVENT = 'readystatechange';
10443 function isDocumentReady(doc) {
10444 return (doc.readyState === 'complete' ||
10445 doc.readyState === requiredReadyState);
10446 }
10447
10448 // call <callback> when we ensure the document is in a ready state
10449 function whenDocumentReady(callback, doc) {
10450 if (!isDocumentReady(doc)) {
10451 var checkReady = function() {
10452 if (doc.readyState === 'complete' ||
10453 doc.readyState === requiredReadyState) {
10454 doc.removeEventListener(READY_EVENT, checkReady);
10455 whenDocumentReady(callback, doc);
10456 }
10457 }
10458 doc.addEventListener(READY_EVENT, checkReady);
10459 } else if (callback) {
10460 callback();
10461 }
10462 }
10463
10464 // call <callback> when we ensure all imports have loaded
10465 function watchImportsLoad(callback, doc) {
10466 var imports = doc.querySelectorAll('link[rel=import]');
10467 var loaded = 0, l = imports.length;
10468 function checkDone(d) {
10469 if (loaded == l) {
10470 // go async to ensure parser isn't stuck on a script tag
10471 requestAnimationFrame(callback);
10472 }
10473 }
10474 function loadedImport(e) {
10475 loaded++;
10476 checkDone();
10477 }
10478 if (l) {
10479 for (var i=0, imp; (i<l) && (imp=imports[i]); i++) {
10480 if (isImportLoaded(imp)) {
10481 loadedImport.call(imp);
10482 } else {
10483 imp.addEventListener('load', loadedImport);
10484 imp.addEventListener('error', loadedImport);
10485 }
10486 }
10487 } else {
10488 checkDone();
10489 }
10490 }
10491
10492 function isImportLoaded(link) {
10493 return useNative ? (link.import && (link.import.readyState !== 'loading')) :
10494 link.__importParsed;
10495 }
10496
10497 // exports
10498 scope.hasNative = hasNative;
10499 scope.useNative = useNative;
10500 scope.importer = importer;
10501 scope.whenImportsReady = whenImportsReady;
10502 scope.IMPORT_LINK_TYPE = IMPORT_LINK_TYPE;
10503 scope.isImportLoaded = isImportLoaded;
10504 scope.importLoader = importLoader;
10505
10506 })(window.HTMLImports);
10507
10508 /*
10509 Copyright 2013 The Polymer Authors. All rights reserved.
10510 Use of this source code is governed by a BSD-style
10511 license that can be found in the LICENSE file.
10512 */
10513
10514 (function(scope){
10515
10516 var IMPORT_LINK_TYPE = scope.IMPORT_LINK_TYPE;
10517 var importSelector = 'link[rel=' + IMPORT_LINK_TYPE + ']';
10518 var importer = scope.importer;
10519
10520 // we track mutations for addedNodes, looking for imports
10521 function handler(mutations) {
10522 for (var i=0, l=mutations.length, m; (i<l) && (m=mutations[i]); i++) {
10523 if (m.type === 'childList' && m.addedNodes.length) {
10524 addedNodes(m.addedNodes);
10525 }
10526 }
10527 }
10528
10529 // find loadable elements and add them to the importer
10530 function addedNodes(nodes) {
10531 for (var i=0, l=nodes.length, n; (i<l) && (n=nodes[i]); i++) {
10532 if (shouldLoadNode(n)) {
10533 importer.loadNode(n);
10534 }
10535 if (n.children && n.children.length) {
10536 addedNodes(n.children);
10537 }
10538 }
10539 }
10540
10541 function shouldLoadNode(node) {
10542 return (node.nodeType === 1) && matches.call(node,
10543 importer.loadSelectorsForNode(node));
10544 }
10545
10546 // x-plat matches
10547 var matches = HTMLElement.prototype.matches ||
10548 HTMLElement.prototype.matchesSelector ||
10549 HTMLElement.prototype.webkitMatchesSelector ||
10550 HTMLElement.prototype.mozMatchesSelector ||
10551 HTMLElement.prototype.msMatchesSelector;
10552
10553 var observer = new MutationObserver(handler);
10554
10555 // observe the given root for loadable elements
10556 function observe(root) {
10557 observer.observe(root, {childList: true, subtree: true});
10558 }
10559
10560 // exports
10561 // TODO(sorvell): factor so can put on scope
10562 scope.observe = observe;
10563 importer.observe = observe;
10564
10565 })(HTMLImports);
10566
10567 /*
10568 * Copyright 2013 The Polymer Authors. All rights reserved.
10569 * Use of this source code is governed by a BSD-style
10570 * license that can be found in the LICENSE file.
10571 */
10572 (function(){
10573
10574 // bootstrap
10575
10576 // IE shim for CustomEvent
10577 if (typeof window.CustomEvent !== 'function') {
10578 window.CustomEvent = function(inType, dictionary) {
10579 var e = document.createEvent('HTMLEvents');
10580 e.initEvent(inType,
10581 dictionary.bubbles === false ? false : true,
10582 dictionary.cancelable === false ? false : true,
10583 dictionary.detail);
10584 return e;
10585 };
10586 }
10587
10588 // TODO(sorvell): SD polyfill intrusion
10589 var doc = window.ShadowDOMPolyfill ?
10590 window.ShadowDOMPolyfill.wrapIfNeeded(document) : document;
10591
10592 // Fire the 'HTMLImportsLoaded' event when imports in document at load time
10593 // have loaded. This event is required to simulate the script blocking
10594 // behavior of native imports. A main document script that needs to be sure
10595 // imports have loaded should wait for this event.
10596 HTMLImports.whenImportsReady(function() {
10597 HTMLImports.ready = true;
10598 HTMLImports.readyTime = new Date().getTime();
10599 doc.dispatchEvent(
10600 new CustomEvent('HTMLImportsLoaded', {bubbles: true})
10601 );
10602 });
10603
10604
10605 // no need to bootstrap the polyfill when native imports is available.
10606 if (!HTMLImports.useNative) {
10607 function bootstrap() {
10608 HTMLImports.importer.bootDocument(doc);
10609 }
10610
10611 // TODO(sorvell): SD polyfill does *not* generate mutations for nodes added
10612 // by the parser. For this reason, we must wait until the dom exists to
10613 // bootstrap.
10614 if (document.readyState === 'complete' ||
10615 (document.readyState === 'interactive' && !window.attachEvent)) {
10616 bootstrap();
10617 } else {
10618 document.addEventListener('DOMContentLoaded', bootstrap);
10619 }
10620 }
10621
10622 })();
10623
10624 /*
10625 * Copyright 2013 The Polymer Authors. All rights reserved.
10626 * Use of this source code is governed by a BSD-style
10627 * license that can be found in the LICENSE file.
10628 */
10629 window.CustomElements = window.CustomElements || {flags:{}};
10630 /*
10631 Copyright 2013 The Polymer Authors. All rights reserved.
10632 Use of this source code is governed by a BSD-style
10633 license that can be found in the LICENSE file.
10634 */
10635
10636 (function(scope){
10637
10638 var logFlags = window.logFlags || {};
10639 var IMPORT_LINK_TYPE = window.HTMLImports ? HTMLImports.IMPORT_LINK_TYPE : 'none ';
10640
10641 // walk the subtree rooted at node, applying 'find(element, data)' function
10642 // to each element
10643 // if 'find' returns true for 'element', do not search element's subtree
10644 function findAll(node, find, data) {
10645 var e = node.firstElementChild;
10646 if (!e) {
10647 e = node.firstChild;
10648 while (e && e.nodeType !== Node.ELEMENT_NODE) {
10649 e = e.nextSibling;
10650 }
10651 }
10652 while (e) {
10653 if (find(e, data) !== true) {
10654 findAll(e, find, data);
10655 }
10656 e = e.nextElementSibling;
10657 }
10658 return null;
10659 }
10660
10661 // walk all shadowRoots on a given node.
10662 function forRoots(node, cb) {
10663 var root = node.shadowRoot;
10664 while(root) {
10665 forSubtree(root, cb);
10666 root = root.olderShadowRoot;
10667 }
10668 }
10669
10670 // walk the subtree rooted at node, including descent into shadow-roots,
10671 // applying 'cb' to each element
10672 function forSubtree(node, cb) {
10673 //logFlags.dom && node.childNodes && node.childNodes.length && console.group(' subTree: ', node);
10674 findAll(node, function(e) {
10675 if (cb(e)) {
10676 return true;
10677 }
10678 forRoots(e, cb);
10679 });
10680 forRoots(node, cb);
10681 //logFlags.dom && node.childNodes && node.childNodes.length && console.groupEn d();
10682 }
10683
10684 // manage lifecycle on added node
10685 function added(node) {
10686 if (upgrade(node)) {
10687 insertedNode(node);
10688 return true;
10689 }
10690 inserted(node);
10691 }
10692
10693 // manage lifecycle on added node's subtree only
10694 function addedSubtree(node) {
10695 forSubtree(node, function(e) {
10696 if (added(e)) {
10697 return true;
10698 }
10699 });
10700 }
10701
10702 // manage lifecycle on added node and it's subtree
10703 function addedNode(node) {
10704 return added(node) || addedSubtree(node);
10705 }
10706
10707 // upgrade custom elements at node, if applicable
10708 function upgrade(node) {
10709 if (!node.__upgraded__ && node.nodeType === Node.ELEMENT_NODE) {
10710 var type = node.getAttribute('is') || node.localName;
10711 var definition = scope.registry[type];
10712 if (definition) {
10713 logFlags.dom && console.group('upgrade:', node.localName);
10714 scope.upgrade(node);
10715 logFlags.dom && console.groupEnd();
10716 return true;
10717 }
10718 }
10719 }
10720
10721 function insertedNode(node) {
10722 inserted(node);
10723 if (inDocument(node)) {
10724 forSubtree(node, function(e) {
10725 inserted(e);
10726 });
10727 }
10728 }
10729
10730 // TODO(sorvell): on platforms without MutationObserver, mutations may not be
10731 // reliable and therefore attached/detached are not reliable.
10732 // To make these callbacks less likely to fail, we defer all inserts and removes
10733 // to give a chance for elements to be inserted into dom.
10734 // This ensures attachedCallback fires for elements that are created and
10735 // immediately added to dom.
10736 var hasPolyfillMutations = (!window.MutationObserver ||
10737 (window.MutationObserver === window.JsMutationObserver));
10738 scope.hasPolyfillMutations = hasPolyfillMutations;
10739
10740 var isPendingMutations = false;
10741 var pendingMutations = [];
10742 function deferMutation(fn) {
10743 pendingMutations.push(fn);
10744 if (!isPendingMutations) {
10745 isPendingMutations = true;
10746 var async = (window.Platform && window.Platform.endOfMicrotask) ||
10747 setTimeout;
10748 async(takeMutations);
10749 }
10750 }
10751
10752 function takeMutations() {
10753 isPendingMutations = false;
10754 var $p = pendingMutations;
10755 for (var i=0, l=$p.length, p; (i<l) && (p=$p[i]); i++) {
10756 p();
10757 }
10758 pendingMutations = [];
10759 }
10760
10761 function inserted(element) {
10762 if (hasPolyfillMutations) {
10763 deferMutation(function() {
10764 _inserted(element);
10765 });
10766 } else {
10767 _inserted(element);
10768 }
10769 }
10770
10771 // TODO(sjmiles): if there are descents into trees that can never have inDocumen t(*) true, fix this
10772 function _inserted(element) {
10773 // TODO(sjmiles): it's possible we were inserted and removed in the space
10774 // of one microtask, in which case we won't be 'inDocument' here
10775 // But there are other cases where we are testing for inserted without
10776 // specific knowledge of mutations, and must test 'inDocument' to determine
10777 // whether to call inserted
10778 // If we can factor these cases into separate code paths we can have
10779 // better diagnostics.
10780 // TODO(sjmiles): when logging, do work on all custom elements so we can
10781 // track behavior even when callbacks not defined
10782 //console.log('inserted: ', element.localName);
10783 if (element.attachedCallback || element.detachedCallback || (element.__upgrade d__ && logFlags.dom)) {
10784 logFlags.dom && console.group('inserted:', element.localName);
10785 if (inDocument(element)) {
10786 element.__inserted = (element.__inserted || 0) + 1;
10787 // if we are in a 'removed' state, bluntly adjust to an 'inserted' state
10788 if (element.__inserted < 1) {
10789 element.__inserted = 1;
10790 }
10791 // if we are 'over inserted', squelch the callback
10792 if (element.__inserted > 1) {
10793 logFlags.dom && console.warn('inserted:', element.localName,
10794 'insert/remove count:', element.__inserted)
10795 } else if (element.attachedCallback) {
10796 logFlags.dom && console.log('inserted:', element.localName);
10797 element.attachedCallback();
10798 }
10799 }
10800 logFlags.dom && console.groupEnd();
10801 }
10802 }
10803
10804 function removedNode(node) {
10805 removed(node);
10806 forSubtree(node, function(e) {
10807 removed(e);
10808 });
10809 }
10810
10811 function removed(element) {
10812 if (hasPolyfillMutations) {
10813 deferMutation(function() {
10814 _removed(element);
10815 });
10816 } else {
10817 _removed(element);
10818 }
10819 }
10820
10821 function _removed(element) {
10822 // TODO(sjmiles): temporary: do work on all custom elements so we can track
10823 // behavior even when callbacks not defined
10824 if (element.attachedCallback || element.detachedCallback || (element.__upgrade d__ && logFlags.dom)) {
10825 logFlags.dom && console.group('removed:', element.localName);
10826 if (!inDocument(element)) {
10827 element.__inserted = (element.__inserted || 0) - 1;
10828 // if we are in a 'inserted' state, bluntly adjust to an 'removed' state
10829 if (element.__inserted > 0) {
10830 element.__inserted = 0;
10831 }
10832 // if we are 'over removed', squelch the callback
10833 if (element.__inserted < 0) {
10834 logFlags.dom && console.warn('removed:', element.localName,
10835 'insert/remove count:', element.__inserted)
10836 } else if (element.detachedCallback) {
10837 element.detachedCallback();
10838 }
10839 }
10840 logFlags.dom && console.groupEnd();
10841 }
10842 }
10843
10844 // SD polyfill intrustion due mainly to the fact that 'document'
10845 // is not entirely wrapped
10846 function wrapIfNeeded(node) {
10847 return window.ShadowDOMPolyfill ? ShadowDOMPolyfill.wrapIfNeeded(node)
10848 : node;
10849 }
10850
10851 function inDocument(element) {
10852 var p = element;
10853 var doc = wrapIfNeeded(document);
10854 while (p) {
10855 if (p == doc) {
10856 return true;
10857 }
10858 p = p.parentNode || p.host;
10859 }
10860 }
10861
10862 function watchShadow(node) {
10863 if (node.shadowRoot && !node.shadowRoot.__watched) {
10864 logFlags.dom && console.log('watching shadow-root for: ', node.localName);
10865 // watch all unwatched roots...
10866 var root = node.shadowRoot;
10867 while (root) {
10868 watchRoot(root);
10869 root = root.olderShadowRoot;
10870 }
10871 }
10872 }
10873
10874 function watchRoot(root) {
10875 if (!root.__watched) {
10876 observe(root);
10877 root.__watched = true;
10878 }
10879 }
10880
10881 function handler(mutations) {
10882 //
10883 if (logFlags.dom) {
10884 var mx = mutations[0];
10885 if (mx && mx.type === 'childList' && mx.addedNodes) {
10886 if (mx.addedNodes) {
10887 var d = mx.addedNodes[0];
10888 while (d && d !== document && !d.host) {
10889 d = d.parentNode;
10890 }
10891 var u = d && (d.URL || d._URL || (d.host && d.host.localName)) || '';
10892 u = u.split('/?').shift().split('/').pop();
10893 }
10894 }
10895 console.group('mutations (%d) [%s]', mutations.length, u || '');
10896 }
10897 //
10898 mutations.forEach(function(mx) {
10899 //logFlags.dom && console.group('mutation');
10900 if (mx.type === 'childList') {
10901 forEach(mx.addedNodes, function(n) {
10902 //logFlags.dom && console.log(n.localName);
10903 if (!n.localName) {
10904 return;
10905 }
10906 // nodes added may need lifecycle management
10907 addedNode(n);
10908 });
10909 // removed nodes may need lifecycle management
10910 forEach(mx.removedNodes, function(n) {
10911 //logFlags.dom && console.log(n.localName);
10912 if (!n.localName) {
10913 return;
10914 }
10915 removedNode(n);
10916 });
10917 }
10918 //logFlags.dom && console.groupEnd();
10919 });
10920 logFlags.dom && console.groupEnd();
10921 };
10922
10923 var observer = new MutationObserver(handler);
10924
10925 function takeRecords() {
10926 // TODO(sjmiles): ask Raf why we have to call handler ourselves
10927 handler(observer.takeRecords());
10928 takeMutations();
10929 }
10930
10931 var forEach = Array.prototype.forEach.call.bind(Array.prototype.forEach);
10932
10933 function observe(inRoot) {
10934 observer.observe(inRoot, {childList: true, subtree: true});
10935 }
10936
10937 function observeDocument(doc) {
10938 observe(doc);
10939 }
10940
10941 function upgradeDocument(doc) {
10942 logFlags.dom && console.group('upgradeDocument: ', (doc.baseURI).split('/').po p());
10943 addedNode(doc);
10944 logFlags.dom && console.groupEnd();
10945 }
10946
10947 function upgradeDocumentTree(doc) {
10948 doc = wrapIfNeeded(doc);
10949 //console.log('upgradeDocumentTree: ', (doc.baseURI).split('/').pop());
10950 // upgrade contained imported documents
10951 var imports = doc.querySelectorAll('link[rel=' + IMPORT_LINK_TYPE + ']');
10952 for (var i=0, l=imports.length, n; (i<l) && (n=imports[i]); i++) {
10953 if (n.import && n.import.__parsed) {
10954 upgradeDocumentTree(n.import);
10955 }
10956 }
10957 upgradeDocument(doc);
10958 }
10959
10960 // exports
10961 scope.IMPORT_LINK_TYPE = IMPORT_LINK_TYPE;
10962 scope.watchShadow = watchShadow;
10963 scope.upgradeDocumentTree = upgradeDocumentTree;
10964 scope.upgradeAll = addedNode;
10965 scope.upgradeSubtree = addedSubtree;
10966 scope.insertedNode = insertedNode;
10967
10968 scope.observeDocument = observeDocument;
10969 scope.upgradeDocument = upgradeDocument;
10970
10971 scope.takeRecords = takeRecords;
10972
10973 })(window.CustomElements);
10974
10975 /*
10976 * Copyright 2013 The Polymer Authors. All rights reserved.
10977 * Use of this source code is governed by a BSD-style
10978 * license that can be found in the LICENSE file.
10979 */
10980
10981 /**
10982 * Implements `document.register`
10983 * @module CustomElements
10984 */
10985
10986 /**
10987 * Polyfilled extensions to the `document` object.
10988 * @class Document
10989 */
10990
10991 (function(scope) {
10992
10993 // imports
10994
10995 if (!scope) {
10996 scope = window.CustomElements = {flags:{}};
10997 }
10998 var flags = scope.flags;
10999
11000 // native document.registerElement?
11001
11002 var hasNative = Boolean(document.registerElement);
11003 // TODO(sorvell): See https://github.com/Polymer/polymer/issues/399
11004 // we'll address this by defaulting to CE polyfill in the presence of the SD
11005 // polyfill. This will avoid spamming excess attached/detached callbacks.
11006 // If there is a compelling need to run CE native with SD polyfill,
11007 // we'll need to fix this issue.
11008 var useNative = !flags.register && hasNative && !window.ShadowDOMPolyfill;
11009
11010 if (useNative) {
11011
11012 // stub
11013 var nop = function() {};
11014
11015 // exports
11016 scope.registry = {};
11017 scope.upgradeElement = nop;
11018
11019 scope.watchShadow = nop;
11020 scope.upgrade = nop;
11021 scope.upgradeAll = nop;
11022 scope.upgradeSubtree = nop;
11023 scope.observeDocument = nop;
11024 scope.upgradeDocument = nop;
11025 scope.upgradeDocumentTree = nop;
11026 scope.takeRecords = nop;
11027 scope.reservedTagList = [];
11028
11029 } else {
11030
11031 /**
11032 * Registers a custom tag name with the document.
11033 *
11034 * When a registered element is created, a `readyCallback` method is called
11035 * in the scope of the element. The `readyCallback` method can be specified on
11036 * either `options.prototype` or `options.lifecycle` with the latter taking
11037 * precedence.
11038 *
11039 * @method register
11040 * @param {String} name The tag name to register. Must include a dash ('-'),
11041 * for example 'x-component'.
11042 * @param {Object} options
11043 * @param {String} [options.extends]
11044 * (_off spec_) Tag name of an element to extend (or blank for a new
11045 * element). This parameter is not part of the specification, but instead
11046 * is a hint for the polyfill because the extendee is difficult to infer.
11047 * Remember that the input prototype must chain to the extended element's
11048 * prototype (or HTMLElement.prototype) regardless of the value of
11049 * `extends`.
11050 * @param {Object} options.prototype The prototype to use for the new
11051 * element. The prototype must inherit from HTMLElement.
11052 * @param {Object} [options.lifecycle]
11053 * Callbacks that fire at important phases in the life of the custom
11054 * element.
11055 *
11056 * @example
11057 * FancyButton = document.registerElement("fancy-button", {
11058 * extends: 'button',
11059 * prototype: Object.create(HTMLButtonElement.prototype, {
11060 * readyCallback: {
11061 * value: function() {
11062 * console.log("a fancy-button was created",
11063 * }
11064 * }
11065 * })
11066 * });
11067 * @return {Function} Constructor for the newly registered type.
11068 */
11069 function register(name, options) {
11070 //console.warn('document.registerElement("' + name + '", ', options, ')');
11071 // construct a defintion out of options
11072 // TODO(sjmiles): probably should clone options instead of mutating it
11073 var definition = options || {};
11074 if (!name) {
11075 // TODO(sjmiles): replace with more appropriate error (EricB can probably
11076 // offer guidance)
11077 throw new Error('document.registerElement: first argument `name` must not be empty');
11078 }
11079 if (name.indexOf('-') < 0) {
11080 // TODO(sjmiles): replace with more appropriate error (EricB can probably
11081 // offer guidance)
11082 throw new Error('document.registerElement: first argument (\'name\') must contain a dash (\'-\'). Argument provided was \'' + String(name) + '\'.');
11083 }
11084 // prevent registering reserved names
11085 if (isReservedTag(name)) {
11086 throw new Error('Failed to execute \'registerElement\' on \'Document\': Re gistration failed for type \'' + String(name) + '\'. The type name is invalid.') ;
11087 }
11088 // elements may only be registered once
11089 if (getRegisteredDefinition(name)) {
11090 throw new Error('DuplicateDefinitionError: a type with name \'' + String(n ame) + '\' is already registered');
11091 }
11092 // must have a prototype, default to an extension of HTMLElement
11093 // TODO(sjmiles): probably should throw if no prototype, check spec
11094 if (!definition.prototype) {
11095 // TODO(sjmiles): replace with more appropriate error (EricB can probably
11096 // offer guidance)
11097 throw new Error('Options missing required prototype property');
11098 }
11099 // record name
11100 definition.__name = name.toLowerCase();
11101 // ensure a lifecycle object so we don't have to null test it
11102 definition.lifecycle = definition.lifecycle || {};
11103 // build a list of ancestral custom elements (for native base detection)
11104 // TODO(sjmiles): we used to need to store this, but current code only
11105 // uses it in 'resolveTagName': it should probably be inlined
11106 definition.ancestry = ancestry(definition.extends);
11107 // extensions of native specializations of HTMLElement require localName
11108 // to remain native, and use secondary 'is' specifier for extension type
11109 resolveTagName(definition);
11110 // some platforms require modifications to the user-supplied prototype
11111 // chain
11112 resolvePrototypeChain(definition);
11113 // overrides to implement attributeChanged callback
11114 overrideAttributeApi(definition.prototype);
11115 // 7.1.5: Register the DEFINITION with DOCUMENT
11116 registerDefinition(definition.__name, definition);
11117 // 7.1.7. Run custom element constructor generation algorithm with PROTOTYPE
11118 // 7.1.8. Return the output of the previous step.
11119 definition.ctor = generateConstructor(definition);
11120 definition.ctor.prototype = definition.prototype;
11121 // force our .constructor to be our actual constructor
11122 definition.prototype.constructor = definition.ctor;
11123 // if initial parsing is complete
11124 if (scope.ready) {
11125 // upgrade any pre-existing nodes of this type
11126 scope.upgradeDocumentTree(document);
11127 }
11128 return definition.ctor;
11129 }
11130
11131 function isReservedTag(name) {
11132 for (var i = 0; i < reservedTagList.length; i++) {
11133 if (name === reservedTagList[i]) {
11134 return true;
11135 }
11136 }
11137 }
11138
11139 var reservedTagList = [
11140 'annotation-xml', 'color-profile', 'font-face', 'font-face-src',
11141 'font-face-uri', 'font-face-format', 'font-face-name', 'missing-glyph'
11142 ];
11143
11144 function ancestry(extnds) {
11145 var extendee = getRegisteredDefinition(extnds);
11146 if (extendee) {
11147 return ancestry(extendee.extends).concat([extendee]);
11148 }
11149 return [];
11150 }
11151
11152 function resolveTagName(definition) {
11153 // if we are explicitly extending something, that thing is our
11154 // baseTag, unless it represents a custom component
11155 var baseTag = definition.extends;
11156 // if our ancestry includes custom components, we only have a
11157 // baseTag if one of them does
11158 for (var i=0, a; (a=definition.ancestry[i]); i++) {
11159 baseTag = a.is && a.tag;
11160 }
11161 // our tag is our baseTag, if it exists, and otherwise just our name
11162 definition.tag = baseTag || definition.__name;
11163 if (baseTag) {
11164 // if there is a base tag, use secondary 'is' specifier
11165 definition.is = definition.__name;
11166 }
11167 }
11168
11169 function resolvePrototypeChain(definition) {
11170 // if we don't support __proto__ we need to locate the native level
11171 // prototype for precise mixing in
11172 if (!Object.__proto__) {
11173 // default prototype
11174 var nativePrototype = HTMLElement.prototype;
11175 // work out prototype when using type-extension
11176 if (definition.is) {
11177 var inst = document.createElement(definition.tag);
11178 nativePrototype = Object.getPrototypeOf(inst);
11179 }
11180 // ensure __proto__ reference is installed at each point on the prototype
11181 // chain.
11182 // NOTE: On platforms without __proto__, a mixin strategy is used instead
11183 // of prototype swizzling. In this case, this generated __proto__ provides
11184 // limited support for prototype traversal.
11185 var proto = definition.prototype, ancestor;
11186 while (proto && (proto !== nativePrototype)) {
11187 var ancestor = Object.getPrototypeOf(proto);
11188 proto.__proto__ = ancestor;
11189 proto = ancestor;
11190 }
11191 }
11192 // cache this in case of mixin
11193 definition.native = nativePrototype;
11194 }
11195
11196 // SECTION 4
11197
11198 function instantiate(definition) {
11199 // 4.a.1. Create a new object that implements PROTOTYPE
11200 // 4.a.2. Let ELEMENT by this new object
11201 //
11202 // the custom element instantiation algorithm must also ensure that the
11203 // output is a valid DOM element with the proper wrapper in place.
11204 //
11205 return upgrade(domCreateElement(definition.tag), definition);
11206 }
11207
11208 function upgrade(element, definition) {
11209 // some definitions specify an 'is' attribute
11210 if (definition.is) {
11211 element.setAttribute('is', definition.is);
11212 }
11213 // remove 'unresolved' attr, which is a standin for :unresolved.
11214 element.removeAttribute('unresolved');
11215 // make 'element' implement definition.prototype
11216 implement(element, definition);
11217 // flag as upgraded
11218 element.__upgraded__ = true;
11219 // lifecycle management
11220 created(element);
11221 // attachedCallback fires in tree order, call before recursing
11222 scope.insertedNode(element);
11223 // there should never be a shadow root on element at this point
11224 scope.upgradeSubtree(element);
11225 // OUTPUT
11226 return element;
11227 }
11228
11229 function implement(element, definition) {
11230 // prototype swizzling is best
11231 if (Object.__proto__) {
11232 element.__proto__ = definition.prototype;
11233 } else {
11234 // where above we can re-acquire inPrototype via
11235 // getPrototypeOf(Element), we cannot do so when
11236 // we use mixin, so we install a magic reference
11237 customMixin(element, definition.prototype, definition.native);
11238 element.__proto__ = definition.prototype;
11239 }
11240 }
11241
11242 function customMixin(inTarget, inSrc, inNative) {
11243 // TODO(sjmiles): 'used' allows us to only copy the 'youngest' version of
11244 // any property. This set should be precalculated. We also need to
11245 // consider this for supporting 'super'.
11246 var used = {};
11247 // start with inSrc
11248 var p = inSrc;
11249 // The default is HTMLElement.prototype, so we add a test to avoid mixing in
11250 // native prototypes
11251 while (p !== inNative && p !== HTMLElement.prototype) {
11252 var keys = Object.getOwnPropertyNames(p);
11253 for (var i=0, k; k=keys[i]; i++) {
11254 if (!used[k]) {
11255 Object.defineProperty(inTarget, k,
11256 Object.getOwnPropertyDescriptor(p, k));
11257 used[k] = 1;
11258 }
11259 }
11260 p = Object.getPrototypeOf(p);
11261 }
11262 }
11263
11264 function created(element) {
11265 // invoke createdCallback
11266 if (element.createdCallback) {
11267 element.createdCallback();
11268 }
11269 }
11270
11271 // attribute watching
11272
11273 function overrideAttributeApi(prototype) {
11274 // overrides to implement callbacks
11275 // TODO(sjmiles): should support access via .attributes NamedNodeMap
11276 // TODO(sjmiles): preserves user defined overrides, if any
11277 if (prototype.setAttribute._polyfilled) {
11278 return;
11279 }
11280 var setAttribute = prototype.setAttribute;
11281 prototype.setAttribute = function(name, value) {
11282 changeAttribute.call(this, name, value, setAttribute);
11283 }
11284 var removeAttribute = prototype.removeAttribute;
11285 prototype.removeAttribute = function(name) {
11286 changeAttribute.call(this, name, null, removeAttribute);
11287 }
11288 prototype.setAttribute._polyfilled = true;
11289 }
11290
11291 // https://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/custom/
11292 // index.html#dfn-attribute-changed-callback
11293 function changeAttribute(name, value, operation) {
11294 var oldValue = this.getAttribute(name);
11295 operation.apply(this, arguments);
11296 var newValue = this.getAttribute(name);
11297 if (this.attributeChangedCallback
11298 && (newValue !== oldValue)) {
11299 this.attributeChangedCallback(name, oldValue, newValue);
11300 }
11301 }
11302
11303 // element registry (maps tag names to definitions)
11304
11305 var registry = {};
11306
11307 function getRegisteredDefinition(name) {
11308 if (name) {
11309 return registry[name.toLowerCase()];
11310 }
11311 }
11312
11313 function registerDefinition(name, definition) {
11314 registry[name] = definition;
11315 }
11316
11317 function generateConstructor(definition) {
11318 return function() {
11319 return instantiate(definition);
11320 };
11321 }
11322
11323 var HTML_NAMESPACE = 'http://www.w3.org/1999/xhtml';
11324 function createElementNS(namespace, tag, typeExtension) {
11325 // NOTE: we do not support non-HTML elements,
11326 // just call createElementNS for non HTML Elements
11327 if (namespace === HTML_NAMESPACE) {
11328 return createElement(tag, typeExtension);
11329 } else {
11330 return domCreateElementNS(namespace, tag);
11331 }
11332 }
11333
11334 function createElement(tag, typeExtension) {
11335 // TODO(sjmiles): ignore 'tag' when using 'typeExtension', we could
11336 // error check it, or perhaps there should only ever be one argument
11337 var definition = getRegisteredDefinition(typeExtension || tag);
11338 if (definition) {
11339 if (tag == definition.tag && typeExtension == definition.is) {
11340 return new definition.ctor();
11341 }
11342 // Handle empty string for type extension.
11343 if (!typeExtension && !definition.is) {
11344 return new definition.ctor();
11345 }
11346 }
11347
11348 if (typeExtension) {
11349 var element = createElement(tag);
11350 element.setAttribute('is', typeExtension);
11351 return element;
11352 }
11353 var element = domCreateElement(tag);
11354 // Custom tags should be HTMLElements even if not upgraded.
11355 if (tag.indexOf('-') >= 0) {
11356 implement(element, HTMLElement);
11357 }
11358 return element;
11359 }
11360
11361 function upgradeElement(element) {
11362 if (!element.__upgraded__ && (element.nodeType === Node.ELEMENT_NODE)) {
11363 var is = element.getAttribute('is');
11364 var definition = getRegisteredDefinition(is || element.localName);
11365 if (definition) {
11366 if (is && definition.tag == element.localName) {
11367 return upgrade(element, definition);
11368 } else if (!is && !definition.extends) {
11369 return upgrade(element, definition);
11370 }
11371 }
11372 }
11373 }
11374
11375 function cloneNode(deep) {
11376 // call original clone
11377 var n = domCloneNode.call(this, deep);
11378 // upgrade the element and subtree
11379 scope.upgradeAll(n);
11380 // return the clone
11381 return n;
11382 }
11383 // capture native createElement before we override it
11384
11385 var domCreateElement = document.createElement.bind(document);
11386 var domCreateElementNS = document.createElementNS.bind(document);
11387
11388 // capture native cloneNode before we override it
11389
11390 var domCloneNode = Node.prototype.cloneNode;
11391
11392 // exports
11393
11394 document.registerElement = register;
11395 document.createElement = createElement; // override
11396 document.createElementNS = createElementNS; // override
11397 Node.prototype.cloneNode = cloneNode; // override
11398
11399 scope.registry = registry;
11400
11401 /**
11402 * Upgrade an element to a custom element. Upgrading an element
11403 * causes the custom prototype to be applied, an `is` attribute
11404 * to be attached (as needed), and invocation of the `readyCallback`.
11405 * `upgrade` does nothing if the element is already upgraded, or
11406 * if it matches no registered custom tag name.
11407 *
11408 * @method ugprade
11409 * @param {Element} element The element to upgrade.
11410 * @return {Element} The upgraded element.
11411 */
11412 scope.upgrade = upgradeElement;
11413 }
11414
11415 // Create a custom 'instanceof'. This is necessary when CustomElements
11416 // are implemented via a mixin strategy, as for example on IE10.
11417 var isInstance;
11418 if (!Object.__proto__ && !useNative) {
11419 isInstance = function(obj, ctor) {
11420 var p = obj;
11421 while (p) {
11422 // NOTE: this is not technically correct since we're not checking if
11423 // an object is an instance of a constructor; however, this should
11424 // be good enough for the mixin strategy.
11425 if (p === ctor.prototype) {
11426 return true;
11427 }
11428 p = p.__proto__;
11429 }
11430 return false;
11431 }
11432 } else {
11433 isInstance = function(obj, base) {
11434 return obj instanceof base;
11435 }
11436 }
11437
11438 // exports
11439 scope.instanceof = isInstance;
11440 scope.reservedTagList = reservedTagList;
11441
11442 // bc
11443 document.register = document.registerElement;
11444
11445 scope.hasNative = hasNative;
11446 scope.useNative = useNative;
11447
11448 })(window.CustomElements);
11449
11450 /*
11451 * Copyright 2013 The Polymer Authors. All rights reserved.
11452 * Use of this source code is governed by a BSD-style
11453 * license that can be found in the LICENSE file.
11454 */
11455
11456 (function(scope) {
11457
11458 // import
11459
11460 var IMPORT_LINK_TYPE = scope.IMPORT_LINK_TYPE;
11461
11462 // highlander object for parsing a document tree
11463
11464 var parser = {
11465 selectors: [
11466 'link[rel=' + IMPORT_LINK_TYPE + ']'
11467 ],
11468 map: {
11469 link: 'parseLink'
11470 },
11471 parse: function(inDocument) {
11472 if (!inDocument.__parsed) {
11473 // only parse once
11474 inDocument.__parsed = true;
11475 // all parsable elements in inDocument (depth-first pre-order traversal)
11476 var elts = inDocument.querySelectorAll(parser.selectors);
11477 // for each parsable node type, call the mapped parsing method
11478 forEach(elts, function(e) {
11479 parser[parser.map[e.localName]](e);
11480 });
11481 // upgrade all upgradeable static elements, anything dynamically
11482 // created should be caught by observer
11483 CustomElements.upgradeDocument(inDocument);
11484 // observe document for dom changes
11485 CustomElements.observeDocument(inDocument);
11486 }
11487 },
11488 parseLink: function(linkElt) {
11489 // imports
11490 if (isDocumentLink(linkElt)) {
11491 this.parseImport(linkElt);
11492 }
11493 },
11494 parseImport: function(linkElt) {
11495 if (linkElt.import) {
11496 parser.parse(linkElt.import);
11497 }
11498 }
11499 };
11500
11501 function isDocumentLink(inElt) {
11502 return (inElt.localName === 'link'
11503 && inElt.getAttribute('rel') === IMPORT_LINK_TYPE);
11504 }
11505
11506 var forEach = Array.prototype.forEach.call.bind(Array.prototype.forEach);
11507
11508 // exports
11509
11510 scope.parser = parser;
11511 scope.IMPORT_LINK_TYPE = IMPORT_LINK_TYPE;
11512
11513 })(window.CustomElements);
11514 /*
11515 * Copyright 2013 The Polymer Authors. All rights reserved.
11516 * Use of this source code is governed by a BSD-style
11517 * license that can be found in the LICENSE file.
11518 */
11519 (function(scope){
11520
11521 // bootstrap parsing
11522 function bootstrap() {
11523 // parse document
11524 CustomElements.parser.parse(document);
11525 // one more pass before register is 'live'
11526 CustomElements.upgradeDocument(document);
11527 // choose async
11528 var async = window.Platform && Platform.endOfMicrotask ?
11529 Platform.endOfMicrotask :
11530 setTimeout;
11531 async(function() {
11532 // set internal 'ready' flag, now document.registerElement will trigger
11533 // synchronous upgrades
11534 CustomElements.ready = true;
11535 // capture blunt profiling data
11536 CustomElements.readyTime = Date.now();
11537 if (window.HTMLImports) {
11538 CustomElements.elapsed = CustomElements.readyTime - HTMLImports.readyTime;
11539 }
11540 // notify the system that we are bootstrapped
11541 document.dispatchEvent(
11542 new CustomEvent('WebComponentsReady', {bubbles: true})
11543 );
11544
11545 // install upgrade hook if HTMLImports are available
11546 if (window.HTMLImports) {
11547 HTMLImports.__importsParsingHook = function(elt) {
11548 CustomElements.parser.parse(elt.import);
11549 }
11550 }
11551 });
11552 }
11553
11554 // CustomEvent shim for IE
11555 if (typeof window.CustomEvent !== 'function') {
11556 window.CustomEvent = function(inType) {
11557 var e = document.createEvent('HTMLEvents');
11558 e.initEvent(inType, true, true);
11559 return e;
11560 };
11561 }
11562
11563 // When loading at readyState complete time (or via flag), boot custom elements
11564 // immediately.
11565 // If relevant, HTMLImports must already be loaded.
11566 if (document.readyState === 'complete' || scope.flags.eager) {
11567 bootstrap();
11568 // When loading at readyState interactive time, bootstrap only if HTMLImports
11569 // are not pending. Also avoid IE as the semantics of this state are unreliable.
11570 } else if (document.readyState === 'interactive' && !window.attachEvent &&
11571 (!window.HTMLImports || window.HTMLImports.ready)) {
11572 bootstrap();
11573 // When loading at other readyStates, wait for the appropriate DOM event to
11574 // bootstrap.
11575 } else {
11576 var loadEvent = window.HTMLImports && !HTMLImports.ready ?
11577 'HTMLImportsLoaded' : 'DOMContentLoaded';
11578 window.addEventListener(loadEvent, bootstrap);
11579 }
11580
11581 })(window.CustomElements);
11582
11583 /*
11584 * Copyright 2013 The Polymer Authors. All rights reserved.
11585 * Use of this source code is governed by a BSD-style
11586 * license that can be found in the LICENSE file.
11587 */
11588 (function() {
11589
11590 if (window.ShadowDOMPolyfill) {
11591
11592 // ensure wrapped inputs for these functions
11593 var fns = ['upgradeAll', 'upgradeSubtree', 'observeDocument',
11594 'upgradeDocument'];
11595
11596 // cache originals
11597 var original = {};
11598 fns.forEach(function(fn) {
11599 original[fn] = CustomElements[fn];
11600 });
11601
11602 // override
11603 fns.forEach(function(fn) {
11604 CustomElements[fn] = function(inNode) {
11605 return original[fn](wrap(inNode));
11606 };
11607 });
11608
11609 }
11610
11611 })();
11612
11613 /*
11614 * Copyright 2014 The Polymer Authors. All rights reserved.
11615 * Use of this source code is governed by a BSD-style
11616 * license that can be found in the LICENSE file.
11617 */
11618 (function(scope) {
11619 var endOfMicrotask = scope.endOfMicrotask;
11620
11621 // Generic url loader
11622 function Loader(regex) {
11623 this.regex = regex;
11624 }
11625 Loader.prototype = {
11626 // TODO(dfreedm): there may be a better factoring here
11627 // extract absolute urls from the text (full of relative urls)
11628 extractUrls: function(text, base) {
11629 var matches = [];
11630 var matched, u;
11631 while ((matched = this.regex.exec(text))) {
11632 u = new URL(matched[1], base);
11633 matches.push({matched: matched[0], url: u.href});
11634 }
11635 return matches;
11636 },
11637 // take a text blob, a root url, and a callback and load all the urls found within the text
11638 // returns a map of absolute url to text
11639 process: function(text, root, callback) {
11640 var matches = this.extractUrls(text, root);
11641 this.fetch(matches, {}, callback);
11642 },
11643 // build a mapping of url -> text from matches
11644 fetch: function(matches, map, callback) {
11645 var inflight = matches.length;
11646
11647 // return early if there is no fetching to be done
11648 if (!inflight) {
11649 return callback(map);
11650 }
11651
11652 var done = function() {
11653 if (--inflight === 0) {
11654 callback(map);
11655 }
11656 };
11657
11658 // map url -> responseText
11659 var handleXhr = function(err, request) {
11660 var match = request.match;
11661 var key = match.url;
11662 // handle errors with an empty string
11663 if (err) {
11664 map[key] = '';
11665 return done();
11666 }
11667 var response = request.response || request.responseText;
11668 map[key] = response;
11669 this.fetch(this.extractUrls(response, key), map, done);
11670 };
11671
11672 var m, req, url;
11673 for (var i = 0; i < inflight; i++) {
11674 m = matches[i];
11675 url = m.url;
11676 // if this url has already been requested, skip requesting it again
11677 if (map[url]) {
11678 // Async call to done to simplify the inflight logic
11679 endOfMicrotask(done);
11680 continue;
11681 }
11682 req = this.xhr(url, handleXhr, this);
11683 req.match = m;
11684 // tag the map with an XHR request to deduplicate at the same level
11685 map[url] = req;
11686 }
11687 },
11688 xhr: function(url, callback, scope) {
11689 var request = new XMLHttpRequest();
11690 request.open('GET', url, true);
11691 request.send();
11692 request.onload = function() {
11693 callback.call(scope, null, request);
11694 };
11695 request.onerror = function() {
11696 callback.call(scope, null, request);
11697 };
11698 return request;
11699 }
11700 };
11701
11702 scope.Loader = Loader;
11703 })(window.Platform);
11704
11705 /*
11706 * Copyright 2014 The Polymer Authors. All rights reserved.
11707 * Use of this source code is governed by a BSD-style
11708 * license that can be found in the LICENSE file.
11709 */
11710 (function(scope) {
11711
11712 var urlResolver = scope.urlResolver;
11713 var Loader = scope.Loader;
11714
11715 function StyleResolver() {
11716 this.loader = new Loader(this.regex);
11717 }
11718 StyleResolver.prototype = {
11719 regex: /@import\s+(?:url)?["'\(]*([^'"\)]*)['"\)]*;/g,
11720 // Recursively replace @imports with the text at that url
11721 resolve: function(text, url, callback) {
11722 var done = function(map) {
11723 callback(this.flatten(text, url, map));
11724 }.bind(this);
11725 this.loader.process(text, url, done);
11726 },
11727 // resolve the textContent of a style node
11728 resolveNode: function(style, callback) {
11729 var text = style.textContent;
11730 var url = style.ownerDocument.baseURI;
11731 var done = function(text) {
11732 style.textContent = text;
11733 callback(style);
11734 };
11735 this.resolve(text, url, done);
11736 },
11737 // flatten all the @imports to text
11738 flatten: function(text, base, map) {
11739 var matches = this.loader.extractUrls(text, base);
11740 var match, url, intermediate;
11741 for (var i = 0; i < matches.length; i++) {
11742 match = matches[i];
11743 url = match.url;
11744 // resolve any css text to be relative to the importer
11745 intermediate = urlResolver.resolveCssText(map[url], url);
11746 // flatten intermediate @imports
11747 intermediate = this.flatten(intermediate, url, map);
11748 text = text.replace(match.matched, intermediate);
11749 }
11750 return text;
11751 },
11752 loadStyles: function(styles, callback) {
11753 var loaded=0, l = styles.length;
11754 // called in the context of the style
11755 function loadedStyle(style) {
11756 loaded++;
11757 if (loaded === l && callback) {
11758 callback();
11759 }
11760 }
11761 for (var i=0, s; (i<l) && (s=styles[i]); i++) {
11762 this.resolveNode(s, loadedStyle);
11763 }
11764 }
11765 };
11766
11767 var styleResolver = new StyleResolver();
11768
11769 // exports
11770 scope.styleResolver = styleResolver;
11771
11772 })(window.Platform);
11773
11774 /*
11775 * Copyright 2013 The Polymer Authors. All rights reserved.
11776 * Use of this source code is governed by a BSD-style
11777 * license that can be found in the LICENSE file.
11778 */
11779
11780 (function(scope) {
11781 scope = scope || {};
11782 scope.external = scope.external || {};
11783 var target = {
11784 shadow: function(inEl) {
11785 if (inEl) {
11786 return inEl.shadowRoot || inEl.webkitShadowRoot;
11787 }
11788 },
11789 canTarget: function(shadow) {
11790 return shadow && Boolean(shadow.elementFromPoint);
11791 },
11792 targetingShadow: function(inEl) {
11793 var s = this.shadow(inEl);
11794 if (this.canTarget(s)) {
11795 return s;
11796 }
11797 },
11798 olderShadow: function(shadow) {
11799 var os = shadow.olderShadowRoot;
11800 if (!os) {
11801 var se = shadow.querySelector('shadow');
11802 if (se) {
11803 os = se.olderShadowRoot;
11804 }
11805 }
11806 return os;
11807 },
11808 allShadows: function(element) {
11809 var shadows = [], s = this.shadow(element);
11810 while(s) {
11811 shadows.push(s);
11812 s = this.olderShadow(s);
11813 }
11814 return shadows;
11815 },
11816 searchRoot: function(inRoot, x, y) {
11817 if (inRoot) {
11818 var t = inRoot.elementFromPoint(x, y);
11819 var st, sr, os;
11820 // is element a shadow host?
11821 sr = this.targetingShadow(t);
11822 while (sr) {
11823 // find the the element inside the shadow root
11824 st = sr.elementFromPoint(x, y);
11825 if (!st) {
11826 // check for older shadows
11827 sr = this.olderShadow(sr);
11828 } else {
11829 // shadowed element may contain a shadow root
11830 var ssr = this.targetingShadow(st);
11831 return this.searchRoot(ssr, x, y) || st;
11832 }
11833 }
11834 // light dom element is the target
11835 return t;
11836 }
11837 },
11838 owner: function(element) {
11839 var s = element;
11840 // walk up until you hit the shadow root or document
11841 while (s.parentNode) {
11842 s = s.parentNode;
11843 }
11844 // the owner element is expected to be a Document or ShadowRoot
11845 if (s.nodeType != Node.DOCUMENT_NODE && s.nodeType != Node.DOCUMENT_FRAGME NT_NODE) {
11846 s = document;
11847 }
11848 return s;
11849 },
11850 findTarget: function(inEvent) {
11851 var x = inEvent.clientX, y = inEvent.clientY;
11852 // if the listener is in the shadow root, it is much faster to start there
11853 var s = this.owner(inEvent.target);
11854 // if x, y is not in this root, fall back to document search
11855 if (!s.elementFromPoint(x, y)) {
11856 s = document;
11857 }
11858 return this.searchRoot(s, x, y);
11859 }
11860 };
11861 scope.targetFinding = target;
11862 scope.findTarget = target.findTarget.bind(target);
11863
11864 window.PointerEventsPolyfill = scope;
11865 })(window.PointerEventsPolyfill);
11866
11867 /*
11868 * Copyright 2013 The Polymer Authors. All rights reserved.
11869 * Use of this source code is governed by a BSD-style
11870 * license that can be found in the LICENSE file.
11871 */
11872 (function() {
11873 function shadowSelector(v) {
11874 return 'body /shadow-deep/ ' + selector(v);
11875 }
11876 function selector(v) {
11877 return '[touch-action="' + v + '"]';
11878 }
11879 function rule(v) {
11880 return '{ -ms-touch-action: ' + v + '; touch-action: ' + v + '; touch-action -delay: none; }';
11881 }
11882 var attrib2css = [
11883 'none',
11884 'auto',
11885 'pan-x',
11886 'pan-y',
11887 {
11888 rule: 'pan-x pan-y',
11889 selectors: [
11890 'pan-x pan-y',
11891 'pan-y pan-x'
11892 ]
11893 }
11894 ];
11895 var styles = '';
11896 // only install stylesheet if the browser has touch action support
11897 var head = document.head;
11898 var hasNativePE = window.PointerEvent || window.MSPointerEvent;
11899 // only add shadow selectors if shadowdom is supported
11900 var hasShadowRoot = !window.ShadowDOMPolyfill && document.head.createShadowRoo t;
11901
11902 if (hasNativePE) {
11903 attrib2css.forEach(function(r) {
11904 if (String(r) === r) {
11905 styles += selector(r) + rule(r) + '\n';
11906 if (hasShadowRoot) {
11907 styles += shadowSelector(r) + rule(r) + '\n';
11908 }
11909 } else {
11910 styles += r.selectors.map(selector) + rule(r.rule) + '\n';
11911 if (hasShadowRoot) {
11912 styles += r.selectors.map(shadowSelector) + rule(r.rule) + '\n';
11913 }
11914 }
11915 });
11916
11917 var el = document.createElement('style');
11918 el.textContent = styles;
11919 document.head.appendChild(el);
11920 }
11921 })();
11922
11923 /*
11924 * Copyright 2013 The Polymer Authors. All rights reserved.
11925 * Use of this source code is governed by a BSD-style
11926 * license that can be found in the LICENSE file.
11927 */
11928
11929 /**
11930 * This is the constructor for new PointerEvents.
11931 *
11932 * New Pointer Events must be given a type, and an optional dictionary of
11933 * initialization properties.
11934 *
11935 * Due to certain platform requirements, events returned from the constructor
11936 * identify as MouseEvents.
11937 *
11938 * @constructor
11939 * @param {String} inType The type of the event to create.
11940 * @param {Object} [inDict] An optional dictionary of initial event properties.
11941 * @return {Event} A new PointerEvent of type `inType` and initialized with prop erties from `inDict`.
11942 */
11943 (function(scope) {
11944
11945 var MOUSE_PROPS = [
11946 'bubbles',
11947 'cancelable',
11948 'view',
11949 'detail',
11950 'screenX',
11951 'screenY',
11952 'clientX',
11953 'clientY',
11954 'ctrlKey',
11955 'altKey',
11956 'shiftKey',
11957 'metaKey',
11958 'button',
11959 'relatedTarget',
11960 'pageX',
11961 'pageY'
11962 ];
11963
11964 var MOUSE_DEFAULTS = [
11965 false,
11966 false,
11967 null,
11968 null,
11969 0,
11970 0,
11971 0,
11972 0,
11973 false,
11974 false,
11975 false,
11976 false,
11977 0,
11978 null,
11979 0,
11980 0
11981 ];
11982
11983 function PointerEvent(inType, inDict) {
11984 inDict = inDict || Object.create(null);
11985
11986 var e = document.createEvent('Event');
11987 e.initEvent(inType, inDict.bubbles || false, inDict.cancelable || false);
11988
11989 // define inherited MouseEvent properties
11990 for(var i = 0, p; i < MOUSE_PROPS.length; i++) {
11991 p = MOUSE_PROPS[i];
11992 e[p] = inDict[p] || MOUSE_DEFAULTS[i];
11993 }
11994 e.buttons = inDict.buttons || 0;
11995
11996 // Spec requires that pointers without pressure specified use 0.5 for down
11997 // state and 0 for up state.
11998 var pressure = 0;
11999 if (inDict.pressure) {
12000 pressure = inDict.pressure;
12001 } else {
12002 pressure = e.buttons ? 0.5 : 0;
12003 }
12004
12005 // add x/y properties aliased to clientX/Y
12006 e.x = e.clientX;
12007 e.y = e.clientY;
12008
12009 // define the properties of the PointerEvent interface
12010 e.pointerId = inDict.pointerId || 0;
12011 e.width = inDict.width || 0;
12012 e.height = inDict.height || 0;
12013 e.pressure = pressure;
12014 e.tiltX = inDict.tiltX || 0;
12015 e.tiltY = inDict.tiltY || 0;
12016 e.pointerType = inDict.pointerType || '';
12017 e.hwTimestamp = inDict.hwTimestamp || 0;
12018 e.isPrimary = inDict.isPrimary || false;
12019 return e;
12020 }
12021
12022 // attach to window
12023 if (!scope.PointerEvent) {
12024 scope.PointerEvent = PointerEvent;
12025 }
12026 })(window);
12027
12028 /*
12029 * Copyright 2013 The Polymer Authors. All rights reserved.
12030 * Use of this source code is governed by a BSD-style
12031 * license that can be found in the LICENSE file.
12032 */
12033
12034 /**
12035 * This module implements an map of pointer states
12036 */
12037 (function(scope) {
12038 var USE_MAP = window.Map && window.Map.prototype.forEach;
12039 var POINTERS_FN = function(){ return this.size; };
12040 function PointerMap() {
12041 if (USE_MAP) {
12042 var m = new Map();
12043 m.pointers = POINTERS_FN;
12044 return m;
12045 } else {
12046 this.keys = [];
12047 this.values = [];
12048 }
12049 }
12050
12051 PointerMap.prototype = {
12052 set: function(inId, inEvent) {
12053 var i = this.keys.indexOf(inId);
12054 if (i > -1) {
12055 this.values[i] = inEvent;
12056 } else {
12057 this.keys.push(inId);
12058 this.values.push(inEvent);
12059 }
12060 },
12061 has: function(inId) {
12062 return this.keys.indexOf(inId) > -1;
12063 },
12064 'delete': function(inId) {
12065 var i = this.keys.indexOf(inId);
12066 if (i > -1) {
12067 this.keys.splice(i, 1);
12068 this.values.splice(i, 1);
12069 }
12070 },
12071 get: function(inId) {
12072 var i = this.keys.indexOf(inId);
12073 return this.values[i];
12074 },
12075 clear: function() {
12076 this.keys.length = 0;
12077 this.values.length = 0;
12078 },
12079 // return value, key, map
12080 forEach: function(callback, thisArg) {
12081 this.values.forEach(function(v, i) {
12082 callback.call(thisArg, v, this.keys[i], this);
12083 }, this);
12084 },
12085 pointers: function() {
12086 return this.keys.length;
12087 }
12088 };
12089
12090 scope.PointerMap = PointerMap;
12091 })(window.PointerEventsPolyfill);
12092
12093 /*
12094 * Copyright 2013 The Polymer Authors. All rights reserved.
12095 * Use of this source code is governed by a BSD-style
12096 * license that can be found in the LICENSE file.
12097 */
12098
12099 (function(scope) {
12100 var CLONE_PROPS = [
12101 // MouseEvent
12102 'bubbles',
12103 'cancelable',
12104 'view',
12105 'detail',
12106 'screenX',
12107 'screenY',
12108 'clientX',
12109 'clientY',
12110 'ctrlKey',
12111 'altKey',
12112 'shiftKey',
12113 'metaKey',
12114 'button',
12115 'relatedTarget',
12116 // DOM Level 3
12117 'buttons',
12118 // PointerEvent
12119 'pointerId',
12120 'width',
12121 'height',
12122 'pressure',
12123 'tiltX',
12124 'tiltY',
12125 'pointerType',
12126 'hwTimestamp',
12127 'isPrimary',
12128 // event instance
12129 'type',
12130 'target',
12131 'currentTarget',
12132 'which',
12133 'pageX',
12134 'pageY'
12135 ];
12136
12137 var CLONE_DEFAULTS = [
12138 // MouseEvent
12139 false,
12140 false,
12141 null,
12142 null,
12143 0,
12144 0,
12145 0,
12146 0,
12147 false,
12148 false,
12149 false,
12150 false,
12151 0,
12152 null,
12153 // DOM Level 3
12154 0,
12155 // PointerEvent
12156 0,
12157 0,
12158 0,
12159 0,
12160 0,
12161 0,
12162 '',
12163 0,
12164 false,
12165 // event instance
12166 '',
12167 null,
12168 null,
12169 0,
12170 0,
12171 0
12172 ];
12173
12174 var HAS_SVG_INSTANCE = (typeof SVGElementInstance !== 'undefined');
12175
12176 /**
12177 * This module is for normalizing events. Mouse and Touch events will be
12178 * collected here, and fire PointerEvents that have the same semantics, no
12179 * matter the source.
12180 * Events fired:
12181 * - pointerdown: a pointing is added
12182 * - pointerup: a pointer is removed
12183 * - pointermove: a pointer is moved
12184 * - pointerover: a pointer crosses into an element
12185 * - pointerout: a pointer leaves an element
12186 * - pointercancel: a pointer will no longer generate events
12187 */
12188 var dispatcher = {
12189 pointermap: new scope.PointerMap(),
12190 eventMap: Object.create(null),
12191 captureInfo: Object.create(null),
12192 // Scope objects for native events.
12193 // This exists for ease of testing.
12194 eventSources: Object.create(null),
12195 eventSourceList: [],
12196 /**
12197 * Add a new event source that will generate pointer events.
12198 *
12199 * `inSource` must contain an array of event names named `events`, and
12200 * functions with the names specified in the `events` array.
12201 * @param {string} name A name for the event source
12202 * @param {Object} source A new source of platform events.
12203 */
12204 registerSource: function(name, source) {
12205 var s = source;
12206 var newEvents = s.events;
12207 if (newEvents) {
12208 newEvents.forEach(function(e) {
12209 if (s[e]) {
12210 this.eventMap[e] = s[e].bind(s);
12211 }
12212 }, this);
12213 this.eventSources[name] = s;
12214 this.eventSourceList.push(s);
12215 }
12216 },
12217 register: function(element) {
12218 var l = this.eventSourceList.length;
12219 for (var i = 0, es; (i < l) && (es = this.eventSourceList[i]); i++) {
12220 // call eventsource register
12221 es.register.call(es, element);
12222 }
12223 },
12224 unregister: function(element) {
12225 var l = this.eventSourceList.length;
12226 for (var i = 0, es; (i < l) && (es = this.eventSourceList[i]); i++) {
12227 // call eventsource register
12228 es.unregister.call(es, element);
12229 }
12230 },
12231 contains: scope.external.contains || function(container, contained) {
12232 return container.contains(contained);
12233 },
12234 // EVENTS
12235 down: function(inEvent) {
12236 inEvent.bubbles = true;
12237 this.fireEvent('pointerdown', inEvent);
12238 },
12239 move: function(inEvent) {
12240 inEvent.bubbles = true;
12241 this.fireEvent('pointermove', inEvent);
12242 },
12243 up: function(inEvent) {
12244 inEvent.bubbles = true;
12245 this.fireEvent('pointerup', inEvent);
12246 },
12247 enter: function(inEvent) {
12248 inEvent.bubbles = false;
12249 this.fireEvent('pointerenter', inEvent);
12250 },
12251 leave: function(inEvent) {
12252 inEvent.bubbles = false;
12253 this.fireEvent('pointerleave', inEvent);
12254 },
12255 over: function(inEvent) {
12256 inEvent.bubbles = true;
12257 this.fireEvent('pointerover', inEvent);
12258 },
12259 out: function(inEvent) {
12260 inEvent.bubbles = true;
12261 this.fireEvent('pointerout', inEvent);
12262 },
12263 cancel: function(inEvent) {
12264 inEvent.bubbles = true;
12265 this.fireEvent('pointercancel', inEvent);
12266 },
12267 leaveOut: function(event) {
12268 this.out(event);
12269 if (!this.contains(event.target, event.relatedTarget)) {
12270 this.leave(event);
12271 }
12272 },
12273 enterOver: function(event) {
12274 this.over(event);
12275 if (!this.contains(event.target, event.relatedTarget)) {
12276 this.enter(event);
12277 }
12278 },
12279 // LISTENER LOGIC
12280 eventHandler: function(inEvent) {
12281 // This is used to prevent multiple dispatch of pointerevents from
12282 // platform events. This can happen when two elements in different scopes
12283 // are set up to create pointer events, which is relevant to Shadow DOM.
12284 if (inEvent._handledByPE) {
12285 return;
12286 }
12287 var type = inEvent.type;
12288 var fn = this.eventMap && this.eventMap[type];
12289 if (fn) {
12290 fn(inEvent);
12291 }
12292 inEvent._handledByPE = true;
12293 },
12294 // set up event listeners
12295 listen: function(target, events) {
12296 events.forEach(function(e) {
12297 this.addEvent(target, e);
12298 }, this);
12299 },
12300 // remove event listeners
12301 unlisten: function(target, events) {
12302 events.forEach(function(e) {
12303 this.removeEvent(target, e);
12304 }, this);
12305 },
12306 addEvent: scope.external.addEvent || function(target, eventName) {
12307 target.addEventListener(eventName, this.boundHandler);
12308 },
12309 removeEvent: scope.external.removeEvent || function(target, eventName) {
12310 target.removeEventListener(eventName, this.boundHandler);
12311 },
12312 // EVENT CREATION AND TRACKING
12313 /**
12314 * Creates a new Event of type `inType`, based on the information in
12315 * `inEvent`.
12316 *
12317 * @param {string} inType A string representing the type of event to create
12318 * @param {Event} inEvent A platform event with a target
12319 * @return {Event} A PointerEvent of type `inType`
12320 */
12321 makeEvent: function(inType, inEvent) {
12322 // relatedTarget must be null if pointer is captured
12323 if (this.captureInfo[inEvent.pointerId]) {
12324 inEvent.relatedTarget = null;
12325 }
12326 var e = new PointerEvent(inType, inEvent);
12327 if (inEvent.preventDefault) {
12328 e.preventDefault = inEvent.preventDefault;
12329 }
12330 e._target = e._target || inEvent.target;
12331 return e;
12332 },
12333 // make and dispatch an event in one call
12334 fireEvent: function(inType, inEvent) {
12335 var e = this.makeEvent(inType, inEvent);
12336 return this.dispatchEvent(e);
12337 },
12338 /**
12339 * Returns a snapshot of inEvent, with writable properties.
12340 *
12341 * @param {Event} inEvent An event that contains properties to copy.
12342 * @return {Object} An object containing shallow copies of `inEvent`'s
12343 * properties.
12344 */
12345 cloneEvent: function(inEvent) {
12346 var eventCopy = Object.create(null), p;
12347 for (var i = 0; i < CLONE_PROPS.length; i++) {
12348 p = CLONE_PROPS[i];
12349 eventCopy[p] = inEvent[p] || CLONE_DEFAULTS[i];
12350 // Work around SVGInstanceElement shadow tree
12351 // Return the <use> element that is represented by the instance for Safa ri, Chrome, IE.
12352 // This is the behavior implemented by Firefox.
12353 if (HAS_SVG_INSTANCE && (p === 'target' || p === 'relatedTarget')) {
12354 if (eventCopy[p] instanceof SVGElementInstance) {
12355 eventCopy[p] = eventCopy[p].correspondingUseElement;
12356 }
12357 }
12358 }
12359 // keep the semantics of preventDefault
12360 if (inEvent.preventDefault) {
12361 eventCopy.preventDefault = function() {
12362 inEvent.preventDefault();
12363 };
12364 }
12365 return eventCopy;
12366 },
12367 getTarget: function(inEvent) {
12368 // if pointer capture is set, route all events for the specified pointerId
12369 // to the capture target
12370 return this.captureInfo[inEvent.pointerId] || inEvent._target;
12371 },
12372 setCapture: function(inPointerId, inTarget) {
12373 if (this.captureInfo[inPointerId]) {
12374 this.releaseCapture(inPointerId);
12375 }
12376 this.captureInfo[inPointerId] = inTarget;
12377 var e = document.createEvent('Event');
12378 e.initEvent('gotpointercapture', true, false);
12379 e.pointerId = inPointerId;
12380 this.implicitRelease = this.releaseCapture.bind(this, inPointerId);
12381 document.addEventListener('pointerup', this.implicitRelease);
12382 document.addEventListener('pointercancel', this.implicitRelease);
12383 e._target = inTarget;
12384 this.asyncDispatchEvent(e);
12385 },
12386 releaseCapture: function(inPointerId) {
12387 var t = this.captureInfo[inPointerId];
12388 if (t) {
12389 var e = document.createEvent('Event');
12390 e.initEvent('lostpointercapture', true, false);
12391 e.pointerId = inPointerId;
12392 this.captureInfo[inPointerId] = undefined;
12393 document.removeEventListener('pointerup', this.implicitRelease);
12394 document.removeEventListener('pointercancel', this.implicitRelease);
12395 e._target = t;
12396 this.asyncDispatchEvent(e);
12397 }
12398 },
12399 /**
12400 * Dispatches the event to its target.
12401 *
12402 * @param {Event} inEvent The event to be dispatched.
12403 * @return {Boolean} True if an event handler returns true, false otherwise.
12404 */
12405 dispatchEvent: scope.external.dispatchEvent || function(inEvent) {
12406 var t = this.getTarget(inEvent);
12407 if (t) {
12408 return t.dispatchEvent(inEvent);
12409 }
12410 },
12411 asyncDispatchEvent: function(inEvent) {
12412 requestAnimationFrame(this.dispatchEvent.bind(this, inEvent));
12413 }
12414 };
12415 dispatcher.boundHandler = dispatcher.eventHandler.bind(dispatcher);
12416 scope.dispatcher = dispatcher;
12417 scope.register = dispatcher.register.bind(dispatcher);
12418 scope.unregister = dispatcher.unregister.bind(dispatcher);
12419 })(window.PointerEventsPolyfill);
12420
12421 /*
12422 * Copyright 2013 The Polymer Authors. All rights reserved.
12423 * Use of this source code is governed by a BSD-style
12424 * license that can be found in the LICENSE file.
12425 */
12426
12427 /**
12428 * This module uses Mutation Observers to dynamically adjust which nodes will
12429 * generate Pointer Events.
12430 *
12431 * All nodes that wish to generate Pointer Events must have the attribute
12432 * `touch-action` set to `none`.
12433 */
12434 (function(scope) {
12435 var forEach = Array.prototype.forEach.call.bind(Array.prototype.forEach);
12436 var map = Array.prototype.map.call.bind(Array.prototype.map);
12437 var toArray = Array.prototype.slice.call.bind(Array.prototype.slice);
12438 var filter = Array.prototype.filter.call.bind(Array.prototype.filter);
12439 var MO = window.MutationObserver || window.WebKitMutationObserver;
12440 var SELECTOR = '[touch-action]';
12441 var OBSERVER_INIT = {
12442 subtree: true,
12443 childList: true,
12444 attributes: true,
12445 attributeOldValue: true,
12446 attributeFilter: ['touch-action']
12447 };
12448
12449 function Installer(add, remove, changed, binder) {
12450 this.addCallback = add.bind(binder);
12451 this.removeCallback = remove.bind(binder);
12452 this.changedCallback = changed.bind(binder);
12453 if (MO) {
12454 this.observer = new MO(this.mutationWatcher.bind(this));
12455 }
12456 }
12457
12458 Installer.prototype = {
12459 watchSubtree: function(target) {
12460 // Only watch scopes that can target find, as these are top-level.
12461 // Otherwise we can see duplicate additions and removals that add noise.
12462 //
12463 // TODO(dfreedman): For some instances with ShadowDOMPolyfill, we can see
12464 // a removal without an insertion when a node is redistributed among
12465 // shadows. Since it all ends up correct in the document, watching only
12466 // the document will yield the correct mutations to watch.
12467 if (scope.targetFinding.canTarget(target)) {
12468 this.observer.observe(target, OBSERVER_INIT);
12469 }
12470 },
12471 enableOnSubtree: function(target) {
12472 this.watchSubtree(target);
12473 if (target === document && document.readyState !== 'complete') {
12474 this.installOnLoad();
12475 } else {
12476 this.installNewSubtree(target);
12477 }
12478 },
12479 installNewSubtree: function(target) {
12480 forEach(this.findElements(target), this.addElement, this);
12481 },
12482 findElements: function(target) {
12483 if (target.querySelectorAll) {
12484 return target.querySelectorAll(SELECTOR);
12485 }
12486 return [];
12487 },
12488 removeElement: function(el) {
12489 this.removeCallback(el);
12490 },
12491 addElement: function(el) {
12492 this.addCallback(el);
12493 },
12494 elementChanged: function(el, oldValue) {
12495 this.changedCallback(el, oldValue);
12496 },
12497 concatLists: function(accum, list) {
12498 return accum.concat(toArray(list));
12499 },
12500 // register all touch-action = none nodes on document load
12501 installOnLoad: function() {
12502 document.addEventListener('readystatechange', function() {
12503 if (document.readyState === 'complete') {
12504 this.installNewSubtree(document);
12505 }
12506 }.bind(this));
12507 },
12508 isElement: function(n) {
12509 return n.nodeType === Node.ELEMENT_NODE;
12510 },
12511 flattenMutationTree: function(inNodes) {
12512 // find children with touch-action
12513 var tree = map(inNodes, this.findElements, this);
12514 // make sure the added nodes are accounted for
12515 tree.push(filter(inNodes, this.isElement));
12516 // flatten the list
12517 return tree.reduce(this.concatLists, []);
12518 },
12519 mutationWatcher: function(mutations) {
12520 mutations.forEach(this.mutationHandler, this);
12521 },
12522 mutationHandler: function(m) {
12523 if (m.type === 'childList') {
12524 var added = this.flattenMutationTree(m.addedNodes);
12525 added.forEach(this.addElement, this);
12526 var removed = this.flattenMutationTree(m.removedNodes);
12527 removed.forEach(this.removeElement, this);
12528 } else if (m.type === 'attributes') {
12529 this.elementChanged(m.target, m.oldValue);
12530 }
12531 }
12532 };
12533
12534 if (!MO) {
12535 Installer.prototype.watchSubtree = function(){
12536 console.warn('PointerEventsPolyfill: MutationObservers not found, touch-ac tion will not be dynamically detected');
12537 };
12538 }
12539
12540 scope.Installer = Installer;
12541 })(window.PointerEventsPolyfill);
12542
12543 /*
12544 * Copyright 2013 The Polymer Authors. All rights reserved.
12545 * Use of this source code is governed by a BSD-style
12546 * license that can be found in the LICENSE file.
12547 */
12548
12549 (function (scope) {
12550 var dispatcher = scope.dispatcher;
12551 var pointermap = dispatcher.pointermap;
12552 // radius around touchend that swallows mouse events
12553 var DEDUP_DIST = 25;
12554
12555 var WHICH_TO_BUTTONS = [0, 1, 4, 2];
12556
12557 var HAS_BUTTONS = false;
12558 try {
12559 HAS_BUTTONS = new MouseEvent('test', {buttons: 1}).buttons === 1;
12560 } catch (e) {}
12561
12562 // handler block for native mouse events
12563 var mouseEvents = {
12564 POINTER_ID: 1,
12565 POINTER_TYPE: 'mouse',
12566 events: [
12567 'mousedown',
12568 'mousemove',
12569 'mouseup',
12570 'mouseover',
12571 'mouseout'
12572 ],
12573 register: function(target) {
12574 dispatcher.listen(target, this.events);
12575 },
12576 unregister: function(target) {
12577 dispatcher.unlisten(target, this.events);
12578 },
12579 lastTouches: [],
12580 // collide with the global mouse listener
12581 isEventSimulatedFromTouch: function(inEvent) {
12582 var lts = this.lastTouches;
12583 var x = inEvent.clientX, y = inEvent.clientY;
12584 for (var i = 0, l = lts.length, t; i < l && (t = lts[i]); i++) {
12585 // simulated mouse events will be swallowed near a primary touchend
12586 var dx = Math.abs(x - t.x), dy = Math.abs(y - t.y);
12587 if (dx <= DEDUP_DIST && dy <= DEDUP_DIST) {
12588 return true;
12589 }
12590 }
12591 },
12592 prepareEvent: function(inEvent) {
12593 var e = dispatcher.cloneEvent(inEvent);
12594 // forward mouse preventDefault
12595 var pd = e.preventDefault;
12596 e.preventDefault = function() {
12597 inEvent.preventDefault();
12598 pd();
12599 };
12600 e.pointerId = this.POINTER_ID;
12601 e.isPrimary = true;
12602 e.pointerType = this.POINTER_TYPE;
12603 if (!HAS_BUTTONS) {
12604 e.buttons = WHICH_TO_BUTTONS[e.which] || 0;
12605 }
12606 return e;
12607 },
12608 mousedown: function(inEvent) {
12609 if (!this.isEventSimulatedFromTouch(inEvent)) {
12610 var p = pointermap.has(this.POINTER_ID);
12611 // TODO(dfreedman) workaround for some elements not sending mouseup
12612 // http://crbug/149091
12613 if (p) {
12614 this.cancel(inEvent);
12615 }
12616 var e = this.prepareEvent(inEvent);
12617 pointermap.set(this.POINTER_ID, inEvent);
12618 dispatcher.down(e);
12619 }
12620 },
12621 mousemove: function(inEvent) {
12622 if (!this.isEventSimulatedFromTouch(inEvent)) {
12623 var e = this.prepareEvent(inEvent);
12624 dispatcher.move(e);
12625 }
12626 },
12627 mouseup: function(inEvent) {
12628 if (!this.isEventSimulatedFromTouch(inEvent)) {
12629 var p = pointermap.get(this.POINTER_ID);
12630 if (p && p.button === inEvent.button) {
12631 var e = this.prepareEvent(inEvent);
12632 dispatcher.up(e);
12633 this.cleanupMouse();
12634 }
12635 }
12636 },
12637 mouseover: function(inEvent) {
12638 if (!this.isEventSimulatedFromTouch(inEvent)) {
12639 var e = this.prepareEvent(inEvent);
12640 dispatcher.enterOver(e);
12641 }
12642 },
12643 mouseout: function(inEvent) {
12644 if (!this.isEventSimulatedFromTouch(inEvent)) {
12645 var e = this.prepareEvent(inEvent);
12646 dispatcher.leaveOut(e);
12647 }
12648 },
12649 cancel: function(inEvent) {
12650 var e = this.prepareEvent(inEvent);
12651 dispatcher.cancel(e);
12652 this.cleanupMouse();
12653 },
12654 cleanupMouse: function() {
12655 pointermap['delete'](this.POINTER_ID);
12656 }
12657 };
12658
12659 scope.mouseEvents = mouseEvents;
12660 })(window.PointerEventsPolyfill);
12661
12662 /*
12663 * Copyright 2013 The Polymer Authors. All rights reserved.
12664 * Use of this source code is governed by a BSD-style
12665 * license that can be found in the LICENSE file.
12666 */
12667
12668 (function(scope) {
12669 var dispatcher = scope.dispatcher;
12670 var captureInfo = dispatcher.captureInfo;
12671 var findTarget = scope.findTarget;
12672 var allShadows = scope.targetFinding.allShadows.bind(scope.targetFinding);
12673 var pointermap = dispatcher.pointermap;
12674 var touchMap = Array.prototype.map.call.bind(Array.prototype.map);
12675 // This should be long enough to ignore compat mouse events made by touch
12676 var DEDUP_TIMEOUT = 2500;
12677 var CLICK_COUNT_TIMEOUT = 200;
12678 var ATTRIB = 'touch-action';
12679 var INSTALLER;
12680 // The presence of touch event handlers blocks scrolling, and so we must be ca reful to
12681 // avoid adding handlers unnecessarily. Chrome plans to add a touch-action-de lay property
12682 // (crbug.com/329559) to address this, and once we have that we can opt-in to a simpler
12683 // handler registration mechanism. Rather than try to predict how exactly to opt-in to
12684 // that we'll just leave this disabled until there is a build of Chrome to tes t.
12685 var HAS_TOUCH_ACTION_DELAY = false;
12686
12687 // handler block for native touch events
12688 var touchEvents = {
12689 events: [
12690 'touchstart',
12691 'touchmove',
12692 'touchend',
12693 'touchcancel'
12694 ],
12695 register: function(target) {
12696 if (HAS_TOUCH_ACTION_DELAY) {
12697 dispatcher.listen(target, this.events);
12698 } else {
12699 INSTALLER.enableOnSubtree(target);
12700 }
12701 },
12702 unregister: function(target) {
12703 if (HAS_TOUCH_ACTION_DELAY) {
12704 dispatcher.unlisten(target, this.events);
12705 } else {
12706 // TODO(dfreedman): is it worth it to disconnect the MO?
12707 }
12708 },
12709 elementAdded: function(el) {
12710 var a = el.getAttribute(ATTRIB);
12711 var st = this.touchActionToScrollType(a);
12712 if (st) {
12713 el._scrollType = st;
12714 dispatcher.listen(el, this.events);
12715 // set touch-action on shadows as well
12716 allShadows(el).forEach(function(s) {
12717 s._scrollType = st;
12718 dispatcher.listen(s, this.events);
12719 }, this);
12720 }
12721 },
12722 elementRemoved: function(el) {
12723 el._scrollType = undefined;
12724 dispatcher.unlisten(el, this.events);
12725 // remove touch-action from shadow
12726 allShadows(el).forEach(function(s) {
12727 s._scrollType = undefined;
12728 dispatcher.unlisten(s, this.events);
12729 }, this);
12730 },
12731 elementChanged: function(el, oldValue) {
12732 var a = el.getAttribute(ATTRIB);
12733 var st = this.touchActionToScrollType(a);
12734 var oldSt = this.touchActionToScrollType(oldValue);
12735 // simply update scrollType if listeners are already established
12736 if (st && oldSt) {
12737 el._scrollType = st;
12738 allShadows(el).forEach(function(s) {
12739 s._scrollType = st;
12740 }, this);
12741 } else if (oldSt) {
12742 this.elementRemoved(el);
12743 } else if (st) {
12744 this.elementAdded(el);
12745 }
12746 },
12747 scrollTypes: {
12748 EMITTER: 'none',
12749 XSCROLLER: 'pan-x',
12750 YSCROLLER: 'pan-y',
12751 SCROLLER: /^(?:pan-x pan-y)|(?:pan-y pan-x)|auto$/
12752 },
12753 touchActionToScrollType: function(touchAction) {
12754 var t = touchAction;
12755 var st = this.scrollTypes;
12756 if (t === 'none') {
12757 return 'none';
12758 } else if (t === st.XSCROLLER) {
12759 return 'X';
12760 } else if (t === st.YSCROLLER) {
12761 return 'Y';
12762 } else if (st.SCROLLER.exec(t)) {
12763 return 'XY';
12764 }
12765 },
12766 POINTER_TYPE: 'touch',
12767 firstTouch: null,
12768 isPrimaryTouch: function(inTouch) {
12769 return this.firstTouch === inTouch.identifier;
12770 },
12771 setPrimaryTouch: function(inTouch) {
12772 // set primary touch if there no pointers, or the only pointer is the mous e
12773 if (pointermap.pointers() === 0 || (pointermap.pointers() === 1 && pointer map.has(1))) {
12774 this.firstTouch = inTouch.identifier;
12775 this.firstXY = {X: inTouch.clientX, Y: inTouch.clientY};
12776 this.scrolling = false;
12777 this.cancelResetClickCount();
12778 }
12779 },
12780 removePrimaryPointer: function(inPointer) {
12781 if (inPointer.isPrimary) {
12782 this.firstTouch = null;
12783 this.firstXY = null;
12784 this.resetClickCount();
12785 }
12786 },
12787 clickCount: 0,
12788 resetId: null,
12789 resetClickCount: function() {
12790 var fn = function() {
12791 this.clickCount = 0;
12792 this.resetId = null;
12793 }.bind(this);
12794 this.resetId = setTimeout(fn, CLICK_COUNT_TIMEOUT);
12795 },
12796 cancelResetClickCount: function() {
12797 if (this.resetId) {
12798 clearTimeout(this.resetId);
12799 }
12800 },
12801 typeToButtons: function(type) {
12802 var ret = 0;
12803 if (type === 'touchstart' || type === 'touchmove') {
12804 ret = 1;
12805 }
12806 return ret;
12807 },
12808 touchToPointer: function(inTouch) {
12809 var cte = this.currentTouchEvent;
12810 var e = dispatcher.cloneEvent(inTouch);
12811 // Spec specifies that pointerId 1 is reserved for Mouse.
12812 // Touch identifiers can start at 0.
12813 // Add 2 to the touch identifier for compatibility.
12814 var id = e.pointerId = inTouch.identifier + 2;
12815 e.target = captureInfo[id] || findTarget(e);
12816 e.bubbles = true;
12817 e.cancelable = true;
12818 e.detail = this.clickCount;
12819 e.button = 0;
12820 e.buttons = this.typeToButtons(cte.type);
12821 e.width = inTouch.webkitRadiusX || inTouch.radiusX || 0;
12822 e.height = inTouch.webkitRadiusY || inTouch.radiusY || 0;
12823 e.pressure = inTouch.webkitForce || inTouch.force || 0.5;
12824 e.isPrimary = this.isPrimaryTouch(inTouch);
12825 e.pointerType = this.POINTER_TYPE;
12826 // forward touch preventDefaults
12827 var self = this;
12828 e.preventDefault = function() {
12829 self.scrolling = false;
12830 self.firstXY = null;
12831 cte.preventDefault();
12832 };
12833 return e;
12834 },
12835 processTouches: function(inEvent, inFunction) {
12836 var tl = inEvent.changedTouches;
12837 this.currentTouchEvent = inEvent;
12838 for (var i = 0, t; i < tl.length; i++) {
12839 t = tl[i];
12840 inFunction.call(this, this.touchToPointer(t));
12841 }
12842 },
12843 // For single axis scrollers, determines whether the element should emit
12844 // pointer events or behave as a scroller
12845 shouldScroll: function(inEvent) {
12846 if (this.firstXY) {
12847 var ret;
12848 var scrollAxis = inEvent.currentTarget._scrollType;
12849 if (scrollAxis === 'none') {
12850 // this element is a touch-action: none, should never scroll
12851 ret = false;
12852 } else if (scrollAxis === 'XY') {
12853 // this element should always scroll
12854 ret = true;
12855 } else {
12856 var t = inEvent.changedTouches[0];
12857 // check the intended scroll axis, and other axis
12858 var a = scrollAxis;
12859 var oa = scrollAxis === 'Y' ? 'X' : 'Y';
12860 var da = Math.abs(t['client' + a] - this.firstXY[a]);
12861 var doa = Math.abs(t['client' + oa] - this.firstXY[oa]);
12862 // if delta in the scroll axis > delta other axis, scroll instead of
12863 // making events
12864 ret = da >= doa;
12865 }
12866 this.firstXY = null;
12867 return ret;
12868 }
12869 },
12870 findTouch: function(inTL, inId) {
12871 for (var i = 0, l = inTL.length, t; i < l && (t = inTL[i]); i++) {
12872 if (t.identifier === inId) {
12873 return true;
12874 }
12875 }
12876 },
12877 // In some instances, a touchstart can happen without a touchend. This
12878 // leaves the pointermap in a broken state.
12879 // Therefore, on every touchstart, we remove the touches that did not fire a
12880 // touchend event.
12881 // To keep state globally consistent, we fire a
12882 // pointercancel for this "abandoned" touch
12883 vacuumTouches: function(inEvent) {
12884 var tl = inEvent.touches;
12885 // pointermap.pointers() should be < tl.length here, as the touchstart has not
12886 // been processed yet.
12887 if (pointermap.pointers() >= tl.length) {
12888 var d = [];
12889 pointermap.forEach(function(value, key) {
12890 // Never remove pointerId == 1, which is mouse.
12891 // Touch identifiers are 2 smaller than their pointerId, which is the
12892 // index in pointermap.
12893 if (key !== 1 && !this.findTouch(tl, key - 2)) {
12894 var p = value.out;
12895 d.push(p);
12896 }
12897 }, this);
12898 d.forEach(this.cancelOut, this);
12899 }
12900 },
12901 touchstart: function(inEvent) {
12902 this.vacuumTouches(inEvent);
12903 this.setPrimaryTouch(inEvent.changedTouches[0]);
12904 this.dedupSynthMouse(inEvent);
12905 if (!this.scrolling) {
12906 this.clickCount++;
12907 this.processTouches(inEvent, this.overDown);
12908 }
12909 },
12910 overDown: function(inPointer) {
12911 var p = pointermap.set(inPointer.pointerId, {
12912 target: inPointer.target,
12913 out: inPointer,
12914 outTarget: inPointer.target
12915 });
12916 dispatcher.over(inPointer);
12917 dispatcher.enter(inPointer);
12918 dispatcher.down(inPointer);
12919 },
12920 touchmove: function(inEvent) {
12921 if (!this.scrolling) {
12922 if (this.shouldScroll(inEvent)) {
12923 this.scrolling = true;
12924 this.touchcancel(inEvent);
12925 } else {
12926 inEvent.preventDefault();
12927 this.processTouches(inEvent, this.moveOverOut);
12928 }
12929 }
12930 },
12931 moveOverOut: function(inPointer) {
12932 var event = inPointer;
12933 var pointer = pointermap.get(event.pointerId);
12934 // a finger drifted off the screen, ignore it
12935 if (!pointer) {
12936 return;
12937 }
12938 var outEvent = pointer.out;
12939 var outTarget = pointer.outTarget;
12940 dispatcher.move(event);
12941 if (outEvent && outTarget !== event.target) {
12942 outEvent.relatedTarget = event.target;
12943 event.relatedTarget = outTarget;
12944 // recover from retargeting by shadow
12945 outEvent.target = outTarget;
12946 if (event.target) {
12947 dispatcher.leaveOut(outEvent);
12948 dispatcher.enterOver(event);
12949 } else {
12950 // clean up case when finger leaves the screen
12951 event.target = outTarget;
12952 event.relatedTarget = null;
12953 this.cancelOut(event);
12954 }
12955 }
12956 pointer.out = event;
12957 pointer.outTarget = event.target;
12958 },
12959 touchend: function(inEvent) {
12960 this.dedupSynthMouse(inEvent);
12961 this.processTouches(inEvent, this.upOut);
12962 },
12963 upOut: function(inPointer) {
12964 if (!this.scrolling) {
12965 dispatcher.up(inPointer);
12966 dispatcher.out(inPointer);
12967 dispatcher.leave(inPointer);
12968 }
12969 this.cleanUpPointer(inPointer);
12970 },
12971 touchcancel: function(inEvent) {
12972 this.processTouches(inEvent, this.cancelOut);
12973 },
12974 cancelOut: function(inPointer) {
12975 dispatcher.cancel(inPointer);
12976 dispatcher.out(inPointer);
12977 dispatcher.leave(inPointer);
12978 this.cleanUpPointer(inPointer);
12979 },
12980 cleanUpPointer: function(inPointer) {
12981 pointermap['delete'](inPointer.pointerId);
12982 this.removePrimaryPointer(inPointer);
12983 },
12984 // prevent synth mouse events from creating pointer events
12985 dedupSynthMouse: function(inEvent) {
12986 var lts = scope.mouseEvents.lastTouches;
12987 var t = inEvent.changedTouches[0];
12988 // only the primary finger will synth mouse events
12989 if (this.isPrimaryTouch(t)) {
12990 // remember x/y of last touch
12991 var lt = {x: t.clientX, y: t.clientY};
12992 lts.push(lt);
12993 var fn = (function(lts, lt){
12994 var i = lts.indexOf(lt);
12995 if (i > -1) {
12996 lts.splice(i, 1);
12997 }
12998 }).bind(null, lts, lt);
12999 setTimeout(fn, DEDUP_TIMEOUT);
13000 }
13001 }
13002 };
13003
13004 if (!HAS_TOUCH_ACTION_DELAY) {
13005 INSTALLER = new scope.Installer(touchEvents.elementAdded, touchEvents.elemen tRemoved, touchEvents.elementChanged, touchEvents);
13006 }
13007
13008 scope.touchEvents = touchEvents;
13009 })(window.PointerEventsPolyfill);
13010
13011 /*
13012 * Copyright 2013 The Polymer Authors. All rights reserved.
13013 * Use of this source code is governed by a BSD-style
13014 * license that can be found in the LICENSE file.
13015 */
13016
13017 (function(scope) {
13018 var dispatcher = scope.dispatcher;
13019 var pointermap = dispatcher.pointermap;
13020 var HAS_BITMAP_TYPE = window.MSPointerEvent && typeof window.MSPointerEvent.MS POINTER_TYPE_MOUSE === 'number';
13021 var msEvents = {
13022 events: [
13023 'MSPointerDown',
13024 'MSPointerMove',
13025 'MSPointerUp',
13026 'MSPointerOut',
13027 'MSPointerOver',
13028 'MSPointerCancel',
13029 'MSGotPointerCapture',
13030 'MSLostPointerCapture'
13031 ],
13032 register: function(target) {
13033 dispatcher.listen(target, this.events);
13034 },
13035 unregister: function(target) {
13036 dispatcher.unlisten(target, this.events);
13037 },
13038 POINTER_TYPES: [
13039 '',
13040 'unavailable',
13041 'touch',
13042 'pen',
13043 'mouse'
13044 ],
13045 prepareEvent: function(inEvent) {
13046 var e = inEvent;
13047 if (HAS_BITMAP_TYPE) {
13048 e = dispatcher.cloneEvent(inEvent);
13049 e.pointerType = this.POINTER_TYPES[inEvent.pointerType];
13050 }
13051 return e;
13052 },
13053 cleanup: function(id) {
13054 pointermap['delete'](id);
13055 },
13056 MSPointerDown: function(inEvent) {
13057 pointermap.set(inEvent.pointerId, inEvent);
13058 var e = this.prepareEvent(inEvent);
13059 dispatcher.down(e);
13060 },
13061 MSPointerMove: function(inEvent) {
13062 var e = this.prepareEvent(inEvent);
13063 dispatcher.move(e);
13064 },
13065 MSPointerUp: function(inEvent) {
13066 var e = this.prepareEvent(inEvent);
13067 dispatcher.up(e);
13068 this.cleanup(inEvent.pointerId);
13069 },
13070 MSPointerOut: function(inEvent) {
13071 var e = this.prepareEvent(inEvent);
13072 dispatcher.leaveOut(e);
13073 },
13074 MSPointerOver: function(inEvent) {
13075 var e = this.prepareEvent(inEvent);
13076 dispatcher.enterOver(e);
13077 },
13078 MSPointerCancel: function(inEvent) {
13079 var e = this.prepareEvent(inEvent);
13080 dispatcher.cancel(e);
13081 this.cleanup(inEvent.pointerId);
13082 },
13083 MSLostPointerCapture: function(inEvent) {
13084 var e = dispatcher.makeEvent('lostpointercapture', inEvent);
13085 dispatcher.dispatchEvent(e);
13086 },
13087 MSGotPointerCapture: function(inEvent) {
13088 var e = dispatcher.makeEvent('gotpointercapture', inEvent);
13089 dispatcher.dispatchEvent(e);
13090 }
13091 };
13092
13093 scope.msEvents = msEvents;
13094 })(window.PointerEventsPolyfill);
13095
13096 /*
13097 * Copyright 2013 The Polymer Authors. All rights reserved.
13098 * Use of this source code is governed by a BSD-style
13099 * license that can be found in the LICENSE file.
13100 */
13101
13102 /**
13103 * This module contains the handlers for native platform events.
13104 * From here, the dispatcher is called to create unified pointer events.
13105 * Included are touch events (v1), mouse events, and MSPointerEvents.
13106 */
13107 (function(scope) {
13108 var dispatcher = scope.dispatcher;
13109
13110 // only activate if this platform does not have pointer events
13111 if (window.PointerEvent !== scope.PointerEvent) {
13112
13113 if (window.navigator.msPointerEnabled) {
13114 var tp = window.navigator.msMaxTouchPoints;
13115 Object.defineProperty(window.navigator, 'maxTouchPoints', {
13116 value: tp,
13117 enumerable: true
13118 });
13119 dispatcher.registerSource('ms', scope.msEvents);
13120 } else {
13121 dispatcher.registerSource('mouse', scope.mouseEvents);
13122 if (window.ontouchstart !== undefined) {
13123 dispatcher.registerSource('touch', scope.touchEvents);
13124 }
13125 }
13126
13127 dispatcher.register(document);
13128 }
13129 })(window.PointerEventsPolyfill);
13130
13131 /*
13132 * Copyright 2013 The Polymer Authors. All rights reserved.
13133 * Use of this source code is governed by a BSD-style
13134 * license that can be found in the LICENSE file.
13135 */
13136
13137 (function(scope) {
13138 var dispatcher = scope.dispatcher;
13139 var n = window.navigator;
13140 var s, r;
13141 function assertDown(id) {
13142 if (!dispatcher.pointermap.has(id)) {
13143 throw new Error('InvalidPointerId');
13144 }
13145 }
13146 if (n.msPointerEnabled) {
13147 s = function(pointerId) {
13148 assertDown(pointerId);
13149 this.msSetPointerCapture(pointerId);
13150 };
13151 r = function(pointerId) {
13152 assertDown(pointerId);
13153 this.msReleasePointerCapture(pointerId);
13154 };
13155 } else {
13156 s = function setPointerCapture(pointerId) {
13157 assertDown(pointerId);
13158 dispatcher.setCapture(pointerId, this);
13159 };
13160 r = function releasePointerCapture(pointerId) {
13161 assertDown(pointerId);
13162 dispatcher.releaseCapture(pointerId, this);
13163 };
13164 }
13165 if (window.Element && !Element.prototype.setPointerCapture) {
13166 Object.defineProperties(Element.prototype, {
13167 'setPointerCapture': {
13168 value: s
13169 },
13170 'releasePointerCapture': {
13171 value: r
13172 }
13173 });
13174 }
13175 })(window.PointerEventsPolyfill);
13176
13177 /*
13178 * Copyright 2013 The Polymer Authors. All rights reserved.
13179 * Use of this source code is governed by a BSD-style
13180 * license that can be found in the LICENSE file.
13181 */
13182
13183 /**
13184 * PointerGestureEvent is the constructor for all PointerGesture events.
13185 *
13186 * @module PointerGestures
13187 * @class PointerGestureEvent
13188 * @extends UIEvent
13189 * @constructor
13190 * @param {String} inType Event type
13191 * @param {Object} [inDict] Dictionary of properties to initialize on the event
13192 */
13193
13194 function PointerGestureEvent(inType, inDict) {
13195 var dict = inDict || {};
13196 var e = document.createEvent('Event');
13197 var props = {
13198 bubbles: Boolean(dict.bubbles) === dict.bubbles || true,
13199 cancelable: Boolean(dict.cancelable) === dict.cancelable || true
13200 };
13201
13202 e.initEvent(inType, props.bubbles, props.cancelable);
13203
13204 var keys = Object.keys(dict), k;
13205 for (var i = 0; i < keys.length; i++) {
13206 k = keys[i];
13207 e[k] = dict[k];
13208 }
13209
13210 e.preventTap = this.preventTap;
13211
13212 return e;
13213 }
13214
13215 /**
13216 * Allows for any gesture to prevent the tap gesture.
13217 *
13218 * @method preventTap
13219 */
13220 PointerGestureEvent.prototype.preventTap = function() {
13221 this.tapPrevented = true;
13222 };
13223
13224
13225 /*
13226 * Copyright 2013 The Polymer Authors. All rights reserved.
13227 * Use of this source code is governed by a BSD-style
13228 * license that can be found in the LICENSE file.
13229 */
13230
13231 (function(scope) {
13232 /**
13233 * This class contains the gesture recognizers that create the PointerGesture
13234 * events.
13235 *
13236 * @class PointerGestures
13237 * @static
13238 */
13239 scope = scope || {};
13240 scope.utils = {
13241 LCA: {
13242 // Determines the lowest node in the ancestor chain of a and b
13243 find: function(a, b) {
13244 if (a === b) {
13245 return a;
13246 }
13247 // fast case, a is a direct descendant of b or vice versa
13248 if (a.contains) {
13249 if (a.contains(b)) {
13250 return a;
13251 }
13252 if (b.contains(a)) {
13253 return b;
13254 }
13255 }
13256 var adepth = this.depth(a);
13257 var bdepth = this.depth(b);
13258 var d = adepth - bdepth;
13259 if (d > 0) {
13260 a = this.walk(a, d);
13261 } else {
13262 b = this.walk(b, -d);
13263 }
13264 while(a && b && a !== b) {
13265 a = this.walk(a, 1);
13266 b = this.walk(b, 1);
13267 }
13268 return a;
13269 },
13270 walk: function(n, u) {
13271 for (var i = 0; i < u; i++) {
13272 n = n.parentNode;
13273 }
13274 return n;
13275 },
13276 depth: function(n) {
13277 var d = 0;
13278 while(n) {
13279 d++;
13280 n = n.parentNode;
13281 }
13282 return d;
13283 }
13284 }
13285 };
13286 scope.findLCA = function(a, b) {
13287 return scope.utils.LCA.find(a, b);
13288 }
13289 window.PointerGestures = scope;
13290 })(window.PointerGestures);
13291
13292 /*
13293 * Copyright 2013 The Polymer Authors. All rights reserved.
13294 * Use of this source code is governed by a BSD-style
13295 * license that can be found in the LICENSE file.
13296 */
13297
13298 /**
13299 * This module implements an map of pointer states
13300 */
13301 (function(scope) {
13302 var USE_MAP = window.Map && window.Map.prototype.forEach;
13303 var POINTERS_FN = function(){ return this.size; };
13304 function PointerMap() {
13305 if (USE_MAP) {
13306 var m = new Map();
13307 m.pointers = POINTERS_FN;
13308 return m;
13309 } else {
13310 this.keys = [];
13311 this.values = [];
13312 }
13313 }
13314
13315 PointerMap.prototype = {
13316 set: function(inId, inEvent) {
13317 var i = this.keys.indexOf(inId);
13318 if (i > -1) {
13319 this.values[i] = inEvent;
13320 } else {
13321 this.keys.push(inId);
13322 this.values.push(inEvent);
13323 }
13324 },
13325 has: function(inId) {
13326 return this.keys.indexOf(inId) > -1;
13327 },
13328 'delete': function(inId) {
13329 var i = this.keys.indexOf(inId);
13330 if (i > -1) {
13331 this.keys.splice(i, 1);
13332 this.values.splice(i, 1);
13333 }
13334 },
13335 get: function(inId) {
13336 var i = this.keys.indexOf(inId);
13337 return this.values[i];
13338 },
13339 clear: function() {
13340 this.keys.length = 0;
13341 this.values.length = 0;
13342 },
13343 // return value, key, map
13344 forEach: function(callback, thisArg) {
13345 this.values.forEach(function(v, i) {
13346 callback.call(thisArg, v, this.keys[i], this);
13347 }, this);
13348 },
13349 pointers: function() {
13350 return this.keys.length;
13351 }
13352 };
13353
13354 scope.PointerMap = PointerMap;
13355 })(window.PointerGestures);
13356
13357 /*
13358 * Copyright 2013 The Polymer Authors. All rights reserved.
13359 * Use of this source code is governed by a BSD-style
13360 * license that can be found in the LICENSE file.
13361 */
13362
13363 (function(scope) {
13364 var CLONE_PROPS = [
13365 // MouseEvent
13366 'bubbles',
13367 'cancelable',
13368 'view',
13369 'detail',
13370 'screenX',
13371 'screenY',
13372 'clientX',
13373 'clientY',
13374 'ctrlKey',
13375 'altKey',
13376 'shiftKey',
13377 'metaKey',
13378 'button',
13379 'relatedTarget',
13380 // DOM Level 3
13381 'buttons',
13382 // PointerEvent
13383 'pointerId',
13384 'width',
13385 'height',
13386 'pressure',
13387 'tiltX',
13388 'tiltY',
13389 'pointerType',
13390 'hwTimestamp',
13391 'isPrimary',
13392 // event instance
13393 'type',
13394 'target',
13395 'currentTarget',
13396 'screenX',
13397 'screenY',
13398 'pageX',
13399 'pageY',
13400 'tapPrevented'
13401 ];
13402
13403 var CLONE_DEFAULTS = [
13404 // MouseEvent
13405 false,
13406 false,
13407 null,
13408 null,
13409 0,
13410 0,
13411 0,
13412 0,
13413 false,
13414 false,
13415 false,
13416 false,
13417 0,
13418 null,
13419 // DOM Level 3
13420 0,
13421 // PointerEvent
13422 0,
13423 0,
13424 0,
13425 0,
13426 0,
13427 0,
13428 '',
13429 0,
13430 false,
13431 // event instance
13432 '',
13433 null,
13434 null,
13435 0,
13436 0,
13437 0,
13438 0
13439 ];
13440
13441 var dispatcher = {
13442 handledEvents: new WeakMap(),
13443 targets: new WeakMap(),
13444 handlers: {},
13445 recognizers: {},
13446 events: {},
13447 // Add a new gesture recognizer to the event listeners.
13448 // Recognizer needs an `events` property.
13449 registerRecognizer: function(inName, inRecognizer) {
13450 var r = inRecognizer;
13451 this.recognizers[inName] = r;
13452 r.events.forEach(function(e) {
13453 if (r[e]) {
13454 this.events[e] = true;
13455 var f = r[e].bind(r);
13456 this.addHandler(e, f);
13457 }
13458 }, this);
13459 },
13460 addHandler: function(inEvent, inFn) {
13461 var e = inEvent;
13462 if (!this.handlers[e]) {
13463 this.handlers[e] = [];
13464 }
13465 this.handlers[e].push(inFn);
13466 },
13467 // add event listeners for inTarget
13468 registerTarget: function(inTarget) {
13469 this.listen(Object.keys(this.events), inTarget);
13470 },
13471 // remove event listeners for inTarget
13472 unregisterTarget: function(inTarget) {
13473 this.unlisten(Object.keys(this.events), inTarget);
13474 },
13475 // LISTENER LOGIC
13476 eventHandler: function(inEvent) {
13477 if (this.handledEvents.get(inEvent)) {
13478 return;
13479 }
13480 var type = inEvent.type, fns = this.handlers[type];
13481 if (fns) {
13482 this.makeQueue(fns, inEvent);
13483 }
13484 this.handledEvents.set(inEvent, true);
13485 },
13486 // queue event for async dispatch
13487 makeQueue: function(inHandlerFns, inEvent) {
13488 // must clone events to keep the (possibly shadowed) target correct for
13489 // async dispatching
13490 var e = this.cloneEvent(inEvent);
13491 requestAnimationFrame(this.runQueue.bind(this, inHandlerFns, e));
13492 },
13493 // Dispatch the queued events
13494 runQueue: function(inHandlers, inEvent) {
13495 this.currentPointerId = inEvent.pointerId;
13496 for (var i = 0, f, l = inHandlers.length; (i < l) && (f = inHandlers[i]); i++) {
13497 f(inEvent);
13498 }
13499 this.currentPointerId = 0;
13500 },
13501 // set up event listeners
13502 listen: function(inEvents, inTarget) {
13503 inEvents.forEach(function(e) {
13504 this.addEvent(e, this.boundHandler, false, inTarget);
13505 }, this);
13506 },
13507 // remove event listeners
13508 unlisten: function(inEvents) {
13509 inEvents.forEach(function(e) {
13510 this.removeEvent(e, this.boundHandler, false, inTarget);
13511 }, this);
13512 },
13513 addEvent: function(inEventName, inEventHandler, inCapture, inTarget) {
13514 inTarget.addEventListener(inEventName, inEventHandler, inCapture);
13515 },
13516 removeEvent: function(inEventName, inEventHandler, inCapture, inTarget) {
13517 inTarget.removeEventListener(inEventName, inEventHandler, inCapture);
13518 },
13519 // EVENT CREATION AND TRACKING
13520 // Creates a new Event of type `inType`, based on the information in
13521 // `inEvent`.
13522 makeEvent: function(inType, inDict) {
13523 return new PointerGestureEvent(inType, inDict);
13524 },
13525 /*
13526 * Returns a snapshot of inEvent, with writable properties.
13527 *
13528 * @method cloneEvent
13529 * @param {Event} inEvent An event that contains properties to copy.
13530 * @return {Object} An object containing shallow copies of `inEvent`'s
13531 * properties.
13532 */
13533 cloneEvent: function(inEvent) {
13534 var eventCopy = {}, p;
13535 for (var i = 0; i < CLONE_PROPS.length; i++) {
13536 p = CLONE_PROPS[i];
13537 eventCopy[p] = inEvent[p] || CLONE_DEFAULTS[i];
13538 }
13539 return eventCopy;
13540 },
13541 // Dispatches the event to its target.
13542 dispatchEvent: function(inEvent, inTarget) {
13543 var t = inTarget || this.targets.get(inEvent);
13544 if (t) {
13545 t.dispatchEvent(inEvent);
13546 if (inEvent.tapPrevented) {
13547 this.preventTap(this.currentPointerId);
13548 }
13549 }
13550 },
13551 asyncDispatchEvent: function(inEvent, inTarget) {
13552 requestAnimationFrame(this.dispatchEvent.bind(this, inEvent, inTarget));
13553 },
13554 preventTap: function(inPointerId) {
13555 var t = this.recognizers.tap;
13556 if (t){
13557 t.preventTap(inPointerId);
13558 }
13559 }
13560 };
13561 dispatcher.boundHandler = dispatcher.eventHandler.bind(dispatcher);
13562 // recognizers call into the dispatcher and load later
13563 // solve the chicken and egg problem by having registerScopes module run last
13564 dispatcher.registerQueue = [];
13565 dispatcher.immediateRegister = false;
13566 scope.dispatcher = dispatcher;
13567 /**
13568 * Enable gesture events for a given scope, typically
13569 * [ShadowRoots](https://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/shadow /index.html#shadow-root-object).
13570 *
13571 * @for PointerGestures
13572 * @method register
13573 * @param {ShadowRoot} scope A top level scope to enable gesture
13574 * support on.
13575 */
13576 scope.register = function(inScope) {
13577 if (dispatcher.immediateRegister) {
13578 var pe = window.PointerEventsPolyfill;
13579 if (pe) {
13580 pe.register(inScope);
13581 }
13582 scope.dispatcher.registerTarget(inScope);
13583 } else {
13584 dispatcher.registerQueue.push(inScope);
13585 }
13586 };
13587 scope.register(document);
13588 })(window.PointerGestures);
13589
13590 /*
13591 * Copyright 2013 The Polymer Authors. All rights reserved.
13592 * Use of this source code is governed by a BSD-style
13593 * license that can be found in the LICENSE file.
13594 */
13595
13596 /**
13597 * This event is fired when a pointer is held down for 200ms.
13598 *
13599 * @module PointerGestures
13600 * @submodule Events
13601 * @class hold
13602 */
13603 /**
13604 * Type of pointer that made the holding event.
13605 * @type String
13606 * @property pointerType
13607 */
13608 /**
13609 * Screen X axis position of the held pointer
13610 * @type Number
13611 * @property clientX
13612 */
13613 /**
13614 * Screen Y axis position of the held pointer
13615 * @type Number
13616 * @property clientY
13617 */
13618 /**
13619 * Type of pointer that made the holding event.
13620 * @type String
13621 * @property pointerType
13622 */
13623 /**
13624 * This event is fired every 200ms while a pointer is held down.
13625 *
13626 * @class holdpulse
13627 * @extends hold
13628 */
13629 /**
13630 * Milliseconds pointer has been held down.
13631 * @type Number
13632 * @property holdTime
13633 */
13634 /**
13635 * This event is fired when a held pointer is released or moved.
13636 *
13637 * @class released
13638 */
13639
13640 (function(scope) {
13641 var dispatcher = scope.dispatcher;
13642 var hold = {
13643 // wait at least HOLD_DELAY ms between hold and pulse events
13644 HOLD_DELAY: 200,
13645 // pointer can move WIGGLE_THRESHOLD pixels before not counting as a hold
13646 WIGGLE_THRESHOLD: 16,
13647 events: [
13648 'pointerdown',
13649 'pointermove',
13650 'pointerup',
13651 'pointercancel'
13652 ],
13653 heldPointer: null,
13654 holdJob: null,
13655 pulse: function() {
13656 var hold = Date.now() - this.heldPointer.timeStamp;
13657 var type = this.held ? 'holdpulse' : 'hold';
13658 this.fireHold(type, hold);
13659 this.held = true;
13660 },
13661 cancel: function() {
13662 clearInterval(this.holdJob);
13663 if (this.held) {
13664 this.fireHold('release');
13665 }
13666 this.held = false;
13667 this.heldPointer = null;
13668 this.target = null;
13669 this.holdJob = null;
13670 },
13671 pointerdown: function(inEvent) {
13672 if (inEvent.isPrimary && !this.heldPointer) {
13673 this.heldPointer = inEvent;
13674 this.target = inEvent.target;
13675 this.holdJob = setInterval(this.pulse.bind(this), this.HOLD_DELAY);
13676 }
13677 },
13678 pointerup: function(inEvent) {
13679 if (this.heldPointer && this.heldPointer.pointerId === inEvent.pointerId) {
13680 this.cancel();
13681 }
13682 },
13683 pointercancel: function(inEvent) {
13684 this.cancel();
13685 },
13686 pointermove: function(inEvent) {
13687 if (this.heldPointer && this.heldPointer.pointerId === inEvent.pointerId) {
13688 var x = inEvent.clientX - this.heldPointer.clientX;
13689 var y = inEvent.clientY - this.heldPointer.clientY;
13690 if ((x * x + y * y) > this.WIGGLE_THRESHOLD) {
13691 this.cancel();
13692 }
13693 }
13694 },
13695 fireHold: function(inType, inHoldTime) {
13696 var p = {
13697 pointerType: this.heldPointer.pointerType,
13698 clientX: this.heldPointer.clientX,
13699 clientY: this.heldPointer.clientY
13700 };
13701 if (inHoldTime) {
13702 p.holdTime = inHoldTime;
13703 }
13704 var e = dispatcher.makeEvent(inType, p);
13705 dispatcher.dispatchEvent(e, this.target);
13706 if (e.tapPrevented) {
13707 dispatcher.preventTap(this.heldPointer.pointerId);
13708 }
13709 }
13710 };
13711 dispatcher.registerRecognizer('hold', hold);
13712 })(window.PointerGestures);
13713
13714 /*
13715 * Copyright 2013 The Polymer Authors. All rights reserved.
13716 * Use of this source code is governed by a BSD-style
13717 * license that can be found in the LICENSE file.
13718 */
13719
13720 /**
13721 * This event denotes the beginning of a series of tracking events.
13722 *
13723 * @module PointerGestures
13724 * @submodule Events
13725 * @class trackstart
13726 */
13727 /**
13728 * Pixels moved in the x direction since trackstart.
13729 * @type Number
13730 * @property dx
13731 */
13732 /**
13733 * Pixes moved in the y direction since trackstart.
13734 * @type Number
13735 * @property dy
13736 */
13737 /**
13738 * Pixels moved in the x direction since the last track.
13739 * @type Number
13740 * @property ddx
13741 */
13742 /**
13743 * Pixles moved in the y direction since the last track.
13744 * @type Number
13745 * @property ddy
13746 */
13747 /**
13748 * The clientX position of the track gesture.
13749 * @type Number
13750 * @property clientX
13751 */
13752 /**
13753 * The clientY position of the track gesture.
13754 * @type Number
13755 * @property clientY
13756 */
13757 /**
13758 * The pageX position of the track gesture.
13759 * @type Number
13760 * @property pageX
13761 */
13762 /**
13763 * The pageY position of the track gesture.
13764 * @type Number
13765 * @property pageY
13766 */
13767 /**
13768 * The screenX position of the track gesture.
13769 * @type Number
13770 * @property screenX
13771 */
13772 /**
13773 * The screenY position of the track gesture.
13774 * @type Number
13775 * @property screenY
13776 */
13777 /**
13778 * The last x axis direction of the pointer.
13779 * @type Number
13780 * @property xDirection
13781 */
13782 /**
13783 * The last y axis direction of the pointer.
13784 * @type Number
13785 * @property yDirection
13786 */
13787 /**
13788 * A shared object between all tracking events.
13789 * @type Object
13790 * @property trackInfo
13791 */
13792 /**
13793 * The element currently under the pointer.
13794 * @type Element
13795 * @property relatedTarget
13796 */
13797 /**
13798 * The type of pointer that make the track gesture.
13799 * @type String
13800 * @property pointerType
13801 */
13802 /**
13803 *
13804 * This event fires for all pointer movement being tracked.
13805 *
13806 * @class track
13807 * @extends trackstart
13808 */
13809 /**
13810 * This event fires when the pointer is no longer being tracked.
13811 *
13812 * @class trackend
13813 * @extends trackstart
13814 */
13815
13816 (function(scope) {
13817 var dispatcher = scope.dispatcher;
13818 var pointermap = new scope.PointerMap();
13819 var track = {
13820 events: [
13821 'pointerdown',
13822 'pointermove',
13823 'pointerup',
13824 'pointercancel'
13825 ],
13826 WIGGLE_THRESHOLD: 4,
13827 clampDir: function(inDelta) {
13828 return inDelta > 0 ? 1 : -1;
13829 },
13830 calcPositionDelta: function(inA, inB) {
13831 var x = 0, y = 0;
13832 if (inA && inB) {
13833 x = inB.pageX - inA.pageX;
13834 y = inB.pageY - inA.pageY;
13835 }
13836 return {x: x, y: y};
13837 },
13838 fireTrack: function(inType, inEvent, inTrackingData) {
13839 var t = inTrackingData;
13840 var d = this.calcPositionDelta(t.downEvent, inEvent);
13841 var dd = this.calcPositionDelta(t.lastMoveEvent, inEvent);
13842 if (dd.x) {
13843 t.xDirection = this.clampDir(dd.x);
13844 }
13845 if (dd.y) {
13846 t.yDirection = this.clampDir(dd.y);
13847 }
13848 var trackData = {
13849 dx: d.x,
13850 dy: d.y,
13851 ddx: dd.x,
13852 ddy: dd.y,
13853 clientX: inEvent.clientX,
13854 clientY: inEvent.clientY,
13855 pageX: inEvent.pageX,
13856 pageY: inEvent.pageY,
13857 screenX: inEvent.screenX,
13858 screenY: inEvent.screenY,
13859 xDirection: t.xDirection,
13860 yDirection: t.yDirection,
13861 trackInfo: t.trackInfo,
13862 relatedTarget: inEvent.target,
13863 pointerType: inEvent.pointerType
13864 };
13865 var e = dispatcher.makeEvent(inType, trackData);
13866 t.lastMoveEvent = inEvent;
13867 dispatcher.dispatchEvent(e, t.downTarget);
13868 },
13869 pointerdown: function(inEvent) {
13870 if (inEvent.isPrimary && (inEvent.pointerType === 'mouse' ? inEvent.butto ns === 1 : true)) {
13871 var p = {
13872 downEvent: inEvent,
13873 downTarget: inEvent.target,
13874 trackInfo: {},
13875 lastMoveEvent: null,
13876 xDirection: 0,
13877 yDirection: 0,
13878 tracking: false
13879 };
13880 pointermap.set(inEvent.pointerId, p);
13881 }
13882 },
13883 pointermove: function(inEvent) {
13884 var p = pointermap.get(inEvent.pointerId);
13885 if (p) {
13886 if (!p.tracking) {
13887 var d = this.calcPositionDelta(p.downEvent, inEvent);
13888 var move = d.x * d.x + d.y * d.y;
13889 // start tracking only if finger moves more than WIGGLE_THRESHOLD
13890 if (move > this.WIGGLE_THRESHOLD) {
13891 p.tracking = true;
13892 this.fireTrack('trackstart', p.downEvent, p);
13893 this.fireTrack('track', inEvent, p);
13894 }
13895 } else {
13896 this.fireTrack('track', inEvent, p);
13897 }
13898 }
13899 },
13900 pointerup: function(inEvent) {
13901 var p = pointermap.get(inEvent.pointerId);
13902 if (p) {
13903 if (p.tracking) {
13904 this.fireTrack('trackend', inEvent, p);
13905 }
13906 pointermap.delete(inEvent.pointerId);
13907 }
13908 },
13909 pointercancel: function(inEvent) {
13910 this.pointerup(inEvent);
13911 }
13912 };
13913 dispatcher.registerRecognizer('track', track);
13914 })(window.PointerGestures);
13915
13916 /*
13917 * Copyright 2013 The Polymer Authors. All rights reserved.
13918 * Use of this source code is governed by a BSD-style
13919 * license that can be found in the LICENSE file.
13920 */
13921
13922 /**
13923 * This event denotes a rapid down/move/up sequence from a pointer.
13924 *
13925 * The event is sent to the first element the pointer went down on.
13926 *
13927 * @module PointerGestures
13928 * @submodule Events
13929 * @class flick
13930 */
13931 /**
13932 * Signed velocity of the flick in the x direction.
13933 * @property xVelocity
13934 * @type Number
13935 */
13936 /**
13937 * Signed velocity of the flick in the y direction.
13938 * @type Number
13939 * @property yVelocity
13940 */
13941 /**
13942 * Unsigned total velocity of the flick.
13943 * @type Number
13944 * @property velocity
13945 */
13946 /**
13947 * Angle of the flick in degrees, with 0 along the
13948 * positive x axis.
13949 * @type Number
13950 * @property angle
13951 */
13952 /**
13953 * Axis with the greatest absolute velocity. Denoted
13954 * with 'x' or 'y'.
13955 * @type String
13956 * @property majorAxis
13957 */
13958 /**
13959 * Type of the pointer that made the flick.
13960 * @type String
13961 * @property pointerType
13962 */
13963
13964 (function(scope) {
13965 var dispatcher = scope.dispatcher;
13966 var flick = {
13967 // TODO(dfreedman): value should be low enough for low speed flicks, but
13968 // high enough to remove accidental flicks
13969 MIN_VELOCITY: 0.5 /* px/ms */,
13970 MAX_QUEUE: 4,
13971 moveQueue: [],
13972 target: null,
13973 pointerId: null,
13974 events: [
13975 'pointerdown',
13976 'pointermove',
13977 'pointerup',
13978 'pointercancel'
13979 ],
13980 pointerdown: function(inEvent) {
13981 if (inEvent.isPrimary && !this.pointerId) {
13982 this.pointerId = inEvent.pointerId;
13983 this.target = inEvent.target;
13984 this.addMove(inEvent);
13985 }
13986 },
13987 pointermove: function(inEvent) {
13988 if (inEvent.pointerId === this.pointerId) {
13989 this.addMove(inEvent);
13990 }
13991 },
13992 pointerup: function(inEvent) {
13993 if (inEvent.pointerId === this.pointerId) {
13994 this.fireFlick(inEvent);
13995 }
13996 this.cleanup();
13997 },
13998 pointercancel: function(inEvent) {
13999 this.cleanup();
14000 },
14001 cleanup: function() {
14002 this.moveQueue = [];
14003 this.target = null;
14004 this.pointerId = null;
14005 },
14006 addMove: function(inEvent) {
14007 if (this.moveQueue.length >= this.MAX_QUEUE) {
14008 this.moveQueue.shift();
14009 }
14010 this.moveQueue.push(inEvent);
14011 },
14012 fireFlick: function(inEvent) {
14013 var e = inEvent;
14014 var l = this.moveQueue.length;
14015 var dt, dx, dy, tx, ty, tv, x = 0, y = 0, v = 0;
14016 // flick based off the fastest segment of movement
14017 for (var i = 0, m; i < l && (m = this.moveQueue[i]); i++) {
14018 dt = e.timeStamp - m.timeStamp;
14019 dx = e.clientX - m.clientX, dy = e.clientY - m.clientY;
14020 tx = dx / dt, ty = dy / dt, tv = Math.sqrt(tx * tx + ty * ty);
14021 if (tv > v) {
14022 x = tx, y = ty, v = tv;
14023 }
14024 }
14025 var ma = Math.abs(x) > Math.abs(y) ? 'x' : 'y';
14026 var a = this.calcAngle(x, y);
14027 if (Math.abs(v) >= this.MIN_VELOCITY) {
14028 var ev = dispatcher.makeEvent('flick', {
14029 xVelocity: x,
14030 yVelocity: y,
14031 velocity: v,
14032 angle: a,
14033 majorAxis: ma,
14034 pointerType: inEvent.pointerType
14035 });
14036 dispatcher.dispatchEvent(ev, this.target);
14037 }
14038 },
14039 calcAngle: function(inX, inY) {
14040 return (Math.atan2(inY, inX) * 180 / Math.PI);
14041 }
14042 };
14043 dispatcher.registerRecognizer('flick', flick);
14044 })(window.PointerGestures);
14045
14046 /*
14047 * Copyright 2013 The Polymer Authors. All rights reserved.
14048 * Use of this source code is governed by a BSD-style
14049 * license that can be found in the LICENSE file.
14050 */
14051
14052 /*
14053 * Basic strategy: find the farthest apart points, use as diameter of circle
14054 * react to size change and rotation of the chord
14055 */
14056
14057 /**
14058 * @module PointerGestures
14059 * @submodule Events
14060 * @class pinch
14061 */
14062 /**
14063 * Scale of the pinch zoom gesture
14064 * @property scale
14065 * @type Number
14066 */
14067 /**
14068 * Center X position of pointers causing pinch
14069 * @property centerX
14070 * @type Number
14071 */
14072 /**
14073 * Center Y position of pointers causing pinch
14074 * @property centerY
14075 * @type Number
14076 */
14077
14078 /**
14079 * @module PointerGestures
14080 * @submodule Events
14081 * @class rotate
14082 */
14083 /**
14084 * Angle (in degrees) of rotation. Measured from starting positions of pointers.
14085 * @property angle
14086 * @type Number
14087 */
14088 /**
14089 * Center X position of pointers causing rotation
14090 * @property centerX
14091 * @type Number
14092 */
14093 /**
14094 * Center Y position of pointers causing rotation
14095 * @property centerY
14096 * @type Number
14097 */
14098 (function(scope) {
14099 var dispatcher = scope.dispatcher;
14100 var pointermap = new scope.PointerMap();
14101 var RAD_TO_DEG = 180 / Math.PI;
14102 var pinch = {
14103 events: [
14104 'pointerdown',
14105 'pointermove',
14106 'pointerup',
14107 'pointercancel'
14108 ],
14109 reference: {},
14110 pointerdown: function(ev) {
14111 pointermap.set(ev.pointerId, ev);
14112 if (pointermap.pointers() == 2) {
14113 var points = this.calcChord();
14114 var angle = this.calcAngle(points);
14115 this.reference = {
14116 angle: angle,
14117 diameter: points.diameter,
14118 target: scope.findLCA(points.a.target, points.b.target)
14119 };
14120 }
14121 },
14122 pointerup: function(ev) {
14123 pointermap.delete(ev.pointerId);
14124 },
14125 pointermove: function(ev) {
14126 if (pointermap.has(ev.pointerId)) {
14127 pointermap.set(ev.pointerId, ev);
14128 if (pointermap.pointers() > 1) {
14129 this.calcPinchRotate();
14130 }
14131 }
14132 },
14133 pointercancel: function(ev) {
14134 this.pointerup(ev);
14135 },
14136 dispatchPinch: function(diameter, points) {
14137 var zoom = diameter / this.reference.diameter;
14138 var ev = dispatcher.makeEvent('pinch', {
14139 scale: zoom,
14140 centerX: points.center.x,
14141 centerY: points.center.y
14142 });
14143 dispatcher.dispatchEvent(ev, this.reference.target);
14144 },
14145 dispatchRotate: function(angle, points) {
14146 var diff = Math.round((angle - this.reference.angle) % 360);
14147 var ev = dispatcher.makeEvent('rotate', {
14148 angle: diff,
14149 centerX: points.center.x,
14150 centerY: points.center.y
14151 });
14152 dispatcher.dispatchEvent(ev, this.reference.target);
14153 },
14154 calcPinchRotate: function() {
14155 var points = this.calcChord();
14156 var diameter = points.diameter;
14157 var angle = this.calcAngle(points);
14158 if (diameter != this.reference.diameter) {
14159 this.dispatchPinch(diameter, points);
14160 }
14161 if (angle != this.reference.angle) {
14162 this.dispatchRotate(angle, points);
14163 }
14164 },
14165 calcChord: function() {
14166 var pointers = [];
14167 pointermap.forEach(function(p) {
14168 pointers.push(p);
14169 });
14170 var dist = 0;
14171 // start with at least two pointers
14172 var points = {a: pointers[0], b: pointers[1]};
14173 var x, y, d;
14174 for (var i = 0; i < pointers.length; i++) {
14175 var a = pointers[i];
14176 for (var j = i + 1; j < pointers.length; j++) {
14177 var b = pointers[j];
14178 x = Math.abs(a.clientX - b.clientX);
14179 y = Math.abs(a.clientY - b.clientY);
14180 d = x + y;
14181 if (d > dist) {
14182 dist = d;
14183 points = {a: a, b: b};
14184 }
14185 }
14186 }
14187 x = Math.abs(points.a.clientX + points.b.clientX) / 2;
14188 y = Math.abs(points.a.clientY + points.b.clientY) / 2;
14189 points.center = { x: x, y: y };
14190 points.diameter = dist;
14191 return points;
14192 },
14193 calcAngle: function(points) {
14194 var x = points.a.clientX - points.b.clientX;
14195 var y = points.a.clientY - points.b.clientY;
14196 return (360 + Math.atan2(y, x) * RAD_TO_DEG) % 360;
14197 },
14198 };
14199 dispatcher.registerRecognizer('pinch', pinch);
14200 })(window.PointerGestures);
14201
14202 /*
14203 * Copyright 2013 The Polymer Authors. All rights reserved.
14204 * Use of this source code is governed by a BSD-style
14205 * license that can be found in the LICENSE file.
14206 */
14207
14208 /**
14209 * This event is fired when a pointer quickly goes down and up, and is used to
14210 * denote activation.
14211 *
14212 * Any gesture event can prevent the tap event from being created by calling
14213 * `event.preventTap`.
14214 *
14215 * Any pointer event can prevent the tap by setting the `tapPrevented` property
14216 * on itself.
14217 *
14218 * @module PointerGestures
14219 * @submodule Events
14220 * @class tap
14221 */
14222 /**
14223 * X axis position of the tap.
14224 * @property x
14225 * @type Number
14226 */
14227 /**
14228 * Y axis position of the tap.
14229 * @property y
14230 * @type Number
14231 */
14232 /**
14233 * Type of the pointer that made the tap.
14234 * @property pointerType
14235 * @type String
14236 */
14237 (function(scope) {
14238 var dispatcher = scope.dispatcher;
14239 var pointermap = new scope.PointerMap();
14240 var tap = {
14241 events: [
14242 'pointerdown',
14243 'pointermove',
14244 'pointerup',
14245 'pointercancel',
14246 'keyup'
14247 ],
14248 pointerdown: function(inEvent) {
14249 if (inEvent.isPrimary && !inEvent.tapPrevented) {
14250 pointermap.set(inEvent.pointerId, {
14251 target: inEvent.target,
14252 buttons: inEvent.buttons,
14253 x: inEvent.clientX,
14254 y: inEvent.clientY
14255 });
14256 }
14257 },
14258 pointermove: function(inEvent) {
14259 if (inEvent.isPrimary) {
14260 var start = pointermap.get(inEvent.pointerId);
14261 if (start) {
14262 if (inEvent.tapPrevented) {
14263 pointermap.delete(inEvent.pointerId);
14264 }
14265 }
14266 }
14267 },
14268 shouldTap: function(e, downState) {
14269 if (!e.tapPrevented) {
14270 if (e.pointerType === 'mouse') {
14271 // only allow left click to tap for mouse
14272 return downState.buttons === 1;
14273 } else {
14274 return true;
14275 }
14276 }
14277 },
14278 pointerup: function(inEvent) {
14279 var start = pointermap.get(inEvent.pointerId);
14280 if (start && this.shouldTap(inEvent, start)) {
14281 var t = scope.findLCA(start.target, inEvent.target);
14282 if (t) {
14283 var e = dispatcher.makeEvent('tap', {
14284 x: inEvent.clientX,
14285 y: inEvent.clientY,
14286 detail: inEvent.detail,
14287 pointerType: inEvent.pointerType
14288 });
14289 dispatcher.dispatchEvent(e, t);
14290 }
14291 }
14292 pointermap.delete(inEvent.pointerId);
14293 },
14294 pointercancel: function(inEvent) {
14295 pointermap.delete(inEvent.pointerId);
14296 },
14297 keyup: function(inEvent) {
14298 var code = inEvent.keyCode;
14299 // 32 == spacebar
14300 if (code === 32) {
14301 var t = inEvent.target;
14302 if (!(t instanceof HTMLInputElement || t instanceof HTMLTextAreaElement) ) {
14303 dispatcher.dispatchEvent(dispatcher.makeEvent('tap', {
14304 x: 0,
14305 y: 0,
14306 detail: 0,
14307 pointerType: 'unavailable'
14308 }), t);
14309 }
14310 }
14311 },
14312 preventTap: function(inPointerId) {
14313 pointermap.delete(inPointerId);
14314 }
14315 };
14316 dispatcher.registerRecognizer('tap', tap);
14317 })(window.PointerGestures);
14318
14319 /*
14320 * Copyright 2014 The Polymer Authors. All rights reserved.
14321 * Use of this source code is governed by a BSD-style
14322 * license that can be found in the LICENSE file.
14323 */
14324
14325 /**
14326 * Because recognizers are loaded after dispatcher, we have to wait to register
14327 * scopes until after all the recognizers.
14328 */
14329 (function(scope) {
14330 var dispatcher = scope.dispatcher;
14331 function registerScopes() {
14332 dispatcher.immediateRegister = true;
14333 var rq = dispatcher.registerQueue;
14334 rq.forEach(scope.register);
14335 rq.length = 0;
14336 }
14337 if (document.readyState === 'complete') {
14338 registerScopes();
14339 } else {
14340 // register scopes after a steadystate is reached
14341 // less MutationObserver churn
14342 document.addEventListener('readystatechange', function() {
14343 if (document.readyState === 'complete') {
14344 registerScopes();
14345 }
14346 });
14347 }
14348 })(window.PointerGestures);
14349
14350 // Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
14351 // This code may only be used under the BSD style license found at http://polyme r.github.io/LICENSE.txt
14352 // The complete set of authors may be found at http://polymer.github.io/AUTHORS. txt
14353 // The complete set of contributors may be found at http://polymer.github.io/CON TRIBUTORS.txt
14354 // Code distributed by Google as part of the polymer project is also
14355 // subject to an additional IP rights grant found at http://polymer.github.io/PA TENTS.txt
14356
14357 (function(global) {
14358 'use strict';
14359
14360 var filter = Array.prototype.filter.call.bind(Array.prototype.filter);
14361
14362 function getTreeScope(node) {
14363 while (node.parentNode) {
14364 node = node.parentNode;
14365 }
14366
14367 return typeof node.getElementById === 'function' ? node : null;
14368 }
14369
14370 Node.prototype.bind = function(name, observable) {
14371 console.error('Unhandled binding to Node: ', this, name, observable);
14372 };
14373
14374 function updateBindings(node, name, binding) {
14375 var bindings = node.bindings_;
14376 if (!bindings)
14377 bindings = node.bindings_ = {};
14378
14379 if (bindings[name])
14380 binding[name].close();
14381
14382 return bindings[name] = binding;
14383 }
14384
14385 function returnBinding(node, name, binding) {
14386 return binding;
14387 }
14388
14389 function sanitizeValue(value) {
14390 return value == null ? '' : value;
14391 }
14392
14393 function updateText(node, value) {
14394 node.data = sanitizeValue(value);
14395 }
14396
14397 function textBinding(node) {
14398 return function(value) {
14399 return updateText(node, value);
14400 };
14401 }
14402
14403 var maybeUpdateBindings = returnBinding;
14404
14405 Object.defineProperty(Platform, 'enableBindingsReflection', {
14406 get: function() {
14407 return maybeUpdateBindings === updateBindings;
14408 },
14409 set: function(enable) {
14410 maybeUpdateBindings = enable ? updateBindings : returnBinding;
14411 return enable;
14412 },
14413 configurable: true
14414 });
14415
14416 Text.prototype.bind = function(name, value, oneTime) {
14417 if (name !== 'textContent')
14418 return Node.prototype.bind.call(this, name, value, oneTime);
14419
14420 if (oneTime)
14421 return updateText(this, value);
14422
14423 var observable = value;
14424 updateText(this, observable.open(textBinding(this)));
14425 return maybeUpdateBindings(this, name, observable);
14426 }
14427
14428 function updateAttribute(el, name, conditional, value) {
14429 if (conditional) {
14430 if (value)
14431 el.setAttribute(name, '');
14432 else
14433 el.removeAttribute(name);
14434 return;
14435 }
14436
14437 el.setAttribute(name, sanitizeValue(value));
14438 }
14439
14440 function attributeBinding(el, name, conditional) {
14441 return function(value) {
14442 updateAttribute(el, name, conditional, value);
14443 };
14444 }
14445
14446 Element.prototype.bind = function(name, value, oneTime) {
14447 var conditional = name[name.length - 1] == '?';
14448 if (conditional) {
14449 this.removeAttribute(name);
14450 name = name.slice(0, -1);
14451 }
14452
14453 if (oneTime)
14454 return updateAttribute(this, name, conditional, value);
14455
14456
14457 var observable = value;
14458 updateAttribute(this, name, conditional,
14459 observable.open(attributeBinding(this, name, conditional)));
14460
14461 return maybeUpdateBindings(this, name, observable);
14462 };
14463
14464 var checkboxEventType;
14465 (function() {
14466 // Attempt to feature-detect which event (change or click) is fired first
14467 // for checkboxes.
14468 var div = document.createElement('div');
14469 var checkbox = div.appendChild(document.createElement('input'));
14470 checkbox.setAttribute('type', 'checkbox');
14471 var first;
14472 var count = 0;
14473 checkbox.addEventListener('click', function(e) {
14474 count++;
14475 first = first || 'click';
14476 });
14477 checkbox.addEventListener('change', function() {
14478 count++;
14479 first = first || 'change';
14480 });
14481
14482 var event = document.createEvent('MouseEvent');
14483 event.initMouseEvent("click", true, true, window, 0, 0, 0, 0, 0, false,
14484 false, false, false, 0, null);
14485 checkbox.dispatchEvent(event);
14486 // WebKit/Blink don't fire the change event if the element is outside the
14487 // document, so assume 'change' for that case.
14488 checkboxEventType = count == 1 ? 'change' : first;
14489 })();
14490
14491 function getEventForInputType(element) {
14492 switch (element.type) {
14493 case 'checkbox':
14494 return checkboxEventType;
14495 case 'radio':
14496 case 'select-multiple':
14497 case 'select-one':
14498 return 'change';
14499 case 'range':
14500 if (/Trident|MSIE/.test(navigator.userAgent))
14501 return 'change';
14502 default:
14503 return 'input';
14504 }
14505 }
14506
14507 function updateInput(input, property, value, santizeFn) {
14508 input[property] = (santizeFn || sanitizeValue)(value);
14509 }
14510
14511 function inputBinding(input, property, santizeFn) {
14512 return function(value) {
14513 return updateInput(input, property, value, santizeFn);
14514 }
14515 }
14516
14517 function noop() {}
14518
14519 function bindInputEvent(input, property, observable, postEventFn) {
14520 var eventType = getEventForInputType(input);
14521
14522 function eventHandler() {
14523 observable.setValue(input[property]);
14524 observable.discardChanges();
14525 (postEventFn || noop)(input);
14526 Platform.performMicrotaskCheckpoint();
14527 }
14528 input.addEventListener(eventType, eventHandler);
14529
14530 return {
14531 close: function() {
14532 input.removeEventListener(eventType, eventHandler);
14533 observable.close();
14534 },
14535
14536 observable_: observable
14537 }
14538 }
14539
14540 function booleanSanitize(value) {
14541 return Boolean(value);
14542 }
14543
14544 // |element| is assumed to be an HTMLInputElement with |type| == 'radio'.
14545 // Returns an array containing all radio buttons other than |element| that
14546 // have the same |name|, either in the form that |element| belongs to or,
14547 // if no form, in the document tree to which |element| belongs.
14548 //
14549 // This implementation is based upon the HTML spec definition of a
14550 // "radio button group":
14551 // http://www.whatwg.org/specs/web-apps/current-work/multipage/number-state. html#radio-button-group
14552 //
14553 function getAssociatedRadioButtons(element) {
14554 if (element.form) {
14555 return filter(element.form.elements, function(el) {
14556 return el != element &&
14557 el.tagName == 'INPUT' &&
14558 el.type == 'radio' &&
14559 el.name == element.name;
14560 });
14561 } else {
14562 var treeScope = getTreeScope(element);
14563 if (!treeScope)
14564 return [];
14565 var radios = treeScope.querySelectorAll(
14566 'input[type="radio"][name="' + element.name + '"]');
14567 return filter(radios, function(el) {
14568 return el != element && !el.form;
14569 });
14570 }
14571 }
14572
14573 function checkedPostEvent(input) {
14574 // Only the radio button that is getting checked gets an event. We
14575 // therefore find all the associated radio buttons and update their
14576 // check binding manually.
14577 if (input.tagName === 'INPUT' &&
14578 input.type === 'radio') {
14579 getAssociatedRadioButtons(input).forEach(function(radio) {
14580 var checkedBinding = radio.bindings_.checked;
14581 if (checkedBinding) {
14582 // Set the value directly to avoid an infinite call stack.
14583 checkedBinding.observable_.setValue(false);
14584 }
14585 });
14586 }
14587 }
14588
14589 HTMLInputElement.prototype.bind = function(name, value, oneTime) {
14590 if (name !== 'value' && name !== 'checked')
14591 return HTMLElement.prototype.bind.call(this, name, value, oneTime);
14592
14593 this.removeAttribute(name);
14594 var sanitizeFn = name == 'checked' ? booleanSanitize : sanitizeValue;
14595 var postEventFn = name == 'checked' ? checkedPostEvent : noop;
14596
14597 if (oneTime)
14598 return updateInput(this, name, value, sanitizeFn);
14599
14600
14601 var observable = value;
14602 var binding = bindInputEvent(this, name, observable, postEventFn);
14603 updateInput(this, name,
14604 observable.open(inputBinding(this, name, sanitizeFn)),
14605 sanitizeFn);
14606
14607 // Checkboxes may need to update bindings of other checkboxes.
14608 return updateBindings(this, name, binding);
14609 }
14610
14611 HTMLTextAreaElement.prototype.bind = function(name, value, oneTime) {
14612 if (name !== 'value')
14613 return HTMLElement.prototype.bind.call(this, name, value, oneTime);
14614
14615 this.removeAttribute('value');
14616
14617 if (oneTime)
14618 return updateInput(this, 'value', value);
14619
14620 var observable = value;
14621 var binding = bindInputEvent(this, 'value', observable);
14622 updateInput(this, 'value',
14623 observable.open(inputBinding(this, 'value', sanitizeValue)));
14624 return maybeUpdateBindings(this, name, binding);
14625 }
14626
14627 function updateOption(option, value) {
14628 var parentNode = option.parentNode;;
14629 var select;
14630 var selectBinding;
14631 var oldValue;
14632 if (parentNode instanceof HTMLSelectElement &&
14633 parentNode.bindings_ &&
14634 parentNode.bindings_.value) {
14635 select = parentNode;
14636 selectBinding = select.bindings_.value;
14637 oldValue = select.value;
14638 }
14639
14640 option.value = sanitizeValue(value);
14641
14642 if (select && select.value != oldValue) {
14643 selectBinding.observable_.setValue(select.value);
14644 selectBinding.observable_.discardChanges();
14645 Platform.performMicrotaskCheckpoint();
14646 }
14647 }
14648
14649 function optionBinding(option) {
14650 return function(value) {
14651 updateOption(option, value);
14652 }
14653 }
14654
14655 HTMLOptionElement.prototype.bind = function(name, value, oneTime) {
14656 if (name !== 'value')
14657 return HTMLElement.prototype.bind.call(this, name, value, oneTime);
14658
14659 this.removeAttribute('value');
14660
14661 if (oneTime)
14662 return updateOption(this, value);
14663
14664 var observable = value;
14665 var binding = bindInputEvent(this, 'value', observable);
14666 updateOption(this, observable.open(optionBinding(this)));
14667 return maybeUpdateBindings(this, name, binding);
14668 }
14669
14670 HTMLSelectElement.prototype.bind = function(name, value, oneTime) {
14671 if (name === 'selectedindex')
14672 name = 'selectedIndex';
14673
14674 if (name !== 'selectedIndex' && name !== 'value')
14675 return HTMLElement.prototype.bind.call(this, name, value, oneTime);
14676
14677 this.removeAttribute(name);
14678
14679 if (oneTime)
14680 return updateInput(this, name, value);
14681
14682 var observable = value;
14683 var binding = bindInputEvent(this, name, observable);
14684 updateInput(this, name,
14685 observable.open(inputBinding(this, name)));
14686
14687 // Option update events may need to access select bindings.
14688 return updateBindings(this, name, binding);
14689 }
14690 })(this);
14691
14692 // Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
14693 // This code may only be used under the BSD style license found at http://polyme r.github.io/LICENSE.txt
14694 // The complete set of authors may be found at http://polymer.github.io/AUTHORS. txt
14695 // The complete set of contributors may be found at http://polymer.github.io/CON TRIBUTORS.txt
14696 // Code distributed by Google as part of the polymer project is also
14697 // subject to an additional IP rights grant found at http://polymer.github.io/PA TENTS.txt
14698
14699 (function(global) {
14700 'use strict';
14701
14702 function assert(v) {
14703 if (!v)
14704 throw new Error('Assertion failed');
14705 }
14706
14707 var forEach = Array.prototype.forEach.call.bind(Array.prototype.forEach);
14708
14709 function getFragmentRoot(node) {
14710 var p;
14711 while (p = node.parentNode) {
14712 node = p;
14713 }
14714
14715 return node;
14716 }
14717
14718 function searchRefId(node, id) {
14719 if (!id)
14720 return;
14721
14722 var ref;
14723 var selector = '#' + id;
14724 while (!ref) {
14725 node = getFragmentRoot(node);
14726
14727 if (node.protoContent_)
14728 ref = node.protoContent_.querySelector(selector);
14729 else if (node.getElementById)
14730 ref = node.getElementById(id);
14731
14732 if (ref || !node.templateCreator_)
14733 break
14734
14735 node = node.templateCreator_;
14736 }
14737
14738 return ref;
14739 }
14740
14741 function getInstanceRoot(node) {
14742 while (node.parentNode) {
14743 node = node.parentNode;
14744 }
14745 return node.templateCreator_ ? node : null;
14746 }
14747
14748 var Map;
14749 if (global.Map && typeof global.Map.prototype.forEach === 'function') {
14750 Map = global.Map;
14751 } else {
14752 Map = function() {
14753 this.keys = [];
14754 this.values = [];
14755 };
14756
14757 Map.prototype = {
14758 set: function(key, value) {
14759 var index = this.keys.indexOf(key);
14760 if (index < 0) {
14761 this.keys.push(key);
14762 this.values.push(value);
14763 } else {
14764 this.values[index] = value;
14765 }
14766 },
14767
14768 get: function(key) {
14769 var index = this.keys.indexOf(key);
14770 if (index < 0)
14771 return;
14772
14773 return this.values[index];
14774 },
14775
14776 delete: function(key, value) {
14777 var index = this.keys.indexOf(key);
14778 if (index < 0)
14779 return false;
14780
14781 this.keys.splice(index, 1);
14782 this.values.splice(index, 1);
14783 return true;
14784 },
14785
14786 forEach: function(f, opt_this) {
14787 for (var i = 0; i < this.keys.length; i++)
14788 f.call(opt_this || this, this.values[i], this.keys[i], this);
14789 }
14790 };
14791 }
14792
14793 // JScript does not have __proto__. We wrap all object literals with
14794 // createObject which uses Object.create, Object.defineProperty and
14795 // Object.getOwnPropertyDescriptor to create a new object that does the exact
14796 // same thing. The main downside to this solution is that we have to extract
14797 // all those property descriptors for IE.
14798 var createObject = ('__proto__' in {}) ?
14799 function(obj) { return obj; } :
14800 function(obj) {
14801 var proto = obj.__proto__;
14802 if (!proto)
14803 return obj;
14804 var newObject = Object.create(proto);
14805 Object.getOwnPropertyNames(obj).forEach(function(name) {
14806 Object.defineProperty(newObject, name,
14807 Object.getOwnPropertyDescriptor(obj, name));
14808 });
14809 return newObject;
14810 };
14811
14812 // IE does not support have Document.prototype.contains.
14813 if (typeof document.contains != 'function') {
14814 Document.prototype.contains = function(node) {
14815 if (node === this || node.parentNode === this)
14816 return true;
14817 return this.documentElement.contains(node);
14818 }
14819 }
14820
14821 var BIND = 'bind';
14822 var REPEAT = 'repeat';
14823 var IF = 'if';
14824
14825 var templateAttributeDirectives = {
14826 'template': true,
14827 'repeat': true,
14828 'bind': true,
14829 'ref': true
14830 };
14831
14832 var semanticTemplateElements = {
14833 'THEAD': true,
14834 'TBODY': true,
14835 'TFOOT': true,
14836 'TH': true,
14837 'TR': true,
14838 'TD': true,
14839 'COLGROUP': true,
14840 'COL': true,
14841 'CAPTION': true,
14842 'OPTION': true,
14843 'OPTGROUP': true
14844 };
14845
14846 var hasTemplateElement = typeof HTMLTemplateElement !== 'undefined';
14847 if (hasTemplateElement) {
14848 // TODO(rafaelw): Remove when fix for
14849 // https://codereview.chromium.org/164803002/
14850 // makes it to Chrome release.
14851 (function() {
14852 var t = document.createElement('template');
14853 var d = t.content.ownerDocument;
14854 var html = d.appendChild(d.createElement('html'));
14855 var head = html.appendChild(d.createElement('head'));
14856 var base = d.createElement('base');
14857 base.href = document.baseURI;
14858 head.appendChild(base);
14859 })();
14860 }
14861
14862 var allTemplatesSelectors = 'template, ' +
14863 Object.keys(semanticTemplateElements).map(function(tagName) {
14864 return tagName.toLowerCase() + '[template]';
14865 }).join(', ');
14866
14867 function isSVGTemplate(el) {
14868 return el.tagName == 'template' &&
14869 el.namespaceURI == 'http://www.w3.org/2000/svg';
14870 }
14871
14872 function isHTMLTemplate(el) {
14873 return el.tagName == 'TEMPLATE' &&
14874 el.namespaceURI == 'http://www.w3.org/1999/xhtml';
14875 }
14876
14877 function isAttributeTemplate(el) {
14878 return Boolean(semanticTemplateElements[el.tagName] &&
14879 el.hasAttribute('template'));
14880 }
14881
14882 function isTemplate(el) {
14883 if (el.isTemplate_ === undefined)
14884 el.isTemplate_ = el.tagName == 'TEMPLATE' || isAttributeTemplate(el);
14885
14886 return el.isTemplate_;
14887 }
14888
14889 // FIXME: Observe templates being added/removed from documents
14890 // FIXME: Expose imperative API to decorate and observe templates in
14891 // "disconnected tress" (e.g. ShadowRoot)
14892 document.addEventListener('DOMContentLoaded', function(e) {
14893 bootstrapTemplatesRecursivelyFrom(document);
14894 // FIXME: Is this needed? Seems like it shouldn't be.
14895 Platform.performMicrotaskCheckpoint();
14896 }, false);
14897
14898 function forAllTemplatesFrom(node, fn) {
14899 var subTemplates = node.querySelectorAll(allTemplatesSelectors);
14900
14901 if (isTemplate(node))
14902 fn(node)
14903 forEach(subTemplates, fn);
14904 }
14905
14906 function bootstrapTemplatesRecursivelyFrom(node) {
14907 function bootstrap(template) {
14908 if (!HTMLTemplateElement.decorate(template))
14909 bootstrapTemplatesRecursivelyFrom(template.content);
14910 }
14911
14912 forAllTemplatesFrom(node, bootstrap);
14913 }
14914
14915 if (!hasTemplateElement) {
14916 /**
14917 * This represents a <template> element.
14918 * @constructor
14919 * @extends {HTMLElement}
14920 */
14921 global.HTMLTemplateElement = function() {
14922 throw TypeError('Illegal constructor');
14923 };
14924 }
14925
14926 var hasProto = '__proto__' in {};
14927
14928 function mixin(to, from) {
14929 Object.getOwnPropertyNames(from).forEach(function(name) {
14930 Object.defineProperty(to, name,
14931 Object.getOwnPropertyDescriptor(from, name));
14932 });
14933 }
14934
14935 // http://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/templates/index.html# dfn-template-contents-owner
14936 function getOrCreateTemplateContentsOwner(template) {
14937 var doc = template.ownerDocument
14938 if (!doc.defaultView)
14939 return doc;
14940 var d = doc.templateContentsOwner_;
14941 if (!d) {
14942 // TODO(arv): This should either be a Document or HTMLDocument depending
14943 // on doc.
14944 d = doc.implementation.createHTMLDocument('');
14945 while (d.lastChild) {
14946 d.removeChild(d.lastChild);
14947 }
14948 doc.templateContentsOwner_ = d;
14949 }
14950 return d;
14951 }
14952
14953 function getTemplateStagingDocument(template) {
14954 if (!template.stagingDocument_) {
14955 var owner = template.ownerDocument;
14956 if (!owner.stagingDocument_) {
14957 owner.stagingDocument_ = owner.implementation.createHTMLDocument('');
14958
14959 // TODO(rafaelw): Remove when fix for
14960 // https://codereview.chromium.org/164803002/
14961 // makes it to Chrome release.
14962 var base = owner.stagingDocument_.createElement('base');
14963 base.href = document.baseURI;
14964 owner.stagingDocument_.head.appendChild(base);
14965
14966 owner.stagingDocument_.stagingDocument_ = owner.stagingDocument_;
14967 }
14968
14969 template.stagingDocument_ = owner.stagingDocument_;
14970 }
14971
14972 return template.stagingDocument_;
14973 }
14974
14975 // For non-template browsers, the parser will disallow <template> in certain
14976 // locations, so we allow "attribute templates" which combine the template
14977 // element with the top-level container node of the content, e.g.
14978 //
14979 // <tr template repeat="{{ foo }}"" class="bar"><td>Bar</td></tr>
14980 //
14981 // becomes
14982 //
14983 // <template repeat="{{ foo }}">
14984 // + #document-fragment
14985 // + <tr class="bar">
14986 // + <td>Bar</td>
14987 //
14988 function extractTemplateFromAttributeTemplate(el) {
14989 var template = el.ownerDocument.createElement('template');
14990 el.parentNode.insertBefore(template, el);
14991
14992 var attribs = el.attributes;
14993 var count = attribs.length;
14994 while (count-- > 0) {
14995 var attrib = attribs[count];
14996 if (templateAttributeDirectives[attrib.name]) {
14997 if (attrib.name !== 'template')
14998 template.setAttribute(attrib.name, attrib.value);
14999 el.removeAttribute(attrib.name);
15000 }
15001 }
15002
15003 return template;
15004 }
15005
15006 function extractTemplateFromSVGTemplate(el) {
15007 var template = el.ownerDocument.createElement('template');
15008 el.parentNode.insertBefore(template, el);
15009
15010 var attribs = el.attributes;
15011 var count = attribs.length;
15012 while (count-- > 0) {
15013 var attrib = attribs[count];
15014 template.setAttribute(attrib.name, attrib.value);
15015 el.removeAttribute(attrib.name);
15016 }
15017
15018 el.parentNode.removeChild(el);
15019 return template;
15020 }
15021
15022 function liftNonNativeTemplateChildrenIntoContent(template, el, useRoot) {
15023 var content = template.content;
15024 if (useRoot) {
15025 content.appendChild(el);
15026 return;
15027 }
15028
15029 var child;
15030 while (child = el.firstChild) {
15031 content.appendChild(child);
15032 }
15033 }
15034
15035 var templateObserver;
15036 if (typeof MutationObserver == 'function') {
15037 templateObserver = new MutationObserver(function(records) {
15038 for (var i = 0; i < records.length; i++) {
15039 records[i].target.refChanged_();
15040 }
15041 });
15042 }
15043
15044 /**
15045 * Ensures proper API and content model for template elements.
15046 * @param {HTMLTemplateElement} opt_instanceRef The template element which
15047 * |el| template element will return as the value of its ref(), and whose
15048 * content will be used as source when createInstance() is invoked.
15049 */
15050 HTMLTemplateElement.decorate = function(el, opt_instanceRef) {
15051 if (el.templateIsDecorated_)
15052 return false;
15053
15054 var templateElement = el;
15055 templateElement.templateIsDecorated_ = true;
15056
15057 var isNativeHTMLTemplate = isHTMLTemplate(templateElement) &&
15058 hasTemplateElement;
15059 var bootstrapContents = isNativeHTMLTemplate;
15060 var liftContents = !isNativeHTMLTemplate;
15061 var liftRoot = false;
15062
15063 if (!isNativeHTMLTemplate) {
15064 if (isAttributeTemplate(templateElement)) {
15065 assert(!opt_instanceRef);
15066 templateElement = extractTemplateFromAttributeTemplate(el);
15067 templateElement.templateIsDecorated_ = true;
15068 isNativeHTMLTemplate = hasTemplateElement;
15069 liftRoot = true;
15070 } else if (isSVGTemplate(templateElement)) {
15071 templateElement = extractTemplateFromSVGTemplate(el);
15072 templateElement.templateIsDecorated_ = true;
15073 isNativeHTMLTemplate = hasTemplateElement;
15074 }
15075 }
15076
15077 if (!isNativeHTMLTemplate) {
15078 fixTemplateElementPrototype(templateElement);
15079 var doc = getOrCreateTemplateContentsOwner(templateElement);
15080 templateElement.content_ = doc.createDocumentFragment();
15081 }
15082
15083 if (opt_instanceRef) {
15084 // template is contained within an instance, its direct content must be
15085 // empty
15086 templateElement.instanceRef_ = opt_instanceRef;
15087 } else if (liftContents) {
15088 liftNonNativeTemplateChildrenIntoContent(templateElement,
15089 el,
15090 liftRoot);
15091 } else if (bootstrapContents) {
15092 bootstrapTemplatesRecursivelyFrom(templateElement.content);
15093 }
15094
15095 return true;
15096 };
15097
15098 // TODO(rafaelw): This used to decorate recursively all templates from a given
15099 // node. This happens by default on 'DOMContentLoaded', but may be needed
15100 // in subtrees not descendent from document (e.g. ShadowRoot).
15101 // Review whether this is the right public API.
15102 HTMLTemplateElement.bootstrap = bootstrapTemplatesRecursivelyFrom;
15103
15104 var htmlElement = global.HTMLUnknownElement || HTMLElement;
15105
15106 var contentDescriptor = {
15107 get: function() {
15108 return this.content_;
15109 },
15110 enumerable: true,
15111 configurable: true
15112 };
15113
15114 if (!hasTemplateElement) {
15115 // Gecko is more picky with the prototype than WebKit. Make sure to use the
15116 // same prototype as created in the constructor.
15117 HTMLTemplateElement.prototype = Object.create(htmlElement.prototype);
15118
15119 Object.defineProperty(HTMLTemplateElement.prototype, 'content',
15120 contentDescriptor);
15121 }
15122
15123 function fixTemplateElementPrototype(el) {
15124 if (hasProto)
15125 el.__proto__ = HTMLTemplateElement.prototype;
15126 else
15127 mixin(el, HTMLTemplateElement.prototype);
15128 }
15129
15130 function ensureSetModelScheduled(template) {
15131 if (!template.setModelFn_) {
15132 template.setModelFn_ = function() {
15133 template.setModelFnScheduled_ = false;
15134 var map = getBindings(template,
15135 template.delegate_ && template.delegate_.prepareBinding);
15136 processBindings(template, map, template.model_);
15137 };
15138 }
15139
15140 if (!template.setModelFnScheduled_) {
15141 template.setModelFnScheduled_ = true;
15142 Observer.runEOM_(template.setModelFn_);
15143 }
15144 }
15145
15146 mixin(HTMLTemplateElement.prototype, {
15147 bind: function(name, value, oneTime) {
15148 if (name != 'ref')
15149 return Element.prototype.bind.call(this, name, value, oneTime);
15150
15151 var self = this;
15152 var ref = oneTime ? value : value.open(function(ref) {
15153 self.setAttribute('ref', ref);
15154 self.refChanged_();
15155 });
15156
15157 this.setAttribute('ref', ref);
15158 this.refChanged_();
15159 if (oneTime)
15160 return;
15161
15162 if (!this.bindings_) {
15163 this.bindings_ = { ref: value };
15164 } else {
15165 this.bindings_.ref = value;
15166 }
15167
15168 return value;
15169 },
15170
15171 processBindingDirectives_: function(directives) {
15172 if (this.iterator_)
15173 this.iterator_.closeDeps();
15174
15175 if (!directives.if && !directives.bind && !directives.repeat) {
15176 if (this.iterator_) {
15177 this.iterator_.close();
15178 this.iterator_ = undefined;
15179 }
15180
15181 return;
15182 }
15183
15184 if (!this.iterator_) {
15185 this.iterator_ = new TemplateIterator(this);
15186 }
15187
15188 this.iterator_.updateDependencies(directives, this.model_);
15189
15190 if (templateObserver) {
15191 templateObserver.observe(this, { attributes: true,
15192 attributeFilter: ['ref'] });
15193 }
15194
15195 return this.iterator_;
15196 },
15197
15198 createInstance: function(model, bindingDelegate, delegate_) {
15199 if (bindingDelegate)
15200 delegate_ = this.newDelegate_(bindingDelegate);
15201
15202 if (!this.refContent_)
15203 this.refContent_ = this.ref_.content;
15204 var content = this.refContent_;
15205 if (content.firstChild === null)
15206 return emptyInstance;
15207
15208 var map = this.bindingMap_;
15209 if (!map || map.content !== content) {
15210 // TODO(rafaelw): Setup a MutationObserver on content to detect
15211 // when the instanceMap is invalid.
15212 map = createInstanceBindingMap(content,
15213 delegate_ && delegate_.prepareBinding) || [];
15214 map.content = content;
15215 this.bindingMap_ = map;
15216 }
15217
15218 var stagingDocument = getTemplateStagingDocument(this);
15219 var instance = stagingDocument.createDocumentFragment();
15220 instance.templateCreator_ = this;
15221 instance.protoContent_ = content;
15222 instance.bindings_ = [];
15223 instance.terminator_ = null;
15224 var instanceRecord = instance.templateInstance_ = {
15225 firstNode: null,
15226 lastNode: null,
15227 model: model
15228 };
15229
15230 var i = 0;
15231 var collectTerminator = false;
15232 for (var child = content.firstChild; child; child = child.nextSibling) {
15233 // The terminator of the instance is the clone of the last child of the
15234 // content. If the last child is an active template, it may produce
15235 // instances as a result of production, so simply collecting the last
15236 // child of the instance after it has finished producing may be wrong.
15237 if (child.nextSibling === null)
15238 collectTerminator = true;
15239
15240 var clone = cloneAndBindInstance(child, instance, stagingDocument,
15241 map.children[i++],
15242 model,
15243 delegate_,
15244 instance.bindings_);
15245 clone.templateInstance_ = instanceRecord;
15246 if (collectTerminator)
15247 instance.terminator_ = clone;
15248 }
15249
15250 instanceRecord.firstNode = instance.firstChild;
15251 instanceRecord.lastNode = instance.lastChild;
15252 instance.templateCreator_ = undefined;
15253 instance.protoContent_ = undefined;
15254 return instance;
15255 },
15256
15257 get model() {
15258 return this.model_;
15259 },
15260
15261 set model(model) {
15262 this.model_ = model;
15263 ensureSetModelScheduled(this);
15264 },
15265
15266 get bindingDelegate() {
15267 return this.delegate_ && this.delegate_.raw;
15268 },
15269
15270 refChanged_: function() {
15271 if (!this.iterator_ || this.refContent_ === this.ref_.content)
15272 return;
15273
15274 this.refContent_ = undefined;
15275 this.iterator_.valueChanged();
15276 this.iterator_.updateIteratedValue();
15277 },
15278
15279 clear: function() {
15280 this.model_ = undefined;
15281 this.delegate_ = undefined;
15282 if (this.bindings_ && this.bindings_.ref)
15283 this.bindings_.ref.close()
15284 this.refContent_ = undefined;
15285 if (!this.iterator_)
15286 return;
15287 this.iterator_.valueChanged();
15288 this.iterator_.close()
15289 this.iterator_ = undefined;
15290 },
15291
15292 setDelegate_: function(delegate) {
15293 this.delegate_ = delegate;
15294 this.bindingMap_ = undefined;
15295 if (this.iterator_) {
15296 this.iterator_.instancePositionChangedFn_ = undefined;
15297 this.iterator_.instanceModelFn_ = undefined;
15298 }
15299 },
15300
15301 newDelegate_: function(bindingDelegate) {
15302 if (!bindingDelegate)
15303 return {};
15304
15305 function delegateFn(name) {
15306 var fn = bindingDelegate && bindingDelegate[name];
15307 if (typeof fn != 'function')
15308 return;
15309
15310 return function() {
15311 return fn.apply(bindingDelegate, arguments);
15312 };
15313 }
15314
15315 return {
15316 raw: bindingDelegate,
15317 prepareBinding: delegateFn('prepareBinding'),
15318 prepareInstanceModel: delegateFn('prepareInstanceModel'),
15319 prepareInstancePositionChanged:
15320 delegateFn('prepareInstancePositionChanged')
15321 };
15322 },
15323
15324 // TODO(rafaelw): Assigning .bindingDelegate always succeeds. It may
15325 // make sense to issue a warning or even throw if the template is already
15326 // "activated", since this would be a strange thing to do.
15327 set bindingDelegate(bindingDelegate) {
15328 if (this.delegate_) {
15329 throw Error('Template must be cleared before a new bindingDelegate ' +
15330 'can be assigned');
15331 }
15332
15333 this.setDelegate_(this.newDelegate_(bindingDelegate));
15334 },
15335
15336 get ref_() {
15337 var ref = searchRefId(this, this.getAttribute('ref'));
15338 if (!ref)
15339 ref = this.instanceRef_;
15340
15341 if (!ref)
15342 return this;
15343
15344 var nextRef = ref.ref_;
15345 return nextRef ? nextRef : ref;
15346 }
15347 });
15348
15349 // Returns
15350 // a) undefined if there are no mustaches.
15351 // b) [TEXT, (ONE_TIME?, PATH, DELEGATE_FN, TEXT)+] if there is at least one mustache.
15352 function parseMustaches(s, name, node, prepareBindingFn) {
15353 if (!s || !s.length)
15354 return;
15355
15356 var tokens;
15357 var length = s.length;
15358 var startIndex = 0, lastIndex = 0, endIndex = 0;
15359 var onlyOneTime = true;
15360 while (lastIndex < length) {
15361 var startIndex = s.indexOf('{{', lastIndex);
15362 var oneTimeStart = s.indexOf('[[', lastIndex);
15363 var oneTime = false;
15364 var terminator = '}}';
15365
15366 if (oneTimeStart >= 0 &&
15367 (startIndex < 0 || oneTimeStart < startIndex)) {
15368 startIndex = oneTimeStart;
15369 oneTime = true;
15370 terminator = ']]';
15371 }
15372
15373 endIndex = startIndex < 0 ? -1 : s.indexOf(terminator, startIndex + 2);
15374
15375 if (endIndex < 0) {
15376 if (!tokens)
15377 return;
15378
15379 tokens.push(s.slice(lastIndex)); // TEXT
15380 break;
15381 }
15382
15383 tokens = tokens || [];
15384 tokens.push(s.slice(lastIndex, startIndex)); // TEXT
15385 var pathString = s.slice(startIndex + 2, endIndex).trim();
15386 tokens.push(oneTime); // ONE_TIME?
15387 onlyOneTime = onlyOneTime && oneTime;
15388 var delegateFn = prepareBindingFn &&
15389 prepareBindingFn(pathString, name, node);
15390 // Don't try to parse the expression if there's a prepareBinding function
15391 if (delegateFn == null) {
15392 tokens.push(Path.get(pathString)); // PATH
15393 } else {
15394 tokens.push(null);
15395 }
15396 tokens.push(delegateFn); // DELEGATE_FN
15397 lastIndex = endIndex + 2;
15398 }
15399
15400 if (lastIndex === length)
15401 tokens.push(''); // TEXT
15402
15403 tokens.hasOnePath = tokens.length === 5;
15404 tokens.isSimplePath = tokens.hasOnePath &&
15405 tokens[0] == '' &&
15406 tokens[4] == '';
15407 tokens.onlyOneTime = onlyOneTime;
15408
15409 tokens.combinator = function(values) {
15410 var newValue = tokens[0];
15411
15412 for (var i = 1; i < tokens.length; i += 4) {
15413 var value = tokens.hasOnePath ? values : values[(i - 1) / 4];
15414 if (value !== undefined)
15415 newValue += value;
15416 newValue += tokens[i + 3];
15417 }
15418
15419 return newValue;
15420 }
15421
15422 return tokens;
15423 };
15424
15425 function processOneTimeBinding(name, tokens, node, model) {
15426 if (tokens.hasOnePath) {
15427 var delegateFn = tokens[3];
15428 var value = delegateFn ? delegateFn(model, node, true) :
15429 tokens[2].getValueFrom(model);
15430 return tokens.isSimplePath ? value : tokens.combinator(value);
15431 }
15432
15433 var values = [];
15434 for (var i = 1; i < tokens.length; i += 4) {
15435 var delegateFn = tokens[i + 2];
15436 values[(i - 1) / 4] = delegateFn ? delegateFn(model, node) :
15437 tokens[i + 1].getValueFrom(model);
15438 }
15439
15440 return tokens.combinator(values);
15441 }
15442
15443 function processSinglePathBinding(name, tokens, node, model) {
15444 var delegateFn = tokens[3];
15445 var observer = delegateFn ? delegateFn(model, node, false) :
15446 new PathObserver(model, tokens[2]);
15447
15448 return tokens.isSimplePath ? observer :
15449 new ObserverTransform(observer, tokens.combinator);
15450 }
15451
15452 function processBinding(name, tokens, node, model) {
15453 if (tokens.onlyOneTime)
15454 return processOneTimeBinding(name, tokens, node, model);
15455
15456 if (tokens.hasOnePath)
15457 return processSinglePathBinding(name, tokens, node, model);
15458
15459 var observer = new CompoundObserver();
15460
15461 for (var i = 1; i < tokens.length; i += 4) {
15462 var oneTime = tokens[i];
15463 var delegateFn = tokens[i + 2];
15464
15465 if (delegateFn) {
15466 var value = delegateFn(model, node, oneTime);
15467 if (oneTime)
15468 observer.addPath(value)
15469 else
15470 observer.addObserver(value);
15471 continue;
15472 }
15473
15474 var path = tokens[i + 1];
15475 if (oneTime)
15476 observer.addPath(path.getValueFrom(model))
15477 else
15478 observer.addPath(model, path);
15479 }
15480
15481 return new ObserverTransform(observer, tokens.combinator);
15482 }
15483
15484 function processBindings(node, bindings, model, instanceBindings) {
15485 for (var i = 0; i < bindings.length; i += 2) {
15486 var name = bindings[i]
15487 var tokens = bindings[i + 1];
15488 var value = processBinding(name, tokens, node, model);
15489 var binding = node.bind(name, value, tokens.onlyOneTime);
15490 if (binding && instanceBindings)
15491 instanceBindings.push(binding);
15492 }
15493
15494 if (!bindings.isTemplate)
15495 return;
15496
15497 node.model_ = model;
15498 var iter = node.processBindingDirectives_(bindings);
15499 if (instanceBindings && iter)
15500 instanceBindings.push(iter);
15501 }
15502
15503 function parseWithDefault(el, name, prepareBindingFn) {
15504 var v = el.getAttribute(name);
15505 return parseMustaches(v == '' ? '{{}}' : v, name, el, prepareBindingFn);
15506 }
15507
15508 function parseAttributeBindings(element, prepareBindingFn) {
15509 assert(element);
15510
15511 var bindings = [];
15512 var ifFound = false;
15513 var bindFound = false;
15514
15515 for (var i = 0; i < element.attributes.length; i++) {
15516 var attr = element.attributes[i];
15517 var name = attr.name;
15518 var value = attr.value;
15519
15520 // Allow bindings expressed in attributes to be prefixed with underbars.
15521 // We do this to allow correct semantics for browsers that don't implement
15522 // <template> where certain attributes might trigger side-effects -- and
15523 // for IE which sanitizes certain attributes, disallowing mustache
15524 // replacements in their text.
15525 while (name[0] === '_') {
15526 name = name.substring(1);
15527 }
15528
15529 if (isTemplate(element) &&
15530 (name === IF || name === BIND || name === REPEAT)) {
15531 continue;
15532 }
15533
15534 var tokens = parseMustaches(value, name, element,
15535 prepareBindingFn);
15536 if (!tokens)
15537 continue;
15538
15539 bindings.push(name, tokens);
15540 }
15541
15542 if (isTemplate(element)) {
15543 bindings.isTemplate = true;
15544 bindings.if = parseWithDefault(element, IF, prepareBindingFn);
15545 bindings.bind = parseWithDefault(element, BIND, prepareBindingFn);
15546 bindings.repeat = parseWithDefault(element, REPEAT, prepareBindingFn);
15547
15548 if (bindings.if && !bindings.bind && !bindings.repeat)
15549 bindings.bind = parseMustaches('{{}}', BIND, element, prepareBindingFn);
15550 }
15551
15552 return bindings;
15553 }
15554
15555 function getBindings(node, prepareBindingFn) {
15556 if (node.nodeType === Node.ELEMENT_NODE)
15557 return parseAttributeBindings(node, prepareBindingFn);
15558
15559 if (node.nodeType === Node.TEXT_NODE) {
15560 var tokens = parseMustaches(node.data, 'textContent', node,
15561 prepareBindingFn);
15562 if (tokens)
15563 return ['textContent', tokens];
15564 }
15565
15566 return [];
15567 }
15568
15569 function cloneAndBindInstance(node, parent, stagingDocument, bindings, model,
15570 delegate,
15571 instanceBindings,
15572 instanceRecord) {
15573 var clone = parent.appendChild(stagingDocument.importNode(node, false));
15574
15575 var i = 0;
15576 for (var child = node.firstChild; child; child = child.nextSibling) {
15577 cloneAndBindInstance(child, clone, stagingDocument,
15578 bindings.children[i++],
15579 model,
15580 delegate,
15581 instanceBindings);
15582 }
15583
15584 if (bindings.isTemplate) {
15585 HTMLTemplateElement.decorate(clone, node);
15586 if (delegate)
15587 clone.setDelegate_(delegate);
15588 }
15589
15590 processBindings(clone, bindings, model, instanceBindings);
15591 return clone;
15592 }
15593
15594 function createInstanceBindingMap(node, prepareBindingFn) {
15595 var map = getBindings(node, prepareBindingFn);
15596 map.children = {};
15597 var index = 0;
15598 for (var child = node.firstChild; child; child = child.nextSibling) {
15599 map.children[index++] = createInstanceBindingMap(child, prepareBindingFn);
15600 }
15601
15602 return map;
15603 }
15604
15605 Object.defineProperty(Node.prototype, 'templateInstance', {
15606 get: function() {
15607 var instance = this.templateInstance_;
15608 return instance ? instance :
15609 (this.parentNode ? this.parentNode.templateInstance : undefined);
15610 }
15611 });
15612
15613 var emptyInstance = document.createDocumentFragment();
15614 emptyInstance.bindings_ = [];
15615 emptyInstance.terminator_ = null;
15616
15617 function TemplateIterator(templateElement) {
15618 this.closed = false;
15619 this.templateElement_ = templateElement;
15620 this.instances = [];
15621 this.deps = undefined;
15622 this.iteratedValue = [];
15623 this.presentValue = undefined;
15624 this.arrayObserver = undefined;
15625 }
15626
15627 TemplateIterator.prototype = {
15628 closeDeps: function() {
15629 var deps = this.deps;
15630 if (deps) {
15631 if (deps.ifOneTime === false)
15632 deps.ifValue.close();
15633 if (deps.oneTime === false)
15634 deps.value.close();
15635 }
15636 },
15637
15638 updateDependencies: function(directives, model) {
15639 this.closeDeps();
15640
15641 var deps = this.deps = {};
15642 var template = this.templateElement_;
15643
15644 if (directives.if) {
15645 deps.hasIf = true;
15646 deps.ifOneTime = directives.if.onlyOneTime;
15647 deps.ifValue = processBinding(IF, directives.if, template, model);
15648
15649 // oneTime if & predicate is false. nothing else to do.
15650 if (deps.ifOneTime && !deps.ifValue) {
15651 this.updateIteratedValue();
15652 return;
15653 }
15654
15655 if (!deps.ifOneTime)
15656 deps.ifValue.open(this.updateIteratedValue, this);
15657 }
15658
15659 if (directives.repeat) {
15660 deps.repeat = true;
15661 deps.oneTime = directives.repeat.onlyOneTime;
15662 deps.value = processBinding(REPEAT, directives.repeat, template, model);
15663 } else {
15664 deps.repeat = false;
15665 deps.oneTime = directives.bind.onlyOneTime;
15666 deps.value = processBinding(BIND, directives.bind, template, model);
15667 }
15668
15669 if (!deps.oneTime)
15670 deps.value.open(this.updateIteratedValue, this);
15671
15672 this.updateIteratedValue();
15673 },
15674
15675 updateIteratedValue: function() {
15676 if (this.deps.hasIf) {
15677 var ifValue = this.deps.ifValue;
15678 if (!this.deps.ifOneTime)
15679 ifValue = ifValue.discardChanges();
15680 if (!ifValue) {
15681 this.valueChanged();
15682 return;
15683 }
15684 }
15685
15686 var value = this.deps.value;
15687 if (!this.deps.oneTime)
15688 value = value.discardChanges();
15689 if (!this.deps.repeat)
15690 value = [value];
15691 var observe = this.deps.repeat &&
15692 !this.deps.oneTime &&
15693 Array.isArray(value);
15694 this.valueChanged(value, observe);
15695 },
15696
15697 valueChanged: function(value, observeValue) {
15698 if (!Array.isArray(value))
15699 value = [];
15700
15701 if (value === this.iteratedValue)
15702 return;
15703
15704 this.unobserve();
15705 this.presentValue = value;
15706 if (observeValue) {
15707 this.arrayObserver = new ArrayObserver(this.presentValue);
15708 this.arrayObserver.open(this.handleSplices, this);
15709 }
15710
15711 this.handleSplices(ArrayObserver.calculateSplices(this.presentValue,
15712 this.iteratedValue));
15713 },
15714
15715 getLastInstanceNode: function(index) {
15716 if (index == -1)
15717 return this.templateElement_;
15718 var instance = this.instances[index];
15719 var terminator = instance.terminator_;
15720 if (!terminator)
15721 return this.getLastInstanceNode(index - 1);
15722
15723 if (terminator.nodeType !== Node.ELEMENT_NODE ||
15724 this.templateElement_ === terminator) {
15725 return terminator;
15726 }
15727
15728 var subtemplateIterator = terminator.iterator_;
15729 if (!subtemplateIterator)
15730 return terminator;
15731
15732 return subtemplateIterator.getLastTemplateNode();
15733 },
15734
15735 getLastTemplateNode: function() {
15736 return this.getLastInstanceNode(this.instances.length - 1);
15737 },
15738
15739 insertInstanceAt: function(index, fragment) {
15740 var previousInstanceLast = this.getLastInstanceNode(index - 1);
15741 var parent = this.templateElement_.parentNode;
15742 this.instances.splice(index, 0, fragment);
15743
15744 parent.insertBefore(fragment, previousInstanceLast.nextSibling);
15745 },
15746
15747 extractInstanceAt: function(index) {
15748 var previousInstanceLast = this.getLastInstanceNode(index - 1);
15749 var lastNode = this.getLastInstanceNode(index);
15750 var parent = this.templateElement_.parentNode;
15751 var instance = this.instances.splice(index, 1)[0];
15752
15753 while (lastNode !== previousInstanceLast) {
15754 var node = previousInstanceLast.nextSibling;
15755 if (node == lastNode)
15756 lastNode = previousInstanceLast;
15757
15758 instance.appendChild(parent.removeChild(node));
15759 }
15760
15761 return instance;
15762 },
15763
15764 getDelegateFn: function(fn) {
15765 fn = fn && fn(this.templateElement_);
15766 return typeof fn === 'function' ? fn : null;
15767 },
15768
15769 handleSplices: function(splices) {
15770 if (this.closed || !splices.length)
15771 return;
15772
15773 var template = this.templateElement_;
15774
15775 if (!template.parentNode) {
15776 this.close();
15777 return;
15778 }
15779
15780 ArrayObserver.applySplices(this.iteratedValue, this.presentValue,
15781 splices);
15782
15783 var delegate = template.delegate_;
15784 if (this.instanceModelFn_ === undefined) {
15785 this.instanceModelFn_ =
15786 this.getDelegateFn(delegate && delegate.prepareInstanceModel);
15787 }
15788
15789 if (this.instancePositionChangedFn_ === undefined) {
15790 this.instancePositionChangedFn_ =
15791 this.getDelegateFn(delegate &&
15792 delegate.prepareInstancePositionChanged);
15793 }
15794
15795 // Instance Removals
15796 var instanceCache = new Map;
15797 var removeDelta = 0;
15798 for (var i = 0; i < splices.length; i++) {
15799 var splice = splices[i];
15800 var removed = splice.removed;
15801 for (var j = 0; j < removed.length; j++) {
15802 var model = removed[j];
15803 var instance = this.extractInstanceAt(splice.index + removeDelta);
15804 if (instance !== emptyInstance) {
15805 instanceCache.set(model, instance);
15806 }
15807 }
15808
15809 removeDelta -= splice.addedCount;
15810 }
15811
15812 // Instance Insertions
15813 for (var i = 0; i < splices.length; i++) {
15814 var splice = splices[i];
15815 var addIndex = splice.index;
15816 for (; addIndex < splice.index + splice.addedCount; addIndex++) {
15817 var model = this.iteratedValue[addIndex];
15818 var instance = instanceCache.get(model);
15819 if (instance) {
15820 instanceCache.delete(model);
15821 } else {
15822 if (this.instanceModelFn_) {
15823 model = this.instanceModelFn_(model);
15824 }
15825
15826 if (model === undefined) {
15827 instance = emptyInstance;
15828 } else {
15829 instance = template.createInstance(model, undefined, delegate);
15830 }
15831 }
15832
15833 this.insertInstanceAt(addIndex, instance);
15834 }
15835 }
15836
15837 instanceCache.forEach(function(instance) {
15838 this.closeInstanceBindings(instance);
15839 }, this);
15840
15841 if (this.instancePositionChangedFn_)
15842 this.reportInstancesMoved(splices);
15843 },
15844
15845 reportInstanceMoved: function(index) {
15846 var instance = this.instances[index];
15847 if (instance === emptyInstance)
15848 return;
15849
15850 this.instancePositionChangedFn_(instance.templateInstance_, index);
15851 },
15852
15853 reportInstancesMoved: function(splices) {
15854 var index = 0;
15855 var offset = 0;
15856 for (var i = 0; i < splices.length; i++) {
15857 var splice = splices[i];
15858 if (offset != 0) {
15859 while (index < splice.index) {
15860 this.reportInstanceMoved(index);
15861 index++;
15862 }
15863 } else {
15864 index = splice.index;
15865 }
15866
15867 while (index < splice.index + splice.addedCount) {
15868 this.reportInstanceMoved(index);
15869 index++;
15870 }
15871
15872 offset += splice.addedCount - splice.removed.length;
15873 }
15874
15875 if (offset == 0)
15876 return;
15877
15878 var length = this.instances.length;
15879 while (index < length) {
15880 this.reportInstanceMoved(index);
15881 index++;
15882 }
15883 },
15884
15885 closeInstanceBindings: function(instance) {
15886 var bindings = instance.bindings_;
15887 for (var i = 0; i < bindings.length; i++) {
15888 bindings[i].close();
15889 }
15890 },
15891
15892 unobserve: function() {
15893 if (!this.arrayObserver)
15894 return;
15895
15896 this.arrayObserver.close();
15897 this.arrayObserver = undefined;
15898 },
15899
15900 close: function() {
15901 if (this.closed)
15902 return;
15903 this.unobserve();
15904 for (var i = 0; i < this.instances.length; i++) {
15905 this.closeInstanceBindings(this.instances[i]);
15906 }
15907
15908 this.instances.length = 0;
15909 this.closeDeps();
15910 this.templateElement_.iterator_ = undefined;
15911 this.closed = true;
15912 }
15913 };
15914
15915 // Polyfill-specific API.
15916 HTMLTemplateElement.forAllTemplatesFrom_ = forAllTemplatesFrom;
15917 })(this);
15918
15919 /*
15920 Copyright (C) 2013 Ariya Hidayat <ariya.hidayat@gmail.com>
15921 Copyright (C) 2013 Thaddee Tyl <thaddee.tyl@gmail.com>
15922 Copyright (C) 2012 Ariya Hidayat <ariya.hidayat@gmail.com>
15923 Copyright (C) 2012 Mathias Bynens <mathias@qiwi.be>
15924 Copyright (C) 2012 Joost-Wim Boekesteijn <joost-wim@boekesteijn.nl>
15925 Copyright (C) 2012 Kris Kowal <kris.kowal@cixar.com>
15926 Copyright (C) 2012 Yusuke Suzuki <utatane.tea@gmail.com>
15927 Copyright (C) 2012 Arpad Borsos <arpad.borsos@googlemail.com>
15928 Copyright (C) 2011 Ariya Hidayat <ariya.hidayat@gmail.com>
15929
15930 Redistribution and use in source and binary forms, with or without
15931 modification, are permitted provided that the following conditions are met:
15932
15933 * Redistributions of source code must retain the above copyright
15934 notice, this list of conditions and the following disclaimer.
15935 * Redistributions in binary form must reproduce the above copyright
15936 notice, this list of conditions and the following disclaimer in the
15937 documentation and/or other materials provided with the distribution.
15938
15939 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15940 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15941 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
15942 ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
15943 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
15944 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
15945 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
15946 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
15947 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
15948 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
15949 */
15950
15951 (function (global) {
15952 'use strict';
15953
15954 var Token,
15955 TokenName,
15956 Syntax,
15957 Messages,
15958 source,
15959 index,
15960 length,
15961 delegate,
15962 lookahead,
15963 state;
15964
15965 Token = {
15966 BooleanLiteral: 1,
15967 EOF: 2,
15968 Identifier: 3,
15969 Keyword: 4,
15970 NullLiteral: 5,
15971 NumericLiteral: 6,
15972 Punctuator: 7,
15973 StringLiteral: 8
15974 };
15975
15976 TokenName = {};
15977 TokenName[Token.BooleanLiteral] = 'Boolean';
15978 TokenName[Token.EOF] = '<end>';
15979 TokenName[Token.Identifier] = 'Identifier';
15980 TokenName[Token.Keyword] = 'Keyword';
15981 TokenName[Token.NullLiteral] = 'Null';
15982 TokenName[Token.NumericLiteral] = 'Numeric';
15983 TokenName[Token.Punctuator] = 'Punctuator';
15984 TokenName[Token.StringLiteral] = 'String';
15985
15986 Syntax = {
15987 ArrayExpression: 'ArrayExpression',
15988 BinaryExpression: 'BinaryExpression',
15989 CallExpression: 'CallExpression',
15990 ConditionalExpression: 'ConditionalExpression',
15991 EmptyStatement: 'EmptyStatement',
15992 ExpressionStatement: 'ExpressionStatement',
15993 Identifier: 'Identifier',
15994 Literal: 'Literal',
15995 LabeledStatement: 'LabeledStatement',
15996 LogicalExpression: 'LogicalExpression',
15997 MemberExpression: 'MemberExpression',
15998 ObjectExpression: 'ObjectExpression',
15999 Program: 'Program',
16000 Property: 'Property',
16001 ThisExpression: 'ThisExpression',
16002 UnaryExpression: 'UnaryExpression'
16003 };
16004
16005 // Error messages should be identical to V8.
16006 Messages = {
16007 UnexpectedToken: 'Unexpected token %0',
16008 UnknownLabel: 'Undefined label \'%0\'',
16009 Redeclaration: '%0 \'%1\' has already been declared'
16010 };
16011
16012 // Ensure the condition is true, otherwise throw an error.
16013 // This is only to have a better contract semantic, i.e. another safety net
16014 // to catch a logic error. The condition shall be fulfilled in normal case.
16015 // Do NOT use this to enforce a certain condition on any user input.
16016
16017 function assert(condition, message) {
16018 if (!condition) {
16019 throw new Error('ASSERT: ' + message);
16020 }
16021 }
16022
16023 function isDecimalDigit(ch) {
16024 return (ch >= 48 && ch <= 57); // 0..9
16025 }
16026
16027
16028 // 7.2 White Space
16029
16030 function isWhiteSpace(ch) {
16031 return (ch === 32) || // space
16032 (ch === 9) || // tab
16033 (ch === 0xB) ||
16034 (ch === 0xC) ||
16035 (ch === 0xA0) ||
16036 (ch >= 0x1680 && '\u1680\u180E\u2000\u2001\u2002\u2003\u2004\u2005\u 2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000\uFEFF'.indexOf(String.fromCharCod e(ch)) > 0);
16037 }
16038
16039 // 7.3 Line Terminators
16040
16041 function isLineTerminator(ch) {
16042 return (ch === 10) || (ch === 13) || (ch === 0x2028) || (ch === 0x2029);
16043 }
16044
16045 // 7.6 Identifier Names and Identifiers
16046
16047 function isIdentifierStart(ch) {
16048 return (ch === 36) || (ch === 95) || // $ (dollar) and _ (underscore)
16049 (ch >= 65 && ch <= 90) || // A..Z
16050 (ch >= 97 && ch <= 122); // a..z
16051 }
16052
16053 function isIdentifierPart(ch) {
16054 return (ch === 36) || (ch === 95) || // $ (dollar) and _ (underscore)
16055 (ch >= 65 && ch <= 90) || // A..Z
16056 (ch >= 97 && ch <= 122) || // a..z
16057 (ch >= 48 && ch <= 57); // 0..9
16058 }
16059
16060 // 7.6.1.1 Keywords
16061
16062 function isKeyword(id) {
16063 return (id === 'this')
16064 }
16065
16066 // 7.4 Comments
16067
16068 function skipWhitespace() {
16069 while (index < length && isWhiteSpace(source.charCodeAt(index))) {
16070 ++index;
16071 }
16072 }
16073
16074 function getIdentifier() {
16075 var start, ch;
16076
16077 start = index++;
16078 while (index < length) {
16079 ch = source.charCodeAt(index);
16080 if (isIdentifierPart(ch)) {
16081 ++index;
16082 } else {
16083 break;
16084 }
16085 }
16086
16087 return source.slice(start, index);
16088 }
16089
16090 function scanIdentifier() {
16091 var start, id, type;
16092
16093 start = index;
16094
16095 id = getIdentifier();
16096
16097 // There is no keyword or literal with only one character.
16098 // Thus, it must be an identifier.
16099 if (id.length === 1) {
16100 type = Token.Identifier;
16101 } else if (isKeyword(id)) {
16102 type = Token.Keyword;
16103 } else if (id === 'null') {
16104 type = Token.NullLiteral;
16105 } else if (id === 'true' || id === 'false') {
16106 type = Token.BooleanLiteral;
16107 } else {
16108 type = Token.Identifier;
16109 }
16110
16111 return {
16112 type: type,
16113 value: id,
16114 range: [start, index]
16115 };
16116 }
16117
16118
16119 // 7.7 Punctuators
16120
16121 function scanPunctuator() {
16122 var start = index,
16123 code = source.charCodeAt(index),
16124 code2,
16125 ch1 = source[index],
16126 ch2;
16127
16128 switch (code) {
16129
16130 // Check for most common single-character punctuators.
16131 case 46: // . dot
16132 case 40: // ( open bracket
16133 case 41: // ) close bracket
16134 case 59: // ; semicolon
16135 case 44: // , comma
16136 case 123: // { open curly brace
16137 case 125: // } close curly brace
16138 case 91: // [
16139 case 93: // ]
16140 case 58: // :
16141 case 63: // ?
16142 ++index;
16143 return {
16144 type: Token.Punctuator,
16145 value: String.fromCharCode(code),
16146 range: [start, index]
16147 };
16148
16149 default:
16150 code2 = source.charCodeAt(index + 1);
16151
16152 // '=' (char #61) marks an assignment or comparison operator.
16153 if (code2 === 61) {
16154 switch (code) {
16155 case 37: // %
16156 case 38: // &
16157 case 42: // *:
16158 case 43: // +
16159 case 45: // -
16160 case 47: // /
16161 case 60: // <
16162 case 62: // >
16163 case 124: // |
16164 index += 2;
16165 return {
16166 type: Token.Punctuator,
16167 value: String.fromCharCode(code) + String.fromCharCode(c ode2),
16168 range: [start, index]
16169 };
16170
16171 case 33: // !
16172 case 61: // =
16173 index += 2;
16174
16175 // !== and ===
16176 if (source.charCodeAt(index) === 61) {
16177 ++index;
16178 }
16179 return {
16180 type: Token.Punctuator,
16181 value: source.slice(start, index),
16182 range: [start, index]
16183 };
16184 default:
16185 break;
16186 }
16187 }
16188 break;
16189 }
16190
16191 // Peek more characters.
16192
16193 ch2 = source[index + 1];
16194
16195 // Other 2-character punctuators: && ||
16196
16197 if (ch1 === ch2 && ('&|'.indexOf(ch1) >= 0)) {
16198 index += 2;
16199 return {
16200 type: Token.Punctuator,
16201 value: ch1 + ch2,
16202 range: [start, index]
16203 };
16204 }
16205
16206 if ('<>=!+-*%&|^/'.indexOf(ch1) >= 0) {
16207 ++index;
16208 return {
16209 type: Token.Punctuator,
16210 value: ch1,
16211 range: [start, index]
16212 };
16213 }
16214
16215 throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
16216 }
16217
16218 // 7.8.3 Numeric Literals
16219 function scanNumericLiteral() {
16220 var number, start, ch;
16221
16222 ch = source[index];
16223 assert(isDecimalDigit(ch.charCodeAt(0)) || (ch === '.'),
16224 'Numeric literal must start with a decimal digit or a decimal point' );
16225
16226 start = index;
16227 number = '';
16228 if (ch !== '.') {
16229 number = source[index++];
16230 ch = source[index];
16231
16232 // Hex number starts with '0x'.
16233 // Octal number starts with '0'.
16234 if (number === '0') {
16235 // decimal number starts with '0' such as '09' is illegal.
16236 if (ch && isDecimalDigit(ch.charCodeAt(0))) {
16237 throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
16238 }
16239 }
16240
16241 while (isDecimalDigit(source.charCodeAt(index))) {
16242 number += source[index++];
16243 }
16244 ch = source[index];
16245 }
16246
16247 if (ch === '.') {
16248 number += source[index++];
16249 while (isDecimalDigit(source.charCodeAt(index))) {
16250 number += source[index++];
16251 }
16252 ch = source[index];
16253 }
16254
16255 if (ch === 'e' || ch === 'E') {
16256 number += source[index++];
16257
16258 ch = source[index];
16259 if (ch === '+' || ch === '-') {
16260 number += source[index++];
16261 }
16262 if (isDecimalDigit(source.charCodeAt(index))) {
16263 while (isDecimalDigit(source.charCodeAt(index))) {
16264 number += source[index++];
16265 }
16266 } else {
16267 throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
16268 }
16269 }
16270
16271 if (isIdentifierStart(source.charCodeAt(index))) {
16272 throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
16273 }
16274
16275 return {
16276 type: Token.NumericLiteral,
16277 value: parseFloat(number),
16278 range: [start, index]
16279 };
16280 }
16281
16282 // 7.8.4 String Literals
16283
16284 function scanStringLiteral() {
16285 var str = '', quote, start, ch, octal = false;
16286
16287 quote = source[index];
16288 assert((quote === '\'' || quote === '"'),
16289 'String literal must starts with a quote');
16290
16291 start = index;
16292 ++index;
16293
16294 while (index < length) {
16295 ch = source[index++];
16296
16297 if (ch === quote) {
16298 quote = '';
16299 break;
16300 } else if (ch === '\\') {
16301 ch = source[index++];
16302 if (!ch || !isLineTerminator(ch.charCodeAt(0))) {
16303 switch (ch) {
16304 case 'n':
16305 str += '\n';
16306 break;
16307 case 'r':
16308 str += '\r';
16309 break;
16310 case 't':
16311 str += '\t';
16312 break;
16313 case 'b':
16314 str += '\b';
16315 break;
16316 case 'f':
16317 str += '\f';
16318 break;
16319 case 'v':
16320 str += '\x0B';
16321 break;
16322
16323 default:
16324 str += ch;
16325 break;
16326 }
16327 } else {
16328 if (ch === '\r' && source[index] === '\n') {
16329 ++index;
16330 }
16331 }
16332 } else if (isLineTerminator(ch.charCodeAt(0))) {
16333 break;
16334 } else {
16335 str += ch;
16336 }
16337 }
16338
16339 if (quote !== '') {
16340 throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
16341 }
16342
16343 return {
16344 type: Token.StringLiteral,
16345 value: str,
16346 octal: octal,
16347 range: [start, index]
16348 };
16349 }
16350
16351 function isIdentifierName(token) {
16352 return token.type === Token.Identifier ||
16353 token.type === Token.Keyword ||
16354 token.type === Token.BooleanLiteral ||
16355 token.type === Token.NullLiteral;
16356 }
16357
16358 function advance() {
16359 var ch;
16360
16361 skipWhitespace();
16362
16363 if (index >= length) {
16364 return {
16365 type: Token.EOF,
16366 range: [index, index]
16367 };
16368 }
16369
16370 ch = source.charCodeAt(index);
16371
16372 // Very common: ( and ) and ;
16373 if (ch === 40 || ch === 41 || ch === 58) {
16374 return scanPunctuator();
16375 }
16376
16377 // String literal starts with single quote (#39) or double quote (#34).
16378 if (ch === 39 || ch === 34) {
16379 return scanStringLiteral();
16380 }
16381
16382 if (isIdentifierStart(ch)) {
16383 return scanIdentifier();
16384 }
16385
16386 // Dot (.) char #46 can also start a floating-point number, hence the ne ed
16387 // to check the next character.
16388 if (ch === 46) {
16389 if (isDecimalDigit(source.charCodeAt(index + 1))) {
16390 return scanNumericLiteral();
16391 }
16392 return scanPunctuator();
16393 }
16394
16395 if (isDecimalDigit(ch)) {
16396 return scanNumericLiteral();
16397 }
16398
16399 return scanPunctuator();
16400 }
16401
16402 function lex() {
16403 var token;
16404
16405 token = lookahead;
16406 index = token.range[1];
16407
16408 lookahead = advance();
16409
16410 index = token.range[1];
16411
16412 return token;
16413 }
16414
16415 function peek() {
16416 var pos;
16417
16418 pos = index;
16419 lookahead = advance();
16420 index = pos;
16421 }
16422
16423 // Throw an exception
16424
16425 function throwError(token, messageFormat) {
16426 var error,
16427 args = Array.prototype.slice.call(arguments, 2),
16428 msg = messageFormat.replace(
16429 /%(\d)/g,
16430 function (whole, index) {
16431 assert(index < args.length, 'Message reference must be in ra nge');
16432 return args[index];
16433 }
16434 );
16435
16436 error = new Error(msg);
16437 error.index = index;
16438 error.description = msg;
16439 throw error;
16440 }
16441
16442 // Throw an exception because of the token.
16443
16444 function throwUnexpected(token) {
16445 throwError(token, Messages.UnexpectedToken, token.value);
16446 }
16447
16448 // Expect the next token to match the specified punctuator.
16449 // If not, an exception will be thrown.
16450
16451 function expect(value) {
16452 var token = lex();
16453 if (token.type !== Token.Punctuator || token.value !== value) {
16454 throwUnexpected(token);
16455 }
16456 }
16457
16458 // Return true if the next token matches the specified punctuator.
16459
16460 function match(value) {
16461 return lookahead.type === Token.Punctuator && lookahead.value === value;
16462 }
16463
16464 // Return true if the next token matches the specified keyword
16465
16466 function matchKeyword(keyword) {
16467 return lookahead.type === Token.Keyword && lookahead.value === keyword;
16468 }
16469
16470 function consumeSemicolon() {
16471 // Catch the very common case first: immediately a semicolon (char #59).
16472 if (source.charCodeAt(index) === 59) {
16473 lex();
16474 return;
16475 }
16476
16477 skipWhitespace();
16478
16479 if (match(';')) {
16480 lex();
16481 return;
16482 }
16483
16484 if (lookahead.type !== Token.EOF && !match('}')) {
16485 throwUnexpected(lookahead);
16486 }
16487 }
16488
16489 // 11.1.4 Array Initialiser
16490
16491 function parseArrayInitialiser() {
16492 var elements = [];
16493
16494 expect('[');
16495
16496 while (!match(']')) {
16497 if (match(',')) {
16498 lex();
16499 elements.push(null);
16500 } else {
16501 elements.push(parseExpression());
16502
16503 if (!match(']')) {
16504 expect(',');
16505 }
16506 }
16507 }
16508
16509 expect(']');
16510
16511 return delegate.createArrayExpression(elements);
16512 }
16513
16514 // 11.1.5 Object Initialiser
16515
16516 function parseObjectPropertyKey() {
16517 var token;
16518
16519 skipWhitespace();
16520 token = lex();
16521
16522 // Note: This function is called only from parseObjectProperty(), where
16523 // EOF and Punctuator tokens are already filtered out.
16524 if (token.type === Token.StringLiteral || token.type === Token.NumericLi teral) {
16525 return delegate.createLiteral(token);
16526 }
16527
16528 return delegate.createIdentifier(token.value);
16529 }
16530
16531 function parseObjectProperty() {
16532 var token, key;
16533
16534 token = lookahead;
16535 skipWhitespace();
16536
16537 if (token.type === Token.EOF || token.type === Token.Punctuator) {
16538 throwUnexpected(token);
16539 }
16540
16541 key = parseObjectPropertyKey();
16542 expect(':');
16543 return delegate.createProperty('init', key, parseExpression());
16544 }
16545
16546 function parseObjectInitialiser() {
16547 var properties = [];
16548
16549 expect('{');
16550
16551 while (!match('}')) {
16552 properties.push(parseObjectProperty());
16553
16554 if (!match('}')) {
16555 expect(',');
16556 }
16557 }
16558
16559 expect('}');
16560
16561 return delegate.createObjectExpression(properties);
16562 }
16563
16564 // 11.1.6 The Grouping Operator
16565
16566 function parseGroupExpression() {
16567 var expr;
16568
16569 expect('(');
16570
16571 expr = parseExpression();
16572
16573 expect(')');
16574
16575 return expr;
16576 }
16577
16578
16579 // 11.1 Primary Expressions
16580
16581 function parsePrimaryExpression() {
16582 var type, token, expr;
16583
16584 if (match('(')) {
16585 return parseGroupExpression();
16586 }
16587
16588 type = lookahead.type;
16589
16590 if (type === Token.Identifier) {
16591 expr = delegate.createIdentifier(lex().value);
16592 } else if (type === Token.StringLiteral || type === Token.NumericLiteral ) {
16593 expr = delegate.createLiteral(lex());
16594 } else if (type === Token.Keyword) {
16595 if (matchKeyword('this')) {
16596 lex();
16597 expr = delegate.createThisExpression();
16598 }
16599 } else if (type === Token.BooleanLiteral) {
16600 token = lex();
16601 token.value = (token.value === 'true');
16602 expr = delegate.createLiteral(token);
16603 } else if (type === Token.NullLiteral) {
16604 token = lex();
16605 token.value = null;
16606 expr = delegate.createLiteral(token);
16607 } else if (match('[')) {
16608 expr = parseArrayInitialiser();
16609 } else if (match('{')) {
16610 expr = parseObjectInitialiser();
16611 }
16612
16613 if (expr) {
16614 return expr;
16615 }
16616
16617 throwUnexpected(lex());
16618 }
16619
16620 // 11.2 Left-Hand-Side Expressions
16621
16622 function parseArguments() {
16623 var args = [];
16624
16625 expect('(');
16626
16627 if (!match(')')) {
16628 while (index < length) {
16629 args.push(parseExpression());
16630 if (match(')')) {
16631 break;
16632 }
16633 expect(',');
16634 }
16635 }
16636
16637 expect(')');
16638
16639 return args;
16640 }
16641
16642 function parseNonComputedProperty() {
16643 var token;
16644
16645 token = lex();
16646
16647 if (!isIdentifierName(token)) {
16648 throwUnexpected(token);
16649 }
16650
16651 return delegate.createIdentifier(token.value);
16652 }
16653
16654 function parseNonComputedMember() {
16655 expect('.');
16656
16657 return parseNonComputedProperty();
16658 }
16659
16660 function parseComputedMember() {
16661 var expr;
16662
16663 expect('[');
16664
16665 expr = parseExpression();
16666
16667 expect(']');
16668
16669 return expr;
16670 }
16671
16672 function parseLeftHandSideExpression() {
16673 var expr, property;
16674
16675 expr = parsePrimaryExpression();
16676
16677 while (match('.') || match('[')) {
16678 if (match('[')) {
16679 property = parseComputedMember();
16680 expr = delegate.createMemberExpression('[', expr, property);
16681 } else {
16682 property = parseNonComputedMember();
16683 expr = delegate.createMemberExpression('.', expr, property);
16684 }
16685 }
16686
16687 return expr;
16688 }
16689
16690 // 11.3 Postfix Expressions
16691
16692 var parsePostfixExpression = parseLeftHandSideExpression;
16693
16694 // 11.4 Unary Operators
16695
16696 function parseUnaryExpression() {
16697 var token, expr;
16698
16699 if (lookahead.type !== Token.Punctuator && lookahead.type !== Token.Keyw ord) {
16700 expr = parsePostfixExpression();
16701 } else if (match('+') || match('-') || match('!')) {
16702 token = lex();
16703 expr = parseUnaryExpression();
16704 expr = delegate.createUnaryExpression(token.value, expr);
16705 } else if (matchKeyword('delete') || matchKeyword('void') || matchKeywor d('typeof')) {
16706 throwError({}, Messages.UnexpectedToken);
16707 } else {
16708 expr = parsePostfixExpression();
16709 }
16710
16711 return expr;
16712 }
16713
16714 function binaryPrecedence(token) {
16715 var prec = 0;
16716
16717 if (token.type !== Token.Punctuator && token.type !== Token.Keyword) {
16718 return 0;
16719 }
16720
16721 switch (token.value) {
16722 case '||':
16723 prec = 1;
16724 break;
16725
16726 case '&&':
16727 prec = 2;
16728 break;
16729
16730 case '==':
16731 case '!=':
16732 case '===':
16733 case '!==':
16734 prec = 6;
16735 break;
16736
16737 case '<':
16738 case '>':
16739 case '<=':
16740 case '>=':
16741 case 'instanceof':
16742 prec = 7;
16743 break;
16744
16745 case 'in':
16746 prec = 7;
16747 break;
16748
16749 case '+':
16750 case '-':
16751 prec = 9;
16752 break;
16753
16754 case '*':
16755 case '/':
16756 case '%':
16757 prec = 11;
16758 break;
16759
16760 default:
16761 break;
16762 }
16763
16764 return prec;
16765 }
16766
16767 // 11.5 Multiplicative Operators
16768 // 11.6 Additive Operators
16769 // 11.7 Bitwise Shift Operators
16770 // 11.8 Relational Operators
16771 // 11.9 Equality Operators
16772 // 11.10 Binary Bitwise Operators
16773 // 11.11 Binary Logical Operators
16774
16775 function parseBinaryExpression() {
16776 var expr, token, prec, stack, right, operator, left, i;
16777
16778 left = parseUnaryExpression();
16779
16780 token = lookahead;
16781 prec = binaryPrecedence(token);
16782 if (prec === 0) {
16783 return left;
16784 }
16785 token.prec = prec;
16786 lex();
16787
16788 right = parseUnaryExpression();
16789
16790 stack = [left, token, right];
16791
16792 while ((prec = binaryPrecedence(lookahead)) > 0) {
16793
16794 // Reduce: make a binary expression from the three topmost entries.
16795 while ((stack.length > 2) && (prec <= stack[stack.length - 2].prec)) {
16796 right = stack.pop();
16797 operator = stack.pop().value;
16798 left = stack.pop();
16799 expr = delegate.createBinaryExpression(operator, left, right);
16800 stack.push(expr);
16801 }
16802
16803 // Shift.
16804 token = lex();
16805 token.prec = prec;
16806 stack.push(token);
16807 expr = parseUnaryExpression();
16808 stack.push(expr);
16809 }
16810
16811 // Final reduce to clean-up the stack.
16812 i = stack.length - 1;
16813 expr = stack[i];
16814 while (i > 1) {
16815 expr = delegate.createBinaryExpression(stack[i - 1].value, stack[i - 2], expr);
16816 i -= 2;
16817 }
16818
16819 return expr;
16820 }
16821
16822
16823 // 11.12 Conditional Operator
16824
16825 function parseConditionalExpression() {
16826 var expr, consequent, alternate;
16827
16828 expr = parseBinaryExpression();
16829
16830 if (match('?')) {
16831 lex();
16832 consequent = parseConditionalExpression();
16833 expect(':');
16834 alternate = parseConditionalExpression();
16835
16836 expr = delegate.createConditionalExpression(expr, consequent, altern ate);
16837 }
16838
16839 return expr;
16840 }
16841
16842 // Simplification since we do not support AssignmentExpression.
16843 var parseExpression = parseConditionalExpression;
16844
16845 // Polymer Syntax extensions
16846
16847 // Filter ::
16848 // Identifier
16849 // Identifier "(" ")"
16850 // Identifier "(" FilterArguments ")"
16851
16852 function parseFilter() {
16853 var identifier, args;
16854
16855 identifier = lex();
16856
16857 if (identifier.type !== Token.Identifier) {
16858 throwUnexpected(identifier);
16859 }
16860
16861 args = match('(') ? parseArguments() : [];
16862
16863 return delegate.createFilter(identifier.value, args);
16864 }
16865
16866 // Filters ::
16867 // "|" Filter
16868 // Filters "|" Filter
16869
16870 function parseFilters() {
16871 while (match('|')) {
16872 lex();
16873 parseFilter();
16874 }
16875 }
16876
16877 // TopLevel ::
16878 // LabelledExpressions
16879 // AsExpression
16880 // InExpression
16881 // FilterExpression
16882
16883 // AsExpression ::
16884 // FilterExpression as Identifier
16885
16886 // InExpression ::
16887 // Identifier, Identifier in FilterExpression
16888 // Identifier in FilterExpression
16889
16890 // FilterExpression ::
16891 // Expression
16892 // Expression Filters
16893
16894 function parseTopLevel() {
16895 skipWhitespace();
16896 peek();
16897
16898 var expr = parseExpression();
16899 if (expr) {
16900 if (lookahead.value === ',' || lookahead.value == 'in' &&
16901 expr.type === Syntax.Identifier) {
16902 parseInExpression(expr);
16903 } else {
16904 parseFilters();
16905 if (lookahead.value === 'as') {
16906 parseAsExpression(expr);
16907 } else {
16908 delegate.createTopLevel(expr);
16909 }
16910 }
16911 }
16912
16913 if (lookahead.type !== Token.EOF) {
16914 throwUnexpected(lookahead);
16915 }
16916 }
16917
16918 function parseAsExpression(expr) {
16919 lex(); // as
16920 var identifier = lex().value;
16921 delegate.createAsExpression(expr, identifier);
16922 }
16923
16924 function parseInExpression(identifier) {
16925 var indexName;
16926 if (lookahead.value === ',') {
16927 lex();
16928 if (lookahead.type !== Token.Identifier)
16929 throwUnexpected(lookahead);
16930 indexName = lex().value;
16931 }
16932
16933 lex(); // in
16934 var expr = parseExpression();
16935 parseFilters();
16936 delegate.createInExpression(identifier.name, indexName, expr);
16937 }
16938
16939 function parse(code, inDelegate) {
16940 delegate = inDelegate;
16941 source = code;
16942 index = 0;
16943 length = source.length;
16944 lookahead = null;
16945 state = {
16946 labelSet: {}
16947 };
16948
16949 return parseTopLevel();
16950 }
16951
16952 global.esprima = {
16953 parse: parse
16954 };
16955 })(this);
16956
16957 // Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
16958 // This code may only be used under the BSD style license found at http://polyme r.github.io/LICENSE.txt
16959 // The complete set of authors may be found at http://polymer.github.io/AUTHORS. txt
16960 // The complete set of contributors may be found at http://polymer.github.io/CON TRIBUTORS.txt
16961 // Code distributed by Google as part of the polymer project is also
16962 // subject to an additional IP rights grant found at http://polymer.github.io/PA TENTS.txt
16963
16964 (function (global) {
16965 'use strict';
16966
16967 // JScript does not have __proto__. We wrap all object literals with
16968 // createObject which uses Object.create, Object.defineProperty and
16969 // Object.getOwnPropertyDescriptor to create a new object that does the exact
16970 // same thing. The main downside to this solution is that we have to extract
16971 // all those property descriptors for IE.
16972 var createObject = ('__proto__' in {}) ?
16973 function(obj) { return obj; } :
16974 function(obj) {
16975 var proto = obj.__proto__;
16976 if (!proto)
16977 return obj;
16978 var newObject = Object.create(proto);
16979 Object.getOwnPropertyNames(obj).forEach(function(name) {
16980 Object.defineProperty(newObject, name,
16981 Object.getOwnPropertyDescriptor(obj, name));
16982 });
16983 return newObject;
16984 };
16985
16986 function prepareBinding(expressionText, name, node, filterRegistry) {
16987 var expression;
16988 try {
16989 expression = getExpression(expressionText);
16990 if (expression.scopeIdent &&
16991 (node.nodeType !== Node.ELEMENT_NODE ||
16992 node.tagName !== 'TEMPLATE' ||
16993 (name !== 'bind' && name !== 'repeat'))) {
16994 throw Error('as and in can only be used within <template bind/repeat>');
16995 }
16996 } catch (ex) {
16997 console.error('Invalid expression syntax: ' + expressionText, ex);
16998 return;
16999 }
17000
17001 return function(model, node, oneTime) {
17002 var binding = expression.getBinding(model, filterRegistry, oneTime);
17003 if (expression.scopeIdent && binding) {
17004 node.polymerExpressionScopeIdent_ = expression.scopeIdent;
17005 if (expression.indexIdent)
17006 node.polymerExpressionIndexIdent_ = expression.indexIdent;
17007 }
17008
17009 return binding;
17010 }
17011 }
17012
17013 // TODO(rafaelw): Implement simple LRU.
17014 var expressionParseCache = Object.create(null);
17015
17016 function getExpression(expressionText) {
17017 var expression = expressionParseCache[expressionText];
17018 if (!expression) {
17019 var delegate = new ASTDelegate();
17020 esprima.parse(expressionText, delegate);
17021 expression = new Expression(delegate);
17022 expressionParseCache[expressionText] = expression;
17023 }
17024 return expression;
17025 }
17026
17027 function Literal(value) {
17028 this.value = value;
17029 this.valueFn_ = undefined;
17030 }
17031
17032 Literal.prototype = {
17033 valueFn: function() {
17034 if (!this.valueFn_) {
17035 var value = this.value;
17036 this.valueFn_ = function() {
17037 return value;
17038 }
17039 }
17040
17041 return this.valueFn_;
17042 }
17043 }
17044
17045 function IdentPath(name) {
17046 this.name = name;
17047 this.path = Path.get(name);
17048 }
17049
17050 IdentPath.prototype = {
17051 valueFn: function() {
17052 if (!this.valueFn_) {
17053 var name = this.name;
17054 var path = this.path;
17055 this.valueFn_ = function(model, observer) {
17056 if (observer)
17057 observer.addPath(model, path);
17058
17059 return path.getValueFrom(model);
17060 }
17061 }
17062
17063 return this.valueFn_;
17064 },
17065
17066 setValue: function(model, newValue) {
17067 if (this.path.length == 1);
17068 model = findScope(model, this.path[0]);
17069
17070 return this.path.setValueFrom(model, newValue);
17071 }
17072 };
17073
17074 function MemberExpression(object, property, accessor) {
17075 // convert literal computed property access where literal value is a value
17076 // path to ident dot-access.
17077 if (accessor == '[' &&
17078 property instanceof Literal &&
17079 Path.get(property.value).valid) {
17080 accessor = '.';
17081 property = new IdentPath(property.value);
17082 }
17083
17084 this.dynamicDeps = typeof object == 'function' || object.dynamic;
17085
17086 this.dynamic = typeof property == 'function' ||
17087 property.dynamic ||
17088 accessor == '[';
17089
17090 this.simplePath =
17091 !this.dynamic &&
17092 !this.dynamicDeps &&
17093 property instanceof IdentPath &&
17094 (object instanceof MemberExpression || object instanceof IdentPath);
17095
17096 this.object = this.simplePath ? object : getFn(object);
17097 this.property = accessor == '.' ? property : getFn(property);
17098 }
17099
17100 MemberExpression.prototype = {
17101 get fullPath() {
17102 if (!this.fullPath_) {
17103 var last = this.object instanceof IdentPath ?
17104 this.object.name : this.object.fullPath;
17105 this.fullPath_ = Path.get(last + '.' + this.property.name);
17106 }
17107
17108 return this.fullPath_;
17109 },
17110
17111 valueFn: function() {
17112 if (!this.valueFn_) {
17113 var object = this.object;
17114
17115 if (this.simplePath) {
17116 var path = this.fullPath;
17117
17118 this.valueFn_ = function(model, observer) {
17119 if (observer)
17120 observer.addPath(model, path);
17121
17122 return path.getValueFrom(model);
17123 };
17124 } else if (this.property instanceof IdentPath) {
17125 var path = Path.get(this.property.name);
17126
17127 this.valueFn_ = function(model, observer) {
17128 var context = object(model, observer);
17129
17130 if (observer)
17131 observer.addPath(context, path);
17132
17133 return path.getValueFrom(context);
17134 }
17135 } else {
17136 // Computed property.
17137 var property = this.property;
17138
17139 this.valueFn_ = function(model, observer) {
17140 var context = object(model, observer);
17141 var propName = property(model, observer);
17142 if (observer)
17143 observer.addPath(context, propName);
17144
17145 return context ? context[propName] : undefined;
17146 };
17147 }
17148 }
17149 return this.valueFn_;
17150 },
17151
17152 setValue: function(model, newValue) {
17153 if (this.simplePath) {
17154 this.fullPath.setValueFrom(model, newValue);
17155 return newValue;
17156 }
17157
17158 var object = this.object(model);
17159 var propName = this.property instanceof IdentPath ? this.property.name :
17160 this.property(model);
17161 return object[propName] = newValue;
17162 }
17163 };
17164
17165 function Filter(name, args) {
17166 this.name = name;
17167 this.args = [];
17168 for (var i = 0; i < args.length; i++) {
17169 this.args[i] = getFn(args[i]);
17170 }
17171 }
17172
17173 Filter.prototype = {
17174 transform: function(value, toModelDirection, filterRegistry, model,
17175 observer) {
17176 var fn = filterRegistry[this.name];
17177 var context = model;
17178 if (fn) {
17179 context = undefined;
17180 } else {
17181 fn = context[this.name];
17182 if (!fn) {
17183 console.error('Cannot find filter: ' + this.name);
17184 return;
17185 }
17186 }
17187
17188 // If toModelDirection is falsey, then the "normal" (dom-bound) direction
17189 // is used. Otherwise, it looks for a 'toModel' property function on the
17190 // object.
17191 if (toModelDirection) {
17192 fn = fn.toModel;
17193 } else if (typeof fn.toDOM == 'function') {
17194 fn = fn.toDOM;
17195 }
17196
17197 if (typeof fn != 'function') {
17198 console.error('No ' + (toModelDirection ? 'toModel' : 'toDOM') +
17199 ' found on' + this.name);
17200 return;
17201 }
17202
17203 var args = [value];
17204 for (var i = 0; i < this.args.length; i++) {
17205 args[i + 1] = getFn(this.args[i])(model, observer);
17206 }
17207
17208 return fn.apply(context, args);
17209 }
17210 };
17211
17212 function notImplemented() { throw Error('Not Implemented'); }
17213
17214 var unaryOperators = {
17215 '+': function(v) { return +v; },
17216 '-': function(v) { return -v; },
17217 '!': function(v) { return !v; }
17218 };
17219
17220 var binaryOperators = {
17221 '+': function(l, r) { return l+r; },
17222 '-': function(l, r) { return l-r; },
17223 '*': function(l, r) { return l*r; },
17224 '/': function(l, r) { return l/r; },
17225 '%': function(l, r) { return l%r; },
17226 '<': function(l, r) { return l<r; },
17227 '>': function(l, r) { return l>r; },
17228 '<=': function(l, r) { return l<=r; },
17229 '>=': function(l, r) { return l>=r; },
17230 '==': function(l, r) { return l==r; },
17231 '!=': function(l, r) { return l!=r; },
17232 '===': function(l, r) { return l===r; },
17233 '!==': function(l, r) { return l!==r; },
17234 '&&': function(l, r) { return l&&r; },
17235 '||': function(l, r) { return l||r; },
17236 };
17237
17238 function getFn(arg) {
17239 return typeof arg == 'function' ? arg : arg.valueFn();
17240 }
17241
17242 function ASTDelegate() {
17243 this.expression = null;
17244 this.filters = [];
17245 this.deps = {};
17246 this.currentPath = undefined;
17247 this.scopeIdent = undefined;
17248 this.indexIdent = undefined;
17249 this.dynamicDeps = false;
17250 }
17251
17252 ASTDelegate.prototype = {
17253 createUnaryExpression: function(op, argument) {
17254 if (!unaryOperators[op])
17255 throw Error('Disallowed operator: ' + op);
17256
17257 argument = getFn(argument);
17258
17259 return function(model, observer) {
17260 return unaryOperators[op](argument(model, observer));
17261 };
17262 },
17263
17264 createBinaryExpression: function(op, left, right) {
17265 if (!binaryOperators[op])
17266 throw Error('Disallowed operator: ' + op);
17267
17268 left = getFn(left);
17269 right = getFn(right);
17270
17271 return function(model, observer) {
17272 return binaryOperators[op](left(model, observer),
17273 right(model, observer));
17274 };
17275 },
17276
17277 createConditionalExpression: function(test, consequent, alternate) {
17278 test = getFn(test);
17279 consequent = getFn(consequent);
17280 alternate = getFn(alternate);
17281
17282 return function(model, observer) {
17283 return test(model, observer) ?
17284 consequent(model, observer) : alternate(model, observer);
17285 }
17286 },
17287
17288 createIdentifier: function(name) {
17289 var ident = new IdentPath(name);
17290 ident.type = 'Identifier';
17291 return ident;
17292 },
17293
17294 createMemberExpression: function(accessor, object, property) {
17295 var ex = new MemberExpression(object, property, accessor);
17296 if (ex.dynamicDeps)
17297 this.dynamicDeps = true;
17298 return ex;
17299 },
17300
17301 createLiteral: function(token) {
17302 return new Literal(token.value);
17303 },
17304
17305 createArrayExpression: function(elements) {
17306 for (var i = 0; i < elements.length; i++)
17307 elements[i] = getFn(elements[i]);
17308
17309 return function(model, observer) {
17310 var arr = []
17311 for (var i = 0; i < elements.length; i++)
17312 arr.push(elements[i](model, observer));
17313 return arr;
17314 }
17315 },
17316
17317 createProperty: function(kind, key, value) {
17318 return {
17319 key: key instanceof IdentPath ? key.name : key.value,
17320 value: value
17321 };
17322 },
17323
17324 createObjectExpression: function(properties) {
17325 for (var i = 0; i < properties.length; i++)
17326 properties[i].value = getFn(properties[i].value);
17327
17328 return function(model, observer) {
17329 var obj = {};
17330 for (var i = 0; i < properties.length; i++)
17331 obj[properties[i].key] = properties[i].value(model, observer);
17332 return obj;
17333 }
17334 },
17335
17336 createFilter: function(name, args) {
17337 this.filters.push(new Filter(name, args));
17338 },
17339
17340 createAsExpression: function(expression, scopeIdent) {
17341 this.expression = expression;
17342 this.scopeIdent = scopeIdent;
17343 },
17344
17345 createInExpression: function(scopeIdent, indexIdent, expression) {
17346 this.expression = expression;
17347 this.scopeIdent = scopeIdent;
17348 this.indexIdent = indexIdent;
17349 },
17350
17351 createTopLevel: function(expression) {
17352 this.expression = expression;
17353 },
17354
17355 createThisExpression: notImplemented
17356 }
17357
17358 function ConstantObservable(value) {
17359 this.value_ = value;
17360 }
17361
17362 ConstantObservable.prototype = {
17363 open: function() { return this.value_; },
17364 discardChanges: function() { return this.value_; },
17365 deliver: function() {},
17366 close: function() {},
17367 }
17368
17369 function Expression(delegate) {
17370 this.scopeIdent = delegate.scopeIdent;
17371 this.indexIdent = delegate.indexIdent;
17372
17373 if (!delegate.expression)
17374 throw Error('No expression found.');
17375
17376 this.expression = delegate.expression;
17377 getFn(this.expression); // forces enumeration of path dependencies
17378
17379 this.filters = delegate.filters;
17380 this.dynamicDeps = delegate.dynamicDeps;
17381 }
17382
17383 Expression.prototype = {
17384 getBinding: function(model, filterRegistry, oneTime) {
17385 if (oneTime)
17386 return this.getValue(model, undefined, filterRegistry);
17387
17388 var observer = new CompoundObserver();
17389 // captures deps.
17390 var firstValue = this.getValue(model, observer, filterRegistry);
17391 var firstTime = true;
17392 var self = this;
17393
17394 function valueFn() {
17395 // deps cannot have changed on first value retrieval.
17396 if (firstTime) {
17397 firstTime = false;
17398 return firstValue;
17399 }
17400
17401 if (self.dynamicDeps)
17402 observer.startReset();
17403
17404 var value = self.getValue(model,
17405 self.dynamicDeps ? observer : undefined,
17406 filterRegistry);
17407 if (self.dynamicDeps)
17408 observer.finishReset();
17409
17410 return value;
17411 }
17412
17413 function setValueFn(newValue) {
17414 self.setValue(model, newValue, filterRegistry);
17415 return newValue;
17416 }
17417
17418 return new ObserverTransform(observer, valueFn, setValueFn, true);
17419 },
17420
17421 getValue: function(model, observer, filterRegistry) {
17422 var value = getFn(this.expression)(model, observer);
17423 for (var i = 0; i < this.filters.length; i++) {
17424 value = this.filters[i].transform(value, false, filterRegistry, model,
17425 observer);
17426 }
17427
17428 return value;
17429 },
17430
17431 setValue: function(model, newValue, filterRegistry) {
17432 var count = this.filters ? this.filters.length : 0;
17433 while (count-- > 0) {
17434 newValue = this.filters[count].transform(newValue, true, filterRegistry,
17435 model);
17436 }
17437
17438 if (this.expression.setValue)
17439 return this.expression.setValue(model, newValue);
17440 }
17441 }
17442
17443 /**
17444 * Converts a style property name to a css property name. For example:
17445 * "WebkitUserSelect" to "-webkit-user-select"
17446 */
17447 function convertStylePropertyName(name) {
17448 return String(name).replace(/[A-Z]/g, function(c) {
17449 return '-' + c.toLowerCase();
17450 });
17451 }
17452
17453 function isEventHandler(name) {
17454 return name[0] === 'o' &&
17455 name[1] === 'n' &&
17456 name[2] === '-';
17457 }
17458
17459 var mixedCaseEventTypes = {};
17460 [
17461 'webkitAnimationStart',
17462 'webkitAnimationEnd',
17463 'webkitTransitionEnd',
17464 'DOMFocusOut',
17465 'DOMFocusIn',
17466 'DOMMouseScroll'
17467 ].forEach(function(e) {
17468 mixedCaseEventTypes[e.toLowerCase()] = e;
17469 });
17470
17471 var parentScopeName = '@' + Math.random().toString(36).slice(2);
17472
17473 // Single ident paths must bind directly to the appropriate scope object.
17474 // I.e. Pushed values in two-bindings need to be assigned to the actual model
17475 // object.
17476 function findScope(model, prop) {
17477 while (model[parentScopeName] &&
17478 !Object.prototype.hasOwnProperty.call(model, prop)) {
17479 model = model[parentScopeName];
17480 }
17481
17482 return model;
17483 }
17484
17485 function resolveEventReceiver(model, path, node) {
17486 if (path.length == 0)
17487 return undefined;
17488
17489 if (path.length == 1)
17490 return findScope(model, path[0]);
17491
17492 for (var i = 0; model != null && i < path.length - 1; i++) {
17493 model = model[path[i]];
17494 }
17495
17496 return model;
17497 }
17498
17499 function prepareEventBinding(path, name, polymerExpressions) {
17500 var eventType = name.substring(3);
17501 eventType = mixedCaseEventTypes[eventType] || eventType;
17502
17503 return function(model, node, oneTime) {
17504 var fn, receiver, handler;
17505 if (typeof polymerExpressions.resolveEventHandler == 'function') {
17506 handler = function(e) {
17507 fn = fn || polymerExpressions.resolveEventHandler(model, path, node);
17508 fn(e, e.detail, e.currentTarget);
17509
17510 if (Platform && typeof Platform.flush == 'function')
17511 Platform.flush();
17512 };
17513 } else {
17514 handler = function(e) {
17515 fn = fn || path.getValueFrom(model);
17516 receiver = receiver || resolveEventReceiver(model, path, node);
17517
17518 fn.apply(receiver, [e, e.detail, e.currentTarget]);
17519
17520 if (Platform && typeof Platform.flush == 'function')
17521 Platform.flush();
17522 };
17523 }
17524
17525 node.addEventListener(eventType, handler);
17526
17527 if (oneTime)
17528 return;
17529
17530 function bindingValue() {
17531 return '{{ ' + path + ' }}';
17532 }
17533
17534 return {
17535 open: bindingValue,
17536 discardChanges: bindingValue,
17537 close: function() {
17538 node.removeEventListener(eventType, handler);
17539 }
17540 };
17541 }
17542 }
17543
17544 function isLiteralExpression(pathString) {
17545 switch (pathString) {
17546 case '':
17547 return false;
17548
17549 case 'false':
17550 case 'null':
17551 case 'true':
17552 return true;
17553 }
17554
17555 if (!isNaN(Number(pathString)))
17556 return true;
17557
17558 return false;
17559 };
17560
17561 function PolymerExpressions() {}
17562
17563 PolymerExpressions.prototype = {
17564 // "built-in" filters
17565 styleObject: function(value) {
17566 var parts = [];
17567 for (var key in value) {
17568 parts.push(convertStylePropertyName(key) + ': ' + value[key]);
17569 }
17570 return parts.join('; ');
17571 },
17572
17573 tokenList: function(value) {
17574 var tokens = [];
17575 for (var key in value) {
17576 if (value[key])
17577 tokens.push(key);
17578 }
17579 return tokens.join(' ');
17580 },
17581
17582 // binding delegate API
17583 prepareInstancePositionChanged: function(template) {
17584 var indexIdent = template.polymerExpressionIndexIdent_;
17585 if (!indexIdent)
17586 return;
17587
17588 return function(templateInstance, index) {
17589 templateInstance.model[indexIdent] = index;
17590 };
17591 },
17592
17593 prepareBinding: function(pathString, name, node) {
17594 var path = Path.get(pathString);
17595 if (isEventHandler(name)) {
17596 if (!path.valid) {
17597 console.error('on-* bindings must be simple path expressions');
17598 return;
17599 }
17600
17601 return prepareEventBinding(path, name, this);
17602 }
17603
17604 if (!isLiteralExpression(pathString) && path.valid) {
17605 if (path.length == 1) {
17606 return function(model, node, oneTime) {
17607 if (oneTime)
17608 return path.getValueFrom(model);
17609
17610 var scope = findScope(model, path[0]);
17611 return new PathObserver(scope, path);
17612 };
17613 }
17614 return; // bail out early if pathString is simple path.
17615 }
17616
17617 return prepareBinding(pathString, name, node, this);
17618 },
17619
17620 prepareInstanceModel: function(template) {
17621 var scopeName = template.polymerExpressionScopeIdent_;
17622 if (!scopeName)
17623 return;
17624
17625 var parentScope = template.templateInstance ?
17626 template.templateInstance.model :
17627 template.model;
17628
17629 var indexName = template.polymerExpressionIndexIdent_;
17630
17631 return function(model) {
17632 var scope = Object.create(parentScope);
17633 scope[scopeName] = model;
17634 scope[indexName] = undefined;
17635 scope[parentScopeName] = parentScope;
17636 return scope;
17637 };
17638 }
17639 };
17640
17641 global.PolymerExpressions = PolymerExpressions;
17642 if (global.exposeGetExpression)
17643 global.getExpression_ = getExpression;
17644
17645 global.PolymerExpressions.prepareEventBinding = prepareEventBinding;
17646 })(this);
17647
17648 /*
17649 * Copyright 2013 The Polymer Authors. All rights reserved.
17650 * Use of this source code is governed by a BSD-style
17651 * license that can be found in the LICENSE file.
17652 */
17653 (function(scope) {
17654
17655 // inject style sheet
17656 var style = document.createElement('style');
17657 style.textContent = 'template {display: none !important;} /* injected by platfor m.js */';
17658 var head = document.querySelector('head');
17659 head.insertBefore(style, head.firstChild);
17660
17661 // flush (with logging)
17662 var flushing;
17663 function flush() {
17664 if (!flushing) {
17665 flushing = true;
17666 scope.endOfMicrotask(function() {
17667 flushing = false;
17668 logFlags.data && console.group('Platform.flush()');
17669 scope.performMicrotaskCheckpoint();
17670 logFlags.data && console.groupEnd();
17671 });
17672 }
17673 };
17674
17675 // polling dirty checker
17676 // flush periodically if platform does not have object observe.
17677 if (!Observer.hasObjectObserve) {
17678 var FLUSH_POLL_INTERVAL = 125;
17679 window.addEventListener('WebComponentsReady', function() {
17680 flush();
17681 scope.flushPoll = setInterval(flush, FLUSH_POLL_INTERVAL);
17682 });
17683 } else {
17684 // make flush a no-op when we have Object.observe
17685 flush = function() {};
17686 }
17687
17688 if (window.CustomElements && !CustomElements.useNative) {
17689 var originalImportNode = Document.prototype.importNode;
17690 Document.prototype.importNode = function(node, deep) {
17691 var imported = originalImportNode.call(this, node, deep);
17692 CustomElements.upgradeAll(imported);
17693 return imported;
17694 }
17695 }
17696
17697 // exports
17698 scope.flush = flush;
17699
17700 })(window.Platform);
17701
17702
17703 //# sourceMappingURL=platform.concat.js.map
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698