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

Side by Side Diff: pkg/web_components/lib/platform.concat.js

Issue 158083002: introduce web_components pkg for consolidated polyfills (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « pkg/web_components/lib/platform.js ('k') | pkg/web_components/lib/platform.concat.js.map » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 var PROP_ADD_TYPE = 'add';
56 var PROP_UPDATE_TYPE = 'update';
57 var PROP_RECONFIGURE_TYPE = 'reconfigure';
58 var PROP_DELETE_TYPE = 'delete';
59 var ARRAY_SPLICE_TYPE = 'splice';
60
61 // Detect and do basic sanity checking on Object/Array.observe.
62 function detectObjectObserve() {
63 if (typeof Object.observe !== 'function' ||
64 typeof Array.observe !== 'function') {
65 return false;
66 }
67
68 var records = [];
69
70 function callback(recs) {
71 records = recs;
72 }
73
74 var test = {};
75 Object.observe(test, callback);
76 test.id = 1;
77 test.id = 2;
78 delete test.id;
79 Object.deliverChangeRecords(callback);
80 if (records.length !== 3)
81 return false;
82
83 // TODO(rafaelw): Remove this when new change record type names make it to
84 // chrome release.
85 if (records[0].type == 'new' &&
86 records[1].type == 'updated' &&
87 records[2].type == 'deleted') {
88 PROP_ADD_TYPE = 'new';
89 PROP_UPDATE_TYPE = 'updated';
90 PROP_RECONFIGURE_TYPE = 'reconfigured';
91 PROP_DELETE_TYPE = 'deleted';
92 } else if (records[0].type != 'add' ||
93 records[1].type != 'update' ||
94 records[2].type != 'delete') {
95 console.error('Unexpected change record names for Object.observe. ' +
96 'Using dirty-checking instead');
97 return false;
98 }
99 Object.unobserve(test, callback);
100
101 test = [0];
102 Array.observe(test, callback);
103 test[1] = 1;
104 test.length = 0;
105 Object.deliverChangeRecords(callback);
106 if (records.length != 2)
107 return false;
108 if (records[0].type != ARRAY_SPLICE_TYPE ||
109 records[1].type != ARRAY_SPLICE_TYPE) {
110 return false;
111 }
112 Array.unobserve(test, callback);
113
114 return true;
115 }
116
117 var hasObserve = detectObjectObserve();
118
119 function detectEval() {
120 // don't test for eval if document has CSP securityPolicy object and we can see that
121 // eval is not supported. This avoids an error message in console even when the exception
122 // is caught
123 if (global.document &&
124 'securityPolicy' in global.document &&
125 !global.document.securityPolicy.allowsEval) {
126 return false;
127 }
128
129 try {
130 var f = new Function('', 'return true;');
131 return f();
132 } catch (ex) {
133 return false;
134 }
135 }
136
137 var hasEval = detectEval();
138
139 function isIndex(s) {
140 return +s === s >>> 0;
141 }
142
143 function toNumber(s) {
144 return +s;
145 }
146
147 function isObject(obj) {
148 return obj === Object(obj);
149 }
150
151 var numberIsNaN = global.Number.isNaN || function isNaN(value) {
152 return typeof value === 'number' && global.isNaN(value);
153 }
154
155 function areSameValue(left, right) {
156 if (left === right)
157 return left !== 0 || 1 / left === 1 / right;
158 if (numberIsNaN(left) && numberIsNaN(right))
159 return true;
160
161 return left !== left && right !== right;
162 }
163
164 var createObject = ('__proto__' in {}) ?
165 function(obj) { return obj; } :
166 function(obj) {
167 var proto = obj.__proto__;
168 if (!proto)
169 return obj;
170 var newObject = Object.create(proto);
171 Object.getOwnPropertyNames(obj).forEach(function(name) {
172 Object.defineProperty(newObject, name,
173 Object.getOwnPropertyDescriptor(obj, name));
174 });
175 return newObject;
176 };
177
178 var identStart = '[\$_a-zA-Z]';
179 var identPart = '[\$_a-zA-Z0-9]';
180 var ident = identStart + '+' + identPart + '*';
181 var elementIndex = '(?:[0-9]|[1-9]+[0-9]+)';
182 var identOrElementIndex = '(?:' + ident + '|' + elementIndex + ')';
183 var path = '(?:' + identOrElementIndex + ')(?:\\s*\\.\\s*' + identOrElementInd ex + ')*';
184 var pathRegExp = new RegExp('^' + path + '$');
185
186 function isPathValid(s) {
187 if (typeof s != 'string')
188 return false;
189 s = s.trim();
190
191 if (s == '')
192 return true;
193
194 if (s[0] == '.')
195 return false;
196
197 return pathRegExp.test(s);
198 }
199
200 var constructorIsPrivate = {};
201
202 function Path(s, privateToken) {
203 if (privateToken !== constructorIsPrivate)
204 throw Error('Use Path.get to retrieve path objects');
205
206 if (s.trim() == '')
207 return this;
208
209 if (isIndex(s)) {
210 this.push(s);
211 return this;
212 }
213
214 s.split(/\s*\.\s*/).filter(function(part) {
215 return part;
216 }).forEach(function(part) {
217 this.push(part);
218 }, this);
219
220 if (hasEval && this.length) {
221 this.getValueFrom = this.compiledGetValueFromFn();
222 }
223 }
224
225 // TODO(rafaelw): Make simple LRU cache
226 var pathCache = {};
227
228 function getPath(pathString) {
229 if (pathString instanceof Path)
230 return pathString;
231
232 if (pathString == null)
233 pathString = '';
234
235 if (typeof pathString !== 'string')
236 pathString = String(pathString);
237
238 var path = pathCache[pathString];
239 if (path)
240 return path;
241 if (!isPathValid(pathString))
242 return invalidPath;
243 var path = new Path(pathString, constructorIsPrivate);
244 pathCache[pathString] = path;
245 return path;
246 }
247
248 Path.get = getPath;
249
250 Path.prototype = createObject({
251 __proto__: [],
252 valid: true,
253
254 toString: function() {
255 return this.join('.');
256 },
257
258 getValueFrom: function(obj, directObserver) {
259 for (var i = 0; i < this.length; i++) {
260 if (obj == null)
261 return;
262 obj = obj[this[i]];
263 }
264 return obj;
265 },
266
267 iterateObjects: function(obj, observe) {
268 for (var i = 0; i < this.length; i++) {
269 if (i)
270 obj = obj[this[i - 1]];
271 if (!obj)
272 return;
273 observe(obj);
274 }
275 },
276
277 compiledGetValueFromFn: function() {
278 var accessors = this.map(function(ident) {
279 return isIndex(ident) ? '["' + ident + '"]' : '.' + ident;
280 });
281
282 var str = '';
283 var pathString = 'obj';
284 str += 'if (obj != null';
285 var i = 0;
286 for (; i < (this.length - 1); i++) {
287 var ident = this[i];
288 pathString += accessors[i];
289 str += ' &&\n ' + pathString + ' != null';
290 }
291 str += ')\n';
292
293 pathString += accessors[i];
294
295 str += ' return ' + pathString + ';\nelse\n return undefined;';
296 return new Function('obj', str);
297 },
298
299 setValueFrom: function(obj, value) {
300 if (!this.length)
301 return false;
302
303 for (var i = 0; i < this.length - 1; i++) {
304 if (!isObject(obj))
305 return false;
306 obj = obj[this[i]];
307 }
308
309 if (!isObject(obj))
310 return false;
311
312 obj[this[i]] = value;
313 return true;
314 }
315 });
316
317 var invalidPath = new Path('', constructorIsPrivate);
318 invalidPath.valid = false;
319 invalidPath.getValueFrom = invalidPath.setValueFrom = function() {};
320
321 var MAX_DIRTY_CHECK_CYCLES = 1000;
322
323 function dirtyCheck(observer) {
324 var cycles = 0;
325 while (cycles < MAX_DIRTY_CHECK_CYCLES && observer.check_()) {
326 cycles++;
327 }
328 if (global.testingExposeCycleCount)
329 global.dirtyCheckCycleCount = cycles;
330
331 return cycles > 0;
332 }
333
334 function objectIsEmpty(object) {
335 for (var prop in object)
336 return false;
337 return true;
338 }
339
340 function diffIsEmpty(diff) {
341 return objectIsEmpty(diff.added) &&
342 objectIsEmpty(diff.removed) &&
343 objectIsEmpty(diff.changed);
344 }
345
346 function diffObjectFromOldObject(object, oldObject) {
347 var added = {};
348 var removed = {};
349 var changed = {};
350
351 for (var prop in oldObject) {
352 var newValue = object[prop];
353
354 if (newValue !== undefined && newValue === oldObject[prop])
355 continue;
356
357 if (!(prop in object)) {
358 removed[prop] = undefined;
359 continue;
360 }
361
362 if (newValue !== oldObject[prop])
363 changed[prop] = newValue;
364 }
365
366 for (var prop in object) {
367 if (prop in oldObject)
368 continue;
369
370 added[prop] = object[prop];
371 }
372
373 if (Array.isArray(object) && object.length !== oldObject.length)
374 changed.length = object.length;
375
376 return {
377 added: added,
378 removed: removed,
379 changed: changed
380 };
381 }
382
383 var eomTasks = [];
384 function runEOMTasks() {
385 if (!eomTasks.length)
386 return false;
387
388 for (var i = 0; i < eomTasks.length; i++) {
389 eomTasks[i]();
390 }
391 eomTasks.length = 0;
392 return true;
393 }
394
395 var runEOM = hasObserve ? (function(){
396 var eomObj = { pingPong: true };
397 var eomRunScheduled = false;
398
399 Object.observe(eomObj, function() {
400 runEOMTasks();
401 eomRunScheduled = false;
402 });
403
404 return function(fn) {
405 eomTasks.push(fn);
406 if (!eomRunScheduled) {
407 eomRunScheduled = true;
408 eomObj.pingPong = !eomObj.pingPong;
409 }
410 };
411 })() :
412 (function() {
413 return function(fn) {
414 eomTasks.push(fn);
415 };
416 })();
417
418 var observedObjectCache = [];
419
420 function newObservedObject() {
421 var observer;
422 var object;
423 var discardRecords = false;
424 var first = true;
425
426 function callback(records) {
427 if (observer && observer.state_ === OPENED && !discardRecords)
428 observer.check_(records);
429 }
430
431 return {
432 open: function(obs) {
433 if (observer)
434 throw Error('ObservedObject in use');
435
436 if (!first)
437 Object.deliverChangeRecords(callback);
438
439 observer = obs;
440 first = false;
441 },
442 observe: function(obj, arrayObserve) {
443 object = obj;
444 if (arrayObserve)
445 Array.observe(object, callback);
446 else
447 Object.observe(object, callback);
448 },
449 deliver: function(discard) {
450 discardRecords = discard;
451 Object.deliverChangeRecords(callback);
452 discardRecords = false;
453 },
454 close: function() {
455 observer = undefined;
456 Object.unobserve(object, callback);
457 observedObjectCache.push(this);
458 }
459 };
460 }
461
462 function getObservedObject(observer, object, arrayObserve) {
463 var dir = observedObjectCache.pop() || newObservedObject();
464 dir.open(observer);
465 dir.observe(object, arrayObserve);
466 return dir;
467 }
468
469 var emptyArray = [];
470 var observedSetCache = [];
471
472 function newObservedSet() {
473 var observers = [];
474 var observerCount = 0;
475 var objects = [];
476 var toRemove = emptyArray;
477 var resetNeeded = false;
478 var resetScheduled = false;
479
480 function observe(obj) {
481 if (!isObject(obj))
482 return;
483
484 var index = toRemove.indexOf(obj);
485 if (index >= 0) {
486 toRemove[index] = undefined;
487 objects.push(obj);
488 } else if (objects.indexOf(obj) < 0) {
489 objects.push(obj);
490 Object.observe(obj, callback);
491 }
492
493 observe(Object.getPrototypeOf(obj));
494 }
495
496 function reset() {
497 resetScheduled = false;
498 if (!resetNeeded)
499 return;
500
501 var objs = toRemove === emptyArray ? [] : toRemove;
502 toRemove = objects;
503 objects = objs;
504
505 var observer;
506 for (var id in observers) {
507 observer = observers[id];
508 if (!observer || observer.state_ != OPENED)
509 continue;
510
511 observer.iterateObjects_(observe);
512 }
513
514 for (var i = 0; i < toRemove.length; i++) {
515 var obj = toRemove[i];
516 if (obj)
517 Object.unobserve(obj, callback);
518 }
519
520 toRemove.length = 0;
521 }
522
523 function scheduleReset() {
524 if (resetScheduled)
525 return;
526
527 resetNeeded = true;
528 resetScheduled = true;
529 runEOM(reset);
530 }
531
532 function callback() {
533 var observer;
534
535 for (var id in observers) {
536 observer = observers[id];
537 if (!observer || observer.state_ != OPENED)
538 continue;
539
540 observer.check_();
541 }
542
543 scheduleReset();
544 }
545
546 var record = {
547 object: undefined,
548 objects: objects,
549 open: function(obs) {
550 observers[obs.id_] = obs;
551 observerCount++;
552 obs.iterateObjects_(observe);
553 },
554 close: function(obs) {
555 var anyLeft = false;
556
557 observers[obs.id_] = undefined;
558 observerCount--;
559
560 if (observerCount) {
561 scheduleReset();
562 return;
563 }
564 resetNeeded = false;
565
566 for (var i = 0; i < objects.length; i++) {
567 Object.unobserve(objects[i], callback);
568 Observer.unobservedCount++;
569 }
570
571 observers.length = 0;
572 objects.length = 0;
573 observedSetCache.push(this);
574 },
575 reset: scheduleReset
576 };
577
578 return record;
579 }
580
581 var lastObservedSet;
582
583 function getObservedSet(observer, obj) {
584 if (!lastObservedSet || lastObservedSet.object !== obj) {
585 lastObservedSet = observedSetCache.pop() || newObservedSet();
586 lastObservedSet.object = obj;
587 }
588 lastObservedSet.open(observer);
589 return lastObservedSet;
590 }
591
592 var UNOPENED = 0;
593 var OPENED = 1;
594 var CLOSED = 2;
595 var RESETTING = 3;
596
597 var nextObserverId = 1;
598
599 function Observer() {
600 this.state_ = UNOPENED;
601 this.callback_ = undefined;
602 this.target_ = undefined; // TODO(rafaelw): Should be WeakRef
603 this.directObserver_ = undefined;
604 this.value_ = undefined;
605 this.id_ = nextObserverId++;
606 }
607
608 Observer.prototype = {
609 open: function(callback, target) {
610 if (this.state_ != UNOPENED)
611 throw Error('Observer has already been opened.');
612
613 addToAll(this);
614 this.callback_ = callback;
615 this.target_ = target;
616 this.state_ = OPENED;
617 this.connect_();
618 return this.value_;
619 },
620
621 close: function() {
622 if (this.state_ != OPENED)
623 return;
624
625 removeFromAll(this);
626 this.state_ = CLOSED;
627 this.disconnect_();
628 this.value_ = undefined;
629 this.callback_ = undefined;
630 this.target_ = undefined;
631 },
632
633 deliver: function() {
634 if (this.state_ != OPENED)
635 return;
636
637 dirtyCheck(this);
638 },
639
640 report_: function(changes) {
641 try {
642 this.callback_.apply(this.target_, changes);
643 } catch (ex) {
644 Observer._errorThrownDuringCallback = true;
645 console.error('Exception caught during observer callback: ' +
646 (ex.stack || ex));
647 }
648 },
649
650 discardChanges: function() {
651 this.check_(undefined, true);
652 return this.value_;
653 }
654 }
655
656 var collectObservers = !hasObserve;
657 var allObservers;
658 Observer._allObserversCount = 0;
659
660 if (collectObservers) {
661 allObservers = [];
662 }
663
664 function addToAll(observer) {
665 Observer._allObserversCount++;
666 if (!collectObservers)
667 return;
668
669 allObservers.push(observer);
670 }
671
672 function removeFromAll(observer) {
673 Observer._allObserversCount--;
674 }
675
676 var runningMicrotaskCheckpoint = false;
677
678 var hasDebugForceFullDelivery = typeof Object.deliverAllChangeRecords == 'func tion';
679
680 global.Platform = global.Platform || {};
681
682 global.Platform.performMicrotaskCheckpoint = function() {
683 if (runningMicrotaskCheckpoint)
684 return;
685
686 if (hasDebugForceFullDelivery) {
687 Object.deliverAllChangeRecords();
688 return;
689 }
690
691 if (!collectObservers)
692 return;
693
694 runningMicrotaskCheckpoint = true;
695
696 var cycles = 0;
697 var anyChanged, toCheck;
698
699 do {
700 cycles++;
701 toCheck = allObservers;
702 allObservers = [];
703 anyChanged = false;
704
705 for (var i = 0; i < toCheck.length; i++) {
706 var observer = toCheck[i];
707 if (observer.state_ != OPENED)
708 continue;
709
710 if (observer.check_())
711 anyChanged = true;
712
713 allObservers.push(observer);
714 }
715 if (runEOMTasks())
716 anyChanged = true;
717 } while (cycles < MAX_DIRTY_CHECK_CYCLES && anyChanged);
718
719 if (global.testingExposeCycleCount)
720 global.dirtyCheckCycleCount = cycles;
721
722 runningMicrotaskCheckpoint = false;
723 };
724
725 if (collectObservers) {
726 global.Platform.clearObservers = function() {
727 allObservers = [];
728 };
729 }
730
731 function ObjectObserver(object) {
732 Observer.call(this);
733 this.value_ = object;
734 this.oldObject_ = undefined;
735 }
736
737 ObjectObserver.prototype = createObject({
738 __proto__: Observer.prototype,
739
740 arrayObserve: false,
741
742 connect_: function(callback, target) {
743 if (hasObserve) {
744 this.directObserver_ = getObservedObject(this, this.value_,
745 this.arrayObserve);
746 } else {
747 this.oldObject_ = this.copyObject(this.value_);
748 }
749
750 },
751
752 copyObject: function(object) {
753 var copy = Array.isArray(object) ? [] : {};
754 for (var prop in object) {
755 copy[prop] = object[prop];
756 };
757 if (Array.isArray(object))
758 copy.length = object.length;
759 return copy;
760 },
761
762 check_: function(changeRecords, skipChanges) {
763 var diff;
764 var oldValues;
765 if (hasObserve) {
766 if (!changeRecords)
767 return false;
768
769 oldValues = {};
770 diff = diffObjectFromChangeRecords(this.value_, changeRecords,
771 oldValues);
772 } else {
773 oldValues = this.oldObject_;
774 diff = diffObjectFromOldObject(this.value_, this.oldObject_);
775 }
776
777 if (diffIsEmpty(diff))
778 return false;
779
780 if (!hasObserve)
781 this.oldObject_ = this.copyObject(this.value_);
782
783 this.report_([
784 diff.added || {},
785 diff.removed || {},
786 diff.changed || {},
787 function(property) {
788 return oldValues[property];
789 }
790 ]);
791
792 return true;
793 },
794
795 disconnect_: function() {
796 if (hasObserve) {
797 this.directObserver_.close();
798 this.directObserver_ = undefined;
799 } else {
800 this.oldObject_ = undefined;
801 }
802 },
803
804 deliver: function() {
805 if (this.state_ != OPENED)
806 return;
807
808 if (hasObserve)
809 this.directObserver_.deliver(false);
810 else
811 dirtyCheck(this);
812 },
813
814 discardChanges: function() {
815 if (this.directObserver_)
816 this.directObserver_.deliver(true);
817 else
818 this.oldObject_ = this.copyObject(this.value_);
819
820 return this.value_;
821 }
822 });
823
824 function ArrayObserver(array) {
825 if (!Array.isArray(array))
826 throw Error('Provided object is not an Array');
827 ObjectObserver.call(this, array);
828 }
829
830 ArrayObserver.prototype = createObject({
831
832 __proto__: ObjectObserver.prototype,
833
834 arrayObserve: true,
835
836 copyObject: function(arr) {
837 return arr.slice();
838 },
839
840 check_: function(changeRecords) {
841 var splices;
842 if (hasObserve) {
843 if (!changeRecords)
844 return false;
845 splices = projectArraySplices(this.value_, changeRecords);
846 } else {
847 splices = calcSplices(this.value_, 0, this.value_.length,
848 this.oldObject_, 0, this.oldObject_.length);
849 }
850
851 if (!splices || !splices.length)
852 return false;
853
854 if (!hasObserve)
855 this.oldObject_ = this.copyObject(this.value_);
856
857 this.report_([splices]);
858 return true;
859 }
860 });
861
862 ArrayObserver.applySplices = function(previous, current, splices) {
863 splices.forEach(function(splice) {
864 var spliceArgs = [splice.index, splice.removed.length];
865 var addIndex = splice.index;
866 while (addIndex < splice.index + splice.addedCount) {
867 spliceArgs.push(current[addIndex]);
868 addIndex++;
869 }
870
871 Array.prototype.splice.apply(previous, spliceArgs);
872 });
873 };
874
875 function PathObserver(object, path) {
876 Observer.call(this);
877
878 this.object_ = object;
879 this.path_ = path instanceof Path ? path : getPath(path);
880 this.directObserver_ = undefined;
881 }
882
883 PathObserver.prototype = createObject({
884 __proto__: Observer.prototype,
885
886 connect_: function() {
887 if (hasObserve)
888 this.directObserver_ = getObservedSet(this, this.object_);
889
890 this.check_(undefined, true);
891 },
892
893 disconnect_: function() {
894 this.value_ = undefined;
895
896 if (this.directObserver_) {
897 this.directObserver_.close(this);
898 this.directObserver_ = undefined;
899 }
900 },
901
902 iterateObjects_: function(observe) {
903 this.path_.iterateObjects(this.object_, observe);
904 },
905
906 check_: function(changeRecords, skipChanges) {
907 var oldValue = this.value_;
908 this.value_ = this.path_.getValueFrom(this.object_);
909 if (skipChanges || areSameValue(this.value_, oldValue))
910 return false;
911
912 this.report_([this.value_, oldValue]);
913 return true;
914 },
915
916 setValue: function(newValue) {
917 if (this.path_)
918 this.path_.setValueFrom(this.object_, newValue);
919 }
920 });
921
922 function CompoundObserver() {
923 Observer.call(this);
924
925 this.value_ = [];
926 this.directObserver_ = undefined;
927 this.observed_ = [];
928 }
929
930 var observerSentinel = {};
931
932 CompoundObserver.prototype = createObject({
933 __proto__: Observer.prototype,
934
935 connect_: function() {
936 this.check_(undefined, true);
937
938 if (!hasObserve)
939 return;
940
941 var object;
942 var needsDirectObserver = false;
943 for (var i = 0; i < this.observed_.length; i += 2) {
944 object = this.observed_[i]
945 if (object !== observerSentinel) {
946 needsDirectObserver = true;
947 break;
948 }
949 }
950
951 if (this.directObserver_) {
952 if (needsDirectObserver) {
953 this.directObserver_.reset();
954 return;
955 }
956 this.directObserver_.close();
957 this.directObserver_ = undefined;
958 return;
959 }
960
961 if (needsDirectObserver)
962 this.directObserver_ = getObservedSet(this, object);
963 },
964
965 closeObservers_: function() {
966 for (var i = 0; i < this.observed_.length; i += 2) {
967 if (this.observed_[i] === observerSentinel)
968 this.observed_[i + 1].close();
969 }
970 this.observed_.length = 0;
971 },
972
973 disconnect_: function() {
974 this.value_ = undefined;
975
976 if (this.directObserver_) {
977 this.directObserver_.close(this);
978 this.directObserver_ = undefined;
979 }
980
981 this.closeObservers_();
982 },
983
984 addPath: function(object, path) {
985 if (this.state_ != UNOPENED && this.state_ != RESETTING)
986 throw Error('Cannot add paths once started.');
987
988 this.observed_.push(object, path instanceof Path ? path : getPath(path));
989 },
990
991 addObserver: function(observer) {
992 if (this.state_ != UNOPENED && this.state_ != RESETTING)
993 throw Error('Cannot add observers once started.');
994
995 observer.open(this.deliver, this);
996 this.observed_.push(observerSentinel, observer);
997 },
998
999 startReset: function() {
1000 if (this.state_ != OPENED)
1001 throw Error('Can only reset while open');
1002
1003 this.state_ = RESETTING;
1004 this.closeObservers_();
1005 },
1006
1007 finishReset: function() {
1008 if (this.state_ != RESETTING)
1009 throw Error('Can only finishReset after startReset');
1010 this.state_ = OPENED;
1011 this.connect_();
1012
1013 return this.value_;
1014 },
1015
1016 iterateObjects_: function(observe) {
1017 var object;
1018 for (var i = 0; i < this.observed_.length; i += 2) {
1019 object = this.observed_[i]
1020 if (object !== observerSentinel)
1021 this.observed_[i + 1].iterateObjects(object, observe)
1022 }
1023 },
1024
1025 check_: function(changeRecords, skipChanges) {
1026 var oldValues;
1027 for (var i = 0; i < this.observed_.length; i += 2) {
1028 var pathOrObserver = this.observed_[i+1];
1029 var object = this.observed_[i];
1030 var value = object === observerSentinel ?
1031 pathOrObserver.discardChanges() :
1032 pathOrObserver.getValueFrom(object)
1033
1034 if (skipChanges) {
1035 this.value_[i / 2] = value;
1036 continue;
1037 }
1038
1039 if (areSameValue(value, this.value_[i / 2]))
1040 continue;
1041
1042 oldValues = oldValues || [];
1043 oldValues[i / 2] = this.value_[i / 2];
1044 this.value_[i / 2] = value;
1045 }
1046
1047 if (!oldValues)
1048 return false;
1049
1050 // TODO(rafaelw): Having observed_ as the third callback arg here is
1051 // pretty lame API. Fix.
1052 this.report_([this.value_, oldValues, this.observed_]);
1053 return true;
1054 }
1055 });
1056
1057 function identFn(value) { return value; }
1058
1059 function ObserverTransform(observable, getValueFn, setValueFn,
1060 dontPassThroughSet) {
1061 this.callback_ = undefined;
1062 this.target_ = undefined;
1063 this.value_ = undefined;
1064 this.observable_ = observable;
1065 this.getValueFn_ = getValueFn || identFn;
1066 this.setValueFn_ = setValueFn || identFn;
1067 // TODO(rafaelw): This is a temporary hack. PolymerExpressions needs this
1068 // at the moment because of a bug in it's dependency tracking.
1069 this.dontPassThroughSet_ = dontPassThroughSet;
1070 }
1071
1072 ObserverTransform.prototype = {
1073 open: function(callback, target) {
1074 this.callback_ = callback;
1075 this.target_ = target;
1076 this.value_ =
1077 this.getValueFn_(this.observable_.open(this.observedCallback_, this));
1078 return this.value_;
1079 },
1080
1081 observedCallback_: function(value) {
1082 value = this.getValueFn_(value);
1083 if (areSameValue(value, this.value_))
1084 return;
1085 var oldValue = this.value_;
1086 this.value_ = value;
1087 this.callback_.call(this.target_, this.value_, oldValue);
1088 },
1089
1090 discardChanges: function() {
1091 this.value_ = this.getValueFn_(this.observable_.discardChanges());
1092 return this.value_;
1093 },
1094
1095 deliver: function() {
1096 return this.observable_.deliver();
1097 },
1098
1099 setValue: function(value) {
1100 value = this.setValueFn_(value);
1101 if (!this.dontPassThroughSet_ && this.observable_.setValue)
1102 return this.observable_.setValue(value);
1103 },
1104
1105 close: function() {
1106 if (this.observable_)
1107 this.observable_.close();
1108 this.callback_ = undefined;
1109 this.target_ = undefined;
1110 this.observable_ = undefined;
1111 this.value_ = undefined;
1112 this.getValueFn_ = undefined;
1113 this.setValueFn_ = undefined;
1114 }
1115 }
1116
1117 var expectedRecordTypes = {};
1118 expectedRecordTypes[PROP_ADD_TYPE] = true;
1119 expectedRecordTypes[PROP_UPDATE_TYPE] = true;
1120 expectedRecordTypes[PROP_DELETE_TYPE] = true;
1121
1122 function notifyFunction(object, name) {
1123 if (typeof Object.observe !== 'function')
1124 return;
1125
1126 var notifier = Object.getNotifier(object);
1127 return function(type, oldValue) {
1128 var changeRecord = {
1129 object: object,
1130 type: type,
1131 name: name
1132 };
1133 if (arguments.length === 2)
1134 changeRecord.oldValue = oldValue;
1135 notifier.notify(changeRecord);
1136 }
1137 }
1138
1139 Observer.defineComputedProperty = function(target, name, observable) {
1140 var notify = notifyFunction(target, name);
1141 var value = observable.open(function(newValue, oldValue) {
1142 value = newValue;
1143 if (notify)
1144 notify(PROP_UPDATE_TYPE, oldValue);
1145 });
1146
1147 Object.defineProperty(target, name, {
1148 get: function() {
1149 observable.deliver();
1150 return value;
1151 },
1152 set: function(newValue) {
1153 observable.setValue(newValue);
1154 return newValue;
1155 },
1156 configurable: true
1157 });
1158
1159 return {
1160 close: function() {
1161 observable.close();
1162 Object.defineProperty(target, name, {
1163 value: value,
1164 writable: true,
1165 configurable: true
1166 });
1167 }
1168 };
1169 }
1170
1171 function diffObjectFromChangeRecords(object, changeRecords, oldValues) {
1172 var added = {};
1173 var removed = {};
1174
1175 for (var i = 0; i < changeRecords.length; i++) {
1176 var record = changeRecords[i];
1177 if (!expectedRecordTypes[record.type]) {
1178 console.error('Unknown changeRecord type: ' + record.type);
1179 console.error(record);
1180 continue;
1181 }
1182
1183 if (!(record.name in oldValues))
1184 oldValues[record.name] = record.oldValue;
1185
1186 if (record.type == PROP_UPDATE_TYPE)
1187 continue;
1188
1189 if (record.type == PROP_ADD_TYPE) {
1190 if (record.name in removed)
1191 delete removed[record.name];
1192 else
1193 added[record.name] = true;
1194
1195 continue;
1196 }
1197
1198 // type = 'delete'
1199 if (record.name in added) {
1200 delete added[record.name];
1201 delete oldValues[record.name];
1202 } else {
1203 removed[record.name] = true;
1204 }
1205 }
1206
1207 for (var prop in added)
1208 added[prop] = object[prop];
1209
1210 for (var prop in removed)
1211 removed[prop] = undefined;
1212
1213 var changed = {};
1214 for (var prop in oldValues) {
1215 if (prop in added || prop in removed)
1216 continue;
1217
1218 var newValue = object[prop];
1219 if (oldValues[prop] !== newValue)
1220 changed[prop] = newValue;
1221 }
1222
1223 return {
1224 added: added,
1225 removed: removed,
1226 changed: changed
1227 };
1228 }
1229
1230 function newSplice(index, removed, addedCount) {
1231 return {
1232 index: index,
1233 removed: removed,
1234 addedCount: addedCount
1235 };
1236 }
1237
1238 var EDIT_LEAVE = 0;
1239 var EDIT_UPDATE = 1;
1240 var EDIT_ADD = 2;
1241 var EDIT_DELETE = 3;
1242
1243 function ArraySplice() {}
1244
1245 ArraySplice.prototype = {
1246
1247 // Note: This function is *based* on the computation of the Levenshtein
1248 // "edit" distance. The one change is that "updates" are treated as two
1249 // edits - not one. With Array splices, an update is really a delete
1250 // followed by an add. By retaining this, we optimize for "keeping" the
1251 // maximum array items in the original array. For example:
1252 //
1253 // 'xxxx123' -> '123yyyy'
1254 //
1255 // With 1-edit updates, the shortest path would be just to update all seven
1256 // characters. With 2-edit updates, we delete 4, leave 3, and add 4. This
1257 // leaves the substring '123' intact.
1258 calcEditDistances: function(current, currentStart, currentEnd,
1259 old, oldStart, oldEnd) {
1260 // "Deletion" columns
1261 var rowCount = oldEnd - oldStart + 1;
1262 var columnCount = currentEnd - currentStart + 1;
1263 var distances = new Array(rowCount);
1264
1265 // "Addition" rows. Initialize null column.
1266 for (var i = 0; i < rowCount; i++) {
1267 distances[i] = new Array(columnCount);
1268 distances[i][0] = i;
1269 }
1270
1271 // Initialize null row
1272 for (var j = 0; j < columnCount; j++)
1273 distances[0][j] = j;
1274
1275 for (var i = 1; i < rowCount; i++) {
1276 for (var j = 1; j < columnCount; j++) {
1277 if (this.equals(current[currentStart + j - 1], old[oldStart + i - 1]))
1278 distances[i][j] = distances[i - 1][j - 1];
1279 else {
1280 var north = distances[i - 1][j] + 1;
1281 var west = distances[i][j - 1] + 1;
1282 distances[i][j] = north < west ? north : west;
1283 }
1284 }
1285 }
1286
1287 return distances;
1288 },
1289
1290 // This starts at the final weight, and walks "backward" by finding
1291 // the minimum previous weight recursively until the origin of the weight
1292 // matrix.
1293 spliceOperationsFromEditDistances: function(distances) {
1294 var i = distances.length - 1;
1295 var j = distances[0].length - 1;
1296 var current = distances[i][j];
1297 var edits = [];
1298 while (i > 0 || j > 0) {
1299 if (i == 0) {
1300 edits.push(EDIT_ADD);
1301 j--;
1302 continue;
1303 }
1304 if (j == 0) {
1305 edits.push(EDIT_DELETE);
1306 i--;
1307 continue;
1308 }
1309 var northWest = distances[i - 1][j - 1];
1310 var west = distances[i - 1][j];
1311 var north = distances[i][j - 1];
1312
1313 var min;
1314 if (west < north)
1315 min = west < northWest ? west : northWest;
1316 else
1317 min = north < northWest ? north : northWest;
1318
1319 if (min == northWest) {
1320 if (northWest == current) {
1321 edits.push(EDIT_LEAVE);
1322 } else {
1323 edits.push(EDIT_UPDATE);
1324 current = northWest;
1325 }
1326 i--;
1327 j--;
1328 } else if (min == west) {
1329 edits.push(EDIT_DELETE);
1330 i--;
1331 current = west;
1332 } else {
1333 edits.push(EDIT_ADD);
1334 j--;
1335 current = north;
1336 }
1337 }
1338
1339 edits.reverse();
1340 return edits;
1341 },
1342
1343 /**
1344 * Splice Projection functions:
1345 *
1346 * A splice map is a representation of how a previous array of items
1347 * was transformed into a new array of items. Conceptually it is a list of
1348 * tuples of
1349 *
1350 * <index, removed, addedCount>
1351 *
1352 * which are kept in ascending index order of. The tuple represents that at
1353 * the |index|, |removed| sequence of items were removed, and counting forwa rd
1354 * from |index|, |addedCount| items were added.
1355 */
1356
1357 /**
1358 * Lacking individual splice mutation information, the minimal set of
1359 * splices can be synthesized given the previous state and final state of an
1360 * array. The basic approach is to calculate the edit distance matrix and
1361 * choose the shortest path through it.
1362 *
1363 * Complexity: O(l * p)
1364 * l: The length of the current array
1365 * p: The length of the old array
1366 */
1367 calcSplices: function(current, currentStart, currentEnd,
1368 old, oldStart, oldEnd) {
1369 var prefixCount = 0;
1370 var suffixCount = 0;
1371
1372 var minLength = Math.min(currentEnd - currentStart, oldEnd - oldStart);
1373 if (currentStart == 0 && oldStart == 0)
1374 prefixCount = this.sharedPrefix(current, old, minLength);
1375
1376 if (currentEnd == current.length && oldEnd == old.length)
1377 suffixCount = this.sharedSuffix(current, old, minLength - prefixCount);
1378
1379 currentStart += prefixCount;
1380 oldStart += prefixCount;
1381 currentEnd -= suffixCount;
1382 oldEnd -= suffixCount;
1383
1384 if (currentEnd - currentStart == 0 && oldEnd - oldStart == 0)
1385 return [];
1386
1387 if (currentStart == currentEnd) {
1388 var splice = newSplice(currentStart, [], 0);
1389 while (oldStart < oldEnd)
1390 splice.removed.push(old[oldStart++]);
1391
1392 return [ splice ];
1393 } else if (oldStart == oldEnd)
1394 return [ newSplice(currentStart, [], currentEnd - currentStart) ];
1395
1396 var ops = this.spliceOperationsFromEditDistances(
1397 this.calcEditDistances(current, currentStart, currentEnd,
1398 old, oldStart, oldEnd));
1399
1400 var splice = undefined;
1401 var splices = [];
1402 var index = currentStart;
1403 var oldIndex = oldStart;
1404 for (var i = 0; i < ops.length; i++) {
1405 switch(ops[i]) {
1406 case EDIT_LEAVE:
1407 if (splice) {
1408 splices.push(splice);
1409 splice = undefined;
1410 }
1411
1412 index++;
1413 oldIndex++;
1414 break;
1415 case EDIT_UPDATE:
1416 if (!splice)
1417 splice = newSplice(index, [], 0);
1418
1419 splice.addedCount++;
1420 index++;
1421
1422 splice.removed.push(old[oldIndex]);
1423 oldIndex++;
1424 break;
1425 case EDIT_ADD:
1426 if (!splice)
1427 splice = newSplice(index, [], 0);
1428
1429 splice.addedCount++;
1430 index++;
1431 break;
1432 case EDIT_DELETE:
1433 if (!splice)
1434 splice = newSplice(index, [], 0);
1435
1436 splice.removed.push(old[oldIndex]);
1437 oldIndex++;
1438 break;
1439 }
1440 }
1441
1442 if (splice) {
1443 splices.push(splice);
1444 }
1445 return splices;
1446 },
1447
1448 sharedPrefix: function(current, old, searchLength) {
1449 for (var i = 0; i < searchLength; i++)
1450 if (!this.equals(current[i], old[i]))
1451 return i;
1452 return searchLength;
1453 },
1454
1455 sharedSuffix: function(current, old, searchLength) {
1456 var index1 = current.length;
1457 var index2 = old.length;
1458 var count = 0;
1459 while (count < searchLength && this.equals(current[--index1], old[--index2 ]))
1460 count++;
1461
1462 return count;
1463 },
1464
1465 calculateSplices: function(current, previous) {
1466 return this.calcSplices(current, 0, current.length, previous, 0,
1467 previous.length);
1468 },
1469
1470 equals: function(currentValue, previousValue) {
1471 return currentValue === previousValue;
1472 }
1473 };
1474
1475 var arraySplice = new ArraySplice();
1476
1477 function calcSplices(current, currentStart, currentEnd,
1478 old, oldStart, oldEnd) {
1479 return arraySplice.calcSplices(current, currentStart, currentEnd,
1480 old, oldStart, oldEnd);
1481 }
1482
1483 function intersect(start1, end1, start2, end2) {
1484 // Disjoint
1485 if (end1 < start2 || end2 < start1)
1486 return -1;
1487
1488 // Adjacent
1489 if (end1 == start2 || end2 == start1)
1490 return 0;
1491
1492 // Non-zero intersect, span1 first
1493 if (start1 < start2) {
1494 if (end1 < end2)
1495 return end1 - start2; // Overlap
1496 else
1497 return end2 - start2; // Contained
1498 } else {
1499 // Non-zero intersect, span2 first
1500 if (end2 < end1)
1501 return end2 - start1; // Overlap
1502 else
1503 return end1 - start1; // Contained
1504 }
1505 }
1506
1507 function mergeSplice(splices, index, removed, addedCount) {
1508
1509 var splice = newSplice(index, removed, addedCount);
1510
1511 var inserted = false;
1512 var insertionOffset = 0;
1513
1514 for (var i = 0; i < splices.length; i++) {
1515 var current = splices[i];
1516 current.index += insertionOffset;
1517
1518 if (inserted)
1519 continue;
1520
1521 var intersectCount = intersect(splice.index,
1522 splice.index + splice.removed.length,
1523 current.index,
1524 current.index + current.addedCount);
1525
1526 if (intersectCount >= 0) {
1527 // Merge the two splices
1528
1529 splices.splice(i, 1);
1530 i--;
1531
1532 insertionOffset -= current.addedCount - current.removed.length;
1533
1534 splice.addedCount += current.addedCount - intersectCount;
1535 var deleteCount = splice.removed.length +
1536 current.removed.length - intersectCount;
1537
1538 if (!splice.addedCount && !deleteCount) {
1539 // merged splice is a noop. discard.
1540 inserted = true;
1541 } else {
1542 var removed = current.removed;
1543
1544 if (splice.index < current.index) {
1545 // some prefix of splice.removed is prepended to current.removed.
1546 var prepend = splice.removed.slice(0, current.index - splice.index);
1547 Array.prototype.push.apply(prepend, removed);
1548 removed = prepend;
1549 }
1550
1551 if (splice.index + splice.removed.length > current.index + current.add edCount) {
1552 // some suffix of splice.removed is appended to current.removed.
1553 var append = splice.removed.slice(current.index + current.addedCount - splice.index);
1554 Array.prototype.push.apply(removed, append);
1555 }
1556
1557 splice.removed = removed;
1558 if (current.index < splice.index) {
1559 splice.index = current.index;
1560 }
1561 }
1562 } else if (splice.index < current.index) {
1563 // Insert splice here.
1564
1565 inserted = true;
1566
1567 splices.splice(i, 0, splice);
1568 i++;
1569
1570 var offset = splice.addedCount - splice.removed.length
1571 current.index += offset;
1572 insertionOffset += offset;
1573 }
1574 }
1575
1576 if (!inserted)
1577 splices.push(splice);
1578 }
1579
1580 function createInitialSplices(array, changeRecords) {
1581 var splices = [];
1582
1583 for (var i = 0; i < changeRecords.length; i++) {
1584 var record = changeRecords[i];
1585 switch(record.type) {
1586 case ARRAY_SPLICE_TYPE:
1587 mergeSplice(splices, record.index, record.removed.slice(), record.adde dCount);
1588 break;
1589 case PROP_ADD_TYPE:
1590 case PROP_UPDATE_TYPE:
1591 case PROP_DELETE_TYPE:
1592 if (!isIndex(record.name))
1593 continue;
1594 var index = toNumber(record.name);
1595 if (index < 0)
1596 continue;
1597 mergeSplice(splices, index, [record.oldValue], 1);
1598 break;
1599 default:
1600 console.error('Unexpected record type: ' + JSON.stringify(record));
1601 break;
1602 }
1603 }
1604
1605 return splices;
1606 }
1607
1608 function projectArraySplices(array, changeRecords) {
1609 var splices = [];
1610
1611 createInitialSplices(array, changeRecords).forEach(function(splice) {
1612 if (splice.addedCount == 1 && splice.removed.length == 1) {
1613 if (splice.removed[0] !== array[splice.index])
1614 splices.push(splice);
1615
1616 return
1617 };
1618
1619 splices = splices.concat(calcSplices(array, splice.index, splice.index + s plice.addedCount,
1620 splice.removed, 0, splice.removed.len gth));
1621 });
1622
1623 return splices;
1624 }
1625
1626 global.Observer = Observer;
1627 global.Observer.runEOM_ = runEOM;
1628 global.Observer.hasObjectObserve = hasObserve;
1629 global.ArrayObserver = ArrayObserver;
1630 global.ArrayObserver.calculateSplices = function(current, previous) {
1631 return arraySplice.calculateSplices(current, previous);
1632 };
1633
1634 global.ArraySplice = ArraySplice;
1635 global.ObjectObserver = ObjectObserver;
1636 global.PathObserver = PathObserver;
1637 global.CompoundObserver = CompoundObserver;
1638 global.Path = Path;
1639 global.ObserverTransform = ObserverTransform;
1640
1641 // TODO(rafaelw): Only needed for testing until new change record names
1642 // make it to release.
1643 global.Observer.changeRecordTypes = {
1644 add: PROP_ADD_TYPE,
1645 update: PROP_UPDATE_TYPE,
1646 reconfigure: PROP_RECONFIGURE_TYPE,
1647 'delete': PROP_DELETE_TYPE,
1648 splice: ARRAY_SPLICE_TYPE
1649 };
1650 })(typeof global !== 'undefined' && global && typeof module !== 'undefined' && m odule ? global : this || window);
1651
1652 // prepoulate window.Platform.flags for default controls
1653 window.Platform = window.Platform || {};
1654 // prepopulate window.logFlags if necessary
1655 window.logFlags = window.logFlags || {};
1656 // process flags
1657 (function(scope){
1658 // import
1659 var flags = scope.flags || {};
1660 // populate flags from location
1661 location.search.slice(1).split('&').forEach(function(o) {
1662 o = o.split('=');
1663 o[0] && (flags[o[0]] = o[1] || true);
1664 });
1665 var entryPoint = document.currentScript || document.querySelector('script[src* ="platform.js"]');
1666 if (entryPoint) {
1667 var a = entryPoint.attributes;
1668 for (var i = 0, n; i < a.length; i++) {
1669 n = a[i];
1670 if (n.name !== 'src') {
1671 flags[n.name] = n.value || true;
1672 }
1673 }
1674 }
1675 if (flags.log) {
1676 flags.log.split(',').forEach(function(f) {
1677 window.logFlags[f] = true;
1678 });
1679 }
1680 // If any of these flags match 'native', then force native ShadowDOM; any
1681 // other truthy value, or failure to detect native
1682 // ShadowDOM, results in polyfill
1683 flags.shadow = (flags.shadow || flags.shadowdom || flags.polyfill);
1684 if (flags.shadow === 'native') {
1685 flags.shadow = false;
1686 } else {
1687 flags.shadow = flags.shadow || !HTMLElement.prototype.createShadowRoot;
1688 }
1689
1690 // CustomElements polyfill flag
1691 if (flags.register) {
1692 window.CustomElements = window.CustomElements || {flags: {}};
1693 window.CustomElements.flags.register = flags.register;
1694 }
1695
1696 if (flags.imports) {
1697 window.HTMLImports = window.HTMLImports || {flags: {}};
1698 window.HTMLImports.flags.imports = flags.imports;
1699 }
1700
1701 // export
1702 scope.flags = flags;
1703 })(Platform);
1704
1705 // select ShadowDOM impl
1706 if (Platform.flags.shadow) {
1707
1708 // Copyright 2012 The Polymer Authors. All rights reserved.
1709 // Use of this source code is goverened by a BSD-style
1710 // license that can be found in the LICENSE file.
1711
1712 window.ShadowDOMPolyfill = {};
1713
1714 (function(scope) {
1715 'use strict';
1716
1717 var constructorTable = new WeakMap();
1718 var nativePrototypeTable = new WeakMap();
1719 var wrappers = Object.create(null);
1720
1721 // Don't test for eval if document has CSP securityPolicy object and we can
1722 // see that eval is not supported. This avoids an error message in console
1723 // even when the exception is caught
1724 var hasEval = !('securityPolicy' in document) ||
1725 document.securityPolicy.allowsEval;
1726 if (hasEval) {
1727 try {
1728 var f = new Function('', 'return true;');
1729 hasEval = f();
1730 } catch (ex) {
1731 hasEval = false;
1732 }
1733 }
1734
1735 function assert(b) {
1736 if (!b)
1737 throw new Error('Assertion failed');
1738 };
1739
1740 var defineProperty = Object.defineProperty;
1741 var getOwnPropertyNames = Object.getOwnPropertyNames;
1742 var getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
1743
1744 function mixin(to, from) {
1745 getOwnPropertyNames(from).forEach(function(name) {
1746 defineProperty(to, name, getOwnPropertyDescriptor(from, name));
1747 });
1748 return to;
1749 };
1750
1751 function mixinStatics(to, from) {
1752 getOwnPropertyNames(from).forEach(function(name) {
1753 switch (name) {
1754 case 'arguments':
1755 case 'caller':
1756 case 'length':
1757 case 'name':
1758 case 'prototype':
1759 case 'toString':
1760 return;
1761 }
1762 defineProperty(to, name, getOwnPropertyDescriptor(from, name));
1763 });
1764 return to;
1765 };
1766
1767 function oneOf(object, propertyNames) {
1768 for (var i = 0; i < propertyNames.length; i++) {
1769 if (propertyNames[i] in object)
1770 return propertyNames[i];
1771 }
1772 }
1773
1774 // Mozilla's old DOM bindings are bretty busted:
1775 // https://bugzilla.mozilla.org/show_bug.cgi?id=855844
1776 // Make sure they are create before we start modifying things.
1777 getOwnPropertyNames(window);
1778
1779 function getWrapperConstructor(node) {
1780 var nativePrototype = node.__proto__ || Object.getPrototypeOf(node);
1781 var wrapperConstructor = constructorTable.get(nativePrototype);
1782 if (wrapperConstructor)
1783 return wrapperConstructor;
1784
1785 var parentWrapperConstructor = getWrapperConstructor(nativePrototype);
1786
1787 var GeneratedWrapper = createWrapperConstructor(parentWrapperConstructor);
1788 registerInternal(nativePrototype, GeneratedWrapper, node);
1789
1790 return GeneratedWrapper;
1791 }
1792
1793 function addForwardingProperties(nativePrototype, wrapperPrototype) {
1794 installProperty(nativePrototype, wrapperPrototype, true);
1795 }
1796
1797 function registerInstanceProperties(wrapperPrototype, instanceObject) {
1798 installProperty(instanceObject, wrapperPrototype, false);
1799 }
1800
1801 var isFirefox = /Firefox/.test(navigator.userAgent);
1802
1803 // This is used as a fallback when getting the descriptor fails in
1804 // installProperty.
1805 var dummyDescriptor = {
1806 get: function() {},
1807 set: function(v) {},
1808 configurable: true,
1809 enumerable: true
1810 };
1811
1812 function isEventHandlerName(name) {
1813 return /^on[a-z]+$/.test(name);
1814 }
1815
1816 function isIdentifierName(name) {
1817 return /^\w[a-zA-Z_0-9]*$/.test(name);
1818 }
1819
1820 function getGetter(name) {
1821 return hasEval && isIdentifierName(name) ?
1822 new Function('return this.impl.' + name) :
1823 function() { return this.impl[name]; };
1824 }
1825
1826 function getSetter(name) {
1827 return hasEval && isIdentifierName(name) ?
1828 new Function('v', 'this.impl.' + name + ' = v') :
1829 function(v) { this.impl[name] = v; };
1830 }
1831
1832 function getMethod(name) {
1833 return hasEval && isIdentifierName(name) ?
1834 new Function('return this.impl.' + name +
1835 '.apply(this.impl, arguments)') :
1836 function() { return this.impl[name].apply(this.impl, arguments); };
1837 }
1838
1839 function getDescriptor(source, name) {
1840 try {
1841 return Object.getOwnPropertyDescriptor(source, name);
1842 } catch (ex) {
1843 // JSC and V8 both use data properties instead of accessors which can
1844 // cause getting the property desciptor to throw an exception.
1845 // https://bugs.webkit.org/show_bug.cgi?id=49739
1846 return dummyDescriptor;
1847 }
1848 }
1849
1850 function installProperty(source, target, allowMethod, opt_blacklist) {
1851 var names = getOwnPropertyNames(source);
1852 for (var i = 0; i < names.length; i++) {
1853 var name = names[i];
1854 if (name === 'polymerBlackList_')
1855 continue;
1856
1857 if (name in target)
1858 continue;
1859
1860 if (source.polymerBlackList_ && source.polymerBlackList_[name])
1861 continue;
1862
1863 if (isFirefox) {
1864 // Tickle Firefox's old bindings.
1865 source.__lookupGetter__(name);
1866 }
1867 var descriptor = getDescriptor(source, name);
1868 var getter, setter;
1869 if (allowMethod && typeof descriptor.value === 'function') {
1870 target[name] = getMethod(name);
1871 continue;
1872 }
1873
1874 var isEvent = isEventHandlerName(name);
1875 if (isEvent)
1876 getter = scope.getEventHandlerGetter(name);
1877 else
1878 getter = getGetter(name);
1879
1880 if (descriptor.writable || descriptor.set) {
1881 if (isEvent)
1882 setter = scope.getEventHandlerSetter(name);
1883 else
1884 setter = getSetter(name);
1885 }
1886
1887 defineProperty(target, name, {
1888 get: getter,
1889 set: setter,
1890 configurable: descriptor.configurable,
1891 enumerable: descriptor.enumerable
1892 });
1893 }
1894 }
1895
1896 /**
1897 * @param {Function} nativeConstructor
1898 * @param {Function} wrapperConstructor
1899 * @param {Object=} opt_instance If present, this is used to extract
1900 * properties from an instance object.
1901 */
1902 function register(nativeConstructor, wrapperConstructor, opt_instance) {
1903 var nativePrototype = nativeConstructor.prototype;
1904 registerInternal(nativePrototype, wrapperConstructor, opt_instance);
1905 mixinStatics(wrapperConstructor, nativeConstructor);
1906 }
1907
1908 function registerInternal(nativePrototype, wrapperConstructor, opt_instance) {
1909 var wrapperPrototype = wrapperConstructor.prototype;
1910 assert(constructorTable.get(nativePrototype) === undefined);
1911
1912 constructorTable.set(nativePrototype, wrapperConstructor);
1913 nativePrototypeTable.set(wrapperPrototype, nativePrototype);
1914
1915 addForwardingProperties(nativePrototype, wrapperPrototype);
1916 if (opt_instance)
1917 registerInstanceProperties(wrapperPrototype, opt_instance);
1918 defineProperty(wrapperPrototype, 'constructor', {
1919 value: wrapperConstructor,
1920 configurable: true,
1921 enumerable: false,
1922 writable: true
1923 });
1924 }
1925
1926 function isWrapperFor(wrapperConstructor, nativeConstructor) {
1927 return constructorTable.get(nativeConstructor.prototype) ===
1928 wrapperConstructor;
1929 }
1930
1931 /**
1932 * Creates a generic wrapper constructor based on |object| and its
1933 * constructor.
1934 * @param {Node} object
1935 * @return {Function} The generated constructor.
1936 */
1937 function registerObject(object) {
1938 var nativePrototype = Object.getPrototypeOf(object);
1939
1940 var superWrapperConstructor = getWrapperConstructor(nativePrototype);
1941 var GeneratedWrapper = createWrapperConstructor(superWrapperConstructor);
1942 registerInternal(nativePrototype, GeneratedWrapper, object);
1943
1944 return GeneratedWrapper;
1945 }
1946
1947 function createWrapperConstructor(superWrapperConstructor) {
1948 function GeneratedWrapper(node) {
1949 superWrapperConstructor.call(this, node);
1950 }
1951 GeneratedWrapper.prototype =
1952 Object.create(superWrapperConstructor.prototype);
1953 GeneratedWrapper.prototype.constructor = GeneratedWrapper;
1954
1955 return GeneratedWrapper;
1956 }
1957
1958 var OriginalDOMImplementation = window.DOMImplementation;
1959 var OriginalEventTarget = window.EventTarget;
1960 var OriginalEvent = window.Event;
1961 var OriginalNode = window.Node;
1962 var OriginalWindow = window.Window;
1963 var OriginalRange = window.Range;
1964 var OriginalCanvasRenderingContext2D = window.CanvasRenderingContext2D;
1965 var OriginalWebGLRenderingContext = window.WebGLRenderingContext;
1966 var OriginalSVGElementInstance = window.SVGElementInstance;
1967
1968 function isWrapper(object) {
1969 return object instanceof wrappers.EventTarget ||
1970 object instanceof wrappers.Event ||
1971 object instanceof wrappers.Range ||
1972 object instanceof wrappers.DOMImplementation ||
1973 object instanceof wrappers.CanvasRenderingContext2D ||
1974 wrappers.WebGLRenderingContext &&
1975 object instanceof wrappers.WebGLRenderingContext;
1976 }
1977
1978 function isNative(object) {
1979 return OriginalEventTarget && object instanceof OriginalEventTarget ||
1980 object instanceof OriginalNode ||
1981 object instanceof OriginalEvent ||
1982 object instanceof OriginalWindow ||
1983 object instanceof OriginalRange ||
1984 object instanceof OriginalDOMImplementation ||
1985 object instanceof OriginalCanvasRenderingContext2D ||
1986 OriginalWebGLRenderingContext &&
1987 object instanceof OriginalWebGLRenderingContext ||
1988 OriginalSVGElementInstance &&
1989 object instanceof OriginalSVGElementInstance;
1990 }
1991
1992 /**
1993 * Wraps a node in a WrapperNode. If there already exists a wrapper for the
1994 * |node| that wrapper is returned instead.
1995 * @param {Node} node
1996 * @return {WrapperNode}
1997 */
1998 function wrap(impl) {
1999 if (impl === null)
2000 return null;
2001
2002 assert(isNative(impl));
2003 return impl.polymerWrapper_ ||
2004 (impl.polymerWrapper_ = new (getWrapperConstructor(impl))(impl));
2005 }
2006
2007 /**
2008 * Unwraps a wrapper and returns the node it is wrapping.
2009 * @param {WrapperNode} wrapper
2010 * @return {Node}
2011 */
2012 function unwrap(wrapper) {
2013 if (wrapper === null)
2014 return null;
2015 assert(isWrapper(wrapper));
2016 return wrapper.impl;
2017 }
2018
2019 /**
2020 * Unwraps object if it is a wrapper.
2021 * @param {Object} object
2022 * @return {Object} The native implementation object.
2023 */
2024 function unwrapIfNeeded(object) {
2025 return object && isWrapper(object) ? unwrap(object) : object;
2026 }
2027
2028 /**
2029 * Wraps object if it is not a wrapper.
2030 * @param {Object} object
2031 * @return {Object} The wrapper for object.
2032 */
2033 function wrapIfNeeded(object) {
2034 return object && !isWrapper(object) ? wrap(object) : object;
2035 }
2036
2037 /**
2038 * Overrides the current wrapper (if any) for node.
2039 * @param {Node} node
2040 * @param {WrapperNode=} wrapper If left out the wrapper will be created as
2041 * needed next time someone wraps the node.
2042 */
2043 function rewrap(node, wrapper) {
2044 if (wrapper === null)
2045 return;
2046 assert(isNative(node));
2047 assert(wrapper === undefined || isWrapper(wrapper));
2048 node.polymerWrapper_ = wrapper;
2049 }
2050
2051 function defineGetter(constructor, name, getter) {
2052 defineProperty(constructor.prototype, name, {
2053 get: getter,
2054 configurable: true,
2055 enumerable: true
2056 });
2057 }
2058
2059 function defineWrapGetter(constructor, name) {
2060 defineGetter(constructor, name, function() {
2061 return wrap(this.impl[name]);
2062 });
2063 }
2064
2065 /**
2066 * Forwards existing methods on the native object to the wrapper methods.
2067 * This does not wrap any of the arguments or the return value since the
2068 * wrapper implementation already takes care of that.
2069 * @param {Array.<Function>} constructors
2070 * @parem {Array.<string>} names
2071 */
2072 function forwardMethodsToWrapper(constructors, names) {
2073 constructors.forEach(function(constructor) {
2074 names.forEach(function(name) {
2075 constructor.prototype[name] = function() {
2076 var w = wrapIfNeeded(this);
2077 return w[name].apply(w, arguments);
2078 };
2079 });
2080 });
2081 }
2082
2083 scope.assert = assert;
2084 scope.constructorTable = constructorTable;
2085 scope.defineGetter = defineGetter;
2086 scope.defineWrapGetter = defineWrapGetter;
2087 scope.forwardMethodsToWrapper = forwardMethodsToWrapper;
2088 scope.isWrapper = isWrapper;
2089 scope.isWrapperFor = isWrapperFor;
2090 scope.mixin = mixin;
2091 scope.nativePrototypeTable = nativePrototypeTable;
2092 scope.oneOf = oneOf;
2093 scope.registerObject = registerObject;
2094 scope.registerWrapper = register;
2095 scope.rewrap = rewrap;
2096 scope.unwrap = unwrap;
2097 scope.unwrapIfNeeded = unwrapIfNeeded;
2098 scope.wrap = wrap;
2099 scope.wrapIfNeeded = wrapIfNeeded;
2100 scope.wrappers = wrappers;
2101
2102 })(window.ShadowDOMPolyfill);
2103
2104 /*
2105 * Copyright 2013 The Polymer Authors. All rights reserved.
2106 * Use of this source code is goverened by a BSD-style
2107 * license that can be found in the LICENSE file.
2108 */
2109
2110 (function(context) {
2111 'use strict';
2112
2113 var OriginalMutationObserver = window.MutationObserver;
2114 var callbacks = [];
2115 var pending = false;
2116 var timerFunc;
2117
2118 function handle() {
2119 pending = false;
2120 var copies = callbacks.slice(0);
2121 callbacks = [];
2122 for (var i = 0; i < copies.length; i++) {
2123 (0, copies[i])();
2124 }
2125 }
2126
2127 if (OriginalMutationObserver) {
2128 var counter = 1;
2129 var observer = new OriginalMutationObserver(handle);
2130 var textNode = document.createTextNode(counter);
2131 observer.observe(textNode, {characterData: true});
2132
2133 timerFunc = function() {
2134 counter = (counter + 1) % 2;
2135 textNode.data = counter;
2136 };
2137
2138 } else {
2139 timerFunc = window.setImmediate || window.setTimeout;
2140 }
2141
2142 function setEndOfMicrotask(func) {
2143 callbacks.push(func);
2144 if (pending)
2145 return;
2146 pending = true;
2147 timerFunc(handle, 0);
2148 }
2149
2150 context.setEndOfMicrotask = setEndOfMicrotask;
2151
2152 })(window.ShadowDOMPolyfill);
2153
2154 /*
2155 * Copyright 2013 The Polymer Authors. All rights reserved.
2156 * Use of this source code is goverened by a BSD-style
2157 * license that can be found in the LICENSE file.
2158 */
2159
2160 (function(scope) {
2161 'use strict';
2162
2163 var setEndOfMicrotask = scope.setEndOfMicrotask
2164 var wrapIfNeeded = scope.wrapIfNeeded
2165 var wrappers = scope.wrappers;
2166
2167 var registrationsTable = new WeakMap();
2168 var globalMutationObservers = [];
2169 var isScheduled = false;
2170
2171 function scheduleCallback(observer) {
2172 if (isScheduled)
2173 return;
2174 setEndOfMicrotask(notifyObservers);
2175 isScheduled = true;
2176 }
2177
2178 // http://dom.spec.whatwg.org/#mutation-observers
2179 function notifyObservers() {
2180 isScheduled = false;
2181
2182 do {
2183 var notifyList = globalMutationObservers.slice();
2184 var anyNonEmpty = false;
2185 for (var i = 0; i < notifyList.length; i++) {
2186 var mo = notifyList[i];
2187 var queue = mo.takeRecords();
2188 removeTransientObserversFor(mo);
2189 if (queue.length) {
2190 mo.callback_(queue, mo);
2191 anyNonEmpty = true;
2192 }
2193 }
2194 } while (anyNonEmpty);
2195 }
2196
2197 /**
2198 * @param {string} type
2199 * @param {Node} target
2200 * @constructor
2201 */
2202 function MutationRecord(type, target) {
2203 this.type = type;
2204 this.target = target;
2205 this.addedNodes = new wrappers.NodeList();
2206 this.removedNodes = new wrappers.NodeList();
2207 this.previousSibling = null;
2208 this.nextSibling = null;
2209 this.attributeName = null;
2210 this.attributeNamespace = null;
2211 this.oldValue = null;
2212 }
2213
2214 /**
2215 * Registers transient observers to ancestor and its ancesors for the node
2216 * which was removed.
2217 * @param {!Node} ancestor
2218 * @param {!Node} node
2219 */
2220 function registerTransientObservers(ancestor, node) {
2221 for (; ancestor; ancestor = ancestor.parentNode) {
2222 var registrations = registrationsTable.get(ancestor);
2223 if (!registrations)
2224 continue;
2225 for (var i = 0; i < registrations.length; i++) {
2226 var registration = registrations[i];
2227 if (registration.options.subtree)
2228 registration.addTransientObserver(node);
2229 }
2230 }
2231 }
2232
2233 function removeTransientObserversFor(observer) {
2234 for (var i = 0; i < observer.nodes_.length; i++) {
2235 var node = observer.nodes_[i];
2236 var registrations = registrationsTable.get(node);
2237 if (!registrations)
2238 return;
2239 for (var j = 0; j < registrations.length; j++) {
2240 var registration = registrations[j];
2241 if (registration.observer === observer)
2242 registration.removeTransientObservers();
2243 }
2244 }
2245 }
2246
2247 // http://dom.spec.whatwg.org/#queue-a-mutation-record
2248 function enqueueMutation(target, type, data) {
2249 // 1.
2250 var interestedObservers = Object.create(null);
2251 var associatedStrings = Object.create(null);
2252
2253 // 2.
2254 for (var node = target; node; node = node.parentNode) {
2255 // 3.
2256 var registrations = registrationsTable.get(node);
2257 if (!registrations)
2258 continue;
2259 for (var j = 0; j < registrations.length; j++) {
2260 var registration = registrations[j];
2261 var options = registration.options;
2262 // 1.
2263 if (node !== target && !options.subtree)
2264 continue;
2265
2266 // 2.
2267 if (type === 'attributes' && !options.attributes)
2268 continue;
2269
2270 // 3. If type is "attributes", options's attributeFilter is present, and
2271 // either options's attributeFilter does not contain name or namespace
2272 // is non-null, continue.
2273 if (type === 'attributes' && options.attributeFilter &&
2274 (data.namespace !== null ||
2275 options.attributeFilter.indexOf(data.name) === -1)) {
2276 continue;
2277 }
2278
2279 // 4.
2280 if (type === 'characterData' && !options.characterData)
2281 continue;
2282
2283 // 5.
2284 if (type === 'childList' && !options.childList)
2285 continue;
2286
2287 // 6.
2288 var observer = registration.observer;
2289 interestedObservers[observer.uid_] = observer;
2290
2291 // 7. If either type is "attributes" and options's attributeOldValue is
2292 // true, or type is "characterData" and options's characterDataOldValue
2293 // is true, set the paired string of registered observer's observer in
2294 // interested observers to oldValue.
2295 if (type === 'attributes' && options.attributeOldValue ||
2296 type === 'characterData' && options.characterDataOldValue) {
2297 associatedStrings[observer.uid_] = data.oldValue;
2298 }
2299 }
2300 }
2301
2302 var anyRecordsEnqueued = false;
2303
2304 // 4.
2305 for (var uid in interestedObservers) {
2306 var observer = interestedObservers[uid];
2307 var record = new MutationRecord(type, target);
2308
2309 // 2.
2310 if ('name' in data && 'namespace' in data) {
2311 record.attributeName = data.name;
2312 record.attributeNamespace = data.namespace;
2313 }
2314
2315 // 3.
2316 if (data.addedNodes)
2317 record.addedNodes = data.addedNodes;
2318
2319 // 4.
2320 if (data.removedNodes)
2321 record.removedNodes = data.removedNodes;
2322
2323 // 5.
2324 if (data.previousSibling)
2325 record.previousSibling = data.previousSibling;
2326
2327 // 6.
2328 if (data.nextSibling)
2329 record.nextSibling = data.nextSibling;
2330
2331 // 7.
2332 if (associatedStrings[uid] !== undefined)
2333 record.oldValue = associatedStrings[uid];
2334
2335 // 8.
2336 observer.records_.push(record);
2337
2338 anyRecordsEnqueued = true;
2339 }
2340
2341 if (anyRecordsEnqueued)
2342 scheduleCallback();
2343 }
2344
2345 var slice = Array.prototype.slice;
2346
2347 /**
2348 * @param {!Object} options
2349 * @constructor
2350 */
2351 function MutationObserverOptions(options) {
2352 this.childList = !!options.childList;
2353 this.subtree = !!options.subtree;
2354
2355 // 1. If either options' attributeOldValue or attributeFilter is present
2356 // and options' attributes is omitted, set options' attributes to true.
2357 if (!('attributes' in options) &&
2358 ('attributeOldValue' in options || 'attributeFilter' in options)) {
2359 this.attributes = true;
2360 } else {
2361 this.attributes = !!options.attributes;
2362 }
2363
2364 // 2. If options' characterDataOldValue is present and options'
2365 // characterData is omitted, set options' characterData to true.
2366 if ('characterDataOldValue' in options && !('characterData' in options))
2367 this.characterData = true;
2368 else
2369 this.characterData = !!options.characterData;
2370
2371 // 3. & 4.
2372 if (!this.attributes &&
2373 (options.attributeOldValue || 'attributeFilter' in options) ||
2374 // 5.
2375 !this.characterData && options.characterDataOldValue) {
2376 throw new TypeError();
2377 }
2378
2379 this.characterData = !!options.characterData;
2380 this.attributeOldValue = !!options.attributeOldValue;
2381 this.characterDataOldValue = !!options.characterDataOldValue;
2382 if ('attributeFilter' in options) {
2383 if (options.attributeFilter == null ||
2384 typeof options.attributeFilter !== 'object') {
2385 throw new TypeError();
2386 }
2387 this.attributeFilter = slice.call(options.attributeFilter);
2388 } else {
2389 this.attributeFilter = null;
2390 }
2391 }
2392
2393 var uidCounter = 0;
2394
2395 /**
2396 * The class that maps to the DOM MutationObserver interface.
2397 * @param {Function} callback.
2398 * @constructor
2399 */
2400 function MutationObserver(callback) {
2401 this.callback_ = callback;
2402 this.nodes_ = [];
2403 this.records_ = [];
2404 this.uid_ = ++uidCounter;
2405
2406 // This will leak. There is no way to implement this without WeakRefs :'(
2407 globalMutationObservers.push(this);
2408 }
2409
2410 MutationObserver.prototype = {
2411 // http://dom.spec.whatwg.org/#dom-mutationobserver-observe
2412 observe: function(target, options) {
2413 target = wrapIfNeeded(target);
2414
2415 var newOptions = new MutationObserverOptions(options);
2416
2417 // 6.
2418 var registration;
2419 var registrations = registrationsTable.get(target);
2420 if (!registrations)
2421 registrationsTable.set(target, registrations = []);
2422
2423 for (var i = 0; i < registrations.length; i++) {
2424 if (registrations[i].observer === this) {
2425 registration = registrations[i];
2426 // 6.1.
2427 registration.removeTransientObservers();
2428 // 6.2.
2429 registration.options = newOptions;
2430 }
2431 }
2432
2433 // 7.
2434 if (!registration) {
2435 registration = new Registration(this, target, newOptions);
2436 registrations.push(registration);
2437 this.nodes_.push(target);
2438 }
2439 },
2440
2441 // http://dom.spec.whatwg.org/#dom-mutationobserver-disconnect
2442 disconnect: function() {
2443 this.nodes_.forEach(function(node) {
2444 var registrations = registrationsTable.get(node);
2445 for (var i = 0; i < registrations.length; i++) {
2446 var registration = registrations[i];
2447 if (registration.observer === this) {
2448 registrations.splice(i, 1);
2449 // Each node can only have one registered observer associated with
2450 // this observer.
2451 break;
2452 }
2453 }
2454 }, this);
2455 this.records_ = [];
2456 },
2457
2458 takeRecords: function() {
2459 var copyOfRecords = this.records_;
2460 this.records_ = [];
2461 return copyOfRecords;
2462 }
2463 };
2464
2465 /**
2466 * Class used to represent a registered observer.
2467 * @param {MutationObserver} observer
2468 * @param {Node} target
2469 * @param {MutationObserverOptions} options
2470 * @constructor
2471 */
2472 function Registration(observer, target, options) {
2473 this.observer = observer;
2474 this.target = target;
2475 this.options = options;
2476 this.transientObservedNodes = [];
2477 }
2478
2479 Registration.prototype = {
2480 /**
2481 * Adds a transient observer on node. The transient observer gets removed
2482 * next time we deliver the change records.
2483 * @param {Node} node
2484 */
2485 addTransientObserver: function(node) {
2486 // Don't add transient observers on the target itself. We already have all
2487 // the required listeners set up on the target.
2488 if (node === this.target)
2489 return;
2490
2491 this.transientObservedNodes.push(node);
2492 var registrations = registrationsTable.get(node);
2493 if (!registrations)
2494 registrationsTable.set(node, registrations = []);
2495
2496 // We know that registrations does not contain this because we already
2497 // checked if node === this.target.
2498 registrations.push(this);
2499 },
2500
2501 removeTransientObservers: function() {
2502 var transientObservedNodes = this.transientObservedNodes;
2503 this.transientObservedNodes = [];
2504
2505 for (var i = 0; i < transientObservedNodes.length; i++) {
2506 var node = transientObservedNodes[i];
2507 var registrations = registrationsTable.get(node);
2508 for (var j = 0; j < registrations.length; j++) {
2509 if (registrations[j] === this) {
2510 registrations.splice(j, 1);
2511 // Each node can only have one registered observer associated with
2512 // this observer.
2513 break;
2514 }
2515 }
2516 }
2517 }
2518 };
2519
2520 scope.enqueueMutation = enqueueMutation;
2521 scope.registerTransientObservers = registerTransientObservers;
2522 scope.wrappers.MutationObserver = MutationObserver;
2523 scope.wrappers.MutationRecord = MutationRecord;
2524
2525 })(window.ShadowDOMPolyfill);
2526
2527 // Copyright 2013 The Polymer Authors. All rights reserved.
2528 // Use of this source code is goverened by a BSD-style
2529 // license that can be found in the LICENSE file.
2530
2531 (function(scope) {
2532 'use strict';
2533
2534 var forwardMethodsToWrapper = scope.forwardMethodsToWrapper;
2535 var mixin = scope.mixin;
2536 var registerWrapper = scope.registerWrapper;
2537 var unwrap = scope.unwrap;
2538 var wrap = scope.wrap;
2539 var wrappers = scope.wrappers;
2540
2541 var wrappedFuns = new WeakMap();
2542 var listenersTable = new WeakMap();
2543 var handledEventsTable = new WeakMap();
2544 var currentlyDispatchingEvents = new WeakMap();
2545 var targetTable = new WeakMap();
2546 var currentTargetTable = new WeakMap();
2547 var relatedTargetTable = new WeakMap();
2548 var eventPhaseTable = new WeakMap();
2549 var stopPropagationTable = new WeakMap();
2550 var stopImmediatePropagationTable = new WeakMap();
2551 var eventHandlersTable = new WeakMap();
2552 var eventPathTable = new WeakMap();
2553
2554 function isShadowRoot(node) {
2555 return node instanceof wrappers.ShadowRoot;
2556 }
2557
2558 function isInsertionPoint(node) {
2559 var localName = node.localName;
2560 return localName === 'content' || localName === 'shadow';
2561 }
2562
2563 function isShadowHost(node) {
2564 return !!node.shadowRoot;
2565 }
2566
2567 function getEventParent(node) {
2568 var dv;
2569 return node.parentNode || (dv = node.defaultView) && wrap(dv) || null;
2570 }
2571
2572 // https://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/shadow/index.html#df n-adjusted-parent
2573 function calculateParents(node, context, ancestors) {
2574 if (ancestors.length)
2575 return ancestors.shift();
2576
2577 // 1.
2578 if (isShadowRoot(node))
2579 return getInsertionParent(node) || node.host;
2580
2581 // 2.
2582 var eventParents = scope.eventParentsTable.get(node);
2583 if (eventParents) {
2584 // Copy over the remaining event parents for next iteration.
2585 for (var i = 1; i < eventParents.length; i++) {
2586 ancestors[i - 1] = eventParents[i];
2587 }
2588 return eventParents[0];
2589 }
2590
2591 // 3.
2592 if (context && isInsertionPoint(node)) {
2593 var parentNode = node.parentNode;
2594 if (parentNode && isShadowHost(parentNode)) {
2595 var trees = scope.getShadowTrees(parentNode);
2596 var p = getInsertionParent(context);
2597 for (var i = 0; i < trees.length; i++) {
2598 if (trees[i].contains(p))
2599 return p;
2600 }
2601 }
2602 }
2603
2604 return getEventParent(node);
2605 }
2606
2607 // https://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/shadow/index.html#ev ent-retargeting
2608 function retarget(node) {
2609 var stack = []; // 1.
2610 var ancestor = node; // 2.
2611 var targets = [];
2612 var ancestors = [];
2613 while (ancestor) { // 3.
2614 var context = null; // 3.2.
2615 // TODO(arv): Change order of these. If the stack is empty we always end
2616 // up pushing ancestor, no matter what.
2617 if (isInsertionPoint(ancestor)) { // 3.1.
2618 context = topMostNotInsertionPoint(stack); // 3.1.1.
2619 var top = stack[stack.length - 1] || ancestor; // 3.1.2.
2620 stack.push(top);
2621 } else if (!stack.length) {
2622 stack.push(ancestor); // 3.3.
2623 }
2624 var target = stack[stack.length - 1]; // 3.4.
2625 targets.push({target: target, currentTarget: ancestor}); // 3.5.
2626 if (isShadowRoot(ancestor)) // 3.6.
2627 stack.pop(); // 3.6.1.
2628
2629 ancestor = calculateParents(ancestor, context, ancestors); // 3.7.
2630 }
2631 return targets;
2632 }
2633
2634 function topMostNotInsertionPoint(stack) {
2635 for (var i = stack.length - 1; i >= 0; i--) {
2636 if (!isInsertionPoint(stack[i]))
2637 return stack[i];
2638 }
2639 return null;
2640 }
2641
2642 // https://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/shadow/index.html#df n-adjusted-related-target
2643 function adjustRelatedTarget(target, related) {
2644 var ancestors = [];
2645 while (target) { // 3.
2646 var stack = []; // 3.1.
2647 var ancestor = related; // 3.2.
2648 var last = undefined; // 3.3. Needs to be reset every iteration.
2649 while (ancestor) {
2650 var context = null;
2651 if (!stack.length) {
2652 stack.push(ancestor);
2653 } else {
2654 if (isInsertionPoint(ancestor)) { // 3.4.3.
2655 context = topMostNotInsertionPoint(stack);
2656 // isDistributed is more general than checking whether last is
2657 // assigned into ancestor.
2658 if (isDistributed(last)) { // 3.4.3.2.
2659 var head = stack[stack.length - 1];
2660 stack.push(head);
2661 }
2662 }
2663 }
2664
2665 if (inSameTree(ancestor, target)) // 3.4.4.
2666 return stack[stack.length - 1];
2667
2668 if (isShadowRoot(ancestor)) // 3.4.5.
2669 stack.pop();
2670
2671 last = ancestor; // 3.4.6.
2672 ancestor = calculateParents(ancestor, context, ancestors); // 3.4.7.
2673 }
2674 if (isShadowRoot(target)) // 3.5.
2675 target = target.host;
2676 else
2677 target = target.parentNode; // 3.6.
2678 }
2679 }
2680
2681 function getInsertionParent(node) {
2682 return scope.insertionParentTable.get(node);
2683 }
2684
2685 function isDistributed(node) {
2686 return getInsertionParent(node);
2687 }
2688
2689 function rootOfNode(node) {
2690 var p;
2691 while (p = node.parentNode) {
2692 node = p;
2693 }
2694 return node;
2695 }
2696
2697 function inSameTree(a, b) {
2698 return rootOfNode(a) === rootOfNode(b);
2699 }
2700
2701 function enclosedBy(a, b) {
2702 if (a === b)
2703 return true;
2704 if (a instanceof wrappers.ShadowRoot)
2705 return enclosedBy(rootOfNode(a.host), b);
2706 return false;
2707 }
2708
2709
2710 function dispatchOriginalEvent(originalEvent) {
2711 // Make sure this event is only dispatched once.
2712 if (handledEventsTable.get(originalEvent))
2713 return;
2714 handledEventsTable.set(originalEvent, true);
2715
2716 return dispatchEvent(wrap(originalEvent), wrap(originalEvent.target));
2717 }
2718
2719 function dispatchEvent(event, originalWrapperTarget) {
2720 if (currentlyDispatchingEvents.get(event))
2721 throw new Error('InvalidStateError')
2722 currentlyDispatchingEvents.set(event, true);
2723
2724 // Render to ensure that the event path is correct.
2725 scope.renderAllPending();
2726 var eventPath = retarget(originalWrapperTarget);
2727
2728 // For window load events the load event is dispatched at the window but
2729 // the target is set to the document.
2730 //
2731 // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-end.html# the-end
2732 //
2733 // TODO(arv): Find a less hacky way to do this.
2734 if (event.type === 'load' &&
2735 eventPath.length === 2 &&
2736 eventPath[0].target instanceof wrappers.Document) {
2737 eventPath.shift();
2738 }
2739
2740 eventPathTable.set(event, eventPath);
2741
2742 if (dispatchCapturing(event, eventPath)) {
2743 if (dispatchAtTarget(event, eventPath)) {
2744 dispatchBubbling(event, eventPath);
2745 }
2746 }
2747
2748 eventPhaseTable.set(event, Event.NONE);
2749 currentTargetTable.delete(event, null);
2750 currentlyDispatchingEvents.delete(event);
2751
2752 return event.defaultPrevented;
2753 }
2754
2755 function dispatchCapturing(event, eventPath) {
2756 var phase;
2757
2758 for (var i = eventPath.length - 1; i > 0; i--) {
2759 var target = eventPath[i].target;
2760 var currentTarget = eventPath[i].currentTarget;
2761 if (target === currentTarget)
2762 continue;
2763
2764 phase = Event.CAPTURING_PHASE;
2765 if (!invoke(eventPath[i], event, phase))
2766 return false;
2767 }
2768
2769 return true;
2770 }
2771
2772 function dispatchAtTarget(event, eventPath) {
2773 var phase = Event.AT_TARGET;
2774 return invoke(eventPath[0], event, phase);
2775 }
2776
2777 function dispatchBubbling(event, eventPath) {
2778 var bubbles = event.bubbles;
2779 var phase;
2780
2781 for (var i = 1; i < eventPath.length; i++) {
2782 var target = eventPath[i].target;
2783 var currentTarget = eventPath[i].currentTarget;
2784 if (target === currentTarget)
2785 phase = Event.AT_TARGET;
2786 else if (bubbles && !stopImmediatePropagationTable.get(event))
2787 phase = Event.BUBBLING_PHASE;
2788 else
2789 continue;
2790
2791 if (!invoke(eventPath[i], event, phase))
2792 return;
2793 }
2794 }
2795
2796 function invoke(tuple, event, phase) {
2797 var target = tuple.target;
2798 var currentTarget = tuple.currentTarget;
2799
2800 var listeners = listenersTable.get(currentTarget);
2801 if (!listeners)
2802 return true;
2803
2804 if ('relatedTarget' in event) {
2805 var originalEvent = unwrap(event);
2806 // X-Tag sets relatedTarget on a CustomEvent. If they do that there is no
2807 // way to have relatedTarget return the adjusted target but worse is that
2808 // the originalEvent might not have a relatedTarget so we hit an assert
2809 // when we try to wrap it.
2810 if (originalEvent.relatedTarget) {
2811 var relatedTarget = wrap(originalEvent.relatedTarget);
2812
2813 var adjusted = adjustRelatedTarget(currentTarget, relatedTarget);
2814 if (adjusted === target)
2815 return true;
2816
2817 relatedTargetTable.set(event, adjusted);
2818 }
2819 }
2820
2821 eventPhaseTable.set(event, phase);
2822 var type = event.type;
2823
2824 var anyRemoved = false;
2825 targetTable.set(event, target);
2826 currentTargetTable.set(event, currentTarget);
2827
2828 for (var i = 0; i < listeners.length; i++) {
2829 var listener = listeners[i];
2830 if (listener.removed) {
2831 anyRemoved = true;
2832 continue;
2833 }
2834
2835 if (listener.type !== type ||
2836 !listener.capture && phase === Event.CAPTURING_PHASE ||
2837 listener.capture && phase === Event.BUBBLING_PHASE) {
2838 continue;
2839 }
2840
2841 try {
2842 if (typeof listener.handler === 'function')
2843 listener.handler.call(currentTarget, event);
2844 else
2845 listener.handler.handleEvent(event);
2846
2847 if (stopImmediatePropagationTable.get(event))
2848 return false;
2849
2850 } catch (ex) {
2851 if (window.onerror)
2852 window.onerror(ex.message);
2853 else
2854 console.error(ex, ex.stack);
2855 }
2856 }
2857
2858 if (anyRemoved) {
2859 var copy = listeners.slice();
2860 listeners.length = 0;
2861 for (var i = 0; i < copy.length; i++) {
2862 if (!copy[i].removed)
2863 listeners.push(copy[i]);
2864 }
2865 }
2866
2867 return !stopPropagationTable.get(event);
2868 }
2869
2870 function Listener(type, handler, capture) {
2871 this.type = type;
2872 this.handler = handler;
2873 this.capture = Boolean(capture);
2874 }
2875 Listener.prototype = {
2876 equals: function(that) {
2877 return this.handler === that.handler && this.type === that.type &&
2878 this.capture === that.capture;
2879 },
2880 get removed() {
2881 return this.handler === null;
2882 },
2883 remove: function() {
2884 this.handler = null;
2885 }
2886 };
2887
2888 var OriginalEvent = window.Event;
2889 OriginalEvent.prototype.polymerBlackList_ = {
2890 returnValue: true,
2891 // TODO(arv): keyLocation is part of KeyboardEvent but Firefox does not
2892 // support constructable KeyboardEvent so we keep it here for now.
2893 keyLocation: true
2894 };
2895
2896 /**
2897 * Creates a new Event wrapper or wraps an existin native Event object.
2898 * @param {string|Event} type
2899 * @param {Object=} options
2900 * @constructor
2901 */
2902 function Event(type, options) {
2903 if (type instanceof OriginalEvent)
2904 this.impl = type;
2905 else
2906 return wrap(constructEvent(OriginalEvent, 'Event', type, options));
2907 }
2908 Event.prototype = {
2909 get target() {
2910 return targetTable.get(this);
2911 },
2912 get currentTarget() {
2913 return currentTargetTable.get(this);
2914 },
2915 get eventPhase() {
2916 return eventPhaseTable.get(this);
2917 },
2918 get path() {
2919 var nodeList = new wrappers.NodeList();
2920 var eventPath = eventPathTable.get(this);
2921 if (eventPath) {
2922 var index = 0;
2923 var lastIndex = eventPath.length - 1;
2924 var baseRoot = rootOfNode(currentTargetTable.get(this));
2925
2926 for (var i = 0; i <= lastIndex; i++) {
2927 var currentTarget = eventPath[i].currentTarget;
2928 var currentRoot = rootOfNode(currentTarget);
2929 if (enclosedBy(baseRoot, currentRoot) &&
2930 // Make sure we do not add Window to the path.
2931 (i !== lastIndex || currentTarget instanceof wrappers.Node)) {
2932 nodeList[index++] = currentTarget;
2933 }
2934 }
2935 nodeList.length = index;
2936 }
2937 return nodeList;
2938 },
2939 stopPropagation: function() {
2940 stopPropagationTable.set(this, true);
2941 },
2942 stopImmediatePropagation: function() {
2943 stopPropagationTable.set(this, true);
2944 stopImmediatePropagationTable.set(this, true);
2945 }
2946 };
2947 registerWrapper(OriginalEvent, Event, document.createEvent('Event'));
2948
2949 function unwrapOptions(options) {
2950 if (!options || !options.relatedTarget)
2951 return options;
2952 return Object.create(options, {
2953 relatedTarget: {value: unwrap(options.relatedTarget)}
2954 });
2955 }
2956
2957 function registerGenericEvent(name, SuperEvent, prototype) {
2958 var OriginalEvent = window[name];
2959 var GenericEvent = function(type, options) {
2960 if (type instanceof OriginalEvent)
2961 this.impl = type;
2962 else
2963 return wrap(constructEvent(OriginalEvent, name, type, options));
2964 };
2965 GenericEvent.prototype = Object.create(SuperEvent.prototype);
2966 if (prototype)
2967 mixin(GenericEvent.prototype, prototype);
2968 if (OriginalEvent) {
2969 // - Old versions of Safari fails on new FocusEvent (and others?).
2970 // - IE does not support event constructors.
2971 // - createEvent('FocusEvent') throws in Firefox.
2972 // => Try the best practice solution first and fallback to the old way
2973 // if needed.
2974 try {
2975 registerWrapper(OriginalEvent, GenericEvent, new OriginalEvent('temp'));
2976 } catch (ex) {
2977 registerWrapper(OriginalEvent, GenericEvent,
2978 document.createEvent(name));
2979 }
2980 }
2981 return GenericEvent;
2982 }
2983
2984 var UIEvent = registerGenericEvent('UIEvent', Event);
2985 var CustomEvent = registerGenericEvent('CustomEvent', Event);
2986
2987 var relatedTargetProto = {
2988 get relatedTarget() {
2989 return relatedTargetTable.get(this) || wrap(unwrap(this).relatedTarget);
2990 }
2991 };
2992
2993 function getInitFunction(name, relatedTargetIndex) {
2994 return function() {
2995 arguments[relatedTargetIndex] = unwrap(arguments[relatedTargetIndex]);
2996 var impl = unwrap(this);
2997 impl[name].apply(impl, arguments);
2998 };
2999 }
3000
3001 var mouseEventProto = mixin({
3002 initMouseEvent: getInitFunction('initMouseEvent', 14)
3003 }, relatedTargetProto);
3004
3005 var focusEventProto = mixin({
3006 initFocusEvent: getInitFunction('initFocusEvent', 5)
3007 }, relatedTargetProto);
3008
3009 var MouseEvent = registerGenericEvent('MouseEvent', UIEvent, mouseEventProto);
3010 var FocusEvent = registerGenericEvent('FocusEvent', UIEvent, focusEventProto);
3011
3012 // In case the browser does not support event constructors we polyfill that
3013 // by calling `createEvent('Foo')` and `initFooEvent` where the arguments to
3014 // `initFooEvent` are derived from the registered default event init dict.
3015 var defaultInitDicts = Object.create(null);
3016
3017 var supportsEventConstructors = (function() {
3018 try {
3019 new window.FocusEvent('focus');
3020 } catch (ex) {
3021 return false;
3022 }
3023 return true;
3024 })();
3025
3026 /**
3027 * Constructs a new native event.
3028 */
3029 function constructEvent(OriginalEvent, name, type, options) {
3030 if (supportsEventConstructors)
3031 return new OriginalEvent(type, unwrapOptions(options));
3032
3033 // Create the arguments from the default dictionary.
3034 var event = unwrap(document.createEvent(name));
3035 var defaultDict = defaultInitDicts[name];
3036 var args = [type];
3037 Object.keys(defaultDict).forEach(function(key) {
3038 var v = options != null && key in options ?
3039 options[key] : defaultDict[key];
3040 if (key === 'relatedTarget')
3041 v = unwrap(v);
3042 args.push(v);
3043 });
3044 event['init' + name].apply(event, args);
3045 return event;
3046 }
3047
3048 if (!supportsEventConstructors) {
3049 var configureEventConstructor = function(name, initDict, superName) {
3050 if (superName) {
3051 var superDict = defaultInitDicts[superName];
3052 initDict = mixin(mixin({}, superDict), initDict);
3053 }
3054
3055 defaultInitDicts[name] = initDict;
3056 };
3057
3058 // The order of the default event init dictionary keys is important, the
3059 // arguments to initFooEvent is derived from that.
3060 configureEventConstructor('Event', {bubbles: false, cancelable: false});
3061 configureEventConstructor('CustomEvent', {detail: null}, 'Event');
3062 configureEventConstructor('UIEvent', {view: null, detail: 0}, 'Event');
3063 configureEventConstructor('MouseEvent', {
3064 screenX: 0,
3065 screenY: 0,
3066 clientX: 0,
3067 clientY: 0,
3068 ctrlKey: false,
3069 altKey: false,
3070 shiftKey: false,
3071 metaKey: false,
3072 button: 0,
3073 relatedTarget: null
3074 }, 'UIEvent');
3075 configureEventConstructor('FocusEvent', {relatedTarget: null}, 'UIEvent');
3076 }
3077
3078 function BeforeUnloadEvent(impl) {
3079 Event.call(this);
3080 }
3081 BeforeUnloadEvent.prototype = Object.create(Event.prototype);
3082 mixin(BeforeUnloadEvent.prototype, {
3083 get returnValue() {
3084 return this.impl.returnValue;
3085 },
3086 set returnValue(v) {
3087 this.impl.returnValue = v;
3088 }
3089 });
3090
3091 function isValidListener(fun) {
3092 if (typeof fun === 'function')
3093 return true;
3094 return fun && fun.handleEvent;
3095 }
3096
3097 function isMutationEvent(type) {
3098 switch (type) {
3099 case 'DOMAttrModified':
3100 case 'DOMAttributeNameChanged':
3101 case 'DOMCharacterDataModified':
3102 case 'DOMElementNameChanged':
3103 case 'DOMNodeInserted':
3104 case 'DOMNodeInsertedIntoDocument':
3105 case 'DOMNodeRemoved':
3106 case 'DOMNodeRemovedFromDocument':
3107 case 'DOMSubtreeModified':
3108 return true;
3109 }
3110 return false;
3111 }
3112
3113 var OriginalEventTarget = window.EventTarget;
3114
3115 /**
3116 * This represents a wrapper for an EventTarget.
3117 * @param {!EventTarget} impl The original event target.
3118 * @constructor
3119 */
3120 function EventTarget(impl) {
3121 this.impl = impl;
3122 }
3123
3124 // Node and Window have different internal type checks in WebKit so we cannot
3125 // use the same method as the original function.
3126 var methodNames = [
3127 'addEventListener',
3128 'removeEventListener',
3129 'dispatchEvent'
3130 ];
3131
3132 [Node, Window].forEach(function(constructor) {
3133 var p = constructor.prototype;
3134 methodNames.forEach(function(name) {
3135 Object.defineProperty(p, name + '_', {value: p[name]});
3136 });
3137 });
3138
3139 function getTargetToListenAt(wrapper) {
3140 if (wrapper instanceof wrappers.ShadowRoot)
3141 wrapper = wrapper.host;
3142 return unwrap(wrapper);
3143 }
3144
3145 EventTarget.prototype = {
3146 addEventListener: function(type, fun, capture) {
3147 if (!isValidListener(fun) || isMutationEvent(type))
3148 return;
3149
3150 var listener = new Listener(type, fun, capture);
3151 var listeners = listenersTable.get(this);
3152 if (!listeners) {
3153 listeners = [];
3154 listenersTable.set(this, listeners);
3155 } else {
3156 // Might have a duplicate.
3157 for (var i = 0; i < listeners.length; i++) {
3158 if (listener.equals(listeners[i]))
3159 return;
3160 }
3161 }
3162
3163 listeners.push(listener);
3164
3165 var target = getTargetToListenAt(this);
3166 target.addEventListener_(type, dispatchOriginalEvent, true);
3167 },
3168 removeEventListener: function(type, fun, capture) {
3169 capture = Boolean(capture);
3170 var listeners = listenersTable.get(this);
3171 if (!listeners)
3172 return;
3173 var count = 0, found = false;
3174 for (var i = 0; i < listeners.length; i++) {
3175 if (listeners[i].type === type && listeners[i].capture === capture) {
3176 count++;
3177 if (listeners[i].handler === fun) {
3178 found = true;
3179 listeners[i].remove();
3180 }
3181 }
3182 }
3183
3184 if (found && count === 1) {
3185 var target = getTargetToListenAt(this);
3186 target.removeEventListener_(type, dispatchOriginalEvent, true);
3187 }
3188 },
3189 dispatchEvent: function(event) {
3190 // We want to use the native dispatchEvent because it triggers the default
3191 // actions (like checking a checkbox). However, if there are no listeners
3192 // in the composed tree then there are no events that will trigger and
3193 // listeners in the non composed tree that are part of the event path are
3194 // not notified.
3195 //
3196 // If we find out that there are no listeners in the composed tree we add
3197 // a temporary listener to the target which makes us get called back even
3198 // in that case.
3199
3200 var nativeEvent = unwrap(event);
3201 var eventType = nativeEvent.type;
3202
3203 // Allow dispatching the same event again. This is safe because if user
3204 // code calls this during an existing dispatch of the same event the
3205 // native dispatchEvent throws (that is required by the spec).
3206 handledEventsTable.set(nativeEvent, false);
3207
3208 // Force rendering since we prefer native dispatch and that works on the
3209 // composed tree.
3210 scope.renderAllPending();
3211
3212 var tempListener;
3213 if (!hasListenerInAncestors(this, eventType)) {
3214 tempListener = function() {};
3215 this.addEventListener(eventType, tempListener, true);
3216 }
3217
3218 try {
3219 return unwrap(this).dispatchEvent_(nativeEvent);
3220 } finally {
3221 if (tempListener)
3222 this.removeEventListener(eventType, tempListener, true);
3223 }
3224 }
3225 };
3226
3227 function hasListener(node, type) {
3228 var listeners = listenersTable.get(node);
3229 if (listeners) {
3230 for (var i = 0; i < listeners.length; i++) {
3231 if (!listeners[i].removed && listeners[i].type === type)
3232 return true;
3233 }
3234 }
3235 return false;
3236 }
3237
3238 function hasListenerInAncestors(target, type) {
3239 for (var node = unwrap(target); node; node = node.parentNode) {
3240 if (hasListener(wrap(node), type))
3241 return true;
3242 }
3243 return false;
3244 }
3245
3246 if (OriginalEventTarget)
3247 registerWrapper(OriginalEventTarget, EventTarget);
3248
3249 function wrapEventTargetMethods(constructors) {
3250 forwardMethodsToWrapper(constructors, methodNames);
3251 }
3252
3253 var originalElementFromPoint = document.elementFromPoint;
3254
3255 function elementFromPoint(self, document, x, y) {
3256 scope.renderAllPending();
3257
3258 var element = wrap(originalElementFromPoint.call(document.impl, x, y));
3259 var targets = retarget(element, this)
3260 for (var i = 0; i < targets.length; i++) {
3261 var target = targets[i];
3262 if (target.currentTarget === self)
3263 return target.target;
3264 }
3265 return null;
3266 }
3267
3268 /**
3269 * Returns a function that is to be used as a getter for `onfoo` properties.
3270 * @param {string} name
3271 * @return {Function}
3272 */
3273 function getEventHandlerGetter(name) {
3274 return function() {
3275 var inlineEventHandlers = eventHandlersTable.get(this);
3276 return inlineEventHandlers && inlineEventHandlers[name] &&
3277 inlineEventHandlers[name].value || null;
3278 };
3279 }
3280
3281 /**
3282 * Returns a function that is to be used as a setter for `onfoo` properties.
3283 * @param {string} name
3284 * @return {Function}
3285 */
3286 function getEventHandlerSetter(name) {
3287 var eventType = name.slice(2);
3288 return function(value) {
3289 var inlineEventHandlers = eventHandlersTable.get(this);
3290 if (!inlineEventHandlers) {
3291 inlineEventHandlers = Object.create(null);
3292 eventHandlersTable.set(this, inlineEventHandlers);
3293 }
3294
3295 var old = inlineEventHandlers[name];
3296 if (old)
3297 this.removeEventListener(eventType, old.wrapped, false);
3298
3299 if (typeof value === 'function') {
3300 var wrapped = function(e) {
3301 var rv = value.call(this, e);
3302 if (rv === false)
3303 e.preventDefault();
3304 else if (name === 'onbeforeunload' && typeof rv === 'string')
3305 e.returnValue = rv;
3306 // mouseover uses true for preventDefault but preventDefault for
3307 // mouseover is ignored by browsers these day.
3308 };
3309
3310 this.addEventListener(eventType, wrapped, false);
3311 inlineEventHandlers[name] = {
3312 value: value,
3313 wrapped: wrapped
3314 };
3315 }
3316 };
3317 }
3318
3319 scope.adjustRelatedTarget = adjustRelatedTarget;
3320 scope.elementFromPoint = elementFromPoint;
3321 scope.getEventHandlerGetter = getEventHandlerGetter;
3322 scope.getEventHandlerSetter = getEventHandlerSetter;
3323 scope.wrapEventTargetMethods = wrapEventTargetMethods;
3324 scope.wrappers.BeforeUnloadEvent = BeforeUnloadEvent;
3325 scope.wrappers.CustomEvent = CustomEvent;
3326 scope.wrappers.Event = Event;
3327 scope.wrappers.EventTarget = EventTarget;
3328 scope.wrappers.FocusEvent = FocusEvent;
3329 scope.wrappers.MouseEvent = MouseEvent;
3330 scope.wrappers.UIEvent = UIEvent;
3331
3332 })(window.ShadowDOMPolyfill);
3333
3334 // Copyright 2012 The Polymer Authors. All rights reserved.
3335 // Use of this source code is goverened by a BSD-style
3336 // license that can be found in the LICENSE file.
3337
3338 (function(scope) {
3339 'use strict';
3340
3341 var wrap = scope.wrap;
3342
3343 function nonEnum(obj, prop) {
3344 Object.defineProperty(obj, prop, {enumerable: false});
3345 }
3346
3347 function NodeList() {
3348 this.length = 0;
3349 nonEnum(this, 'length');
3350 }
3351 NodeList.prototype = {
3352 item: function(index) {
3353 return this[index];
3354 }
3355 };
3356 nonEnum(NodeList.prototype, 'item');
3357
3358 function wrapNodeList(list) {
3359 if (list == null)
3360 return list;
3361 var wrapperList = new NodeList();
3362 for (var i = 0, length = list.length; i < length; i++) {
3363 wrapperList[i] = wrap(list[i]);
3364 }
3365 wrapperList.length = length;
3366 return wrapperList;
3367 }
3368
3369 function addWrapNodeListMethod(wrapperConstructor, name) {
3370 wrapperConstructor.prototype[name] = function() {
3371 return wrapNodeList(this.impl[name].apply(this.impl, arguments));
3372 };
3373 }
3374
3375 scope.wrappers.NodeList = NodeList;
3376 scope.addWrapNodeListMethod = addWrapNodeListMethod;
3377 scope.wrapNodeList = wrapNodeList;
3378
3379 })(window.ShadowDOMPolyfill);
3380
3381 // Copyright 2012 The Polymer Authors. All rights reserved.
3382 // Use of this source code is goverened by a BSD-style
3383 // license that can be found in the LICENSE file.
3384
3385 (function(scope) {
3386 'use strict';
3387
3388 var EventTarget = scope.wrappers.EventTarget;
3389 var NodeList = scope.wrappers.NodeList;
3390 var assert = scope.assert;
3391 var defineWrapGetter = scope.defineWrapGetter;
3392 var enqueueMutation = scope.enqueueMutation;
3393 var isWrapper = scope.isWrapper;
3394 var mixin = scope.mixin;
3395 var registerTransientObservers = scope.registerTransientObservers;
3396 var registerWrapper = scope.registerWrapper;
3397 var unwrap = scope.unwrap;
3398 var wrap = scope.wrap;
3399 var wrapIfNeeded = scope.wrapIfNeeded;
3400 var wrappers = scope.wrappers;
3401
3402 function assertIsNodeWrapper(node) {
3403 assert(node instanceof Node);
3404 }
3405
3406 function createOneElementNodeList(node) {
3407 var nodes = new NodeList();
3408 nodes[0] = node;
3409 nodes.length = 1;
3410 return nodes;
3411 }
3412
3413 var surpressMutations = false;
3414
3415 /**
3416 * Called before node is inserted into a node to enqueue its removal from its
3417 * old parent.
3418 * @param {!Node} node The node that is about to be removed.
3419 * @param {!Node} parent The parent node that the node is being removed from.
3420 * @param {!NodeList} nodes The collected nodes.
3421 */
3422 function enqueueRemovalForInsertedNodes(node, parent, nodes) {
3423 enqueueMutation(parent, 'childList', {
3424 removedNodes: nodes,
3425 previousSibling: node.previousSibling,
3426 nextSibling: node.nextSibling
3427 });
3428 }
3429
3430 function enqueueRemovalForInsertedDocumentFragment(df, nodes) {
3431 enqueueMutation(df, 'childList', {
3432 removedNodes: nodes
3433 });
3434 }
3435
3436 /**
3437 * Collects nodes from a DocumentFragment or a Node for removal followed
3438 * by an insertion.
3439 *
3440 * This updates the internal pointers for node, previousNode and nextNode.
3441 */
3442 function collectNodes(node, parentNode, previousNode, nextNode) {
3443 if (node instanceof DocumentFragment) {
3444 var nodes = collectNodesForDocumentFragment(node);
3445
3446 // The extra loop is to work around bugs with DocumentFragments in IE.
3447 surpressMutations = true;
3448 for (var i = nodes.length - 1; i >= 0; i--) {
3449 node.removeChild(nodes[i]);
3450 nodes[i].parentNode_ = parentNode;
3451 }
3452 surpressMutations = false;
3453
3454 for (var i = 0; i < nodes.length; i++) {
3455 nodes[i].previousSibling_ = nodes[i - 1] || previousNode;
3456 nodes[i].nextSibling_ = nodes[i + 1] || nextNode;
3457 }
3458
3459 if (previousNode)
3460 previousNode.nextSibling_ = nodes[0];
3461 if (nextNode)
3462 nextNode.previousSibling_ = nodes[nodes.length - 1];
3463
3464 return nodes;
3465 }
3466
3467 var nodes = createOneElementNodeList(node);
3468 var oldParent = node.parentNode;
3469 if (oldParent) {
3470 // This will enqueue the mutation record for the removal as needed.
3471 oldParent.removeChild(node);
3472 }
3473
3474 node.parentNode_ = parentNode;
3475 node.previousSibling_ = previousNode;
3476 node.nextSibling_ = nextNode;
3477 if (previousNode)
3478 previousNode.nextSibling_ = node;
3479 if (nextNode)
3480 nextNode.previousSibling_ = node;
3481
3482 return nodes;
3483 }
3484
3485 function collectNodesNative(node) {
3486 if (node instanceof DocumentFragment)
3487 return collectNodesForDocumentFragment(node);
3488
3489 var nodes = createOneElementNodeList(node);
3490 var oldParent = node.parentNode;
3491 if (oldParent)
3492 enqueueRemovalForInsertedNodes(node, oldParent, nodes);
3493 return nodes;
3494 }
3495
3496 function collectNodesForDocumentFragment(node) {
3497 var nodes = new NodeList();
3498 var i = 0;
3499 for (var child = node.firstChild; child; child = child.nextSibling) {
3500 nodes[i++] = child;
3501 }
3502 nodes.length = i;
3503 enqueueRemovalForInsertedDocumentFragment(node, nodes);
3504 return nodes;
3505 }
3506
3507 function snapshotNodeList(nodeList) {
3508 // NodeLists are not live at the moment so just return the same object.
3509 return nodeList;
3510 }
3511
3512 // http://dom.spec.whatwg.org/#node-is-inserted
3513 function nodeWasAdded(node) {
3514 node.nodeIsInserted_();
3515 }
3516
3517 function nodesWereAdded(nodes) {
3518 for (var i = 0; i < nodes.length; i++) {
3519 nodeWasAdded(nodes[i]);
3520 }
3521 }
3522
3523 // http://dom.spec.whatwg.org/#node-is-removed
3524 function nodeWasRemoved(node) {
3525 // Nothing at this point in time.
3526 }
3527
3528 function nodesWereRemoved(nodes) {
3529 // Nothing at this point in time.
3530 }
3531
3532 function ensureSameOwnerDocument(parent, child) {
3533 var ownerDoc = parent.nodeType === Node.DOCUMENT_NODE ?
3534 parent : parent.ownerDocument;
3535 if (ownerDoc !== child.ownerDocument)
3536 ownerDoc.adoptNode(child);
3537 }
3538
3539 function adoptNodesIfNeeded(owner, nodes) {
3540 if (!nodes.length)
3541 return;
3542
3543 var ownerDoc = owner.ownerDocument;
3544
3545 // All nodes have the same ownerDocument when we get here.
3546 if (ownerDoc === nodes[0].ownerDocument)
3547 return;
3548
3549 for (var i = 0; i < nodes.length; i++) {
3550 scope.adoptNodeNoRemove(nodes[i], ownerDoc);
3551 }
3552 }
3553
3554 function unwrapNodesForInsertion(owner, nodes) {
3555 adoptNodesIfNeeded(owner, nodes);
3556 var length = nodes.length;
3557
3558 if (length === 1)
3559 return unwrap(nodes[0]);
3560
3561 var df = unwrap(owner.ownerDocument.createDocumentFragment());
3562 for (var i = 0; i < length; i++) {
3563 df.appendChild(unwrap(nodes[i]));
3564 }
3565 return df;
3566 }
3567
3568 function clearChildNodes(wrapper) {
3569 if (wrapper.firstChild_ !== undefined) {
3570 var child = wrapper.firstChild_;
3571 while (child) {
3572 var tmp = child;
3573 child = child.nextSibling_;
3574 tmp.parentNode_ = tmp.previousSibling_ = tmp.nextSibling_ = undefined;
3575 }
3576 }
3577 wrapper.firstChild_ = wrapper.lastChild_ = undefined;
3578 }
3579
3580 function removeAllChildNodes(wrapper) {
3581 if (wrapper.invalidateShadowRenderer()) {
3582 var childWrapper = wrapper.firstChild;
3583 while (childWrapper) {
3584 assert(childWrapper.parentNode === wrapper);
3585 var nextSibling = childWrapper.nextSibling;
3586 var childNode = unwrap(childWrapper);
3587 var parentNode = childNode.parentNode;
3588 if (parentNode)
3589 originalRemoveChild.call(parentNode, childNode);
3590 childWrapper.previousSibling_ = childWrapper.nextSibling_ =
3591 childWrapper.parentNode_ = null;
3592 childWrapper = nextSibling;
3593 }
3594 wrapper.firstChild_ = wrapper.lastChild_ = null;
3595 } else {
3596 var node = unwrap(wrapper);
3597 var child = node.firstChild;
3598 var nextSibling;
3599 while (child) {
3600 nextSibling = child.nextSibling;
3601 originalRemoveChild.call(node, child);
3602 child = nextSibling;
3603 }
3604 }
3605 }
3606
3607 function invalidateParent(node) {
3608 var p = node.parentNode;
3609 return p && p.invalidateShadowRenderer();
3610 }
3611
3612 function cleanupNodes(nodes) {
3613 for (var i = 0, n; i < nodes.length; i++) {
3614 n = nodes[i];
3615 n.parentNode.removeChild(n);
3616 }
3617 }
3618
3619 var originalImportNode = document.importNode;
3620 var originalCloneNode = window.Node.prototype.cloneNode;
3621
3622 function cloneNode(node, deep, opt_doc) {
3623 var clone;
3624 if (opt_doc)
3625 clone = wrap(originalImportNode.call(opt_doc, node.impl, false));
3626 else
3627 clone = wrap(originalCloneNode.call(node.impl, false));
3628
3629 if (deep) {
3630 for (var child = node.firstChild; child; child = child.nextSibling) {
3631 clone.appendChild(cloneNode(child, true, opt_doc));
3632 }
3633
3634 if (node instanceof wrappers.HTMLTemplateElement) {
3635 var cloneContent = clone.content;
3636 for (var child = node.content.firstChild;
3637 child;
3638 child = child.nextSibling) {
3639 cloneContent.appendChild(cloneNode(child, true, opt_doc));
3640 }
3641 }
3642 }
3643 // TODO(arv): Some HTML elements also clone other data like value.
3644 return clone;
3645 }
3646
3647 var OriginalNode = window.Node;
3648
3649 /**
3650 * This represents a wrapper of a native DOM node.
3651 * @param {!Node} original The original DOM node, aka, the visual DOM node.
3652 * @constructor
3653 * @extends {EventTarget}
3654 */
3655 function Node(original) {
3656 assert(original instanceof OriginalNode);
3657
3658 EventTarget.call(this, original);
3659
3660 // These properties are used to override the visual references with the
3661 // logical ones. If the value is undefined it means that the logical is the
3662 // same as the visual.
3663
3664 /**
3665 * @type {Node|undefined}
3666 * @private
3667 */
3668 this.parentNode_ = undefined;
3669
3670 /**
3671 * @type {Node|undefined}
3672 * @private
3673 */
3674 this.firstChild_ = undefined;
3675
3676 /**
3677 * @type {Node|undefined}
3678 * @private
3679 */
3680 this.lastChild_ = undefined;
3681
3682 /**
3683 * @type {Node|undefined}
3684 * @private
3685 */
3686 this.nextSibling_ = undefined;
3687
3688 /**
3689 * @type {Node|undefined}
3690 * @private
3691 */
3692 this.previousSibling_ = undefined;
3693 }
3694
3695 var OriginalDocumentFragment = window.DocumentFragment;
3696 var originalAppendChild = OriginalNode.prototype.appendChild;
3697 var originalCompareDocumentPosition =
3698 OriginalNode.prototype.compareDocumentPosition;
3699 var originalInsertBefore = OriginalNode.prototype.insertBefore;
3700 var originalRemoveChild = OriginalNode.prototype.removeChild;
3701 var originalReplaceChild = OriginalNode.prototype.replaceChild;
3702
3703 var isIe = /Trident/.test(navigator.userAgent);
3704
3705 var removeChildOriginalHelper = isIe ?
3706 function(parent, child) {
3707 try {
3708 originalRemoveChild.call(parent, child);
3709 } catch (ex) {
3710 if (!(parent instanceof OriginalDocumentFragment))
3711 throw ex;
3712 }
3713 } :
3714 function(parent, child) {
3715 originalRemoveChild.call(parent, child);
3716 };
3717
3718 Node.prototype = Object.create(EventTarget.prototype);
3719 mixin(Node.prototype, {
3720 appendChild: function(childWrapper) {
3721 return this.insertBefore(childWrapper, null);
3722 },
3723
3724 insertBefore: function(childWrapper, refWrapper) {
3725 assertIsNodeWrapper(childWrapper);
3726
3727 var refNode;
3728 if (refWrapper) {
3729 if (isWrapper(refWrapper)) {
3730 refNode = unwrap(refWrapper);
3731 } else {
3732 refNode = refWrapper;
3733 refWrapper = wrap(refNode);
3734 }
3735 } else {
3736 refWrapper = null;
3737 refNode = null;
3738 }
3739
3740 refWrapper && assert(refWrapper.parentNode === this);
3741
3742 var nodes;
3743 var previousNode =
3744 refWrapper ? refWrapper.previousSibling : this.lastChild;
3745
3746 var useNative = !this.invalidateShadowRenderer() &&
3747 !invalidateParent(childWrapper);
3748
3749 if (useNative)
3750 nodes = collectNodesNative(childWrapper);
3751 else
3752 nodes = collectNodes(childWrapper, this, previousNode, refWrapper);
3753
3754 if (useNative) {
3755 ensureSameOwnerDocument(this, childWrapper);
3756 clearChildNodes(this);
3757 originalInsertBefore.call(this.impl, unwrap(childWrapper), refNode);
3758 } else {
3759 if (!previousNode)
3760 this.firstChild_ = nodes[0];
3761 if (!refWrapper)
3762 this.lastChild_ = nodes[nodes.length - 1];
3763
3764 var parentNode = refNode ? refNode.parentNode : this.impl;
3765
3766 // insertBefore refWrapper no matter what the parent is?
3767 if (parentNode) {
3768 originalInsertBefore.call(parentNode,
3769 unwrapNodesForInsertion(this, nodes), refNode);
3770 } else {
3771 adoptNodesIfNeeded(this, nodes);
3772 }
3773 }
3774
3775 enqueueMutation(this, 'childList', {
3776 addedNodes: nodes,
3777 nextSibling: refWrapper,
3778 previousSibling: previousNode
3779 });
3780
3781 nodesWereAdded(nodes);
3782
3783 return childWrapper;
3784 },
3785
3786 removeChild: function(childWrapper) {
3787 assertIsNodeWrapper(childWrapper);
3788 if (childWrapper.parentNode !== this) {
3789 // IE has invalid DOM trees at times.
3790 var found = false;
3791 var childNodes = this.childNodes;
3792 for (var ieChild = this.firstChild; ieChild;
3793 ieChild = ieChild.nextSibling) {
3794 if (ieChild === childWrapper) {
3795 found = true;
3796 break;
3797 }
3798 }
3799 if (!found) {
3800 // TODO(arv): DOMException
3801 throw new Error('NotFoundError');
3802 }
3803 }
3804
3805 var childNode = unwrap(childWrapper);
3806 var childWrapperNextSibling = childWrapper.nextSibling;
3807 var childWrapperPreviousSibling = childWrapper.previousSibling;
3808
3809 if (this.invalidateShadowRenderer()) {
3810 // We need to remove the real node from the DOM before updating the
3811 // pointers. This is so that that mutation event is dispatched before
3812 // the pointers have changed.
3813 var thisFirstChild = this.firstChild;
3814 var thisLastChild = this.lastChild;
3815
3816 var parentNode = childNode.parentNode;
3817 if (parentNode)
3818 removeChildOriginalHelper(parentNode, childNode);
3819
3820 if (thisFirstChild === childWrapper)
3821 this.firstChild_ = childWrapperNextSibling;
3822 if (thisLastChild === childWrapper)
3823 this.lastChild_ = childWrapperPreviousSibling;
3824 if (childWrapperPreviousSibling)
3825 childWrapperPreviousSibling.nextSibling_ = childWrapperNextSibling;
3826 if (childWrapperNextSibling) {
3827 childWrapperNextSibling.previousSibling_ =
3828 childWrapperPreviousSibling;
3829 }
3830
3831 childWrapper.previousSibling_ = childWrapper.nextSibling_ =
3832 childWrapper.parentNode_ = undefined;
3833 } else {
3834 clearChildNodes(this);
3835 removeChildOriginalHelper(this.impl, childNode);
3836 }
3837
3838 if (!surpressMutations) {
3839 enqueueMutation(this, 'childList', {
3840 removedNodes: createOneElementNodeList(childWrapper),
3841 nextSibling: childWrapperNextSibling,
3842 previousSibling: childWrapperPreviousSibling
3843 });
3844 }
3845
3846 registerTransientObservers(this, childWrapper);
3847
3848 return childWrapper;
3849 },
3850
3851 replaceChild: function(newChildWrapper, oldChildWrapper) {
3852 assertIsNodeWrapper(newChildWrapper);
3853
3854 var oldChildNode;
3855 if (isWrapper(oldChildWrapper)) {
3856 oldChildNode = unwrap(oldChildWrapper);
3857 } else {
3858 oldChildNode = oldChildWrapper;
3859 oldChildWrapper = wrap(oldChildNode);
3860 }
3861
3862 if (oldChildWrapper.parentNode !== this) {
3863 // TODO(arv): DOMException
3864 throw new Error('NotFoundError');
3865 }
3866
3867 var nextNode = oldChildWrapper.nextSibling;
3868 var previousNode = oldChildWrapper.previousSibling;
3869 var nodes;
3870
3871 var useNative = !this.invalidateShadowRenderer() &&
3872 !invalidateParent(newChildWrapper);
3873
3874 if (useNative) {
3875 nodes = collectNodesNative(newChildWrapper);
3876 } else {
3877 if (nextNode === newChildWrapper)
3878 nextNode = newChildWrapper.nextSibling;
3879 nodes = collectNodes(newChildWrapper, this, previousNode, nextNode);
3880 }
3881
3882 if (!useNative) {
3883 if (this.firstChild === oldChildWrapper)
3884 this.firstChild_ = nodes[0];
3885 if (this.lastChild === oldChildWrapper)
3886 this.lastChild_ = nodes[nodes.length - 1];
3887
3888 oldChildWrapper.previousSibling_ = oldChildWrapper.nextSibling_ =
3889 oldChildWrapper.parentNode_ = undefined;
3890
3891 // replaceChild no matter what the parent is?
3892 if (oldChildNode.parentNode) {
3893 originalReplaceChild.call(
3894 oldChildNode.parentNode,
3895 unwrapNodesForInsertion(this, nodes),
3896 oldChildNode);
3897 }
3898 } else {
3899 ensureSameOwnerDocument(this, newChildWrapper);
3900 clearChildNodes(this);
3901 originalReplaceChild.call(this.impl, unwrap(newChildWrapper),
3902 oldChildNode);
3903 }
3904
3905 enqueueMutation(this, 'childList', {
3906 addedNodes: nodes,
3907 removedNodes: createOneElementNodeList(oldChildWrapper),
3908 nextSibling: nextNode,
3909 previousSibling: previousNode
3910 });
3911
3912 nodeWasRemoved(oldChildWrapper);
3913 nodesWereAdded(nodes);
3914
3915 return oldChildWrapper;
3916 },
3917
3918 /**
3919 * Called after a node was inserted. Subclasses override this to invalidate
3920 * the renderer as needed.
3921 * @private
3922 */
3923 nodeIsInserted_: function() {
3924 for (var child = this.firstChild; child; child = child.nextSibling) {
3925 child.nodeIsInserted_();
3926 }
3927 },
3928
3929 hasChildNodes: function() {
3930 return this.firstChild !== null;
3931 },
3932
3933 /** @type {Node} */
3934 get parentNode() {
3935 // If the parentNode has not been overridden, use the original parentNode.
3936 return this.parentNode_ !== undefined ?
3937 this.parentNode_ : wrap(this.impl.parentNode);
3938 },
3939
3940 /** @type {Node} */
3941 get firstChild() {
3942 return this.firstChild_ !== undefined ?
3943 this.firstChild_ : wrap(this.impl.firstChild);
3944 },
3945
3946 /** @type {Node} */
3947 get lastChild() {
3948 return this.lastChild_ !== undefined ?
3949 this.lastChild_ : wrap(this.impl.lastChild);
3950 },
3951
3952 /** @type {Node} */
3953 get nextSibling() {
3954 return this.nextSibling_ !== undefined ?
3955 this.nextSibling_ : wrap(this.impl.nextSibling);
3956 },
3957
3958 /** @type {Node} */
3959 get previousSibling() {
3960 return this.previousSibling_ !== undefined ?
3961 this.previousSibling_ : wrap(this.impl.previousSibling);
3962 },
3963
3964 get parentElement() {
3965 var p = this.parentNode;
3966 while (p && p.nodeType !== Node.ELEMENT_NODE) {
3967 p = p.parentNode;
3968 }
3969 return p;
3970 },
3971
3972 get textContent() {
3973 // TODO(arv): This should fallback to this.impl.textContent if there
3974 // are no shadow trees below or above the context node.
3975 var s = '';
3976 for (var child = this.firstChild; child; child = child.nextSibling) {
3977 if (child.nodeType != Node.COMMENT_NODE) {
3978 s += child.textContent;
3979 }
3980 }
3981 return s;
3982 },
3983 set textContent(textContent) {
3984 var removedNodes = snapshotNodeList(this.childNodes);
3985
3986 if (this.invalidateShadowRenderer()) {
3987 removeAllChildNodes(this);
3988 if (textContent !== '') {
3989 var textNode = this.impl.ownerDocument.createTextNode(textContent);
3990 this.appendChild(textNode);
3991 }
3992 } else {
3993 clearChildNodes(this);
3994 this.impl.textContent = textContent;
3995 }
3996
3997 var addedNodes = snapshotNodeList(this.childNodes);
3998
3999 enqueueMutation(this, 'childList', {
4000 addedNodes: addedNodes,
4001 removedNodes: removedNodes
4002 });
4003
4004 nodesWereRemoved(removedNodes);
4005 nodesWereAdded(addedNodes);
4006 },
4007
4008 get childNodes() {
4009 var wrapperList = new NodeList();
4010 var i = 0;
4011 for (var child = this.firstChild; child; child = child.nextSibling) {
4012 wrapperList[i++] = child;
4013 }
4014 wrapperList.length = i;
4015 return wrapperList;
4016 },
4017
4018 cloneNode: function(deep) {
4019 return cloneNode(this, deep);
4020 },
4021
4022 contains: function(child) {
4023 if (!child)
4024 return false;
4025
4026 child = wrapIfNeeded(child);
4027
4028 // TODO(arv): Optimize using ownerDocument etc.
4029 if (child === this)
4030 return true;
4031 var parentNode = child.parentNode;
4032 if (!parentNode)
4033 return false;
4034 return this.contains(parentNode);
4035 },
4036
4037 compareDocumentPosition: function(otherNode) {
4038 // This only wraps, it therefore only operates on the composed DOM and not
4039 // the logical DOM.
4040 return originalCompareDocumentPosition.call(this.impl, unwrap(otherNode));
4041 },
4042
4043 normalize: function() {
4044 var nodes = snapshotNodeList(this.childNodes);
4045 var remNodes = [];
4046 var s = '';
4047 var modNode;
4048
4049 for (var i = 0, n; i < nodes.length; i++) {
4050 n = nodes[i];
4051 if (n.nodeType === Node.TEXT_NODE) {
4052 if (!modNode && !n.data.length)
4053 this.removeNode(n);
4054 else if (!modNode)
4055 modNode = n;
4056 else {
4057 s += n.data;
4058 remNodes.push(n);
4059 }
4060 } else {
4061 if (modNode && remNodes.length) {
4062 modNode.data += s;
4063 cleanUpNodes(remNodes);
4064 }
4065 remNodes = [];
4066 s = '';
4067 modNode = null;
4068 if (n.childNodes.length)
4069 n.normalize();
4070 }
4071 }
4072
4073 // handle case where >1 text nodes are the last children
4074 if (modNode && remNodes.length) {
4075 modNode.data += s;
4076 cleanupNodes(remNodes);
4077 }
4078 }
4079 });
4080
4081 defineWrapGetter(Node, 'ownerDocument');
4082
4083 // We use a DocumentFragment as a base and then delete the properties of
4084 // DocumentFragment.prototype from the wrapper Node. Since delete makes
4085 // objects slow in some JS engines we recreate the prototype object.
4086 registerWrapper(OriginalNode, Node, document.createDocumentFragment());
4087 delete Node.prototype.querySelector;
4088 delete Node.prototype.querySelectorAll;
4089 Node.prototype = mixin(Object.create(EventTarget.prototype), Node.prototype);
4090
4091 scope.nodeWasAdded = nodeWasAdded;
4092 scope.nodeWasRemoved = nodeWasRemoved;
4093 scope.nodesWereAdded = nodesWereAdded;
4094 scope.nodesWereRemoved = nodesWereRemoved;
4095 scope.snapshotNodeList = snapshotNodeList;
4096 scope.wrappers.Node = Node;
4097 scope.cloneNode = cloneNode;
4098
4099 })(window.ShadowDOMPolyfill);
4100
4101 // Copyright 2013 The Polymer Authors. All rights reserved.
4102 // Use of this source code is governed by a BSD-style
4103 // license that can be found in the LICENSE file.
4104
4105 (function(scope) {
4106 'use strict';
4107
4108 function findOne(node, selector) {
4109 var m, el = node.firstElementChild;
4110 while (el) {
4111 if (el.matches(selector))
4112 return el;
4113 m = findOne(el, selector);
4114 if (m)
4115 return m;
4116 el = el.nextElementSibling;
4117 }
4118 return null;
4119 }
4120
4121 function findAll(node, selector, results) {
4122 var el = node.firstElementChild;
4123 while (el) {
4124 if (el.matches(selector))
4125 results[results.length++] = el;
4126 findAll(el, selector, results);
4127 el = el.nextElementSibling;
4128 }
4129 return results;
4130 }
4131
4132 // find and findAll will only match Simple Selectors,
4133 // Structural Pseudo Classes are not guarenteed to be correct
4134 // http://www.w3.org/TR/css3-selectors/#simple-selectors
4135
4136 var SelectorsInterface = {
4137 querySelector: function(selector) {
4138 return findOne(this, selector);
4139 },
4140 querySelectorAll: function(selector) {
4141 return findAll(this, selector, new NodeList())
4142 }
4143 };
4144
4145 var GetElementsByInterface = {
4146 getElementsByTagName: function(tagName) {
4147 // TODO(arv): Check tagName?
4148 return this.querySelectorAll(tagName);
4149 },
4150 getElementsByClassName: function(className) {
4151 // TODO(arv): Check className?
4152 return this.querySelectorAll('.' + className);
4153 },
4154 getElementsByTagNameNS: function(ns, tagName) {
4155 if (ns === '*')
4156 return this.getElementsByTagName(tagName);
4157
4158 // TODO(arv): Check tagName?
4159 var result = new NodeList;
4160 var els = this.getElementsByTagName(tagName);
4161 for (var i = 0, j = 0; i < els.length; i++) {
4162 if (els[i].namespaceURI === ns)
4163 result[j++] = els[i];
4164 }
4165 result.length = j;
4166 return result;
4167 }
4168 };
4169
4170 scope.GetElementsByInterface = GetElementsByInterface;
4171 scope.SelectorsInterface = SelectorsInterface;
4172
4173 })(window.ShadowDOMPolyfill);
4174
4175 // Copyright 2013 The Polymer Authors. All rights reserved.
4176 // Use of this source code is goverened by a BSD-style
4177 // license that can be found in the LICENSE file.
4178
4179 (function(scope) {
4180 'use strict';
4181
4182 var NodeList = scope.wrappers.NodeList;
4183
4184 function forwardElement(node) {
4185 while (node && node.nodeType !== Node.ELEMENT_NODE) {
4186 node = node.nextSibling;
4187 }
4188 return node;
4189 }
4190
4191 function backwardsElement(node) {
4192 while (node && node.nodeType !== Node.ELEMENT_NODE) {
4193 node = node.previousSibling;
4194 }
4195 return node;
4196 }
4197
4198 var ParentNodeInterface = {
4199 get firstElementChild() {
4200 return forwardElement(this.firstChild);
4201 },
4202
4203 get lastElementChild() {
4204 return backwardsElement(this.lastChild);
4205 },
4206
4207 get childElementCount() {
4208 var count = 0;
4209 for (var child = this.firstElementChild;
4210 child;
4211 child = child.nextElementSibling) {
4212 count++;
4213 }
4214 return count;
4215 },
4216
4217 get children() {
4218 var wrapperList = new NodeList();
4219 var i = 0;
4220 for (var child = this.firstElementChild;
4221 child;
4222 child = child.nextElementSibling) {
4223 wrapperList[i++] = child;
4224 }
4225 wrapperList.length = i;
4226 return wrapperList;
4227 }
4228 };
4229
4230 var ChildNodeInterface = {
4231 get nextElementSibling() {
4232 return forwardElement(this.nextSibling);
4233 },
4234
4235 get previousElementSibling() {
4236 return backwardsElement(this.previousSibling);
4237 }
4238 };
4239
4240 scope.ChildNodeInterface = ChildNodeInterface;
4241 scope.ParentNodeInterface = ParentNodeInterface;
4242
4243 })(window.ShadowDOMPolyfill);
4244
4245 // Copyright 2013 The Polymer Authors. All rights reserved.
4246 // Use of this source code is goverened by a BSD-style
4247 // license that can be found in the LICENSE file.
4248
4249 (function(scope) {
4250 'use strict';
4251
4252 var ChildNodeInterface = scope.ChildNodeInterface;
4253 var Node = scope.wrappers.Node;
4254 var enqueueMutation = scope.enqueueMutation;
4255 var mixin = scope.mixin;
4256 var registerWrapper = scope.registerWrapper;
4257
4258 var OriginalCharacterData = window.CharacterData;
4259
4260 function CharacterData(node) {
4261 Node.call(this, node);
4262 }
4263 CharacterData.prototype = Object.create(Node.prototype);
4264 mixin(CharacterData.prototype, {
4265 get textContent() {
4266 return this.data;
4267 },
4268 set textContent(value) {
4269 this.data = value;
4270 },
4271 get data() {
4272 return this.impl.data;
4273 },
4274 set data(value) {
4275 var oldValue = this.impl.data;
4276 enqueueMutation(this, 'characterData', {
4277 oldValue: oldValue
4278 });
4279 this.impl.data = value;
4280 }
4281 });
4282
4283 mixin(CharacterData.prototype, ChildNodeInterface);
4284
4285 registerWrapper(OriginalCharacterData, CharacterData,
4286 document.createTextNode(''));
4287
4288 scope.wrappers.CharacterData = CharacterData;
4289 })(window.ShadowDOMPolyfill);
4290
4291 // Copyright 2014 The Polymer Authors. All rights reserved.
4292 // Use of this source code is goverened by a BSD-style
4293 // license that can be found in the LICENSE file.
4294
4295 (function(scope) {
4296 'use strict';
4297
4298 var CharacterData = scope.wrappers.CharacterData;
4299 var enqueueMutation = scope.enqueueMutation;
4300 var mixin = scope.mixin;
4301 var registerWrapper = scope.registerWrapper;
4302
4303 function toUInt32(x) {
4304 return x >>> 0;
4305 }
4306
4307 var OriginalText = window.Text;
4308
4309 function Text(node) {
4310 CharacterData.call(this, node);
4311 }
4312 Text.prototype = Object.create(CharacterData.prototype);
4313 mixin(Text.prototype, {
4314 splitText: function(offset) {
4315 offset = toUInt32(offset);
4316 var s = this.data;
4317 if (offset > s.length)
4318 throw new Error('IndexSizeError');
4319 var head = s.slice(0, offset);
4320 var tail = s.slice(offset);
4321 this.data = head;
4322 var newTextNode = this.ownerDocument.createTextNode(tail);
4323 if (this.parentNode)
4324 this.parentNode.insertBefore(newTextNode, this.nextSibling);
4325 return newTextNode;
4326 }
4327 });
4328
4329 registerWrapper(OriginalText, Text, document.createTextNode(''));
4330
4331 scope.wrappers.Text = Text;
4332 })(window.ShadowDOMPolyfill);
4333
4334 // Copyright 2013 The Polymer Authors. All rights reserved.
4335 // Use of this source code is goverened by a BSD-style
4336 // license that can be found in the LICENSE file.
4337
4338 (function(scope) {
4339 'use strict';
4340
4341 var ChildNodeInterface = scope.ChildNodeInterface;
4342 var GetElementsByInterface = scope.GetElementsByInterface;
4343 var Node = scope.wrappers.Node;
4344 var ParentNodeInterface = scope.ParentNodeInterface;
4345 var SelectorsInterface = scope.SelectorsInterface;
4346 var addWrapNodeListMethod = scope.addWrapNodeListMethod;
4347 var enqueueMutation = scope.enqueueMutation;
4348 var mixin = scope.mixin;
4349 var oneOf = scope.oneOf;
4350 var registerWrapper = scope.registerWrapper;
4351 var wrappers = scope.wrappers;
4352
4353 var OriginalElement = window.Element;
4354
4355 var matchesNames = [
4356 'matches', // needs to come first.
4357 'mozMatchesSelector',
4358 'msMatchesSelector',
4359 'webkitMatchesSelector',
4360 ].filter(function(name) {
4361 return OriginalElement.prototype[name];
4362 });
4363
4364 var matchesName = matchesNames[0];
4365
4366 var originalMatches = OriginalElement.prototype[matchesName];
4367
4368 function invalidateRendererBasedOnAttribute(element, name) {
4369 // Only invalidate if parent node is a shadow host.
4370 var p = element.parentNode;
4371 if (!p || !p.shadowRoot)
4372 return;
4373
4374 var renderer = scope.getRendererForHost(p);
4375 if (renderer.dependsOnAttribute(name))
4376 renderer.invalidate();
4377 }
4378
4379 function enqueAttributeChange(element, name, oldValue) {
4380 // This is not fully spec compliant. We should use localName (which might
4381 // have a different case than name) and the namespace (which requires us
4382 // to get the Attr object).
4383 enqueueMutation(element, 'attributes', {
4384 name: name,
4385 namespace: null,
4386 oldValue: oldValue
4387 });
4388 }
4389
4390 function Element(node) {
4391 Node.call(this, node);
4392 }
4393 Element.prototype = Object.create(Node.prototype);
4394 mixin(Element.prototype, {
4395 createShadowRoot: function() {
4396 var newShadowRoot = new wrappers.ShadowRoot(this);
4397 this.impl.polymerShadowRoot_ = newShadowRoot;
4398
4399 var renderer = scope.getRendererForHost(this);
4400 renderer.invalidate();
4401
4402 return newShadowRoot;
4403 },
4404
4405 get shadowRoot() {
4406 return this.impl.polymerShadowRoot_ || null;
4407 },
4408
4409 setAttribute: function(name, value) {
4410 var oldValue = this.impl.getAttribute(name);
4411 this.impl.setAttribute(name, value);
4412 enqueAttributeChange(this, name, oldValue);
4413 invalidateRendererBasedOnAttribute(this, name);
4414 },
4415
4416 removeAttribute: function(name) {
4417 var oldValue = this.impl.getAttribute(name);
4418 this.impl.removeAttribute(name);
4419 enqueAttributeChange(this, name, oldValue);
4420 invalidateRendererBasedOnAttribute(this, name);
4421 },
4422
4423 matches: function(selector) {
4424 return originalMatches.call(this.impl, selector);
4425 }
4426 });
4427
4428 matchesNames.forEach(function(name) {
4429 if (name !== 'matches') {
4430 Element.prototype[name] = function(selector) {
4431 return this.matches(selector);
4432 };
4433 }
4434 });
4435
4436 if (OriginalElement.prototype.webkitCreateShadowRoot) {
4437 Element.prototype.webkitCreateShadowRoot =
4438 Element.prototype.createShadowRoot;
4439 }
4440
4441 /**
4442 * Useful for generating the accessor pair for a property that reflects an
4443 * attribute.
4444 */
4445 function setterDirtiesAttribute(prototype, propertyName, opt_attrName) {
4446 var attrName = opt_attrName || propertyName;
4447 Object.defineProperty(prototype, propertyName, {
4448 get: function() {
4449 return this.impl[propertyName];
4450 },
4451 set: function(v) {
4452 this.impl[propertyName] = v;
4453 invalidateRendererBasedOnAttribute(this, attrName);
4454 },
4455 configurable: true,
4456 enumerable: true
4457 });
4458 }
4459
4460 setterDirtiesAttribute(Element.prototype, 'id');
4461 setterDirtiesAttribute(Element.prototype, 'className', 'class');
4462
4463 mixin(Element.prototype, ChildNodeInterface);
4464 mixin(Element.prototype, GetElementsByInterface);
4465 mixin(Element.prototype, ParentNodeInterface);
4466 mixin(Element.prototype, SelectorsInterface);
4467
4468 registerWrapper(OriginalElement, Element,
4469 document.createElementNS(null, 'x'));
4470
4471 // TODO(arv): Export setterDirtiesAttribute and apply it to more bindings
4472 // that reflect attributes.
4473 scope.matchesNames = matchesNames;
4474 scope.wrappers.Element = Element;
4475 })(window.ShadowDOMPolyfill);
4476
4477 // Copyright 2013 The Polymer Authors. All rights reserved.
4478 // Use of this source code is goverened by a BSD-style
4479 // license that can be found in the LICENSE file.
4480
4481 (function(scope) {
4482 'use strict';
4483
4484 var Element = scope.wrappers.Element;
4485 var defineGetter = scope.defineGetter;
4486 var enqueueMutation = scope.enqueueMutation;
4487 var mixin = scope.mixin;
4488 var nodesWereAdded = scope.nodesWereAdded;
4489 var nodesWereRemoved = scope.nodesWereRemoved;
4490 var registerWrapper = scope.registerWrapper;
4491 var snapshotNodeList = scope.snapshotNodeList;
4492 var unwrap = scope.unwrap;
4493 var wrap = scope.wrap;
4494 var wrappers = scope.wrappers;
4495
4496 /////////////////////////////////////////////////////////////////////////////
4497 // innerHTML and outerHTML
4498
4499 // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-end.html#es capingString
4500 var escapeAttrRegExp = /[&\u00A0"]/g;
4501 var escapeDataRegExp = /[&\u00A0<>]/g;
4502
4503 function escapeReplace(c) {
4504 switch (c) {
4505 case '&':
4506 return '&amp;';
4507 case '<':
4508 return '&lt;';
4509 case '>':
4510 return '&gt;';
4511 case '"':
4512 return '&quot;'
4513 case '\u00A0':
4514 return '&nbsp;';
4515 }
4516 }
4517
4518 function escapeAttr(s) {
4519 return s.replace(escapeAttrRegExp, escapeReplace);
4520 }
4521
4522 function escapeData(s) {
4523 return s.replace(escapeDataRegExp, escapeReplace);
4524 }
4525
4526 function makeSet(arr) {
4527 var set = {};
4528 for (var i = 0; i < arr.length; i++) {
4529 set[arr[i]] = true;
4530 }
4531 return set;
4532 }
4533
4534 // http://www.whatwg.org/specs/web-apps/current-work/#void-elements
4535 var voidElements = makeSet([
4536 'area',
4537 'base',
4538 'br',
4539 'col',
4540 'command',
4541 'embed',
4542 'hr',
4543 'img',
4544 'input',
4545 'keygen',
4546 'link',
4547 'meta',
4548 'param',
4549 'source',
4550 'track',
4551 'wbr'
4552 ]);
4553
4554 var plaintextParents = makeSet([
4555 'style',
4556 'script',
4557 'xmp',
4558 'iframe',
4559 'noembed',
4560 'noframes',
4561 'plaintext',
4562 'noscript'
4563 ]);
4564
4565 function getOuterHTML(node, parentNode) {
4566 switch (node.nodeType) {
4567 case Node.ELEMENT_NODE:
4568 var tagName = node.tagName.toLowerCase();
4569 var s = '<' + tagName;
4570 var attrs = node.attributes;
4571 for (var i = 0, attr; attr = attrs[i]; i++) {
4572 s += ' ' + attr.name + '="' + escapeAttr(attr.value) + '"';
4573 }
4574 s += '>';
4575 if (voidElements[tagName])
4576 return s;
4577
4578 return s + getInnerHTML(node) + '</' + tagName + '>';
4579
4580 case Node.TEXT_NODE:
4581 var data = node.data;
4582 if (parentNode && plaintextParents[parentNode.localName])
4583 return data;
4584 return escapeData(data);
4585
4586 case Node.COMMENT_NODE:
4587 return '<!--' + node.data + '-->';
4588
4589 default:
4590 console.error(node);
4591 throw new Error('not implemented');
4592 }
4593 }
4594
4595 function getInnerHTML(node) {
4596 if (node instanceof wrappers.HTMLTemplateElement)
4597 node = node.content;
4598
4599 var s = '';
4600 for (var child = node.firstChild; child; child = child.nextSibling) {
4601 s += getOuterHTML(child, node);
4602 }
4603 return s;
4604 }
4605
4606 function setInnerHTML(node, value, opt_tagName) {
4607 var tagName = opt_tagName || 'div';
4608 node.textContent = '';
4609 var tempElement = unwrap(node.ownerDocument.createElement(tagName));
4610 tempElement.innerHTML = value;
4611 var firstChild;
4612 while (firstChild = tempElement.firstChild) {
4613 node.appendChild(wrap(firstChild));
4614 }
4615 }
4616
4617 // IE11 does not have MSIE in the user agent string.
4618 var oldIe = /MSIE/.test(navigator.userAgent);
4619
4620 var OriginalHTMLElement = window.HTMLElement;
4621 var OriginalHTMLTemplateElement = window.HTMLTemplateElement;
4622
4623 function HTMLElement(node) {
4624 Element.call(this, node);
4625 }
4626 HTMLElement.prototype = Object.create(Element.prototype);
4627 mixin(HTMLElement.prototype, {
4628 get innerHTML() {
4629 return getInnerHTML(this);
4630 },
4631 set innerHTML(value) {
4632 // IE9 does not handle set innerHTML correctly on plaintextParents. It
4633 // creates element children. For example
4634 //
4635 // scriptElement.innerHTML = '<a>test</a>'
4636 //
4637 // Creates a single HTMLAnchorElement child.
4638 if (oldIe && plaintextParents[this.localName]) {
4639 this.textContent = value;
4640 return;
4641 }
4642
4643 var removedNodes = snapshotNodeList(this.childNodes);
4644
4645 if (this.invalidateShadowRenderer()) {
4646 if (this instanceof wrappers.HTMLTemplateElement)
4647 setInnerHTML(this.content, value);
4648 else
4649 setInnerHTML(this, value, this.tagName);
4650
4651 // If we have a non native template element we need to handle this
4652 // manually since setting impl.innerHTML would add the html as direct
4653 // children and not be moved over to the content fragment.
4654 } else if (!OriginalHTMLTemplateElement &&
4655 this instanceof wrappers.HTMLTemplateElement) {
4656 setInnerHTML(this.content, value);
4657 } else {
4658 this.impl.innerHTML = value;
4659 }
4660
4661 var addedNodes = snapshotNodeList(this.childNodes);
4662
4663 enqueueMutation(this, 'childList', {
4664 addedNodes: addedNodes,
4665 removedNodes: removedNodes
4666 });
4667
4668 nodesWereRemoved(removedNodes);
4669 nodesWereAdded(addedNodes);
4670 },
4671
4672 get outerHTML() {
4673 return getOuterHTML(this, this.parentNode);
4674 },
4675 set outerHTML(value) {
4676 var p = this.parentNode;
4677 if (p) {
4678 p.invalidateShadowRenderer();
4679 var df = frag(p, value);
4680 p.replaceChild(df, this);
4681 }
4682 },
4683
4684 insertAdjacentHTML: function(position, text) {
4685 var contextElement, refNode;
4686 switch (String(position).toLowerCase()) {
4687 case 'beforebegin':
4688 contextElement = this.parentNode;
4689 refNode = this;
4690 break;
4691 case 'afterend':
4692 contextElement = this.parentNode;
4693 refNode = this.nextSibling;
4694 break;
4695 case 'afterbegin':
4696 contextElement = this;
4697 refNode = this.firstChild;
4698 break;
4699 case 'beforeend':
4700 contextElement = this;
4701 refNode = null;
4702 break;
4703 default:
4704 return;
4705 }
4706
4707 var df = frag(contextElement, text);
4708 contextElement.insertBefore(df, refNode);
4709 }
4710 });
4711
4712 function frag(contextElement, html) {
4713 // TODO(arv): This does not work with SVG and other non HTML elements.
4714 var p = unwrap(contextElement.cloneNode(false));
4715 p.innerHTML = html;
4716 var df = unwrap(document.createDocumentFragment());
4717 var c;
4718 while (c = p.firstChild) {
4719 df.appendChild(c);
4720 }
4721 return wrap(df);
4722 }
4723
4724 function getter(name) {
4725 return function() {
4726 scope.renderAllPending();
4727 return this.impl[name];
4728 };
4729 }
4730
4731 function getterRequiresRendering(name) {
4732 defineGetter(HTMLElement, name, getter(name));
4733 }
4734
4735 [
4736 'clientHeight',
4737 'clientLeft',
4738 'clientTop',
4739 'clientWidth',
4740 'offsetHeight',
4741 'offsetLeft',
4742 'offsetTop',
4743 'offsetWidth',
4744 'scrollHeight',
4745 'scrollWidth',
4746 ].forEach(getterRequiresRendering);
4747
4748 function getterAndSetterRequiresRendering(name) {
4749 Object.defineProperty(HTMLElement.prototype, name, {
4750 get: getter(name),
4751 set: function(v) {
4752 scope.renderAllPending();
4753 this.impl[name] = v;
4754 },
4755 configurable: true,
4756 enumerable: true
4757 });
4758 }
4759
4760 [
4761 'scrollLeft',
4762 'scrollTop',
4763 ].forEach(getterAndSetterRequiresRendering);
4764
4765 function methodRequiresRendering(name) {
4766 Object.defineProperty(HTMLElement.prototype, name, {
4767 value: function() {
4768 scope.renderAllPending();
4769 return this.impl[name].apply(this.impl, arguments);
4770 },
4771 configurable: true,
4772 enumerable: true
4773 });
4774 }
4775
4776 [
4777 'getBoundingClientRect',
4778 'getClientRects',
4779 'scrollIntoView'
4780 ].forEach(methodRequiresRendering);
4781
4782 // HTMLElement is abstract so we use a subclass that has no members.
4783 registerWrapper(OriginalHTMLElement, HTMLElement,
4784 document.createElement('b'));
4785
4786 scope.wrappers.HTMLElement = HTMLElement;
4787
4788 // TODO: Find a better way to share these two with WrapperShadowRoot.
4789 scope.getInnerHTML = getInnerHTML;
4790 scope.setInnerHTML = setInnerHTML
4791 })(window.ShadowDOMPolyfill);
4792
4793 // Copyright 2013 The Polymer Authors. All rights reserved.
4794 // Use of this source code is goverened by a BSD-style
4795 // license that can be found in the LICENSE file.
4796
4797 (function(scope) {
4798 'use strict';
4799
4800 var HTMLElement = scope.wrappers.HTMLElement;
4801 var mixin = scope.mixin;
4802 var registerWrapper = scope.registerWrapper;
4803 var wrap = scope.wrap;
4804
4805 var OriginalHTMLCanvasElement = window.HTMLCanvasElement;
4806
4807 function HTMLCanvasElement(node) {
4808 HTMLElement.call(this, node);
4809 }
4810 HTMLCanvasElement.prototype = Object.create(HTMLElement.prototype);
4811
4812 mixin(HTMLCanvasElement.prototype, {
4813 getContext: function() {
4814 var context = this.impl.getContext.apply(this.impl, arguments);
4815 return context && wrap(context);
4816 }
4817 });
4818
4819 registerWrapper(OriginalHTMLCanvasElement, HTMLCanvasElement,
4820 document.createElement('canvas'));
4821
4822 scope.wrappers.HTMLCanvasElement = HTMLCanvasElement;
4823 })(window.ShadowDOMPolyfill);
4824
4825 // Copyright 2013 The Polymer Authors. All rights reserved.
4826 // Use of this source code is goverened by a BSD-style
4827 // license that can be found in the LICENSE file.
4828
4829 (function(scope) {
4830 'use strict';
4831
4832 var HTMLElement = scope.wrappers.HTMLElement;
4833 var mixin = scope.mixin;
4834 var registerWrapper = scope.registerWrapper;
4835
4836 var OriginalHTMLContentElement = window.HTMLContentElement;
4837
4838 function HTMLContentElement(node) {
4839 HTMLElement.call(this, node);
4840 }
4841 HTMLContentElement.prototype = Object.create(HTMLElement.prototype);
4842 mixin(HTMLContentElement.prototype, {
4843 get select() {
4844 return this.getAttribute('select');
4845 },
4846 set select(value) {
4847 this.setAttribute('select', value);
4848 },
4849
4850 setAttribute: function(n, v) {
4851 HTMLElement.prototype.setAttribute.call(this, n, v);
4852 if (String(n).toLowerCase() === 'select')
4853 this.invalidateShadowRenderer(true);
4854 }
4855
4856 // getDistributedNodes is added in ShadowRenderer
4857
4858 // TODO: attribute boolean resetStyleInheritance;
4859 });
4860
4861 if (OriginalHTMLContentElement)
4862 registerWrapper(OriginalHTMLContentElement, HTMLContentElement);
4863
4864 scope.wrappers.HTMLContentElement = HTMLContentElement;
4865 })(window.ShadowDOMPolyfill);
4866
4867 // Copyright 2013 The Polymer Authors. All rights reserved.
4868 // Use of this source code is goverened by a BSD-style
4869 // license that can be found in the LICENSE file.
4870
4871 (function(scope) {
4872 'use strict';
4873
4874 var HTMLElement = scope.wrappers.HTMLElement;
4875 var registerWrapper = scope.registerWrapper;
4876 var unwrap = scope.unwrap;
4877 var rewrap = scope.rewrap;
4878
4879 var OriginalHTMLImageElement = window.HTMLImageElement;
4880
4881 function HTMLImageElement(node) {
4882 HTMLElement.call(this, node);
4883 }
4884 HTMLImageElement.prototype = Object.create(HTMLElement.prototype);
4885
4886 registerWrapper(OriginalHTMLImageElement, HTMLImageElement,
4887 document.createElement('img'));
4888
4889 function Image(width, height) {
4890 if (!(this instanceof Image)) {
4891 throw new TypeError(
4892 'DOM object constructor cannot be called as a function.');
4893 }
4894
4895 var node = unwrap(document.createElement('img'));
4896 HTMLElement.call(this, node);
4897 rewrap(node, this);
4898
4899 if (width !== undefined)
4900 node.width = width;
4901 if (height !== undefined)
4902 node.height = height;
4903 }
4904
4905 Image.prototype = HTMLImageElement.prototype;
4906
4907 scope.wrappers.HTMLImageElement = HTMLImageElement;
4908 scope.wrappers.Image = Image;
4909 })(window.ShadowDOMPolyfill);
4910
4911 // Copyright 2013 The Polymer Authors. All rights reserved.
4912 // Use of this source code is goverened by a BSD-style
4913 // license that can be found in the LICENSE file.
4914
4915 (function(scope) {
4916 'use strict';
4917
4918 var HTMLElement = scope.wrappers.HTMLElement;
4919 var mixin = scope.mixin;
4920 var registerWrapper = scope.registerWrapper;
4921
4922 var OriginalHTMLShadowElement = window.HTMLShadowElement;
4923
4924 function HTMLShadowElement(node) {
4925 HTMLElement.call(this, node);
4926 }
4927 HTMLShadowElement.prototype = Object.create(HTMLElement.prototype);
4928 mixin(HTMLShadowElement.prototype, {
4929 // TODO: attribute boolean resetStyleInheritance;
4930 });
4931
4932 if (OriginalHTMLShadowElement)
4933 registerWrapper(OriginalHTMLShadowElement, HTMLShadowElement);
4934
4935 scope.wrappers.HTMLShadowElement = HTMLShadowElement;
4936 })(window.ShadowDOMPolyfill);
4937
4938 // Copyright 2013 The Polymer Authors. All rights reserved.
4939 // Use of this source code is goverened by a BSD-style
4940 // license that can be found in the LICENSE file.
4941
4942 (function(scope) {
4943 'use strict';
4944
4945 var HTMLElement = scope.wrappers.HTMLElement;
4946 var mixin = scope.mixin;
4947 var registerWrapper = scope.registerWrapper;
4948 var unwrap = scope.unwrap;
4949 var wrap = scope.wrap;
4950
4951 var contentTable = new WeakMap();
4952 var templateContentsOwnerTable = new WeakMap();
4953
4954 // http://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/templates/index.html# dfn-template-contents-owner
4955 function getTemplateContentsOwner(doc) {
4956 if (!doc.defaultView)
4957 return doc;
4958 var d = templateContentsOwnerTable.get(doc);
4959 if (!d) {
4960 // TODO(arv): This should either be a Document or HTMLDocument depending
4961 // on doc.
4962 d = doc.implementation.createHTMLDocument('');
4963 while (d.lastChild) {
4964 d.removeChild(d.lastChild);
4965 }
4966 templateContentsOwnerTable.set(doc, d);
4967 }
4968 return d;
4969 }
4970
4971 function extractContent(templateElement) {
4972 // templateElement is not a wrapper here.
4973 var doc = getTemplateContentsOwner(templateElement.ownerDocument);
4974 var df = unwrap(doc.createDocumentFragment());
4975 var child;
4976 while (child = templateElement.firstChild) {
4977 df.appendChild(child);
4978 }
4979 return df;
4980 }
4981
4982 var OriginalHTMLTemplateElement = window.HTMLTemplateElement;
4983
4984 function HTMLTemplateElement(node) {
4985 HTMLElement.call(this, node);
4986 if (!OriginalHTMLTemplateElement) {
4987 var content = extractContent(node);
4988 contentTable.set(this, wrap(content));
4989 }
4990 }
4991 HTMLTemplateElement.prototype = Object.create(HTMLElement.prototype);
4992
4993 mixin(HTMLTemplateElement.prototype, {
4994 get content() {
4995 if (OriginalHTMLTemplateElement)
4996 return wrap(this.impl.content);
4997 return contentTable.get(this);
4998 },
4999
5000 // TODO(arv): cloneNode needs to clone content.
5001
5002 });
5003
5004 if (OriginalHTMLTemplateElement)
5005 registerWrapper(OriginalHTMLTemplateElement, HTMLTemplateElement);
5006
5007 scope.wrappers.HTMLTemplateElement = HTMLTemplateElement;
5008 })(window.ShadowDOMPolyfill);
5009
5010 // Copyright 2013 The Polymer Authors. All rights reserved.
5011 // Use of this source code is goverened by a BSD-style
5012 // license that can be found in the LICENSE file.
5013
5014 (function(scope) {
5015 'use strict';
5016
5017 var HTMLElement = scope.wrappers.HTMLElement;
5018 var registerWrapper = scope.registerWrapper;
5019
5020 var OriginalHTMLMediaElement = window.HTMLMediaElement;
5021
5022 function HTMLMediaElement(node) {
5023 HTMLElement.call(this, node);
5024 }
5025 HTMLMediaElement.prototype = Object.create(HTMLElement.prototype);
5026
5027 registerWrapper(OriginalHTMLMediaElement, HTMLMediaElement,
5028 document.createElement('audio'));
5029
5030 scope.wrappers.HTMLMediaElement = HTMLMediaElement;
5031 })(window.ShadowDOMPolyfill);
5032
5033 // Copyright 2013 The Polymer Authors. All rights reserved.
5034 // Use of this source code is goverened by a BSD-style
5035 // license that can be found in the LICENSE file.
5036
5037 (function(scope) {
5038 'use strict';
5039
5040 var HTMLMediaElement = scope.wrappers.HTMLMediaElement;
5041 var registerWrapper = scope.registerWrapper;
5042 var unwrap = scope.unwrap;
5043 var rewrap = scope.rewrap;
5044
5045 var OriginalHTMLAudioElement = window.HTMLAudioElement;
5046
5047 function HTMLAudioElement(node) {
5048 HTMLMediaElement.call(this, node);
5049 }
5050 HTMLAudioElement.prototype = Object.create(HTMLMediaElement.prototype);
5051
5052 registerWrapper(OriginalHTMLAudioElement, HTMLAudioElement,
5053 document.createElement('audio'));
5054
5055 function Audio(src) {
5056 if (!(this instanceof Audio)) {
5057 throw new TypeError(
5058 'DOM object constructor cannot be called as a function.');
5059 }
5060
5061 var node = unwrap(document.createElement('audio'));
5062 HTMLMediaElement.call(this, node);
5063 rewrap(node, this);
5064
5065 node.setAttribute('preload', 'auto');
5066 if (src !== undefined)
5067 node.setAttribute('src', src);
5068 }
5069
5070 Audio.prototype = HTMLAudioElement.prototype;
5071
5072 scope.wrappers.HTMLAudioElement = HTMLAudioElement;
5073 scope.wrappers.Audio = Audio;
5074 })(window.ShadowDOMPolyfill);
5075
5076 // Copyright 2013 The Polymer Authors. All rights reserved.
5077 // Use of this source code is goverened by a BSD-style
5078 // license that can be found in the LICENSE file.
5079
5080 (function(scope) {
5081 'use strict';
5082
5083 var HTMLElement = scope.wrappers.HTMLElement;
5084 var mixin = scope.mixin;
5085 var registerWrapper = scope.registerWrapper;
5086 var rewrap = scope.rewrap;
5087 var unwrap = scope.unwrap;
5088 var wrap = scope.wrap;
5089
5090 var OriginalHTMLOptionElement = window.HTMLOptionElement;
5091
5092 function trimText(s) {
5093 return s.replace(/\s+/g, ' ').trim();
5094 }
5095
5096 function HTMLOptionElement(node) {
5097 HTMLElement.call(this, node);
5098 }
5099 HTMLOptionElement.prototype = Object.create(HTMLElement.prototype);
5100 mixin(HTMLOptionElement.prototype, {
5101 get text() {
5102 return trimText(this.textContent);
5103 },
5104 set text(value) {
5105 this.textContent = trimText(String(value));
5106 },
5107 get form() {
5108 return wrap(unwrap(this).form);
5109 }
5110 });
5111
5112 registerWrapper(OriginalHTMLOptionElement, HTMLOptionElement,
5113 document.createElement('option'));
5114
5115 function Option(text, value, defaultSelected, selected) {
5116 if (!(this instanceof Option)) {
5117 throw new TypeError(
5118 'DOM object constructor cannot be called as a function.');
5119 }
5120
5121 var node = unwrap(document.createElement('option'));
5122 HTMLElement.call(this, node);
5123 rewrap(node, this);
5124
5125 if (text !== undefined)
5126 node.text = text;
5127 if (value !== undefined)
5128 node.setAttribute('value', value);
5129 if (defaultSelected === true)
5130 node.setAttribute('selected', '');
5131 node.selected = selected === true;
5132 }
5133
5134 Option.prototype = HTMLOptionElement.prototype;
5135
5136 scope.wrappers.HTMLOptionElement = HTMLOptionElement;
5137 scope.wrappers.Option = Option;
5138 })(window.ShadowDOMPolyfill);
5139
5140 // Copyright 2013 The Polymer Authors. All rights reserved.
5141 // Use of this source code is goverened by a BSD-style
5142 // license that can be found in the LICENSE file.
5143
5144 (function(scope) {
5145 'use strict';
5146
5147 var HTMLContentElement = scope.wrappers.HTMLContentElement;
5148 var HTMLElement = scope.wrappers.HTMLElement;
5149 var HTMLShadowElement = scope.wrappers.HTMLShadowElement;
5150 var HTMLTemplateElement = scope.wrappers.HTMLTemplateElement;
5151 var mixin = scope.mixin;
5152 var registerWrapper = scope.registerWrapper;
5153
5154 var OriginalHTMLUnknownElement = window.HTMLUnknownElement;
5155
5156 function HTMLUnknownElement(node) {
5157 switch (node.localName) {
5158 case 'content':
5159 return new HTMLContentElement(node);
5160 case 'shadow':
5161 return new HTMLShadowElement(node);
5162 case 'template':
5163 return new HTMLTemplateElement(node);
5164 }
5165 HTMLElement.call(this, node);
5166 }
5167 HTMLUnknownElement.prototype = Object.create(HTMLElement.prototype);
5168 registerWrapper(OriginalHTMLUnknownElement, HTMLUnknownElement);
5169 scope.wrappers.HTMLUnknownElement = HTMLUnknownElement;
5170 })(window.ShadowDOMPolyfill);
5171
5172 // Copyright 2014 The Polymer Authors. All rights reserved.
5173 // Use of this source code is goverened by a BSD-style
5174 // license that can be found in the LICENSE file.
5175
5176 (function(scope) {
5177 'use strict';
5178
5179 var registerObject = scope.registerObject;
5180
5181 var SVG_NS = 'http://www.w3.org/2000/svg';
5182 var svgTitleElement = document.createElementNS(SVG_NS, 'title');
5183 var SVGTitleElement = registerObject(svgTitleElement);
5184 var SVGElement = Object.getPrototypeOf(SVGTitleElement.prototype).constructor;
5185
5186 scope.wrappers.SVGElement = SVGElement;
5187 })(window.ShadowDOMPolyfill);
5188
5189 // Copyright 2014 The Polymer Authors. All rights reserved.
5190 // Use of this source code is goverened by a BSD-style
5191 // license that can be found in the LICENSE file.
5192
5193 (function(scope) {
5194 'use strict';
5195
5196 var mixin = scope.mixin;
5197 var registerWrapper = scope.registerWrapper;
5198 var unwrap = scope.unwrap;
5199 var wrap = scope.wrap;
5200
5201 var OriginalSVGUseElement = window.SVGUseElement;
5202
5203 // IE uses SVGElement as parent interface, SVG2 (Blink & Gecko) uses
5204 // SVGGraphicsElement. Use the <g> element to get the right prototype.
5205
5206 var SVG_NS = 'http://www.w3.org/2000/svg';
5207 var gWrapper = wrap(document.createElementNS(SVG_NS, 'g'));
5208 var useElement = document.createElementNS(SVG_NS, 'use');
5209 var SVGGElement = gWrapper.constructor;
5210 var parentInterfacePrototype = Object.getPrototypeOf(SVGGElement.prototype);
5211 var parentInterface = parentInterfacePrototype.constructor;
5212
5213 function SVGUseElement(impl) {
5214 parentInterface.call(this, impl);
5215 }
5216
5217 SVGUseElement.prototype = Object.create(parentInterfacePrototype);
5218
5219 // Firefox does not expose instanceRoot.
5220 if ('instanceRoot' in useElement) {
5221 mixin(SVGUseElement.prototype, {
5222 get instanceRoot() {
5223 return wrap(unwrap(this).instanceRoot);
5224 },
5225 get animatedInstanceRoot() {
5226 return wrap(unwrap(this).animatedInstanceRoot);
5227 },
5228 });
5229 }
5230
5231 registerWrapper(OriginalSVGUseElement, SVGUseElement, useElement);
5232
5233 scope.wrappers.SVGUseElement = SVGUseElement;
5234 })(window.ShadowDOMPolyfill);
5235
5236 // Copyright 2014 The Polymer Authors. All rights reserved.
5237 // Use of this source code is goverened by a BSD-style
5238 // license that can be found in the LICENSE file.
5239
5240 (function(scope) {
5241 'use strict';
5242
5243 var EventTarget = scope.wrappers.EventTarget;
5244 var mixin = scope.mixin;
5245 var registerWrapper = scope.registerWrapper;
5246 var wrap = scope.wrap;
5247
5248 var OriginalSVGElementInstance = window.SVGElementInstance;
5249 if (!OriginalSVGElementInstance)
5250 return;
5251
5252 function SVGElementInstance(impl) {
5253 EventTarget.call(this, impl);
5254 }
5255
5256 SVGElementInstance.prototype = Object.create(EventTarget.prototype);
5257 mixin(SVGElementInstance.prototype, {
5258 /** @type {SVGElement} */
5259 get correspondingElement() {
5260 return wrap(this.impl.correspondingElement);
5261 },
5262
5263 /** @type {SVGUseElement} */
5264 get correspondingUseElement() {
5265 return wrap(this.impl.correspondingUseElement);
5266 },
5267
5268 /** @type {SVGElementInstance} */
5269 get parentNode() {
5270 return wrap(this.impl.parentNode);
5271 },
5272
5273 /** @type {SVGElementInstanceList} */
5274 get childNodes() {
5275 throw new Error('Not implemented');
5276 },
5277
5278 /** @type {SVGElementInstance} */
5279 get firstChild() {
5280 return wrap(this.impl.firstChild);
5281 },
5282
5283 /** @type {SVGElementInstance} */
5284 get lastChild() {
5285 return wrap(this.impl.lastChild);
5286 },
5287
5288 /** @type {SVGElementInstance} */
5289 get previousSibling() {
5290 return wrap(this.impl.previousSibling);
5291 },
5292
5293 /** @type {SVGElementInstance} */
5294 get nextSibling() {
5295 return wrap(this.impl.nextSibling);
5296 }
5297 });
5298
5299 registerWrapper(OriginalSVGElementInstance, SVGElementInstance);
5300
5301 scope.wrappers.SVGElementInstance = SVGElementInstance;
5302 })(window.ShadowDOMPolyfill);
5303
5304 // Copyright 2013 The Polymer Authors. All rights reserved.
5305 // Use of this source code is goverened by a BSD-style
5306 // license that can be found in the LICENSE file.
5307
5308 (function(scope) {
5309 'use strict';
5310
5311 var mixin = scope.mixin;
5312 var registerWrapper = scope.registerWrapper;
5313 var unwrap = scope.unwrap;
5314 var unwrapIfNeeded = scope.unwrapIfNeeded;
5315 var wrap = scope.wrap;
5316
5317 var OriginalCanvasRenderingContext2D = window.CanvasRenderingContext2D;
5318
5319 function CanvasRenderingContext2D(impl) {
5320 this.impl = impl;
5321 }
5322
5323 mixin(CanvasRenderingContext2D.prototype, {
5324 get canvas() {
5325 return wrap(this.impl.canvas);
5326 },
5327
5328 drawImage: function() {
5329 arguments[0] = unwrapIfNeeded(arguments[0]);
5330 this.impl.drawImage.apply(this.impl, arguments);
5331 },
5332
5333 createPattern: function() {
5334 arguments[0] = unwrap(arguments[0]);
5335 return this.impl.createPattern.apply(this.impl, arguments);
5336 }
5337 });
5338
5339 registerWrapper(OriginalCanvasRenderingContext2D, CanvasRenderingContext2D,
5340 document.createElement('canvas').getContext('2d'));
5341
5342 scope.wrappers.CanvasRenderingContext2D = CanvasRenderingContext2D;
5343 })(window.ShadowDOMPolyfill);
5344
5345 // Copyright 2013 The Polymer Authors. All rights reserved.
5346 // Use of this source code is goverened by a BSD-style
5347 // license that can be found in the LICENSE file.
5348
5349 (function(scope) {
5350 'use strict';
5351
5352 var mixin = scope.mixin;
5353 var registerWrapper = scope.registerWrapper;
5354 var unwrapIfNeeded = scope.unwrapIfNeeded;
5355 var wrap = scope.wrap;
5356
5357 var OriginalWebGLRenderingContext = window.WebGLRenderingContext;
5358
5359 // IE10 does not have WebGL.
5360 if (!OriginalWebGLRenderingContext)
5361 return;
5362
5363 function WebGLRenderingContext(impl) {
5364 this.impl = impl;
5365 }
5366
5367 mixin(WebGLRenderingContext.prototype, {
5368 get canvas() {
5369 return wrap(this.impl.canvas);
5370 },
5371
5372 texImage2D: function() {
5373 arguments[5] = unwrapIfNeeded(arguments[5]);
5374 this.impl.texImage2D.apply(this.impl, arguments);
5375 },
5376
5377 texSubImage2D: function() {
5378 arguments[6] = unwrapIfNeeded(arguments[6]);
5379 this.impl.texSubImage2D.apply(this.impl, arguments);
5380 }
5381 });
5382
5383 // Blink/WebKit has broken DOM bindings. Usually we would create an instance
5384 // of the object and pass it into registerWrapper as a "blueprint" but
5385 // creating WebGL contexts is expensive and might fail so we use a dummy
5386 // object with dummy instance properties for these broken browsers.
5387 var instanceProperties = /WebKit/.test(navigator.userAgent) ?
5388 {drawingBufferHeight: null, drawingBufferWidth: null} : {};
5389
5390 registerWrapper(OriginalWebGLRenderingContext, WebGLRenderingContext,
5391 instanceProperties);
5392
5393 scope.wrappers.WebGLRenderingContext = WebGLRenderingContext;
5394 })(window.ShadowDOMPolyfill);
5395
5396 // Copyright 2013 The Polymer Authors. All rights reserved.
5397 // Use of this source code is goverened by a BSD-style
5398 // license that can be found in the LICENSE file.
5399
5400 (function(scope) {
5401 'use strict';
5402
5403 var registerWrapper = scope.registerWrapper;
5404 var unwrap = scope.unwrap;
5405 var unwrapIfNeeded = scope.unwrapIfNeeded;
5406 var wrap = scope.wrap;
5407
5408 var OriginalRange = window.Range;
5409
5410 function Range(impl) {
5411 this.impl = impl;
5412 }
5413 Range.prototype = {
5414 get startContainer() {
5415 return wrap(this.impl.startContainer);
5416 },
5417 get endContainer() {
5418 return wrap(this.impl.endContainer);
5419 },
5420 get commonAncestorContainer() {
5421 return wrap(this.impl.commonAncestorContainer);
5422 },
5423 setStart: function(refNode,offset) {
5424 this.impl.setStart(unwrapIfNeeded(refNode), offset);
5425 },
5426 setEnd: function(refNode,offset) {
5427 this.impl.setEnd(unwrapIfNeeded(refNode), offset);
5428 },
5429 setStartBefore: function(refNode) {
5430 this.impl.setStartBefore(unwrapIfNeeded(refNode));
5431 },
5432 setStartAfter: function(refNode) {
5433 this.impl.setStartAfter(unwrapIfNeeded(refNode));
5434 },
5435 setEndBefore: function(refNode) {
5436 this.impl.setEndBefore(unwrapIfNeeded(refNode));
5437 },
5438 setEndAfter: function(refNode) {
5439 this.impl.setEndAfter(unwrapIfNeeded(refNode));
5440 },
5441 selectNode: function(refNode) {
5442 this.impl.selectNode(unwrapIfNeeded(refNode));
5443 },
5444 selectNodeContents: function(refNode) {
5445 this.impl.selectNodeContents(unwrapIfNeeded(refNode));
5446 },
5447 compareBoundaryPoints: function(how, sourceRange) {
5448 return this.impl.compareBoundaryPoints(how, unwrap(sourceRange));
5449 },
5450 extractContents: function() {
5451 return wrap(this.impl.extractContents());
5452 },
5453 cloneContents: function() {
5454 return wrap(this.impl.cloneContents());
5455 },
5456 insertNode: function(node) {
5457 this.impl.insertNode(unwrapIfNeeded(node));
5458 },
5459 surroundContents: function(newParent) {
5460 this.impl.surroundContents(unwrapIfNeeded(newParent));
5461 },
5462 cloneRange: function() {
5463 return wrap(this.impl.cloneRange());
5464 },
5465 isPointInRange: function(node, offset) {
5466 return this.impl.isPointInRange(unwrapIfNeeded(node), offset);
5467 },
5468 comparePoint: function(node, offset) {
5469 return this.impl.comparePoint(unwrapIfNeeded(node), offset);
5470 },
5471 intersectsNode: function(node) {
5472 return this.impl.intersectsNode(unwrapIfNeeded(node));
5473 },
5474 toString: function() {
5475 return this.impl.toString();
5476 }
5477 };
5478
5479 // IE9 does not have createContextualFragment.
5480 if (OriginalRange.prototype.createContextualFragment) {
5481 Range.prototype.createContextualFragment = function(html) {
5482 return wrap(this.impl.createContextualFragment(html));
5483 };
5484 }
5485
5486 registerWrapper(window.Range, Range, document.createRange());
5487
5488 scope.wrappers.Range = Range;
5489
5490 })(window.ShadowDOMPolyfill);
5491
5492 // Copyright 2013 The Polymer Authors. All rights reserved.
5493 // Use of this source code is goverened by a BSD-style
5494 // license that can be found in the LICENSE file.
5495
5496 (function(scope) {
5497 'use strict';
5498
5499 var GetElementsByInterface = scope.GetElementsByInterface;
5500 var ParentNodeInterface = scope.ParentNodeInterface;
5501 var SelectorsInterface = scope.SelectorsInterface;
5502 var mixin = scope.mixin;
5503 var registerObject = scope.registerObject;
5504
5505 var DocumentFragment = registerObject(document.createDocumentFragment());
5506 mixin(DocumentFragment.prototype, ParentNodeInterface);
5507 mixin(DocumentFragment.prototype, SelectorsInterface);
5508 mixin(DocumentFragment.prototype, GetElementsByInterface);
5509
5510 var Comment = registerObject(document.createComment(''));
5511
5512 scope.wrappers.Comment = Comment;
5513 scope.wrappers.DocumentFragment = DocumentFragment;
5514
5515 })(window.ShadowDOMPolyfill);
5516
5517 // Copyright 2013 The Polymer Authors. All rights reserved.
5518 // Use of this source code is goverened by a BSD-style
5519 // license that can be found in the LICENSE file.
5520
5521 (function(scope) {
5522 'use strict';
5523
5524 var DocumentFragment = scope.wrappers.DocumentFragment;
5525 var elementFromPoint = scope.elementFromPoint;
5526 var getInnerHTML = scope.getInnerHTML;
5527 var mixin = scope.mixin;
5528 var rewrap = scope.rewrap;
5529 var setInnerHTML = scope.setInnerHTML;
5530 var unwrap = scope.unwrap;
5531
5532 var shadowHostTable = new WeakMap();
5533 var nextOlderShadowTreeTable = new WeakMap();
5534
5535 var spaceCharRe = /[ \t\n\r\f]/;
5536
5537 function ShadowRoot(hostWrapper) {
5538 var node = unwrap(hostWrapper.impl.ownerDocument.createDocumentFragment());
5539 DocumentFragment.call(this, node);
5540
5541 // createDocumentFragment associates the node with a wrapper
5542 // DocumentFragment instance. Override that.
5543 rewrap(node, this);
5544
5545 var oldShadowRoot = hostWrapper.shadowRoot;
5546 nextOlderShadowTreeTable.set(this, oldShadowRoot);
5547
5548 shadowHostTable.set(this, hostWrapper);
5549 }
5550 ShadowRoot.prototype = Object.create(DocumentFragment.prototype);
5551 mixin(ShadowRoot.prototype, {
5552 get innerHTML() {
5553 return getInnerHTML(this);
5554 },
5555 set innerHTML(value) {
5556 setInnerHTML(this, value);
5557 this.invalidateShadowRenderer();
5558 },
5559
5560 get olderShadowRoot() {
5561 return nextOlderShadowTreeTable.get(this) || null;
5562 },
5563
5564 get host() {
5565 return shadowHostTable.get(this) || null;
5566 },
5567
5568 invalidateShadowRenderer: function() {
5569 return shadowHostTable.get(this).invalidateShadowRenderer();
5570 },
5571
5572 elementFromPoint: function(x, y) {
5573 return elementFromPoint(this, this.ownerDocument, x, y);
5574 },
5575
5576 getElementById: function(id) {
5577 if (spaceCharRe.test(id))
5578 return null;
5579 return this.querySelector('[id="' + id + '"]');
5580 }
5581 });
5582
5583 scope.wrappers.ShadowRoot = ShadowRoot;
5584
5585 })(window.ShadowDOMPolyfill);
5586
5587 // Copyright 2013 The Polymer Authors. All rights reserved.
5588 // Use of this source code is governed by a BSD-style
5589 // license that can be found in the LICENSE file.
5590
5591 (function(scope) {
5592 'use strict';
5593
5594 var Element = scope.wrappers.Element;
5595 var HTMLContentElement = scope.wrappers.HTMLContentElement;
5596 var HTMLShadowElement = scope.wrappers.HTMLShadowElement;
5597 var Node = scope.wrappers.Node;
5598 var ShadowRoot = scope.wrappers.ShadowRoot;
5599 var assert = scope.assert;
5600 var mixin = scope.mixin;
5601 var oneOf = scope.oneOf;
5602 var unwrap = scope.unwrap;
5603 var wrap = scope.wrap;
5604
5605 /**
5606 * Updates the fields of a wrapper to a snapshot of the logical DOM as needed.
5607 * Up means parentNode
5608 * Sideways means previous and next sibling.
5609 * @param {!Node} wrapper
5610 */
5611 function updateWrapperUpAndSideways(wrapper) {
5612 wrapper.previousSibling_ = wrapper.previousSibling;
5613 wrapper.nextSibling_ = wrapper.nextSibling;
5614 wrapper.parentNode_ = wrapper.parentNode;
5615 }
5616
5617 /**
5618 * Updates the fields of a wrapper to a snapshot of the logical DOM as needed.
5619 * Down means first and last child
5620 * @param {!Node} wrapper
5621 */
5622 function updateWrapperDown(wrapper) {
5623 wrapper.firstChild_ = wrapper.firstChild;
5624 wrapper.lastChild_ = wrapper.lastChild;
5625 }
5626
5627 function updateAllChildNodes(parentNodeWrapper) {
5628 assert(parentNodeWrapper instanceof Node);
5629 for (var childWrapper = parentNodeWrapper.firstChild;
5630 childWrapper;
5631 childWrapper = childWrapper.nextSibling) {
5632 updateWrapperUpAndSideways(childWrapper);
5633 }
5634 updateWrapperDown(parentNodeWrapper);
5635 }
5636
5637 function insertBefore(parentNodeWrapper, newChildWrapper, refChildWrapper) {
5638 var parentNode = unwrap(parentNodeWrapper);
5639 var newChild = unwrap(newChildWrapper);
5640 var refChild = refChildWrapper ? unwrap(refChildWrapper) : null;
5641
5642 remove(newChildWrapper);
5643 updateWrapperUpAndSideways(newChildWrapper);
5644
5645 if (!refChildWrapper) {
5646 parentNodeWrapper.lastChild_ = parentNodeWrapper.lastChild;
5647 if (parentNodeWrapper.lastChild === parentNodeWrapper.firstChild)
5648 parentNodeWrapper.firstChild_ = parentNodeWrapper.firstChild;
5649
5650 var lastChildWrapper = wrap(parentNode.lastChild);
5651 if (lastChildWrapper)
5652 lastChildWrapper.nextSibling_ = lastChildWrapper.nextSibling;
5653 } else {
5654 if (parentNodeWrapper.firstChild === refChildWrapper)
5655 parentNodeWrapper.firstChild_ = refChildWrapper;
5656
5657 refChildWrapper.previousSibling_ = refChildWrapper.previousSibling;
5658 }
5659
5660 parentNode.insertBefore(newChild, refChild);
5661 }
5662
5663 function remove(nodeWrapper) {
5664 var node = unwrap(nodeWrapper)
5665 var parentNode = node.parentNode;
5666 if (!parentNode)
5667 return;
5668
5669 var parentNodeWrapper = wrap(parentNode);
5670 updateWrapperUpAndSideways(nodeWrapper);
5671
5672 if (nodeWrapper.previousSibling)
5673 nodeWrapper.previousSibling.nextSibling_ = nodeWrapper;
5674 if (nodeWrapper.nextSibling)
5675 nodeWrapper.nextSibling.previousSibling_ = nodeWrapper;
5676
5677 if (parentNodeWrapper.lastChild === nodeWrapper)
5678 parentNodeWrapper.lastChild_ = nodeWrapper;
5679 if (parentNodeWrapper.firstChild === nodeWrapper)
5680 parentNodeWrapper.firstChild_ = nodeWrapper;
5681
5682 parentNode.removeChild(node);
5683 }
5684
5685 var distributedChildNodesTable = new WeakMap();
5686 var eventParentsTable = new WeakMap();
5687 var insertionParentTable = new WeakMap();
5688 var rendererForHostTable = new WeakMap();
5689
5690 function distributeChildToInsertionPoint(child, insertionPoint) {
5691 getDistributedChildNodes(insertionPoint).push(child);
5692 assignToInsertionPoint(child, insertionPoint);
5693
5694 var eventParents = eventParentsTable.get(child);
5695 if (!eventParents)
5696 eventParentsTable.set(child, eventParents = []);
5697 eventParents.push(insertionPoint);
5698 }
5699
5700 function resetDistributedChildNodes(insertionPoint) {
5701 distributedChildNodesTable.set(insertionPoint, []);
5702 }
5703
5704 function getDistributedChildNodes(insertionPoint) {
5705 return distributedChildNodesTable.get(insertionPoint);
5706 }
5707
5708 function getChildNodesSnapshot(node) {
5709 var result = [], i = 0;
5710 for (var child = node.firstChild; child; child = child.nextSibling) {
5711 result[i++] = child;
5712 }
5713 return result;
5714 }
5715
5716 /**
5717 * Visits all nodes in the tree that fulfils the |predicate|. If the |visitor|
5718 * function returns |false| the traversal is aborted.
5719 * @param {!Node} tree
5720 * @param {function(!Node) : boolean} predicate
5721 * @param {function(!Node) : *} visitor
5722 */
5723 function visit(tree, predicate, visitor) {
5724 // This operates on logical DOM.
5725 for (var node = tree.firstChild; node; node = node.nextSibling) {
5726 if (predicate(node)) {
5727 if (visitor(node) === false)
5728 return;
5729 } else {
5730 visit(node, predicate, visitor);
5731 }
5732 }
5733 }
5734
5735 // Matching Insertion Points
5736 // http://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/shadow/index.html#mat ching-insertion-points
5737
5738 // TODO(arv): Verify this... I don't remember why I picked this regexp.
5739 var selectorMatchRegExp = /^[*.:#[a-zA-Z_|]/;
5740
5741 var allowedPseudoRegExp = new RegExp('^:(' + [
5742 'link',
5743 'visited',
5744 'target',
5745 'enabled',
5746 'disabled',
5747 'checked',
5748 'indeterminate',
5749 'nth-child',
5750 'nth-last-child',
5751 'nth-of-type',
5752 'nth-last-of-type',
5753 'first-child',
5754 'last-child',
5755 'first-of-type',
5756 'last-of-type',
5757 'only-of-type',
5758 ].join('|') + ')');
5759
5760
5761 /**
5762 * @param {Element} node
5763 * @oaram {Element} point The insertion point element.
5764 * @return {boolean} Whether the node matches the insertion point.
5765 */
5766 function matchesCriteria(node, point) {
5767 var select = point.getAttribute('select');
5768 if (!select)
5769 return true;
5770
5771 // Here we know the select attribute is a non empty string.
5772 select = select.trim();
5773 if (!select)
5774 return true;
5775
5776 if (!(node instanceof Element))
5777 return false;
5778
5779 // The native matches function in IE9 does not correctly work with elements
5780 // that are not in the document.
5781 // TODO(arv): Implement matching in JS.
5782 // https://github.com/Polymer/ShadowDOM/issues/361
5783 if (select === '*' || select === node.localName)
5784 return true;
5785
5786 // TODO(arv): This does not seem right. Need to check for a simple selector.
5787 if (!selectorMatchRegExp.test(select))
5788 return false;
5789
5790 // TODO(arv): This no longer matches the spec.
5791 if (select[0] === ':' && !allowedPseudoRegExp.test(select))
5792 return false;
5793
5794 try {
5795 return node.matches(select);
5796 } catch (ex) {
5797 // Invalid selector.
5798 return false;
5799 }
5800 }
5801
5802 var request = oneOf(window, [
5803 'requestAnimationFrame',
5804 'mozRequestAnimationFrame',
5805 'webkitRequestAnimationFrame',
5806 'setTimeout'
5807 ]);
5808
5809 var pendingDirtyRenderers = [];
5810 var renderTimer;
5811
5812 function renderAllPending() {
5813 for (var i = 0; i < pendingDirtyRenderers.length; i++) {
5814 pendingDirtyRenderers[i].render();
5815 }
5816 pendingDirtyRenderers = [];
5817 }
5818
5819 function handleRequestAnimationFrame() {
5820 renderTimer = null;
5821 renderAllPending();
5822 }
5823
5824 /**
5825 * Returns existing shadow renderer for a host or creates it if it is needed.
5826 * @params {!Element} host
5827 * @return {!ShadowRenderer}
5828 */
5829 function getRendererForHost(host) {
5830 var renderer = rendererForHostTable.get(host);
5831 if (!renderer) {
5832 renderer = new ShadowRenderer(host);
5833 rendererForHostTable.set(host, renderer);
5834 }
5835 return renderer;
5836 }
5837
5838 function getShadowRootAncestor(node) {
5839 for (; node; node = node.parentNode) {
5840 if (node instanceof ShadowRoot)
5841 return node;
5842 }
5843 return null;
5844 }
5845
5846 function getRendererForShadowRoot(shadowRoot) {
5847 return getRendererForHost(shadowRoot.host);
5848 }
5849
5850 var spliceDiff = new ArraySplice();
5851 spliceDiff.equals = function(renderNode, rawNode) {
5852 return unwrap(renderNode.node) === rawNode;
5853 };
5854
5855 /**
5856 * RenderNode is used as an in memory "render tree". When we render the
5857 * composed tree we create a tree of RenderNodes, then we diff this against
5858 * the real DOM tree and make minimal changes as needed.
5859 */
5860 function RenderNode(node) {
5861 this.skip = false;
5862 this.node = node;
5863 this.childNodes = [];
5864 }
5865
5866 RenderNode.prototype = {
5867 append: function(node) {
5868 var rv = new RenderNode(node);
5869 this.childNodes.push(rv);
5870 return rv;
5871 },
5872
5873 sync: function(opt_added) {
5874 if (this.skip)
5875 return;
5876
5877 var nodeWrapper = this.node;
5878 // plain array of RenderNodes
5879 var newChildren = this.childNodes;
5880 // plain array of real nodes.
5881 var oldChildren = getChildNodesSnapshot(unwrap(nodeWrapper));
5882 var added = opt_added || new WeakMap();
5883
5884 var splices = spliceDiff.calculateSplices(newChildren, oldChildren);
5885
5886 var newIndex = 0, oldIndex = 0;
5887 var lastIndex = 0;
5888 for (var i = 0; i < splices.length; i++) {
5889 var splice = splices[i];
5890 for (; lastIndex < splice.index; lastIndex++) {
5891 oldIndex++;
5892 newChildren[newIndex++].sync(added);
5893 }
5894
5895 var removedCount = splice.removed.length;
5896 for (var j = 0; j < removedCount; j++) {
5897 var wrapper = wrap(oldChildren[oldIndex++]);
5898 if (!added.get(wrapper))
5899 remove(wrapper);
5900 }
5901
5902 var addedCount = splice.addedCount;
5903 var refNode = oldChildren[oldIndex] && wrap(oldChildren[oldIndex]);
5904 for (var j = 0; j < addedCount; j++) {
5905 var newChildRenderNode = newChildren[newIndex++];
5906 var newChildWrapper = newChildRenderNode.node;
5907 insertBefore(nodeWrapper, newChildWrapper, refNode);
5908
5909 // Keep track of added so that we do not remove the node after it
5910 // has been added.
5911 added.set(newChildWrapper, true);
5912
5913 newChildRenderNode.sync(added);
5914 }
5915
5916 lastIndex += addedCount;
5917 }
5918
5919 for (var i = lastIndex; i < newChildren.length; i++) {
5920 newChildren[i].sync(added);
5921 }
5922 }
5923 };
5924
5925 function ShadowRenderer(host) {
5926 this.host = host;
5927 this.dirty = false;
5928 this.invalidateAttributes();
5929 this.associateNode(host);
5930 }
5931
5932 ShadowRenderer.prototype = {
5933
5934 // http://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/shadow/index.html#r endering-shadow-trees
5935 render: function(opt_renderNode) {
5936 if (!this.dirty)
5937 return;
5938
5939 this.invalidateAttributes();
5940 this.treeComposition();
5941
5942 var host = this.host;
5943 var shadowRoot = host.shadowRoot;
5944
5945 this.associateNode(host);
5946 var topMostRenderer = !renderNode;
5947 var renderNode = opt_renderNode || new RenderNode(host);
5948
5949 for (var node = shadowRoot.firstChild; node; node = node.nextSibling) {
5950 this.renderNode(shadowRoot, renderNode, node, false);
5951 }
5952
5953 if (topMostRenderer)
5954 renderNode.sync();
5955
5956 this.dirty = false;
5957 },
5958
5959 invalidate: function() {
5960 if (!this.dirty) {
5961 this.dirty = true;
5962 pendingDirtyRenderers.push(this);
5963 if (renderTimer)
5964 return;
5965 renderTimer = window[request](handleRequestAnimationFrame, 0);
5966 }
5967 },
5968
5969 renderNode: function(shadowRoot, renderNode, node, isNested) {
5970 if (isShadowHost(node)) {
5971 renderNode = renderNode.append(node);
5972 var renderer = getRendererForHost(node);
5973 renderer.dirty = true; // Need to rerender due to reprojection.
5974 renderer.render(renderNode);
5975 } else if (isInsertionPoint(node)) {
5976 this.renderInsertionPoint(shadowRoot, renderNode, node, isNested);
5977 } else if (isShadowInsertionPoint(node)) {
5978 this.renderShadowInsertionPoint(shadowRoot, renderNode, node);
5979 } else {
5980 this.renderAsAnyDomTree(shadowRoot, renderNode, node, isNested);
5981 }
5982 },
5983
5984 renderAsAnyDomTree: function(shadowRoot, renderNode, node, isNested) {
5985 renderNode = renderNode.append(node);
5986
5987 if (isShadowHost(node)) {
5988 var renderer = getRendererForHost(node);
5989 renderNode.skip = !renderer.dirty;
5990 renderer.render(renderNode);
5991 } else {
5992 for (var child = node.firstChild; child; child = child.nextSibling) {
5993 this.renderNode(shadowRoot, renderNode, child, isNested);
5994 }
5995 }
5996 },
5997
5998 renderInsertionPoint: function(shadowRoot, renderNode, insertionPoint,
5999 isNested) {
6000 var distributedChildNodes = getDistributedChildNodes(insertionPoint);
6001 if (distributedChildNodes.length) {
6002 this.associateNode(insertionPoint);
6003
6004 for (var i = 0; i < distributedChildNodes.length; i++) {
6005 var child = distributedChildNodes[i];
6006 if (isInsertionPoint(child) && isNested)
6007 this.renderInsertionPoint(shadowRoot, renderNode, child, isNested);
6008 else
6009 this.renderAsAnyDomTree(shadowRoot, renderNode, child, isNested);
6010 }
6011 } else {
6012 this.renderFallbackContent(shadowRoot, renderNode, insertionPoint);
6013 }
6014 this.associateNode(insertionPoint.parentNode);
6015 },
6016
6017 renderShadowInsertionPoint: function(shadowRoot, renderNode,
6018 shadowInsertionPoint) {
6019 var nextOlderTree = shadowRoot.olderShadowRoot;
6020 if (nextOlderTree) {
6021 assignToInsertionPoint(nextOlderTree, shadowInsertionPoint);
6022 this.associateNode(shadowInsertionPoint.parentNode);
6023 for (var node = nextOlderTree.firstChild;
6024 node;
6025 node = node.nextSibling) {
6026 this.renderNode(nextOlderTree, renderNode, node, true);
6027 }
6028 } else {
6029 this.renderFallbackContent(shadowRoot, renderNode,
6030 shadowInsertionPoint);
6031 }
6032 },
6033
6034 renderFallbackContent: function(shadowRoot, renderNode, fallbackHost) {
6035 this.associateNode(fallbackHost);
6036 this.associateNode(fallbackHost.parentNode);
6037 for (var node = fallbackHost.firstChild; node; node = node.nextSibling) {
6038 this.renderAsAnyDomTree(shadowRoot, renderNode, node, false);
6039 }
6040 },
6041
6042 /**
6043 * Invalidates the attributes used to keep track of which attributes may
6044 * cause the renderer to be invalidated.
6045 */
6046 invalidateAttributes: function() {
6047 this.attributes = Object.create(null);
6048 },
6049
6050 /**
6051 * Parses the selector and makes this renderer dependent on the attribute
6052 * being used in the selector.
6053 * @param {string} selector
6054 */
6055 updateDependentAttributes: function(selector) {
6056 if (!selector)
6057 return;
6058
6059 var attributes = this.attributes;
6060
6061 // .class
6062 if (/\.\w+/.test(selector))
6063 attributes['class'] = true;
6064
6065 // #id
6066 if (/#\w+/.test(selector))
6067 attributes['id'] = true;
6068
6069 selector.replace(/\[\s*([^\s=\|~\]]+)/g, function(_, name) {
6070 attributes[name] = true;
6071 });
6072
6073 // Pseudo selectors have been removed from the spec.
6074 },
6075
6076 dependsOnAttribute: function(name) {
6077 return this.attributes[name];
6078 },
6079
6080 // http://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/shadow/index.html#d fn-distribution-algorithm
6081 distribute: function(tree, pool) {
6082 var self = this;
6083
6084 visit(tree, isActiveInsertionPoint,
6085 function(insertionPoint) {
6086 resetDistributedChildNodes(insertionPoint);
6087 self.updateDependentAttributes(
6088 insertionPoint.getAttribute('select'));
6089
6090 for (var i = 0; i < pool.length; i++) { // 1.2
6091 var node = pool[i]; // 1.2.1
6092 if (node === undefined) // removed
6093 continue;
6094 if (matchesCriteria(node, insertionPoint)) { // 1.2.2
6095 distributeChildToInsertionPoint(node, insertionPoint); // 1.2.2 .1
6096 pool[i] = undefined; // 1.2.2.2
6097 }
6098 }
6099 });
6100 },
6101
6102 // http://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/shadow/index.html#d fn-tree-composition
6103 treeComposition: function () {
6104 var shadowHost = this.host;
6105 var tree = shadowHost.shadowRoot; // 1.
6106 var pool = []; // 2.
6107
6108 for (var child = shadowHost.firstChild;
6109 child;
6110 child = child.nextSibling) { // 3.
6111 if (isInsertionPoint(child)) { // 3.2.
6112 var reprojected = getDistributedChildNodes(child); // 3.2.1.
6113 // if reprojected is undef... reset it?
6114 if (!reprojected || !reprojected.length) // 3.2.2.
6115 reprojected = getChildNodesSnapshot(child);
6116 pool.push.apply(pool, reprojected); // 3.2.3.
6117 } else {
6118 pool.push(child); // 3.3.
6119 }
6120 }
6121
6122 var shadowInsertionPoint, point;
6123 while (tree) { // 4.
6124 // 4.1.
6125 shadowInsertionPoint = undefined; // Reset every iteration.
6126 visit(tree, isActiveShadowInsertionPoint, function(point) {
6127 shadowInsertionPoint = point;
6128 return false;
6129 });
6130 point = shadowInsertionPoint;
6131
6132 this.distribute(tree, pool); // 4.2.
6133 if (point) { // 4.3.
6134 var nextOlderTree = tree.olderShadowRoot; // 4.3.1.
6135 if (!nextOlderTree) {
6136 break; // 4.3.1.1.
6137 } else {
6138 tree = nextOlderTree; // 4.3.2.2.
6139 assignToInsertionPoint(tree, point); // 4.3.2.2.
6140 continue; // 4.3.2.3.
6141 }
6142 } else {
6143 break; // 4.4.
6144 }
6145 }
6146 },
6147
6148 associateNode: function(node) {
6149 node.impl.polymerShadowRenderer_ = this;
6150 }
6151 };
6152
6153 function isInsertionPoint(node) {
6154 // Should this include <shadow>?
6155 return node instanceof HTMLContentElement;
6156 }
6157
6158 function isActiveInsertionPoint(node) {
6159 // <content> inside another <content> or <shadow> is considered inactive.
6160 return node instanceof HTMLContentElement;
6161 }
6162
6163 function isShadowInsertionPoint(node) {
6164 return node instanceof HTMLShadowElement;
6165 }
6166
6167 function isActiveShadowInsertionPoint(node) {
6168 // <shadow> inside another <content> or <shadow> is considered inactive.
6169 return node instanceof HTMLShadowElement;
6170 }
6171
6172 function isShadowHost(shadowHost) {
6173 return shadowHost.shadowRoot;
6174 }
6175
6176 function getShadowTrees(host) {
6177 var trees = [];
6178
6179 for (var tree = host.shadowRoot; tree; tree = tree.olderShadowRoot) {
6180 trees.push(tree);
6181 }
6182 return trees;
6183 }
6184
6185 function assignToInsertionPoint(tree, point) {
6186 insertionParentTable.set(tree, point);
6187 }
6188
6189 // http://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/shadow/index.html#ren dering-shadow-trees
6190 function render(host) {
6191 new ShadowRenderer(host).render();
6192 };
6193
6194 // Need to rerender shadow host when:
6195 //
6196 // - a direct child to the ShadowRoot is added or removed
6197 // - a direct child to the host is added or removed
6198 // - a new shadow root is created
6199 // - a direct child to a content/shadow element is added or removed
6200 // - a sibling to a content/shadow element is added or removed
6201 // - content[select] is changed
6202 // - an attribute in a direct child to a host is modified
6203
6204 /**
6205 * This gets called when a node was added or removed to it.
6206 */
6207 Node.prototype.invalidateShadowRenderer = function(force) {
6208 var renderer = this.impl.polymerShadowRenderer_;
6209 if (renderer) {
6210 renderer.invalidate();
6211 return true;
6212 }
6213
6214 return false;
6215 };
6216
6217 HTMLContentElement.prototype.getDistributedNodes = function() {
6218 // TODO(arv): We should only rerender the dirty ancestor renderers (from
6219 // the root and down).
6220 renderAllPending();
6221 return getDistributedChildNodes(this);
6222 };
6223
6224 HTMLShadowElement.prototype.nodeIsInserted_ =
6225 HTMLContentElement.prototype.nodeIsInserted_ = function() {
6226 // Invalidate old renderer if any.
6227 this.invalidateShadowRenderer();
6228
6229 var shadowRoot = getShadowRootAncestor(this);
6230 var renderer;
6231 if (shadowRoot)
6232 renderer = getRendererForShadowRoot(shadowRoot);
6233 this.impl.polymerShadowRenderer_ = renderer;
6234 if (renderer)
6235 renderer.invalidate();
6236 };
6237
6238 scope.eventParentsTable = eventParentsTable;
6239 scope.getRendererForHost = getRendererForHost;
6240 scope.getShadowTrees = getShadowTrees;
6241 scope.insertionParentTable = insertionParentTable;
6242 scope.renderAllPending = renderAllPending;
6243
6244 // Exposed for testing
6245 scope.visual = {
6246 insertBefore: insertBefore,
6247 remove: remove,
6248 };
6249
6250 })(window.ShadowDOMPolyfill);
6251
6252 // Copyright 2013 The Polymer Authors. All rights reserved.
6253 // Use of this source code is goverened by a BSD-style
6254 // license that can be found in the LICENSE file.
6255
6256 (function(scope) {
6257 'use strict';
6258
6259 var HTMLElement = scope.wrappers.HTMLElement;
6260 var assert = scope.assert;
6261 var mixin = scope.mixin;
6262 var registerWrapper = scope.registerWrapper;
6263 var unwrap = scope.unwrap;
6264 var wrap = scope.wrap;
6265
6266 var elementsWithFormProperty = [
6267 'HTMLButtonElement',
6268 'HTMLFieldSetElement',
6269 'HTMLInputElement',
6270 'HTMLKeygenElement',
6271 'HTMLLabelElement',
6272 'HTMLLegendElement',
6273 'HTMLObjectElement',
6274 // HTMLOptionElement is handled in HTMLOptionElement.js
6275 'HTMLOutputElement',
6276 'HTMLSelectElement',
6277 'HTMLTextAreaElement',
6278 ];
6279
6280 function createWrapperConstructor(name) {
6281 if (!window[name])
6282 return;
6283
6284 // Ensure we are not overriding an already existing constructor.
6285 assert(!scope.wrappers[name]);
6286
6287 var GeneratedWrapper = function(node) {
6288 // At this point all of them extend HTMLElement.
6289 HTMLElement.call(this, node);
6290 }
6291 GeneratedWrapper.prototype = Object.create(HTMLElement.prototype);
6292 mixin(GeneratedWrapper.prototype, {
6293 get form() {
6294 return wrap(unwrap(this).form);
6295 },
6296 });
6297
6298 registerWrapper(window[name], GeneratedWrapper,
6299 document.createElement(name.slice(4, -7)));
6300 scope.wrappers[name] = GeneratedWrapper;
6301 }
6302
6303 elementsWithFormProperty.forEach(createWrapperConstructor);
6304
6305 })(window.ShadowDOMPolyfill);
6306
6307 // Copyright 2014 The Polymer Authors. All rights reserved.
6308 // Use of this source code is goverened by a BSD-style
6309 // license that can be found in the LICENSE file.
6310
6311 (function(scope) {
6312 'use strict';
6313
6314 var registerWrapper = scope.registerWrapper;
6315 var unwrap = scope.unwrap;
6316 var unwrapIfNeeded = scope.unwrapIfNeeded;
6317 var wrap = scope.wrap;
6318
6319 var OriginalSelection = window.Selection;
6320
6321 function Selection(impl) {
6322 this.impl = impl;
6323 }
6324 Selection.prototype = {
6325 get anchorNode() {
6326 return wrap(this.impl.anchorNode);
6327 },
6328 get focusNode() {
6329 return wrap(this.impl.focusNode);
6330 },
6331 addRange: function(range) {
6332 this.impl.addRange(unwrap(range));
6333 },
6334 collapse: function(node, index) {
6335 this.impl.collapse(unwrapIfNeeded(node), index);
6336 },
6337 containsNode: function(node, allowPartial) {
6338 return this.impl.containsNode(unwrapIfNeeded(node), allowPartial);
6339 },
6340 extend: function(node, offset) {
6341 this.impl.extend(unwrapIfNeeded(node), offset);
6342 },
6343 getRangeAt: function(index) {
6344 return wrap(this.impl.getRangeAt(index));
6345 },
6346 removeRange: function(range) {
6347 this.impl.removeRange(unwrap(range));
6348 },
6349 selectAllChildren: function(node) {
6350 this.impl.selectAllChildren(unwrapIfNeeded(node));
6351 },
6352 toString: function() {
6353 return this.impl.toString();
6354 }
6355 };
6356
6357 // WebKit extensions. Not implemented.
6358 // readonly attribute Node baseNode;
6359 // readonly attribute long baseOffset;
6360 // readonly attribute Node extentNode;
6361 // readonly attribute long extentOffset;
6362 // [RaisesException] void setBaseAndExtent([Default=Undefined] optional Node b aseNode,
6363 // [Default=Undefined] optional long baseOffset,
6364 // [Default=Undefined] optional Node extentNode,
6365 // [Default=Undefined] optional long extentOffset);
6366 // [RaisesException, ImplementedAs=collapse] void setPosition([Default=Undefin ed] optional Node node,
6367 // [Default=Undefined] optional long offset);
6368
6369 registerWrapper(window.Selection, Selection, window.getSelection());
6370
6371 scope.wrappers.Selection = Selection;
6372
6373 })(window.ShadowDOMPolyfill);
6374
6375 // Copyright 2013 The Polymer Authors. All rights reserved.
6376 // Use of this source code is goverened by a BSD-style
6377 // license that can be found in the LICENSE file.
6378
6379 (function(scope) {
6380 'use strict';
6381
6382 var GetElementsByInterface = scope.GetElementsByInterface;
6383 var Node = scope.wrappers.Node;
6384 var ParentNodeInterface = scope.ParentNodeInterface;
6385 var Selection = scope.wrappers.Selection;
6386 var SelectorsInterface = scope.SelectorsInterface;
6387 var ShadowRoot = scope.wrappers.ShadowRoot;
6388 var cloneNode = scope.cloneNode;
6389 var defineWrapGetter = scope.defineWrapGetter;
6390 var elementFromPoint = scope.elementFromPoint;
6391 var forwardMethodsToWrapper = scope.forwardMethodsToWrapper;
6392 var matchesNames = scope.matchesNames;
6393 var mixin = scope.mixin;
6394 var registerWrapper = scope.registerWrapper;
6395 var renderAllPending = scope.renderAllPending;
6396 var rewrap = scope.rewrap;
6397 var unwrap = scope.unwrap;
6398 var wrap = scope.wrap;
6399 var wrapEventTargetMethods = scope.wrapEventTargetMethods;
6400 var wrapNodeList = scope.wrapNodeList;
6401
6402 var implementationTable = new WeakMap();
6403
6404 function Document(node) {
6405 Node.call(this, node);
6406 }
6407 Document.prototype = Object.create(Node.prototype);
6408
6409 defineWrapGetter(Document, 'documentElement');
6410
6411 // Conceptually both body and head can be in a shadow but suporting that seems
6412 // overkill at this point.
6413 defineWrapGetter(Document, 'body');
6414 defineWrapGetter(Document, 'head');
6415
6416 // document cannot be overridden so we override a bunch of its methods
6417 // directly on the instance.
6418
6419 function wrapMethod(name) {
6420 var original = document[name];
6421 Document.prototype[name] = function() {
6422 return wrap(original.apply(this.impl, arguments));
6423 };
6424 }
6425
6426 [
6427 'createComment',
6428 'createDocumentFragment',
6429 'createElement',
6430 'createElementNS',
6431 'createEvent',
6432 'createEventNS',
6433 'createRange',
6434 'createTextNode',
6435 'getElementById'
6436 ].forEach(wrapMethod);
6437
6438 var originalAdoptNode = document.adoptNode;
6439
6440 function adoptNodeNoRemove(node, doc) {
6441 originalAdoptNode.call(doc.impl, unwrap(node));
6442 adoptSubtree(node, doc);
6443 }
6444
6445 function adoptSubtree(node, doc) {
6446 if (node.shadowRoot)
6447 doc.adoptNode(node.shadowRoot);
6448 if (node instanceof ShadowRoot)
6449 adoptOlderShadowRoots(node, doc);
6450 for (var child = node.firstChild; child; child = child.nextSibling) {
6451 adoptSubtree(child, doc);
6452 }
6453 }
6454
6455 function adoptOlderShadowRoots(shadowRoot, doc) {
6456 var oldShadowRoot = shadowRoot.olderShadowRoot;
6457 if (oldShadowRoot)
6458 doc.adoptNode(oldShadowRoot);
6459 }
6460
6461 var originalGetSelection = document.getSelection;
6462
6463 mixin(Document.prototype, {
6464 adoptNode: function(node) {
6465 if (node.parentNode)
6466 node.parentNode.removeChild(node);
6467 adoptNodeNoRemove(node, this);
6468 return node;
6469 },
6470 elementFromPoint: function(x, y) {
6471 return elementFromPoint(this, this, x, y);
6472 },
6473 importNode: function(node, deep) {
6474 return cloneNode(node, deep, this.impl);
6475 },
6476 getSelection: function() {
6477 renderAllPending();
6478 return new Selection(originalGetSelection.call(unwrap(this)));
6479 }
6480 });
6481
6482 if (document.registerElement) {
6483 var originalRegisterElement = document.registerElement;
6484 Document.prototype.registerElement = function(tagName, object) {
6485 var prototype = object.prototype;
6486
6487 // If we already used the object as a prototype for another custom
6488 // element.
6489 if (scope.nativePrototypeTable.get(prototype)) {
6490 // TODO(arv): DOMException
6491 throw new Error('NotSupportedError');
6492 }
6493
6494 // Find first object on the prototype chain that already have a native
6495 // prototype. Keep track of all the objects before that so we can create
6496 // a similar structure for the native case.
6497 var proto = Object.getPrototypeOf(prototype);
6498 var nativePrototype;
6499 var prototypes = [];
6500 while (proto) {
6501 nativePrototype = scope.nativePrototypeTable.get(proto);
6502 if (nativePrototype)
6503 break;
6504 prototypes.push(proto);
6505 proto = Object.getPrototypeOf(proto);
6506 }
6507
6508 if (!nativePrototype) {
6509 // TODO(arv): DOMException
6510 throw new Error('NotSupportedError');
6511 }
6512
6513 // This works by creating a new prototype object that is empty, but has
6514 // the native prototype as its proto. The original prototype object
6515 // passed into register is used as the wrapper prototype.
6516
6517 var newPrototype = Object.create(nativePrototype);
6518 for (var i = prototypes.length - 1; i >= 0; i--) {
6519 newPrototype = Object.create(newPrototype);
6520 }
6521
6522 // Add callbacks if present.
6523 // Names are taken from:
6524 // 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
6525 // and not from the spec since the spec is out of date.
6526 [
6527 'createdCallback',
6528 'attachedCallback',
6529 'detachedCallback',
6530 'attributeChangedCallback',
6531 ].forEach(function(name) {
6532 var f = prototype[name];
6533 if (!f)
6534 return;
6535 newPrototype[name] = function() {
6536 // if this element has been wrapped prior to registration,
6537 // the wrapper is stale; in this case rewrap
6538 if (!(wrap(this) instanceof CustomElementConstructor)) {
6539 rewrap(this);
6540 }
6541 f.apply(wrap(this), arguments);
6542 };
6543 });
6544
6545 var p = {prototype: newPrototype};
6546 if (object.extends)
6547 p.extends = object.extends;
6548
6549 function CustomElementConstructor(node) {
6550 if (!node) {
6551 if (object.extends) {
6552 return document.createElement(object.extends, tagName);
6553 } else {
6554 return document.createElement(tagName);
6555 }
6556 }
6557 this.impl = node;
6558 }
6559 CustomElementConstructor.prototype = prototype;
6560 CustomElementConstructor.prototype.constructor = CustomElementConstructor;
6561
6562 scope.constructorTable.set(newPrototype, CustomElementConstructor);
6563 scope.nativePrototypeTable.set(prototype, newPrototype);
6564
6565 // registration is synchronous so do it last
6566 var nativeConstructor = originalRegisterElement.call(unwrap(this),
6567 tagName, p);
6568 return CustomElementConstructor;
6569 };
6570
6571 forwardMethodsToWrapper([
6572 window.HTMLDocument || window.Document, // Gecko adds these to HTMLDocume nt
6573 ], [
6574 'registerElement',
6575 ]);
6576 }
6577
6578 // We also override some of the methods on document.body and document.head
6579 // for convenience.
6580 forwardMethodsToWrapper([
6581 window.HTMLBodyElement,
6582 window.HTMLDocument || window.Document, // Gecko adds these to HTMLDocument
6583 window.HTMLHeadElement,
6584 window.HTMLHtmlElement,
6585 ], [
6586 'appendChild',
6587 'compareDocumentPosition',
6588 'contains',
6589 'getElementsByClassName',
6590 'getElementsByTagName',
6591 'getElementsByTagNameNS',
6592 'insertBefore',
6593 'querySelector',
6594 'querySelectorAll',
6595 'removeChild',
6596 'replaceChild',
6597 ].concat(matchesNames));
6598
6599 forwardMethodsToWrapper([
6600 window.HTMLDocument || window.Document, // Gecko adds these to HTMLDocument
6601 ], [
6602 'adoptNode',
6603 'importNode',
6604 'contains',
6605 'createComment',
6606 'createDocumentFragment',
6607 'createElement',
6608 'createElementNS',
6609 'createEvent',
6610 'createEventNS',
6611 'createRange',
6612 'createTextNode',
6613 'elementFromPoint',
6614 'getElementById',
6615 'getSelection',
6616 ]);
6617
6618 mixin(Document.prototype, GetElementsByInterface);
6619 mixin(Document.prototype, ParentNodeInterface);
6620 mixin(Document.prototype, SelectorsInterface);
6621
6622 mixin(Document.prototype, {
6623 get implementation() {
6624 var implementation = implementationTable.get(this);
6625 if (implementation)
6626 return implementation;
6627 implementation =
6628 new DOMImplementation(unwrap(this).implementation);
6629 implementationTable.set(this, implementation);
6630 return implementation;
6631 }
6632 });
6633
6634 registerWrapper(window.Document, Document,
6635 document.implementation.createHTMLDocument(''));
6636
6637 // Both WebKit and Gecko uses HTMLDocument for document. HTML5/DOM only has
6638 // one Document interface and IE implements the standard correctly.
6639 if (window.HTMLDocument)
6640 registerWrapper(window.HTMLDocument, Document);
6641
6642 wrapEventTargetMethods([
6643 window.HTMLBodyElement,
6644 window.HTMLDocument || window.Document, // Gecko adds these to HTMLDocument
6645 window.HTMLHeadElement,
6646 ]);
6647
6648 function DOMImplementation(impl) {
6649 this.impl = impl;
6650 }
6651
6652 function wrapImplMethod(constructor, name) {
6653 var original = document.implementation[name];
6654 constructor.prototype[name] = function() {
6655 return wrap(original.apply(this.impl, arguments));
6656 };
6657 }
6658
6659 function forwardImplMethod(constructor, name) {
6660 var original = document.implementation[name];
6661 constructor.prototype[name] = function() {
6662 return original.apply(this.impl, arguments);
6663 };
6664 }
6665
6666 wrapImplMethod(DOMImplementation, 'createDocumentType');
6667 wrapImplMethod(DOMImplementation, 'createDocument');
6668 wrapImplMethod(DOMImplementation, 'createHTMLDocument');
6669 forwardImplMethod(DOMImplementation, 'hasFeature');
6670
6671 registerWrapper(window.DOMImplementation, DOMImplementation);
6672
6673 forwardMethodsToWrapper([
6674 window.DOMImplementation,
6675 ], [
6676 'createDocumentType',
6677 'createDocument',
6678 'createHTMLDocument',
6679 'hasFeature',
6680 ]);
6681
6682 scope.adoptNodeNoRemove = adoptNodeNoRemove;
6683 scope.wrappers.DOMImplementation = DOMImplementation;
6684 scope.wrappers.Document = Document;
6685
6686 })(window.ShadowDOMPolyfill);
6687
6688 // Copyright 2013 The Polymer Authors. All rights reserved.
6689 // Use of this source code is goverened by a BSD-style
6690 // license that can be found in the LICENSE file.
6691
6692 (function(scope) {
6693 'use strict';
6694
6695 var EventTarget = scope.wrappers.EventTarget;
6696 var Selection = scope.wrappers.Selection;
6697 var mixin = scope.mixin;
6698 var registerWrapper = scope.registerWrapper;
6699 var renderAllPending = scope.renderAllPending;
6700 var unwrap = scope.unwrap;
6701 var unwrapIfNeeded = scope.unwrapIfNeeded;
6702 var wrap = scope.wrap;
6703
6704 var OriginalWindow = window.Window;
6705 var originalGetComputedStyle = window.getComputedStyle;
6706 var originalGetSelection = window.getSelection;
6707
6708 function Window(impl) {
6709 EventTarget.call(this, impl);
6710 }
6711 Window.prototype = Object.create(EventTarget.prototype);
6712
6713 OriginalWindow.prototype.getComputedStyle = function(el, pseudo) {
6714 return wrap(this || window).getComputedStyle(unwrapIfNeeded(el), pseudo);
6715 };
6716
6717 OriginalWindow.prototype.getSelection = function() {
6718 return wrap(this || window).getSelection();
6719 };
6720
6721 // Work around for https://bugzilla.mozilla.org/show_bug.cgi?id=943065
6722 delete window.getComputedStyle;
6723 delete window.getSelection;
6724
6725 ['addEventListener', 'removeEventListener', 'dispatchEvent'].forEach(
6726 function(name) {
6727 OriginalWindow.prototype[name] = function() {
6728 var w = wrap(this || window);
6729 return w[name].apply(w, arguments);
6730 };
6731
6732 // Work around for https://bugzilla.mozilla.org/show_bug.cgi?id=943065
6733 delete window[name];
6734 });
6735
6736 mixin(Window.prototype, {
6737 getComputedStyle: function(el, pseudo) {
6738 renderAllPending();
6739 return originalGetComputedStyle.call(unwrap(this), unwrapIfNeeded(el),
6740 pseudo);
6741 },
6742 getSelection: function() {
6743 renderAllPending();
6744 return new Selection(originalGetSelection.call(unwrap(this)));
6745 },
6746 });
6747
6748 registerWrapper(OriginalWindow, Window);
6749
6750 scope.wrappers.Window = Window;
6751
6752 })(window.ShadowDOMPolyfill);
6753
6754 // Copyright 2013 The Polymer Authors. All rights reserved.
6755 // Use of this source code is goverened by a BSD-style
6756 // license that can be found in the LICENSE file.
6757
6758 (function(scope) {
6759 'use strict';
6760
6761 var isWrapperFor = scope.isWrapperFor;
6762
6763 // This is a list of the elements we currently override the global constructor
6764 // for.
6765 var elements = {
6766 'a': 'HTMLAnchorElement',
6767
6768 // Do not create an applet element by default since it shows a warning in
6769 // IE.
6770 // https://github.com/Polymer/polymer/issues/217
6771 // 'applet': 'HTMLAppletElement',
6772
6773 'area': 'HTMLAreaElement',
6774 'br': 'HTMLBRElement',
6775 'base': 'HTMLBaseElement',
6776 'body': 'HTMLBodyElement',
6777 'button': 'HTMLButtonElement',
6778 // 'command': 'HTMLCommandElement', // Not fully implemented in Gecko.
6779 'dl': 'HTMLDListElement',
6780 'datalist': 'HTMLDataListElement',
6781 'data': 'HTMLDataElement',
6782 'dir': 'HTMLDirectoryElement',
6783 'div': 'HTMLDivElement',
6784 'embed': 'HTMLEmbedElement',
6785 'fieldset': 'HTMLFieldSetElement',
6786 'font': 'HTMLFontElement',
6787 'form': 'HTMLFormElement',
6788 'frame': 'HTMLFrameElement',
6789 'frameset': 'HTMLFrameSetElement',
6790 'hr': 'HTMLHRElement',
6791 'head': 'HTMLHeadElement',
6792 'h1': 'HTMLHeadingElement',
6793 'html': 'HTMLHtmlElement',
6794 'iframe': 'HTMLIFrameElement',
6795 'input': 'HTMLInputElement',
6796 'li': 'HTMLLIElement',
6797 'label': 'HTMLLabelElement',
6798 'legend': 'HTMLLegendElement',
6799 'link': 'HTMLLinkElement',
6800 'map': 'HTMLMapElement',
6801 'marquee': 'HTMLMarqueeElement',
6802 'menu': 'HTMLMenuElement',
6803 'menuitem': 'HTMLMenuItemElement',
6804 'meta': 'HTMLMetaElement',
6805 'meter': 'HTMLMeterElement',
6806 'del': 'HTMLModElement',
6807 'ol': 'HTMLOListElement',
6808 'object': 'HTMLObjectElement',
6809 'optgroup': 'HTMLOptGroupElement',
6810 'option': 'HTMLOptionElement',
6811 'output': 'HTMLOutputElement',
6812 'p': 'HTMLParagraphElement',
6813 'param': 'HTMLParamElement',
6814 'pre': 'HTMLPreElement',
6815 'progress': 'HTMLProgressElement',
6816 'q': 'HTMLQuoteElement',
6817 'script': 'HTMLScriptElement',
6818 'select': 'HTMLSelectElement',
6819 'source': 'HTMLSourceElement',
6820 'span': 'HTMLSpanElement',
6821 'style': 'HTMLStyleElement',
6822 'time': 'HTMLTimeElement',
6823 'caption': 'HTMLTableCaptionElement',
6824 // WebKit and Moz are wrong:
6825 // https://bugs.webkit.org/show_bug.cgi?id=111469
6826 // https://bugzilla.mozilla.org/show_bug.cgi?id=848096
6827 // 'td': 'HTMLTableCellElement',
6828 'col': 'HTMLTableColElement',
6829 'table': 'HTMLTableElement',
6830 'tr': 'HTMLTableRowElement',
6831 'thead': 'HTMLTableSectionElement',
6832 'tbody': 'HTMLTableSectionElement',
6833 'textarea': 'HTMLTextAreaElement',
6834 'track': 'HTMLTrackElement',
6835 'title': 'HTMLTitleElement',
6836 'ul': 'HTMLUListElement',
6837 'video': 'HTMLVideoElement',
6838 };
6839
6840 function overrideConstructor(tagName) {
6841 var nativeConstructorName = elements[tagName];
6842 var nativeConstructor = window[nativeConstructorName];
6843 if (!nativeConstructor)
6844 return;
6845 var element = document.createElement(tagName);
6846 var wrapperConstructor = element.constructor;
6847 window[nativeConstructorName] = wrapperConstructor;
6848 }
6849
6850 Object.keys(elements).forEach(overrideConstructor);
6851
6852 Object.getOwnPropertyNames(scope.wrappers).forEach(function(name) {
6853 window[name] = scope.wrappers[name]
6854 });
6855
6856 // Export for testing.
6857 scope.knownElements = elements;
6858
6859 })(window.ShadowDOMPolyfill);
6860
6861 /*
6862 * Copyright 2013 The Polymer Authors. All rights reserved.
6863 * Use of this source code is governed by a BSD-style
6864 * license that can be found in the LICENSE file.
6865 */
6866 (function() {
6867
6868 // convenient global
6869 window.wrap = ShadowDOMPolyfill.wrapIfNeeded;
6870 window.unwrap = ShadowDOMPolyfill.unwrapIfNeeded;
6871
6872 // users may want to customize other types
6873 // TODO(sjmiles): 'button' is now supported by ShadowDOMPolyfill, but
6874 // I've left this code here in case we need to temporarily patch another
6875 // type
6876 /*
6877 (function() {
6878 var elts = {HTMLButtonElement: 'button'};
6879 for (var c in elts) {
6880 window[c] = function() { throw 'Patched Constructor'; };
6881 window[c].prototype = Object.getPrototypeOf(
6882 document.createElement(elts[c]));
6883 }
6884 })();
6885 */
6886
6887 // patch in prefixed name
6888 Object.defineProperty(Element.prototype, 'webkitShadowRoot',
6889 Object.getOwnPropertyDescriptor(Element.prototype, 'shadowRoot'));
6890
6891 var originalCreateShadowRoot = Element.prototype.createShadowRoot;
6892 Element.prototype.createShadowRoot = function() {
6893 var root = originalCreateShadowRoot.call(this);
6894 CustomElements.watchShadow(this);
6895 return root;
6896 };
6897
6898 Element.prototype.webkitCreateShadowRoot = Element.prototype.createShadowRoot;
6899 })();
6900
6901 /*
6902 * Copyright 2012 The Polymer Authors. All rights reserved.
6903 * Use of this source code is governed by a BSD-style
6904 * license that can be found in the LICENSE file.
6905 */
6906
6907 /*
6908 This is a limited shim for ShadowDOM css styling.
6909 https://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/shadow/index.html#style s
6910
6911 The intention here is to support only the styling features which can be
6912 relatively simply implemented. The goal is to allow users to avoid the
6913 most obvious pitfalls and do so without compromising performance significantly .
6914 For ShadowDOM styling that's not covered here, a set of best practices
6915 can be provided that should allow users to accomplish more complex styling.
6916
6917 The following is a list of specific ShadowDOM styling features and a brief
6918 discussion of the approach used to shim.
6919
6920 Shimmed features:
6921
6922 * :host, :ancestor: ShadowDOM allows styling of the shadowRoot's host
6923 element using the :host rule. To shim this feature, the :host styles are
6924 reformatted and prefixed with a given scope name and promoted to a
6925 document level stylesheet.
6926 For example, given a scope name of .foo, a rule like this:
6927
6928 :host {
6929 background: red;
6930 }
6931 }
6932
6933 becomes:
6934
6935 .foo {
6936 background: red;
6937 }
6938
6939 * encapsultion: Styles defined within ShadowDOM, apply only to
6940 dom inside the ShadowDOM. Polymer uses one of two techniques to imlement
6941 this feature.
6942
6943 By default, rules are prefixed with the host element tag name
6944 as a descendant selector. This ensures styling does not leak out of the 'top'
6945 of the element's ShadowDOM. For example,
6946
6947 div {
6948 font-weight: bold;
6949 }
6950
6951 becomes:
6952
6953 x-foo div {
6954 font-weight: bold;
6955 }
6956
6957 becomes:
6958
6959
6960 Alternatively, if Platform.ShadowCSS.strictStyling is set to true then
6961 selectors are scoped by adding an attribute selector suffix to each
6962 simple selector that contains the host element tag name. Each element
6963 in the element's ShadowDOM template is also given the scope attribute.
6964 Thus, these rules match only elements that have the scope attribute.
6965 For example, given a scope name of x-foo, a rule like this:
6966
6967 div {
6968 font-weight: bold;
6969 }
6970
6971 becomes:
6972
6973 div[x-foo] {
6974 font-weight: bold;
6975 }
6976
6977 Note that elements that are dynamically added to a scope must have the scope
6978 selector added to them manually.
6979
6980 * upper/lower bound encapsulation: Styles which are defined outside a
6981 shadowRoot should not cross the ShadowDOM boundary and should not apply
6982 inside a shadowRoot.
6983
6984 This styling behavior is not emulated. Some possible ways to do this that
6985 were rejected due to complexity and/or performance concerns include: (1) reset
6986 every possible property for every possible selector for a given scope name;
6987 (2) re-implement css in javascript.
6988
6989 As an alternative, users should make sure to use selectors
6990 specific to the scope in which they are working.
6991
6992 * ::distributed: This behavior is not emulated. It's often not necessary
6993 to style the contents of a specific insertion point and instead, descendants
6994 of the host element can be styled selectively. Users can also create an
6995 extra node around an insertion point and style that node's contents
6996 via descendent selectors. For example, with a shadowRoot like this:
6997
6998 <style>
6999 ::content(div) {
7000 background: red;
7001 }
7002 </style>
7003 <content></content>
7004
7005 could become:
7006
7007 <style>
7008 / *@polyfill .content-container div * /
7009 ::content(div) {
7010 background: red;
7011 }
7012 </style>
7013 <div class="content-container">
7014 <content></content>
7015 </div>
7016
7017 Note the use of @polyfill in the comment above a ShadowDOM specific style
7018 declaration. This is a directive to the styling shim to use the selector
7019 in comments in lieu of the next selector when running under polyfill.
7020 */
7021 (function(scope) {
7022
7023 var ShadowCSS = {
7024 strictStyling: false,
7025 registry: {},
7026 // Shim styles for a given root associated with a name and extendsName
7027 // 1. cache root styles by name
7028 // 2. optionally tag root nodes with scope name
7029 // 3. shim polyfill directives /* @polyfill */ and /* @polyfill-rule */
7030 // 4. shim :host and scoping
7031 shimStyling: function(root, name, extendsName) {
7032 var typeExtension = this.isTypeExtension(extendsName);
7033 // use caching to make working with styles nodes easier and to facilitate
7034 // lookup of extendee
7035 var def = this.registerDefinition(root, name, extendsName);
7036 // find styles and apply shimming...
7037 if (this.strictStyling) {
7038 this.applyScopeToContent(root, name);
7039 }
7040 var cssText = this.stylesToShimmedCssText(def.rootStyles, def.scopeStyles,
7041 name, typeExtension);
7042 // provide shimmedStyle for user extensibility
7043 def.shimmedStyle = cssTextToStyle(cssText);
7044 if (root) {
7045 root.shimmedStyle = def.shimmedStyle;
7046 }
7047 // remove existing style elements
7048 for (var i=0, l=def.rootStyles.length, s; (i<l) && (s=def.rootStyles[i]);
7049 i++) {
7050 s.parentNode.removeChild(s);
7051 }
7052 // add style to document
7053 addCssToDocument(cssText);
7054 },
7055 // apply @polyfill rules + :host and scope shimming
7056 stylesToShimmedCssText: function(rootStyles, scopeStyles, name,
7057 typeExtension) {
7058 name = name || '';
7059 // insert @polyfill and @polyfill-rule rules into style elements
7060 // scoping process takes care of shimming these
7061 this.insertPolyfillDirectives(rootStyles);
7062 this.insertPolyfillRules(rootStyles);
7063 var cssText = this.shimScoping(scopeStyles, name, typeExtension);
7064 // note: we only need to do rootStyles since these are unscoped.
7065 cssText += this.extractPolyfillUnscopedRules(rootStyles);
7066 return cssText;
7067 },
7068 registerDefinition: function(root, name, extendsName) {
7069 var def = this.registry[name] = {
7070 root: root,
7071 name: name,
7072 extendsName: extendsName
7073 }
7074 var styles = root ? root.querySelectorAll('style') : [];
7075 styles = styles ? Array.prototype.slice.call(styles, 0) : [];
7076 def.rootStyles = styles;
7077 def.scopeStyles = def.rootStyles;
7078 var extendee = this.registry[def.extendsName];
7079 if (extendee && (!root || root.querySelector('shadow'))) {
7080 def.scopeStyles = extendee.scopeStyles.concat(def.scopeStyles);
7081 }
7082 return def;
7083 },
7084 isTypeExtension: function(extendsName) {
7085 return extendsName && extendsName.indexOf('-') < 0;
7086 },
7087 applyScopeToContent: function(root, name) {
7088 if (root) {
7089 // add the name attribute to each node in root.
7090 Array.prototype.forEach.call(root.querySelectorAll('*'),
7091 function(node) {
7092 node.setAttribute(name, '');
7093 });
7094 // and template contents too
7095 Array.prototype.forEach.call(root.querySelectorAll('template'),
7096 function(template) {
7097 this.applyScopeToContent(template.content, name);
7098 },
7099 this);
7100 }
7101 },
7102 /*
7103 * Process styles to convert native ShadowDOM rules that will trip
7104 * up the css parser; we rely on decorating the stylesheet with comments.
7105 *
7106 * For example, we convert this rule:
7107 *
7108 * (comment start) @polyfill :host menu-item (comment end)
7109 * shadow::-webkit-distributed(menu-item) {
7110 *
7111 * to this:
7112 *
7113 * scopeName menu-item {
7114 *
7115 **/
7116 insertPolyfillDirectives: function(styles) {
7117 if (styles) {
7118 Array.prototype.forEach.call(styles, function(s) {
7119 s.textContent = this.insertPolyfillDirectivesInCssText(s.textContent);
7120 }, this);
7121 }
7122 },
7123 insertPolyfillDirectivesInCssText: function(cssText) {
7124 return cssText.replace(cssPolyfillCommentRe, function(match, p1) {
7125 // remove end comment delimiter and add block start
7126 return p1.slice(0, -2) + '{';
7127 });
7128 },
7129 /*
7130 * Process styles to add rules which will only apply under the polyfill
7131 *
7132 * For example, we convert this rule:
7133 *
7134 * (comment start) @polyfill-rule :host menu-item {
7135 * ... } (comment end)
7136 *
7137 * to this:
7138 *
7139 * scopeName menu-item {...}
7140 *
7141 **/
7142 insertPolyfillRules: function(styles) {
7143 if (styles) {
7144 Array.prototype.forEach.call(styles, function(s) {
7145 s.textContent = this.insertPolyfillRulesInCssText(s.textContent);
7146 }, this);
7147 }
7148 },
7149 insertPolyfillRulesInCssText: function(cssText) {
7150 return cssText.replace(cssPolyfillRuleCommentRe, function(match, p1) {
7151 // remove end comment delimiter
7152 return p1.slice(0, -1);
7153 });
7154 },
7155 /*
7156 * Process styles to add rules which will only apply under the polyfill
7157 * and do not process via CSSOM. (CSSOM is destructive to rules on rare
7158 * occasions, e.g. -webkit-calc on Safari.)
7159 * For example, we convert this rule:
7160 *
7161 * (comment start) @polyfill-unscoped-rule menu-item {
7162 * ... } (comment end)
7163 *
7164 * to this:
7165 *
7166 * menu-item {...}
7167 *
7168 **/
7169 extractPolyfillUnscopedRules: function(styles) {
7170 var cssText = '';
7171 if (styles) {
7172 Array.prototype.forEach.call(styles, function(s) {
7173 cssText += this.extractPolyfillUnscopedRulesFromCssText(
7174 s.textContent) + '\n\n';
7175 }, this);
7176 }
7177 return cssText;
7178 },
7179 extractPolyfillUnscopedRulesFromCssText: function(cssText) {
7180 var r = '', matches;
7181 while (matches = cssPolyfillUnscopedRuleCommentRe.exec(cssText)) {
7182 r += matches[1].slice(0, -1) + '\n\n';
7183 }
7184 return r;
7185 },
7186 /* Ensure styles are scoped. Pseudo-scoping takes a rule like:
7187 *
7188 * .foo {... }
7189 *
7190 * and converts this to
7191 *
7192 * scopeName .foo { ... }
7193 */
7194 shimScoping: function(styles, name, typeExtension) {
7195 if (styles) {
7196 return this.convertScopedStyles(styles, name, typeExtension);
7197 }
7198 },
7199 convertScopedStyles: function(styles, name, typeExtension) {
7200 var cssText = stylesToCssText(styles);
7201 cssText = this.insertPolyfillHostInCssText(cssText);
7202 cssText = this.convertColonHost(cssText);
7203 cssText = this.convertColonAncestor(cssText);
7204 cssText = this.convertCombinators(cssText);
7205 var rules = cssToRules(cssText);
7206 if (name) {
7207 cssText = this.scopeRules(rules, name, typeExtension);
7208 }
7209 return cssText;
7210 },
7211 /*
7212 * convert a rule like :host(.foo) > .bar { }
7213 *
7214 * to
7215 *
7216 * scopeName.foo > .bar
7217 */
7218 convertColonHost: function(cssText) {
7219 return this.convertColonRule(cssText, cssColonHostRe,
7220 this.colonHostPartReplacer);
7221 },
7222 /*
7223 * convert a rule like :ancestor(.foo) > .bar { }
7224 *
7225 * to
7226 *
7227 * scopeName.foo > .bar, .foo scopeName > .bar { }
7228 *
7229 * and
7230 *
7231 * :ancestor(.foo:host) .bar { ... }
7232 *
7233 * to
7234 *
7235 * scopeName.foo .bar { ... }
7236 */
7237 convertColonAncestor: function(cssText) {
7238 return this.convertColonRule(cssText, cssColonAncestorRe,
7239 this.colonAncestorPartReplacer);
7240 },
7241 convertColonRule: function(cssText, regExp, partReplacer) {
7242 // p1 = :host, p2 = contents of (), p3 rest of rule
7243 return cssText.replace(regExp, function(m, p1, p2, p3) {
7244 p1 = polyfillHostNoCombinator;
7245 if (p2) {
7246 var parts = p2.split(','), r = [];
7247 for (var i=0, l=parts.length, p; (i<l) && (p=parts[i]); i++) {
7248 p = p.trim();
7249 r.push(partReplacer(p1, p, p3));
7250 }
7251 return r.join(',');
7252 } else {
7253 return p1 + p3;
7254 }
7255 });
7256 },
7257 colonAncestorPartReplacer: function(host, part, suffix) {
7258 if (part.match(polyfillHost)) {
7259 return this.colonHostPartReplacer(host, part, suffix);
7260 } else {
7261 return host + part + suffix + ', ' + part + ' ' + host + suffix;
7262 }
7263 },
7264 colonHostPartReplacer: function(host, part, suffix) {
7265 return host + part.replace(polyfillHost, '') + suffix;
7266 },
7267 /*
7268 * Convert ^ and ^^ combinators by replacing with space.
7269 */
7270 convertCombinators: function(cssText) {
7271 return cssText.replace(/\^\^/g, ' ').replace(/\^/g, ' ');
7272 },
7273 // change a selector like 'div' to 'name div'
7274 scopeRules: function(cssRules, name, typeExtension) {
7275 var cssText = '';
7276 Array.prototype.forEach.call(cssRules, function(rule) {
7277 if (rule.selectorText && (rule.style && rule.style.cssText)) {
7278 cssText += this.scopeSelector(rule.selectorText, name, typeExtension,
7279 this.strictStyling) + ' {\n\t';
7280 cssText += this.propertiesFromRule(rule) + '\n}\n\n';
7281 } else if (rule.media) {
7282 cssText += '@media ' + rule.media.mediaText + ' {\n';
7283 cssText += this.scopeRules(rule.cssRules, name, typeExtension);
7284 cssText += '\n}\n\n';
7285 } else if (rule.cssText) {
7286 cssText += rule.cssText + '\n\n';
7287 }
7288 }, this);
7289 return cssText;
7290 },
7291 scopeSelector: function(selector, name, typeExtension, strict) {
7292 var r = [], parts = selector.split(',');
7293 parts.forEach(function(p) {
7294 p = p.trim();
7295 if (this.selectorNeedsScoping(p, name, typeExtension)) {
7296 p = (strict && !p.match(polyfillHostNoCombinator)) ?
7297 this.applyStrictSelectorScope(p, name) :
7298 this.applySimpleSelectorScope(p, name, typeExtension);
7299 }
7300 r.push(p);
7301 }, this);
7302 return r.join(', ');
7303 },
7304 selectorNeedsScoping: function(selector, name, typeExtension) {
7305 var re = this.makeScopeMatcher(name, typeExtension);
7306 return !selector.match(re);
7307 },
7308 makeScopeMatcher: function(name, typeExtension) {
7309 var matchScope = typeExtension ? '\\[is=[\'"]?' + name + '[\'"]?\\]' : name;
7310 return new RegExp('^(' + matchScope + ')' + selectorReSuffix, 'm');
7311 },
7312 // scope via name and [is=name]
7313 applySimpleSelectorScope: function(selector, name, typeExtension) {
7314 var scoper = typeExtension ? '[is=' + name + ']' : name;
7315 if (selector.match(polyfillHostRe)) {
7316 selector = selector.replace(polyfillHostNoCombinator, scoper);
7317 return selector.replace(polyfillHostRe, scoper + ' ');
7318 } else {
7319 return scoper + ' ' + selector;
7320 }
7321 },
7322 // return a selector with [name] suffix on each simple selector
7323 // e.g. .foo.bar > .zot becomes .foo[name].bar[name] > .zot[name]
7324 applyStrictSelectorScope: function(selector, name) {
7325 var splits = [' ', '>', '+', '~'],
7326 scoped = selector,
7327 attrName = '[' + name + ']';
7328 splits.forEach(function(sep) {
7329 var parts = scoped.split(sep);
7330 scoped = parts.map(function(p) {
7331 // remove :host since it should be unnecessary
7332 var t = p.trim().replace(polyfillHostRe, '');
7333 if (t && (splits.indexOf(t) < 0) && (t.indexOf(attrName) < 0)) {
7334 p = t.replace(/([^:]*)(:*)(.*)/, '$1' + attrName + '$2$3')
7335 }
7336 return p;
7337 }).join(sep);
7338 });
7339 return scoped;
7340 },
7341 insertPolyfillHostInCssText: function(selector) {
7342 return selector.replace(hostRe, polyfillHost).replace(colonHostRe,
7343 polyfillHost).replace(colonAncestorRe, polyfillAncestor);
7344 },
7345 propertiesFromRule: function(rule) {
7346 // TODO(sorvell): Safari cssom incorrectly removes quotes from the content
7347 // property. (https://bugs.webkit.org/show_bug.cgi?id=118045)
7348 if (rule.style.content && !rule.style.content.match(/['"]+/)) {
7349 return rule.style.cssText.replace(/content:[^;]*;/g, 'content: \'' +
7350 rule.style.content + '\';');
7351 }
7352 return rule.style.cssText;
7353 }
7354 };
7355
7356 var selectorRe = /([^{]*)({[\s\S]*?})/gim,
7357 cssCommentRe = /\/\*[^*]*\*+([^/*][^*]*\*+)*\//gim,
7358 cssPolyfillCommentRe = /\/\*\s*@polyfill ([^*]*\*+([^/*][^*]*\*+)*\/)([^{]*? ){/gim,
7359 cssPolyfillRuleCommentRe = /\/\*\s@polyfill-rule([^*]*\*+([^/*][^*]*\*+)*)\/ /gim,
7360 cssPolyfillUnscopedRuleCommentRe = /\/\*\s@polyfill-unscoped-rule([^*]*\*+([ ^/*][^*]*\*+)*)\//gim,
7361 cssPseudoRe = /::(x-[^\s{,(]*)/gim,
7362 cssPartRe = /::part\(([^)]*)\)/gim,
7363 // note: :host pre-processed to -shadowcsshost.
7364 polyfillHost = '-shadowcsshost',
7365 // note: :ancestor pre-processed to -shadowcssancestor.
7366 polyfillAncestor = '-shadowcssancestor',
7367 parenSuffix = ')(?:\\((' +
7368 '(?:\\([^)(]*\\)|[^)(]*)+?' +
7369 ')\\))?([^,{]*)';
7370 cssColonHostRe = new RegExp('(' + polyfillHost + parenSuffix, 'gim'),
7371 cssColonAncestorRe = new RegExp('(' + polyfillAncestor + parenSuffix, 'gim') ,
7372 selectorReSuffix = '([>\\s~+\[.,{:][\\s\\S]*)?$',
7373 hostRe = /@host/gim,
7374 colonHostRe = /\:host/gim,
7375 colonAncestorRe = /\:ancestor/gim,
7376 /* host name without combinator */
7377 polyfillHostNoCombinator = polyfillHost + '-no-combinator',
7378 polyfillHostRe = new RegExp(polyfillHost, 'gim');
7379 polyfillAncestorRe = new RegExp(polyfillAncestor, 'gim');
7380
7381 function stylesToCssText(styles, preserveComments) {
7382 var cssText = '';
7383 Array.prototype.forEach.call(styles, function(s) {
7384 cssText += s.textContent + '\n\n';
7385 });
7386 // strip comments for easier processing
7387 if (!preserveComments) {
7388 cssText = cssText.replace(cssCommentRe, '');
7389 }
7390 return cssText;
7391 }
7392
7393 function cssTextToStyle(cssText) {
7394 var style = document.createElement('style');
7395 style.textContent = cssText;
7396 return style;
7397 }
7398
7399 function cssToRules(cssText) {
7400 var style = cssTextToStyle(cssText);
7401 document.head.appendChild(style);
7402 var rules = style.sheet.cssRules;
7403 style.parentNode.removeChild(style);
7404 return rules;
7405 }
7406
7407 function rulesToCss(cssRules) {
7408 for (var i=0, css=[]; i < cssRules.length; i++) {
7409 css.push(cssRules[i].cssText);
7410 }
7411 return css.join('\n\n');
7412 }
7413
7414 function addCssToDocument(cssText) {
7415 if (cssText) {
7416 getSheet().appendChild(document.createTextNode(cssText));
7417 }
7418 }
7419
7420 var SHIM_ATTRIBUTE = 'shim-shadowdom';
7421 var SHIMMED_ATTRIBUTE = 'shim-shadowdom-css';
7422
7423 var sheet;
7424 function getSheet() {
7425 if (!sheet) {
7426 sheet = document.createElement("style");
7427 sheet.setAttribute(SHIMMED_ATTRIBUTE, '');
7428 sheet[SHIMMED_ATTRIBUTE] = true;
7429 }
7430 return sheet;
7431 }
7432
7433 // add polyfill stylesheet to document
7434 if (window.ShadowDOMPolyfill) {
7435 addCssToDocument('style { display: none !important; }\n');
7436 var doc = wrap(document);
7437 var head = doc.querySelector('head');
7438 head.insertBefore(getSheet(), head.childNodes[0]);
7439
7440 // TODO(sorvell): monkey-patching HTMLImports is abusive;
7441 // consider a better solution.
7442 document.addEventListener('DOMContentLoaded', function() {
7443 var urlResolver = scope.urlResolver;
7444
7445 if (window.HTMLImports && !HTMLImports.useNative) {
7446 var SHIM_SHEET_SELECTOR = 'link[rel=stylesheet]' +
7447 '[' + SHIM_ATTRIBUTE + ']';
7448 var SHIM_STYLE_SELECTOR = 'style[' + SHIM_ATTRIBUTE + ']';
7449 HTMLImports.importer.documentPreloadSelectors += ',' + SHIM_SHEET_SELECTOR ;
7450 HTMLImports.importer.importsPreloadSelectors += ',' + SHIM_SHEET_SELECTOR;
7451
7452 HTMLImports.parser.documentSelectors = [
7453 HTMLImports.parser.documentSelectors,
7454 SHIM_SHEET_SELECTOR,
7455 SHIM_STYLE_SELECTOR
7456 ].join(',');
7457
7458 HTMLImports.parser.parseGeneric = function(elt) {
7459 if (elt[SHIMMED_ATTRIBUTE]) {
7460 return;
7461 }
7462 var style = elt.__importElement || elt;
7463 if (elt.__resource) {
7464 style = elt.ownerDocument.createElement('style');
7465 style.textContent = urlResolver.resolveCssText(
7466 elt.__resource, elt.href);
7467 } else {
7468 urlResolver.resolveStyles(style);
7469 }
7470 var styles = [style];
7471 style.textContent = ShadowCSS.stylesToShimmedCssText(styles, styles);
7472 style.removeAttribute(SHIM_ATTRIBUTE, '');
7473 style.setAttribute(SHIMMED_ATTRIBUTE, '');
7474 style[SHIMMED_ATTRIBUTE] = true;
7475 // place in document
7476 if (style.parentNode !== head) {
7477 // replace links in head
7478 if (elt.parentNode === head) {
7479 head.replaceChild(style, elt);
7480 } else {
7481 head.appendChild(style);
7482 }
7483 }
7484 style.__importParsed = true
7485 this.markParsingComplete(elt);
7486 }
7487
7488 var hasResource = HTMLImports.parser.hasResource;
7489 HTMLImports.parser.hasResource = function(node) {
7490 if (node.localName === 'link' && node.rel === 'stylesheet' &&
7491 node.hasAttribute(SHIM_ATTRIBUTE)) {
7492 return (node.__resource);
7493 } else {
7494 return hasResource.call(this, node);
7495 }
7496 }
7497
7498 }
7499 });
7500 }
7501
7502 // exports
7503 scope.ShadowCSS = ShadowCSS;
7504
7505 })(window.Platform);
7506 } else {
7507 /*
7508 * Copyright 2013 The Polymer Authors. All rights reserved.
7509 * Use of this source code is governed by a BSD-style
7510 * license that can be found in the LICENSE file.
7511 */
7512 (function() {
7513
7514 // poor man's adapter for template.content on various platform scenarios
7515 window.templateContent = window.templateContent || function(inTemplate) {
7516 return inTemplate.content;
7517 };
7518
7519 // so we can call wrap/unwrap without testing for ShadowDOMPolyfill
7520
7521 window.wrap = window.unwrap = function(n){
7522 return n;
7523 }
7524
7525 var originalCreateShadowRoot = Element.prototype.webkitCreateShadowRoot;
7526 Element.prototype.webkitCreateShadowRoot = function() {
7527 var elderRoot = this.webkitShadowRoot;
7528 var root = originalCreateShadowRoot.call(this);
7529 root.olderShadowRoot = elderRoot;
7530 root.host = this;
7531 CustomElements.watchShadow(this);
7532 return root;
7533 }
7534
7535 Object.defineProperties(Element.prototype, {
7536 shadowRoot: {
7537 get: function() {
7538 return this.webkitShadowRoot;
7539 }
7540 },
7541 createShadowRoot: {
7542 value: function() {
7543 return this.webkitCreateShadowRoot();
7544 }
7545 }
7546 });
7547
7548 window.templateContent = function(inTemplate) {
7549 // if MDV exists, it may need to boostrap this template to reveal content
7550 if (window.HTMLTemplateElement && HTMLTemplateElement.bootstrap) {
7551 HTMLTemplateElement.bootstrap(inTemplate);
7552 }
7553 // fallback when there is no Shadow DOM polyfill, no MDV polyfill, and no
7554 // native template support
7555 if (!inTemplate.content && !inTemplate._content) {
7556 var frag = document.createDocumentFragment();
7557 while (inTemplate.firstChild) {
7558 frag.appendChild(inTemplate.firstChild);
7559 }
7560 inTemplate._content = frag;
7561 }
7562 return inTemplate.content || inTemplate._content;
7563 };
7564
7565 })();
7566 }
7567 /* Any copyright is dedicated to the Public Domain.
7568 * http://creativecommons.org/publicdomain/zero/1.0/ */
7569
7570 (function(scope) {
7571 'use strict';
7572
7573 // feature detect for URL constructor
7574 var hasWorkingUrl = false;
7575 if (!scope.forceJURL) {
7576 try {
7577 var u = new URL('b', 'http://a');
7578 hasWorkingUrl = u.href === 'http://a/b';
7579 } catch(e) {}
7580 }
7581
7582 if (hasWorkingUrl)
7583 return;
7584
7585 var relative = Object.create(null);
7586 relative['ftp'] = 21;
7587 relative['file'] = 0;
7588 relative['gopher'] = 70;
7589 relative['http'] = 80;
7590 relative['https'] = 443;
7591 relative['ws'] = 80;
7592 relative['wss'] = 443;
7593
7594 var relativePathDotMapping = Object.create(null);
7595 relativePathDotMapping['%2e'] = '.';
7596 relativePathDotMapping['.%2e'] = '..';
7597 relativePathDotMapping['%2e.'] = '..';
7598 relativePathDotMapping['%2e%2e'] = '..';
7599
7600 function isRelativeScheme(scheme) {
7601 return relative[scheme] !== undefined;
7602 }
7603
7604 function invalid() {
7605 clear.call(this);
7606 this._isInvalid = true;
7607 }
7608
7609 function IDNAToASCII(h) {
7610 if ('' == h) {
7611 invalid.call(this)
7612 }
7613 // XXX
7614 return h.toLowerCase()
7615 }
7616
7617 function percentEscape(c) {
7618 var unicode = c.charCodeAt(0);
7619 if (unicode > 0x20 &&
7620 unicode < 0x7F &&
7621 // " # < > ? `
7622 [0x22, 0x23, 0x3C, 0x3E, 0x3F, 0x60].indexOf(unicode) == -1
7623 ) {
7624 return c;
7625 }
7626 return encodeURIComponent(c);
7627 }
7628
7629 function percentEscapeQuery(c) {
7630 // XXX This actually needs to encode c using encoding and then
7631 // convert the bytes one-by-one.
7632
7633 var unicode = c.charCodeAt(0);
7634 if (unicode > 0x20 &&
7635 unicode < 0x7F &&
7636 // " # < > ` (do not escape '?')
7637 [0x22, 0x23, 0x3C, 0x3E, 0x60].indexOf(unicode) == -1
7638 ) {
7639 return c;
7640 }
7641 return encodeURIComponent(c);
7642 }
7643
7644 var EOF = undefined,
7645 ALPHA = /[a-zA-Z]/,
7646 ALPHANUMERIC = /[a-zA-Z0-9\+\-\.]/;
7647
7648 function parse(input, stateOverride, base) {
7649 function err(message) {
7650 errors.push(message)
7651 }
7652
7653 var state = stateOverride || 'scheme start',
7654 cursor = 0,
7655 buffer = '',
7656 seenAt = false,
7657 seenBracket = false,
7658 errors = [];
7659
7660 loop: while ((input[cursor - 1] != EOF || cursor == 0) && !this._isInvalid) {
7661 var c = input[cursor];
7662 switch (state) {
7663 case 'scheme start':
7664 if (c && ALPHA.test(c)) {
7665 buffer += c.toLowerCase(); // ASCII-safe
7666 state = 'scheme';
7667 } else if (!stateOverride) {
7668 buffer = '';
7669 state = 'no scheme';
7670 continue;
7671 } else {
7672 err('Invalid scheme.');
7673 break loop;
7674 }
7675 break;
7676
7677 case 'scheme':
7678 if (c && ALPHANUMERIC.test(c)) {
7679 buffer += c.toLowerCase(); // ASCII-safe
7680 } else if (':' == c) {
7681 this._scheme = buffer;
7682 buffer = '';
7683 if (stateOverride) {
7684 break loop;
7685 }
7686 if (isRelativeScheme(this._scheme)) {
7687 this._isRelative = true;
7688 }
7689 if ('file' == this._scheme) {
7690 state = 'relative';
7691 } else if (this._isRelative && base && base._scheme == this._scheme) {
7692 state = 'relative or authority';
7693 } else if (this._isRelative) {
7694 state = 'authority first slash';
7695 } else {
7696 state = 'scheme data';
7697 }
7698 } else if (!stateOverride) {
7699 buffer = '';
7700 cursor = 0;
7701 state = 'no scheme';
7702 continue;
7703 } else if (EOF == c) {
7704 break loop;
7705 } else {
7706 err('Code point not allowed in scheme: ' + c)
7707 break loop;
7708 }
7709 break;
7710
7711 case 'scheme data':
7712 if ('?' == c) {
7713 query = '?';
7714 state = 'query';
7715 } else if ('#' == c) {
7716 this._fragment = '#';
7717 state = 'fragment';
7718 } else {
7719 // XXX error handling
7720 if (EOF != c && '\t' != c && '\n' != c && '\r' != c) {
7721 this._schemeData += percentEscape(c);
7722 }
7723 }
7724 break;
7725
7726 case 'no scheme':
7727 if (!base || !(isRelativeScheme(base._scheme))) {
7728 err('Missing scheme.');
7729 invalid.call(this);
7730 } else {
7731 state = 'relative';
7732 continue;
7733 }
7734 break;
7735
7736 case 'relative or authority':
7737 if ('/' == c && '/' == input[cursor+1]) {
7738 state = 'authority ignore slashes';
7739 } else {
7740 err('Expected /, got: ' + c);
7741 state = 'relative';
7742 continue
7743 }
7744 break;
7745
7746 case 'relative':
7747 this._isRelative = true;
7748 if ('file' != this._scheme)
7749 this._scheme = base._scheme;
7750 if (EOF == c) {
7751 this._host = base._host;
7752 this._port = base._port;
7753 this._path = base._path.slice();
7754 this._query = base._query;
7755 break loop;
7756 } else if ('/' == c || '\\' == c) {
7757 if ('\\' == c)
7758 err('\\ is an invalid code point.');
7759 state = 'relative slash';
7760 } else if ('?' == c) {
7761 this._host = base._host;
7762 this._port = base._port;
7763 this._path = base._path.slice();
7764 this._query = '?';
7765 state = 'query';
7766 } else if ('#' == c) {
7767 this._host = base._host;
7768 this._port = base._port;
7769 this._path = base._path.slice();
7770 this._query = base._query;
7771 this._fragment = '#';
7772 state = 'fragment';
7773 } else {
7774 var nextC = input[cursor+1]
7775 var nextNextC = input[cursor+2]
7776 if (
7777 'file' != this._scheme || !ALPHA.test(c) ||
7778 (nextC != ':' && nextC != '|') ||
7779 (EOF != nextNextC && '/' != nextNextC && '\\' != nextNextC && '?' != nextNextC && '#' != nextNextC)) {
7780 this._host = base._host;
7781 this._port = base._port;
7782 this._path = base._path.slice();
7783 this._path.pop();
7784 }
7785 state = 'relative path';
7786 continue;
7787 }
7788 break;
7789
7790 case 'relative slash':
7791 if ('/' == c || '\\' == c) {
7792 if ('\\' == c) {
7793 err('\\ is an invalid code point.');
7794 }
7795 if ('file' == this._scheme) {
7796 state = 'file host';
7797 } else {
7798 state = 'authority ignore slashes';
7799 }
7800 } else {
7801 if ('file' != this._scheme) {
7802 this._host = base._host;
7803 this._port = base._port;
7804 }
7805 state = 'relative path';
7806 continue;
7807 }
7808 break;
7809
7810 case 'authority first slash':
7811 if ('/' == c) {
7812 state = 'authority second slash';
7813 } else {
7814 err("Expected '/', got: " + c);
7815 state = 'authority ignore slashes';
7816 continue;
7817 }
7818 break;
7819
7820 case 'authority second slash':
7821 state = 'authority ignore slashes';
7822 if ('/' != c) {
7823 err("Expected '/', got: " + c);
7824 continue;
7825 }
7826 break;
7827
7828 case 'authority ignore slashes':
7829 if ('/' != c && '\\' != c) {
7830 state = 'authority';
7831 continue;
7832 } else {
7833 err('Expected authority, got: ' + c);
7834 }
7835 break;
7836
7837 case 'authority':
7838 if ('@' == c) {
7839 if (seenAt) {
7840 err('@ already seen.');
7841 buffer += '%40';
7842 }
7843 seenAt = true;
7844 for (var i = 0; i < buffer.length; i++) {
7845 var cp = buffer[i];
7846 if ('\t' == cp || '\n' == cp || '\r' == cp) {
7847 err('Invalid whitespace in authority.');
7848 continue;
7849 }
7850 // XXX check URL code points
7851 if (':' == cp && null === this._password) {
7852 this._password = '';
7853 continue;
7854 }
7855 var tempC = percentEscape(cp);
7856 (null !== this._password) ? this._password += tempC : this._userna me += tempC;
7857 }
7858 buffer = '';
7859 } else if (EOF == c || '/' == c || '\\' == c || '?' == c || '#' == c) {
7860 cursor -= buffer.length;
7861 buffer = '';
7862 state = 'host';
7863 continue;
7864 } else {
7865 buffer += c;
7866 }
7867 break;
7868
7869 case 'file host':
7870 if (EOF == c || '/' == c || '\\' == c || '?' == c || '#' == c) {
7871 if (buffer.length == 2 && ALPHA.test(buffer[0]) && (buffer[1] == ':' || buffer[1] == '|')) {
7872 state = 'relative path';
7873 } else if (buffer.length == 0) {
7874 state = 'relative path start';
7875 } else {
7876 this._host = IDNAToASCII.call(this, buffer);
7877 buffer = '';
7878 state = 'relative path start';
7879 }
7880 continue;
7881 } else if ('\t' == c || '\n' == c || '\r' == c) {
7882 err('Invalid whitespace in file host.');
7883 } else {
7884 buffer += c;
7885 }
7886 break;
7887
7888 case 'host':
7889 case 'hostname':
7890 if (':' == c && !seenBracket) {
7891 // XXX host parsing
7892 this._host = IDNAToASCII.call(this, buffer);
7893 buffer = '';
7894 state = 'port';
7895 if ('hostname' == stateOverride) {
7896 break loop;
7897 }
7898 } else if (EOF == c || '/' == c || '\\' == c || '?' == c || '#' == c) {
7899 this._host = IDNAToASCII.call(this, buffer);
7900 buffer = '';
7901 state = 'relative path start';
7902 if (stateOverride) {
7903 break loop;
7904 }
7905 continue;
7906 } else if ('\t' != c && '\n' != c && '\r' != c) {
7907 if ('[' == c) {
7908 seenBracket = true;
7909 } else if (']' == c) {
7910 seenBracket = false;
7911 }
7912 buffer += c;
7913 } else {
7914 err('Invalid code point in host/hostname: ' + c);
7915 }
7916 break;
7917
7918 case 'port':
7919 if (/[0-9]/.test(c)) {
7920 buffer += c;
7921 } else if (EOF == c || '/' == c || '\\' == c || '?' == c || '#' == c | | stateOverride) {
7922 if ('' != buffer) {
7923 var temp = parseInt(buffer, 10);
7924 if (temp != relative[this._scheme]) {
7925 this._port = temp + '';
7926 }
7927 buffer = '';
7928 }
7929 if (stateOverride) {
7930 break loop;
7931 }
7932 state = 'relative path start';
7933 continue;
7934 } else if ('\t' == c || '\n' == c || '\r' == c) {
7935 err('Invalid code point in port: ' + c);
7936 } else {
7937 invalid.call(this);
7938 }
7939 break;
7940
7941 case 'relative path start':
7942 if ('\\' == c)
7943 err("'\\' not allowed in path.");
7944 state = 'relative path';
7945 if ('/' != c && '\\' != c) {
7946 continue;
7947 }
7948 break;
7949
7950 case 'relative path':
7951 if (EOF == c || '/' == c || '\\' == c || (!stateOverride && ('?' == c || '#' == c))) {
7952 if ('\\' == c) {
7953 err('\\ not allowed in relative path.');
7954 }
7955 var tmp;
7956 if (tmp = relativePathDotMapping[buffer.toLowerCase()]) {
7957 buffer = tmp;
7958 }
7959 if ('..' == buffer) {
7960 this._path.pop();
7961 if ('/' != c && '\\' != c) {
7962 this._path.push('');
7963 }
7964 } else if ('.' == buffer && '/' != c && '\\' != c) {
7965 this._path.push('');
7966 } else if ('.' != buffer) {
7967 if ('file' == this._scheme && this._path.length == 0 && buffer.len gth == 2 && ALPHA.test(buffer[0]) && buffer[1] == '|') {
7968 buffer = buffer[0] + ':';
7969 }
7970 this._path.push(buffer);
7971 }
7972 buffer = '';
7973 if ('?' == c) {
7974 this._query = '?';
7975 state = 'query';
7976 } else if ('#' == c) {
7977 this._fragment = '#';
7978 state = 'fragment';
7979 }
7980 } else if ('\t' != c && '\n' != c && '\r' != c) {
7981 buffer += percentEscape(c);
7982 }
7983 break;
7984
7985 case 'query':
7986 if (!stateOverride && '#' == c) {
7987 this._fragment = '#';
7988 state = 'fragment';
7989 } else if (EOF != c && '\t' != c && '\n' != c && '\r' != c) {
7990 this._query += percentEscapeQuery(c);
7991 }
7992 break;
7993
7994 case 'fragment':
7995 if (EOF != c && '\t' != c && '\n' != c && '\r' != c) {
7996 this._fragment += c;
7997 }
7998 break;
7999 }
8000
8001 cursor++;
8002 }
8003 }
8004
8005 function clear() {
8006 this._scheme = '';
8007 this._schemeData = '';
8008 this._username = '';
8009 this._password = null;
8010 this._host = '';
8011 this._port = '';
8012 this._path = [];
8013 this._query = '';
8014 this._fragment = '';
8015 this._isInvalid = false;
8016 this._isRelative = false;
8017 }
8018
8019 // Does not process domain names or IP addresses.
8020 // Does not handle encoding for the query parameter.
8021 function jURL(url, base /* , encoding */) {
8022 if (base !== undefined && !(base instanceof jURL))
8023 base = new jURL(String(base));
8024
8025 this._url = url;
8026 clear.call(this);
8027
8028 var input = url.replace(/^[ \t\r\n\f]+|[ \t\r\n\f]+$/g, '');
8029 // encoding = encoding || 'utf-8'
8030
8031 parse.call(this, input, null, base);
8032 }
8033
8034 jURL.prototype = {
8035 get href() {
8036 if (this._isInvalid)
8037 return this._url;
8038
8039 var authority = '';
8040 if ('' != this._username || null != this._password) {
8041 authority = this._username +
8042 (null != this._password ? ':' + this._password : '') + '@';
8043 }
8044
8045 return this.protocol +
8046 (this._isRelative ? '//' + authority + this.host : '') +
8047 this.pathname + this._query + this._fragment;
8048 },
8049 set href(href) {
8050 clear.call(this);
8051 parse.call(this, href);
8052 },
8053
8054 get protocol() {
8055 return this._scheme + ':';
8056 },
8057 set protocol(protocol) {
8058 if (this._isInvalid)
8059 return;
8060 parse.call(this, protocol + ':', 'scheme start');
8061 },
8062
8063 get host() {
8064 return this._isInvalid ? '' : this._port ?
8065 this._host + ':' + this._port : this._host;
8066 },
8067 set host(host) {
8068 if (this._isInvalid || !this._isRelative)
8069 return;
8070 parse.call(this, host, 'host');
8071 },
8072
8073 get hostname() {
8074 return this._host;
8075 },
8076 set hostname(hostname) {
8077 if (this._isInvalid || !this._isRelative)
8078 return;
8079 parse.call(this, hostname, 'hostname');
8080 },
8081
8082 get port() {
8083 return this._port;
8084 },
8085 set port(port) {
8086 if (this._isInvalid || !this._isRelative)
8087 return;
8088 parse.call(this, port, 'port');
8089 },
8090
8091 get pathname() {
8092 return this._isInvalid ? '' : this._isRelative ?
8093 '/' + this._path.join('/') : this._schemeData;
8094 },
8095 set pathname(pathname) {
8096 if (this._isInvalid || !this._isRelative)
8097 return;
8098 this._path = [];
8099 parse.call(this, pathname, 'relative path start');
8100 },
8101
8102 get search() {
8103 return this._isInvalid || !this._query || '?' == this._query ?
8104 '' : this._query;
8105 },
8106 set search(search) {
8107 if (this._isInvalid || !this._isRelative)
8108 return;
8109 this._query = '?';
8110 if ('?' == search[0])
8111 search = search.slice(1);
8112 parse.call(this, search, 'query');
8113 },
8114
8115 get hash() {
8116 return this._isInvalid || !this._fragment || '#' == this._fragment ?
8117 '' : this._fragment;
8118 },
8119 set hash(hash) {
8120 if (this._isInvalid)
8121 return;
8122 this._fragment = '#';
8123 if ('#' == hash[0])
8124 hash = hash.slice(1);
8125 parse.call(this, hash, 'fragment');
8126 }
8127 };
8128
8129 scope.URL = jURL;
8130
8131 })(window);
8132
8133 /*
8134 * Copyright 2013 The Polymer Authors. All rights reserved.
8135 * Use of this source code is governed by a BSD-style
8136 * license that can be found in the LICENSE file.
8137 */
8138
8139 (function(scope) {
8140
8141 // Old versions of iOS do not have bind.
8142
8143 if (!Function.prototype.bind) {
8144 Function.prototype.bind = function(scope) {
8145 var self = this;
8146 var args = Array.prototype.slice.call(arguments, 1);
8147 return function() {
8148 var args2 = args.slice();
8149 args2.push.apply(args2, arguments);
8150 return self.apply(scope, args2);
8151 };
8152 };
8153 }
8154
8155 // mixin
8156
8157 // copy all properties from inProps (et al) to inObj
8158 function mixin(inObj/*, inProps, inMoreProps, ...*/) {
8159 var obj = inObj || {};
8160 for (var i = 1; i < arguments.length; i++) {
8161 var p = arguments[i];
8162 try {
8163 for (var n in p) {
8164 copyProperty(n, p, obj);
8165 }
8166 } catch(x) {
8167 }
8168 }
8169 return obj;
8170 }
8171
8172 // copy property inName from inSource object to inTarget object
8173 function copyProperty(inName, inSource, inTarget) {
8174 var pd = getPropertyDescriptor(inSource, inName);
8175 Object.defineProperty(inTarget, inName, pd);
8176 }
8177
8178 // get property descriptor for inName on inObject, even if
8179 // inName exists on some link in inObject's prototype chain
8180 function getPropertyDescriptor(inObject, inName) {
8181 if (inObject) {
8182 var pd = Object.getOwnPropertyDescriptor(inObject, inName);
8183 return pd || getPropertyDescriptor(Object.getPrototypeOf(inObject), inName);
8184 }
8185 }
8186
8187 // export
8188
8189 scope.mixin = mixin;
8190
8191 })(window.Platform);
8192 // Copyright 2011 Google Inc.
8193 //
8194 // Licensed under the Apache License, Version 2.0 (the "License");
8195 // you may not use this file except in compliance with the License.
8196 // You may obtain a copy of the License at
8197 //
8198 // http://www.apache.org/licenses/LICENSE-2.0
8199 //
8200 // Unless required by applicable law or agreed to in writing, software
8201 // distributed under the License is distributed on an "AS IS" BASIS,
8202 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
8203 // See the License for the specific language governing permissions and
8204 // limitations under the License.
8205
8206 (function(scope) {
8207
8208 'use strict';
8209
8210 // polyfill DOMTokenList
8211 // * add/remove: allow these methods to take multiple classNames
8212 // * toggle: add a 2nd argument which forces the given state rather
8213 // than toggling.
8214
8215 var add = DOMTokenList.prototype.add;
8216 var remove = DOMTokenList.prototype.remove;
8217 DOMTokenList.prototype.add = function() {
8218 for (var i = 0; i < arguments.length; i++) {
8219 add.call(this, arguments[i]);
8220 }
8221 };
8222 DOMTokenList.prototype.remove = function() {
8223 for (var i = 0; i < arguments.length; i++) {
8224 remove.call(this, arguments[i]);
8225 }
8226 };
8227 DOMTokenList.prototype.toggle = function(name, bool) {
8228 if (arguments.length == 1) {
8229 bool = !this.contains(name);
8230 }
8231 bool ? this.add(name) : this.remove(name);
8232 };
8233 DOMTokenList.prototype.switch = function(oldName, newName) {
8234 oldName && this.remove(oldName);
8235 newName && this.add(newName);
8236 };
8237
8238 // add array() to NodeList, NamedNodeMap, HTMLCollection
8239
8240 var ArraySlice = function() {
8241 return Array.prototype.slice.call(this);
8242 };
8243
8244 var namedNodeMap = (window.NamedNodeMap || window.MozNamedAttrMap || {});
8245
8246 NodeList.prototype.array = ArraySlice;
8247 namedNodeMap.prototype.array = ArraySlice;
8248 HTMLCollection.prototype.array = ArraySlice;
8249
8250 // polyfill performance.now
8251
8252 if (!window.performance) {
8253 var start = Date.now();
8254 // only at millisecond precision
8255 window.performance = {now: function(){ return Date.now() - start }};
8256 }
8257
8258 // polyfill for requestAnimationFrame
8259
8260 if (!window.requestAnimationFrame) {
8261 window.requestAnimationFrame = (function() {
8262 var nativeRaf = window.webkitRequestAnimationFrame ||
8263 window.mozRequestAnimationFrame;
8264
8265 return nativeRaf ?
8266 function(callback) {
8267 return nativeRaf(function() {
8268 callback(performance.now());
8269 });
8270 } :
8271 function( callback ){
8272 return window.setTimeout(callback, 1000 / 60);
8273 };
8274 })();
8275 }
8276
8277 if (!window.cancelAnimationFrame) {
8278 window.cancelAnimationFrame = (function() {
8279 return window.webkitCancelAnimationFrame ||
8280 window.mozCancelAnimationFrame ||
8281 function(id) {
8282 clearTimeout(id);
8283 };
8284 })();
8285 }
8286
8287 // TODO(sorvell): workaround for bug:
8288 // https://code.google.com/p/chromium/issues/detail?id=229142
8289 // remove when this bug is addressed
8290 // give main document templates a base that allows them to fetch eagerly
8291 // resolved paths relative to the main document
8292 var template = document.createElement('template');
8293 var base = document.createElement('base');
8294 base.href = document.baseURI;
8295 template.content.ownerDocument.appendChild(base);
8296
8297
8298 // utility
8299
8300 function createDOM(inTagOrNode, inHTML, inAttrs) {
8301 var dom = typeof inTagOrNode == 'string' ?
8302 document.createElement(inTagOrNode) : inTagOrNode.cloneNode(true);
8303 dom.innerHTML = inHTML;
8304 if (inAttrs) {
8305 for (var n in inAttrs) {
8306 dom.setAttribute(n, inAttrs[n]);
8307 }
8308 }
8309 return dom;
8310 }
8311 // Make a stub for Polymer() for polyfill purposes; under the HTMLImports
8312 // polyfill, scripts in the main document run before imports. That means
8313 // if (1) polymer is imported and (2) Polymer() is called in the main document
8314 // in a script after the import, 2 occurs before 1. We correct this here
8315 // by specfiically patching Polymer(); this is not necessary under native
8316 // HTMLImports.
8317 var elementDeclarations = [];
8318
8319 var polymerStub = function(name, dictionary) {
8320 elementDeclarations.push(arguments);
8321 }
8322 window.Polymer = polymerStub;
8323
8324 // deliver queued delcarations
8325 scope.deliverDeclarations = function() {
8326 scope.deliverDeclarations = null;
8327 return elementDeclarations;
8328 }
8329
8330 // Once DOMContent has loaded, any main document scripts that depend on
8331 // Polymer() should have run. Calling Polymer() now is an error until
8332 // polymer is imported.
8333 window.addEventListener('DOMContentLoaded', function() {
8334 if (window.Polymer === polymerStub) {
8335 window.Polymer = function() {
8336 console.error('You tried to use polymer without loading it first. To ' +
8337 'load polymer, <link rel="import" href="' +
8338 'components/polymer/polymer.html">');
8339 };
8340 }
8341 });
8342
8343 // exports
8344 scope.createDOM = createDOM;
8345
8346 })(window.Platform);
8347
8348 /*
8349 * Copyright 2013 The Polymer Authors. All rights reserved.
8350 * Use of this source code is governed by a BSD-style
8351 * license that can be found in the LICENSE file.
8352 */
8353
8354 // poor man's adapter for template.content on various platform scenarios
8355 window.templateContent = window.templateContent || function(inTemplate) {
8356 return inTemplate.content;
8357 };
8358 (function(scope) {
8359
8360 scope = scope || (window.Inspector = {});
8361
8362 var inspector;
8363
8364 window.sinspect = function(inNode, inProxy) {
8365 if (!inspector) {
8366 inspector = window.open('', 'ShadowDOM Inspector', null, true);
8367 inspector.document.write(inspectorHTML);
8368 //inspector.document.close();
8369 inspector.api = {
8370 shadowize: shadowize
8371 };
8372 }
8373 inspect(inNode || wrap(document.body), inProxy);
8374 };
8375
8376 var inspectorHTML = [
8377 '<!DOCTYPE html>',
8378 '<html>',
8379 ' <head>',
8380 ' <title>ShadowDOM Inspector</title>',
8381 ' <style>',
8382 ' body {',
8383 ' }',
8384 ' pre {',
8385 ' font: 9pt "Courier New", monospace;',
8386 ' line-height: 1.5em;',
8387 ' }',
8388 ' tag {',
8389 ' color: purple;',
8390 ' }',
8391 ' ul {',
8392 ' margin: 0;',
8393 ' padding: 0;',
8394 ' list-style: none;',
8395 ' }',
8396 ' li {',
8397 ' display: inline-block;',
8398 ' background-color: #f1f1f1;',
8399 ' padding: 4px 6px;',
8400 ' border-radius: 4px;',
8401 ' margin-right: 4px;',
8402 ' }',
8403 ' </style>',
8404 ' </head>',
8405 ' <body>',
8406 ' <ul id="crumbs">',
8407 ' </ul>',
8408 ' <div id="tree"></div>',
8409 ' </body>',
8410 '</html>'
8411 ].join('\n');
8412
8413 var crumbs = [];
8414
8415 var displayCrumbs = function() {
8416 // alias our document
8417 var d = inspector.document;
8418 // get crumbbar
8419 var cb = d.querySelector('#crumbs');
8420 // clear crumbs
8421 cb.textContent = '';
8422 // build new crumbs
8423 for (var i=0, c; c=crumbs[i]; i++) {
8424 var a = d.createElement('a');
8425 a.href = '#';
8426 a.textContent = c.localName;
8427 a.idx = i;
8428 a.onclick = function(event) {
8429 var c;
8430 while (crumbs.length > this.idx) {
8431 c = crumbs.pop();
8432 }
8433 inspect(c.shadow || c, c);
8434 event.preventDefault();
8435 };
8436 cb.appendChild(d.createElement('li')).appendChild(a);
8437 }
8438 };
8439
8440 var inspect = function(inNode, inProxy) {
8441 // alias our document
8442 var d = inspector.document;
8443 // reset list of drillable nodes
8444 drillable = [];
8445 // memoize our crumb proxy
8446 var proxy = inProxy || inNode;
8447 crumbs.push(proxy);
8448 // update crumbs
8449 displayCrumbs();
8450 // reflect local tree
8451 d.body.querySelector('#tree').innerHTML =
8452 '<pre>' + output(inNode, inNode.childNodes) + '</pre>';
8453 };
8454
8455 var forEach = Array.prototype.forEach.call.bind(Array.prototype.forEach);
8456
8457 var blacklisted = {STYLE:1, SCRIPT:1, "#comment": 1, TEMPLATE: 1};
8458 var blacklist = function(inNode) {
8459 return blacklisted[inNode.nodeName];
8460 };
8461
8462 var output = function(inNode, inChildNodes, inIndent) {
8463 if (blacklist(inNode)) {
8464 return '';
8465 }
8466 var indent = inIndent || '';
8467 if (inNode.localName || inNode.nodeType == 11) {
8468 var name = inNode.localName || 'shadow-root';
8469 //inChildNodes = ShadowDOM.localNodes(inNode);
8470 var info = indent + describe(inNode);
8471 // if only textNodes
8472 // TODO(sjmiles): make correct for ShadowDOM
8473 /*if (!inNode.children.length && inNode.localName !== 'content' && inNode. localName !== 'shadow') {
8474 info += catTextContent(inChildNodes);
8475 } else*/ {
8476 // TODO(sjmiles): native <shadow> has no reference to its projection
8477 if (name == 'content' /*|| name == 'shadow'*/) {
8478 inChildNodes = inNode.getDistributedNodes();
8479 }
8480 info += '<br/>';
8481 var ind = indent + '&nbsp;&nbsp;';
8482 forEach(inChildNodes, function(n) {
8483 info += output(n, n.childNodes, ind);
8484 });
8485 info += indent;
8486 }
8487 if (!({br:1}[name])) {
8488 info += '<tag>&lt;/' + name + '&gt;</tag>';
8489 info += '<br/>';
8490 }
8491 } else {
8492 var text = inNode.textContent.trim();
8493 info = text ? indent + '"' + text + '"' + '<br/>' : '';
8494 }
8495 return info;
8496 };
8497
8498 var catTextContent = function(inChildNodes) {
8499 var info = '';
8500 forEach(inChildNodes, function(n) {
8501 info += n.textContent.trim();
8502 });
8503 return info;
8504 };
8505
8506 var drillable = [];
8507
8508 var describe = function(inNode) {
8509 var tag = '<tag>' + '&lt;';
8510 var name = inNode.localName || 'shadow-root';
8511 if (inNode.webkitShadowRoot || inNode.shadowRoot) {
8512 tag += ' <button idx="' + drillable.length +
8513 '" onclick="api.shadowize.call(this)">' + name + '</button>';
8514 drillable.push(inNode);
8515 } else {
8516 tag += name || 'shadow-root';
8517 }
8518 if (inNode.attributes) {
8519 forEach(inNode.attributes, function(a) {
8520 tag += ' ' + a.name + (a.value ? '="' + a.value + '"' : '');
8521 });
8522 }
8523 tag += '&gt;'+ '</tag>';
8524 return tag;
8525 };
8526
8527 // remote api
8528
8529 shadowize = function() {
8530 var idx = Number(this.attributes.idx.value);
8531 //alert(idx);
8532 var node = drillable[idx];
8533 if (node) {
8534 inspect(node.webkitShadowRoot || node.shadowRoot, node)
8535 } else {
8536 console.log("bad shadowize node");
8537 console.dir(this);
8538 }
8539 };
8540
8541 // export
8542
8543 scope.output = output;
8544
8545 })(window.Inspector);
8546
8547
8548
8549 /*
8550 * Copyright 2013 The Polymer Authors. All rights reserved.
8551 * Use of this source code is governed by a BSD-style
8552 * license that can be found in the LICENSE file.
8553 */
8554 (function(scope) {
8555
8556 // TODO(sorvell): It's desireable to provide a default stylesheet
8557 // that's convenient for styling unresolved elements, but
8558 // it's cumbersome to have to include this manually in every page.
8559 // It would make sense to put inside some HTMLImport but
8560 // the HTMLImports polyfill does not allow loading of stylesheets
8561 // that block rendering. Therefore this injection is tolerated here.
8562
8563 var style = document.createElement('style');
8564 style.textContent = ''
8565 + 'body {'
8566 + 'transition: opacity ease-in 0.2s;'
8567 + ' } \n'
8568 + 'body[unresolved] {'
8569 + 'opacity: 0; display: block; overflow: hidden;'
8570 + ' } \n'
8571 ;
8572 var head = document.querySelector('head');
8573 head.insertBefore(style, head.firstChild);
8574
8575 })(Platform);
8576
8577 (function(scope) {
8578
8579 function withDependencies(task, depends) {
8580 depends = depends || [];
8581 if (!depends.map) {
8582 depends = [depends];
8583 }
8584 return task.apply(this, depends.map(marshal));
8585 }
8586
8587 function module(name, dependsOrFactory, moduleFactory) {
8588 var module;
8589 switch (arguments.length) {
8590 case 0:
8591 return;
8592 case 1:
8593 module = null;
8594 break;
8595 case 2:
8596 module = dependsOrFactory.apply(this);
8597 break;
8598 default:
8599 module = withDependencies(moduleFactory, dependsOrFactory);
8600 break;
8601 }
8602 modules[name] = module;
8603 };
8604
8605 function marshal(name) {
8606 return modules[name];
8607 }
8608
8609 var modules = {};
8610
8611 function using(depends, task) {
8612 HTMLImports.whenImportsReady(function() {
8613 withDependencies(task, depends);
8614 });
8615 };
8616
8617 // exports
8618
8619 scope.marshal = marshal;
8620 scope.module = module;
8621 scope.using = using;
8622
8623 })(window);
8624 /*
8625 * Copyright 2013 The Polymer Authors. All rights reserved.
8626 * Use of this source code is governed by a BSD-style
8627 * license that can be found in the LICENSE file.
8628 */
8629 (function(scope) {
8630
8631 var iterations = 0;
8632 var callbacks = [];
8633 var twiddle = document.createTextNode('');
8634
8635 function endOfMicrotask(callback) {
8636 twiddle.textContent = iterations++;
8637 callbacks.push(callback);
8638 }
8639
8640 function atEndOfMicrotask() {
8641 while (callbacks.length) {
8642 callbacks.shift()();
8643 }
8644 }
8645
8646 new (window.MutationObserver || JsMutationObserver)(atEndOfMicrotask)
8647 .observe(twiddle, {characterData: true})
8648 ;
8649
8650 // exports
8651
8652 scope.endOfMicrotask = endOfMicrotask;
8653
8654 })(Platform);
8655
8656
8657 /*
8658 * Copyright 2013 The Polymer Authors. All rights reserved.
8659 * Use of this source code is governed by a BSD-style
8660 * license that can be found in the LICENSE file.
8661 */
8662
8663 (function(scope) {
8664
8665 var urlResolver = {
8666 resolveDom: function(root, url) {
8667 url = url || root.ownerDocument.baseURI;
8668 this.resolveAttributes(root, url);
8669 this.resolveStyles(root, url);
8670 // handle template.content
8671 var templates = root.querySelectorAll('template');
8672 if (templates) {
8673 for (var i = 0, l = templates.length, t; (i < l) && (t = templates[i]); i+ +) {
8674 if (t.content) {
8675 this.resolveDom(t.content, url);
8676 }
8677 }
8678 }
8679 },
8680 resolveStyles: function(root, url) {
8681 var styles = root.querySelectorAll('style');
8682 if (styles) {
8683 for (var i = 0, l = styles.length, s; (i < l) && (s = styles[i]); i++) {
8684 this.resolveStyle(s, url);
8685 }
8686 }
8687 },
8688 resolveStyle: function(style, url) {
8689 url = url || style.ownerDocument.baseURI;
8690 style.textContent = this.resolveCssText(style.textContent, url);
8691 },
8692 resolveCssText: function(cssText, baseUrl) {
8693 cssText = replaceUrlsInCssText(cssText, baseUrl, CSS_URL_REGEXP);
8694 return replaceUrlsInCssText(cssText, baseUrl, CSS_IMPORT_REGEXP);
8695 },
8696 resolveAttributes: function(root, url) {
8697 if (root.hasAttributes && root.hasAttributes()) {
8698 this.resolveElementAttributes(root, url);
8699 }
8700 // search for attributes that host urls
8701 var nodes = root && root.querySelectorAll(URL_ATTRS_SELECTOR);
8702 if (nodes) {
8703 for (var i = 0, l = nodes.length, n; (i < l) && (n = nodes[i]); i++) {
8704 this.resolveElementAttributes(n, url);
8705 }
8706 }
8707 },
8708 resolveElementAttributes: function(node, url) {
8709 url = url || node.ownerDocument.baseURI;
8710 URL_ATTRS.forEach(function(v) {
8711 var attr = node.attributes[v];
8712 if (attr && attr.value &&
8713 (attr.value.search(URL_TEMPLATE_SEARCH) < 0)) {
8714 var urlPath = resolveRelativeUrl(url, attr.value);
8715 attr.value = urlPath;
8716 }
8717 });
8718 }
8719 };
8720
8721 var CSS_URL_REGEXP = /(url\()([^)]*)(\))/g;
8722 var CSS_IMPORT_REGEXP = /(@import[\s]+(?!url\())([^;]*)(;)/g;
8723 var URL_ATTRS = ['href', 'src', 'action'];
8724 var URL_ATTRS_SELECTOR = '[' + URL_ATTRS.join('],[') + ']';
8725 var URL_TEMPLATE_SEARCH = '{{.*}}';
8726
8727 function replaceUrlsInCssText(cssText, baseUrl, regexp) {
8728 return cssText.replace(regexp, function(m, pre, url, post) {
8729 var urlPath = url.replace(/["']/g, '');
8730 urlPath = resolveRelativeUrl(baseUrl, urlPath);
8731 return pre + '\'' + urlPath + '\'' + post;
8732 });
8733 }
8734
8735 function resolveRelativeUrl(baseUrl, url) {
8736 var u = new URL(url, baseUrl);
8737 return makeDocumentRelPath(u.href);
8738 }
8739
8740 function makeDocumentRelPath(url) {
8741 var root = document.baseURI;
8742 var u = new URL(url, root);
8743 if (u.host === root.host && u.port === root.port &&
8744 u.protocol === root.protocol) {
8745 return makeRelPath(root.pathname, u.pathname);
8746 } else {
8747 return url;
8748 }
8749 }
8750
8751 // make a relative path from source to target
8752 function makeRelPath(source, target) {
8753 var s = source.split('/');
8754 var t = target.split('/');
8755 while (s.length && s[0] === t[0]){
8756 s.shift();
8757 t.shift();
8758 }
8759 for (var i = 0, l = s.length - 1; i < l; i++) {
8760 t.unshift('..');
8761 }
8762 return t.join('/');
8763 }
8764
8765 // exports
8766 scope.urlResolver = urlResolver;
8767
8768 })(Platform);
8769
8770 /*
8771 * Copyright 2012 The Polymer Authors. All rights reserved.
8772 * Use of this source code is goverened by a BSD-style
8773 * license that can be found in the LICENSE file.
8774 */
8775
8776 (function(global) {
8777
8778 var registrationsTable = new WeakMap();
8779
8780 // We use setImmediate or postMessage for our future callback.
8781 var setImmediate = window.msSetImmediate;
8782
8783 // Use post message to emulate setImmediate.
8784 if (!setImmediate) {
8785 var setImmediateQueue = [];
8786 var sentinel = String(Math.random());
8787 window.addEventListener('message', function(e) {
8788 if (e.data === sentinel) {
8789 var queue = setImmediateQueue;
8790 setImmediateQueue = [];
8791 queue.forEach(function(func) {
8792 func();
8793 });
8794 }
8795 });
8796 setImmediate = function(func) {
8797 setImmediateQueue.push(func);
8798 window.postMessage(sentinel, '*');
8799 };
8800 }
8801
8802 // This is used to ensure that we never schedule 2 callas to setImmediate
8803 var isScheduled = false;
8804
8805 // Keep track of observers that needs to be notified next time.
8806 var scheduledObservers = [];
8807
8808 /**
8809 * Schedules |dispatchCallback| to be called in the future.
8810 * @param {MutationObserver} observer
8811 */
8812 function scheduleCallback(observer) {
8813 scheduledObservers.push(observer);
8814 if (!isScheduled) {
8815 isScheduled = true;
8816 setImmediate(dispatchCallbacks);
8817 }
8818 }
8819
8820 function wrapIfNeeded(node) {
8821 return window.ShadowDOMPolyfill &&
8822 window.ShadowDOMPolyfill.wrapIfNeeded(node) ||
8823 node;
8824 }
8825
8826 function dispatchCallbacks() {
8827 // http://dom.spec.whatwg.org/#mutation-observers
8828
8829 isScheduled = false; // Used to allow a new setImmediate call above.
8830
8831 var observers = scheduledObservers;
8832 scheduledObservers = [];
8833 // Sort observers based on their creation UID (incremental).
8834 observers.sort(function(o1, o2) {
8835 return o1.uid_ - o2.uid_;
8836 });
8837
8838 var anyNonEmpty = false;
8839 observers.forEach(function(observer) {
8840
8841 // 2.1, 2.2
8842 var queue = observer.takeRecords();
8843 // 2.3. Remove all transient registered observers whose observer is mo.
8844 removeTransientObserversFor(observer);
8845
8846 // 2.4
8847 if (queue.length) {
8848 observer.callback_(queue, observer);
8849 anyNonEmpty = true;
8850 }
8851 });
8852
8853 // 3.
8854 if (anyNonEmpty)
8855 dispatchCallbacks();
8856 }
8857
8858 function removeTransientObserversFor(observer) {
8859 observer.nodes_.forEach(function(node) {
8860 var registrations = registrationsTable.get(node);
8861 if (!registrations)
8862 return;
8863 registrations.forEach(function(registration) {
8864 if (registration.observer === observer)
8865 registration.removeTransientObservers();
8866 });
8867 });
8868 }
8869
8870 /**
8871 * This function is used for the "For each registered observer observer (with
8872 * observer's options as options) in target's list of registered observers,
8873 * run these substeps:" and the "For each ancestor ancestor of target, and for
8874 * each registered observer observer (with options options) in ancestor's list
8875 * of registered observers, run these substeps:" part of the algorithms. The
8876 * |options.subtree| is checked to ensure that the callback is called
8877 * correctly.
8878 *
8879 * @param {Node} target
8880 * @param {function(MutationObserverInit):MutationRecord} callback
8881 */
8882 function forEachAncestorAndObserverEnqueueRecord(target, callback) {
8883 for (var node = target; node; node = node.parentNode) {
8884 var registrations = registrationsTable.get(node);
8885
8886 if (registrations) {
8887 for (var j = 0; j < registrations.length; j++) {
8888 var registration = registrations[j];
8889 var options = registration.options;
8890
8891 // Only target ignores subtree.
8892 if (node !== target && !options.subtree)
8893 continue;
8894
8895 var record = callback(options);
8896 if (record)
8897 registration.enqueue(record);
8898 }
8899 }
8900 }
8901 }
8902
8903 var uidCounter = 0;
8904
8905 /**
8906 * The class that maps to the DOM MutationObserver interface.
8907 * @param {Function} callback.
8908 * @constructor
8909 */
8910 function JsMutationObserver(callback) {
8911 this.callback_ = callback;
8912 this.nodes_ = [];
8913 this.records_ = [];
8914 this.uid_ = ++uidCounter;
8915 }
8916
8917 JsMutationObserver.prototype = {
8918 observe: function(target, options) {
8919 target = wrapIfNeeded(target);
8920
8921 // 1.1
8922 if (!options.childList && !options.attributes && !options.characterData ||
8923
8924 // 1.2
8925 options.attributeOldValue && !options.attributes ||
8926
8927 // 1.3
8928 options.attributeFilter && options.attributeFilter.length &&
8929 !options.attributes ||
8930
8931 // 1.4
8932 options.characterDataOldValue && !options.characterData) {
8933
8934 throw new SyntaxError();
8935 }
8936
8937 var registrations = registrationsTable.get(target);
8938 if (!registrations)
8939 registrationsTable.set(target, registrations = []);
8940
8941 // 2
8942 // If target's list of registered observers already includes a registered
8943 // observer associated with the context object, replace that registered
8944 // observer's options with options.
8945 var registration;
8946 for (var i = 0; i < registrations.length; i++) {
8947 if (registrations[i].observer === this) {
8948 registration = registrations[i];
8949 registration.removeListeners();
8950 registration.options = options;
8951 break;
8952 }
8953 }
8954
8955 // 3.
8956 // Otherwise, add a new registered observer to target's list of registered
8957 // observers with the context object as the observer and options as the
8958 // options, and add target to context object's list of nodes on which it
8959 // is registered.
8960 if (!registration) {
8961 registration = new Registration(this, target, options);
8962 registrations.push(registration);
8963 this.nodes_.push(target);
8964 }
8965
8966 registration.addListeners();
8967 },
8968
8969 disconnect: function() {
8970 this.nodes_.forEach(function(node) {
8971 var registrations = registrationsTable.get(node);
8972 for (var i = 0; i < registrations.length; i++) {
8973 var registration = registrations[i];
8974 if (registration.observer === this) {
8975 registration.removeListeners();
8976 registrations.splice(i, 1);
8977 // Each node can only have one registered observer associated with
8978 // this observer.
8979 break;
8980 }
8981 }
8982 }, this);
8983 this.records_ = [];
8984 },
8985
8986 takeRecords: function() {
8987 var copyOfRecords = this.records_;
8988 this.records_ = [];
8989 return copyOfRecords;
8990 }
8991 };
8992
8993 /**
8994 * @param {string} type
8995 * @param {Node} target
8996 * @constructor
8997 */
8998 function MutationRecord(type, target) {
8999 this.type = type;
9000 this.target = target;
9001 this.addedNodes = [];
9002 this.removedNodes = [];
9003 this.previousSibling = null;
9004 this.nextSibling = null;
9005 this.attributeName = null;
9006 this.attributeNamespace = null;
9007 this.oldValue = null;
9008 }
9009
9010 function copyMutationRecord(original) {
9011 var record = new MutationRecord(original.type, original.target);
9012 record.addedNodes = original.addedNodes.slice();
9013 record.removedNodes = original.removedNodes.slice();
9014 record.previousSibling = original.previousSibling;
9015 record.nextSibling = original.nextSibling;
9016 record.attributeName = original.attributeName;
9017 record.attributeNamespace = original.attributeNamespace;
9018 record.oldValue = original.oldValue;
9019 return record;
9020 };
9021
9022 // We keep track of the two (possibly one) records used in a single mutation.
9023 var currentRecord, recordWithOldValue;
9024
9025 /**
9026 * Creates a record without |oldValue| and caches it as |currentRecord| for
9027 * later use.
9028 * @param {string} oldValue
9029 * @return {MutationRecord}
9030 */
9031 function getRecord(type, target) {
9032 return currentRecord = new MutationRecord(type, target);
9033 }
9034
9035 /**
9036 * Gets or creates a record with |oldValue| based in the |currentRecord|
9037 * @param {string} oldValue
9038 * @return {MutationRecord}
9039 */
9040 function getRecordWithOldValue(oldValue) {
9041 if (recordWithOldValue)
9042 return recordWithOldValue;
9043 recordWithOldValue = copyMutationRecord(currentRecord);
9044 recordWithOldValue.oldValue = oldValue;
9045 return recordWithOldValue;
9046 }
9047
9048 function clearRecords() {
9049 currentRecord = recordWithOldValue = undefined;
9050 }
9051
9052 /**
9053 * @param {MutationRecord} record
9054 * @return {boolean} Whether the record represents a record from the current
9055 * mutation event.
9056 */
9057 function recordRepresentsCurrentMutation(record) {
9058 return record === recordWithOldValue || record === currentRecord;
9059 }
9060
9061 /**
9062 * Selects which record, if any, to replace the last record in the queue.
9063 * This returns |null| if no record should be replaced.
9064 *
9065 * @param {MutationRecord} lastRecord
9066 * @param {MutationRecord} newRecord
9067 * @param {MutationRecord}
9068 */
9069 function selectRecord(lastRecord, newRecord) {
9070 if (lastRecord === newRecord)
9071 return lastRecord;
9072
9073 // Check if the the record we are adding represents the same record. If
9074 // so, we keep the one with the oldValue in it.
9075 if (recordWithOldValue && recordRepresentsCurrentMutation(lastRecord))
9076 return recordWithOldValue;
9077
9078 return null;
9079 }
9080
9081 /**
9082 * Class used to represent a registered observer.
9083 * @param {MutationObserver} observer
9084 * @param {Node} target
9085 * @param {MutationObserverInit} options
9086 * @constructor
9087 */
9088 function Registration(observer, target, options) {
9089 this.observer = observer;
9090 this.target = target;
9091 this.options = options;
9092 this.transientObservedNodes = [];
9093 }
9094
9095 Registration.prototype = {
9096 enqueue: function(record) {
9097 var records = this.observer.records_;
9098 var length = records.length;
9099
9100 // There are cases where we replace the last record with the new record.
9101 // For example if the record represents the same mutation we need to use
9102 // the one with the oldValue. If we get same record (this can happen as we
9103 // walk up the tree) we ignore the new record.
9104 if (records.length > 0) {
9105 var lastRecord = records[length - 1];
9106 var recordToReplaceLast = selectRecord(lastRecord, record);
9107 if (recordToReplaceLast) {
9108 records[length - 1] = recordToReplaceLast;
9109 return;
9110 }
9111 } else {
9112 scheduleCallback(this.observer);
9113 }
9114
9115 records[length] = record;
9116 },
9117
9118 addListeners: function() {
9119 this.addListeners_(this.target);
9120 },
9121
9122 addListeners_: function(node) {
9123 var options = this.options;
9124 if (options.attributes)
9125 node.addEventListener('DOMAttrModified', this, true);
9126
9127 if (options.characterData)
9128 node.addEventListener('DOMCharacterDataModified', this, true);
9129
9130 if (options.childList)
9131 node.addEventListener('DOMNodeInserted', this, true);
9132
9133 if (options.childList || options.subtree)
9134 node.addEventListener('DOMNodeRemoved', this, true);
9135 },
9136
9137 removeListeners: function() {
9138 this.removeListeners_(this.target);
9139 },
9140
9141 removeListeners_: function(node) {
9142 var options = this.options;
9143 if (options.attributes)
9144 node.removeEventListener('DOMAttrModified', this, true);
9145
9146 if (options.characterData)
9147 node.removeEventListener('DOMCharacterDataModified', this, true);
9148
9149 if (options.childList)
9150 node.removeEventListener('DOMNodeInserted', this, true);
9151
9152 if (options.childList || options.subtree)
9153 node.removeEventListener('DOMNodeRemoved', this, true);
9154 },
9155
9156 /**
9157 * Adds a transient observer on node. The transient observer gets removed
9158 * next time we deliver the change records.
9159 * @param {Node} node
9160 */
9161 addTransientObserver: function(node) {
9162 // Don't add transient observers on the target itself. We already have all
9163 // the required listeners set up on the target.
9164 if (node === this.target)
9165 return;
9166
9167 this.addListeners_(node);
9168 this.transientObservedNodes.push(node);
9169 var registrations = registrationsTable.get(node);
9170 if (!registrations)
9171 registrationsTable.set(node, registrations = []);
9172
9173 // We know that registrations does not contain this because we already
9174 // checked if node === this.target.
9175 registrations.push(this);
9176 },
9177
9178 removeTransientObservers: function() {
9179 var transientObservedNodes = this.transientObservedNodes;
9180 this.transientObservedNodes = [];
9181
9182 transientObservedNodes.forEach(function(node) {
9183 // Transient observers are never added to the target.
9184 this.removeListeners_(node);
9185
9186 var registrations = registrationsTable.get(node);
9187 for (var i = 0; i < registrations.length; i++) {
9188 if (registrations[i] === this) {
9189 registrations.splice(i, 1);
9190 // Each node can only have one registered observer associated with
9191 // this observer.
9192 break;
9193 }
9194 }
9195 }, this);
9196 },
9197
9198 handleEvent: function(e) {
9199 // Stop propagation since we are managing the propagation manually.
9200 // This means that other mutation events on the page will not work
9201 // correctly but that is by design.
9202 e.stopImmediatePropagation();
9203
9204 switch (e.type) {
9205 case 'DOMAttrModified':
9206 // http://dom.spec.whatwg.org/#concept-mo-queue-attributes
9207
9208 var name = e.attrName;
9209 var namespace = e.relatedNode.namespaceURI;
9210 var target = e.target;
9211
9212 // 1.
9213 var record = new getRecord('attributes', target);
9214 record.attributeName = name;
9215 record.attributeNamespace = namespace;
9216
9217 // 2.
9218 var oldValue =
9219 e.attrChange === MutationEvent.ADDITION ? null : e.prevValue;
9220
9221 forEachAncestorAndObserverEnqueueRecord(target, function(options) {
9222 // 3.1, 4.2
9223 if (!options.attributes)
9224 return;
9225
9226 // 3.2, 4.3
9227 if (options.attributeFilter && options.attributeFilter.length &&
9228 options.attributeFilter.indexOf(name) === -1 &&
9229 options.attributeFilter.indexOf(namespace) === -1) {
9230 return;
9231 }
9232 // 3.3, 4.4
9233 if (options.attributeOldValue)
9234 return getRecordWithOldValue(oldValue);
9235
9236 // 3.4, 4.5
9237 return record;
9238 });
9239
9240 break;
9241
9242 case 'DOMCharacterDataModified':
9243 // http://dom.spec.whatwg.org/#concept-mo-queue-characterdata
9244 var target = e.target;
9245
9246 // 1.
9247 var record = getRecord('characterData', target);
9248
9249 // 2.
9250 var oldValue = e.prevValue;
9251
9252
9253 forEachAncestorAndObserverEnqueueRecord(target, function(options) {
9254 // 3.1, 4.2
9255 if (!options.characterData)
9256 return;
9257
9258 // 3.2, 4.3
9259 if (options.characterDataOldValue)
9260 return getRecordWithOldValue(oldValue);
9261
9262 // 3.3, 4.4
9263 return record;
9264 });
9265
9266 break;
9267
9268 case 'DOMNodeRemoved':
9269 this.addTransientObserver(e.target);
9270 // Fall through.
9271 case 'DOMNodeInserted':
9272 // http://dom.spec.whatwg.org/#concept-mo-queue-childlist
9273 var target = e.relatedNode;
9274 var changedNode = e.target;
9275 var addedNodes, removedNodes;
9276 if (e.type === 'DOMNodeInserted') {
9277 addedNodes = [changedNode];
9278 removedNodes = [];
9279 } else {
9280
9281 addedNodes = [];
9282 removedNodes = [changedNode];
9283 }
9284 var previousSibling = changedNode.previousSibling;
9285 var nextSibling = changedNode.nextSibling;
9286
9287 // 1.
9288 var record = getRecord('childList', target);
9289 record.addedNodes = addedNodes;
9290 record.removedNodes = removedNodes;
9291 record.previousSibling = previousSibling;
9292 record.nextSibling = nextSibling;
9293
9294 forEachAncestorAndObserverEnqueueRecord(target, function(options) {
9295 // 2.1, 3.2
9296 if (!options.childList)
9297 return;
9298
9299 // 2.2, 3.3
9300 return record;
9301 });
9302
9303 }
9304
9305 clearRecords();
9306 }
9307 };
9308
9309 global.JsMutationObserver = JsMutationObserver;
9310
9311 if (!global.MutationObserver)
9312 global.MutationObserver = JsMutationObserver;
9313
9314
9315 })(this);
9316
9317 /*
9318 * Copyright 2013 The Polymer Authors. All rights reserved.
9319 * Use of this source code is governed by a BSD-style
9320 * license that can be found in the LICENSE file.
9321 */
9322 window.HTMLImports = window.HTMLImports || {flags:{}};
9323 /*
9324 * Copyright 2013 The Polymer Authors. All rights reserved.
9325 * Use of this source code is governed by a BSD-style
9326 * license that can be found in the LICENSE file.
9327 */
9328
9329 (function(scope) {
9330
9331 // imports
9332 var path = scope.path;
9333 var xhr = scope.xhr;
9334 var flags = scope.flags;
9335
9336 // TODO(sorvell): this loader supports a dynamic list of urls
9337 // and an oncomplete callback that is called when the loader is done.
9338 // The polyfill currently does *not* need this dynamism or the onComplete
9339 // concept. Because of this, the loader could be simplified quite a bit.
9340 var Loader = function(onLoad, onComplete) {
9341 this.cache = {};
9342 this.onload = onLoad;
9343 this.oncomplete = onComplete;
9344 this.inflight = 0;
9345 this.pending = {};
9346 };
9347
9348 Loader.prototype = {
9349 addNodes: function(nodes) {
9350 // number of transactions to complete
9351 this.inflight += nodes.length;
9352 // commence transactions
9353 for (var i=0, l=nodes.length, n; (i<l) && (n=nodes[i]); i++) {
9354 this.require(n);
9355 }
9356 // anything to do?
9357 this.checkDone();
9358 },
9359 addNode: function(node) {
9360 // number of transactions to complete
9361 this.inflight++;
9362 // commence transactions
9363 this.require(node);
9364 // anything to do?
9365 this.checkDone();
9366 },
9367 require: function(elt) {
9368 var url = elt.src || elt.href;
9369 // ensure we have a standard url that can be used
9370 // reliably for deduping.
9371 // TODO(sjmiles): ad-hoc
9372 elt.__nodeUrl = url;
9373 // deduplication
9374 if (!this.dedupe(url, elt)) {
9375 // fetch this resource
9376 this.fetch(url, elt);
9377 }
9378 },
9379 dedupe: function(url, elt) {
9380 if (this.pending[url]) {
9381 // add to list of nodes waiting for inUrl
9382 this.pending[url].push(elt);
9383 // don't need fetch
9384 return true;
9385 }
9386 var resource;
9387 if (this.cache[url]) {
9388 this.onload(url, elt, this.cache[url]);
9389 // finished this transaction
9390 this.tail();
9391 // don't need fetch
9392 return true;
9393 }
9394 // first node waiting for inUrl
9395 this.pending[url] = [elt];
9396 // need fetch (not a dupe)
9397 return false;
9398 },
9399 fetch: function(url, elt) {
9400 flags.load && console.log('fetch', url, elt);
9401 var receiveXhr = function(err, resource) {
9402 this.receive(url, elt, err, resource);
9403 }.bind(this);
9404 xhr.load(url, receiveXhr);
9405 // TODO(sorvell): blocked on
9406 // https://code.google.com/p/chromium/issues/detail?id=257221
9407 // xhr'ing for a document makes scripts in imports runnable; otherwise
9408 // they are not; however, it requires that we have doctype=html in
9409 // the import which is unacceptable. This is only needed on Chrome
9410 // to avoid the bug above.
9411 /*
9412 if (isDocumentLink(elt)) {
9413 xhr.loadDocument(url, receiveXhr);
9414 } else {
9415 xhr.load(url, receiveXhr);
9416 }
9417 */
9418 },
9419 receive: function(url, elt, err, resource) {
9420 this.cache[url] = resource;
9421 var $p = this.pending[url];
9422 for (var i=0, l=$p.length, p; (i<l) && (p=$p[i]); i++) {
9423 //if (!err) {
9424 this.onload(url, p, resource);
9425 //}
9426 this.tail();
9427 }
9428 this.pending[url] = null;
9429 },
9430 tail: function() {
9431 --this.inflight;
9432 this.checkDone();
9433 },
9434 checkDone: function() {
9435 if (!this.inflight) {
9436 this.oncomplete();
9437 }
9438 }
9439 };
9440
9441 xhr = xhr || {
9442 async: true,
9443 ok: function(request) {
9444 return (request.status >= 200 && request.status < 300)
9445 || (request.status === 304)
9446 || (request.status === 0);
9447 },
9448 load: function(url, next, nextContext) {
9449 var request = new XMLHttpRequest();
9450 if (scope.flags.debug || scope.flags.bust) {
9451 url += '?' + Math.random();
9452 }
9453 request.open('GET', url, xhr.async);
9454 request.addEventListener('readystatechange', function(e) {
9455 if (request.readyState === 4) {
9456 next.call(nextContext, !xhr.ok(request) && request,
9457 request.response || request.responseText, url);
9458 }
9459 });
9460 request.send();
9461 return request;
9462 },
9463 loadDocument: function(url, next, nextContext) {
9464 this.load(url, next, nextContext).responseType = 'document';
9465 }
9466 };
9467
9468 // exports
9469 scope.xhr = xhr;
9470 scope.Loader = Loader;
9471
9472 })(window.HTMLImports);
9473
9474 /*
9475 * Copyright 2013 The Polymer Authors. All rights reserved.
9476 * Use of this source code is governed by a BSD-style
9477 * license that can be found in the LICENSE file.
9478 */
9479
9480 (function(scope) {
9481
9482 var IMPORT_LINK_TYPE = 'import';
9483 var flags = scope.flags;
9484 var isIe = /Trident/.test(navigator.userAgent);
9485 // TODO(sorvell): SD polyfill intrusion
9486 var mainDoc = window.ShadowDOMPolyfill ?
9487 window.ShadowDOMPolyfill.wrapIfNeeded(document) : document;
9488
9489 // importParser
9490 // highlander object to manage parsing of imports
9491 // parses import related elements
9492 // and ensures proper parse order
9493 // parse order is enforced by crawling the tree and monitoring which elements
9494 // have been parsed; async parsing is also supported.
9495
9496 // highlander object for parsing a document tree
9497 var importParser = {
9498 // parse selectors for main document elements
9499 documentSelectors: 'link[rel=' + IMPORT_LINK_TYPE + ']',
9500 // parse selectors for import document elements
9501 importsSelectors: [
9502 'link[rel=' + IMPORT_LINK_TYPE + ']',
9503 'link[rel=stylesheet]',
9504 'style',
9505 'script:not([type])',
9506 'script[type="text/javascript"]'
9507 ].join(','),
9508 map: {
9509 link: 'parseLink',
9510 script: 'parseScript',
9511 style: 'parseStyle'
9512 },
9513 // try to parse the next import in the tree
9514 parseNext: function() {
9515 var next = this.nextToParse();
9516 if (next) {
9517 this.parse(next);
9518 }
9519 },
9520 parse: function(elt) {
9521 if (this.isParsed(elt)) {
9522 flags.parse && console.log('[%s] is already parsed', elt.localName);
9523 return;
9524 }
9525 var fn = this[this.map[elt.localName]];
9526 if (fn) {
9527 this.markParsing(elt);
9528 fn.call(this, elt);
9529 }
9530 },
9531 // only 1 element may be parsed at a time; parsing is async so, each
9532 // parsing implementation must inform the system that parsing is complete
9533 // via markParsingComplete.
9534 markParsing: function(elt) {
9535 flags.parse && console.log('parsing', elt);
9536 this.parsingElement = elt;
9537 },
9538 markParsingComplete: function(elt) {
9539 elt.__importParsed = true;
9540 if (elt.__importElement) {
9541 elt.__importElement.__importParsed = true;
9542 }
9543 this.parsingElement = null;
9544 flags.parse && console.log('completed', elt);
9545 this.parseNext();
9546 },
9547 parseImport: function(elt) {
9548 elt.import.__importParsed = true;
9549 // TODO(sorvell): consider if there's a better way to do this;
9550 // expose an imports parsing hook; this is needed, for example, by the
9551 // CustomElements polyfill.
9552 if (HTMLImports.__importsParsingHook) {
9553 HTMLImports.__importsParsingHook(elt);
9554 }
9555 // fire load event
9556 if (elt.__resource) {
9557 elt.dispatchEvent(new CustomEvent('load', {bubbles: false}));
9558 } else {
9559 elt.dispatchEvent(new CustomEvent('error', {bubbles: false}));
9560 }
9561 // TODO(sorvell): workaround for Safari addEventListener not working
9562 // for elements not in the main document.
9563 if (elt.__pending) {
9564 var fn;
9565 while (elt.__pending.length) {
9566 fn = elt.__pending.shift();
9567 if (fn) {
9568 fn({target: elt});
9569 }
9570 }
9571 }
9572 this.markParsingComplete(elt);
9573 },
9574 parseLink: function(linkElt) {
9575 if (nodeIsImport(linkElt)) {
9576 this.parseImport(linkElt);
9577 } else {
9578 // make href absolute
9579 linkElt.href = linkElt.href;
9580 this.parseGeneric(linkElt);
9581 }
9582 },
9583 parseStyle: function(elt) {
9584 // TODO(sorvell): style element load event can just not fire so clone styles
9585 var src = elt;
9586 elt = cloneStyle(elt);
9587 elt.__importElement = src;
9588 this.parseGeneric(elt);
9589 },
9590 parseGeneric: function(elt) {
9591 this.trackElement(elt);
9592 document.head.appendChild(elt);
9593 },
9594 // tracks when a loadable element has loaded
9595 trackElement: function(elt) {
9596 var self = this;
9597 var done = function() {
9598 self.markParsingComplete(elt);
9599 };
9600 elt.addEventListener('load', done);
9601 elt.addEventListener('error', done);
9602
9603 // NOTE: IE does not fire "load" event for styles that have already loaded
9604 // This is in violation of the spec, so we try our hardest to work around it
9605 if (isIe && elt.localName === 'style') {
9606 var fakeLoad = false;
9607 // If there's not @import in the textContent, assume it has loaded
9608 if (elt.textContent.indexOf('@import') == -1) {
9609 fakeLoad = true;
9610 // if we have a sheet, we have been parsed
9611 } else if (elt.sheet) {
9612 fakeLoad = true;
9613 var csr = elt.sheet.cssRules;
9614 var len = csr ? csr.length : 0;
9615 // search the rules for @import's
9616 for (var i = 0, r; (i < len) && (r = csr[i]); i++) {
9617 if (r.type === CSSRule.IMPORT_RULE) {
9618 // if every @import has resolved, fake the load
9619 fakeLoad = fakeLoad && Boolean(r.styleSheet);
9620 }
9621 }
9622 }
9623 // dispatch a fake load event and continue parsing
9624 if (fakeLoad) {
9625 elt.dispatchEvent(new CustomEvent('load', {bubbles: false}));
9626 }
9627 }
9628 },
9629 parseScript: function(scriptElt) {
9630 // acquire code to execute
9631 var code = (scriptElt.__resource || scriptElt.textContent).trim();
9632 if (code) {
9633 // calculate source map hint
9634 var moniker = scriptElt.__nodeUrl;
9635 if (!moniker) {
9636 moniker = scriptElt.ownerDocument.baseURI;
9637 // there could be more than one script this url
9638 var tag = '[' + Math.floor((Math.random()+1)*1000) + ']';
9639 // TODO(sjmiles): Polymer hack, should be pluggable if we need to allow
9640 // this sort of thing
9641 var matches = code.match(/Polymer\(['"]([^'"]*)/);
9642 tag = matches && matches[1] || tag;
9643 // tag the moniker
9644 moniker += '/' + tag + '.js';
9645 }
9646 // source map hint
9647 code += "\n//# sourceURL=" + moniker + "\n";
9648 // evaluate the code
9649 scope.currentScript = scriptElt;
9650 eval.call(window, code);
9651 scope.currentScript = null;
9652 }
9653 this.markParsingComplete(scriptElt);
9654 },
9655 // determine the next element in the tree which should be parsed
9656 nextToParse: function() {
9657 return !this.parsingElement && this.nextToParseInDoc(mainDoc);
9658 },
9659 nextToParseInDoc: function(doc, link) {
9660 var nodes = doc.querySelectorAll(this.parseSelectorsForNode(doc));
9661 for (var i=0, l=nodes.length, p=0, n; (i<l) && (n=nodes[i]); i++) {
9662 if (!this.isParsed(n)) {
9663 if (this.hasResource(n)) {
9664 return nodeIsImport(n) ? this.nextToParseInDoc(n.import, n) : n;
9665 } else {
9666 return;
9667 }
9668 }
9669 }
9670 // all nodes have been parsed, ready to parse import, if any
9671 return link;
9672 },
9673 // return the set of parse selectors relevant for this node.
9674 parseSelectorsForNode: function(node) {
9675 var doc = node.ownerDocument || node;
9676 return doc === mainDoc ? this.documentSelectors : this.importsSelectors;
9677 },
9678 isParsed: function(node) {
9679 return node.__importParsed;
9680 },
9681 hasResource: function(node) {
9682 if (nodeIsImport(node) && !node.import) {
9683 return false;
9684 }
9685 if (node.localName === 'script' && node.src && !node.__resource) {
9686 return false;
9687 }
9688 return true;
9689 }
9690 };
9691
9692 function nodeIsImport(elt) {
9693 return (elt.localName === 'link') && (elt.rel === IMPORT_LINK_TYPE);
9694 }
9695
9696 // style/stylesheet handling
9697
9698 // clone style with proper path resolution for main document
9699 // NOTE: styles are the only elements that require direct path fixup.
9700 function cloneStyle(style) {
9701 var clone = style.ownerDocument.createElement('style');
9702 clone.textContent = style.textContent;
9703 path.resolveUrlsInStyle(clone);
9704 return clone;
9705 }
9706
9707 // path fixup: style elements in imports must be made relative to the main
9708 // document. We fixup url's in url() and @import.
9709 var CSS_URL_REGEXP = /(url\()([^)]*)(\))/g;
9710 var CSS_IMPORT_REGEXP = /(@import[\s]+(?!url\())([^;]*)(;)/g;
9711
9712 var path = {
9713 resolveUrlsInStyle: function(style) {
9714 var doc = style.ownerDocument;
9715 var resolver = doc.createElement('a');
9716 style.textContent = this.resolveUrlsInCssText(style.textContent, resolver);
9717 return style;
9718 },
9719 resolveUrlsInCssText: function(cssText, urlObj) {
9720 var r = this.replaceUrls(cssText, urlObj, CSS_URL_REGEXP);
9721 r = this.replaceUrls(r, urlObj, CSS_IMPORT_REGEXP);
9722 return r;
9723 },
9724 replaceUrls: function(text, urlObj, regexp) {
9725 return text.replace(regexp, function(m, pre, url, post) {
9726 var urlPath = url.replace(/["']/g, '');
9727 urlObj.href = urlPath;
9728 urlPath = urlObj.href;
9729 return pre + '\'' + urlPath + '\'' + post;
9730 });
9731 }
9732 }
9733
9734 // exports
9735 scope.parser = importParser;
9736 scope.path = path;
9737 scope.isIE = isIe;
9738
9739 })(HTMLImports);
9740
9741 /*
9742 * Copyright 2013 The Polymer Authors. All rights reserved.
9743 * Use of this source code is governed by a BSD-style
9744 * license that can be found in the LICENSE file.
9745 */
9746
9747 (function(scope) {
9748
9749 var hasNative = ('import' in document.createElement('link'));
9750 var useNative = hasNative;
9751 var flags = scope.flags;
9752 var IMPORT_LINK_TYPE = 'import';
9753
9754 // TODO(sorvell): SD polyfill intrusion
9755 var mainDoc = window.ShadowDOMPolyfill ?
9756 ShadowDOMPolyfill.wrapIfNeeded(document) : document;
9757
9758 if (!useNative) {
9759
9760 // imports
9761 var xhr = scope.xhr;
9762 var Loader = scope.Loader;
9763 var parser = scope.parser;
9764
9765 // importer
9766 // highlander object to manage loading of imports
9767
9768 // for any document, importer:
9769 // - loads any linked import documents (with deduping)
9770 // for any import document, importer also:
9771 // - loads text of external script tags
9772
9773 var importer = {
9774 documents: {},
9775 // nodes to load in the mian document
9776 documentPreloadSelectors: 'link[rel=' + IMPORT_LINK_TYPE + ']',
9777 // nodes to load in imports
9778 importsPreloadSelectors: [
9779 'link[rel=' + IMPORT_LINK_TYPE + ']',
9780 'script[src]:not([type])',
9781 'script[src][type="text/javascript"]'
9782 ].join(','),
9783 loadNode: function(node) {
9784 importLoader.addNode(node);
9785 },
9786 // load all loadable elements within the parent element
9787 loadSubtree: function(parent) {
9788 var nodes = this.marshalNodes(parent);
9789 // add these nodes to loader's queue
9790 importLoader.addNodes(nodes);
9791 },
9792 marshalNodes: function(parent) {
9793 // all preloadable nodes in inDocument
9794 return parent.querySelectorAll(this.loadSelectorsForNode(parent));
9795 },
9796 // find the proper set of load selectors for a given node
9797 loadSelectorsForNode: function(node) {
9798 var doc = node.ownerDocument || node;
9799 return doc === mainDoc ? this.documentPreloadSelectors :
9800 this.importsPreloadSelectors;
9801 },
9802 loaded: function(url, elt, resource) {
9803 flags.load && console.log('loaded', url, elt);
9804 // store generic resource
9805 // TODO(sorvell): fails for nodes inside <template>.content
9806 // see https://code.google.com/p/chromium/issues/detail?id=249381.
9807 elt.__resource = resource;
9808 if (isDocumentLink(elt)) {
9809 var doc = this.documents[url];
9810 // if we've never seen a document at this url
9811 if (!doc) {
9812 // generate an HTMLDocument from data
9813 doc = makeDocument(resource, url);
9814 doc.__importLink = elt;
9815 // TODO(sorvell): we cannot use MO to detect parsed nodes because
9816 // SD polyfill does not report these as mutations.
9817 this.bootDocument(doc);
9818 // cache document
9819 this.documents[url] = doc;
9820 }
9821 // don't store import record until we're actually loaded
9822 // store document resource
9823 elt.import = doc;
9824 }
9825 parser.parseNext();
9826 },
9827 bootDocument: function(doc) {
9828 this.loadSubtree(doc);
9829 this.observe(doc);
9830 parser.parseNext();
9831 },
9832 loadedAll: function() {
9833 parser.parseNext();
9834 }
9835 };
9836
9837 // loader singleton
9838 var importLoader = new Loader(importer.loaded.bind(importer),
9839 importer.loadedAll.bind(importer));
9840
9841 function isDocumentLink(elt) {
9842 return isLinkRel(elt, IMPORT_LINK_TYPE);
9843 }
9844
9845 function isLinkRel(elt, rel) {
9846 return elt.localName === 'link' && elt.getAttribute('rel') === rel;
9847 }
9848
9849 function isScript(elt) {
9850 return elt.localName === 'script';
9851 }
9852
9853 function makeDocument(resource, url) {
9854 // create a new HTML document
9855 var doc = resource;
9856 if (!(doc instanceof Document)) {
9857 doc = document.implementation.createHTMLDocument(IMPORT_LINK_TYPE);
9858 }
9859 // cache the new document's source url
9860 doc._URL = url;
9861 // establish a relative path via <base>
9862 var base = doc.createElement('base');
9863 base.setAttribute('href', url);
9864 // add baseURI support to browsers (IE) that lack it.
9865 if (!doc.baseURI) {
9866 doc.baseURI = url;
9867 }
9868 doc.head.appendChild(base);
9869 // install HTML last as it may trigger CustomElement upgrades
9870 // TODO(sjmiles): problem wrt to template boostrapping below,
9871 // template bootstrapping must (?) come before element upgrade
9872 // but we cannot bootstrap templates until they are in a document
9873 // which is too late
9874 if (!(resource instanceof Document)) {
9875 // install html
9876 doc.body.innerHTML = resource;
9877 }
9878 // TODO(sorvell): ideally this code is not aware of Template polyfill,
9879 // but for now the polyfill needs help to bootstrap these templates
9880 if (window.HTMLTemplateElement && HTMLTemplateElement.bootstrap) {
9881 HTMLTemplateElement.bootstrap(doc);
9882 }
9883 return doc;
9884 }
9885 } else {
9886 // do nothing if using native imports
9887 var importer = {};
9888 }
9889
9890 // NOTE: We cannot polyfill document.currentScript because it's not possible
9891 // both to override and maintain the ability to capture the native value;
9892 // therefore we choose to expose _currentScript both when native imports
9893 // and the polyfill are in use.
9894 var currentScriptDescriptor = {
9895 get: function() {
9896 return HTMLImports.currentScript || document.currentScript;
9897 },
9898 configurable: true
9899 };
9900
9901 Object.defineProperty(document, '_currentScript', currentScriptDescriptor);
9902 Object.defineProperty(mainDoc, '_currentScript', currentScriptDescriptor);
9903
9904 // Polyfill document.baseURI for browsers without it.
9905 if (!document.baseURI) {
9906 var baseURIDescriptor = {
9907 get: function() {
9908 return window.location.href;
9909 },
9910 configurable: true
9911 };
9912
9913 Object.defineProperty(document, 'baseURI', baseURIDescriptor);
9914 Object.defineProperty(mainDoc, 'baseURI', baseURIDescriptor);
9915 }
9916
9917 // call a callback when all HTMLImports in the document at call (or at least
9918 // document ready) time have loaded.
9919 // 1. ensure the document is in a ready state (has dom), then
9920 // 2. watch for loading of imports and call callback when done
9921 function whenImportsReady(callback, doc) {
9922 doc = doc || mainDoc;
9923 // if document is loading, wait and try again
9924 whenDocumentReady(function() {
9925 watchImportsLoad(callback, doc);
9926 }, doc);
9927 }
9928
9929 // call the callback when the document is in a ready state (has dom)
9930 var requiredReadyState = HTMLImports.isIE ? 'complete' : 'interactive';
9931 var READY_EVENT = 'readystatechange';
9932 function isDocumentReady(doc) {
9933 return (doc.readyState === 'complete' ||
9934 doc.readyState === requiredReadyState);
9935 }
9936
9937 // call <callback> when we ensure the document is in a ready state
9938 function whenDocumentReady(callback, doc) {
9939 if (!isDocumentReady(doc)) {
9940 var checkReady = function() {
9941 if (doc.readyState === 'complete' ||
9942 doc.readyState === requiredReadyState) {
9943 doc.removeEventListener(READY_EVENT, checkReady);
9944 whenDocumentReady(callback, doc);
9945 }
9946 }
9947 doc.addEventListener(READY_EVENT, checkReady);
9948 } else if (callback) {
9949 callback();
9950 }
9951 }
9952
9953 // call <callback> when we ensure all imports have loaded
9954 function watchImportsLoad(callback, doc) {
9955 var imports = doc.querySelectorAll('link[rel=import]');
9956 var loaded = 0, l = imports.length;
9957 function checkDone(d) {
9958 if (loaded == l) {
9959 // go async to ensure parser isn't stuck on a script tag
9960 requestAnimationFrame(callback);
9961 }
9962 }
9963 function loadedImport(e) {
9964 loaded++;
9965 checkDone();
9966 }
9967 if (l) {
9968 for (var i=0, imp; (i<l) && (imp=imports[i]); i++) {
9969 if (isImportLoaded(imp)) {
9970 loadedImport.call(imp);
9971 } else {
9972 imp.addEventListener('load', loadedImport);
9973 imp.addEventListener('error', loadedImport);
9974 }
9975 }
9976 } else {
9977 checkDone();
9978 }
9979 }
9980
9981 function isImportLoaded(link) {
9982 return useNative ? (link.import && (link.import.readyState !== 'loading')) :
9983 link.__importParsed;
9984 }
9985
9986 // exports
9987 scope.hasNative = hasNative;
9988 scope.useNative = useNative;
9989 scope.importer = importer;
9990 scope.whenImportsReady = whenImportsReady;
9991 scope.IMPORT_LINK_TYPE = IMPORT_LINK_TYPE;
9992 scope.isImportLoaded = isImportLoaded;
9993 scope.importLoader = importLoader;
9994
9995 })(window.HTMLImports);
9996
9997 /*
9998 Copyright 2013 The Polymer Authors. All rights reserved.
9999 Use of this source code is governed by a BSD-style
10000 license that can be found in the LICENSE file.
10001 */
10002
10003 (function(scope){
10004
10005 var IMPORT_LINK_TYPE = scope.IMPORT_LINK_TYPE;
10006 var importSelector = 'link[rel=' + IMPORT_LINK_TYPE + ']';
10007 var importer = scope.importer;
10008
10009 // we track mutations for addedNodes, looking for imports
10010 function handler(mutations) {
10011 for (var i=0, l=mutations.length, m; (i<l) && (m=mutations[i]); i++) {
10012 if (m.type === 'childList' && m.addedNodes.length) {
10013 addedNodes(m.addedNodes);
10014 }
10015 }
10016 }
10017
10018 // find loadable elements and add them to the importer
10019 function addedNodes(nodes) {
10020 for (var i=0, l=nodes.length, n; (i<l) && (n=nodes[i]); i++) {
10021 if (shouldLoadNode(n)) {
10022 importer.loadNode(n);
10023 }
10024 if (n.children && n.children.length) {
10025 addedNodes(n.children);
10026 }
10027 }
10028 }
10029
10030 function shouldLoadNode(node) {
10031 return (node.nodeType === 1) && matches.call(node,
10032 importer.loadSelectorsForNode(node));
10033 }
10034
10035 // x-plat matches
10036 var matches = HTMLElement.prototype.matches ||
10037 HTMLElement.prototype.matchesSelector ||
10038 HTMLElement.prototype.webkitMatchesSelector ||
10039 HTMLElement.prototype.mozMatchesSelector ||
10040 HTMLElement.prototype.msMatchesSelector;
10041
10042 var observer = new MutationObserver(handler);
10043
10044 // observe the given root for loadable elements
10045 function observe(root) {
10046 observer.observe(root, {childList: true, subtree: true});
10047 }
10048
10049 // exports
10050 // TODO(sorvell): factor so can put on scope
10051 scope.observe = observe;
10052 importer.observe = observe;
10053
10054 })(HTMLImports);
10055
10056 /*
10057 * Copyright 2013 The Polymer Authors. All rights reserved.
10058 * Use of this source code is governed by a BSD-style
10059 * license that can be found in the LICENSE file.
10060 */
10061 (function(){
10062
10063 // bootstrap
10064
10065 // IE shim for CustomEvent
10066 if (typeof window.CustomEvent !== 'function') {
10067 window.CustomEvent = function(inType, dictionary) {
10068 var e = document.createEvent('HTMLEvents');
10069 e.initEvent(inType,
10070 dictionary.bubbles === false ? false : true,
10071 dictionary.cancelable === false ? false : true,
10072 dictionary.detail);
10073 return e;
10074 };
10075 }
10076
10077 // TODO(sorvell): SD polyfill intrusion
10078 var doc = window.ShadowDOMPolyfill ?
10079 window.ShadowDOMPolyfill.wrapIfNeeded(document) : document;
10080
10081 // Fire the 'HTMLImportsLoaded' event when imports in document at load time
10082 // have loaded. This event is required to simulate the script blocking
10083 // behavior of native imports. A main document script that needs to be sure
10084 // imports have loaded should wait for this event.
10085 HTMLImports.whenImportsReady(function() {
10086 HTMLImports.ready = true;
10087 HTMLImports.readyTime = new Date().getTime();
10088 doc.dispatchEvent(
10089 new CustomEvent('HTMLImportsLoaded', {bubbles: true})
10090 );
10091 });
10092
10093
10094 // no need to bootstrap the polyfill when native imports is available.
10095 if (!HTMLImports.useNative) {
10096 function bootstrap() {
10097 HTMLImports.importer.bootDocument(doc);
10098 }
10099
10100 // TODO(sorvell): SD polyfill does *not* generate mutations for nodes added
10101 // by the parser. For this reason, we must wait until the dom exists to
10102 // bootstrap.
10103 if (document.readyState === 'complete' ||
10104 (document.readyState === 'interactive' && !window.attachEvent)) {
10105 bootstrap();
10106 } else {
10107 document.addEventListener('DOMContentLoaded', bootstrap);
10108 }
10109 }
10110
10111 })();
10112
10113 /*
10114 * Copyright 2013 The Polymer Authors. All rights reserved.
10115 * Use of this source code is governed by a BSD-style
10116 * license that can be found in the LICENSE file.
10117 */
10118 window.CustomElements = window.CustomElements || {flags:{}};
10119 /*
10120 Copyright 2013 The Polymer Authors. All rights reserved.
10121 Use of this source code is governed by a BSD-style
10122 license that can be found in the LICENSE file.
10123 */
10124
10125 (function(scope){
10126
10127 var logFlags = window.logFlags || {};
10128 var IMPORT_LINK_TYPE = window.HTMLImports ? HTMLImports.IMPORT_LINK_TYPE : 'none ';
10129
10130 // walk the subtree rooted at node, applying 'find(element, data)' function
10131 // to each element
10132 // if 'find' returns true for 'element', do not search element's subtree
10133 function findAll(node, find, data) {
10134 var e = node.firstElementChild;
10135 if (!e) {
10136 e = node.firstChild;
10137 while (e && e.nodeType !== Node.ELEMENT_NODE) {
10138 e = e.nextSibling;
10139 }
10140 }
10141 while (e) {
10142 if (find(e, data) !== true) {
10143 findAll(e, find, data);
10144 }
10145 e = e.nextElementSibling;
10146 }
10147 return null;
10148 }
10149
10150 // walk all shadowRoots on a given node.
10151 function forRoots(node, cb) {
10152 var root = node.shadowRoot;
10153 while(root) {
10154 forSubtree(root, cb);
10155 root = root.olderShadowRoot;
10156 }
10157 }
10158
10159 // walk the subtree rooted at node, including descent into shadow-roots,
10160 // applying 'cb' to each element
10161 function forSubtree(node, cb) {
10162 //logFlags.dom && node.childNodes && node.childNodes.length && console.group(' subTree: ', node);
10163 findAll(node, function(e) {
10164 if (cb(e)) {
10165 return true;
10166 }
10167 forRoots(e, cb);
10168 });
10169 forRoots(node, cb);
10170 //logFlags.dom && node.childNodes && node.childNodes.length && console.groupEn d();
10171 }
10172
10173 // manage lifecycle on added node
10174 function added(node) {
10175 if (upgrade(node)) {
10176 insertedNode(node);
10177 return true;
10178 }
10179 inserted(node);
10180 }
10181
10182 // manage lifecycle on added node's subtree only
10183 function addedSubtree(node) {
10184 forSubtree(node, function(e) {
10185 if (added(e)) {
10186 return true;
10187 }
10188 });
10189 }
10190
10191 // manage lifecycle on added node and it's subtree
10192 function addedNode(node) {
10193 return added(node) || addedSubtree(node);
10194 }
10195
10196 // upgrade custom elements at node, if applicable
10197 function upgrade(node) {
10198 if (!node.__upgraded__ && node.nodeType === Node.ELEMENT_NODE) {
10199 var type = node.getAttribute('is') || node.localName;
10200 var definition = scope.registry[type];
10201 if (definition) {
10202 logFlags.dom && console.group('upgrade:', node.localName);
10203 scope.upgrade(node);
10204 logFlags.dom && console.groupEnd();
10205 return true;
10206 }
10207 }
10208 }
10209
10210 function insertedNode(node) {
10211 inserted(node);
10212 if (inDocument(node)) {
10213 forSubtree(node, function(e) {
10214 inserted(e);
10215 });
10216 }
10217 }
10218
10219
10220 // TODO(sorvell): on platforms without MutationObserver, mutations may not be
10221 // reliable and therefore attached/detached are not reliable.
10222 // To make these callbacks less likely to fail, we defer all inserts and removes
10223 // to give a chance for elements to be inserted into dom.
10224 // This ensures attachedCallback fires for elements that are created and
10225 // immediately added to dom.
10226 var hasPolyfillMutations = (!window.MutationObserver ||
10227 (window.MutationObserver === window.JsMutationObserver));
10228 scope.hasPolyfillMutations = hasPolyfillMutations;
10229
10230 var isPendingMutations = false;
10231 var pendingMutations = [];
10232 function deferMutation(fn) {
10233 pendingMutations.push(fn);
10234 if (!isPendingMutations) {
10235 isPendingMutations = true;
10236 var async = (window.Platform && window.Platform.endOfMicrotask) ||
10237 setTimeout;
10238 async(takeMutations);
10239 }
10240 }
10241
10242 function takeMutations() {
10243 isPendingMutations = false;
10244 var $p = pendingMutations;
10245 for (var i=0, l=$p.length, p; (i<l) && (p=$p[i]); i++) {
10246 p();
10247 }
10248 pendingMutations = [];
10249 }
10250
10251 function inserted(element) {
10252 if (hasPolyfillMutations) {
10253 deferMutation(function() {
10254 _inserted(element);
10255 });
10256 } else {
10257 _inserted(element);
10258 }
10259 }
10260
10261 // TODO(sjmiles): if there are descents into trees that can never have inDocumen t(*) true, fix this
10262 function _inserted(element) {
10263 // TODO(sjmiles): it's possible we were inserted and removed in the space
10264 // of one microtask, in which case we won't be 'inDocument' here
10265 // But there are other cases where we are testing for inserted without
10266 // specific knowledge of mutations, and must test 'inDocument' to determine
10267 // whether to call inserted
10268 // If we can factor these cases into separate code paths we can have
10269 // better diagnostics.
10270 // TODO(sjmiles): when logging, do work on all custom elements so we can
10271 // track behavior even when callbacks not defined
10272 //console.log('inserted: ', element.localName);
10273 if (element.attachedCallback || element.detachedCallback || (element.__upgrade d__ && logFlags.dom)) {
10274 logFlags.dom && console.group('inserted:', element.localName);
10275 if (inDocument(element)) {
10276 element.__inserted = (element.__inserted || 0) + 1;
10277 // if we are in a 'removed' state, bluntly adjust to an 'inserted' state
10278 if (element.__inserted < 1) {
10279 element.__inserted = 1;
10280 }
10281 // if we are 'over inserted', squelch the callback
10282 if (element.__inserted > 1) {
10283 logFlags.dom && console.warn('inserted:', element.localName,
10284 'insert/remove count:', element.__inserted)
10285 } else if (element.attachedCallback) {
10286 logFlags.dom && console.log('inserted:', element.localName);
10287 element.attachedCallback();
10288 }
10289 }
10290 logFlags.dom && console.groupEnd();
10291 }
10292 }
10293
10294 function removedNode(node) {
10295 removed(node);
10296 forSubtree(node, function(e) {
10297 removed(e);
10298 });
10299 }
10300
10301 function removed(element) {
10302 if (hasPolyfillMutations) {
10303 deferMutation(function() {
10304 _removed(element);
10305 });
10306 } else {
10307 _removed(element);
10308 }
10309 }
10310
10311 function _removed(element) {
10312 // TODO(sjmiles): temporary: do work on all custom elements so we can track
10313 // behavior even when callbacks not defined
10314 if (element.attachedCallback || element.detachedCallback || (element.__upgrade d__ && logFlags.dom)) {
10315 logFlags.dom && console.group('removed:', element.localName);
10316 if (!inDocument(element)) {
10317 element.__inserted = (element.__inserted || 0) - 1;
10318 // if we are in a 'inserted' state, bluntly adjust to an 'removed' state
10319 if (element.__inserted > 0) {
10320 element.__inserted = 0;
10321 }
10322 // if we are 'over removed', squelch the callback
10323 if (element.__inserted < 0) {
10324 logFlags.dom && console.warn('removed:', element.localName,
10325 'insert/remove count:', element.__inserted)
10326 } else if (element.detachedCallback) {
10327 element.detachedCallback();
10328 }
10329 }
10330 logFlags.dom && console.groupEnd();
10331 }
10332 }
10333
10334 // SD polyfill intrustion due mainly to the fact that 'document'
10335 // is not entirely wrapped
10336 function wrapIfNeeded(node) {
10337 return window.ShadowDOMPolyfill ? ShadowDOMPolyfill.wrapIfNeeded(node)
10338 : node;
10339 }
10340
10341 function inDocument(element) {
10342 var p = element;
10343 var doc = wrapIfNeeded(document);
10344 while (p) {
10345 if (p == doc) {
10346 return true;
10347 }
10348 p = p.parentNode || p.host;
10349 }
10350 }
10351
10352 function watchShadow(node) {
10353 if (node.shadowRoot && !node.shadowRoot.__watched) {
10354 logFlags.dom && console.log('watching shadow-root for: ', node.localName);
10355 // watch all unwatched roots...
10356 var root = node.shadowRoot;
10357 while (root) {
10358 watchRoot(root);
10359 root = root.olderShadowRoot;
10360 }
10361 }
10362 }
10363
10364 function watchRoot(root) {
10365 if (!root.__watched) {
10366 observe(root);
10367 root.__watched = true;
10368 }
10369 }
10370
10371 function handler(mutations) {
10372 //
10373 if (logFlags.dom) {
10374 var mx = mutations[0];
10375 if (mx && mx.type === 'childList' && mx.addedNodes) {
10376 if (mx.addedNodes) {
10377 var d = mx.addedNodes[0];
10378 while (d && d !== document && !d.host) {
10379 d = d.parentNode;
10380 }
10381 var u = d && (d.URL || d._URL || (d.host && d.host.localName)) || '';
10382 u = u.split('/?').shift().split('/').pop();
10383 }
10384 }
10385 console.group('mutations (%d) [%s]', mutations.length, u || '');
10386 }
10387 //
10388 mutations.forEach(function(mx) {
10389 //logFlags.dom && console.group('mutation');
10390 if (mx.type === 'childList') {
10391 forEach(mx.addedNodes, function(n) {
10392 //logFlags.dom && console.log(n.localName);
10393 if (!n.localName) {
10394 return;
10395 }
10396 // nodes added may need lifecycle management
10397 addedNode(n);
10398 });
10399 // removed nodes may need lifecycle management
10400 forEach(mx.removedNodes, function(n) {
10401 //logFlags.dom && console.log(n.localName);
10402 if (!n.localName) {
10403 return;
10404 }
10405 removedNode(n);
10406 });
10407 }
10408 //logFlags.dom && console.groupEnd();
10409 });
10410 logFlags.dom && console.groupEnd();
10411 };
10412
10413 var observer = new MutationObserver(handler);
10414
10415 function takeRecords() {
10416 // TODO(sjmiles): ask Raf why we have to call handler ourselves
10417 handler(observer.takeRecords());
10418 takeMutations();
10419 }
10420
10421 var forEach = Array.prototype.forEach.call.bind(Array.prototype.forEach);
10422
10423 function observe(inRoot) {
10424 observer.observe(inRoot, {childList: true, subtree: true});
10425 }
10426
10427 function observeDocument(doc) {
10428 observe(doc);
10429 }
10430
10431 function upgradeDocument(doc) {
10432 logFlags.dom && console.group('upgradeDocument: ', (doc.baseURI).split('/').po p());
10433 addedNode(doc);
10434 logFlags.dom && console.groupEnd();
10435 }
10436
10437 function upgradeDocumentTree(doc) {
10438 doc = wrapIfNeeded(doc);
10439 upgradeDocument(doc);
10440 //console.log('upgradeDocumentTree: ', (doc.baseURI).split('/').pop());
10441 // upgrade contained imported documents
10442 var imports = doc.querySelectorAll('link[rel=' + IMPORT_LINK_TYPE + ']');
10443 for (var i=0, l=imports.length, n; (i<l) && (n=imports[i]); i++) {
10444 if (n.import && n.import.__parsed) {
10445 upgradeDocumentTree(n.import);
10446 }
10447 }
10448 }
10449
10450 // exports
10451 scope.IMPORT_LINK_TYPE = IMPORT_LINK_TYPE;
10452 scope.watchShadow = watchShadow;
10453 scope.upgradeDocumentTree = upgradeDocumentTree;
10454 scope.upgradeAll = addedNode;
10455 scope.upgradeSubtree = addedSubtree;
10456
10457 scope.observeDocument = observeDocument;
10458 scope.upgradeDocument = upgradeDocument;
10459
10460 scope.takeRecords = takeRecords;
10461
10462 })(window.CustomElements);
10463
10464 /*
10465 * Copyright 2013 The Polymer Authors. All rights reserved.
10466 * Use of this source code is governed by a BSD-style
10467 * license that can be found in the LICENSE file.
10468 */
10469
10470 /**
10471 * Implements `document.register`
10472 * @module CustomElements
10473 */
10474
10475 /**
10476 * Polyfilled extensions to the `document` object.
10477 * @class Document
10478 */
10479
10480 (function(scope) {
10481
10482 // imports
10483
10484 if (!scope) {
10485 scope = window.CustomElements = {flags:{}};
10486 }
10487 var flags = scope.flags;
10488
10489 // native document.registerElement?
10490
10491 var hasNative = Boolean(document.registerElement);
10492 // TODO(sorvell): See https://github.com/Polymer/polymer/issues/399
10493 // we'll address this by defaulting to CE polyfill in the presence of the SD
10494 // polyfill. This will avoid spamming excess attached/detached callbacks.
10495 // If there is a compelling need to run CE native with SD polyfill,
10496 // we'll need to fix this issue.
10497 var useNative = !flags.register && hasNative && !window.ShadowDOMPolyfill;
10498
10499 if (useNative) {
10500
10501 // stub
10502 var nop = function() {};
10503
10504 // exports
10505 scope.registry = {};
10506 scope.upgradeElement = nop;
10507
10508 scope.watchShadow = nop;
10509 scope.upgrade = nop;
10510 scope.upgradeAll = nop;
10511 scope.upgradeSubtree = nop;
10512 scope.observeDocument = nop;
10513 scope.upgradeDocument = nop;
10514 scope.upgradeDocumentTree = nop;
10515 scope.takeRecords = nop;
10516
10517 } else {
10518
10519 /**
10520 * Registers a custom tag name with the document.
10521 *
10522 * When a registered element is created, a `readyCallback` method is called
10523 * in the scope of the element. The `readyCallback` method can be specified on
10524 * either `options.prototype` or `options.lifecycle` with the latter taking
10525 * precedence.
10526 *
10527 * @method register
10528 * @param {String} name The tag name to register. Must include a dash ('-'),
10529 * for example 'x-component'.
10530 * @param {Object} options
10531 * @param {String} [options.extends]
10532 * (_off spec_) Tag name of an element to extend (or blank for a new
10533 * element). This parameter is not part of the specification, but instead
10534 * is a hint for the polyfill because the extendee is difficult to infer.
10535 * Remember that the input prototype must chain to the extended element's
10536 * prototype (or HTMLElement.prototype) regardless of the value of
10537 * `extends`.
10538 * @param {Object} options.prototype The prototype to use for the new
10539 * element. The prototype must inherit from HTMLElement.
10540 * @param {Object} [options.lifecycle]
10541 * Callbacks that fire at important phases in the life of the custom
10542 * element.
10543 *
10544 * @example
10545 * FancyButton = document.registerElement("fancy-button", {
10546 * extends: 'button',
10547 * prototype: Object.create(HTMLButtonElement.prototype, {
10548 * readyCallback: {
10549 * value: function() {
10550 * console.log("a fancy-button was created",
10551 * }
10552 * }
10553 * })
10554 * });
10555 * @return {Function} Constructor for the newly registered type.
10556 */
10557 function register(name, options) {
10558 //console.warn('document.registerElement("' + name + '", ', options, ')');
10559 // construct a defintion out of options
10560 // TODO(sjmiles): probably should clone options instead of mutating it
10561 var definition = options || {};
10562 if (!name) {
10563 // TODO(sjmiles): replace with more appropriate error (EricB can probably
10564 // offer guidance)
10565 throw new Error('document.registerElement: first argument `name` must not be empty');
10566 }
10567 if (name.indexOf('-') < 0) {
10568 // TODO(sjmiles): replace with more appropriate error (EricB can probably
10569 // offer guidance)
10570 throw new Error('document.registerElement: first argument (\'name\') must contain a dash (\'-\'). Argument provided was \'' + String(name) + '\'.');
10571 }
10572 // elements may only be registered once
10573 if (getRegisteredDefinition(name)) {
10574 throw new Error('DuplicateDefinitionError: a type with name \'' + String(n ame) + '\' is already registered');
10575 }
10576 // must have a prototype, default to an extension of HTMLElement
10577 // TODO(sjmiles): probably should throw if no prototype, check spec
10578 if (!definition.prototype) {
10579 // TODO(sjmiles): replace with more appropriate error (EricB can probably
10580 // offer guidance)
10581 throw new Error('Options missing required prototype property');
10582 }
10583 // record name
10584 definition.__name = name.toLowerCase();
10585 // ensure a lifecycle object so we don't have to null test it
10586 definition.lifecycle = definition.lifecycle || {};
10587 // build a list of ancestral custom elements (for native base detection)
10588 // TODO(sjmiles): we used to need to store this, but current code only
10589 // uses it in 'resolveTagName': it should probably be inlined
10590 definition.ancestry = ancestry(definition.extends);
10591 // extensions of native specializations of HTMLElement require localName
10592 // to remain native, and use secondary 'is' specifier for extension type
10593 resolveTagName(definition);
10594 // some platforms require modifications to the user-supplied prototype
10595 // chain
10596 resolvePrototypeChain(definition);
10597 // overrides to implement attributeChanged callback
10598 overrideAttributeApi(definition.prototype);
10599 // 7.1.5: Register the DEFINITION with DOCUMENT
10600 registerDefinition(definition.__name, definition);
10601 // 7.1.7. Run custom element constructor generation algorithm with PROTOTYPE
10602 // 7.1.8. Return the output of the previous step.
10603 definition.ctor = generateConstructor(definition);
10604 definition.ctor.prototype = definition.prototype;
10605 // force our .constructor to be our actual constructor
10606 definition.prototype.constructor = definition.ctor;
10607 // if initial parsing is complete
10608 if (scope.ready) {
10609 // upgrade any pre-existing nodes of this type
10610 scope.upgradeDocumentTree(document);
10611 }
10612 return definition.ctor;
10613 }
10614
10615 function ancestry(extnds) {
10616 var extendee = getRegisteredDefinition(extnds);
10617 if (extendee) {
10618 return ancestry(extendee.extends).concat([extendee]);
10619 }
10620 return [];
10621 }
10622
10623 function resolveTagName(definition) {
10624 // if we are explicitly extending something, that thing is our
10625 // baseTag, unless it represents a custom component
10626 var baseTag = definition.extends;
10627 // if our ancestry includes custom components, we only have a
10628 // baseTag if one of them does
10629 for (var i=0, a; (a=definition.ancestry[i]); i++) {
10630 baseTag = a.is && a.tag;
10631 }
10632 // our tag is our baseTag, if it exists, and otherwise just our name
10633 definition.tag = baseTag || definition.__name;
10634 if (baseTag) {
10635 // if there is a base tag, use secondary 'is' specifier
10636 definition.is = definition.__name;
10637 }
10638 }
10639
10640 function resolvePrototypeChain(definition) {
10641 // if we don't support __proto__ we need to locate the native level
10642 // prototype for precise mixing in
10643 if (!Object.__proto__) {
10644 // default prototype
10645 var nativePrototype = HTMLElement.prototype;
10646 // work out prototype when using type-extension
10647 if (definition.is) {
10648 var inst = document.createElement(definition.tag);
10649 nativePrototype = Object.getPrototypeOf(inst);
10650 }
10651 // ensure __proto__ reference is installed at each point on the prototype
10652 // chain.
10653 // NOTE: On platforms without __proto__, a mixin strategy is used instead
10654 // of prototype swizzling. In this case, this generated __proto__ provides
10655 // limited support for prototype traversal.
10656 var proto = definition.prototype, ancestor;
10657 while (proto && (proto !== nativePrototype)) {
10658 var ancestor = Object.getPrototypeOf(proto);
10659 proto.__proto__ = ancestor;
10660 proto = ancestor;
10661 }
10662 }
10663 // cache this in case of mixin
10664 definition.native = nativePrototype;
10665 }
10666
10667 // SECTION 4
10668
10669 function instantiate(definition) {
10670 // 4.a.1. Create a new object that implements PROTOTYPE
10671 // 4.a.2. Let ELEMENT by this new object
10672 //
10673 // the custom element instantiation algorithm must also ensure that the
10674 // output is a valid DOM element with the proper wrapper in place.
10675 //
10676 return upgrade(domCreateElement(definition.tag), definition);
10677 }
10678
10679 function upgrade(element, definition) {
10680 // some definitions specify an 'is' attribute
10681 if (definition.is) {
10682 element.setAttribute('is', definition.is);
10683 }
10684 // remove 'unresolved' attr, which is a standin for :unresolved.
10685 element.removeAttribute('unresolved');
10686 // make 'element' implement definition.prototype
10687 implement(element, definition);
10688 // flag as upgraded
10689 element.__upgraded__ = true;
10690 // lifecycle management
10691 created(element);
10692 // there should never be a shadow root on element at this point
10693 // we require child nodes be upgraded before `created`
10694 scope.upgradeSubtree(element);
10695 // OUTPUT
10696 return element;
10697 }
10698
10699 function implement(element, definition) {
10700 // prototype swizzling is best
10701 if (Object.__proto__) {
10702 element.__proto__ = definition.prototype;
10703 } else {
10704 // where above we can re-acquire inPrototype via
10705 // getPrototypeOf(Element), we cannot do so when
10706 // we use mixin, so we install a magic reference
10707 customMixin(element, definition.prototype, definition.native);
10708 element.__proto__ = definition.prototype;
10709 }
10710 }
10711
10712 function customMixin(inTarget, inSrc, inNative) {
10713 // TODO(sjmiles): 'used' allows us to only copy the 'youngest' version of
10714 // any property. This set should be precalculated. We also need to
10715 // consider this for supporting 'super'.
10716 var used = {};
10717 // start with inSrc
10718 var p = inSrc;
10719 // sometimes the default is HTMLUnknownElement.prototype instead of
10720 // HTMLElement.prototype, so we add a test
10721 // the idea is to avoid mixing in native prototypes, so adding
10722 // the second test is WLOG
10723 while (p !== inNative && p !== HTMLUnknownElement.prototype) {
10724 var keys = Object.getOwnPropertyNames(p);
10725 for (var i=0, k; k=keys[i]; i++) {
10726 if (!used[k]) {
10727 Object.defineProperty(inTarget, k,
10728 Object.getOwnPropertyDescriptor(p, k));
10729 used[k] = 1;
10730 }
10731 }
10732 p = Object.getPrototypeOf(p);
10733 }
10734 }
10735
10736 function created(element) {
10737 // invoke createdCallback
10738 if (element.createdCallback) {
10739 element.createdCallback();
10740 }
10741 }
10742
10743 // attribute watching
10744
10745 function overrideAttributeApi(prototype) {
10746 // overrides to implement callbacks
10747 // TODO(sjmiles): should support access via .attributes NamedNodeMap
10748 // TODO(sjmiles): preserves user defined overrides, if any
10749 if (prototype.setAttribute._polyfilled) {
10750 return;
10751 }
10752 var setAttribute = prototype.setAttribute;
10753 prototype.setAttribute = function(name, value) {
10754 changeAttribute.call(this, name, value, setAttribute);
10755 }
10756 var removeAttribute = prototype.removeAttribute;
10757 prototype.removeAttribute = function(name) {
10758 changeAttribute.call(this, name, null, removeAttribute);
10759 }
10760 prototype.setAttribute._polyfilled = true;
10761 }
10762
10763 // https://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/custom/
10764 // index.html#dfn-attribute-changed-callback
10765 function changeAttribute(name, value, operation) {
10766 var oldValue = this.getAttribute(name);
10767 operation.apply(this, arguments);
10768 var newValue = this.getAttribute(name);
10769 if (this.attributeChangedCallback
10770 && (newValue !== oldValue)) {
10771 this.attributeChangedCallback(name, oldValue, newValue);
10772 }
10773 }
10774
10775 // element registry (maps tag names to definitions)
10776
10777 var registry = {};
10778
10779 function getRegisteredDefinition(name) {
10780 if (name) {
10781 return registry[name.toLowerCase()];
10782 }
10783 }
10784
10785 function registerDefinition(name, definition) {
10786 if (registry[name]) {
10787 throw new Error('a type with that name is already registered.');
10788 }
10789 registry[name] = definition;
10790 }
10791
10792 function generateConstructor(definition) {
10793 return function() {
10794 return instantiate(definition);
10795 };
10796 }
10797
10798 function createElement(tag, typeExtension) {
10799 // TODO(sjmiles): ignore 'tag' when using 'typeExtension', we could
10800 // error check it, or perhaps there should only ever be one argument
10801 var definition = getRegisteredDefinition(typeExtension || tag);
10802 if (definition) {
10803 if (tag == definition.tag && typeExtension == definition.is) {
10804 return new definition.ctor();
10805 }
10806 // Handle empty string for type extension.
10807 if (!typeExtension && !definition.is) {
10808 return new definition.ctor();
10809 }
10810 }
10811
10812 if (typeExtension) {
10813 var element = createElement(tag);
10814 element.setAttribute('is', typeExtension);
10815 return element;
10816 }
10817 var element = domCreateElement(tag);
10818 // Custom tags should be HTMLElements even if not upgraded.
10819 if (tag.indexOf('-') >= 0) {
10820 implement(element, HTMLElement);
10821 }
10822 return element;
10823 }
10824
10825 function upgradeElement(element) {
10826 if (!element.__upgraded__ && (element.nodeType === Node.ELEMENT_NODE)) {
10827 var is = element.getAttribute('is');
10828 var definition = getRegisteredDefinition(is || element.localName);
10829 if (definition) {
10830 if (is && definition.tag == element.localName) {
10831 return upgrade(element, definition);
10832 } else if (!is && !definition.extends) {
10833 return upgrade(element, definition);
10834 }
10835 }
10836 }
10837 }
10838
10839 function cloneNode(deep) {
10840 // call original clone
10841 var n = domCloneNode.call(this, deep);
10842 // upgrade the element and subtree
10843 scope.upgradeAll(n);
10844 // return the clone
10845 return n;
10846 }
10847 // capture native createElement before we override it
10848
10849 var domCreateElement = document.createElement.bind(document);
10850
10851 // capture native cloneNode before we override it
10852
10853 var domCloneNode = Node.prototype.cloneNode;
10854
10855 // exports
10856
10857 document.registerElement = register;
10858 document.createElement = createElement; // override
10859 Node.prototype.cloneNode = cloneNode; // override
10860
10861 scope.registry = registry;
10862
10863 /**
10864 * Upgrade an element to a custom element. Upgrading an element
10865 * causes the custom prototype to be applied, an `is` attribute
10866 * to be attached (as needed), and invocation of the `readyCallback`.
10867 * `upgrade` does nothing if the element is already upgraded, or
10868 * if it matches no registered custom tag name.
10869 *
10870 * @method ugprade
10871 * @param {Element} element The element to upgrade.
10872 * @return {Element} The upgraded element.
10873 */
10874 scope.upgrade = upgradeElement;
10875 }
10876
10877 // bc
10878 document.register = document.registerElement;
10879
10880 scope.hasNative = hasNative;
10881 scope.useNative = useNative;
10882
10883 })(window.CustomElements);
10884
10885 /*
10886 * Copyright 2013 The Polymer Authors. All rights reserved.
10887 * Use of this source code is governed by a BSD-style
10888 * license that can be found in the LICENSE file.
10889 */
10890
10891 (function(scope) {
10892
10893 // import
10894
10895 var IMPORT_LINK_TYPE = scope.IMPORT_LINK_TYPE;
10896
10897 // highlander object for parsing a document tree
10898
10899 var parser = {
10900 selectors: [
10901 'link[rel=' + IMPORT_LINK_TYPE + ']'
10902 ],
10903 map: {
10904 link: 'parseLink'
10905 },
10906 parse: function(inDocument) {
10907 if (!inDocument.__parsed) {
10908 // only parse once
10909 inDocument.__parsed = true;
10910 // all parsable elements in inDocument (depth-first pre-order traversal)
10911 var elts = inDocument.querySelectorAll(parser.selectors);
10912 // for each parsable node type, call the mapped parsing method
10913 forEach(elts, function(e) {
10914 parser[parser.map[e.localName]](e);
10915 });
10916 // upgrade all upgradeable static elements, anything dynamically
10917 // created should be caught by observer
10918 CustomElements.upgradeDocument(inDocument);
10919 // observe document for dom changes
10920 CustomElements.observeDocument(inDocument);
10921 }
10922 },
10923 parseLink: function(linkElt) {
10924 // imports
10925 if (isDocumentLink(linkElt)) {
10926 this.parseImport(linkElt);
10927 }
10928 },
10929 parseImport: function(linkElt) {
10930 if (linkElt.import) {
10931 parser.parse(linkElt.import);
10932 }
10933 }
10934 };
10935
10936 function isDocumentLink(inElt) {
10937 return (inElt.localName === 'link'
10938 && inElt.getAttribute('rel') === IMPORT_LINK_TYPE);
10939 }
10940
10941 var forEach = Array.prototype.forEach.call.bind(Array.prototype.forEach);
10942
10943 // exports
10944
10945 scope.parser = parser;
10946 scope.IMPORT_LINK_TYPE = IMPORT_LINK_TYPE;
10947
10948 })(window.CustomElements);
10949 /*
10950 * Copyright 2013 The Polymer Authors. All rights reserved.
10951 * Use of this source code is governed by a BSD-style
10952 * license that can be found in the LICENSE file.
10953 */
10954 (function(scope){
10955
10956 // bootstrap parsing
10957 function bootstrap() {
10958 // parse document
10959 CustomElements.parser.parse(document);
10960 // one more pass before register is 'live'
10961 CustomElements.upgradeDocument(document);
10962 // choose async
10963 var async = window.Platform && Platform.endOfMicrotask ?
10964 Platform.endOfMicrotask :
10965 setTimeout;
10966 async(function() {
10967 // set internal 'ready' flag, now document.registerElement will trigger
10968 // synchronous upgrades
10969 CustomElements.ready = true;
10970 // capture blunt profiling data
10971 CustomElements.readyTime = Date.now();
10972 if (window.HTMLImports) {
10973 CustomElements.elapsed = CustomElements.readyTime - HTMLImports.readyTime;
10974 }
10975 // notify the system that we are bootstrapped
10976 document.dispatchEvent(
10977 new CustomEvent('WebComponentsReady', {bubbles: true})
10978 );
10979
10980 // install upgrade hook if HTMLImports are available
10981 if (window.HTMLImports) {
10982 HTMLImports.__importsParsingHook = function(elt) {
10983 CustomElements.parser.parse(elt.import);
10984 }
10985 }
10986 });
10987 }
10988
10989 // CustomEvent shim for IE
10990 if (typeof window.CustomEvent !== 'function') {
10991 window.CustomEvent = function(inType) {
10992 var e = document.createEvent('HTMLEvents');
10993 e.initEvent(inType, true, true);
10994 return e;
10995 };
10996 }
10997
10998 // When loading at readyState complete time (or via flag), boot custom elements
10999 // immediately.
11000 // If relevant, HTMLImports must already be loaded.
11001 if (document.readyState === 'complete' || scope.flags.eager) {
11002 bootstrap();
11003 // When loading at readyState interactive time, bootstrap only if HTMLImports
11004 // are not pending. Also avoid IE as the semantics of this state are unreliable.
11005 } else if (document.readyState === 'interactive' && !window.attachEvent &&
11006 (!window.HTMLImports || window.HTMLImports.ready)) {
11007 bootstrap();
11008 // When loading at other readyStates, wait for the appropriate DOM event to
11009 // bootstrap.
11010 } else {
11011 var loadEvent = window.HTMLImports && !HTMLImports.ready ?
11012 'HTMLImportsLoaded' : 'DOMContentLoaded';
11013 window.addEventListener(loadEvent, bootstrap);
11014 }
11015
11016 })(window.CustomElements);
11017
11018 /*
11019 * Copyright 2013 The Polymer Authors. All rights reserved.
11020 * Use of this source code is governed by a BSD-style
11021 * license that can be found in the LICENSE file.
11022 */
11023 (function() {
11024
11025 // inject style sheet
11026 var style = document.createElement('style');
11027 style.textContent = 'element {display: none !important;} /* injected by platform .js */';
11028 var head = document.querySelector('head');
11029 head.insertBefore(style, head.firstChild);
11030
11031 if (window.ShadowDOMPolyfill) {
11032
11033 // ensure wrapped inputs for these functions
11034 var fns = ['upgradeAll', 'upgradeSubtree', 'observeDocument',
11035 'upgradeDocument'];
11036
11037 // cache originals
11038 var original = {};
11039 fns.forEach(function(fn) {
11040 original[fn] = CustomElements[fn];
11041 });
11042
11043 // override
11044 fns.forEach(function(fn) {
11045 CustomElements[fn] = function(inNode) {
11046 return original[fn](wrap(inNode));
11047 };
11048 });
11049
11050 }
11051
11052 })();
11053
11054 /*
11055 * Copyright 2014 The Polymer Authors. All rights reserved.
11056 * Use of this source code is governed by a BSD-style
11057 * license that can be found in the LICENSE file.
11058 */
11059 (function(scope) {
11060
11061 var STYLE_SELECTOR = 'style';
11062
11063 var urlResolver = scope.urlResolver;
11064
11065 var loader = {
11066 cacheStyles: function(styles, callback) {
11067 var css = [];
11068 for (var i=0, l=styles.length, s; (i<l) && (s=styles[i]); i++) {
11069 css.push(s.textContent);
11070 }
11071 cacheCssText(css.join('\n'), callback);
11072 },
11073 xhrStyles: function(styles, callback) {
11074 var loaded=0, l = styles.length;
11075 // called in the context of the style
11076 function loadedStyle(style) {
11077 //console.log(style.textContent);
11078 loaded++;
11079 if (loaded === l && callback) {
11080 callback();
11081 }
11082 }
11083 for (var i=0, s; (i<l) && (s=styles[i]); i++) {
11084 xhrLoadStyle(s, loadedStyle);
11085 }
11086 }
11087 };
11088
11089 // use the platform to preload styles
11090 var preloadElement = document.createElement('preloader');
11091 preloadElement.style.display = 'none';
11092 var preloadRoot = preloadElement.createShadowRoot();
11093 document.head.appendChild(preloadElement);
11094
11095 function cacheCssText(cssText, callback) {
11096 var style = createStyleElement(cssText);
11097 if (callback) {
11098 style.addEventListener('load', callback);
11099 style.addEventListener('error', callback);
11100 }
11101 preloadRoot.appendChild(style);
11102 }
11103
11104 function createStyleElement(cssText, scope) {
11105 scope = scope || document;
11106 scope = scope.createElement ? scope : scope.ownerDocument;
11107 var style = scope.createElement('style');
11108 style.textContent = cssText;
11109 return style;
11110 }
11111
11112 // TODO(sorvell): use a common loader shared with HTMLImports polyfill
11113 // currently, this just loads the first @import per style element
11114 // and does not recurse into loaded elements; we'll address this with a
11115 // generalized loader that's built out of the one in the HTMLImports polyfill.
11116 // polyfill the loading of a style element's @import via xhr
11117 function xhrLoadStyle(style, callback) {
11118 HTMLImports.xhr.load(atImportUrlFromStyle(style), function (err, resource,
11119 url) {
11120 replaceAtImportWithCssText(this, url, resource);
11121 this.textContent = urlResolver.resolveCssText(this.textContent, url);
11122 callback && callback(this);
11123 }, style);
11124 }
11125
11126 var atImportRe = /@import\s[(]?['"]?([^\s'";)]*)/;
11127
11128 // get the first @import rule from a style
11129 function atImportUrlFromStyle(style) {
11130 var matches = style.textContent.match(atImportRe);
11131 return matches && matches[1];
11132 }
11133
11134 function replaceAtImportWithCssText(style, url, cssText) {
11135 var re = new RegExp('@import[^;]*' + url + '[^;]*;', 'i');
11136 style.textContent = style.textContent.replace(re, cssText);
11137 }
11138
11139 // exports
11140 scope.loader = loader;
11141
11142 })(window.Platform);
11143
11144 /*
11145 * Copyright 2013 The Polymer Authors. All rights reserved.
11146 * Use of this source code is governed by a BSD-style
11147 * license that can be found in the LICENSE file.
11148 */
11149
11150 (function(scope) {
11151 scope = scope || {};
11152 scope.external = scope.external || {};
11153 var target = {
11154 shadow: function(inEl) {
11155 if (inEl) {
11156 return inEl.shadowRoot || inEl.webkitShadowRoot;
11157 }
11158 },
11159 canTarget: function(shadow) {
11160 return shadow && Boolean(shadow.elementFromPoint);
11161 },
11162 targetingShadow: function(inEl) {
11163 var s = this.shadow(inEl);
11164 if (this.canTarget(s)) {
11165 return s;
11166 }
11167 },
11168 olderShadow: function(shadow) {
11169 var os = shadow.olderShadowRoot;
11170 if (!os) {
11171 var se = shadow.querySelector('shadow');
11172 if (se) {
11173 os = se.olderShadowRoot;
11174 }
11175 }
11176 return os;
11177 },
11178 allShadows: function(element) {
11179 var shadows = [], s = this.shadow(element);
11180 while(s) {
11181 shadows.push(s);
11182 s = this.olderShadow(s);
11183 }
11184 return shadows;
11185 },
11186 searchRoot: function(inRoot, x, y) {
11187 if (inRoot) {
11188 var t = inRoot.elementFromPoint(x, y);
11189 var st, sr, os;
11190 // is element a shadow host?
11191 sr = this.targetingShadow(t);
11192 while (sr) {
11193 // find the the element inside the shadow root
11194 st = sr.elementFromPoint(x, y);
11195 if (!st) {
11196 // check for older shadows
11197 sr = this.olderShadow(sr);
11198 } else {
11199 // shadowed element may contain a shadow root
11200 var ssr = this.targetingShadow(st);
11201 return this.searchRoot(ssr, x, y) || st;
11202 }
11203 }
11204 // light dom element is the target
11205 return t;
11206 }
11207 },
11208 owner: function(element) {
11209 var s = element;
11210 // walk up until you hit the shadow root or document
11211 while (s.parentNode) {
11212 s = s.parentNode;
11213 }
11214 // the owner element is expected to be a Document or ShadowRoot
11215 if (s.nodeType != Node.DOCUMENT_NODE && s.nodeType != Node.DOCUMENT_FRAGME NT_NODE) {
11216 s = document;
11217 }
11218 return s;
11219 },
11220 findTarget: function(inEvent) {
11221 var x = inEvent.clientX, y = inEvent.clientY;
11222 // if the listener is in the shadow root, it is much faster to start there
11223 var s = this.owner(inEvent.target);
11224 // if x, y is not in this root, fall back to document search
11225 if (!s.elementFromPoint(x, y)) {
11226 s = document;
11227 }
11228 return this.searchRoot(s, x, y);
11229 }
11230 };
11231 scope.targetFinding = target;
11232 scope.findTarget = target.findTarget.bind(target);
11233
11234 window.PointerEventsPolyfill = scope;
11235 })(window.PointerEventsPolyfill);
11236
11237 /*
11238 * Copyright 2013 The Polymer Authors. All rights reserved.
11239 * Use of this source code is governed by a BSD-style
11240 * license that can be found in the LICENSE file.
11241 */
11242 (function() {
11243 function shadowSelector(v) {
11244 return 'body ^^ ' + selector(v);
11245 }
11246 function selector(v) {
11247 return '[touch-action="' + v + '"]';
11248 }
11249 function rule(v) {
11250 return '{ -ms-touch-action: ' + v + '; touch-action: ' + v + '; touch-action -delay: none; }';
11251 }
11252 var attrib2css = [
11253 'none',
11254 'auto',
11255 'pan-x',
11256 'pan-y',
11257 {
11258 rule: 'pan-x pan-y',
11259 selectors: [
11260 'pan-x pan-y',
11261 'pan-y pan-x'
11262 ]
11263 }
11264 ];
11265 var styles = '';
11266 attrib2css.forEach(function(r) {
11267 if (String(r) === r) {
11268 styles += selector(r) + rule(r) + '\n';
11269 styles += shadowSelector(r) + rule(r) + '\n';
11270 } else {
11271 styles += r.selectors.map(selector) + rule(r.rule) + '\n';
11272 styles += r.selectors.map(shadowSelector) + rule(r.rule) + '\n';
11273 }
11274 });
11275 var el = document.createElement('style');
11276 el.textContent = styles;
11277 document.head.appendChild(el);
11278 })();
11279
11280 /*
11281 * Copyright 2013 The Polymer Authors. All rights reserved.
11282 * Use of this source code is governed by a BSD-style
11283 * license that can be found in the LICENSE file.
11284 */
11285
11286 /**
11287 * This is the constructor for new PointerEvents.
11288 *
11289 * New Pointer Events must be given a type, and an optional dictionary of
11290 * initialization properties.
11291 *
11292 * Due to certain platform requirements, events returned from the constructor
11293 * identify as MouseEvents.
11294 *
11295 * @constructor
11296 * @param {String} inType The type of the event to create.
11297 * @param {Object} [inDict] An optional dictionary of initial event properties.
11298 * @return {Event} A new PointerEvent of type `inType` and initialized with prop erties from `inDict`.
11299 */
11300 (function(scope) {
11301 // test for DOM Level 4 Events
11302 var NEW_MOUSE_EVENT = false;
11303 var HAS_BUTTONS = false;
11304 try {
11305 var ev = new MouseEvent('click', {buttons: 1});
11306 NEW_MOUSE_EVENT = true;
11307 HAS_BUTTONS = ev.buttons === 1;
11308 } catch(e) {
11309 }
11310
11311 var MOUSE_PROPS = [
11312 'bubbles',
11313 'cancelable',
11314 'view',
11315 'detail',
11316 'screenX',
11317 'screenY',
11318 'clientX',
11319 'clientY',
11320 'ctrlKey',
11321 'altKey',
11322 'shiftKey',
11323 'metaKey',
11324 'button',
11325 'relatedTarget',
11326 ];
11327
11328 var MOUSE_DEFAULTS = [
11329 false,
11330 false,
11331 null,
11332 null,
11333 0,
11334 0,
11335 0,
11336 0,
11337 false,
11338 false,
11339 false,
11340 false,
11341 0,
11342 null
11343 ];
11344
11345 function PointerEvent(inType, inDict) {
11346 inDict = inDict || {};
11347 // According to the w3c spec,
11348 // http://www.w3.org/TR/DOM-Level-3-Events/#events-MouseEvent-button
11349 // MouseEvent.button == 0 can mean either no mouse button depressed, or the
11350 // left mouse button depressed.
11351 //
11352 // As of now, the only way to distinguish between the two states of
11353 // MouseEvent.button is by using the deprecated MouseEvent.which property, a s
11354 // this maps mouse buttons to positive integers > 0, and uses 0 to mean that
11355 // no mouse button is held.
11356 //
11357 // MouseEvent.which is derived from MouseEvent.button at MouseEvent creation ,
11358 // but initMouseEvent does not expose an argument with which to set
11359 // MouseEvent.which. Calling initMouseEvent with a buttonArg of 0 will set
11360 // MouseEvent.button == 0 and MouseEvent.which == 1, breaking the expectatio ns
11361 // of app developers.
11362 //
11363 // The only way to propagate the correct state of MouseEvent.which and
11364 // MouseEvent.button to a new MouseEvent.button == 0 and MouseEvent.which == 0
11365 // is to call initMouseEvent with a buttonArg value of -1.
11366 //
11367 // This is fixed with DOM Level 4's use of buttons
11368 var buttons;
11369 if (inDict.buttons || HAS_BUTTONS) {
11370 buttons = inDict.buttons;
11371 } else {
11372 switch (inDict.which) {
11373 case 1: buttons = 1; break;
11374 case 2: buttons = 4; break;
11375 case 3: buttons = 2; break;
11376 default: buttons = 0;
11377 }
11378 }
11379
11380 var e;
11381 if (NEW_MOUSE_EVENT) {
11382 e = new MouseEvent(inType, inDict);
11383 } else {
11384 e = document.createEvent('MouseEvent');
11385
11386 // import values from the given dictionary
11387 var props = {}, p;
11388 for(var i = 0; i < MOUSE_PROPS.length; i++) {
11389 p = MOUSE_PROPS[i];
11390 props[p] = inDict[p] || MOUSE_DEFAULTS[i];
11391 }
11392
11393 // define the properties inherited from MouseEvent
11394 e.initMouseEvent(
11395 inType, props.bubbles, props.cancelable, props.view, props.detail,
11396 props.screenX, props.screenY, props.clientX, props.clientY, props.ctrlKe y,
11397 props.altKey, props.shiftKey, props.metaKey, props.button, props.related Target
11398 );
11399 }
11400
11401 // make the event pass instanceof checks
11402 e.__proto__ = PointerEvent.prototype;
11403
11404 // define the buttons property according to DOM Level 3 spec
11405 if (!HAS_BUTTONS) {
11406 // IE 10 has buttons on MouseEvent.prototype as a getter w/o any setting
11407 // mechanism
11408 Object.defineProperty(e, 'buttons', {get: function(){ return buttons; }, e numerable: true});
11409 }
11410
11411 // Spec requires that pointers without pressure specified use 0.5 for down
11412 // state and 0 for up state.
11413 var pressure = 0;
11414 if (inDict.pressure) {
11415 pressure = inDict.pressure;
11416 } else {
11417 pressure = buttons ? 0.5 : 0;
11418 }
11419
11420 // define the properties of the PointerEvent interface
11421 Object.defineProperties(e, {
11422 pointerId: { value: inDict.pointerId || 0, enumerable: true },
11423 width: { value: inDict.width || 0, enumerable: true },
11424 height: { value: inDict.height || 0, enumerable: true },
11425 pressure: { value: pressure, enumerable: true },
11426 tiltX: { value: inDict.tiltX || 0, enumerable: true },
11427 tiltY: { value: inDict.tiltY || 0, enumerable: true },
11428 pointerType: { value: inDict.pointerType || '', enumerable: true },
11429 hwTimestamp: { value: inDict.hwTimestamp || 0, enumerable: true },
11430 isPrimary: { value: inDict.isPrimary || false, enumerable: true }
11431 });
11432 return e;
11433 }
11434
11435 // PointerEvent extends MouseEvent
11436 PointerEvent.prototype = Object.create(MouseEvent.prototype);
11437
11438 // attach to window
11439 if (!scope.PointerEvent) {
11440 scope.PointerEvent = PointerEvent;
11441 }
11442 })(window);
11443
11444 /*
11445 * Copyright 2013 The Polymer Authors. All rights reserved.
11446 * Use of this source code is governed by a BSD-style
11447 * license that can be found in the LICENSE file.
11448 */
11449
11450 /**
11451 * This module implements an map of pointer states
11452 */
11453 (function(scope) {
11454 var USE_MAP = window.Map && window.Map.prototype.forEach;
11455 var POINTERS_FN = function(){ return this.size; };
11456 function PointerMap() {
11457 if (USE_MAP) {
11458 var m = new Map();
11459 m.pointers = POINTERS_FN;
11460 return m;
11461 } else {
11462 this.keys = [];
11463 this.values = [];
11464 }
11465 }
11466
11467 PointerMap.prototype = {
11468 set: function(inId, inEvent) {
11469 var i = this.keys.indexOf(inId);
11470 if (i > -1) {
11471 this.values[i] = inEvent;
11472 } else {
11473 this.keys.push(inId);
11474 this.values.push(inEvent);
11475 }
11476 },
11477 has: function(inId) {
11478 return this.keys.indexOf(inId) > -1;
11479 },
11480 'delete': function(inId) {
11481 var i = this.keys.indexOf(inId);
11482 if (i > -1) {
11483 this.keys.splice(i, 1);
11484 this.values.splice(i, 1);
11485 }
11486 },
11487 get: function(inId) {
11488 var i = this.keys.indexOf(inId);
11489 return this.values[i];
11490 },
11491 clear: function() {
11492 this.keys.length = 0;
11493 this.values.length = 0;
11494 },
11495 // return value, key, map
11496 forEach: function(callback, thisArg) {
11497 this.values.forEach(function(v, i) {
11498 callback.call(thisArg, v, this.keys[i], this);
11499 }, this);
11500 },
11501 pointers: function() {
11502 return this.keys.length;
11503 }
11504 };
11505
11506 scope.PointerMap = PointerMap;
11507 })(window.PointerEventsPolyfill);
11508
11509 /*
11510 * Copyright 2013 The Polymer Authors. All rights reserved.
11511 * Use of this source code is governed by a BSD-style
11512 * license that can be found in the LICENSE file.
11513 */
11514
11515 (function(scope) {
11516 var CLONE_PROPS = [
11517 // MouseEvent
11518 'bubbles',
11519 'cancelable',
11520 'view',
11521 'detail',
11522 'screenX',
11523 'screenY',
11524 'clientX',
11525 'clientY',
11526 'ctrlKey',
11527 'altKey',
11528 'shiftKey',
11529 'metaKey',
11530 'button',
11531 'relatedTarget',
11532 // DOM Level 3
11533 'buttons',
11534 // PointerEvent
11535 'pointerId',
11536 'width',
11537 'height',
11538 'pressure',
11539 'tiltX',
11540 'tiltY',
11541 'pointerType',
11542 'hwTimestamp',
11543 'isPrimary',
11544 // event instance
11545 'type',
11546 'target',
11547 'currentTarget',
11548 'which'
11549 ];
11550
11551 var CLONE_DEFAULTS = [
11552 // MouseEvent
11553 false,
11554 false,
11555 null,
11556 null,
11557 0,
11558 0,
11559 0,
11560 0,
11561 false,
11562 false,
11563 false,
11564 false,
11565 0,
11566 null,
11567 // DOM Level 3
11568 0,
11569 // PointerEvent
11570 0,
11571 0,
11572 0,
11573 0,
11574 0,
11575 0,
11576 '',
11577 0,
11578 false,
11579 // event instance
11580 '',
11581 null,
11582 null,
11583 0
11584 ];
11585
11586 var HAS_SVG_INSTANCE = (typeof SVGElementInstance !== 'undefined');
11587
11588 /**
11589 * This module is for normalizing events. Mouse and Touch events will be
11590 * collected here, and fire PointerEvents that have the same semantics, no
11591 * matter the source.
11592 * Events fired:
11593 * - pointerdown: a pointing is added
11594 * - pointerup: a pointer is removed
11595 * - pointermove: a pointer is moved
11596 * - pointerover: a pointer crosses into an element
11597 * - pointerout: a pointer leaves an element
11598 * - pointercancel: a pointer will no longer generate events
11599 */
11600 var dispatcher = {
11601 targets: new WeakMap(),
11602 handledEvents: new WeakMap(),
11603 pointermap: new scope.PointerMap(),
11604 eventMap: {},
11605 // Scope objects for native events.
11606 // This exists for ease of testing.
11607 eventSources: {},
11608 eventSourceList: [],
11609 /**
11610 * Add a new event source that will generate pointer events.
11611 *
11612 * `inSource` must contain an array of event names named `events`, and
11613 * functions with the names specified in the `events` array.
11614 * @param {string} name A name for the event source
11615 * @param {Object} source A new source of platform events.
11616 */
11617 registerSource: function(name, source) {
11618 var s = source;
11619 var newEvents = s.events;
11620 if (newEvents) {
11621 newEvents.forEach(function(e) {
11622 if (s[e]) {
11623 this.eventMap[e] = s[e].bind(s);
11624 }
11625 }, this);
11626 this.eventSources[name] = s;
11627 this.eventSourceList.push(s);
11628 }
11629 },
11630 register: function(element) {
11631 var l = this.eventSourceList.length;
11632 for (var i = 0, es; (i < l) && (es = this.eventSourceList[i]); i++) {
11633 // call eventsource register
11634 es.register.call(es, element);
11635 }
11636 },
11637 unregister: function(element) {
11638 var l = this.eventSourceList.length;
11639 for (var i = 0, es; (i < l) && (es = this.eventSourceList[i]); i++) {
11640 // call eventsource register
11641 es.unregister.call(es, element);
11642 }
11643 },
11644 contains: scope.external.contains || function(container, contained) {
11645 return container.contains(contained);
11646 },
11647 // EVENTS
11648 down: function(inEvent) {
11649 this.fireEvent('pointerdown', inEvent);
11650 },
11651 move: function(inEvent) {
11652 this.fireEvent('pointermove', inEvent);
11653 },
11654 up: function(inEvent) {
11655 this.fireEvent('pointerup', inEvent);
11656 },
11657 enter: function(inEvent) {
11658 inEvent.bubbles = false;
11659 this.fireEvent('pointerenter', inEvent);
11660 },
11661 leave: function(inEvent) {
11662 inEvent.bubbles = false;
11663 this.fireEvent('pointerleave', inEvent);
11664 },
11665 over: function(inEvent) {
11666 inEvent.bubbles = true;
11667 this.fireEvent('pointerover', inEvent);
11668 },
11669 out: function(inEvent) {
11670 inEvent.bubbles = true;
11671 this.fireEvent('pointerout', inEvent);
11672 },
11673 cancel: function(inEvent) {
11674 this.fireEvent('pointercancel', inEvent);
11675 },
11676 leaveOut: function(event) {
11677 this.out(event);
11678 if (!this.contains(event.target, event.relatedTarget)) {
11679 this.leave(event);
11680 }
11681 },
11682 enterOver: function(event) {
11683 this.over(event);
11684 if (!this.contains(event.target, event.relatedTarget)) {
11685 this.enter(event);
11686 }
11687 },
11688 // LISTENER LOGIC
11689 eventHandler: function(inEvent) {
11690 // This is used to prevent multiple dispatch of pointerevents from
11691 // platform events. This can happen when two elements in different scopes
11692 // are set up to create pointer events, which is relevant to Shadow DOM.
11693 if (this.handledEvents.get(inEvent)) {
11694 return;
11695 }
11696 var type = inEvent.type;
11697 var fn = this.eventMap && this.eventMap[type];
11698 if (fn) {
11699 fn(inEvent);
11700 }
11701 this.handledEvents.set(inEvent, true);
11702 },
11703 // set up event listeners
11704 listen: function(target, events) {
11705 events.forEach(function(e) {
11706 this.addEvent(target, e);
11707 }, this);
11708 },
11709 // remove event listeners
11710 unlisten: function(target, events) {
11711 events.forEach(function(e) {
11712 this.removeEvent(target, e);
11713 }, this);
11714 },
11715 addEvent: scope.external.addEvent || function(target, eventName) {
11716 target.addEventListener(eventName, this.boundHandler);
11717 },
11718 removeEvent: scope.external.removeEvent || function(target, eventName) {
11719 target.removeEventListener(eventName, this.boundHandler);
11720 },
11721 // EVENT CREATION AND TRACKING
11722 /**
11723 * Creates a new Event of type `inType`, based on the information in
11724 * `inEvent`.
11725 *
11726 * @param {string} inType A string representing the type of event to create
11727 * @param {Event} inEvent A platform event with a target
11728 * @return {Event} A PointerEvent of type `inType`
11729 */
11730 makeEvent: function(inType, inEvent) {
11731 // relatedTarget must be null if pointer is captured
11732 if (this.captureInfo) {
11733 inEvent.relatedTarget = null;
11734 }
11735 var e = new PointerEvent(inType, inEvent);
11736 if (inEvent.preventDefault) {
11737 e.preventDefault = inEvent.preventDefault;
11738 }
11739 this.targets.set(e, this.targets.get(inEvent) || inEvent.target);
11740 return e;
11741 },
11742 // make and dispatch an event in one call
11743 fireEvent: function(inType, inEvent) {
11744 var e = this.makeEvent(inType, inEvent);
11745 return this.dispatchEvent(e);
11746 },
11747 /**
11748 * Returns a snapshot of inEvent, with writable properties.
11749 *
11750 * @param {Event} inEvent An event that contains properties to copy.
11751 * @return {Object} An object containing shallow copies of `inEvent`'s
11752 * properties.
11753 */
11754 cloneEvent: function(inEvent) {
11755 var eventCopy = {}, p;
11756 for (var i = 0; i < CLONE_PROPS.length; i++) {
11757 p = CLONE_PROPS[i];
11758 eventCopy[p] = inEvent[p] || CLONE_DEFAULTS[i];
11759 // Work around SVGInstanceElement shadow tree
11760 // Return the <use> element that is represented by the instance for Safa ri, Chrome, IE.
11761 // This is the behavior implemented by Firefox.
11762 if (HAS_SVG_INSTANCE && (p === 'target' || p === 'relatedTarget')) {
11763 if (eventCopy[p] instanceof SVGElementInstance) {
11764 eventCopy[p] = eventCopy[p].correspondingUseElement;
11765 }
11766 }
11767 }
11768 // keep the semantics of preventDefault
11769 if (inEvent.preventDefault) {
11770 eventCopy.preventDefault = function() {
11771 inEvent.preventDefault();
11772 };
11773 }
11774 return eventCopy;
11775 },
11776 getTarget: function(inEvent) {
11777 // if pointer capture is set, route all events for the specified pointerId
11778 // to the capture target
11779 if (this.captureInfo) {
11780 if (this.captureInfo.id === inEvent.pointerId) {
11781 return this.captureInfo.target;
11782 }
11783 }
11784 return this.targets.get(inEvent);
11785 },
11786 setCapture: function(inPointerId, inTarget) {
11787 if (this.captureInfo) {
11788 this.releaseCapture(this.captureInfo.id);
11789 }
11790 this.captureInfo = {id: inPointerId, target: inTarget};
11791 var e = new PointerEvent('gotpointercapture', { bubbles: true });
11792 this.implicitRelease = this.releaseCapture.bind(this, inPointerId);
11793 document.addEventListener('pointerup', this.implicitRelease);
11794 document.addEventListener('pointercancel', this.implicitRelease);
11795 this.targets.set(e, inTarget);
11796 this.asyncDispatchEvent(e);
11797 },
11798 releaseCapture: function(inPointerId) {
11799 if (this.captureInfo && this.captureInfo.id === inPointerId) {
11800 var e = new PointerEvent('lostpointercapture', { bubbles: true });
11801 var t = this.captureInfo.target;
11802 this.captureInfo = null;
11803 document.removeEventListener('pointerup', this.implicitRelease);
11804 document.removeEventListener('pointercancel', this.implicitRelease);
11805 this.targets.set(e, t);
11806 this.asyncDispatchEvent(e);
11807 }
11808 },
11809 /**
11810 * Dispatches the event to its target.
11811 *
11812 * @param {Event} inEvent The event to be dispatched.
11813 * @return {Boolean} True if an event handler returns true, false otherwise.
11814 */
11815 dispatchEvent: scope.external.dispatchEvent || function(inEvent) {
11816 var t = this.getTarget(inEvent);
11817 if (t) {
11818 return t.dispatchEvent(inEvent);
11819 }
11820 },
11821 asyncDispatchEvent: function(inEvent) {
11822 setTimeout(this.dispatchEvent.bind(this, inEvent), 0);
11823 }
11824 };
11825 dispatcher.boundHandler = dispatcher.eventHandler.bind(dispatcher);
11826 scope.dispatcher = dispatcher;
11827 scope.register = dispatcher.register.bind(dispatcher);
11828 scope.unregister = dispatcher.unregister.bind(dispatcher);
11829 })(window.PointerEventsPolyfill);
11830
11831 /*
11832 * Copyright 2013 The Polymer Authors. All rights reserved.
11833 * Use of this source code is governed by a BSD-style
11834 * license that can be found in the LICENSE file.
11835 */
11836
11837 /**
11838 * This module uses Mutation Observers to dynamically adjust which nodes will
11839 * generate Pointer Events.
11840 *
11841 * All nodes that wish to generate Pointer Events must have the attribute
11842 * `touch-action` set to `none`.
11843 */
11844 (function(scope) {
11845 var forEach = Array.prototype.forEach.call.bind(Array.prototype.forEach);
11846 var map = Array.prototype.map.call.bind(Array.prototype.map);
11847 var toArray = Array.prototype.slice.call.bind(Array.prototype.slice);
11848 var filter = Array.prototype.filter.call.bind(Array.prototype.filter);
11849 var MO = window.MutationObserver || window.WebKitMutationObserver;
11850 var SELECTOR = '[touch-action]';
11851 var OBSERVER_INIT = {
11852 subtree: true,
11853 childList: true,
11854 attributes: true,
11855 attributeOldValue: true,
11856 attributeFilter: ['touch-action']
11857 };
11858
11859 function Installer(add, remove, changed, binder) {
11860 this.addCallback = add.bind(binder);
11861 this.removeCallback = remove.bind(binder);
11862 this.changedCallback = changed.bind(binder);
11863 if (MO) {
11864 this.observer = new MO(this.mutationWatcher.bind(this));
11865 }
11866 }
11867
11868 Installer.prototype = {
11869 watchSubtree: function(target) {
11870 // Only watch scopes that can target find, as these are top-level.
11871 // Otherwise we can see duplicate additions and removals that add noise.
11872 //
11873 // TODO(dfreedman): For some instances with ShadowDOMPolyfill, we can see
11874 // a removal without an insertion when a node is redistributed among
11875 // shadows. Since it all ends up correct in the document, watching only
11876 // the document will yield the correct mutations to watch.
11877 if (scope.targetFinding.canTarget(target)) {
11878 this.observer.observe(target, OBSERVER_INIT);
11879 }
11880 },
11881 enableOnSubtree: function(target) {
11882 this.watchSubtree(target);
11883 if (target === document && document.readyState !== 'complete') {
11884 this.installOnLoad();
11885 } else {
11886 this.installNewSubtree(target);
11887 }
11888 },
11889 installNewSubtree: function(target) {
11890 forEach(this.findElements(target), this.addElement, this);
11891 },
11892 findElements: function(target) {
11893 if (target.querySelectorAll) {
11894 return target.querySelectorAll(SELECTOR);
11895 }
11896 return [];
11897 },
11898 removeElement: function(el) {
11899 this.removeCallback(el);
11900 },
11901 addElement: function(el) {
11902 this.addCallback(el);
11903 },
11904 elementChanged: function(el, oldValue) {
11905 this.changedCallback(el, oldValue);
11906 },
11907 concatLists: function(accum, list) {
11908 return accum.concat(toArray(list));
11909 },
11910 // register all touch-action = none nodes on document load
11911 installOnLoad: function() {
11912 document.addEventListener('DOMContentLoaded', this.installNewSubtree.bind( this, document));
11913 },
11914 isElement: function(n) {
11915 return n.nodeType === Node.ELEMENT_NODE;
11916 },
11917 flattenMutationTree: function(inNodes) {
11918 // find children with touch-action
11919 var tree = map(inNodes, this.findElements, this);
11920 // make sure the added nodes are accounted for
11921 tree.push(filter(inNodes, this.isElement));
11922 // flatten the list
11923 return tree.reduce(this.concatLists, []);
11924 },
11925 mutationWatcher: function(mutations) {
11926 mutations.forEach(this.mutationHandler, this);
11927 },
11928 mutationHandler: function(m) {
11929 if (m.type === 'childList') {
11930 var added = this.flattenMutationTree(m.addedNodes);
11931 added.forEach(this.addElement, this);
11932 var removed = this.flattenMutationTree(m.removedNodes);
11933 removed.forEach(this.removeElement, this);
11934 } else if (m.type === 'attributes') {
11935 this.elementChanged(m.target, m.oldValue);
11936 }
11937 }
11938 };
11939
11940 if (!MO) {
11941 Installer.prototype.watchSubtree = function(){
11942 console.warn('PointerEventsPolyfill: MutationObservers not found, touch-ac tion will not be dynamically detected');
11943 };
11944 }
11945
11946 scope.Installer = Installer;
11947 })(window.PointerEventsPolyfill);
11948
11949 /*
11950 * Copyright 2013 The Polymer Authors. All rights reserved.
11951 * Use of this source code is governed by a BSD-style
11952 * license that can be found in the LICENSE file.
11953 */
11954
11955 (function (scope) {
11956 var dispatcher = scope.dispatcher;
11957 var pointermap = dispatcher.pointermap;
11958 // radius around touchend that swallows mouse events
11959 var DEDUP_DIST = 25;
11960
11961 // handler block for native mouse events
11962 var mouseEvents = {
11963 POINTER_ID: 1,
11964 POINTER_TYPE: 'mouse',
11965 events: [
11966 'mousedown',
11967 'mousemove',
11968 'mouseup',
11969 'mouseover',
11970 'mouseout'
11971 ],
11972 register: function(target) {
11973 dispatcher.listen(target, this.events);
11974 },
11975 unregister: function(target) {
11976 dispatcher.unlisten(target, this.events);
11977 },
11978 lastTouches: [],
11979 // collide with the global mouse listener
11980 isEventSimulatedFromTouch: function(inEvent) {
11981 var lts = this.lastTouches;
11982 var x = inEvent.clientX, y = inEvent.clientY;
11983 for (var i = 0, l = lts.length, t; i < l && (t = lts[i]); i++) {
11984 // simulated mouse events will be swallowed near a primary touchend
11985 var dx = Math.abs(x - t.x), dy = Math.abs(y - t.y);
11986 if (dx <= DEDUP_DIST && dy <= DEDUP_DIST) {
11987 return true;
11988 }
11989 }
11990 },
11991 prepareEvent: function(inEvent) {
11992 var e = dispatcher.cloneEvent(inEvent);
11993 // forward mouse preventDefault
11994 var pd = e.preventDefault;
11995 e.preventDefault = function() {
11996 inEvent.preventDefault();
11997 pd();
11998 };
11999 e.pointerId = this.POINTER_ID;
12000 e.isPrimary = true;
12001 e.pointerType = this.POINTER_TYPE;
12002 return e;
12003 },
12004 mousedown: function(inEvent) {
12005 if (!this.isEventSimulatedFromTouch(inEvent)) {
12006 var p = pointermap.has(this.POINTER_ID);
12007 // TODO(dfreedman) workaround for some elements not sending mouseup
12008 // http://crbug/149091
12009 if (p) {
12010 this.cancel(inEvent);
12011 }
12012 var e = this.prepareEvent(inEvent);
12013 pointermap.set(this.POINTER_ID, inEvent);
12014 dispatcher.down(e);
12015 }
12016 },
12017 mousemove: function(inEvent) {
12018 if (!this.isEventSimulatedFromTouch(inEvent)) {
12019 var e = this.prepareEvent(inEvent);
12020 dispatcher.move(e);
12021 }
12022 },
12023 mouseup: function(inEvent) {
12024 if (!this.isEventSimulatedFromTouch(inEvent)) {
12025 var p = pointermap.get(this.POINTER_ID);
12026 if (p && p.button === inEvent.button) {
12027 var e = this.prepareEvent(inEvent);
12028 dispatcher.up(e);
12029 this.cleanupMouse();
12030 }
12031 }
12032 },
12033 mouseover: function(inEvent) {
12034 if (!this.isEventSimulatedFromTouch(inEvent)) {
12035 var e = this.prepareEvent(inEvent);
12036 dispatcher.enterOver(e);
12037 }
12038 },
12039 mouseout: function(inEvent) {
12040 if (!this.isEventSimulatedFromTouch(inEvent)) {
12041 var e = this.prepareEvent(inEvent);
12042 dispatcher.leaveOut(e);
12043 }
12044 },
12045 cancel: function(inEvent) {
12046 var e = this.prepareEvent(inEvent);
12047 dispatcher.cancel(e);
12048 this.cleanupMouse();
12049 },
12050 cleanupMouse: function() {
12051 pointermap['delete'](this.POINTER_ID);
12052 }
12053 };
12054
12055 scope.mouseEvents = mouseEvents;
12056 })(window.PointerEventsPolyfill);
12057
12058 /*
12059 * Copyright 2013 The Polymer Authors. All rights reserved.
12060 * Use of this source code is governed by a BSD-style
12061 * license that can be found in the LICENSE file.
12062 */
12063
12064 (function(scope) {
12065 var dispatcher = scope.dispatcher;
12066 var findTarget = scope.findTarget;
12067 var allShadows = scope.targetFinding.allShadows.bind(scope.targetFinding);
12068 var pointermap = dispatcher.pointermap;
12069 var touchMap = Array.prototype.map.call.bind(Array.prototype.map);
12070 // This should be long enough to ignore compat mouse events made by touch
12071 var DEDUP_TIMEOUT = 2500;
12072 var CLICK_COUNT_TIMEOUT = 200;
12073 var ATTRIB = 'touch-action';
12074 var INSTALLER;
12075 // The presence of touch event handlers blocks scrolling, and so we must be ca reful to
12076 // avoid adding handlers unnecessarily. Chrome plans to add a touch-action-de lay property
12077 // (crbug.com/329559) to address this, and once we have that we can opt-in to a simpler
12078 // handler registration mechanism. Rather than try to predict how exactly to opt-in to
12079 // that we'll just leave this disabled until there is a build of Chrome to tes t.
12080 var HAS_TOUCH_ACTION_DELAY = false;
12081
12082 // handler block for native touch events
12083 var touchEvents = {
12084 scrollType: new WeakMap(),
12085 events: [
12086 'touchstart',
12087 'touchmove',
12088 'touchend',
12089 'touchcancel'
12090 ],
12091 register: function(target) {
12092 if (HAS_TOUCH_ACTION_DELAY) {
12093 dispatcher.listen(target, this.events);
12094 } else {
12095 INSTALLER.enableOnSubtree(target);
12096 }
12097 },
12098 unregister: function(target) {
12099 if (HAS_TOUCH_ACTION_DELAY) {
12100 dispatcher.unlisten(target, this.events);
12101 } else {
12102 // TODO(dfreedman): is it worth it to disconnect the MO?
12103 }
12104 },
12105 elementAdded: function(el) {
12106 var a = el.getAttribute(ATTRIB);
12107 var st = this.touchActionToScrollType(a);
12108 if (st) {
12109 this.scrollType.set(el, st);
12110 dispatcher.listen(el, this.events);
12111 // set touch-action on shadows as well
12112 allShadows(el).forEach(function(s) {
12113 this.scrollType.set(s, st);
12114 dispatcher.listen(s, this.events);
12115 }, this);
12116 }
12117 },
12118 elementRemoved: function(el) {
12119 this.scrollType['delete'](el);
12120 dispatcher.unlisten(el, this.events);
12121 // remove touch-action from shadow
12122 allShadows(el).forEach(function(s) {
12123 this.scrollType['delete'](s);
12124 dispatcher.unlisten(s, this.events);
12125 }, this);
12126 },
12127 elementChanged: function(el, oldValue) {
12128 var a = el.getAttribute(ATTRIB);
12129 var st = this.touchActionToScrollType(a);
12130 var oldSt = this.touchActionToScrollType(oldValue);
12131 // simply update scrollType if listeners are already established
12132 if (st && oldSt) {
12133 this.scrollType.set(el, st);
12134 allShadows(el).forEach(function(s) {
12135 this.scrollType.set(s, st);
12136 }, this);
12137 } else if (oldSt) {
12138 this.elementRemoved(el);
12139 } else if (st) {
12140 this.elementAdded(el);
12141 }
12142 },
12143 scrollTypes: {
12144 EMITTER: 'none',
12145 XSCROLLER: 'pan-x',
12146 YSCROLLER: 'pan-y',
12147 SCROLLER: /^(?:pan-x pan-y)|(?:pan-y pan-x)|auto$/
12148 },
12149 touchActionToScrollType: function(touchAction) {
12150 var t = touchAction;
12151 var st = this.scrollTypes;
12152 if (t === 'none') {
12153 return 'none';
12154 } else if (t === st.XSCROLLER) {
12155 return 'X';
12156 } else if (t === st.YSCROLLER) {
12157 return 'Y';
12158 } else if (st.SCROLLER.exec(t)) {
12159 return 'XY';
12160 }
12161 },
12162 POINTER_TYPE: 'touch',
12163 firstTouch: null,
12164 isPrimaryTouch: function(inTouch) {
12165 return this.firstTouch === inTouch.identifier;
12166 },
12167 setPrimaryTouch: function(inTouch) {
12168 // set primary touch if there no pointers, or the only pointer is the mous e
12169 if (pointermap.pointers() === 0 || (pointermap.pointers() === 1 && pointer map.has(1))) {
12170 this.firstTouch = inTouch.identifier;
12171 this.firstXY = {X: inTouch.clientX, Y: inTouch.clientY};
12172 this.scrolling = false;
12173 this.cancelResetClickCount();
12174 }
12175 },
12176 removePrimaryPointer: function(inPointer) {
12177 if (inPointer.isPrimary) {
12178 this.firstTouch = null;
12179 this.firstXY = null;
12180 this.resetClickCount();
12181 }
12182 },
12183 clickCount: 0,
12184 resetId: null,
12185 resetClickCount: function() {
12186 var fn = function() {
12187 this.clickCount = 0;
12188 this.resetId = null;
12189 }.bind(this);
12190 this.resetId = setTimeout(fn, CLICK_COUNT_TIMEOUT);
12191 },
12192 cancelResetClickCount: function() {
12193 if (this.resetId) {
12194 clearTimeout(this.resetId);
12195 }
12196 },
12197 touchToPointer: function(inTouch) {
12198 var e = dispatcher.cloneEvent(inTouch);
12199 // Spec specifies that pointerId 1 is reserved for Mouse.
12200 // Touch identifiers can start at 0.
12201 // Add 2 to the touch identifier for compatibility.
12202 e.pointerId = inTouch.identifier + 2;
12203 e.target = findTarget(e);
12204 e.bubbles = true;
12205 e.cancelable = true;
12206 e.detail = this.clickCount;
12207 e.button = 0;
12208 e.buttons = 1;
12209 e.width = inTouch.webkitRadiusX || inTouch.radiusX || 0;
12210 e.height = inTouch.webkitRadiusY || inTouch.radiusY || 0;
12211 e.pressure = inTouch.webkitForce || inTouch.force || 0.5;
12212 e.isPrimary = this.isPrimaryTouch(inTouch);
12213 e.pointerType = this.POINTER_TYPE;
12214 return e;
12215 },
12216 processTouches: function(inEvent, inFunction) {
12217 var tl = inEvent.changedTouches;
12218 var pointers = touchMap(tl, this.touchToPointer, this);
12219 // forward touch preventDefaults
12220 pointers.forEach(function(p) {
12221 p.preventDefault = function() {
12222 this.scrolling = false;
12223 this.firstXY = null;
12224 inEvent.preventDefault();
12225 };
12226 }, this);
12227 pointers.forEach(inFunction, this);
12228 },
12229 // For single axis scrollers, determines whether the element should emit
12230 // pointer events or behave as a scroller
12231 shouldScroll: function(inEvent) {
12232 if (this.firstXY) {
12233 var ret;
12234 var scrollAxis = this.scrollType.get(inEvent.currentTarget);
12235 if (scrollAxis === 'none') {
12236 // this element is a touch-action: none, should never scroll
12237 ret = false;
12238 } else if (scrollAxis === 'XY') {
12239 // this element should always scroll
12240 ret = true;
12241 } else {
12242 var t = inEvent.changedTouches[0];
12243 // check the intended scroll axis, and other axis
12244 var a = scrollAxis;
12245 var oa = scrollAxis === 'Y' ? 'X' : 'Y';
12246 var da = Math.abs(t['client' + a] - this.firstXY[a]);
12247 var doa = Math.abs(t['client' + oa] - this.firstXY[oa]);
12248 // if delta in the scroll axis > delta other axis, scroll instead of
12249 // making events
12250 ret = da >= doa;
12251 }
12252 this.firstXY = null;
12253 return ret;
12254 }
12255 },
12256 findTouch: function(inTL, inId) {
12257 for (var i = 0, l = inTL.length, t; i < l && (t = inTL[i]); i++) {
12258 if (t.identifier === inId) {
12259 return true;
12260 }
12261 }
12262 },
12263 // In some instances, a touchstart can happen without a touchend. This
12264 // leaves the pointermap in a broken state.
12265 // Therefore, on every touchstart, we remove the touches that did not fire a
12266 // touchend event.
12267 // To keep state globally consistent, we fire a
12268 // pointercancel for this "abandoned" touch
12269 vacuumTouches: function(inEvent) {
12270 var tl = inEvent.touches;
12271 // pointermap.pointers() should be < tl.length here, as the touchstart has not
12272 // been processed yet.
12273 if (pointermap.pointers() >= tl.length) {
12274 var d = [];
12275 pointermap.forEach(function(value, key) {
12276 // Never remove pointerId == 1, which is mouse.
12277 // Touch identifiers are 2 smaller than their pointerId, which is the
12278 // index in pointermap.
12279 if (key !== 1 && !this.findTouch(tl, key - 2)) {
12280 var p = value.out;
12281 d.push(this.touchToPointer(p));
12282 }
12283 }, this);
12284 d.forEach(this.cancelOut, this);
12285 }
12286 },
12287 touchstart: function(inEvent) {
12288 this.vacuumTouches(inEvent);
12289 this.setPrimaryTouch(inEvent.changedTouches[0]);
12290 this.dedupSynthMouse(inEvent);
12291 if (!this.scrolling) {
12292 this.clickCount++;
12293 this.processTouches(inEvent, this.overDown);
12294 }
12295 },
12296 overDown: function(inPointer) {
12297 var p = pointermap.set(inPointer.pointerId, {
12298 target: inPointer.target,
12299 out: inPointer,
12300 outTarget: inPointer.target
12301 });
12302 dispatcher.over(inPointer);
12303 dispatcher.enter(inPointer);
12304 dispatcher.down(inPointer);
12305 },
12306 touchmove: function(inEvent) {
12307 if (!this.scrolling) {
12308 if (this.shouldScroll(inEvent)) {
12309 this.scrolling = true;
12310 this.touchcancel(inEvent);
12311 } else {
12312 inEvent.preventDefault();
12313 this.processTouches(inEvent, this.moveOverOut);
12314 }
12315 }
12316 },
12317 moveOverOut: function(inPointer) {
12318 var event = inPointer;
12319 var pointer = pointermap.get(event.pointerId);
12320 // a finger drifted off the screen, ignore it
12321 if (!pointer) {
12322 return;
12323 }
12324 var outEvent = pointer.out;
12325 var outTarget = pointer.outTarget;
12326 dispatcher.move(event);
12327 if (outEvent && outTarget !== event.target) {
12328 outEvent.relatedTarget = event.target;
12329 event.relatedTarget = outTarget;
12330 // recover from retargeting by shadow
12331 outEvent.target = outTarget;
12332 if (event.target) {
12333 dispatcher.leaveOut(outEvent);
12334 dispatcher.enterOver(event);
12335 } else {
12336 // clean up case when finger leaves the screen
12337 event.target = outTarget;
12338 event.relatedTarget = null;
12339 this.cancelOut(event);
12340 }
12341 }
12342 pointer.out = event;
12343 pointer.outTarget = event.target;
12344 },
12345 touchend: function(inEvent) {
12346 this.dedupSynthMouse(inEvent);
12347 this.processTouches(inEvent, this.upOut);
12348 },
12349 upOut: function(inPointer) {
12350 if (!this.scrolling) {
12351 dispatcher.up(inPointer);
12352 dispatcher.out(inPointer);
12353 dispatcher.leave(inPointer);
12354 }
12355 this.cleanUpPointer(inPointer);
12356 },
12357 touchcancel: function(inEvent) {
12358 this.processTouches(inEvent, this.cancelOut);
12359 },
12360 cancelOut: function(inPointer) {
12361 dispatcher.cancel(inPointer);
12362 dispatcher.out(inPointer);
12363 dispatcher.leave(inPointer);
12364 this.cleanUpPointer(inPointer);
12365 },
12366 cleanUpPointer: function(inPointer) {
12367 pointermap['delete'](inPointer.pointerId);
12368 this.removePrimaryPointer(inPointer);
12369 },
12370 // prevent synth mouse events from creating pointer events
12371 dedupSynthMouse: function(inEvent) {
12372 var lts = scope.mouseEvents.lastTouches;
12373 var t = inEvent.changedTouches[0];
12374 // only the primary finger will synth mouse events
12375 if (this.isPrimaryTouch(t)) {
12376 // remember x/y of last touch
12377 var lt = {x: t.clientX, y: t.clientY};
12378 lts.push(lt);
12379 var fn = (function(lts, lt){
12380 var i = lts.indexOf(lt);
12381 if (i > -1) {
12382 lts.splice(i, 1);
12383 }
12384 }).bind(null, lts, lt);
12385 setTimeout(fn, DEDUP_TIMEOUT);
12386 }
12387 }
12388 };
12389
12390 if (!HAS_TOUCH_ACTION_DELAY) {
12391 INSTALLER = new scope.Installer(touchEvents.elementAdded, touchEvents.elemen tRemoved, touchEvents.elementChanged, touchEvents);
12392 }
12393
12394 scope.touchEvents = touchEvents;
12395 })(window.PointerEventsPolyfill);
12396
12397 /*
12398 * Copyright 2013 The Polymer Authors. All rights reserved.
12399 * Use of this source code is governed by a BSD-style
12400 * license that can be found in the LICENSE file.
12401 */
12402
12403 (function(scope) {
12404 var dispatcher = scope.dispatcher;
12405 var pointermap = dispatcher.pointermap;
12406 var HAS_BITMAP_TYPE = window.MSPointerEvent && typeof window.MSPointerEvent.MS POINTER_TYPE_MOUSE === 'number';
12407 var msEvents = {
12408 events: [
12409 'MSPointerDown',
12410 'MSPointerMove',
12411 'MSPointerUp',
12412 'MSPointerOut',
12413 'MSPointerOver',
12414 'MSPointerCancel',
12415 'MSGotPointerCapture',
12416 'MSLostPointerCapture'
12417 ],
12418 register: function(target) {
12419 dispatcher.listen(target, this.events);
12420 },
12421 unregister: function(target) {
12422 dispatcher.unlisten(target, this.events);
12423 },
12424 POINTER_TYPES: [
12425 '',
12426 'unavailable',
12427 'touch',
12428 'pen',
12429 'mouse'
12430 ],
12431 prepareEvent: function(inEvent) {
12432 var e = inEvent;
12433 if (HAS_BITMAP_TYPE) {
12434 e = dispatcher.cloneEvent(inEvent);
12435 e.pointerType = this.POINTER_TYPES[inEvent.pointerType];
12436 }
12437 return e;
12438 },
12439 cleanup: function(id) {
12440 pointermap['delete'](id);
12441 },
12442 MSPointerDown: function(inEvent) {
12443 pointermap.set(inEvent.pointerId, inEvent);
12444 var e = this.prepareEvent(inEvent);
12445 dispatcher.down(e);
12446 },
12447 MSPointerMove: function(inEvent) {
12448 var e = this.prepareEvent(inEvent);
12449 dispatcher.move(e);
12450 },
12451 MSPointerUp: function(inEvent) {
12452 var e = this.prepareEvent(inEvent);
12453 dispatcher.up(e);
12454 this.cleanup(inEvent.pointerId);
12455 },
12456 MSPointerOut: function(inEvent) {
12457 var e = this.prepareEvent(inEvent);
12458 dispatcher.leaveOut(e);
12459 },
12460 MSPointerOver: function(inEvent) {
12461 var e = this.prepareEvent(inEvent);
12462 dispatcher.enterOver(e);
12463 },
12464 MSPointerCancel: function(inEvent) {
12465 var e = this.prepareEvent(inEvent);
12466 dispatcher.cancel(e);
12467 this.cleanup(inEvent.pointerId);
12468 },
12469 MSLostPointerCapture: function(inEvent) {
12470 var e = dispatcher.makeEvent('lostpointercapture', inEvent);
12471 dispatcher.dispatchEvent(e);
12472 },
12473 MSGotPointerCapture: function(inEvent) {
12474 var e = dispatcher.makeEvent('gotpointercapture', inEvent);
12475 dispatcher.dispatchEvent(e);
12476 }
12477 };
12478
12479 scope.msEvents = msEvents;
12480 })(window.PointerEventsPolyfill);
12481
12482 /*
12483 * Copyright 2013 The Polymer Authors. All rights reserved.
12484 * Use of this source code is governed by a BSD-style
12485 * license that can be found in the LICENSE file.
12486 */
12487
12488 /**
12489 * This module contains the handlers for native platform events.
12490 * From here, the dispatcher is called to create unified pointer events.
12491 * Included are touch events (v1), mouse events, and MSPointerEvents.
12492 */
12493 (function(scope) {
12494 var dispatcher = scope.dispatcher;
12495
12496 // only activate if this platform does not have pointer events
12497 if (window.navigator.pointerEnabled === undefined) {
12498 Object.defineProperty(window.navigator, 'pointerEnabled', {value: true, enum erable: true});
12499
12500 if (window.navigator.msPointerEnabled) {
12501 var tp = window.navigator.msMaxTouchPoints;
12502 Object.defineProperty(window.navigator, 'maxTouchPoints', {
12503 value: tp,
12504 enumerable: true
12505 });
12506 dispatcher.registerSource('ms', scope.msEvents);
12507 } else {
12508 dispatcher.registerSource('mouse', scope.mouseEvents);
12509 if (window.ontouchstart !== undefined) {
12510 dispatcher.registerSource('touch', scope.touchEvents);
12511 }
12512 }
12513
12514 dispatcher.register(document);
12515 }
12516 })(window.PointerEventsPolyfill);
12517
12518 /*
12519 * Copyright 2013 The Polymer Authors. All rights reserved.
12520 * Use of this source code is governed by a BSD-style
12521 * license that can be found in the LICENSE file.
12522 */
12523
12524 (function(scope) {
12525 var dispatcher = scope.dispatcher;
12526 var n = window.navigator;
12527 var s, r;
12528 function assertDown(id) {
12529 if (!dispatcher.pointermap.has(id)) {
12530 throw new Error('InvalidPointerId');
12531 }
12532 }
12533 if (n.msPointerEnabled) {
12534 s = function(pointerId) {
12535 assertDown(pointerId);
12536 this.msSetPointerCapture(pointerId);
12537 };
12538 r = function(pointerId) {
12539 assertDown(pointerId);
12540 this.msReleasePointerCapture(pointerId);
12541 };
12542 } else {
12543 s = function setPointerCapture(pointerId) {
12544 assertDown(pointerId);
12545 dispatcher.setCapture(pointerId, this);
12546 };
12547 r = function releasePointerCapture(pointerId) {
12548 assertDown(pointerId);
12549 dispatcher.releaseCapture(pointerId, this);
12550 };
12551 }
12552 if (window.Element && !Element.prototype.setPointerCapture) {
12553 Object.defineProperties(Element.prototype, {
12554 'setPointerCapture': {
12555 value: s
12556 },
12557 'releasePointerCapture': {
12558 value: r
12559 }
12560 });
12561 }
12562 })(window.PointerEventsPolyfill);
12563
12564 /*
12565 * Copyright 2013 The Polymer Authors. All rights reserved.
12566 * Use of this source code is governed by a BSD-style
12567 * license that can be found in the LICENSE file.
12568 */
12569
12570 /**
12571 * PointerGestureEvent is the constructor for all PointerGesture events.
12572 *
12573 * @module PointerGestures
12574 * @class PointerGestureEvent
12575 * @extends UIEvent
12576 * @constructor
12577 * @param {String} inType Event type
12578 * @param {Object} [inDict] Dictionary of properties to initialize on the event
12579 */
12580
12581 function PointerGestureEvent(inType, inDict) {
12582 var dict = inDict || {};
12583 var e = document.createEvent('Event');
12584 var props = {
12585 bubbles: Boolean(dict.bubbles) === dict.bubbles || true,
12586 cancelable: Boolean(dict.cancelable) === dict.cancelable || true
12587 };
12588
12589 e.initEvent(inType, props.bubbles, props.cancelable);
12590
12591 var keys = Object.keys(dict), k;
12592 for (var i = 0; i < keys.length; i++) {
12593 k = keys[i];
12594 e[k] = dict[k];
12595 }
12596
12597 e.preventTap = this.preventTap;
12598
12599 return e;
12600 }
12601
12602 /**
12603 * Allows for any gesture to prevent the tap gesture.
12604 *
12605 * @method preventTap
12606 */
12607 PointerGestureEvent.prototype.preventTap = function() {
12608 this.tapPrevented = true;
12609 };
12610
12611
12612 /*
12613 * Copyright 2013 The Polymer Authors. All rights reserved.
12614 * Use of this source code is governed by a BSD-style
12615 * license that can be found in the LICENSE file.
12616 */
12617
12618 (function(scope) {
12619 /**
12620 * This class contains the gesture recognizers that create the PointerGesture
12621 * events.
12622 *
12623 * @class PointerGestures
12624 * @static
12625 */
12626 scope = scope || {};
12627 scope.utils = {
12628 LCA: {
12629 // Determines the lowest node in the ancestor chain of a and b
12630 find: function(a, b) {
12631 if (a === b) {
12632 return a;
12633 }
12634 // fast case, a is a direct descendant of b or vice versa
12635 if (a.contains) {
12636 if (a.contains(b)) {
12637 return a;
12638 }
12639 if (b.contains(a)) {
12640 return b;
12641 }
12642 }
12643 var adepth = this.depth(a);
12644 var bdepth = this.depth(b);
12645 var d = adepth - bdepth;
12646 if (d > 0) {
12647 a = this.walk(a, d);
12648 } else {
12649 b = this.walk(b, -d);
12650 }
12651 while(a && b && a !== b) {
12652 a = this.walk(a, 1);
12653 b = this.walk(b, 1);
12654 }
12655 return a;
12656 },
12657 walk: function(n, u) {
12658 for (var i = 0; i < u; i++) {
12659 n = n.parentNode;
12660 }
12661 return n;
12662 },
12663 depth: function(n) {
12664 var d = 0;
12665 while(n) {
12666 d++;
12667 n = n.parentNode;
12668 }
12669 return d;
12670 }
12671 }
12672 };
12673 scope.findLCA = function(a, b) {
12674 return scope.utils.LCA.find(a, b);
12675 }
12676 window.PointerGestures = scope;
12677 })(window.PointerGestures);
12678
12679 /*
12680 * Copyright 2013 The Polymer Authors. All rights reserved.
12681 * Use of this source code is governed by a BSD-style
12682 * license that can be found in the LICENSE file.
12683 */
12684
12685 /**
12686 * This module implements an map of pointer states
12687 */
12688 (function(scope) {
12689 var USE_MAP = window.Map && window.Map.prototype.forEach;
12690 var POINTERS_FN = function(){ return this.size; };
12691 function PointerMap() {
12692 if (USE_MAP) {
12693 var m = new Map();
12694 m.pointers = POINTERS_FN;
12695 return m;
12696 } else {
12697 this.keys = [];
12698 this.values = [];
12699 }
12700 }
12701
12702 PointerMap.prototype = {
12703 set: function(inId, inEvent) {
12704 var i = this.keys.indexOf(inId);
12705 if (i > -1) {
12706 this.values[i] = inEvent;
12707 } else {
12708 this.keys.push(inId);
12709 this.values.push(inEvent);
12710 }
12711 },
12712 has: function(inId) {
12713 return this.keys.indexOf(inId) > -1;
12714 },
12715 'delete': function(inId) {
12716 var i = this.keys.indexOf(inId);
12717 if (i > -1) {
12718 this.keys.splice(i, 1);
12719 this.values.splice(i, 1);
12720 }
12721 },
12722 get: function(inId) {
12723 var i = this.keys.indexOf(inId);
12724 return this.values[i];
12725 },
12726 clear: function() {
12727 this.keys.length = 0;
12728 this.values.length = 0;
12729 },
12730 // return value, key, map
12731 forEach: function(callback, thisArg) {
12732 this.values.forEach(function(v, i) {
12733 callback.call(thisArg, v, this.keys[i], this);
12734 }, this);
12735 },
12736 pointers: function() {
12737 return this.keys.length;
12738 }
12739 };
12740
12741 scope.PointerMap = PointerMap;
12742 })(window.PointerGestures);
12743
12744 /*
12745 * Copyright 2013 The Polymer Authors. All rights reserved.
12746 * Use of this source code is governed by a BSD-style
12747 * license that can be found in the LICENSE file.
12748 */
12749
12750 (function(scope) {
12751 var CLONE_PROPS = [
12752 // MouseEvent
12753 'bubbles',
12754 'cancelable',
12755 'view',
12756 'detail',
12757 'screenX',
12758 'screenY',
12759 'clientX',
12760 'clientY',
12761 'ctrlKey',
12762 'altKey',
12763 'shiftKey',
12764 'metaKey',
12765 'button',
12766 'relatedTarget',
12767 // DOM Level 3
12768 'buttons',
12769 // PointerEvent
12770 'pointerId',
12771 'width',
12772 'height',
12773 'pressure',
12774 'tiltX',
12775 'tiltY',
12776 'pointerType',
12777 'hwTimestamp',
12778 'isPrimary',
12779 // event instance
12780 'type',
12781 'target',
12782 'currentTarget',
12783 'screenX',
12784 'screenY',
12785 'pageX',
12786 'pageY',
12787 'tapPrevented'
12788 ];
12789
12790 var CLONE_DEFAULTS = [
12791 // MouseEvent
12792 false,
12793 false,
12794 null,
12795 null,
12796 0,
12797 0,
12798 0,
12799 0,
12800 false,
12801 false,
12802 false,
12803 false,
12804 0,
12805 null,
12806 // DOM Level 3
12807 0,
12808 // PointerEvent
12809 0,
12810 0,
12811 0,
12812 0,
12813 0,
12814 0,
12815 '',
12816 0,
12817 false,
12818 // event instance
12819 '',
12820 null,
12821 null,
12822 0,
12823 0,
12824 0,
12825 0
12826 ];
12827
12828 var dispatcher = {
12829 handledEvents: new WeakMap(),
12830 targets: new WeakMap(),
12831 handlers: {},
12832 recognizers: {},
12833 events: {},
12834 // Add a new gesture recognizer to the event listeners.
12835 // Recognizer needs an `events` property.
12836 registerRecognizer: function(inName, inRecognizer) {
12837 var r = inRecognizer;
12838 this.recognizers[inName] = r;
12839 r.events.forEach(function(e) {
12840 if (r[e]) {
12841 this.events[e] = true;
12842 var f = r[e].bind(r);
12843 this.addHandler(e, f);
12844 }
12845 }, this);
12846 },
12847 addHandler: function(inEvent, inFn) {
12848 var e = inEvent;
12849 if (!this.handlers[e]) {
12850 this.handlers[e] = [];
12851 }
12852 this.handlers[e].push(inFn);
12853 },
12854 // add event listeners for inTarget
12855 registerTarget: function(inTarget) {
12856 this.listen(Object.keys(this.events), inTarget);
12857 },
12858 // remove event listeners for inTarget
12859 unregisterTarget: function(inTarget) {
12860 this.unlisten(Object.keys(this.events), inTarget);
12861 },
12862 // LISTENER LOGIC
12863 eventHandler: function(inEvent) {
12864 if (this.handledEvents.get(inEvent)) {
12865 return;
12866 }
12867 var type = inEvent.type, fns = this.handlers[type];
12868 if (fns) {
12869 this.makeQueue(fns, inEvent);
12870 }
12871 this.handledEvents.set(inEvent, true);
12872 },
12873 // queue event for async dispatch
12874 makeQueue: function(inHandlerFns, inEvent) {
12875 // must clone events to keep the (possibly shadowed) target correct for
12876 // async dispatching
12877 var e = this.cloneEvent(inEvent);
12878 setTimeout(this.runQueue.bind(this, inHandlerFns, e), 0);
12879 },
12880 // Dispatch the queued events
12881 runQueue: function(inHandlers, inEvent) {
12882 this.currentPointerId = inEvent.pointerId;
12883 for (var i = 0, f, l = inHandlers.length; (i < l) && (f = inHandlers[i]); i++) {
12884 f(inEvent);
12885 }
12886 this.currentPointerId = 0;
12887 },
12888 // set up event listeners
12889 listen: function(inEvents, inTarget) {
12890 inEvents.forEach(function(e) {
12891 this.addEvent(e, this.boundHandler, false, inTarget);
12892 }, this);
12893 },
12894 // remove event listeners
12895 unlisten: function(inEvents) {
12896 inEvents.forEach(function(e) {
12897 this.removeEvent(e, this.boundHandler, false, inTarget);
12898 }, this);
12899 },
12900 addEvent: function(inEventName, inEventHandler, inCapture, inTarget) {
12901 inTarget.addEventListener(inEventName, inEventHandler, inCapture);
12902 },
12903 removeEvent: function(inEventName, inEventHandler, inCapture, inTarget) {
12904 inTarget.removeEventListener(inEventName, inEventHandler, inCapture);
12905 },
12906 // EVENT CREATION AND TRACKING
12907 // Creates a new Event of type `inType`, based on the information in
12908 // `inEvent`.
12909 makeEvent: function(inType, inDict) {
12910 return new PointerGestureEvent(inType, inDict);
12911 },
12912 /*
12913 * Returns a snapshot of inEvent, with writable properties.
12914 *
12915 * @method cloneEvent
12916 * @param {Event} inEvent An event that contains properties to copy.
12917 * @return {Object} An object containing shallow copies of `inEvent`'s
12918 * properties.
12919 */
12920 cloneEvent: function(inEvent) {
12921 var eventCopy = {}, p;
12922 for (var i = 0; i < CLONE_PROPS.length; i++) {
12923 p = CLONE_PROPS[i];
12924 eventCopy[p] = inEvent[p] || CLONE_DEFAULTS[i];
12925 }
12926 return eventCopy;
12927 },
12928 // Dispatches the event to its target.
12929 dispatchEvent: function(inEvent, inTarget) {
12930 var t = inTarget || this.targets.get(inEvent);
12931 if (t) {
12932 t.dispatchEvent(inEvent);
12933 if (inEvent.tapPrevented) {
12934 this.preventTap(this.currentPointerId);
12935 }
12936 }
12937 },
12938 asyncDispatchEvent: function(inEvent, inTarget) {
12939 var fn = function() {
12940 this.dispatchEvent(inEvent, inTarget);
12941 }.bind(this);
12942 setTimeout(fn, 0);
12943 },
12944 preventTap: function(inPointerId) {
12945 var t = this.recognizers.tap;
12946 if (t){
12947 t.preventTap(inPointerId);
12948 }
12949 }
12950 };
12951 dispatcher.boundHandler = dispatcher.eventHandler.bind(dispatcher);
12952 scope.dispatcher = dispatcher;
12953 var registerQueue = [];
12954 var immediateRegister = false;
12955 /**
12956 * Enable gesture events for a given scope, typically
12957 * [ShadowRoots](https://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/shadow /index.html#shadow-root-object).
12958 *
12959 * @for PointerGestures
12960 * @method register
12961 * @param {ShadowRoot} scope A top level scope to enable gesture
12962 * support on.
12963 */
12964 scope.register = function(inScope) {
12965 if (immediateRegister) {
12966 var pe = window.PointerEventsPolyfill;
12967 if (pe) {
12968 pe.register(inScope);
12969 }
12970 scope.dispatcher.registerTarget(inScope);
12971 } else {
12972 registerQueue.push(inScope);
12973 }
12974 };
12975 // wait to register scopes until recognizers load
12976 document.addEventListener('DOMContentLoaded', function() {
12977 immediateRegister = true;
12978 registerQueue.push(document);
12979 registerQueue.forEach(scope.register);
12980 });
12981 })(window.PointerGestures);
12982
12983 /*
12984 * Copyright 2013 The Polymer Authors. All rights reserved.
12985 * Use of this source code is governed by a BSD-style
12986 * license that can be found in the LICENSE file.
12987 */
12988
12989 /**
12990 * This event is fired when a pointer is held down for 200ms.
12991 *
12992 * @module PointerGestures
12993 * @submodule Events
12994 * @class hold
12995 */
12996 /**
12997 * Milliseconds pointer has been held down.
12998 * @type Number
12999 * @property holdTime
13000 */
13001 /**
13002 * Type of pointer that made the holding event.
13003 * @type String
13004 * @property pointerType
13005 */
13006 /**
13007 * This event is fired every 200ms while a pointer is held down.
13008 *
13009 * @class holdpulse
13010 * @extends hold
13011 */
13012 /**
13013 * This event is fired when a held pointer is released or moved.
13014 *
13015 * @class released
13016 */
13017 /**
13018 * Type of pointer that made the holding event.
13019 * @type String
13020 * @property pointerType
13021 */
13022
13023 (function(scope) {
13024 var dispatcher = scope.dispatcher;
13025 var hold = {
13026 // wait at least HOLD_DELAY ms between hold and pulse events
13027 HOLD_DELAY: 200,
13028 // pointer can move WIGGLE_THRESHOLD pixels before not counting as a hold
13029 WIGGLE_THRESHOLD: 16,
13030 events: [
13031 'pointerdown',
13032 'pointermove',
13033 'pointerup',
13034 'pointercancel'
13035 ],
13036 heldPointer: null,
13037 holdJob: null,
13038 pulse: function() {
13039 var hold = Date.now() - this.heldPointer.timeStamp;
13040 var type = this.held ? 'holdpulse' : 'hold';
13041 this.fireHold(type, hold);
13042 this.held = true;
13043 },
13044 cancel: function() {
13045 clearInterval(this.holdJob);
13046 if (this.held) {
13047 this.fireHold('release');
13048 }
13049 this.held = false;
13050 this.heldPointer = null;
13051 this.target = null;
13052 this.holdJob = null;
13053 },
13054 pointerdown: function(inEvent) {
13055 if (inEvent.isPrimary && !this.heldPointer) {
13056 this.heldPointer = inEvent;
13057 this.target = inEvent.target;
13058 this.holdJob = setInterval(this.pulse.bind(this), this.HOLD_DELAY);
13059 }
13060 },
13061 pointerup: function(inEvent) {
13062 if (this.heldPointer && this.heldPointer.pointerId === inEvent.pointerId) {
13063 this.cancel();
13064 }
13065 },
13066 pointercancel: function(inEvent) {
13067 this.cancel();
13068 },
13069 pointermove: function(inEvent) {
13070 if (this.heldPointer && this.heldPointer.pointerId === inEvent.pointerId) {
13071 var x = inEvent.clientX - this.heldPointer.clientX;
13072 var y = inEvent.clientY - this.heldPointer.clientY;
13073 if ((x * x + y * y) > this.WIGGLE_THRESHOLD) {
13074 this.cancel();
13075 }
13076 }
13077 },
13078 fireHold: function(inType, inHoldTime) {
13079 var p = {
13080 pointerType: this.heldPointer.pointerType
13081 };
13082 if (inHoldTime) {
13083 p.holdTime = inHoldTime;
13084 }
13085 var e = dispatcher.makeEvent(inType, p);
13086 dispatcher.dispatchEvent(e, this.target);
13087 if (e.tapPrevented) {
13088 dispatcher.preventTap(this.heldPointer.pointerId);
13089 }
13090 }
13091 };
13092 dispatcher.registerRecognizer('hold', hold);
13093 })(window.PointerGestures);
13094
13095 /*
13096 * Copyright 2013 The Polymer Authors. All rights reserved.
13097 * Use of this source code is governed by a BSD-style
13098 * license that can be found in the LICENSE file.
13099 */
13100
13101 /**
13102 * This event denotes the beginning of a series of tracking events.
13103 *
13104 * @module PointerGestures
13105 * @submodule Events
13106 * @class trackstart
13107 */
13108 /**
13109 * Pixels moved in the x direction since trackstart.
13110 * @type Number
13111 * @property dx
13112 */
13113 /**
13114 * Pixes moved in the y direction since trackstart.
13115 * @type Number
13116 * @property dy
13117 */
13118 /**
13119 * Pixels moved in the x direction since the last track.
13120 * @type Number
13121 * @property ddx
13122 */
13123 /**
13124 * Pixles moved in the y direction since the last track.
13125 * @type Number
13126 * @property ddy
13127 */
13128 /**
13129 * The clientX position of the track gesture.
13130 * @type Number
13131 * @property clientX
13132 */
13133 /**
13134 * The clientY position of the track gesture.
13135 * @type Number
13136 * @property clientY
13137 */
13138 /**
13139 * The pageX position of the track gesture.
13140 * @type Number
13141 * @property pageX
13142 */
13143 /**
13144 * The pageY position of the track gesture.
13145 * @type Number
13146 * @property pageY
13147 */
13148 /**
13149 * The screenX position of the track gesture.
13150 * @type Number
13151 * @property screenX
13152 */
13153 /**
13154 * The screenY position of the track gesture.
13155 * @type Number
13156 * @property screenY
13157 */
13158 /**
13159 * The last x axis direction of the pointer.
13160 * @type Number
13161 * @property xDirection
13162 */
13163 /**
13164 * The last y axis direction of the pointer.
13165 * @type Number
13166 * @property yDirection
13167 */
13168 /**
13169 * A shared object between all tracking events.
13170 * @type Object
13171 * @property trackInfo
13172 */
13173 /**
13174 * The element currently under the pointer.
13175 * @type Element
13176 * @property relatedTarget
13177 */
13178 /**
13179 * The type of pointer that make the track gesture.
13180 * @type String
13181 * @property pointerType
13182 */
13183 /**
13184 *
13185 * This event fires for all pointer movement being tracked.
13186 *
13187 * @class track
13188 * @extends trackstart
13189 */
13190 /**
13191 * This event fires when the pointer is no longer being tracked.
13192 *
13193 * @class trackend
13194 * @extends trackstart
13195 */
13196
13197 (function(scope) {
13198 var dispatcher = scope.dispatcher;
13199 var pointermap = new scope.PointerMap();
13200 var track = {
13201 events: [
13202 'pointerdown',
13203 'pointermove',
13204 'pointerup',
13205 'pointercancel'
13206 ],
13207 WIGGLE_THRESHOLD: 4,
13208 clampDir: function(inDelta) {
13209 return inDelta > 0 ? 1 : -1;
13210 },
13211 calcPositionDelta: function(inA, inB) {
13212 var x = 0, y = 0;
13213 if (inA && inB) {
13214 x = inB.pageX - inA.pageX;
13215 y = inB.pageY - inA.pageY;
13216 }
13217 return {x: x, y: y};
13218 },
13219 fireTrack: function(inType, inEvent, inTrackingData) {
13220 var t = inTrackingData;
13221 var d = this.calcPositionDelta(t.downEvent, inEvent);
13222 var dd = this.calcPositionDelta(t.lastMoveEvent, inEvent);
13223 if (dd.x) {
13224 t.xDirection = this.clampDir(dd.x);
13225 }
13226 if (dd.y) {
13227 t.yDirection = this.clampDir(dd.y);
13228 }
13229 var trackData = {
13230 dx: d.x,
13231 dy: d.y,
13232 ddx: dd.x,
13233 ddy: dd.y,
13234 clientX: inEvent.clientX,
13235 clientY: inEvent.clientY,
13236 pageX: inEvent.pageX,
13237 pageY: inEvent.pageY,
13238 screenX: inEvent.screenX,
13239 screenY: inEvent.screenY,
13240 xDirection: t.xDirection,
13241 yDirection: t.yDirection,
13242 trackInfo: t.trackInfo,
13243 relatedTarget: inEvent.target,
13244 pointerType: inEvent.pointerType
13245 };
13246 var e = dispatcher.makeEvent(inType, trackData);
13247 t.lastMoveEvent = inEvent;
13248 dispatcher.dispatchEvent(e, t.downTarget);
13249 },
13250 pointerdown: function(inEvent) {
13251 if (inEvent.isPrimary && (inEvent.pointerType === 'mouse' ? inEvent.butto ns === 1 : true)) {
13252 var p = {
13253 downEvent: inEvent,
13254 downTarget: inEvent.target,
13255 trackInfo: {},
13256 lastMoveEvent: null,
13257 xDirection: 0,
13258 yDirection: 0,
13259 tracking: false
13260 };
13261 pointermap.set(inEvent.pointerId, p);
13262 }
13263 },
13264 pointermove: function(inEvent) {
13265 var p = pointermap.get(inEvent.pointerId);
13266 if (p) {
13267 if (!p.tracking) {
13268 var d = this.calcPositionDelta(p.downEvent, inEvent);
13269 var move = d.x * d.x + d.y * d.y;
13270 // start tracking only if finger moves more than WIGGLE_THRESHOLD
13271 if (move > this.WIGGLE_THRESHOLD) {
13272 p.tracking = true;
13273 this.fireTrack('trackstart', p.downEvent, p);
13274 this.fireTrack('track', inEvent, p);
13275 }
13276 } else {
13277 this.fireTrack('track', inEvent, p);
13278 }
13279 }
13280 },
13281 pointerup: function(inEvent) {
13282 var p = pointermap.get(inEvent.pointerId);
13283 if (p) {
13284 if (p.tracking) {
13285 this.fireTrack('trackend', inEvent, p);
13286 }
13287 pointermap.delete(inEvent.pointerId);
13288 }
13289 },
13290 pointercancel: function(inEvent) {
13291 this.pointerup(inEvent);
13292 }
13293 };
13294 dispatcher.registerRecognizer('track', track);
13295 })(window.PointerGestures);
13296
13297 /*
13298 * Copyright 2013 The Polymer Authors. All rights reserved.
13299 * Use of this source code is governed by a BSD-style
13300 * license that can be found in the LICENSE file.
13301 */
13302
13303 /**
13304 * This event denotes a rapid down/move/up sequence from a pointer.
13305 *
13306 * The event is sent to the first element the pointer went down on.
13307 *
13308 * @module PointerGestures
13309 * @submodule Events
13310 * @class flick
13311 */
13312 /**
13313 * Signed velocity of the flick in the x direction.
13314 * @property xVelocity
13315 * @type Number
13316 */
13317 /**
13318 * Signed velocity of the flick in the y direction.
13319 * @type Number
13320 * @property yVelocity
13321 */
13322 /**
13323 * Unsigned total velocity of the flick.
13324 * @type Number
13325 * @property velocity
13326 */
13327 /**
13328 * Angle of the flick in degrees, with 0 along the
13329 * positive x axis.
13330 * @type Number
13331 * @property angle
13332 */
13333 /**
13334 * Axis with the greatest absolute velocity. Denoted
13335 * with 'x' or 'y'.
13336 * @type String
13337 * @property majorAxis
13338 */
13339 /**
13340 * Type of the pointer that made the flick.
13341 * @type String
13342 * @property pointerType
13343 */
13344
13345 (function(scope) {
13346 var dispatcher = scope.dispatcher;
13347 var flick = {
13348 // TODO(dfreedman): value should be low enough for low speed flicks, but
13349 // high enough to remove accidental flicks
13350 MIN_VELOCITY: 0.5 /* px/ms */,
13351 MAX_QUEUE: 4,
13352 moveQueue: [],
13353 target: null,
13354 pointerId: null,
13355 events: [
13356 'pointerdown',
13357 'pointermove',
13358 'pointerup',
13359 'pointercancel'
13360 ],
13361 pointerdown: function(inEvent) {
13362 if (inEvent.isPrimary && !this.pointerId) {
13363 this.pointerId = inEvent.pointerId;
13364 this.target = inEvent.target;
13365 this.addMove(inEvent);
13366 }
13367 },
13368 pointermove: function(inEvent) {
13369 if (inEvent.pointerId === this.pointerId) {
13370 this.addMove(inEvent);
13371 }
13372 },
13373 pointerup: function(inEvent) {
13374 if (inEvent.pointerId === this.pointerId) {
13375 this.fireFlick(inEvent);
13376 }
13377 this.cleanup();
13378 },
13379 pointercancel: function(inEvent) {
13380 this.cleanup();
13381 },
13382 cleanup: function() {
13383 this.moveQueue = [];
13384 this.target = null;
13385 this.pointerId = null;
13386 },
13387 addMove: function(inEvent) {
13388 if (this.moveQueue.length >= this.MAX_QUEUE) {
13389 this.moveQueue.shift();
13390 }
13391 this.moveQueue.push(inEvent);
13392 },
13393 fireFlick: function(inEvent) {
13394 var e = inEvent;
13395 var l = this.moveQueue.length;
13396 var dt, dx, dy, tx, ty, tv, x = 0, y = 0, v = 0;
13397 // flick based off the fastest segment of movement
13398 for (var i = 0, m; i < l && (m = this.moveQueue[i]); i++) {
13399 dt = e.timeStamp - m.timeStamp;
13400 dx = e.clientX - m.clientX, dy = e.clientY - m.clientY;
13401 tx = dx / dt, ty = dy / dt, tv = Math.sqrt(tx * tx + ty * ty);
13402 if (tv > v) {
13403 x = tx, y = ty, v = tv;
13404 }
13405 }
13406 var ma = Math.abs(x) > Math.abs(y) ? 'x' : 'y';
13407 var a = this.calcAngle(x, y);
13408 if (Math.abs(v) >= this.MIN_VELOCITY) {
13409 var ev = dispatcher.makeEvent('flick', {
13410 xVelocity: x,
13411 yVelocity: y,
13412 velocity: v,
13413 angle: a,
13414 majorAxis: ma,
13415 pointerType: inEvent.pointerType
13416 });
13417 dispatcher.dispatchEvent(ev, this.target);
13418 }
13419 },
13420 calcAngle: function(inX, inY) {
13421 return (Math.atan2(inY, inX) * 180 / Math.PI);
13422 }
13423 };
13424 dispatcher.registerRecognizer('flick', flick);
13425 })(window.PointerGestures);
13426
13427 /*
13428 * Copyright 2013 The Polymer Authors. All rights reserved.
13429 * Use of this source code is governed by a BSD-style
13430 * license that can be found in the LICENSE file.
13431 */
13432
13433 /*
13434 * Basic strategy: find the farthest apart points, use as diameter of circle
13435 * react to size change and rotation of the chord
13436 */
13437
13438 /**
13439 * @module PointerGestures
13440 * @submodule Events
13441 * @class pinch
13442 */
13443 /**
13444 * Scale of the pinch zoom gesture
13445 * @property scale
13446 * @type Number
13447 */
13448 /**
13449 * Center X position of pointers causing pinch
13450 * @property centerX
13451 * @type Number
13452 */
13453 /**
13454 * Center Y position of pointers causing pinch
13455 * @property centerY
13456 * @type Number
13457 */
13458
13459 /**
13460 * @module PointerGestures
13461 * @submodule Events
13462 * @class rotate
13463 */
13464 /**
13465 * Angle (in degrees) of rotation. Measured from starting positions of pointers.
13466 * @property angle
13467 * @type Number
13468 */
13469 /**
13470 * Center X position of pointers causing rotation
13471 * @property centerX
13472 * @type Number
13473 */
13474 /**
13475 * Center Y position of pointers causing rotation
13476 * @property centerY
13477 * @type Number
13478 */
13479 (function(scope) {
13480 var dispatcher = scope.dispatcher;
13481 var pointermap = new scope.PointerMap();
13482 var RAD_TO_DEG = 180 / Math.PI;
13483 var pinch = {
13484 events: [
13485 'pointerdown',
13486 'pointermove',
13487 'pointerup',
13488 'pointercancel'
13489 ],
13490 reference: {},
13491 pointerdown: function(ev) {
13492 pointermap.set(ev.pointerId, ev);
13493 if (pointermap.pointers() == 2) {
13494 var points = this.calcChord();
13495 var angle = this.calcAngle(points);
13496 this.reference = {
13497 angle: angle,
13498 diameter: points.diameter,
13499 target: scope.findLCA(points.a.target, points.b.target)
13500 };
13501 }
13502 },
13503 pointerup: function(ev) {
13504 pointermap.delete(ev.pointerId);
13505 },
13506 pointermove: function(ev) {
13507 if (pointermap.has(ev.pointerId)) {
13508 pointermap.set(ev.pointerId, ev);
13509 if (pointermap.pointers() > 1) {
13510 this.calcPinchRotate();
13511 }
13512 }
13513 },
13514 pointercancel: function(ev) {
13515 this.pointerup(ev);
13516 },
13517 dispatchPinch: function(diameter, points) {
13518 var zoom = diameter / this.reference.diameter;
13519 var ev = dispatcher.makeEvent('pinch', {
13520 scale: zoom,
13521 centerX: points.center.x,
13522 centerY: points.center.y
13523 });
13524 dispatcher.dispatchEvent(ev, this.reference.target);
13525 },
13526 dispatchRotate: function(angle, points) {
13527 var diff = Math.round((angle - this.reference.angle) % 360);
13528 var ev = dispatcher.makeEvent('rotate', {
13529 angle: diff,
13530 centerX: points.center.x,
13531 centerY: points.center.y
13532 });
13533 dispatcher.dispatchEvent(ev, this.reference.target);
13534 },
13535 calcPinchRotate: function() {
13536 var points = this.calcChord();
13537 var diameter = points.diameter;
13538 var angle = this.calcAngle(points);
13539 if (diameter != this.reference.diameter) {
13540 this.dispatchPinch(diameter, points);
13541 }
13542 if (angle != this.reference.angle) {
13543 this.dispatchRotate(angle, points);
13544 }
13545 },
13546 calcChord: function() {
13547 var pointers = [];
13548 pointermap.forEach(function(p) {
13549 pointers.push(p);
13550 });
13551 var dist = 0;
13552 var points = {};
13553 var x, y, d;
13554 for (var i = 0; i < pointers.length; i++) {
13555 var a = pointers[i];
13556 for (var j = i + 1; j < pointers.length; j++) {
13557 var b = pointers[j];
13558 x = Math.abs(a.clientX - b.clientX);
13559 y = Math.abs(a.clientY - b.clientY);
13560 d = x + y;
13561 if (d > dist) {
13562 dist = d;
13563 points = {a: a, b: b};
13564 }
13565 }
13566 }
13567 x = Math.abs(points.a.clientX + points.b.clientX) / 2;
13568 y = Math.abs(points.a.clientY + points.b.clientY) / 2;
13569 points.center = { x: x, y: y };
13570 points.diameter = dist;
13571 return points;
13572 },
13573 calcAngle: function(points) {
13574 var x = points.a.clientX - points.b.clientX;
13575 var y = points.a.clientY - points.b.clientY;
13576 return (360 + Math.atan2(y, x) * RAD_TO_DEG) % 360;
13577 },
13578 };
13579 dispatcher.registerRecognizer('pinch', pinch);
13580 })(window.PointerGestures);
13581
13582 /*
13583 * Copyright 2013 The Polymer Authors. All rights reserved.
13584 * Use of this source code is governed by a BSD-style
13585 * license that can be found in the LICENSE file.
13586 */
13587
13588 /**
13589 * This event is fired when a pointer quickly goes down and up, and is used to
13590 * denote activation.
13591 *
13592 * Any gesture event can prevent the tap event from being created by calling
13593 * `event.preventTap`.
13594 *
13595 * Any pointer event can prevent the tap by setting the `tapPrevented` property
13596 * on itself.
13597 *
13598 * @module PointerGestures
13599 * @submodule Events
13600 * @class tap
13601 */
13602 /**
13603 * X axis position of the tap.
13604 * @property x
13605 * @type Number
13606 */
13607 /**
13608 * Y axis position of the tap.
13609 * @property y
13610 * @type Number
13611 */
13612 /**
13613 * Type of the pointer that made the tap.
13614 * @property pointerType
13615 * @type String
13616 */
13617 (function(scope) {
13618 var dispatcher = scope.dispatcher;
13619 var pointermap = new scope.PointerMap();
13620 var tap = {
13621 events: [
13622 'pointerdown',
13623 'pointermove',
13624 'pointerup',
13625 'pointercancel',
13626 'keyup'
13627 ],
13628 pointerdown: function(inEvent) {
13629 if (inEvent.isPrimary && !inEvent.tapPrevented) {
13630 pointermap.set(inEvent.pointerId, {
13631 target: inEvent.target,
13632 x: inEvent.clientX,
13633 y: inEvent.clientY
13634 });
13635 }
13636 },
13637 pointermove: function(inEvent) {
13638 if (inEvent.isPrimary) {
13639 var start = pointermap.get(inEvent.pointerId);
13640 if (start) {
13641 if (inEvent.tapPrevented) {
13642 pointermap.delete(inEvent.pointerId);
13643 }
13644 }
13645 }
13646 },
13647 pointerup: function(inEvent) {
13648 var start = pointermap.get(inEvent.pointerId);
13649 if (start && !inEvent.tapPrevented) {
13650 var t = scope.findLCA(start.target, inEvent.target);
13651 if (t) {
13652 var e = dispatcher.makeEvent('tap', {
13653 x: inEvent.clientX,
13654 y: inEvent.clientY,
13655 detail: inEvent.detail,
13656 pointerType: inEvent.pointerType
13657 });
13658 dispatcher.dispatchEvent(e, t);
13659 }
13660 }
13661 pointermap.delete(inEvent.pointerId);
13662 },
13663 pointercancel: function(inEvent) {
13664 pointermap.delete(inEvent.pointerId);
13665 },
13666 keyup: function(inEvent) {
13667 var code = inEvent.keyCode;
13668 // 32 == spacebar
13669 if (code === 32) {
13670 var t = inEvent.target;
13671 if (!(t instanceof HTMLInputElement || t instanceof HTMLTextAreaElement) ) {
13672 dispatcher.dispatchEvent(dispatcher.makeEvent('tap', {
13673 x: 0,
13674 y: 0,
13675 detail: 0,
13676 pointerType: 'unavailable'
13677 }), t);
13678 }
13679 }
13680 },
13681 preventTap: function(inPointerId) {
13682 pointermap.delete(inPointerId);
13683 }
13684 };
13685 dispatcher.registerRecognizer('tap', tap);
13686 })(window.PointerGestures);
13687
13688 // Copyright 2011 Google Inc.
13689 //
13690 // Licensed under the Apache License, Version 2.0 (the "License");
13691 // you may not use this file except in compliance with the License.
13692 // You may obtain a copy of the License at
13693 //
13694 // http://www.apache.org/licenses/LICENSE-2.0
13695 //
13696 // Unless required by applicable law or agreed to in writing, software
13697 // distributed under the License is distributed on an "AS IS" BASIS,
13698 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13699 // See the License for the specific language governing permissions and
13700 // limitations under the License.
13701
13702 (function(global) {
13703 'use strict';
13704
13705 var filter = Array.prototype.filter.call.bind(Array.prototype.filter);
13706
13707 function getTreeScope(node) {
13708 while (node.parentNode) {
13709 node = node.parentNode;
13710 }
13711
13712 return typeof node.getElementById === 'function' ? node : null;
13713 }
13714
13715 // JScript does not have __proto__. We wrap all object literals with
13716 // createObject which uses Object.create, Object.defineProperty and
13717 // Object.getOwnPropertyDescriptor to create a new object that does the exact
13718 // same thing. The main downside to this solution is that we have to extract
13719 // all those property descriptors for IE.
13720 var createObject = ('__proto__' in {}) ?
13721 function(obj) { return obj; } :
13722 function(obj) {
13723 var proto = obj.__proto__;
13724 if (!proto)
13725 return obj;
13726 var newObject = Object.create(proto);
13727 Object.getOwnPropertyNames(obj).forEach(function(name) {
13728 Object.defineProperty(newObject, name,
13729 Object.getOwnPropertyDescriptor(obj, name));
13730 });
13731 return newObject;
13732 };
13733
13734 // IE does not support have Document.prototype.contains.
13735 if (typeof document.contains != 'function') {
13736 Document.prototype.contains = function(node) {
13737 if (node === this || node.parentNode === this)
13738 return true;
13739 return this.documentElement.contains(node);
13740 }
13741 }
13742
13743 Node.prototype.bind = function(name, observable) {
13744 console.error('Unhandled binding to Node: ', this, name, observable);
13745 };
13746
13747 function unbind(node, name) {
13748 var bindings = node.bindings;
13749 if (!bindings) {
13750 node.bindings = {};
13751 return;
13752 }
13753
13754 var binding = bindings[name];
13755 if (!binding)
13756 return;
13757
13758 binding.close();
13759 bindings[name] = undefined;
13760 }
13761
13762 Node.prototype.unbind = function(name) {
13763 unbind(this, name);
13764 };
13765
13766 Node.prototype.unbindAll = function() {
13767 if (!this.bindings)
13768 return;
13769 var names = Object.keys(this.bindings);
13770 for (var i = 0; i < names.length; i++) {
13771 var binding = this.bindings[names[i]];
13772 if (binding)
13773 binding.close();
13774 }
13775
13776 this.bindings = {};
13777 };
13778
13779 function sanitizeValue(value) {
13780 return value == null ? '' : value;
13781 }
13782
13783 function updateText(node, value) {
13784 node.data = sanitizeValue(value);
13785 }
13786
13787 function textBinding(node) {
13788 return function(value) {
13789 return updateText(node, value);
13790 };
13791 }
13792
13793 Text.prototype.bind = function(name, value, oneTime) {
13794 if (name !== 'textContent')
13795 return Node.prototype.bind.call(this, name, value, oneTime);
13796
13797 if (oneTime)
13798 return updateText(this, value);
13799
13800 unbind(this, 'textContent');
13801 updateText(this, value.open(textBinding(this)));
13802 return this.bindings.textContent = value;
13803 }
13804
13805 function updateAttribute(el, name, conditional, value) {
13806 if (conditional) {
13807 if (value)
13808 el.setAttribute(name, '');
13809 else
13810 el.removeAttribute(name);
13811 return;
13812 }
13813
13814 el.setAttribute(name, sanitizeValue(value));
13815 }
13816
13817 function attributeBinding(el, name, conditional) {
13818 return function(value) {
13819 updateAttribute(el, name, conditional, value);
13820 };
13821 }
13822
13823 Element.prototype.bind = function(name, value, oneTime) {
13824 var conditional = name[name.length - 1] == '?';
13825 if (conditional) {
13826 this.removeAttribute(name);
13827 name = name.slice(0, -1);
13828 }
13829
13830 if (oneTime)
13831 return updateAttribute(this, name, conditional, value);
13832
13833 unbind(this, name);
13834 updateAttribute(this, name, conditional,
13835 value.open(attributeBinding(this, name, conditional)));
13836
13837 return this.bindings[name] = value;
13838 };
13839
13840 var checkboxEventType;
13841 (function() {
13842 // Attempt to feature-detect which event (change or click) is fired first
13843 // for checkboxes.
13844 var div = document.createElement('div');
13845 var checkbox = div.appendChild(document.createElement('input'));
13846 checkbox.setAttribute('type', 'checkbox');
13847 var first;
13848 var count = 0;
13849 checkbox.addEventListener('click', function(e) {
13850 count++;
13851 first = first || 'click';
13852 });
13853 checkbox.addEventListener('change', function() {
13854 count++;
13855 first = first || 'change';
13856 });
13857
13858 var event = document.createEvent('MouseEvent');
13859 event.initMouseEvent("click", true, true, window, 0, 0, 0, 0, 0, false,
13860 false, false, false, 0, null);
13861 checkbox.dispatchEvent(event);
13862 // WebKit/Blink don't fire the change event if the element is outside the
13863 // document, so assume 'change' for that case.
13864 checkboxEventType = count == 1 ? 'change' : first;
13865 })();
13866
13867 function getEventForInputType(element) {
13868 switch (element.type) {
13869 case 'checkbox':
13870 return checkboxEventType;
13871 case 'radio':
13872 case 'select-multiple':
13873 case 'select-one':
13874 return 'change';
13875 default:
13876 return 'input';
13877 }
13878 }
13879
13880 function updateInput(input, property, value, santizeFn) {
13881 input[property] = (santizeFn || sanitizeValue)(value);
13882 }
13883
13884 function inputBinding(input, property, santizeFn) {
13885 return function(value) {
13886 return updateInput(input, property, value, santizeFn);
13887 }
13888 }
13889
13890 function noop() {}
13891
13892 function bindInputEvent(input, property, observable, postEventFn) {
13893 var eventType = getEventForInputType(input);
13894
13895 function eventHandler() {
13896 observable.setValue(input[property]);
13897 observable.discardChanges();
13898 (postEventFn || noop)(input);
13899 Platform.performMicrotaskCheckpoint();
13900 }
13901 input.addEventListener(eventType, eventHandler);
13902
13903 var capturedClose = observable.close;
13904 observable.close = function() {
13905 if (!capturedClose)
13906 return;
13907 input.removeEventListener(eventType, eventHandler);
13908
13909 observable.close = capturedClose;
13910 observable.close();
13911 capturedClose = undefined;
13912 }
13913 }
13914
13915 function booleanSanitize(value) {
13916 return Boolean(value);
13917 }
13918
13919 // |element| is assumed to be an HTMLInputElement with |type| == 'radio'.
13920 // Returns an array containing all radio buttons other than |element| that
13921 // have the same |name|, either in the form that |element| belongs to or,
13922 // if no form, in the document tree to which |element| belongs.
13923 //
13924 // This implementation is based upon the HTML spec definition of a
13925 // "radio button group":
13926 // http://www.whatwg.org/specs/web-apps/current-work/multipage/number-state. html#radio-button-group
13927 //
13928 function getAssociatedRadioButtons(element) {
13929 if (element.form) {
13930 return filter(element.form.elements, function(el) {
13931 return el != element &&
13932 el.tagName == 'INPUT' &&
13933 el.type == 'radio' &&
13934 el.name == element.name;
13935 });
13936 } else {
13937 var treeScope = getTreeScope(element);
13938 if (!treeScope)
13939 return [];
13940 var radios = treeScope.querySelectorAll(
13941 'input[type="radio"][name="' + element.name + '"]');
13942 return filter(radios, function(el) {
13943 return el != element && !el.form;
13944 });
13945 }
13946 }
13947
13948 function checkedPostEvent(input) {
13949 // Only the radio button that is getting checked gets an event. We
13950 // therefore find all the associated radio buttons and update their
13951 // check binding manually.
13952 if (input.tagName === 'INPUT' &&
13953 input.type === 'radio') {
13954 getAssociatedRadioButtons(input).forEach(function(radio) {
13955 var checkedBinding = radio.bindings.checked;
13956 if (checkedBinding) {
13957 // Set the value directly to avoid an infinite call stack.
13958 checkedBinding.setValue(false);
13959 }
13960 });
13961 }
13962 }
13963
13964 HTMLInputElement.prototype.bind = function(name, value, oneTime) {
13965 if (name !== 'value' && name !== 'checked')
13966 return HTMLElement.prototype.bind.call(this, name, value, oneTime);
13967
13968
13969 this.removeAttribute(name);
13970 var sanitizeFn = name == 'checked' ? booleanSanitize : sanitizeValue;
13971 var postEventFn = name == 'checked' ? checkedPostEvent : noop;
13972
13973 if (oneTime)
13974 return updateInput(this, name, value, sanitizeFn);
13975
13976 unbind(this, name);
13977 bindInputEvent(this, name, value, postEventFn);
13978 updateInput(this, name,
13979 value.open(inputBinding(this, name, sanitizeFn)),
13980 sanitizeFn);
13981
13982 return this.bindings[name] = value;
13983 }
13984
13985 HTMLTextAreaElement.prototype.bind = function(name, value, oneTime) {
13986 if (name !== 'value')
13987 return HTMLElement.prototype.bind.call(this, name, value, oneTime);
13988
13989 this.removeAttribute('value');
13990
13991 if (oneTime)
13992 return updateInput(this, 'value', value);
13993
13994 unbind(this, 'value');
13995 bindInputEvent(this, 'value', value);
13996 updateInput(this, 'value',
13997 value.open(inputBinding(this, 'value', sanitizeValue)));
13998
13999 return this.bindings.value = value;
14000 }
14001
14002 function updateOption(option, value) {
14003 var parentNode = option.parentNode;;
14004 var select;
14005 var selectBinding;
14006 var oldValue;
14007 if (parentNode instanceof HTMLSelectElement &&
14008 parentNode.bindings &&
14009 parentNode.bindings.value) {
14010 select = parentNode;
14011 selectBinding = select.bindings.value;
14012 oldValue = select.value;
14013 }
14014
14015 option.value = sanitizeValue(value);
14016
14017 if (select && select.value != oldValue) {
14018 selectBinding.setValue(select.value);
14019 selectBinding.discardChanges();
14020 Platform.performMicrotaskCheckpoint();
14021 }
14022 }
14023
14024 function optionBinding(option) {
14025 return function(value) {
14026 updateOption(option, value);
14027 }
14028 }
14029
14030 HTMLOptionElement.prototype.bind = function(name, value, oneTime) {
14031 if (name !== 'value')
14032 return HTMLElement.prototype.bind.call(this, name, value, oneTime);
14033
14034 this.removeAttribute('value');
14035
14036 if (oneTime)
14037 return updateOption(this, value);
14038
14039 unbind(this, 'value');
14040 bindInputEvent(this, 'value', value);
14041 updateOption(this, value.open(optionBinding(this)));
14042 return this.bindings.value = value;
14043 }
14044
14045 HTMLSelectElement.prototype.bind = function(name, value, oneTime) {
14046 if (name === 'selectedindex')
14047 name = 'selectedIndex';
14048
14049 if (name !== 'selectedIndex' && name !== 'value')
14050 return HTMLElement.prototype.bind.call(this, name, value, oneTime);
14051
14052 this.removeAttribute(name);
14053
14054 if (oneTime)
14055 return updateInput(this, name, value);
14056
14057 unbind(this, name);
14058 bindInputEvent(this, name, value);
14059 updateInput(this, name,
14060 value.open(inputBinding(this, name)));
14061 return this.bindings[name] = value;
14062 }
14063 })(this);
14064
14065 // Copyright 2011 Google Inc.
14066 //
14067 // Licensed under the Apache License, Version 2.0 (the "License");
14068 // you may not use this file except in compliance with the License.
14069 // You may obtain a copy of the License at
14070 //
14071 // http://www.apache.org/licenses/LICENSE-2.0
14072 //
14073 // Unless required by applicable law or agreed to in writing, software
14074 // distributed under the License is distributed on an "AS IS" BASIS,
14075 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14076 // See the License for the specific language governing permissions and
14077 // limitations under the License.
14078
14079 (function(global) {
14080 'use strict';
14081
14082 function assert(v) {
14083 if (!v)
14084 throw new Error('Assertion failed');
14085 }
14086
14087 var forEach = Array.prototype.forEach.call.bind(Array.prototype.forEach);
14088
14089 function getFragmentRoot(node) {
14090 var p;
14091 while (p = node.parentNode) {
14092 node = p;
14093 }
14094
14095 return node;
14096 }
14097
14098 function searchRefId(node, id) {
14099 if (!id)
14100 return;
14101
14102 var ref;
14103 var selector = '#' + id;
14104 while (!ref) {
14105 node = getFragmentRoot(node);
14106
14107 if (node.protoContent_)
14108 ref = node.protoContent_.querySelector(selector);
14109 else if (node.getElementById)
14110 ref = node.getElementById(id);
14111
14112 if (ref || !node.templateCreator_)
14113 break
14114
14115 node = node.templateCreator_;
14116 }
14117
14118 return ref;
14119 }
14120
14121 function getInstanceRoot(node) {
14122 while (node.parentNode) {
14123 node = node.parentNode;
14124 }
14125 return node.templateCreator_ ? node : null;
14126 }
14127
14128 var Map;
14129 if (global.Map && typeof global.Map.prototype.forEach === 'function') {
14130 Map = global.Map;
14131 } else {
14132 Map = function() {
14133 this.keys = [];
14134 this.values = [];
14135 };
14136
14137 Map.prototype = {
14138 set: function(key, value) {
14139 var index = this.keys.indexOf(key);
14140 if (index < 0) {
14141 this.keys.push(key);
14142 this.values.push(value);
14143 } else {
14144 this.values[index] = value;
14145 }
14146 },
14147
14148 get: function(key) {
14149 var index = this.keys.indexOf(key);
14150 if (index < 0)
14151 return;
14152
14153 return this.values[index];
14154 },
14155
14156 delete: function(key, value) {
14157 var index = this.keys.indexOf(key);
14158 if (index < 0)
14159 return false;
14160
14161 this.keys.splice(index, 1);
14162 this.values.splice(index, 1);
14163 return true;
14164 },
14165
14166 forEach: function(f, opt_this) {
14167 for (var i = 0; i < this.keys.length; i++)
14168 f.call(opt_this || this, this.values[i], this.keys[i], this);
14169 }
14170 };
14171 }
14172
14173 // JScript does not have __proto__. We wrap all object literals with
14174 // createObject which uses Object.create, Object.defineProperty and
14175 // Object.getOwnPropertyDescriptor to create a new object that does the exact
14176 // same thing. The main downside to this solution is that we have to extract
14177 // all those property descriptors for IE.
14178 var createObject = ('__proto__' in {}) ?
14179 function(obj) { return obj; } :
14180 function(obj) {
14181 var proto = obj.__proto__;
14182 if (!proto)
14183 return obj;
14184 var newObject = Object.create(proto);
14185 Object.getOwnPropertyNames(obj).forEach(function(name) {
14186 Object.defineProperty(newObject, name,
14187 Object.getOwnPropertyDescriptor(obj, name));
14188 });
14189 return newObject;
14190 };
14191
14192 // IE does not support have Document.prototype.contains.
14193 if (typeof document.contains != 'function') {
14194 Document.prototype.contains = function(node) {
14195 if (node === this || node.parentNode === this)
14196 return true;
14197 return this.documentElement.contains(node);
14198 }
14199 }
14200
14201 var BIND = 'bind';
14202 var REPEAT = 'repeat';
14203 var IF = 'if';
14204
14205 var templateAttributeDirectives = {
14206 'template': true,
14207 'repeat': true,
14208 'bind': true,
14209 'ref': true
14210 };
14211
14212 var semanticTemplateElements = {
14213 'THEAD': true,
14214 'TBODY': true,
14215 'TFOOT': true,
14216 'TH': true,
14217 'TR': true,
14218 'TD': true,
14219 'COLGROUP': true,
14220 'COL': true,
14221 'CAPTION': true,
14222 'OPTION': true,
14223 'OPTGROUP': true
14224 };
14225
14226 var hasTemplateElement = typeof HTMLTemplateElement !== 'undefined';
14227
14228 var allTemplatesSelectors = 'template, ' +
14229 Object.keys(semanticTemplateElements).map(function(tagName) {
14230 return tagName.toLowerCase() + '[template]';
14231 }).join(', ');
14232
14233 function isSVGTemplate(el) {
14234 return el.tagName == 'template' &&
14235 el.namespaceURI == 'http://www.w3.org/2000/svg';
14236 }
14237
14238 function isHTMLTemplate(el) {
14239 return el.tagName == 'TEMPLATE' &&
14240 el.namespaceURI == 'http://www.w3.org/1999/xhtml';
14241 }
14242
14243 function isAttributeTemplate(el) {
14244 return Boolean(semanticTemplateElements[el.tagName] &&
14245 el.hasAttribute('template'));
14246 }
14247
14248 function isTemplate(el) {
14249 if (el.isTemplate_ === undefined)
14250 el.isTemplate_ = el.tagName == 'TEMPLATE' || isAttributeTemplate(el);
14251
14252 return el.isTemplate_;
14253 }
14254
14255 // FIXME: Observe templates being added/removed from documents
14256 // FIXME: Expose imperative API to decorate and observe templates in
14257 // "disconnected tress" (e.g. ShadowRoot)
14258 document.addEventListener('DOMContentLoaded', function(e) {
14259 bootstrapTemplatesRecursivelyFrom(document);
14260 // FIXME: Is this needed? Seems like it shouldn't be.
14261 Platform.performMicrotaskCheckpoint();
14262 }, false);
14263
14264 function forAllTemplatesFrom(node, fn) {
14265 var subTemplates = node.querySelectorAll(allTemplatesSelectors);
14266
14267 if (isTemplate(node))
14268 fn(node)
14269 forEach(subTemplates, fn);
14270 }
14271
14272 function bootstrapTemplatesRecursivelyFrom(node) {
14273 function bootstrap(template) {
14274 if (!HTMLTemplateElement.decorate(template))
14275 bootstrapTemplatesRecursivelyFrom(template.content);
14276 }
14277
14278 forAllTemplatesFrom(node, bootstrap);
14279 }
14280
14281 if (!hasTemplateElement) {
14282 /**
14283 * This represents a <template> element.
14284 * @constructor
14285 * @extends {HTMLElement}
14286 */
14287 global.HTMLTemplateElement = function() {
14288 throw TypeError('Illegal constructor');
14289 };
14290 }
14291
14292 var hasProto = '__proto__' in {};
14293
14294 function mixin(to, from) {
14295 Object.getOwnPropertyNames(from).forEach(function(name) {
14296 Object.defineProperty(to, name,
14297 Object.getOwnPropertyDescriptor(from, name));
14298 });
14299 }
14300
14301 // http://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/templates/index.html# dfn-template-contents-owner
14302 function getOrCreateTemplateContentsOwner(template) {
14303 var doc = template.ownerDocument
14304 if (!doc.defaultView)
14305 return doc;
14306 var d = doc.templateContentsOwner_;
14307 if (!d) {
14308 // TODO(arv): This should either be a Document or HTMLDocument depending
14309 // on doc.
14310 d = doc.implementation.createHTMLDocument('');
14311 while (d.lastChild) {
14312 d.removeChild(d.lastChild);
14313 }
14314 doc.templateContentsOwner_ = d;
14315 }
14316 return d;
14317 }
14318
14319 function getTemplateStagingDocument(template) {
14320 if (!template.stagingDocument_) {
14321 var owner = template.ownerDocument;
14322 if (!owner.stagingDocument_) {
14323 owner.stagingDocument_ = owner.implementation.createHTMLDocument('');
14324 owner.stagingDocument_.stagingDocument_ = owner.stagingDocument_;
14325 }
14326
14327 template.stagingDocument_ = owner.stagingDocument_;
14328 }
14329
14330 return template.stagingDocument_;
14331 }
14332
14333 // For non-template browsers, the parser will disallow <template> in certain
14334 // locations, so we allow "attribute templates" which combine the template
14335 // element with the top-level container node of the content, e.g.
14336 //
14337 // <tr template repeat="{{ foo }}"" class="bar"><td>Bar</td></tr>
14338 //
14339 // becomes
14340 //
14341 // <template repeat="{{ foo }}">
14342 // + #document-fragment
14343 // + <tr class="bar">
14344 // + <td>Bar</td>
14345 //
14346 function extractTemplateFromAttributeTemplate(el) {
14347 var template = el.ownerDocument.createElement('template');
14348 el.parentNode.insertBefore(template, el);
14349
14350 var attribs = el.attributes;
14351 var count = attribs.length;
14352 while (count-- > 0) {
14353 var attrib = attribs[count];
14354 if (templateAttributeDirectives[attrib.name]) {
14355 if (attrib.name !== 'template')
14356 template.setAttribute(attrib.name, attrib.value);
14357 el.removeAttribute(attrib.name);
14358 }
14359 }
14360
14361 return template;
14362 }
14363
14364 function extractTemplateFromSVGTemplate(el) {
14365 var template = el.ownerDocument.createElement('template');
14366 el.parentNode.insertBefore(template, el);
14367
14368 var attribs = el.attributes;
14369 var count = attribs.length;
14370 while (count-- > 0) {
14371 var attrib = attribs[count];
14372 template.setAttribute(attrib.name, attrib.value);
14373 el.removeAttribute(attrib.name);
14374 }
14375
14376 el.parentNode.removeChild(el);
14377 return template;
14378 }
14379
14380 function liftNonNativeTemplateChildrenIntoContent(template, el, useRoot) {
14381 var content = template.content;
14382 if (useRoot) {
14383 content.appendChild(el);
14384 return;
14385 }
14386
14387 var child;
14388 while (child = el.firstChild) {
14389 content.appendChild(child);
14390 }
14391 }
14392
14393 /**
14394 * Ensures proper API and content model for template elements.
14395 * @param {HTMLTemplateElement} opt_instanceRef The template element which
14396 * |el| template element will return as the value of its ref(), and whose
14397 * content will be used as source when createInstance() is invoked.
14398 */
14399 HTMLTemplateElement.decorate = function(el, opt_instanceRef) {
14400 if (el.templateIsDecorated_)
14401 return false;
14402
14403 var templateElement = el;
14404 templateElement.templateIsDecorated_ = true;
14405
14406 var isNativeHTMLTemplate = isHTMLTemplate(templateElement) &&
14407 hasTemplateElement;
14408 var bootstrapContents = isNativeHTMLTemplate;
14409 var liftContents = !isNativeHTMLTemplate;
14410 var liftRoot = false;
14411
14412 if (!isNativeHTMLTemplate) {
14413 if (isAttributeTemplate(templateElement)) {
14414 assert(!opt_instanceRef);
14415 templateElement = extractTemplateFromAttributeTemplate(el);
14416 templateElement.templateIsDecorated_ = true;
14417 isNativeHTMLTemplate = hasTemplateElement;
14418 liftRoot = true;
14419 } else if (isSVGTemplate(templateElement)) {
14420 templateElement = extractTemplateFromSVGTemplate(el);
14421 templateElement.templateIsDecorated_ = true;
14422 isNativeHTMLTemplate = hasTemplateElement;
14423 }
14424 }
14425
14426 if (!isNativeHTMLTemplate) {
14427 fixTemplateElementPrototype(templateElement);
14428 var doc = getOrCreateTemplateContentsOwner(templateElement);
14429 templateElement.content_ = doc.createDocumentFragment();
14430 }
14431
14432 if (opt_instanceRef) {
14433 // template is contained within an instance, its direct content must be
14434 // empty
14435 templateElement.instanceRef_ = opt_instanceRef;
14436 } else if (liftContents) {
14437 liftNonNativeTemplateChildrenIntoContent(templateElement,
14438 el,
14439 liftRoot);
14440 } else if (bootstrapContents) {
14441 bootstrapTemplatesRecursivelyFrom(templateElement.content);
14442 }
14443
14444 return true;
14445 };
14446
14447 // TODO(rafaelw): This used to decorate recursively all templates from a given
14448 // node. This happens by default on 'DOMContentLoaded', but may be needed
14449 // in subtrees not descendent from document (e.g. ShadowRoot).
14450 // Review whether this is the right public API.
14451 HTMLTemplateElement.bootstrap = bootstrapTemplatesRecursivelyFrom;
14452
14453 var htmlElement = global.HTMLUnknownElement || HTMLElement;
14454
14455 var contentDescriptor = {
14456 get: function() {
14457 return this.content_;
14458 },
14459 enumerable: true,
14460 configurable: true
14461 };
14462
14463 if (!hasTemplateElement) {
14464 // Gecko is more picky with the prototype than WebKit. Make sure to use the
14465 // same prototype as created in the constructor.
14466 HTMLTemplateElement.prototype = Object.create(htmlElement.prototype);
14467
14468 Object.defineProperty(HTMLTemplateElement.prototype, 'content',
14469 contentDescriptor);
14470 }
14471
14472 function fixTemplateElementPrototype(el) {
14473 if (hasProto)
14474 el.__proto__ = HTMLTemplateElement.prototype;
14475 else
14476 mixin(el, HTMLTemplateElement.prototype);
14477 }
14478
14479 function ensureSetModelScheduled(template) {
14480 if (!template.setModelFn_) {
14481 template.setModelFn_ = function() {
14482 template.setModelFnScheduled_ = false;
14483 var map = getBindings(template,
14484 template.delegate_ && template.delegate_.prepareBinding);
14485 processBindings(template, map, template.model_);
14486 };
14487 }
14488
14489 if (!template.setModelFnScheduled_) {
14490 template.setModelFnScheduled_ = true;
14491 Observer.runEOM_(template.setModelFn_);
14492 }
14493 }
14494
14495 mixin(HTMLTemplateElement.prototype, {
14496 processBindingDirectives_: function(directives) {
14497 if (this.iterator_)
14498 this.iterator_.closeDeps();
14499
14500 if (!directives.if && !directives.bind && !directives.repeat) {
14501 if (this.iterator_) {
14502 this.iterator_.close();
14503 this.iterator_ = undefined;
14504 this.bindings.iterator = undefined;
14505 }
14506
14507 return;
14508 }
14509
14510 if (!this.iterator_) {
14511 this.iterator_ = new TemplateIterator(this);
14512 this.bindings = this.bindings || {};
14513 this.bindings.iterator = this.iterator_;
14514 }
14515
14516 this.iterator_.updateDependencies(directives, this.model_);
14517 return this.iterator_;
14518 },
14519
14520 createInstance: function(model, bindingDelegate, delegate_,
14521 instanceBindings_) {
14522 if (bindingDelegate)
14523 delegate_ = this.newDelegate_(bindingDelegate);
14524
14525 var content = this.ref.content;
14526 var map = this.bindingMap_;
14527 if (!map || map.content !== content) {
14528 // TODO(rafaelw): Setup a MutationObserver on content to detect
14529 // when the instanceMap is invalid.
14530 map = createInstanceBindingMap(content,
14531 delegate_ && delegate_.prepareBinding) || [];
14532 map.content = content;
14533 this.bindingMap_ = map;
14534 }
14535
14536 var stagingDocument = getTemplateStagingDocument(this);
14537 var instance = stagingDocument.createDocumentFragment();
14538 instance.templateCreator_ = this;
14539 instance.protoContent_ = content;
14540
14541 var instanceRecord = {
14542 firstNode: null,
14543 lastNode: null,
14544 model: model
14545 };
14546
14547 var i = 0;
14548 for (var child = content.firstChild; child; child = child.nextSibling) {
14549 var clone = cloneAndBindInstance(child, instance, stagingDocument,
14550 map.children[i++],
14551 model,
14552 delegate_,
14553 instanceBindings_);
14554 clone.templateInstance_ = instanceRecord;
14555 }
14556
14557 instanceRecord.firstNode = instance.firstChild;
14558 instanceRecord.lastNode = instance.lastChild;
14559 instance.templateCreator_ = undefined;
14560 instance.protoContent_ = undefined;
14561 return instance;
14562 },
14563
14564 get model() {
14565 return this.model_;
14566 },
14567
14568 set model(model) {
14569 this.model_ = model;
14570 ensureSetModelScheduled(this);
14571 },
14572
14573 get bindingDelegate() {
14574 return this.delegate_ && this.delegate_.raw;
14575 },
14576
14577 setDelegate_: function(delegate) {
14578 this.delegate_ = delegate;
14579 this.bindingMap_ = undefined;
14580 if (this.iterator_) {
14581 this.iterator_.instancePositionChangedFn_ = undefined;
14582 this.iterator_.instanceModelFn_ = undefined;
14583 }
14584 },
14585
14586 newDelegate_: function(bindingDelegate) {
14587 if (!bindingDelegate)
14588 return {};
14589
14590 function delegateFn(name) {
14591 var fn = bindingDelegate && bindingDelegate[name];
14592 if (typeof fn != 'function')
14593 return;
14594
14595 return function() {
14596 return fn.apply(bindingDelegate, arguments);
14597 };
14598 }
14599
14600 return {
14601 raw: bindingDelegate,
14602 prepareBinding: delegateFn('prepareBinding'),
14603 prepareInstanceModel: delegateFn('prepareInstanceModel'),
14604 prepareInstancePositionChanged:
14605 delegateFn('prepareInstancePositionChanged')
14606 };
14607 },
14608
14609 // TODO(rafaelw): Assigning .bindingDelegate always succeeds. It may
14610 // make sense to issue a warning or even throw if the template is already
14611 // "activated", since this would be a strange thing to do.
14612 set bindingDelegate(bindingDelegate) {
14613 this.setDelegate_(this.newDelegate_(bindingDelegate));
14614 },
14615
14616 get ref() {
14617 var ref = searchRefId(this, this.getAttribute('ref'));
14618 if (!ref)
14619 ref = this.instanceRef_;
14620
14621 if (!ref)
14622 return this;
14623
14624 var nextRef = ref.ref;
14625 return nextRef ? nextRef : ref;
14626 }
14627 });
14628
14629 // Returns
14630 // a) undefined if there are no mustaches.
14631 // b) [TEXT, (ONE_TIME?, PATH, DELEGATE_FN, TEXT)+] if there is at least one mustache.
14632 function parseMustaches(s, name, node, prepareBindingFn) {
14633 if (!s || !s.length)
14634 return;
14635
14636 var tokens;
14637 var length = s.length;
14638 var startIndex = 0, lastIndex = 0, endIndex = 0;
14639 var onlyOneTime = true;
14640 while (lastIndex < length) {
14641 var startIndex = s.indexOf('{{', lastIndex);
14642 var oneTimeStart = s.indexOf('[[', lastIndex);
14643 var oneTime = false;
14644 var terminator = '}}';
14645
14646 if (oneTimeStart >= 0 &&
14647 (startIndex < 0 || oneTimeStart < startIndex)) {
14648 startIndex = oneTimeStart;
14649 oneTime = true;
14650 terminator = ']]';
14651 }
14652
14653 endIndex = startIndex < 0 ? -1 : s.indexOf(terminator, startIndex + 2);
14654
14655 if (endIndex < 0) {
14656 if (!tokens)
14657 return;
14658
14659 tokens.push(s.slice(lastIndex)); // TEXT
14660 break;
14661 }
14662
14663 tokens = tokens || [];
14664 tokens.push(s.slice(lastIndex, startIndex)); // TEXT
14665 var pathString = s.slice(startIndex + 2, endIndex).trim();
14666 tokens.push(oneTime); // ONE_TIME?
14667 onlyOneTime = onlyOneTime && oneTime;
14668 tokens.push(Path.get(pathString)); // PATH
14669 var delegateFn = prepareBindingFn &&
14670 prepareBindingFn(pathString, name, node);
14671 tokens.push(delegateFn); // DELEGATE_FN
14672 lastIndex = endIndex + 2;
14673 }
14674
14675 if (lastIndex === length)
14676 tokens.push(''); // TEXT
14677
14678 tokens.hasOnePath = tokens.length === 5;
14679 tokens.isSimplePath = tokens.hasOnePath &&
14680 tokens[0] == '' &&
14681 tokens[4] == '';
14682 tokens.onlyOneTime = onlyOneTime;
14683
14684 tokens.combinator = function(values) {
14685 var newValue = tokens[0];
14686
14687 for (var i = 1; i < tokens.length; i += 4) {
14688 var value = tokens.hasOnePath ? values : values[(i - 1) / 4];
14689 if (value !== undefined)
14690 newValue += value;
14691 newValue += tokens[i + 3];
14692 }
14693
14694 return newValue;
14695 }
14696
14697 return tokens;
14698 };
14699
14700 function processOneTimeBinding(name, tokens, node, model) {
14701 if (tokens.hasOnePath) {
14702 var delegateFn = tokens[3];
14703 var value = delegateFn ? delegateFn(model, node, true) :
14704 tokens[2].getValueFrom(model);
14705 return tokens.isSimplePath ? value : tokens.combinator(value);
14706 }
14707
14708 var values = [];
14709 for (var i = 1; i < tokens.length; i += 4) {
14710 var delegateFn = tokens[i + 2];
14711 values[(i - 1) / 4] = delegateFn ? delegateFn(model, node) :
14712 tokens[i + 1].getValueFrom(model);
14713 }
14714
14715 return tokens.combinator(values);
14716 }
14717
14718 function processSinglePathBinding(name, tokens, node, model) {
14719 var delegateFn = tokens[3];
14720 var observer = delegateFn ? delegateFn(model, node, false) :
14721 new PathObserver(model, tokens[2]);
14722
14723 return tokens.isSimplePath ? observer :
14724 new ObserverTransform(observer, tokens.combinator);
14725 }
14726
14727 function processBinding(name, tokens, node, model) {
14728 if (tokens.onlyOneTime)
14729 return processOneTimeBinding(name, tokens, node, model);
14730
14731 if (tokens.hasOnePath)
14732 return processSinglePathBinding(name, tokens, node, model);
14733
14734 var observer = new CompoundObserver();
14735
14736 for (var i = 1; i < tokens.length; i += 4) {
14737 var oneTime = tokens[i];
14738 var delegateFn = tokens[i + 2];
14739
14740 if (delegateFn) {
14741 var value = delegateFn(model, node, oneTime);
14742 if (oneTime)
14743 observer.addPath(value)
14744 else
14745 observer.addObserver(value);
14746 continue;
14747 }
14748
14749 var path = tokens[i + 1];
14750 if (oneTime)
14751 observer.addPath(path.getValueFrom(model))
14752 else
14753 observer.addPath(model, path);
14754 }
14755
14756 return new ObserverTransform(observer, tokens.combinator);
14757 }
14758
14759 function processBindings(node, bindings, model, instanceBindings) {
14760 for (var i = 0; i < bindings.length; i += 2) {
14761 var name = bindings[i]
14762 var tokens = bindings[i + 1];
14763 var value = processBinding(name, tokens, node, model);
14764 var binding = node.bind(name, value, tokens.onlyOneTime);
14765 if (binding && instanceBindings)
14766 instanceBindings.push(binding);
14767 }
14768
14769 if (!bindings.isTemplate)
14770 return;
14771
14772 node.model_ = model;
14773 var iter = node.processBindingDirectives_(bindings);
14774 if (instanceBindings && iter)
14775 instanceBindings.push(iter);
14776 }
14777
14778 function parseWithDefault(el, name, prepareBindingFn) {
14779 var v = el.getAttribute(name);
14780 return parseMustaches(v == '' ? '{{}}' : v, name, el, prepareBindingFn);
14781 }
14782
14783 function parseAttributeBindings(element, prepareBindingFn) {
14784 assert(element);
14785
14786 var bindings = [];
14787 var ifFound = false;
14788 var bindFound = false;
14789
14790 for (var i = 0; i < element.attributes.length; i++) {
14791 var attr = element.attributes[i];
14792 var name = attr.name;
14793 var value = attr.value;
14794
14795 // Allow bindings expressed in attributes to be prefixed with underbars.
14796 // We do this to allow correct semantics for browsers that don't implement
14797 // <template> where certain attributes might trigger side-effects -- and
14798 // for IE which sanitizes certain attributes, disallowing mustache
14799 // replacements in their text.
14800 while (name[0] === '_') {
14801 name = name.substring(1);
14802 }
14803
14804 if (isTemplate(element) &&
14805 (name === IF || name === BIND || name === REPEAT)) {
14806 continue;
14807 }
14808
14809 var tokens = parseMustaches(value, name, element,
14810 prepareBindingFn);
14811 if (!tokens)
14812 continue;
14813
14814 bindings.push(name, tokens);
14815 }
14816
14817 if (isTemplate(element)) {
14818 bindings.isTemplate = true;
14819 bindings.if = parseWithDefault(element, IF, prepareBindingFn);
14820 bindings.bind = parseWithDefault(element, BIND, prepareBindingFn);
14821 bindings.repeat = parseWithDefault(element, REPEAT, prepareBindingFn);
14822
14823 if (bindings.if && !bindings.bind && !bindings.repeat)
14824 bindings.bind = parseMustaches('{{}}', BIND, element, prepareBindingFn);
14825 }
14826
14827 return bindings;
14828 }
14829
14830 function getBindings(node, prepareBindingFn) {
14831 if (node.nodeType === Node.ELEMENT_NODE)
14832 return parseAttributeBindings(node, prepareBindingFn);
14833
14834 if (node.nodeType === Node.TEXT_NODE) {
14835 var tokens = parseMustaches(node.data, 'textContent', node,
14836 prepareBindingFn);
14837 if (tokens)
14838 return ['textContent', tokens];
14839 }
14840
14841 return [];
14842 }
14843
14844 function cloneAndBindInstance(node, parent, stagingDocument, bindings, model,
14845 delegate,
14846 instanceBindings,
14847 instanceRecord) {
14848 var clone = parent.appendChild(stagingDocument.importNode(node, false));
14849
14850 var i = 0;
14851 for (var child = node.firstChild; child; child = child.nextSibling) {
14852 cloneAndBindInstance(child, clone, stagingDocument,
14853 bindings.children[i++],
14854 model,
14855 delegate,
14856 instanceBindings);
14857 }
14858
14859 if (bindings.isTemplate) {
14860 HTMLTemplateElement.decorate(clone, node);
14861 if (delegate)
14862 clone.setDelegate_(delegate);
14863 }
14864
14865 processBindings(clone, bindings, model, instanceBindings);
14866 return clone;
14867 }
14868
14869 function createInstanceBindingMap(node, prepareBindingFn) {
14870 var map = getBindings(node, prepareBindingFn);
14871 map.children = {};
14872 var index = 0;
14873 for (var child = node.firstChild; child; child = child.nextSibling) {
14874 map.children[index++] = createInstanceBindingMap(child, prepareBindingFn);
14875 }
14876
14877 return map;
14878 }
14879
14880 Object.defineProperty(Node.prototype, 'templateInstance', {
14881 get: function() {
14882 var instance = this.templateInstance_;
14883 return instance ? instance :
14884 (this.parentNode ? this.parentNode.templateInstance : undefined);
14885 }
14886 });
14887
14888 function TemplateIterator(templateElement) {
14889 this.closed = false;
14890 this.templateElement_ = templateElement;
14891
14892 // Flattened array of tuples:
14893 // <instanceTerminatorNode, [bindingsSetupByInstance]>
14894 this.terminators = [];
14895
14896 this.deps = undefined;
14897 this.iteratedValue = [];
14898 this.presentValue = undefined;
14899 this.arrayObserver = undefined;
14900 }
14901
14902 TemplateIterator.prototype = {
14903 closeDeps: function() {
14904 var deps = this.deps;
14905 if (deps) {
14906 if (deps.ifOneTime === false)
14907 deps.ifValue.close();
14908 if (deps.oneTime === false)
14909 deps.value.close();
14910 }
14911 },
14912
14913 updateDependencies: function(directives, model) {
14914 this.closeDeps();
14915
14916 var deps = this.deps = {};
14917 var template = this.templateElement_;
14918
14919 if (directives.if) {
14920 deps.hasIf = true;
14921 deps.ifOneTime = directives.if.onlyOneTime;
14922 deps.ifValue = processBinding(IF, directives.if, template, model);
14923
14924 // oneTime if & predicate is false. nothing else to do.
14925 if (deps.ifOneTime && !deps.ifValue) {
14926 this.updateIteratedValue();
14927 return;
14928 }
14929
14930 if (!deps.ifOneTime)
14931 deps.ifValue.open(this.updateIteratedValue, this);
14932 }
14933
14934 if (directives.repeat) {
14935 deps.repeat = true;
14936 deps.oneTime = directives.repeat.onlyOneTime;
14937 deps.value = processBinding(REPEAT, directives.repeat, template, model);
14938 } else {
14939 deps.repeat = false;
14940 deps.oneTime = directives.bind.onlyOneTime;
14941 deps.value = processBinding(BIND, directives.bind, template, model);
14942 }
14943
14944 if (!deps.oneTime)
14945 deps.value.open(this.updateIteratedValue, this);
14946
14947 this.updateIteratedValue();
14948 },
14949
14950 updateIteratedValue: function() {
14951 if (this.deps.hasIf) {
14952 var ifValue = this.deps.ifValue;
14953 if (!this.deps.ifOneTime)
14954 ifValue = ifValue.discardChanges();
14955 if (!ifValue) {
14956 this.valueChanged();
14957 return;
14958 }
14959 }
14960
14961 var value = this.deps.value;
14962 if (!this.deps.oneTime)
14963 value = value.discardChanges();
14964 if (!this.deps.repeat)
14965 value = [value];
14966 var observe = this.deps.repeat &&
14967 !this.deps.oneTime &&
14968 Array.isArray(value);
14969 this.valueChanged(value, observe);
14970 },
14971
14972 valueChanged: function(value, observeValue) {
14973 if (!Array.isArray(value))
14974 value = [];
14975
14976 if (value === this.iteratedValue)
14977 return;
14978
14979 this.unobserve();
14980 this.presentValue = value;
14981 if (observeValue) {
14982 this.arrayObserver = new ArrayObserver(this.presentValue);
14983 this.arrayObserver.open(this.handleSplices, this);
14984 }
14985
14986 this.handleSplices(ArrayObserver.calculateSplices(this.presentValue,
14987 this.iteratedValue));
14988 },
14989
14990 getTerminatorAt: function(index) {
14991 if (index == -1)
14992 return this.templateElement_;
14993 var terminator = this.terminators[index*2];
14994 if (terminator.nodeType !== Node.ELEMENT_NODE ||
14995 this.templateElement_ === terminator) {
14996 return terminator;
14997 }
14998
14999 var subIterator = terminator.iterator_;
15000 if (!subIterator)
15001 return terminator;
15002
15003 return subIterator.getTerminatorAt(subIterator.terminators.length/2 - 1);
15004 },
15005
15006 // TODO(rafaelw): If we inserting sequences of instances we can probably
15007 // avoid lots of calls to getTerminatorAt(), or cache its result.
15008 insertInstanceAt: function(index, fragment, instanceNodes,
15009 instanceBindings) {
15010 var previousTerminator = this.getTerminatorAt(index - 1);
15011 var terminator = previousTerminator;
15012 if (fragment)
15013 terminator = fragment.lastChild || terminator;
15014 else if (instanceNodes)
15015 terminator = instanceNodes[instanceNodes.length - 1] || terminator;
15016
15017 this.terminators.splice(index*2, 0, terminator, instanceBindings);
15018 var parent = this.templateElement_.parentNode;
15019 var insertBeforeNode = previousTerminator.nextSibling;
15020
15021 if (fragment) {
15022 parent.insertBefore(fragment, insertBeforeNode);
15023 } else if (instanceNodes) {
15024 for (var i = 0; i < instanceNodes.length; i++)
15025 parent.insertBefore(instanceNodes[i], insertBeforeNode);
15026 }
15027 },
15028
15029 extractInstanceAt: function(index) {
15030 var instanceNodes = [];
15031 var previousTerminator = this.getTerminatorAt(index - 1);
15032 var terminator = this.getTerminatorAt(index);
15033 instanceNodes.instanceBindings = this.terminators[index*2 + 1];
15034 this.terminators.splice(index*2, 2);
15035
15036 var parent = this.templateElement_.parentNode;
15037 while (terminator !== previousTerminator) {
15038 var node = previousTerminator.nextSibling;
15039 if (node == terminator)
15040 terminator = previousTerminator;
15041
15042 parent.removeChild(node);
15043 instanceNodes.push(node);
15044 }
15045
15046 return instanceNodes;
15047 },
15048
15049 getDelegateFn: function(fn) {
15050 fn = fn && fn(this.templateElement_);
15051 return typeof fn === 'function' ? fn : null;
15052 },
15053
15054 handleSplices: function(splices) {
15055 if (this.closed || !splices.length)
15056 return;
15057
15058 var template = this.templateElement_;
15059
15060 if (!template.parentNode) {
15061 this.close();
15062 return;
15063 }
15064
15065 ArrayObserver.applySplices(this.iteratedValue, this.presentValue,
15066 splices);
15067
15068 var delegate = template.delegate_;
15069 if (this.instanceModelFn_ === undefined) {
15070 this.instanceModelFn_ =
15071 this.getDelegateFn(delegate && delegate.prepareInstanceModel);
15072 }
15073
15074 if (this.instancePositionChangedFn_ === undefined) {
15075 this.instancePositionChangedFn_ =
15076 this.getDelegateFn(delegate &&
15077 delegate.prepareInstancePositionChanged);
15078 }
15079
15080 var instanceCache = new Map;
15081 var removeDelta = 0;
15082 splices.forEach(function(splice) {
15083 splice.removed.forEach(function(model) {
15084 var instanceNodes =
15085 this.extractInstanceAt(splice.index + removeDelta);
15086 instanceCache.set(model, instanceNodes);
15087 }, this);
15088
15089 removeDelta -= splice.addedCount;
15090 }, this);
15091
15092 splices.forEach(function(splice) {
15093 var addIndex = splice.index;
15094 for (; addIndex < splice.index + splice.addedCount; addIndex++) {
15095 var model = this.iteratedValue[addIndex];
15096 var fragment = undefined;
15097 var instanceNodes = instanceCache.get(model);
15098 var instanceBindings;
15099 if (instanceNodes) {
15100 instanceCache.delete(model);
15101 instanceBindings = instanceNodes.instanceBindings;
15102 } else {
15103 instanceBindings = [];
15104 if (this.instanceModelFn_)
15105 model = this.instanceModelFn_(model);
15106
15107 if (model !== undefined) {
15108 fragment = template.createInstance(model, undefined, delegate,
15109 instanceBindings);
15110 }
15111 }
15112
15113 this.insertInstanceAt(addIndex, fragment, instanceNodes,
15114 instanceBindings);
15115 }
15116 }, this);
15117
15118 instanceCache.forEach(function(instanceNodes) {
15119 this.closeInstanceBindings(instanceNodes.instanceBindings);
15120 }, this);
15121
15122 if (this.instancePositionChangedFn_)
15123 this.reportInstancesMoved(splices);
15124 },
15125
15126 reportInstanceMoved: function(index) {
15127 var previousTerminator = this.getTerminatorAt(index - 1);
15128 var terminator = this.getTerminatorAt(index);
15129 if (previousTerminator === terminator)
15130 return; // instance has zero nodes.
15131
15132 // We must use the first node of the instance, because any subsequent
15133 // nodes may have been generated by sub-templates.
15134 // TODO(rafaelw): This is brittle WRT instance mutation -- e.g. if the
15135 // first node was removed by script.
15136 var templateInstance = previousTerminator.nextSibling.templateInstance;
15137 this.instancePositionChangedFn_(templateInstance, index);
15138 },
15139
15140 reportInstancesMoved: function(splices) {
15141 var index = 0;
15142 var offset = 0;
15143 for (var i = 0; i < splices.length; i++) {
15144 var splice = splices[i];
15145 if (offset != 0) {
15146 while (index < splice.index) {
15147 this.reportInstanceMoved(index);
15148 index++;
15149 }
15150 } else {
15151 index = splice.index;
15152 }
15153
15154 while (index < splice.index + splice.addedCount) {
15155 this.reportInstanceMoved(index);
15156 index++;
15157 }
15158
15159 offset += splice.addedCount - splice.removed.length;
15160 }
15161
15162 if (offset == 0)
15163 return;
15164
15165 var length = this.terminators.length / 2;
15166 while (index < length) {
15167 this.reportInstanceMoved(index);
15168 index++;
15169 }
15170 },
15171
15172 closeInstanceBindings: function(instanceBindings) {
15173 for (var i = 0; i < instanceBindings.length; i++) {
15174 instanceBindings[i].close();
15175 }
15176 },
15177
15178 unobserve: function() {
15179 if (!this.arrayObserver)
15180 return;
15181
15182 this.arrayObserver.close();
15183 this.arrayObserver = undefined;
15184 },
15185
15186 close: function() {
15187 if (this.closed)
15188 return;
15189 this.unobserve();
15190 for (var i = 1; i < this.terminators.length; i += 2) {
15191 this.closeInstanceBindings(this.terminators[i]);
15192 }
15193
15194 this.terminators.length = 0;
15195 this.closeDeps();
15196 this.templateElement_.iterator_ = undefined;
15197 this.closed = true;
15198 }
15199 };
15200
15201 // Polyfill-specific API.
15202 HTMLTemplateElement.forAllTemplatesFrom_ = forAllTemplatesFrom;
15203 })(this);
15204
15205 /*
15206 Copyright (C) 2013 Ariya Hidayat <ariya.hidayat@gmail.com>
15207 Copyright (C) 2013 Thaddee Tyl <thaddee.tyl@gmail.com>
15208 Copyright (C) 2012 Ariya Hidayat <ariya.hidayat@gmail.com>
15209 Copyright (C) 2012 Mathias Bynens <mathias@qiwi.be>
15210 Copyright (C) 2012 Joost-Wim Boekesteijn <joost-wim@boekesteijn.nl>
15211 Copyright (C) 2012 Kris Kowal <kris.kowal@cixar.com>
15212 Copyright (C) 2012 Yusuke Suzuki <utatane.tea@gmail.com>
15213 Copyright (C) 2012 Arpad Borsos <arpad.borsos@googlemail.com>
15214 Copyright (C) 2011 Ariya Hidayat <ariya.hidayat@gmail.com>
15215
15216 Redistribution and use in source and binary forms, with or without
15217 modification, are permitted provided that the following conditions are met:
15218
15219 * Redistributions of source code must retain the above copyright
15220 notice, this list of conditions and the following disclaimer.
15221 * Redistributions in binary form must reproduce the above copyright
15222 notice, this list of conditions and the following disclaimer in the
15223 documentation and/or other materials provided with the distribution.
15224
15225 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15226 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15227 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
15228 ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
15229 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
15230 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
15231 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
15232 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
15233 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
15234 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
15235 */
15236
15237 (function (global) {
15238 'use strict';
15239
15240 var Token,
15241 TokenName,
15242 Syntax,
15243 Messages,
15244 source,
15245 index,
15246 length,
15247 delegate,
15248 lookahead,
15249 state;
15250
15251 Token = {
15252 BooleanLiteral: 1,
15253 EOF: 2,
15254 Identifier: 3,
15255 Keyword: 4,
15256 NullLiteral: 5,
15257 NumericLiteral: 6,
15258 Punctuator: 7,
15259 StringLiteral: 8
15260 };
15261
15262 TokenName = {};
15263 TokenName[Token.BooleanLiteral] = 'Boolean';
15264 TokenName[Token.EOF] = '<end>';
15265 TokenName[Token.Identifier] = 'Identifier';
15266 TokenName[Token.Keyword] = 'Keyword';
15267 TokenName[Token.NullLiteral] = 'Null';
15268 TokenName[Token.NumericLiteral] = 'Numeric';
15269 TokenName[Token.Punctuator] = 'Punctuator';
15270 TokenName[Token.StringLiteral] = 'String';
15271
15272 Syntax = {
15273 ArrayExpression: 'ArrayExpression',
15274 BinaryExpression: 'BinaryExpression',
15275 CallExpression: 'CallExpression',
15276 ConditionalExpression: 'ConditionalExpression',
15277 EmptyStatement: 'EmptyStatement',
15278 ExpressionStatement: 'ExpressionStatement',
15279 Identifier: 'Identifier',
15280 Literal: 'Literal',
15281 LabeledStatement: 'LabeledStatement',
15282 LogicalExpression: 'LogicalExpression',
15283 MemberExpression: 'MemberExpression',
15284 ObjectExpression: 'ObjectExpression',
15285 Program: 'Program',
15286 Property: 'Property',
15287 ThisExpression: 'ThisExpression',
15288 UnaryExpression: 'UnaryExpression'
15289 };
15290
15291 // Error messages should be identical to V8.
15292 Messages = {
15293 UnexpectedToken: 'Unexpected token %0',
15294 UnknownLabel: 'Undefined label \'%0\'',
15295 Redeclaration: '%0 \'%1\' has already been declared'
15296 };
15297
15298 // Ensure the condition is true, otherwise throw an error.
15299 // This is only to have a better contract semantic, i.e. another safety net
15300 // to catch a logic error. The condition shall be fulfilled in normal case.
15301 // Do NOT use this to enforce a certain condition on any user input.
15302
15303 function assert(condition, message) {
15304 if (!condition) {
15305 throw new Error('ASSERT: ' + message);
15306 }
15307 }
15308
15309 function isDecimalDigit(ch) {
15310 return (ch >= 48 && ch <= 57); // 0..9
15311 }
15312
15313
15314 // 7.2 White Space
15315
15316 function isWhiteSpace(ch) {
15317 return (ch === 32) || // space
15318 (ch === 9) || // tab
15319 (ch === 0xB) ||
15320 (ch === 0xC) ||
15321 (ch === 0xA0) ||
15322 (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);
15323 }
15324
15325 // 7.3 Line Terminators
15326
15327 function isLineTerminator(ch) {
15328 return (ch === 10) || (ch === 13) || (ch === 0x2028) || (ch === 0x2029);
15329 }
15330
15331 // 7.6 Identifier Names and Identifiers
15332
15333 function isIdentifierStart(ch) {
15334 return (ch === 36) || (ch === 95) || // $ (dollar) and _ (underscore)
15335 (ch >= 65 && ch <= 90) || // A..Z
15336 (ch >= 97 && ch <= 122); // a..z
15337 }
15338
15339 function isIdentifierPart(ch) {
15340 return (ch === 36) || (ch === 95) || // $ (dollar) and _ (underscore)
15341 (ch >= 65 && ch <= 90) || // A..Z
15342 (ch >= 97 && ch <= 122) || // a..z
15343 (ch >= 48 && ch <= 57); // 0..9
15344 }
15345
15346 // 7.6.1.1 Keywords
15347
15348 function isKeyword(id) {
15349 return (id === 'this')
15350 }
15351
15352 // 7.4 Comments
15353
15354 function skipWhitespace() {
15355 while (index < length && isWhiteSpace(source.charCodeAt(index))) {
15356 ++index;
15357 }
15358 }
15359
15360 function getIdentifier() {
15361 var start, ch;
15362
15363 start = index++;
15364 while (index < length) {
15365 ch = source.charCodeAt(index);
15366 if (isIdentifierPart(ch)) {
15367 ++index;
15368 } else {
15369 break;
15370 }
15371 }
15372
15373 return source.slice(start, index);
15374 }
15375
15376 function scanIdentifier() {
15377 var start, id, type;
15378
15379 start = index;
15380
15381 id = getIdentifier();
15382
15383 // There is no keyword or literal with only one character.
15384 // Thus, it must be an identifier.
15385 if (id.length === 1) {
15386 type = Token.Identifier;
15387 } else if (isKeyword(id)) {
15388 type = Token.Keyword;
15389 } else if (id === 'null') {
15390 type = Token.NullLiteral;
15391 } else if (id === 'true' || id === 'false') {
15392 type = Token.BooleanLiteral;
15393 } else {
15394 type = Token.Identifier;
15395 }
15396
15397 return {
15398 type: type,
15399 value: id,
15400 range: [start, index]
15401 };
15402 }
15403
15404
15405 // 7.7 Punctuators
15406
15407 function scanPunctuator() {
15408 var start = index,
15409 code = source.charCodeAt(index),
15410 code2,
15411 ch1 = source[index],
15412 ch2;
15413
15414 switch (code) {
15415
15416 // Check for most common single-character punctuators.
15417 case 46: // . dot
15418 case 40: // ( open bracket
15419 case 41: // ) close bracket
15420 case 59: // ; semicolon
15421 case 44: // , comma
15422 case 123: // { open curly brace
15423 case 125: // } close curly brace
15424 case 91: // [
15425 case 93: // ]
15426 case 58: // :
15427 case 63: // ?
15428 ++index;
15429 return {
15430 type: Token.Punctuator,
15431 value: String.fromCharCode(code),
15432 range: [start, index]
15433 };
15434
15435 default:
15436 code2 = source.charCodeAt(index + 1);
15437
15438 // '=' (char #61) marks an assignment or comparison operator.
15439 if (code2 === 61) {
15440 switch (code) {
15441 case 37: // %
15442 case 38: // &
15443 case 42: // *:
15444 case 43: // +
15445 case 45: // -
15446 case 47: // /
15447 case 60: // <
15448 case 62: // >
15449 case 124: // |
15450 index += 2;
15451 return {
15452 type: Token.Punctuator,
15453 value: String.fromCharCode(code) + String.fromCharCode(c ode2),
15454 range: [start, index]
15455 };
15456
15457 case 33: // !
15458 case 61: // =
15459 index += 2;
15460
15461 // !== and ===
15462 if (source.charCodeAt(index) === 61) {
15463 ++index;
15464 }
15465 return {
15466 type: Token.Punctuator,
15467 value: source.slice(start, index),
15468 range: [start, index]
15469 };
15470 default:
15471 break;
15472 }
15473 }
15474 break;
15475 }
15476
15477 // Peek more characters.
15478
15479 ch2 = source[index + 1];
15480
15481 // Other 2-character punctuators: && ||
15482
15483 if (ch1 === ch2 && ('&|'.indexOf(ch1) >= 0)) {
15484 index += 2;
15485 return {
15486 type: Token.Punctuator,
15487 value: ch1 + ch2,
15488 range: [start, index]
15489 };
15490 }
15491
15492 if ('<>=!+-*%&|^/'.indexOf(ch1) >= 0) {
15493 ++index;
15494 return {
15495 type: Token.Punctuator,
15496 value: ch1,
15497 range: [start, index]
15498 };
15499 }
15500
15501 throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
15502 }
15503
15504 // 7.8.3 Numeric Literals
15505 function scanNumericLiteral() {
15506 var number, start, ch;
15507
15508 ch = source[index];
15509 assert(isDecimalDigit(ch.charCodeAt(0)) || (ch === '.'),
15510 'Numeric literal must start with a decimal digit or a decimal point' );
15511
15512 start = index;
15513 number = '';
15514 if (ch !== '.') {
15515 number = source[index++];
15516 ch = source[index];
15517
15518 // Hex number starts with '0x'.
15519 // Octal number starts with '0'.
15520 if (number === '0') {
15521 // decimal number starts with '0' such as '09' is illegal.
15522 if (ch && isDecimalDigit(ch.charCodeAt(0))) {
15523 throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
15524 }
15525 }
15526
15527 while (isDecimalDigit(source.charCodeAt(index))) {
15528 number += source[index++];
15529 }
15530 ch = source[index];
15531 }
15532
15533 if (ch === '.') {
15534 number += source[index++];
15535 while (isDecimalDigit(source.charCodeAt(index))) {
15536 number += source[index++];
15537 }
15538 ch = source[index];
15539 }
15540
15541 if (ch === 'e' || ch === 'E') {
15542 number += source[index++];
15543
15544 ch = source[index];
15545 if (ch === '+' || ch === '-') {
15546 number += source[index++];
15547 }
15548 if (isDecimalDigit(source.charCodeAt(index))) {
15549 while (isDecimalDigit(source.charCodeAt(index))) {
15550 number += source[index++];
15551 }
15552 } else {
15553 throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
15554 }
15555 }
15556
15557 if (isIdentifierStart(source.charCodeAt(index))) {
15558 throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
15559 }
15560
15561 return {
15562 type: Token.NumericLiteral,
15563 value: parseFloat(number),
15564 range: [start, index]
15565 };
15566 }
15567
15568 // 7.8.4 String Literals
15569
15570 function scanStringLiteral() {
15571 var str = '', quote, start, ch, octal = false;
15572
15573 quote = source[index];
15574 assert((quote === '\'' || quote === '"'),
15575 'String literal must starts with a quote');
15576
15577 start = index;
15578 ++index;
15579
15580 while (index < length) {
15581 ch = source[index++];
15582
15583 if (ch === quote) {
15584 quote = '';
15585 break;
15586 } else if (ch === '\\') {
15587 ch = source[index++];
15588 if (!ch || !isLineTerminator(ch.charCodeAt(0))) {
15589 switch (ch) {
15590 case 'n':
15591 str += '\n';
15592 break;
15593 case 'r':
15594 str += '\r';
15595 break;
15596 case 't':
15597 str += '\t';
15598 break;
15599 case 'b':
15600 str += '\b';
15601 break;
15602 case 'f':
15603 str += '\f';
15604 break;
15605 case 'v':
15606 str += '\x0B';
15607 break;
15608
15609 default:
15610 str += ch;
15611 break;
15612 }
15613 } else {
15614 if (ch === '\r' && source[index] === '\n') {
15615 ++index;
15616 }
15617 }
15618 } else if (isLineTerminator(ch.charCodeAt(0))) {
15619 break;
15620 } else {
15621 str += ch;
15622 }
15623 }
15624
15625 if (quote !== '') {
15626 throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
15627 }
15628
15629 return {
15630 type: Token.StringLiteral,
15631 value: str,
15632 octal: octal,
15633 range: [start, index]
15634 };
15635 }
15636
15637 function isIdentifierName(token) {
15638 return token.type === Token.Identifier ||
15639 token.type === Token.Keyword ||
15640 token.type === Token.BooleanLiteral ||
15641 token.type === Token.NullLiteral;
15642 }
15643
15644 function advance() {
15645 var ch;
15646
15647 skipWhitespace();
15648
15649 if (index >= length) {
15650 return {
15651 type: Token.EOF,
15652 range: [index, index]
15653 };
15654 }
15655
15656 ch = source.charCodeAt(index);
15657
15658 // Very common: ( and ) and ;
15659 if (ch === 40 || ch === 41 || ch === 58) {
15660 return scanPunctuator();
15661 }
15662
15663 // String literal starts with single quote (#39) or double quote (#34).
15664 if (ch === 39 || ch === 34) {
15665 return scanStringLiteral();
15666 }
15667
15668 if (isIdentifierStart(ch)) {
15669 return scanIdentifier();
15670 }
15671
15672 // Dot (.) char #46 can also start a floating-point number, hence the ne ed
15673 // to check the next character.
15674 if (ch === 46) {
15675 if (isDecimalDigit(source.charCodeAt(index + 1))) {
15676 return scanNumericLiteral();
15677 }
15678 return scanPunctuator();
15679 }
15680
15681 if (isDecimalDigit(ch)) {
15682 return scanNumericLiteral();
15683 }
15684
15685 return scanPunctuator();
15686 }
15687
15688 function lex() {
15689 var token;
15690
15691 token = lookahead;
15692 index = token.range[1];
15693
15694 lookahead = advance();
15695
15696 index = token.range[1];
15697
15698 return token;
15699 }
15700
15701 function peek() {
15702 var pos;
15703
15704 pos = index;
15705 lookahead = advance();
15706 index = pos;
15707 }
15708
15709 // Throw an exception
15710
15711 function throwError(token, messageFormat) {
15712 var error,
15713 args = Array.prototype.slice.call(arguments, 2),
15714 msg = messageFormat.replace(
15715 /%(\d)/g,
15716 function (whole, index) {
15717 assert(index < args.length, 'Message reference must be in ra nge');
15718 return args[index];
15719 }
15720 );
15721
15722 error = new Error(msg);
15723 error.index = index;
15724 error.description = msg;
15725 throw error;
15726 }
15727
15728 // Throw an exception because of the token.
15729
15730 function throwUnexpected(token) {
15731 throwError(token, Messages.UnexpectedToken, token.value);
15732 }
15733
15734 // Expect the next token to match the specified punctuator.
15735 // If not, an exception will be thrown.
15736
15737 function expect(value) {
15738 var token = lex();
15739 if (token.type !== Token.Punctuator || token.value !== value) {
15740 throwUnexpected(token);
15741 }
15742 }
15743
15744 // Return true if the next token matches the specified punctuator.
15745
15746 function match(value) {
15747 return lookahead.type === Token.Punctuator && lookahead.value === value;
15748 }
15749
15750 // Return true if the next token matches the specified keyword
15751
15752 function matchKeyword(keyword) {
15753 return lookahead.type === Token.Keyword && lookahead.value === keyword;
15754 }
15755
15756 function consumeSemicolon() {
15757 // Catch the very common case first: immediately a semicolon (char #59).
15758 if (source.charCodeAt(index) === 59) {
15759 lex();
15760 return;
15761 }
15762
15763 skipWhitespace();
15764
15765 if (match(';')) {
15766 lex();
15767 return;
15768 }
15769
15770 if (lookahead.type !== Token.EOF && !match('}')) {
15771 throwUnexpected(lookahead);
15772 }
15773 }
15774
15775 // 11.1.4 Array Initialiser
15776
15777 function parseArrayInitialiser() {
15778 var elements = [];
15779
15780 expect('[');
15781
15782 while (!match(']')) {
15783 if (match(',')) {
15784 lex();
15785 elements.push(null);
15786 } else {
15787 elements.push(parseExpression());
15788
15789 if (!match(']')) {
15790 expect(',');
15791 }
15792 }
15793 }
15794
15795 expect(']');
15796
15797 return delegate.createArrayExpression(elements);
15798 }
15799
15800 // 11.1.5 Object Initialiser
15801
15802 function parseObjectPropertyKey() {
15803 var token;
15804
15805 skipWhitespace();
15806 token = lex();
15807
15808 // Note: This function is called only from parseObjectProperty(), where
15809 // EOF and Punctuator tokens are already filtered out.
15810 if (token.type === Token.StringLiteral || token.type === Token.NumericLi teral) {
15811 return delegate.createLiteral(token);
15812 }
15813
15814 return delegate.createIdentifier(token.value);
15815 }
15816
15817 function parseObjectProperty() {
15818 var token, key;
15819
15820 token = lookahead;
15821 skipWhitespace();
15822
15823 if (token.type === Token.EOF || token.type === Token.Punctuator) {
15824 throwUnexpected(token);
15825 }
15826
15827 key = parseObjectPropertyKey();
15828 expect(':');
15829 return delegate.createProperty('init', key, parseExpression());
15830 }
15831
15832 function parseObjectInitialiser() {
15833 var properties = [];
15834
15835 expect('{');
15836
15837 while (!match('}')) {
15838 properties.push(parseObjectProperty());
15839
15840 if (!match('}')) {
15841 expect(',');
15842 }
15843 }
15844
15845 expect('}');
15846
15847 return delegate.createObjectExpression(properties);
15848 }
15849
15850 // 11.1.6 The Grouping Operator
15851
15852 function parseGroupExpression() {
15853 var expr;
15854
15855 expect('(');
15856
15857 expr = parseExpression();
15858
15859 expect(')');
15860
15861 return expr;
15862 }
15863
15864
15865 // 11.1 Primary Expressions
15866
15867 function parsePrimaryExpression() {
15868 var type, token, expr;
15869
15870 if (match('(')) {
15871 return parseGroupExpression();
15872 }
15873
15874 type = lookahead.type;
15875
15876 if (type === Token.Identifier) {
15877 expr = delegate.createIdentifier(lex().value);
15878 } else if (type === Token.StringLiteral || type === Token.NumericLiteral ) {
15879 expr = delegate.createLiteral(lex());
15880 } else if (type === Token.Keyword) {
15881 if (matchKeyword('this')) {
15882 lex();
15883 expr = delegate.createThisExpression();
15884 }
15885 } else if (type === Token.BooleanLiteral) {
15886 token = lex();
15887 token.value = (token.value === 'true');
15888 expr = delegate.createLiteral(token);
15889 } else if (type === Token.NullLiteral) {
15890 token = lex();
15891 token.value = null;
15892 expr = delegate.createLiteral(token);
15893 } else if (match('[')) {
15894 expr = parseArrayInitialiser();
15895 } else if (match('{')) {
15896 expr = parseObjectInitialiser();
15897 }
15898
15899 if (expr) {
15900 return expr;
15901 }
15902
15903 throwUnexpected(lex());
15904 }
15905
15906 // 11.2 Left-Hand-Side Expressions
15907
15908 function parseArguments() {
15909 var args = [];
15910
15911 expect('(');
15912
15913 if (!match(')')) {
15914 while (index < length) {
15915 args.push(parseExpression());
15916 if (match(')')) {
15917 break;
15918 }
15919 expect(',');
15920 }
15921 }
15922
15923 expect(')');
15924
15925 return args;
15926 }
15927
15928 function parseNonComputedProperty() {
15929 var token;
15930
15931 token = lex();
15932
15933 if (!isIdentifierName(token)) {
15934 throwUnexpected(token);
15935 }
15936
15937 return delegate.createIdentifier(token.value);
15938 }
15939
15940 function parseNonComputedMember() {
15941 expect('.');
15942
15943 return parseNonComputedProperty();
15944 }
15945
15946 function parseComputedMember() {
15947 var expr;
15948
15949 expect('[');
15950
15951 expr = parseExpression();
15952
15953 expect(']');
15954
15955 return expr;
15956 }
15957
15958 function parseLeftHandSideExpression() {
15959 var expr, property;
15960
15961 expr = parsePrimaryExpression();
15962
15963 while (match('.') || match('[')) {
15964 if (match('[')) {
15965 property = parseComputedMember();
15966 expr = delegate.createMemberExpression('[', expr, property);
15967 } else {
15968 property = parseNonComputedMember();
15969 expr = delegate.createMemberExpression('.', expr, property);
15970 }
15971 }
15972
15973 return expr;
15974 }
15975
15976 // 11.3 Postfix Expressions
15977
15978 var parsePostfixExpression = parseLeftHandSideExpression;
15979
15980 // 11.4 Unary Operators
15981
15982 function parseUnaryExpression() {
15983 var token, expr;
15984
15985 if (lookahead.type !== Token.Punctuator && lookahead.type !== Token.Keyw ord) {
15986 expr = parsePostfixExpression();
15987 } else if (match('+') || match('-') || match('!')) {
15988 token = lex();
15989 expr = parseUnaryExpression();
15990 expr = delegate.createUnaryExpression(token.value, expr);
15991 } else if (matchKeyword('delete') || matchKeyword('void') || matchKeywor d('typeof')) {
15992 throwError({}, Messages.UnexpectedToken);
15993 } else {
15994 expr = parsePostfixExpression();
15995 }
15996
15997 return expr;
15998 }
15999
16000 function binaryPrecedence(token) {
16001 var prec = 0;
16002
16003 if (token.type !== Token.Punctuator && token.type !== Token.Keyword) {
16004 return 0;
16005 }
16006
16007 switch (token.value) {
16008 case '||':
16009 prec = 1;
16010 break;
16011
16012 case '&&':
16013 prec = 2;
16014 break;
16015
16016 case '==':
16017 case '!=':
16018 case '===':
16019 case '!==':
16020 prec = 6;
16021 break;
16022
16023 case '<':
16024 case '>':
16025 case '<=':
16026 case '>=':
16027 case 'instanceof':
16028 prec = 7;
16029 break;
16030
16031 case 'in':
16032 prec = 7;
16033 break;
16034
16035 case '+':
16036 case '-':
16037 prec = 9;
16038 break;
16039
16040 case '*':
16041 case '/':
16042 case '%':
16043 prec = 11;
16044 break;
16045
16046 default:
16047 break;
16048 }
16049
16050 return prec;
16051 }
16052
16053 // 11.5 Multiplicative Operators
16054 // 11.6 Additive Operators
16055 // 11.7 Bitwise Shift Operators
16056 // 11.8 Relational Operators
16057 // 11.9 Equality Operators
16058 // 11.10 Binary Bitwise Operators
16059 // 11.11 Binary Logical Operators
16060
16061 function parseBinaryExpression() {
16062 var expr, token, prec, stack, right, operator, left, i;
16063
16064 left = parseUnaryExpression();
16065
16066 token = lookahead;
16067 prec = binaryPrecedence(token);
16068 if (prec === 0) {
16069 return left;
16070 }
16071 token.prec = prec;
16072 lex();
16073
16074 right = parseUnaryExpression();
16075
16076 stack = [left, token, right];
16077
16078 while ((prec = binaryPrecedence(lookahead)) > 0) {
16079
16080 // Reduce: make a binary expression from the three topmost entries.
16081 while ((stack.length > 2) && (prec <= stack[stack.length - 2].prec)) {
16082 right = stack.pop();
16083 operator = stack.pop().value;
16084 left = stack.pop();
16085 expr = delegate.createBinaryExpression(operator, left, right);
16086 stack.push(expr);
16087 }
16088
16089 // Shift.
16090 token = lex();
16091 token.prec = prec;
16092 stack.push(token);
16093 expr = parseUnaryExpression();
16094 stack.push(expr);
16095 }
16096
16097 // Final reduce to clean-up the stack.
16098 i = stack.length - 1;
16099 expr = stack[i];
16100 while (i > 1) {
16101 expr = delegate.createBinaryExpression(stack[i - 1].value, stack[i - 2], expr);
16102 i -= 2;
16103 }
16104
16105 return expr;
16106 }
16107
16108
16109 // 11.12 Conditional Operator
16110
16111 function parseConditionalExpression() {
16112 var expr, consequent, alternate;
16113
16114 expr = parseBinaryExpression();
16115
16116 if (match('?')) {
16117 lex();
16118 consequent = parseConditionalExpression();
16119 expect(':');
16120 alternate = parseConditionalExpression();
16121
16122 expr = delegate.createConditionalExpression(expr, consequent, altern ate);
16123 }
16124
16125 return expr;
16126 }
16127
16128 // Simplification since we do not support AssignmentExpression.
16129 var parseExpression = parseConditionalExpression;
16130
16131 // Polymer Syntax extensions
16132
16133 // Filter ::
16134 // Identifier
16135 // Identifier "(" ")"
16136 // Identifier "(" FilterArguments ")"
16137
16138 function parseFilter() {
16139 var identifier, args;
16140
16141 identifier = lex();
16142
16143 if (identifier.type !== Token.Identifier) {
16144 throwUnexpected(identifier);
16145 }
16146
16147 args = match('(') ? parseArguments() : [];
16148
16149 return delegate.createFilter(identifier.value, args);
16150 }
16151
16152 // Filters ::
16153 // "|" Filter
16154 // Filters "|" Filter
16155
16156 function parseFilters() {
16157 while (match('|')) {
16158 lex();
16159 parseFilter();
16160 }
16161 }
16162
16163 // TopLevel ::
16164 // LabelledExpressions
16165 // AsExpression
16166 // InExpression
16167 // FilterExpression
16168
16169 // AsExpression ::
16170 // FilterExpression as Identifier
16171
16172 // InExpression ::
16173 // Identifier, Identifier in FilterExpression
16174 // Identifier in FilterExpression
16175
16176 // FilterExpression ::
16177 // Expression
16178 // Expression Filters
16179
16180 function parseTopLevel() {
16181 skipWhitespace();
16182 peek();
16183
16184 var expr = parseExpression();
16185 if (expr) {
16186 if (lookahead.value === ',' || lookahead.value == 'in' &&
16187 expr.type === Syntax.Identifier) {
16188 parseInExpression(expr);
16189 } else {
16190 parseFilters();
16191 if (lookahead.value === 'as') {
16192 parseAsExpression(expr);
16193 } else {
16194 delegate.createTopLevel(expr);
16195 }
16196 }
16197 }
16198
16199 if (lookahead.type !== Token.EOF) {
16200 throwUnexpected(lookahead);
16201 }
16202 }
16203
16204 function parseAsExpression(expr) {
16205 lex(); // as
16206 var identifier = lex().value;
16207 delegate.createAsExpression(expr, identifier);
16208 }
16209
16210 function parseInExpression(identifier) {
16211 var indexName;
16212 if (lookahead.value === ',') {
16213 lex();
16214 if (lookahead.type !== Token.Identifier)
16215 throwUnexpected(lookahead);
16216 indexName = lex().value;
16217 }
16218
16219 lex(); // in
16220 var expr = parseExpression();
16221 parseFilters();
16222 delegate.createInExpression(identifier.name, indexName, expr);
16223 }
16224
16225 function parse(code, inDelegate) {
16226 delegate = inDelegate;
16227 source = code;
16228 index = 0;
16229 length = source.length;
16230 lookahead = null;
16231 state = {
16232 labelSet: {}
16233 };
16234
16235 return parseTopLevel();
16236 }
16237
16238 global.esprima = {
16239 parse: parse
16240 };
16241 })(this);
16242
16243 // Copyright 2013 Google Inc.
16244 //
16245 // Licensed under the Apache License, Version 2.0 (the "License");
16246 // you may not use this file except in compliance with the License.
16247 // You may obtain a copy of the License at
16248 //
16249 // http://www.apache.org/licenses/LICENSE-2.0
16250 //
16251 // Unless required by applicable law or agreed to in writing, software
16252 // distributed under the License is distributed on an "AS IS" BASIS,
16253 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16254 // See the License for the specific language governing permissions and
16255 // limitations under the License.
16256
16257 (function (global) {
16258 'use strict';
16259
16260 // JScript does not have __proto__. We wrap all object literals with
16261 // createObject which uses Object.create, Object.defineProperty and
16262 // Object.getOwnPropertyDescriptor to create a new object that does the exact
16263 // same thing. The main downside to this solution is that we have to extract
16264 // all those property descriptors for IE.
16265 var createObject = ('__proto__' in {}) ?
16266 function(obj) { return obj; } :
16267 function(obj) {
16268 var proto = obj.__proto__;
16269 if (!proto)
16270 return obj;
16271 var newObject = Object.create(proto);
16272 Object.getOwnPropertyNames(obj).forEach(function(name) {
16273 Object.defineProperty(newObject, name,
16274 Object.getOwnPropertyDescriptor(obj, name));
16275 });
16276 return newObject;
16277 };
16278
16279 function prepareBinding(expressionText, name, node, filterRegistry) {
16280 var expression;
16281 try {
16282 expression = getExpression(expressionText);
16283 if (expression.scopeIdent &&
16284 (node.nodeType !== Node.ELEMENT_NODE ||
16285 node.tagName !== 'TEMPLATE' ||
16286 (name !== 'bind' && name !== 'repeat'))) {
16287 throw Error('as and in can only be used within <template bind/repeat>');
16288 }
16289 } catch (ex) {
16290 console.error('Invalid expression syntax: ' + expressionText, ex);
16291 return;
16292 }
16293
16294 return function(model, node, oneTime) {
16295 var binding = expression.getBinding(model, filterRegistry, oneTime);
16296 if (expression.scopeIdent && binding) {
16297 node.polymerExpressionScopeIdent_ = expression.scopeIdent;
16298 if (expression.indexIdent)
16299 node.polymerExpressionIndexIdent_ = expression.indexIdent;
16300 }
16301
16302 return binding;
16303 }
16304 }
16305
16306 // TODO(rafaelw): Implement simple LRU.
16307 var expressionParseCache = Object.create(null);
16308
16309 function getExpression(expressionText) {
16310 var expression = expressionParseCache[expressionText];
16311 if (!expression) {
16312 var delegate = new ASTDelegate();
16313 esprima.parse(expressionText, delegate);
16314 expression = new Expression(delegate);
16315 expressionParseCache[expressionText] = expression;
16316 }
16317 return expression;
16318 }
16319
16320 function Literal(value) {
16321 this.value = value;
16322 this.valueFn_ = undefined;
16323 }
16324
16325 Literal.prototype = {
16326 valueFn: function() {
16327 if (!this.valueFn_) {
16328 var value = this.value;
16329 this.valueFn_ = function() {
16330 return value;
16331 }
16332 }
16333
16334 return this.valueFn_;
16335 }
16336 }
16337
16338 function IdentPath(name) {
16339 this.name = name;
16340 this.path = Path.get(name);
16341 }
16342
16343 IdentPath.prototype = {
16344 valueFn: function() {
16345 if (!this.valueFn_) {
16346 var name = this.name;
16347 var path = this.path;
16348 this.valueFn_ = function(model, observer) {
16349 if (observer)
16350 observer.addPath(model, path);
16351
16352 return path.getValueFrom(model);
16353 }
16354 }
16355
16356 return this.valueFn_;
16357 },
16358
16359 setValue: function(model, newValue) {
16360 return this.path.setValueFrom(model, newValue);
16361 }
16362 };
16363
16364 function MemberExpression(object, property, accessor) {
16365 // convert literal computed property access where literal value is a value
16366 // path to ident dot-access.
16367 if (accessor == '[' &&
16368 property instanceof Literal &&
16369 Path.get(property.value).valid) {
16370 accessor = '.';
16371 property = new IdentPath(property.value);
16372 }
16373
16374 this.dynamicDeps = typeof object == 'function' || object.dynamic;
16375
16376 this.dynamic = typeof property == 'function' ||
16377 property.dynamic ||
16378 accessor == '[';
16379
16380 this.simplePath =
16381 !this.dynamic &&
16382 !this.dynamicDeps &&
16383 property instanceof IdentPath &&
16384 (object instanceof MemberExpression || object instanceof IdentPath);
16385
16386 this.object = this.simplePath ? object : getFn(object);
16387 this.property = accessor == '.' ? property : getFn(property);
16388 }
16389
16390 MemberExpression.prototype = {
16391 get fullPath() {
16392 if (!this.fullPath_) {
16393 var last = this.object instanceof IdentPath ?
16394 this.object.name : this.object.fullPath;
16395 this.fullPath_ = Path.get(last + '.' + this.property.name);
16396 }
16397
16398 return this.fullPath_;
16399 },
16400
16401 valueFn: function() {
16402 if (!this.valueFn_) {
16403 var object = this.object;
16404
16405 if (this.simplePath) {
16406 var path = this.fullPath;
16407
16408 this.valueFn_ = function(model, observer) {
16409 if (observer)
16410 observer.addPath(model, path);
16411
16412 return path.getValueFrom(model);
16413 };
16414 } else if (this.property instanceof IdentPath) {
16415 var path = Path.get(this.property.name);
16416
16417 this.valueFn_ = function(model, observer) {
16418 var context = object(model, observer);
16419
16420 if (observer)
16421 observer.addPath(context, path);
16422
16423 return path.getValueFrom(context);
16424 }
16425 } else {
16426 // Computed property.
16427 var property = this.property;
16428
16429 this.valueFn_ = function(model, observer) {
16430 var context = object(model, observer);
16431 var propName = property(model, observer);
16432 if (observer)
16433 observer.addPath(context, propName);
16434
16435 return context ? context[propName] : undefined;
16436 };
16437 }
16438 }
16439 return this.valueFn_;
16440 },
16441
16442 setValue: function(model, newValue) {
16443 if (this.simplePath) {
16444 this.fullPath.setValueFrom(model, newValue);
16445 return newValue;
16446 }
16447
16448 var object = this.object(model);
16449 var propName = this.property instanceof IdentPath ? this.property.name :
16450 this.property(model);
16451 return object[propName] = newValue;
16452 }
16453 };
16454
16455 function Filter(name, args) {
16456 this.name = name;
16457 this.args = [];
16458 for (var i = 0; i < args.length; i++) {
16459 this.args[i] = getFn(args[i]);
16460 }
16461 }
16462
16463 Filter.prototype = {
16464 transform: function(value, toModelDirection, filterRegistry, model,
16465 observer) {
16466 var fn = filterRegistry[this.name];
16467 var context = model;
16468 if (fn) {
16469 context = undefined;
16470 } else {
16471 fn = context[this.name];
16472 if (!fn) {
16473 console.error('Cannot find filter: ' + this.name);
16474 return;
16475 }
16476 }
16477
16478 // If toModelDirection is falsey, then the "normal" (dom-bound) direction
16479 // is used. Otherwise, it looks for a 'toModel' property function on the
16480 // object.
16481 if (toModelDirection) {
16482 fn = fn.toModel;
16483 } else if (typeof fn.toDOM == 'function') {
16484 fn = fn.toDOM;
16485 }
16486
16487 if (typeof fn != 'function') {
16488 console.error('No ' + (toModelDirection ? 'toModel' : 'toDOM') +
16489 ' found on' + this.name);
16490 return;
16491 }
16492
16493 var args = [value];
16494 for (var i = 0; i < this.args.length; i++) {
16495 args[i + 1] = getFn(this.args[i])(model, observer);
16496 }
16497
16498 return fn.apply(context, args);
16499 }
16500 };
16501
16502 function notImplemented() { throw Error('Not Implemented'); }
16503
16504 var unaryOperators = {
16505 '+': function(v) { return +v; },
16506 '-': function(v) { return -v; },
16507 '!': function(v) { return !v; }
16508 };
16509
16510 var binaryOperators = {
16511 '+': function(l, r) { return l+r; },
16512 '-': function(l, r) { return l-r; },
16513 '*': function(l, r) { return l*r; },
16514 '/': function(l, r) { return l/r; },
16515 '%': function(l, r) { return l%r; },
16516 '<': function(l, r) { return l<r; },
16517 '>': function(l, r) { return l>r; },
16518 '<=': function(l, r) { return l<=r; },
16519 '>=': function(l, r) { return l>=r; },
16520 '==': function(l, r) { return l==r; },
16521 '!=': function(l, r) { return l!=r; },
16522 '===': function(l, r) { return l===r; },
16523 '!==': function(l, r) { return l!==r; },
16524 '&&': function(l, r) { return l&&r; },
16525 '||': function(l, r) { return l||r; },
16526 };
16527
16528 function getFn(arg) {
16529 return typeof arg == 'function' ? arg : arg.valueFn();
16530 }
16531
16532 function ASTDelegate() {
16533 this.expression = null;
16534 this.filters = [];
16535 this.deps = {};
16536 this.currentPath = undefined;
16537 this.scopeIdent = undefined;
16538 this.indexIdent = undefined;
16539 this.dynamicDeps = false;
16540 }
16541
16542 ASTDelegate.prototype = {
16543 createUnaryExpression: function(op, argument) {
16544 if (!unaryOperators[op])
16545 throw Error('Disallowed operator: ' + op);
16546
16547 argument = getFn(argument);
16548
16549 return function(model, observer) {
16550 return unaryOperators[op](argument(model, observer));
16551 };
16552 },
16553
16554 createBinaryExpression: function(op, left, right) {
16555 if (!binaryOperators[op])
16556 throw Error('Disallowed operator: ' + op);
16557
16558 left = getFn(left);
16559 right = getFn(right);
16560
16561 return function(model, observer) {
16562 return binaryOperators[op](left(model, observer),
16563 right(model, observer));
16564 };
16565 },
16566
16567 createConditionalExpression: function(test, consequent, alternate) {
16568 test = getFn(test);
16569 consequent = getFn(consequent);
16570 alternate = getFn(alternate);
16571
16572 return function(model, observer) {
16573 return test(model, observer) ?
16574 consequent(model, observer) : alternate(model, observer);
16575 }
16576 },
16577
16578 createIdentifier: function(name) {
16579 var ident = new IdentPath(name);
16580 ident.type = 'Identifier';
16581 return ident;
16582 },
16583
16584 createMemberExpression: function(accessor, object, property) {
16585 var ex = new MemberExpression(object, property, accessor);
16586 if (ex.dynamicDeps)
16587 this.dynamicDeps = true;
16588 return ex;
16589 },
16590
16591 createLiteral: function(token) {
16592 return new Literal(token.value);
16593 },
16594
16595 createArrayExpression: function(elements) {
16596 for (var i = 0; i < elements.length; i++)
16597 elements[i] = getFn(elements[i]);
16598
16599 return function(model, observer) {
16600 var arr = []
16601 for (var i = 0; i < elements.length; i++)
16602 arr.push(elements[i](model, observer));
16603 return arr;
16604 }
16605 },
16606
16607 createProperty: function(kind, key, value) {
16608 return {
16609 key: key instanceof IdentPath ? key.name : key.value,
16610 value: value
16611 };
16612 },
16613
16614 createObjectExpression: function(properties) {
16615 for (var i = 0; i < properties.length; i++)
16616 properties[i].value = getFn(properties[i].value);
16617
16618 return function(model, observer) {
16619 var obj = {};
16620 for (var i = 0; i < properties.length; i++)
16621 obj[properties[i].key] = properties[i].value(model, observer);
16622 return obj;
16623 }
16624 },
16625
16626 createFilter: function(name, args) {
16627 this.filters.push(new Filter(name, args));
16628 },
16629
16630 createAsExpression: function(expression, scopeIdent) {
16631 this.expression = expression;
16632 this.scopeIdent = scopeIdent;
16633 },
16634
16635 createInExpression: function(scopeIdent, indexIdent, expression) {
16636 this.expression = expression;
16637 this.scopeIdent = scopeIdent;
16638 this.indexIdent = indexIdent;
16639 },
16640
16641 createTopLevel: function(expression) {
16642 this.expression = expression;
16643 },
16644
16645 createThisExpression: notImplemented
16646 }
16647
16648 function ConstantObservable(value) {
16649 this.value_ = value;
16650 }
16651
16652 ConstantObservable.prototype = {
16653 open: function() { return this.value_; },
16654 discardChanges: function() { return this.value_; },
16655 deliver: function() {},
16656 close: function() {},
16657 }
16658
16659 function Expression(delegate) {
16660 this.scopeIdent = delegate.scopeIdent;
16661 this.indexIdent = delegate.indexIdent;
16662
16663 if (!delegate.expression)
16664 throw Error('No expression found.');
16665
16666 this.expression = delegate.expression;
16667 getFn(this.expression); // forces enumeration of path dependencies
16668
16669 this.filters = delegate.filters;
16670 this.dynamicDeps = delegate.dynamicDeps;
16671 }
16672
16673 Expression.prototype = {
16674 getBinding: function(model, filterRegistry, oneTime) {
16675 if (oneTime)
16676 return this.getValue(model, undefined, filterRegistry);
16677
16678 var observer = new CompoundObserver();
16679 this.getValue(model, observer, filterRegistry); // captures deps.
16680 var self = this;
16681
16682 function valueFn() {
16683 if (self.dynamicDeps)
16684 observer.startReset();
16685
16686 var value = self.getValue(model,
16687 self.dynamicDeps ? observer : undefined,
16688 filterRegistry);
16689 if (self.dynamicDeps)
16690 observer.finishReset();
16691
16692 return value;
16693 }
16694
16695 function setValueFn(newValue) {
16696 self.setValue(model, newValue, filterRegistry);
16697 return newValue;
16698 }
16699
16700 return new ObserverTransform(observer, valueFn, setValueFn, true);
16701 },
16702
16703 getValue: function(model, observer, filterRegistry) {
16704 var value = getFn(this.expression)(model, observer);
16705 for (var i = 0; i < this.filters.length; i++) {
16706 value = this.filters[i].transform(value, false, filterRegistry, model,
16707 observer);
16708 }
16709
16710 return value;
16711 },
16712
16713 setValue: function(model, newValue, filterRegistry) {
16714 var count = this.filters ? this.filters.length : 0;
16715 while (count-- > 0) {
16716 newValue = this.filters[count].transform(newValue, true, filterRegistry,
16717 model);
16718 }
16719
16720 if (this.expression.setValue)
16721 return this.expression.setValue(model, newValue);
16722 }
16723 }
16724
16725 /**
16726 * Converts a style property name to a css property name. For example:
16727 * "WebkitUserSelect" to "-webkit-user-select"
16728 */
16729 function convertStylePropertyName(name) {
16730 return String(name).replace(/[A-Z]/g, function(c) {
16731 return '-' + c.toLowerCase();
16732 });
16733 }
16734
16735 function isEventHandler(name) {
16736 return name[0] === 'o' &&
16737 name[1] === 'n' &&
16738 name[2] === '-';
16739 }
16740
16741 var mixedCaseEventTypes = {};
16742 [
16743 'webkitAnimationStart',
16744 'webkitAnimationEnd',
16745 'webkitTransitionEnd',
16746 'DOMFocusOut',
16747 'DOMFocusIn',
16748 'DOMMouseScroll'
16749 ].forEach(function(e) {
16750 mixedCaseEventTypes[e.toLowerCase()] = e;
16751 });
16752
16753 function prepareEventBinding(path, name) {
16754 var eventType = name.substring(3);
16755 eventType = mixedCaseEventTypes[eventType] || eventType;
16756
16757 return function(model, node, oneTime) {
16758 var fn = path.getValueFrom(model);
16759
16760 function handler(e) {
16761 if (!oneTime)
16762 fn = path.getValueFrom(model);
16763
16764 fn.apply(model, [e, e.detail, e.currentTarget]);
16765
16766 if (Platform && typeof Platform.flush == 'function')
16767 Platform.flush();
16768 }
16769
16770 node.addEventListener(eventType, handler);
16771
16772 if (oneTime)
16773 return;
16774
16775 function bindingValue() {
16776 return '{{ ' + path + ' }}';
16777 }
16778
16779 return {
16780 open: bindingValue,
16781 discardChanges: bindingValue,
16782 close: function() {
16783 node.removeEventListener(eventType, handler);
16784 }
16785 };
16786 }
16787 }
16788
16789 function PolymerExpressions() {}
16790
16791 PolymerExpressions.prototype = {
16792 // "built-in" filters
16793 styleObject: function(value) {
16794 var parts = [];
16795 for (var key in value) {
16796 parts.push(convertStylePropertyName(key) + ': ' + value[key]);
16797 }
16798 return parts.join('; ');
16799 },
16800
16801 tokenList: function(value) {
16802 var tokens = [];
16803 for (var key in value) {
16804 if (value[key])
16805 tokens.push(key);
16806 }
16807 return tokens.join(' ');
16808 },
16809
16810 // binding delegate API
16811 prepareInstancePositionChanged: function(template) {
16812 var indexIdent = template.polymerExpressionIndexIdent_;
16813 if (!indexIdent)
16814 return;
16815
16816 return function(templateInstance, index) {
16817 templateInstance.model[indexIdent] = index;
16818 };
16819 },
16820
16821 prepareBinding: function(pathString, name, node) {
16822 if (isEventHandler(name)) {
16823 var path = Path.get(pathString);
16824 if (!path.valid) {
16825 console.error('on-* bindings must be simple path expressions');
16826 return;
16827 }
16828
16829 return prepareEventBinding(path, name);
16830 }
16831
16832 if (Path.get(pathString).valid)
16833 return; // bail out early if pathString is simple path.
16834
16835 return prepareBinding(pathString, name, node, this);
16836 },
16837
16838 prepareInstanceModel: function(template) {
16839 var scopeName = template.polymerExpressionScopeIdent_;
16840 if (!scopeName)
16841 return;
16842
16843 var parentScope = template.templateInstance ?
16844 template.templateInstance.model :
16845 template.model;
16846
16847 return function(model) {
16848 var scope = Object.create(parentScope);
16849 scope[scopeName] = model;
16850 return scope;
16851 };
16852 }
16853 };
16854
16855 global.PolymerExpressions = PolymerExpressions;
16856 if (global.exposeGetExpression)
16857 global.getExpression_ = getExpression;
16858
16859 global.PolymerExpressions.prepareEventBinding = prepareEventBinding;
16860 })(this);
16861
16862 /*
16863 * Copyright 2013 The Polymer Authors. All rights reserved.
16864 * Use of this source code is governed by a BSD-style
16865 * license that can be found in the LICENSE file.
16866 */
16867 (function(scope) {
16868
16869 // inject style sheet
16870 var style = document.createElement('style');
16871 style.textContent = 'template {display: none !important;} /* injected by platfor m.js */';
16872 var head = document.querySelector('head');
16873 head.insertBefore(style, head.firstChild);
16874
16875 // flush (with logging)
16876 var flushing;
16877 function flush() {
16878 if (!flushing) {
16879 flushing = true;
16880 scope.endOfMicrotask(function() {
16881 flushing = false;
16882 logFlags.data && console.group('Platform.flush()');
16883 scope.performMicrotaskCheckpoint();
16884 logFlags.data && console.groupEnd();
16885 });
16886 }
16887 };
16888
16889 // polling dirty checker
16890 var FLUSH_POLL_INTERVAL = 125;
16891 window.addEventListener('WebComponentsReady', function() {
16892 flush();
16893 // flush periodically if platform does not have object observe.
16894 if (!Observer.hasObjectObserve) {
16895 scope.flushPoll = setInterval(flush, FLUSH_POLL_INTERVAL);
16896 }
16897 });
16898
16899 if (window.CustomElements && !CustomElements.useNative) {
16900 var originalImportNode = Document.prototype.importNode;
16901 Document.prototype.importNode = function(node, deep) {
16902 var imported = originalImportNode.call(this, node, deep);
16903 CustomElements.upgradeAll(imported);
16904 return imported;
16905 }
16906 }
16907
16908 // exports
16909 scope.flush = flush;
16910
16911 })(window.Platform);
16912
16913
16914 //# sourceMappingURL=platform.concat.js.map
OLDNEW
« no previous file with comments | « pkg/web_components/lib/platform.js ('k') | pkg/web_components/lib/platform.concat.js.map » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698