OLD | NEW |
| 1 /** |
| 2 * Copyright (c) 2014 The Polymer Project Authors. All rights reserved. |
| 3 * This code may only be used under the BSD style license found at http://polyme
r.github.io/LICENSE.txt |
| 4 * The complete set of authors may be found at http://polymer.github.io/AUTHORS.
txt |
| 5 * The complete set of contributors may be found at http://polymer.github.io/CON
TRIBUTORS.txt |
| 6 * Code distributed by Google as part of the polymer project is also |
| 7 * subject to an additional IP rights grant found at http://polymer.github.io/PA
TENTS.txt |
| 8 */ |
| 9 |
| 10 window.Platform = window.Platform || {}; |
| 11 // prepopulate window.logFlags if necessary |
| 12 window.logFlags = window.logFlags || {}; |
| 13 // process flags |
| 14 (function(scope){ |
| 15 // import |
| 16 var flags = scope.flags || {}; |
| 17 // populate flags from location |
| 18 location.search.slice(1).split('&').forEach(function(o) { |
| 19 o = o.split('='); |
| 20 o[0] && (flags[o[0]] = o[1] || true); |
| 21 }); |
| 22 var entryPoint = document.currentScript || |
| 23 document.querySelector('script[src*="platform.js"]'); |
| 24 if (entryPoint) { |
| 25 var a = entryPoint.attributes; |
| 26 for (var i = 0, n; i < a.length; i++) { |
| 27 n = a[i]; |
| 28 if (n.name !== 'src') { |
| 29 flags[n.name] = n.value || true; |
| 30 } |
| 31 } |
| 32 } |
| 33 if (flags.log) { |
| 34 flags.log.split(',').forEach(function(f) { |
| 35 window.logFlags[f] = true; |
| 36 }); |
| 37 } |
| 38 // If any of these flags match 'native', then force native ShadowDOM; any |
| 39 // other truthy value, or failure to detect native |
| 40 // ShadowDOM, results in polyfill |
| 41 flags.shadow = flags.shadow || flags.shadowdom || flags.polyfill; |
| 42 if (flags.shadow === 'native') { |
| 43 flags.shadow = false; |
| 44 } else { |
| 45 flags.shadow = flags.shadow || !HTMLElement.prototype.createShadowRoot; |
| 46 } |
| 47 |
| 48 if (flags.shadow && document.querySelectorAll('script').length > 1) { |
| 49 console.warn('platform.js is not the first script on the page. ' + |
| 50 'See http://www.polymer-project.org/docs/start/platform.html#setup ' + |
| 51 'for details.'); |
| 52 } |
| 53 |
| 54 // CustomElements polyfill flag |
| 55 if (flags.register) { |
| 56 window.CustomElements = window.CustomElements || {flags: {}}; |
| 57 window.CustomElements.flags.register = flags.register; |
| 58 } |
| 59 |
| 60 if (flags.imports) { |
| 61 window.HTMLImports = window.HTMLImports || {flags: {}}; |
| 62 window.HTMLImports.flags.imports = flags.imports; |
| 63 } |
| 64 |
| 65 // export |
| 66 scope.flags = flags; |
| 67 })(Platform); |
| 68 |
1 /* | 69 /* |
2 * Copyright 2012 The Polymer Authors. All rights reserved. | 70 * Copyright 2012 The Polymer Authors. All rights reserved. |
3 * Use of this source code is governed by a BSD-style | 71 * Use of this source code is governed by a BSD-style |
4 * license that can be found in the LICENSE file. | 72 * license that can be found in the LICENSE file. |
5 */ | 73 */ |
6 | 74 |
7 if (typeof WeakMap === 'undefined') { | 75 if (typeof WeakMap === 'undefined') { |
8 (function() { | 76 (function() { |
9 var defineProperty = Object.defineProperty; | 77 var defineProperty = Object.defineProperty; |
10 var counter = Date.now() % 1e9; | 78 var counter = Date.now() % 1e9; |
(...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
89 | 157 |
90 Object.unobserve(test, callback); | 158 Object.unobserve(test, callback); |
91 Array.unobserve(arr, callback); | 159 Array.unobserve(arr, callback); |
92 | 160 |
93 return true; | 161 return true; |
94 } | 162 } |
95 | 163 |
96 var hasObserve = detectObjectObserve(); | 164 var hasObserve = detectObjectObserve(); |
97 | 165 |
98 function detectEval() { | 166 function detectEval() { |
99 // don't test for eval if document has CSP securityPolicy object and we can
see that | 167 // Don't test for eval if we're running in a Chrome App environment. |
100 // eval is not supported. This avoids an error message in console even when
the exception | 168 // We check for APIs set that only exist in a Chrome App context. |
101 // is caught | 169 if (typeof chrome !== 'undefined' && chrome.app && chrome.app.runtime) { |
102 if (global.document && | |
103 'securityPolicy' in global.document && | |
104 !global.document.securityPolicy.allowsEval) { | |
105 return false; | 170 return false; |
106 } | 171 } |
107 | 172 |
108 try { | 173 try { |
109 var f = new Function('', 'return true;'); | 174 var f = new Function('', 'return true;'); |
110 return f(); | 175 return f(); |
111 } catch (ex) { | 176 } catch (ex) { |
112 return false; | 177 return false; |
113 } | 178 } |
114 } | 179 } |
(...skipping 127 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
242 } | 307 } |
243 return obj; | 308 return obj; |
244 }, | 309 }, |
245 | 310 |
246 iterateObjects: function(obj, observe) { | 311 iterateObjects: function(obj, observe) { |
247 for (var i = 0; i < this.length; i++) { | 312 for (var i = 0; i < this.length; i++) { |
248 if (i) | 313 if (i) |
249 obj = obj[this[i - 1]]; | 314 obj = obj[this[i - 1]]; |
250 if (!isObject(obj)) | 315 if (!isObject(obj)) |
251 return; | 316 return; |
252 observe(obj); | 317 observe(obj, this[0]); |
253 } | 318 } |
254 }, | 319 }, |
255 | 320 |
256 compiledGetValueFromFn: function() { | 321 compiledGetValueFromFn: function() { |
257 var accessors = this.map(function(ident) { | 322 var accessors = this.map(function(ident) { |
258 return isIndex(ident) ? '["' + ident + '"]' : '.' + ident; | 323 return isIndex(ident) ? '["' + ident + '"]' : '.' + ident; |
259 }); | 324 }); |
260 | 325 |
261 var str = ''; | 326 var str = ''; |
262 var pathString = 'obj'; | 327 var pathString = 'obj'; |
(...skipping 168 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
431 discardRecords = false; | 496 discardRecords = false; |
432 }, | 497 }, |
433 close: function() { | 498 close: function() { |
434 observer = undefined; | 499 observer = undefined; |
435 Object.unobserve(object, callback); | 500 Object.unobserve(object, callback); |
436 observedObjectCache.push(this); | 501 observedObjectCache.push(this); |
437 } | 502 } |
438 }; | 503 }; |
439 } | 504 } |
440 | 505 |
| 506 /* |
| 507 * The observedSet abstraction is a perf optimization which reduces the total |
| 508 * number of Object.observe observations of a set of objects. The idea is that |
| 509 * groups of Observers will have some object dependencies in common and this |
| 510 * observed set ensures that each object in the transitive closure of |
| 511 * dependencies is only observed once. The observedSet acts as a write barrier |
| 512 * such that whenever any change comes through, all Observers are checked for |
| 513 * changed values. |
| 514 * |
| 515 * Note that this optimization is explicitly moving work from setup-time to |
| 516 * change-time. |
| 517 * |
| 518 * TODO(rafaelw): Implement "garbage collection". In order to move work off |
| 519 * the critical path, when Observers are closed, their observed objects are |
| 520 * not Object.unobserve(d). As a result, it's possible that if the observedSet |
| 521 * is kept open, but some Observers have been closed, it could cause "leaks" |
| 522 * (prevent otherwise collectable objects from being collected). At some |
| 523 * point, we should implement incremental "gc" which keeps a list of |
| 524 * observedSets which may need clean-up and does small amounts of cleanup on a |
| 525 * timeout until all is clean. |
| 526 */ |
| 527 |
441 function getObservedObject(observer, object, arrayObserve) { | 528 function getObservedObject(observer, object, arrayObserve) { |
442 var dir = observedObjectCache.pop() || newObservedObject(); | 529 var dir = observedObjectCache.pop() || newObservedObject(); |
443 dir.open(observer); | 530 dir.open(observer); |
444 dir.observe(object, arrayObserve); | 531 dir.observe(object, arrayObserve); |
445 return dir; | 532 return dir; |
446 } | 533 } |
447 | 534 |
448 var emptyArray = []; | |
449 var observedSetCache = []; | 535 var observedSetCache = []; |
450 | 536 |
451 function newObservedSet() { | 537 function newObservedSet() { |
| 538 var observerCount = 0; |
452 var observers = []; | 539 var observers = []; |
453 var observerCount = 0; | |
454 var objects = []; | 540 var objects = []; |
455 var toRemove = emptyArray; | 541 var rootObj; |
456 var resetNeeded = false; | 542 var rootObjProps; |
457 var resetScheduled = false; | |
458 | 543 |
459 function observe(obj) { | 544 function observe(obj, prop) { |
460 if (!obj) | 545 if (!obj) |
461 return; | 546 return; |
462 | 547 |
463 var index = toRemove.indexOf(obj); | 548 if (obj === rootObj) |
464 if (index >= 0) { | 549 rootObjProps[prop] = true; |
465 toRemove[index] = undefined; | 550 |
466 objects.push(obj); | 551 if (objects.indexOf(obj) < 0) { |
467 } else if (objects.indexOf(obj) < 0) { | |
468 objects.push(obj); | 552 objects.push(obj); |
469 Object.observe(obj, callback); | 553 Object.observe(obj, callback); |
470 } | 554 } |
471 | 555 |
472 observe(Object.getPrototypeOf(obj)); | 556 observe(Object.getPrototypeOf(obj), prop); |
473 } | 557 } |
474 | 558 |
475 function reset() { | 559 function allRootObjNonObservedProps(recs) { |
476 var objs = toRemove === emptyArray ? [] : toRemove; | 560 for (var i = 0; i < recs.length; i++) { |
477 toRemove = objects; | 561 var rec = recs[i]; |
478 objects = objs; | 562 if (rec.object !== rootObj || |
| 563 rootObjProps[rec.name] || |
| 564 rec.type === 'setPrototype') { |
| 565 return false; |
| 566 } |
| 567 } |
| 568 return true; |
| 569 } |
| 570 |
| 571 function callback(recs) { |
| 572 if (allRootObjNonObservedProps(recs)) |
| 573 return; |
479 | 574 |
480 var observer; | 575 var observer; |
481 for (var id in observers) { | 576 for (var i = 0; i < observers.length; i++) { |
482 observer = observers[id]; | 577 observer = observers[i]; |
483 if (!observer || observer.state_ != OPENED) | 578 if (observer.state_ == OPENED) { |
484 continue; | 579 observer.iterateObjects_(observe); |
485 | 580 } |
486 observer.iterateObjects_(observe); | |
487 } | 581 } |
488 | 582 |
489 for (var i = 0; i < toRemove.length; i++) { | 583 for (var i = 0; i < observers.length; i++) { |
490 var obj = toRemove[i]; | 584 observer = observers[i]; |
491 if (obj) | 585 if (observer.state_ == OPENED) { |
492 Object.unobserve(obj, callback); | 586 observer.check_(); |
493 } | 587 } |
494 | |
495 toRemove.length = 0; | |
496 } | |
497 | |
498 function scheduledReset() { | |
499 resetScheduled = false; | |
500 if (!resetNeeded) | |
501 return; | |
502 | |
503 reset(); | |
504 } | |
505 | |
506 function scheduleReset() { | |
507 if (resetScheduled) | |
508 return; | |
509 | |
510 resetNeeded = true; | |
511 resetScheduled = true; | |
512 runEOM(scheduledReset); | |
513 } | |
514 | |
515 function callback() { | |
516 reset(); | |
517 | |
518 var observer; | |
519 | |
520 for (var id in observers) { | |
521 observer = observers[id]; | |
522 if (!observer || observer.state_ != OPENED) | |
523 continue; | |
524 | |
525 observer.check_(); | |
526 } | 588 } |
527 } | 589 } |
528 | 590 |
529 var record = { | 591 var record = { |
530 object: undefined, | 592 object: undefined, |
531 objects: objects, | 593 objects: objects, |
532 open: function(obs) { | 594 open: function(obs, object) { |
533 observers[obs.id_] = obs; | 595 if (!rootObj) { |
| 596 rootObj = object; |
| 597 rootObjProps = {}; |
| 598 } |
| 599 |
| 600 observers.push(obs); |
534 observerCount++; | 601 observerCount++; |
535 obs.iterateObjects_(observe); | 602 obs.iterateObjects_(observe); |
536 }, | 603 }, |
537 close: function(obs) { | 604 close: function(obs) { |
538 var anyLeft = false; | |
539 | |
540 observers[obs.id_] = undefined; | |
541 observerCount--; | 605 observerCount--; |
542 | 606 if (observerCount > 0) { |
543 if (observerCount) { | |
544 scheduleReset(); | |
545 return; | 607 return; |
546 } | 608 } |
547 resetNeeded = false; | |
548 | 609 |
549 for (var i = 0; i < objects.length; i++) { | 610 for (var i = 0; i < objects.length; i++) { |
550 Object.unobserve(objects[i], callback); | 611 Object.unobserve(objects[i], callback); |
551 Observer.unobservedCount++; | 612 Observer.unobservedCount++; |
552 } | 613 } |
553 | 614 |
554 observers.length = 0; | 615 observers.length = 0; |
555 objects.length = 0; | 616 objects.length = 0; |
| 617 rootObj = undefined; |
| 618 rootObjProps = undefined; |
556 observedSetCache.push(this); | 619 observedSetCache.push(this); |
557 }, | 620 } |
558 reset: scheduleReset | |
559 }; | 621 }; |
560 | 622 |
561 return record; | 623 return record; |
562 } | 624 } |
563 | 625 |
564 var lastObservedSet; | 626 var lastObservedSet; |
565 | 627 |
566 function getObservedSet(observer, obj) { | 628 function getObservedSet(observer, obj) { |
567 if (!lastObservedSet || lastObservedSet.object !== obj) { | 629 if (!lastObservedSet || lastObservedSet.object !== obj) { |
568 lastObservedSet = observedSetCache.pop() || newObservedSet(); | 630 lastObservedSet = observedSetCache.pop() || newObservedSet(); |
569 lastObservedSet.object = obj; | 631 lastObservedSet.object = obj; |
570 } | 632 } |
571 lastObservedSet.open(observer); | 633 lastObservedSet.open(observer, obj); |
572 return lastObservedSet; | 634 return lastObservedSet; |
573 } | 635 } |
574 | 636 |
575 var UNOPENED = 0; | 637 var UNOPENED = 0; |
576 var OPENED = 1; | 638 var OPENED = 1; |
577 var CLOSED = 2; | 639 var CLOSED = 2; |
578 var RESETTING = 3; | 640 var RESETTING = 3; |
579 | 641 |
580 var nextObserverId = 1; | 642 var nextObserverId = 1; |
581 | 643 |
582 function Observer() { | 644 function Observer() { |
583 this.state_ = UNOPENED; | 645 this.state_ = UNOPENED; |
584 this.callback_ = undefined; | 646 this.callback_ = undefined; |
585 this.target_ = undefined; // TODO(rafaelw): Should be WeakRef | 647 this.target_ = undefined; // TODO(rafaelw): Should be WeakRef |
586 this.directObserver_ = undefined; | 648 this.directObserver_ = undefined; |
587 this.value_ = undefined; | 649 this.value_ = undefined; |
588 this.id_ = nextObserverId++; | 650 this.id_ = nextObserverId++; |
589 } | 651 } |
590 | 652 |
591 Observer.prototype = { | 653 Observer.prototype = { |
592 open: function(callback, target) { | 654 open: function(callback, target) { |
593 if (this.state_ != UNOPENED) | 655 if (this.state_ != UNOPENED) |
594 throw Error('Observer has already been opened.'); | 656 throw Error('Observer has already been opened.'); |
595 | 657 |
596 addToAll(this); | 658 addToAll(this); |
597 this.callback_ = callback; | 659 this.callback_ = callback; |
598 this.target_ = target; | 660 this.target_ = target; |
| 661 this.connect_(); |
599 this.state_ = OPENED; | 662 this.state_ = OPENED; |
600 this.connect_(); | |
601 return this.value_; | 663 return this.value_; |
602 }, | 664 }, |
603 | 665 |
604 close: function() { | 666 close: function() { |
605 if (this.state_ != OPENED) | 667 if (this.state_ != OPENED) |
606 return; | 668 return; |
607 | 669 |
608 removeFromAll(this); | 670 removeFromAll(this); |
609 this.state_ = CLOSED; | |
610 this.disconnect_(); | 671 this.disconnect_(); |
611 this.value_ = undefined; | 672 this.value_ = undefined; |
612 this.callback_ = undefined; | 673 this.callback_ = undefined; |
613 this.target_ = undefined; | 674 this.target_ = undefined; |
| 675 this.state_ = CLOSED; |
614 }, | 676 }, |
615 | 677 |
616 deliver: function() { | 678 deliver: function() { |
617 if (this.state_ != OPENED) | 679 if (this.state_ != OPENED) |
618 return; | 680 return; |
619 | 681 |
620 dirtyCheck(this); | 682 dirtyCheck(this); |
621 }, | 683 }, |
622 | 684 |
623 report_: function(changes) { | 685 report_: function(changes) { |
(...skipping 235 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
859 } | 921 } |
860 | 922 |
861 Array.prototype.splice.apply(previous, spliceArgs); | 923 Array.prototype.splice.apply(previous, spliceArgs); |
862 }); | 924 }); |
863 }; | 925 }; |
864 | 926 |
865 function PathObserver(object, path) { | 927 function PathObserver(object, path) { |
866 Observer.call(this); | 928 Observer.call(this); |
867 | 929 |
868 this.object_ = object; | 930 this.object_ = object; |
869 this.path_ = path instanceof Path ? path : getPath(path); | 931 this.path_ = getPath(path); |
870 this.directObserver_ = undefined; | 932 this.directObserver_ = undefined; |
871 } | 933 } |
872 | 934 |
873 PathObserver.prototype = createObject({ | 935 PathObserver.prototype = createObject({ |
874 __proto__: Observer.prototype, | 936 __proto__: Observer.prototype, |
875 | 937 |
876 connect_: function() { | 938 connect_: function() { |
877 if (hasObserve) | 939 if (hasObserve) |
878 this.directObserver_ = getObservedSet(this, this.object_); | 940 this.directObserver_ = getObservedSet(this, this.object_); |
879 | 941 |
(...skipping 22 matching lines...) Expand all Loading... |
902 this.report_([this.value_, oldValue]); | 964 this.report_([this.value_, oldValue]); |
903 return true; | 965 return true; |
904 }, | 966 }, |
905 | 967 |
906 setValue: function(newValue) { | 968 setValue: function(newValue) { |
907 if (this.path_) | 969 if (this.path_) |
908 this.path_.setValueFrom(this.object_, newValue); | 970 this.path_.setValueFrom(this.object_, newValue); |
909 } | 971 } |
910 }); | 972 }); |
911 | 973 |
912 function CompoundObserver() { | 974 function CompoundObserver(reportChangesOnOpen) { |
913 Observer.call(this); | 975 Observer.call(this); |
914 | 976 |
| 977 this.reportChangesOnOpen_ = reportChangesOnOpen; |
915 this.value_ = []; | 978 this.value_ = []; |
916 this.directObserver_ = undefined; | 979 this.directObserver_ = undefined; |
917 this.observed_ = []; | 980 this.observed_ = []; |
918 } | 981 } |
919 | 982 |
920 var observerSentinel = {}; | 983 var observerSentinel = {}; |
921 | 984 |
922 CompoundObserver.prototype = createObject({ | 985 CompoundObserver.prototype = createObject({ |
923 __proto__: Observer.prototype, | 986 __proto__: Observer.prototype, |
924 | 987 |
925 connect_: function() { | 988 connect_: function() { |
926 this.check_(undefined, true); | 989 if (hasObserve) { |
| 990 var object; |
| 991 var needsDirectObserver = false; |
| 992 for (var i = 0; i < this.observed_.length; i += 2) { |
| 993 object = this.observed_[i] |
| 994 if (object !== observerSentinel) { |
| 995 needsDirectObserver = true; |
| 996 break; |
| 997 } |
| 998 } |
927 | 999 |
928 if (!hasObserve) | 1000 if (needsDirectObserver) |
929 return; | 1001 this.directObserver_ = getObservedSet(this, object); |
930 | |
931 var object; | |
932 var needsDirectObserver = false; | |
933 for (var i = 0; i < this.observed_.length; i += 2) { | |
934 object = this.observed_[i] | |
935 if (object !== observerSentinel) { | |
936 needsDirectObserver = true; | |
937 break; | |
938 } | |
939 } | 1002 } |
940 | 1003 |
941 if (this.directObserver_) { | 1004 this.check_(undefined, !this.reportChangesOnOpen_); |
942 if (needsDirectObserver) { | |
943 this.directObserver_.reset(); | |
944 return; | |
945 } | |
946 this.directObserver_.close(); | |
947 this.directObserver_ = undefined; | |
948 return; | |
949 } | |
950 | |
951 if (needsDirectObserver) | |
952 this.directObserver_ = getObservedSet(this, object); | |
953 }, | 1005 }, |
954 | 1006 |
955 closeObservers_: function() { | 1007 disconnect_: function() { |
956 for (var i = 0; i < this.observed_.length; i += 2) { | 1008 for (var i = 0; i < this.observed_.length; i += 2) { |
957 if (this.observed_[i] === observerSentinel) | 1009 if (this.observed_[i] === observerSentinel) |
958 this.observed_[i + 1].close(); | 1010 this.observed_[i + 1].close(); |
959 } | 1011 } |
960 this.observed_.length = 0; | 1012 this.observed_.length = 0; |
961 }, | 1013 this.value_.length = 0; |
962 | |
963 disconnect_: function() { | |
964 this.value_ = undefined; | |
965 | 1014 |
966 if (this.directObserver_) { | 1015 if (this.directObserver_) { |
967 this.directObserver_.close(this); | 1016 this.directObserver_.close(this); |
968 this.directObserver_ = undefined; | 1017 this.directObserver_ = undefined; |
969 } | 1018 } |
970 | |
971 this.closeObservers_(); | |
972 }, | 1019 }, |
973 | 1020 |
974 addPath: function(object, path) { | 1021 addPath: function(object, path) { |
975 if (this.state_ != UNOPENED && this.state_ != RESETTING) | 1022 if (this.state_ != UNOPENED && this.state_ != RESETTING) |
976 throw Error('Cannot add paths once started.'); | 1023 throw Error('Cannot add paths once started.'); |
977 | 1024 |
978 this.observed_.push(object, path instanceof Path ? path : getPath(path)); | 1025 var path = getPath(path); |
| 1026 this.observed_.push(object, path); |
| 1027 if (!this.reportChangesOnOpen_) |
| 1028 return; |
| 1029 var index = this.observed_.length / 2 - 1; |
| 1030 this.value_[index] = path.getValueFrom(object); |
979 }, | 1031 }, |
980 | 1032 |
981 addObserver: function(observer) { | 1033 addObserver: function(observer) { |
982 if (this.state_ != UNOPENED && this.state_ != RESETTING) | 1034 if (this.state_ != UNOPENED && this.state_ != RESETTING) |
983 throw Error('Cannot add observers once started.'); | 1035 throw Error('Cannot add observers once started.'); |
984 | 1036 |
985 observer.open(this.deliver, this); | |
986 this.observed_.push(observerSentinel, observer); | 1037 this.observed_.push(observerSentinel, observer); |
| 1038 if (!this.reportChangesOnOpen_) |
| 1039 return; |
| 1040 var index = this.observed_.length / 2 - 1; |
| 1041 this.value_[index] = observer.open(this.deliver, this); |
987 }, | 1042 }, |
988 | 1043 |
989 startReset: function() { | 1044 startReset: function() { |
990 if (this.state_ != OPENED) | 1045 if (this.state_ != OPENED) |
991 throw Error('Can only reset while open'); | 1046 throw Error('Can only reset while open'); |
992 | 1047 |
993 this.state_ = RESETTING; | 1048 this.state_ = RESETTING; |
994 this.closeObservers_(); | 1049 this.disconnect_(); |
995 }, | 1050 }, |
996 | 1051 |
997 finishReset: function() { | 1052 finishReset: function() { |
998 if (this.state_ != RESETTING) | 1053 if (this.state_ != RESETTING) |
999 throw Error('Can only finishReset after startReset'); | 1054 throw Error('Can only finishReset after startReset'); |
1000 this.state_ = OPENED; | 1055 this.state_ = OPENED; |
1001 this.connect_(); | 1056 this.connect_(); |
1002 | 1057 |
1003 return this.value_; | 1058 return this.value_; |
1004 }, | 1059 }, |
1005 | 1060 |
1006 iterateObjects_: function(observe) { | 1061 iterateObjects_: function(observe) { |
1007 var object; | 1062 var object; |
1008 for (var i = 0; i < this.observed_.length; i += 2) { | 1063 for (var i = 0; i < this.observed_.length; i += 2) { |
1009 object = this.observed_[i] | 1064 object = this.observed_[i] |
1010 if (object !== observerSentinel) | 1065 if (object !== observerSentinel) |
1011 this.observed_[i + 1].iterateObjects(object, observe) | 1066 this.observed_[i + 1].iterateObjects(object, observe) |
1012 } | 1067 } |
1013 }, | 1068 }, |
1014 | 1069 |
1015 check_: function(changeRecords, skipChanges) { | 1070 check_: function(changeRecords, skipChanges) { |
1016 var oldValues; | 1071 var oldValues; |
1017 for (var i = 0; i < this.observed_.length; i += 2) { | 1072 for (var i = 0; i < this.observed_.length; i += 2) { |
1018 var pathOrObserver = this.observed_[i+1]; | |
1019 var object = this.observed_[i]; | 1073 var object = this.observed_[i]; |
1020 var value = object === observerSentinel ? | 1074 var path = this.observed_[i+1]; |
1021 pathOrObserver.discardChanges() : | 1075 var value; |
1022 pathOrObserver.getValueFrom(object) | 1076 if (object === observerSentinel) { |
| 1077 var observable = path; |
| 1078 value = this.state_ === UNOPENED ? |
| 1079 observable.open(this.deliver, this) : |
| 1080 observable.discardChanges(); |
| 1081 } else { |
| 1082 value = path.getValueFrom(object); |
| 1083 } |
1023 | 1084 |
1024 if (skipChanges) { | 1085 if (skipChanges) { |
1025 this.value_[i / 2] = value; | 1086 this.value_[i / 2] = value; |
1026 continue; | 1087 continue; |
1027 } | 1088 } |
1028 | 1089 |
1029 if (areSameValue(value, this.value_[i / 2])) | 1090 if (areSameValue(value, this.value_[i / 2])) |
1030 continue; | 1091 continue; |
1031 | 1092 |
1032 oldValues = oldValues || []; | 1093 oldValues = oldValues || []; |
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1103 this.setValueFn_ = undefined; | 1164 this.setValueFn_ = undefined; |
1104 } | 1165 } |
1105 } | 1166 } |
1106 | 1167 |
1107 var expectedRecordTypes = { | 1168 var expectedRecordTypes = { |
1108 add: true, | 1169 add: true, |
1109 update: true, | 1170 update: true, |
1110 delete: true | 1171 delete: true |
1111 }; | 1172 }; |
1112 | 1173 |
1113 function notifyFunction(object, name) { | 1174 var updateRecord = { |
1114 if (typeof Object.observe !== 'function') | 1175 object: undefined, |
| 1176 type: 'update', |
| 1177 name: undefined, |
| 1178 oldValue: undefined |
| 1179 }; |
| 1180 |
| 1181 function notify(object, name, value, oldValue) { |
| 1182 if (areSameValue(value, oldValue)) |
1115 return; | 1183 return; |
1116 | 1184 |
1117 var notifier = Object.getNotifier(object); | 1185 // TODO(rafaelw): Hack hack hack. This entire code really needs to move |
1118 return function(type, oldValue) { | 1186 // out of observe-js into polymer. |
1119 var changeRecord = { | 1187 if (typeof object.propertyChanged_ == 'function') |
1120 object: object, | 1188 object.propertyChanged_(name, value, oldValue); |
1121 type: type, | 1189 |
1122 name: name | 1190 if (!hasObserve) |
1123 }; | 1191 return; |
1124 if (arguments.length === 2) | 1192 |
1125 changeRecord.oldValue = oldValue; | 1193 var notifier = object.notifier_; |
1126 notifier.notify(changeRecord); | 1194 if (!notifier) |
1127 } | 1195 notifier = object.notifier_ = Object.getNotifier(object); |
| 1196 |
| 1197 updateRecord.object = object; |
| 1198 updateRecord.name = name; |
| 1199 updateRecord.oldValue = oldValue; |
| 1200 |
| 1201 notifier.notify(updateRecord); |
1128 } | 1202 } |
1129 | 1203 |
1130 Observer.defineComputedProperty = function(target, name, observable) { | 1204 Observer.createBindablePrototypeAccessor = function(proto, name) { |
1131 var notify = notifyFunction(target, name); | 1205 var privateName = name + '_'; |
1132 var value = observable.open(function(newValue, oldValue) { | 1206 var privateObservable = name + 'Observable_'; |
1133 value = newValue; | |
1134 if (notify) | |
1135 notify('update', oldValue); | |
1136 }); | |
1137 | 1207 |
1138 Object.defineProperty(target, name, { | 1208 proto[privateName] = proto[name]; |
| 1209 |
| 1210 Object.defineProperty(proto, name, { |
1139 get: function() { | 1211 get: function() { |
1140 observable.deliver(); | 1212 var observable = this[privateObservable]; |
| 1213 if (observable) |
| 1214 observable.deliver(); |
| 1215 |
| 1216 return this[privateName]; |
| 1217 }, |
| 1218 set: function(value) { |
| 1219 var observable = this[privateObservable]; |
| 1220 if (observable) { |
| 1221 observable.setValue(value); |
| 1222 return; |
| 1223 } |
| 1224 |
| 1225 var oldValue = this[privateName]; |
| 1226 this[privateName] = value; |
| 1227 notify(this, name, value, oldValue); |
| 1228 |
1141 return value; | 1229 return value; |
1142 }, | 1230 }, |
1143 set: function(newValue) { | |
1144 observable.setValue(newValue); | |
1145 return newValue; | |
1146 }, | |
1147 configurable: true | 1231 configurable: true |
1148 }); | 1232 }); |
| 1233 } |
| 1234 |
| 1235 Observer.bindToInstance = function(instance, name, observable, resolveFn) { |
| 1236 var privateName = name + '_'; |
| 1237 var privateObservable = name + 'Observable_'; |
| 1238 |
| 1239 instance[privateObservable] = observable; |
| 1240 var oldValue = instance[privateName]; |
| 1241 var value = observable.open(function(value, oldValue) { |
| 1242 instance[privateName] = value; |
| 1243 notify(instance, name, value, oldValue); |
| 1244 }); |
| 1245 |
| 1246 if (resolveFn && !areSameValue(oldValue, value)) { |
| 1247 var resolvedValue = resolveFn(oldValue, value); |
| 1248 if (!areSameValue(value, resolvedValue)) { |
| 1249 value = resolvedValue; |
| 1250 if (observable.setValue) |
| 1251 observable.setValue(value); |
| 1252 } |
| 1253 } |
| 1254 |
| 1255 instance[privateName] = value; |
| 1256 notify(instance, name, value, oldValue); |
1149 | 1257 |
1150 return { | 1258 return { |
1151 close: function() { | 1259 close: function() { |
1152 observable.close(); | 1260 observable.close(); |
1153 Object.defineProperty(target, name, { | 1261 instance[privateObservable] = undefined; |
1154 value: value, | |
1155 writable: true, | |
1156 configurable: true | |
1157 }); | |
1158 } | 1262 } |
1159 }; | 1263 }; |
1160 } | 1264 } |
1161 | 1265 |
1162 function diffObjectFromChangeRecords(object, changeRecords, oldValues) { | 1266 function diffObjectFromChangeRecords(object, changeRecords, oldValues) { |
1163 var added = {}; | 1267 var added = {}; |
1164 var removed = {}; | 1268 var removed = {}; |
1165 | 1269 |
1166 for (var i = 0; i < changeRecords.length; i++) { | 1270 for (var i = 0; i < changeRecords.length; i++) { |
1167 var record = changeRecords[i]; | 1271 var record = changeRecords[i]; |
(...skipping 441 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1609 | 1713 |
1610 splices = splices.concat(calcSplices(array, splice.index, splice.index + s
plice.addedCount, | 1714 splices = splices.concat(calcSplices(array, splice.index, splice.index + s
plice.addedCount, |
1611 splice.removed, 0, splice.removed.len
gth)); | 1715 splice.removed, 0, splice.removed.len
gth)); |
1612 }); | 1716 }); |
1613 | 1717 |
1614 return splices; | 1718 return splices; |
1615 } | 1719 } |
1616 | 1720 |
1617 global.Observer = Observer; | 1721 global.Observer = Observer; |
1618 global.Observer.runEOM_ = runEOM; | 1722 global.Observer.runEOM_ = runEOM; |
| 1723 global.Observer.observerSentinel_ = observerSentinel; // for testing. |
1619 global.Observer.hasObjectObserve = hasObserve; | 1724 global.Observer.hasObjectObserve = hasObserve; |
1620 global.ArrayObserver = ArrayObserver; | 1725 global.ArrayObserver = ArrayObserver; |
1621 global.ArrayObserver.calculateSplices = function(current, previous) { | 1726 global.ArrayObserver.calculateSplices = function(current, previous) { |
1622 return arraySplice.calculateSplices(current, previous); | 1727 return arraySplice.calculateSplices(current, previous); |
1623 }; | 1728 }; |
1624 | 1729 |
1625 global.ArraySplice = ArraySplice; | 1730 global.ArraySplice = ArraySplice; |
1626 global.ObjectObserver = ObjectObserver; | 1731 global.ObjectObserver = ObjectObserver; |
1627 global.PathObserver = PathObserver; | 1732 global.PathObserver = PathObserver; |
1628 global.CompoundObserver = CompoundObserver; | 1733 global.CompoundObserver = CompoundObserver; |
1629 global.Path = Path; | 1734 global.Path = Path; |
1630 global.ObserverTransform = ObserverTransform; | 1735 global.ObserverTransform = ObserverTransform; |
1631 })(typeof global !== 'undefined' && global && typeof module !== 'undefined' && m
odule ? global : this || window); | 1736 })(typeof global !== 'undefined' && global && typeof module !== 'undefined' && m
odule ? global : this || window); |
1632 | 1737 |
1633 // prepoulate window.Platform.flags for default controls | |
1634 window.Platform = window.Platform || {}; | |
1635 // prepopulate window.logFlags if necessary | |
1636 window.logFlags = window.logFlags || {}; | |
1637 // process flags | |
1638 (function(scope){ | |
1639 // import | |
1640 var flags = scope.flags || {}; | |
1641 // populate flags from location | |
1642 location.search.slice(1).split('&').forEach(function(o) { | |
1643 o = o.split('='); | |
1644 o[0] && (flags[o[0]] = o[1] || true); | |
1645 }); | |
1646 var entryPoint = document.currentScript || | |
1647 document.querySelector('script[src*="platform.js"]'); | |
1648 if (entryPoint) { | |
1649 var a = entryPoint.attributes; | |
1650 for (var i = 0, n; i < a.length; i++) { | |
1651 n = a[i]; | |
1652 if (n.name !== 'src') { | |
1653 flags[n.name] = n.value || true; | |
1654 } | |
1655 } | |
1656 } | |
1657 if (flags.log) { | |
1658 flags.log.split(',').forEach(function(f) { | |
1659 window.logFlags[f] = true; | |
1660 }); | |
1661 } | |
1662 // If any of these flags match 'native', then force native ShadowDOM; any | |
1663 // other truthy value, or failure to detect native | |
1664 // ShadowDOM, results in polyfill | |
1665 flags.shadow = flags.shadow || flags.shadowdom || flags.polyfill; | |
1666 if (flags.shadow === 'native') { | |
1667 flags.shadow = false; | |
1668 } else { | |
1669 flags.shadow = flags.shadow || !HTMLElement.prototype.createShadowRoot; | |
1670 } | |
1671 | |
1672 if (flags.shadow && document.querySelectorAll('script').length > 1) { | |
1673 console.warn('platform.js is not the first script on the page. ' + | |
1674 'See http://www.polymer-project.org/docs/start/platform.html#setup ' + | |
1675 'for details.'); | |
1676 } | |
1677 | |
1678 // CustomElements polyfill flag | |
1679 if (flags.register) { | |
1680 window.CustomElements = window.CustomElements || {flags: {}}; | |
1681 window.CustomElements.flags.register = flags.register; | |
1682 } | |
1683 | |
1684 if (flags.imports) { | |
1685 window.HTMLImports = window.HTMLImports || {flags: {}}; | |
1686 window.HTMLImports.flags.imports = flags.imports; | |
1687 } | |
1688 | |
1689 // export | |
1690 scope.flags = flags; | |
1691 })(Platform); | |
1692 | |
1693 // select ShadowDOM impl | 1738 // select ShadowDOM impl |
1694 if (Platform.flags.shadow) { | 1739 if (Platform.flags.shadow) { |
1695 | 1740 |
1696 // Copyright 2012 The Polymer Authors. All rights reserved. | 1741 // Copyright 2012 The Polymer Authors. All rights reserved. |
1697 // Use of this source code is goverened by a BSD-style | 1742 // Use of this source code is goverened by a BSD-style |
1698 // license that can be found in the LICENSE file. | 1743 // license that can be found in the LICENSE file. |
1699 | 1744 |
1700 window.ShadowDOMPolyfill = {}; | 1745 window.ShadowDOMPolyfill = {}; |
1701 | 1746 |
1702 (function(scope) { | 1747 (function(scope) { |
1703 'use strict'; | 1748 'use strict'; |
1704 | 1749 |
1705 var constructorTable = new WeakMap(); | 1750 var constructorTable = new WeakMap(); |
1706 var nativePrototypeTable = new WeakMap(); | 1751 var nativePrototypeTable = new WeakMap(); |
1707 var wrappers = Object.create(null); | 1752 var wrappers = Object.create(null); |
1708 | 1753 |
1709 // Don't test for eval if document has CSP securityPolicy object and we can | 1754 function detectEval() { |
1710 // see that eval is not supported. This avoids an error message in console | 1755 // Don't test for eval if we're running in a Chrome App environment. |
1711 // even when the exception is caught | 1756 // We check for APIs set that only exist in a Chrome App context. |
1712 var hasEval = !('securityPolicy' in document) || | 1757 if (typeof chrome !== 'undefined' && chrome.app && chrome.app.runtime) { |
1713 document.securityPolicy.allowsEval; | 1758 return false; |
1714 if (hasEval) { | 1759 } |
| 1760 |
1715 try { | 1761 try { |
1716 var f = new Function('', 'return true;'); | 1762 var f = new Function('return true;'); |
1717 hasEval = f(); | 1763 return f(); |
1718 } catch (ex) { | 1764 } catch (ex) { |
1719 hasEval = false; | 1765 return false; |
1720 } | 1766 } |
1721 } | 1767 } |
1722 | 1768 |
| 1769 var hasEval = detectEval(); |
| 1770 |
1723 function assert(b) { | 1771 function assert(b) { |
1724 if (!b) | 1772 if (!b) |
1725 throw new Error('Assertion failed'); | 1773 throw new Error('Assertion failed'); |
1726 }; | 1774 }; |
1727 | 1775 |
1728 var defineProperty = Object.defineProperty; | 1776 var defineProperty = Object.defineProperty; |
1729 var getOwnPropertyNames = Object.getOwnPropertyNames; | 1777 var getOwnPropertyNames = Object.getOwnPropertyNames; |
1730 var getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor; | 1778 var getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor; |
1731 | 1779 |
1732 function mixin(to, from) { | 1780 function mixin(to, from) { |
1733 getOwnPropertyNames(from).forEach(function(name) { | 1781 var names = getOwnPropertyNames(from); |
| 1782 for (var i = 0; i < names.length; i++) { |
| 1783 var name = names[i]; |
1734 defineProperty(to, name, getOwnPropertyDescriptor(from, name)); | 1784 defineProperty(to, name, getOwnPropertyDescriptor(from, name)); |
1735 }); | 1785 } |
1736 return to; | 1786 return to; |
1737 }; | 1787 }; |
1738 | 1788 |
1739 function mixinStatics(to, from) { | 1789 function mixinStatics(to, from) { |
1740 getOwnPropertyNames(from).forEach(function(name) { | 1790 var names = getOwnPropertyNames(from); |
| 1791 for (var i = 0; i < names.length; i++) { |
| 1792 var name = names[i]; |
1741 switch (name) { | 1793 switch (name) { |
1742 case 'arguments': | 1794 case 'arguments': |
1743 case 'caller': | 1795 case 'caller': |
1744 case 'length': | 1796 case 'length': |
1745 case 'name': | 1797 case 'name': |
1746 case 'prototype': | 1798 case 'prototype': |
1747 case 'toString': | 1799 case 'toString': |
1748 return; | 1800 continue; |
1749 } | 1801 } |
1750 defineProperty(to, name, getOwnPropertyDescriptor(from, name)); | 1802 defineProperty(to, name, getOwnPropertyDescriptor(from, name)); |
1751 }); | 1803 } |
1752 return to; | 1804 return to; |
1753 }; | 1805 }; |
1754 | 1806 |
1755 function oneOf(object, propertyNames) { | 1807 function oneOf(object, propertyNames) { |
1756 for (var i = 0; i < propertyNames.length; i++) { | 1808 for (var i = 0; i < propertyNames.length; i++) { |
1757 if (propertyNames[i] in object) | 1809 if (propertyNames[i] in object) |
1758 return propertyNames[i]; | 1810 return propertyNames[i]; |
1759 } | 1811 } |
1760 } | 1812 } |
1761 | 1813 |
| 1814 var nonEnumerableDataDescriptor = { |
| 1815 value: undefined, |
| 1816 configurable: true, |
| 1817 enumerable: false, |
| 1818 writable: true |
| 1819 }; |
| 1820 |
| 1821 function defineNonEnumerableDataProperty(object, name, value) { |
| 1822 nonEnumerableDataDescriptor.value = value; |
| 1823 defineProperty(object, name, nonEnumerableDataDescriptor); |
| 1824 } |
| 1825 |
1762 // Mozilla's old DOM bindings are bretty busted: | 1826 // Mozilla's old DOM bindings are bretty busted: |
1763 // https://bugzilla.mozilla.org/show_bug.cgi?id=855844 | 1827 // https://bugzilla.mozilla.org/show_bug.cgi?id=855844 |
1764 // Make sure they are create before we start modifying things. | 1828 // Make sure they are create before we start modifying things. |
1765 getOwnPropertyNames(window); | 1829 getOwnPropertyNames(window); |
1766 | 1830 |
1767 function getWrapperConstructor(node) { | 1831 function getWrapperConstructor(node) { |
1768 var nativePrototype = node.__proto__ || Object.getPrototypeOf(node); | 1832 var nativePrototype = node.__proto__ || Object.getPrototypeOf(node); |
1769 var wrapperConstructor = constructorTable.get(nativePrototype); | 1833 var wrapperConstructor = constructorTable.get(nativePrototype); |
1770 if (wrapperConstructor) | 1834 if (wrapperConstructor) |
1771 return wrapperConstructor; | 1835 return wrapperConstructor; |
(...skipping 124 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1896 function registerInternal(nativePrototype, wrapperConstructor, opt_instance) { | 1960 function registerInternal(nativePrototype, wrapperConstructor, opt_instance) { |
1897 var wrapperPrototype = wrapperConstructor.prototype; | 1961 var wrapperPrototype = wrapperConstructor.prototype; |
1898 assert(constructorTable.get(nativePrototype) === undefined); | 1962 assert(constructorTable.get(nativePrototype) === undefined); |
1899 | 1963 |
1900 constructorTable.set(nativePrototype, wrapperConstructor); | 1964 constructorTable.set(nativePrototype, wrapperConstructor); |
1901 nativePrototypeTable.set(wrapperPrototype, nativePrototype); | 1965 nativePrototypeTable.set(wrapperPrototype, nativePrototype); |
1902 | 1966 |
1903 addForwardingProperties(nativePrototype, wrapperPrototype); | 1967 addForwardingProperties(nativePrototype, wrapperPrototype); |
1904 if (opt_instance) | 1968 if (opt_instance) |
1905 registerInstanceProperties(wrapperPrototype, opt_instance); | 1969 registerInstanceProperties(wrapperPrototype, opt_instance); |
1906 defineProperty(wrapperPrototype, 'constructor', { | 1970 |
1907 value: wrapperConstructor, | 1971 defineNonEnumerableDataProperty( |
1908 configurable: true, | 1972 wrapperPrototype, 'constructor', wrapperConstructor); |
1909 enumerable: false, | |
1910 writable: true | |
1911 }); | |
1912 // Set it again. Some VMs optimizes objects that are used as prototypes. | 1973 // Set it again. Some VMs optimizes objects that are used as prototypes. |
1913 wrapperConstructor.prototype = wrapperPrototype; | 1974 wrapperConstructor.prototype = wrapperPrototype; |
1914 } | 1975 } |
1915 | 1976 |
1916 function isWrapperFor(wrapperConstructor, nativeConstructor) { | 1977 function isWrapperFor(wrapperConstructor, nativeConstructor) { |
1917 return constructorTable.get(nativeConstructor.prototype) === | 1978 return constructorTable.get(nativeConstructor.prototype) === |
1918 wrapperConstructor; | 1979 wrapperConstructor; |
1919 } | 1980 } |
1920 | 1981 |
1921 /** | 1982 /** |
(...skipping 109 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2031 * needed next time someone wraps the node. | 2092 * needed next time someone wraps the node. |
2032 */ | 2093 */ |
2033 function rewrap(node, wrapper) { | 2094 function rewrap(node, wrapper) { |
2034 if (wrapper === null) | 2095 if (wrapper === null) |
2035 return; | 2096 return; |
2036 assert(isNative(node)); | 2097 assert(isNative(node)); |
2037 assert(wrapper === undefined || isWrapper(wrapper)); | 2098 assert(wrapper === undefined || isWrapper(wrapper)); |
2038 node.polymerWrapper_ = wrapper; | 2099 node.polymerWrapper_ = wrapper; |
2039 } | 2100 } |
2040 | 2101 |
| 2102 var getterDescriptor = { |
| 2103 get: undefined, |
| 2104 configurable: true, |
| 2105 enumerable: true |
| 2106 }; |
| 2107 |
2041 function defineGetter(constructor, name, getter) { | 2108 function defineGetter(constructor, name, getter) { |
2042 defineProperty(constructor.prototype, name, { | 2109 getterDescriptor.get = getter; |
2043 get: getter, | 2110 defineProperty(constructor.prototype, name, getterDescriptor); |
2044 configurable: true, | |
2045 enumerable: true | |
2046 }); | |
2047 } | 2111 } |
2048 | 2112 |
2049 function defineWrapGetter(constructor, name) { | 2113 function defineWrapGetter(constructor, name) { |
2050 defineGetter(constructor, name, function() { | 2114 defineGetter(constructor, name, function() { |
2051 return wrap(this.impl[name]); | 2115 return wrap(this.impl[name]); |
2052 }); | 2116 }); |
2053 } | 2117 } |
2054 | 2118 |
2055 /** | 2119 /** |
2056 * Forwards existing methods on the native object to the wrapper methods. | 2120 * Forwards existing methods on the native object to the wrapper methods. |
(...skipping 463 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2520 * license that can be found in the LICENSE file. | 2584 * license that can be found in the LICENSE file. |
2521 */ | 2585 */ |
2522 | 2586 |
2523 (function(scope) { | 2587 (function(scope) { |
2524 'use strict'; | 2588 'use strict'; |
2525 | 2589 |
2526 /** | 2590 /** |
2527 * A tree scope represents the root of a tree. All nodes in a tree point to | 2591 * A tree scope represents the root of a tree. All nodes in a tree point to |
2528 * the same TreeScope object. The tree scope of a node get set the first time | 2592 * the same TreeScope object. The tree scope of a node get set the first time |
2529 * it is accessed or when a node is added or remove to a tree. | 2593 * it is accessed or when a node is added or remove to a tree. |
| 2594 * |
| 2595 * The root is a Node that has no parent. |
| 2596 * |
| 2597 * The parent is another TreeScope. For ShadowRoots, it is the TreeScope of |
| 2598 * the host of the ShadowRoot. |
| 2599 * |
| 2600 * @param {!Node} root |
| 2601 * @param {TreeScope} parent |
2530 * @constructor | 2602 * @constructor |
2531 */ | 2603 */ |
2532 function TreeScope(root, parent) { | 2604 function TreeScope(root, parent) { |
| 2605 /** @type {!Node} */ |
2533 this.root = root; | 2606 this.root = root; |
| 2607 |
| 2608 /** @type {TreeScope} */ |
2534 this.parent = parent; | 2609 this.parent = parent; |
2535 } | 2610 } |
2536 | 2611 |
2537 TreeScope.prototype = { | 2612 TreeScope.prototype = { |
2538 get renderer() { | 2613 get renderer() { |
2539 if (this.root instanceof scope.wrappers.ShadowRoot) { | 2614 if (this.root instanceof scope.wrappers.ShadowRoot) { |
2540 return scope.getRendererForHost(this.root.host); | 2615 return scope.getRendererForHost(this.root.host); |
2541 } | 2616 } |
2542 return null; | 2617 return null; |
2543 }, | 2618 }, |
(...skipping 13 matching lines...) Expand all Loading... |
2557 for (var sr = node.shadowRoot; sr; sr = sr.olderShadowRoot) { | 2632 for (var sr = node.shadowRoot; sr; sr = sr.olderShadowRoot) { |
2558 sr.treeScope_.parent = treeScope; | 2633 sr.treeScope_.parent = treeScope; |
2559 } | 2634 } |
2560 for (var child = node.firstChild; child; child = child.nextSibling) { | 2635 for (var child = node.firstChild; child; child = child.nextSibling) { |
2561 setTreeScope(child, treeScope); | 2636 setTreeScope(child, treeScope); |
2562 } | 2637 } |
2563 } | 2638 } |
2564 } | 2639 } |
2565 | 2640 |
2566 function getTreeScope(node) { | 2641 function getTreeScope(node) { |
| 2642 if (node instanceof scope.wrappers.Window) { |
| 2643 debugger; |
| 2644 } |
| 2645 |
2567 if (node.treeScope_) | 2646 if (node.treeScope_) |
2568 return node.treeScope_; | 2647 return node.treeScope_; |
2569 var parent = node.parentNode; | 2648 var parent = node.parentNode; |
2570 var treeScope; | 2649 var treeScope; |
2571 if (parent) | 2650 if (parent) |
2572 treeScope = getTreeScope(parent); | 2651 treeScope = getTreeScope(parent); |
2573 else | 2652 else |
2574 treeScope = new TreeScope(node, null); | 2653 treeScope = new TreeScope(node, null); |
2575 return node.treeScope_ = treeScope; | 2654 return node.treeScope_ = treeScope; |
2576 } | 2655 } |
(...skipping 29 matching lines...) Expand all Loading... |
2606 var eventPhaseTable = new WeakMap(); | 2685 var eventPhaseTable = new WeakMap(); |
2607 var stopPropagationTable = new WeakMap(); | 2686 var stopPropagationTable = new WeakMap(); |
2608 var stopImmediatePropagationTable = new WeakMap(); | 2687 var stopImmediatePropagationTable = new WeakMap(); |
2609 var eventHandlersTable = new WeakMap(); | 2688 var eventHandlersTable = new WeakMap(); |
2610 var eventPathTable = new WeakMap(); | 2689 var eventPathTable = new WeakMap(); |
2611 | 2690 |
2612 function isShadowRoot(node) { | 2691 function isShadowRoot(node) { |
2613 return node instanceof wrappers.ShadowRoot; | 2692 return node instanceof wrappers.ShadowRoot; |
2614 } | 2693 } |
2615 | 2694 |
2616 function isInsertionPoint(node) { | 2695 function rootOfNode(node) { |
2617 var localName = node.localName; | 2696 return getTreeScope(node).root; |
2618 return localName === 'content' || localName === 'shadow'; | 2697 } |
2619 } | 2698 |
2620 | 2699 // http://w3c.github.io/webcomponents/spec/shadow/#event-paths |
2621 function isShadowHost(node) { | 2700 function getEventPath(node, event) { |
2622 return !!node.shadowRoot; | 2701 var path = []; |
2623 } | 2702 var current = node; |
2624 | 2703 path.push(current); |
2625 function getEventParent(node) { | 2704 while (current) { |
2626 var dv; | 2705 // 4.1. |
2627 return node.parentNode || (dv = node.defaultView) && wrap(dv) || null; | 2706 var destinationInsertionPoints = getDestinationInsertionPoints(current); |
2628 } | 2707 if (destinationInsertionPoints && destinationInsertionPoints.length > 0) { |
2629 | 2708 // 4.1.1 |
2630 // https://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/shadow/index.html#df
n-adjusted-parent | 2709 for (var i = 0; i < destinationInsertionPoints.length; i++) { |
2631 function calculateParents(node, context, ancestors) { | 2710 var insertionPoint = destinationInsertionPoints[i]; |
2632 if (ancestors.length) | 2711 // 4.1.1.1 |
2633 return ancestors.shift(); | 2712 if (isShadowInsertionPoint(insertionPoint)) { |
2634 | 2713 var shadowRoot = rootOfNode(insertionPoint); |
2635 // 1. | 2714 // 4.1.1.1.2 |
2636 if (isShadowRoot(node)) | 2715 var olderShadowRoot = shadowRoot.olderShadowRoot; |
2637 return getInsertionParent(node) || node.host; | 2716 if (olderShadowRoot) |
2638 | 2717 path.push(olderShadowRoot); |
2639 // 2. | 2718 } |
2640 var eventParents = scope.eventParentsTable.get(node); | 2719 |
2641 if (eventParents) { | 2720 // 4.1.1.2 |
2642 // Copy over the remaining event parents for next iteration. | 2721 path.push(insertionPoint); |
2643 for (var i = 1; i < eventParents.length; i++) { | |
2644 ancestors[i - 1] = eventParents[i]; | |
2645 } | |
2646 return eventParents[0]; | |
2647 } | |
2648 | |
2649 // 3. | |
2650 if (context && isInsertionPoint(node)) { | |
2651 var parentNode = node.parentNode; | |
2652 if (parentNode && isShadowHost(parentNode)) { | |
2653 var trees = scope.getShadowTrees(parentNode); | |
2654 var p = getInsertionParent(context); | |
2655 for (var i = 0; i < trees.length; i++) { | |
2656 if (trees[i].contains(p)) | |
2657 return p; | |
2658 } | 2722 } |
2659 } | 2723 |
2660 } | 2724 // 4.1.2 |
2661 | 2725 current = destinationInsertionPoints[ |
2662 return getEventParent(node); | 2726 destinationInsertionPoints.length - 1]; |
2663 } | 2727 |
2664 | 2728 // 4.2 |
2665 // https://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/shadow/index.html#ev
ent-retargeting | 2729 } else { |
2666 function retarget(node) { | 2730 if (isShadowRoot(current)) { |
2667 var stack = []; // 1. | 2731 if (inSameTree(node, current) && eventMustBeStopped(event)) { |
2668 var ancestor = node; // 2. | 2732 // Stop this algorithm |
2669 var targets = []; | 2733 break; |
| 2734 } |
| 2735 current = current.host; |
| 2736 path.push(current); |
| 2737 |
| 2738 // 4.2.2 |
| 2739 } else { |
| 2740 current = current.parentNode; |
| 2741 if (current) |
| 2742 path.push(current); |
| 2743 } |
| 2744 } |
| 2745 } |
| 2746 |
| 2747 return path; |
| 2748 } |
| 2749 |
| 2750 // http://w3c.github.io/webcomponents/spec/shadow/#dfn-events-always-stopped |
| 2751 function eventMustBeStopped(event) { |
| 2752 if (!event) |
| 2753 return false; |
| 2754 |
| 2755 switch (event.type) { |
| 2756 case 'abort': |
| 2757 case 'error': |
| 2758 case 'select': |
| 2759 case 'change': |
| 2760 case 'load': |
| 2761 case 'reset': |
| 2762 case 'resize': |
| 2763 case 'scroll': |
| 2764 case 'selectstart': |
| 2765 return true; |
| 2766 } |
| 2767 return false; |
| 2768 } |
| 2769 |
| 2770 // http://w3c.github.io/webcomponents/spec/shadow/#dfn-shadow-insertion-point |
| 2771 function isShadowInsertionPoint(node) { |
| 2772 return node instanceof HTMLShadowElement; |
| 2773 // and make sure that there are no shadow precing this? |
| 2774 // and that there is no content ancestor? |
| 2775 } |
| 2776 |
| 2777 function getDestinationInsertionPoints(node) { |
| 2778 return scope.getDestinationInsertionPoints(node); |
| 2779 } |
| 2780 |
| 2781 // http://w3c.github.io/webcomponents/spec/shadow/#event-retargeting |
| 2782 function eventRetargetting(path, currentTarget) { |
| 2783 if (path.length === 0) |
| 2784 return currentTarget; |
| 2785 |
| 2786 // The currentTarget might be the window object. Use its document for the |
| 2787 // purpose of finding the retargetted node. |
| 2788 if (currentTarget instanceof wrappers.Window) |
| 2789 currentTarget = currentTarget.document; |
| 2790 |
| 2791 var currentTargetTree = getTreeScope(currentTarget); |
| 2792 var originalTarget = path[0]; |
| 2793 var originalTargetTree = getTreeScope(originalTarget); |
| 2794 var relativeTargetTree = |
| 2795 lowestCommonInclusiveAncestor(currentTargetTree, originalTargetTree); |
| 2796 |
| 2797 for (var i = 0; i < path.length; i++) { |
| 2798 var node = path[i]; |
| 2799 if (getTreeScope(node) === relativeTargetTree) |
| 2800 return node; |
| 2801 } |
| 2802 |
| 2803 return path[path.length - 1]; |
| 2804 } |
| 2805 |
| 2806 function getTreeScopeAncestors(treeScope) { |
2670 var ancestors = []; | 2807 var ancestors = []; |
2671 while (ancestor) { // 3. | 2808 for (;treeScope; treeScope = treeScope.parent) { |
2672 var context = null; // 3.2. | 2809 ancestors.push(treeScope); |
2673 // TODO(arv): Change order of these. If the stack is empty we always end | 2810 } |
2674 // up pushing ancestor, no matter what. | 2811 return ancestors; |
2675 if (isInsertionPoint(ancestor)) { // 3.1. | 2812 } |
2676 context = topMostNotInsertionPoint(stack); // 3.1.1. | 2813 |
2677 var top = stack[stack.length - 1] || ancestor; // 3.1.2. | 2814 function lowestCommonInclusiveAncestor(tsA, tsB) { |
2678 stack.push(top); | 2815 var ancestorsA = getTreeScopeAncestors(tsA); |
2679 } else if (!stack.length) { | 2816 var ancestorsB = getTreeScopeAncestors(tsB); |
2680 stack.push(ancestor); // 3.3. | 2817 |
2681 } | 2818 var result = null; |
2682 var target = stack[stack.length - 1]; // 3.4. | 2819 while (ancestorsA.length > 0 && ancestorsB.length > 0) { |
2683 targets.push({target: target, currentTarget: ancestor}); // 3.5. | 2820 var a = ancestorsA.pop(); |
2684 if (isShadowRoot(ancestor)) // 3.6. | 2821 var b = ancestorsB.pop(); |
2685 stack.pop(); // 3.6.1. | 2822 if (a === b) |
2686 | 2823 result = a; |
2687 ancestor = calculateParents(ancestor, context, ancestors); // 3.7. | 2824 else |
2688 } | 2825 break; |
2689 return targets; | 2826 } |
2690 } | 2827 return result; |
2691 | 2828 } |
2692 function topMostNotInsertionPoint(stack) { | 2829 |
2693 for (var i = stack.length - 1; i >= 0; i--) { | 2830 function getTreeScopeRoot(ts) { |
2694 if (!isInsertionPoint(stack[i])) | 2831 if (!ts.parent) |
2695 return stack[i]; | 2832 return ts; |
2696 } | 2833 return getTreeScopeRoot(ts.parent); |
| 2834 } |
| 2835 |
| 2836 function relatedTargetResolution(event, currentTarget, relatedTarget) { |
| 2837 // In case the current target is a window use its document for the purpose |
| 2838 // of retargetting the related target. |
| 2839 if (currentTarget instanceof wrappers.Window) |
| 2840 currentTarget = currentTarget.document; |
| 2841 |
| 2842 var currentTargetTree = getTreeScope(currentTarget); |
| 2843 var relatedTargetTree = getTreeScope(relatedTarget); |
| 2844 |
| 2845 var relatedTargetEventPath = getEventPath(relatedTarget, event); |
| 2846 |
| 2847 var lowestCommonAncestorTree; |
| 2848 |
| 2849 // 4 |
| 2850 var lowestCommonAncestorTree = |
| 2851 lowestCommonInclusiveAncestor(currentTargetTree, relatedTargetTree); |
| 2852 |
| 2853 // 5 |
| 2854 if (!lowestCommonAncestorTree) |
| 2855 lowestCommonAncestorTree = relatedTargetTree.root; |
| 2856 |
| 2857 // 6 |
| 2858 for (var commonAncestorTree = lowestCommonAncestorTree; |
| 2859 commonAncestorTree; |
| 2860 commonAncestorTree = commonAncestorTree.parent) { |
| 2861 // 6.1 |
| 2862 var adjustedRelatedTarget; |
| 2863 for (var i = 0; i < relatedTargetEventPath.length; i++) { |
| 2864 var node = relatedTargetEventPath[i]; |
| 2865 if (getTreeScope(node) === commonAncestorTree) |
| 2866 return node; |
| 2867 } |
| 2868 } |
| 2869 |
2697 return null; | 2870 return null; |
2698 } | 2871 } |
2699 | 2872 |
2700 // https://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/shadow/index.html#df
n-adjusted-related-target | |
2701 function adjustRelatedTarget(target, related) { | |
2702 var ancestors = []; | |
2703 while (target) { // 3. | |
2704 var stack = []; // 3.1. | |
2705 var ancestor = related; // 3.2. | |
2706 var last = undefined; // 3.3. Needs to be reset every iteration. | |
2707 while (ancestor) { | |
2708 var context = null; | |
2709 if (!stack.length) { | |
2710 stack.push(ancestor); | |
2711 } else { | |
2712 if (isInsertionPoint(ancestor)) { // 3.4.3. | |
2713 context = topMostNotInsertionPoint(stack); | |
2714 // isDistributed is more general than checking whether last is | |
2715 // assigned into ancestor. | |
2716 if (isDistributed(last)) { // 3.4.3.2. | |
2717 var head = stack[stack.length - 1]; | |
2718 stack.push(head); | |
2719 } | |
2720 } | |
2721 } | |
2722 | |
2723 if (inSameTree(ancestor, target)) // 3.4.4. | |
2724 return stack[stack.length - 1]; | |
2725 | |
2726 if (isShadowRoot(ancestor)) // 3.4.5. | |
2727 stack.pop(); | |
2728 | |
2729 last = ancestor; // 3.4.6. | |
2730 ancestor = calculateParents(ancestor, context, ancestors); // 3.4.7. | |
2731 } | |
2732 if (isShadowRoot(target)) // 3.5. | |
2733 target = target.host; | |
2734 else | |
2735 target = target.parentNode; // 3.6. | |
2736 } | |
2737 } | |
2738 | |
2739 function getInsertionParent(node) { | |
2740 return scope.insertionParentTable.get(node); | |
2741 } | |
2742 | |
2743 function isDistributed(node) { | |
2744 return getInsertionParent(node); | |
2745 } | |
2746 | |
2747 function inSameTree(a, b) { | 2873 function inSameTree(a, b) { |
2748 return getTreeScope(a) === getTreeScope(b); | 2874 return getTreeScope(a) === getTreeScope(b); |
2749 } | 2875 } |
2750 | 2876 |
| 2877 var NONE = 0; |
| 2878 var CAPTURING_PHASE = 1; |
| 2879 var AT_TARGET = 2; |
| 2880 var BUBBLING_PHASE = 3; |
| 2881 |
| 2882 // pendingError is used to rethrow the first error we got during an event |
| 2883 // dispatch. The browser actually reports all errors but to do that we would |
| 2884 // need to rethrow the error asynchronously. |
| 2885 var pendingError; |
| 2886 |
2751 function dispatchOriginalEvent(originalEvent) { | 2887 function dispatchOriginalEvent(originalEvent) { |
2752 // Make sure this event is only dispatched once. | 2888 // Make sure this event is only dispatched once. |
2753 if (handledEventsTable.get(originalEvent)) | 2889 if (handledEventsTable.get(originalEvent)) |
2754 return; | 2890 return; |
2755 handledEventsTable.set(originalEvent, true); | 2891 handledEventsTable.set(originalEvent, true); |
2756 dispatchEvent(wrap(originalEvent), wrap(originalEvent.target)); | 2892 dispatchEvent(wrap(originalEvent), wrap(originalEvent.target)); |
2757 } | 2893 if (pendingError) { |
2758 | 2894 var err = pendingError; |
2759 function isLoadLikeEvent(event) { | 2895 pendingError = null; |
2760 switch (event.type) { | 2896 throw err; |
2761 case 'beforeunload': | 2897 } |
2762 case 'load': | |
2763 case 'unload': | |
2764 return true; | |
2765 } | |
2766 return false; | |
2767 } | 2898 } |
2768 | 2899 |
2769 function dispatchEvent(event, originalWrapperTarget) { | 2900 function dispatchEvent(event, originalWrapperTarget) { |
2770 if (currentlyDispatchingEvents.get(event)) | 2901 if (currentlyDispatchingEvents.get(event)) |
2771 throw new Error('InvalidStateError') | 2902 throw new Error('InvalidStateError'); |
| 2903 |
2772 currentlyDispatchingEvents.set(event, true); | 2904 currentlyDispatchingEvents.set(event, true); |
2773 | 2905 |
2774 // Render to ensure that the event path is correct. | 2906 // Render to ensure that the event path is correct. |
2775 scope.renderAllPending(); | 2907 scope.renderAllPending(); |
2776 var eventPath = retarget(originalWrapperTarget); | 2908 var eventPath; |
2777 | 2909 |
2778 // For window "load" events the "load" event is dispatched at the window but | 2910 // http://www.whatwg.org/specs/web-apps/current-work/multipage/webappapis.ht
ml#events-and-the-window-object |
2779 // the target is set to the document. | 2911 // All events dispatched on Nodes with a default view, except load events, |
2780 // | 2912 // should propagate to the Window. |
| 2913 |
2781 // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-end.html#
the-end | 2914 // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-end.html#
the-end |
2782 // | 2915 var overrideTarget; |
2783 // TODO(arv): Find a less hacky way to do this. | 2916 var win; |
2784 if (eventPath.length === 2 && | 2917 var type = event.type; |
2785 eventPath[0].target instanceof wrappers.Document && | 2918 |
2786 isLoadLikeEvent(event)) { | 2919 // Should really be not cancelable too but since Firefox has a bug there |
2787 eventPath.shift(); | 2920 // we skip that check. |
| 2921 // https://bugzilla.mozilla.org/show_bug.cgi?id=999456 |
| 2922 if (type === 'load' && !event.bubbles) { |
| 2923 var doc = originalWrapperTarget; |
| 2924 if (doc instanceof wrappers.Document && (win = doc.defaultView)) { |
| 2925 overrideTarget = doc; |
| 2926 eventPath = []; |
| 2927 } |
| 2928 } |
| 2929 |
| 2930 if (!eventPath) { |
| 2931 if (originalWrapperTarget instanceof wrappers.Window) { |
| 2932 win = originalWrapperTarget; |
| 2933 eventPath = []; |
| 2934 } else { |
| 2935 eventPath = getEventPath(originalWrapperTarget, event); |
| 2936 |
| 2937 if (event.type !== 'load') { |
| 2938 var doc = eventPath[eventPath.length - 1]; |
| 2939 if (doc instanceof wrappers.Document) |
| 2940 win = doc.defaultView; |
| 2941 } |
| 2942 } |
2788 } | 2943 } |
2789 | 2944 |
2790 eventPathTable.set(event, eventPath); | 2945 eventPathTable.set(event, eventPath); |
2791 | 2946 |
2792 if (dispatchCapturing(event, eventPath)) { | 2947 if (dispatchCapturing(event, eventPath, win, overrideTarget)) { |
2793 if (dispatchAtTarget(event, eventPath)) { | 2948 if (dispatchAtTarget(event, eventPath, win, overrideTarget)) { |
2794 dispatchBubbling(event, eventPath); | 2949 dispatchBubbling(event, eventPath, win, overrideTarget); |
2795 } | 2950 } |
2796 } | 2951 } |
2797 | 2952 |
2798 eventPhaseTable.set(event, Event.NONE); | 2953 eventPhaseTable.set(event, NONE); |
2799 currentTargetTable.delete(event, null); | 2954 currentTargetTable.delete(event, null); |
2800 currentlyDispatchingEvents.delete(event); | 2955 currentlyDispatchingEvents.delete(event); |
2801 | 2956 |
2802 return event.defaultPrevented; | 2957 return event.defaultPrevented; |
2803 } | 2958 } |
2804 | 2959 |
2805 function dispatchCapturing(event, eventPath) { | 2960 function dispatchCapturing(event, eventPath, win, overrideTarget) { |
2806 var phase; | 2961 var phase = CAPTURING_PHASE; |
| 2962 |
| 2963 if (win) { |
| 2964 if (!invoke(win, event, phase, eventPath, overrideTarget)) |
| 2965 return false; |
| 2966 } |
2807 | 2967 |
2808 for (var i = eventPath.length - 1; i > 0; i--) { | 2968 for (var i = eventPath.length - 1; i > 0; i--) { |
2809 var target = eventPath[i].target; | 2969 if (!invoke(eventPath[i], event, phase, eventPath, overrideTarget)) |
2810 var currentTarget = eventPath[i].currentTarget; | |
2811 if (target === currentTarget) | |
2812 continue; | |
2813 | |
2814 phase = Event.CAPTURING_PHASE; | |
2815 if (!invoke(eventPath[i], event, phase)) | |
2816 return false; | 2970 return false; |
2817 } | 2971 } |
2818 | 2972 |
2819 return true; | 2973 return true; |
2820 } | 2974 } |
2821 | 2975 |
2822 function dispatchAtTarget(event, eventPath) { | 2976 function dispatchAtTarget(event, eventPath, win, overrideTarget) { |
2823 var phase = Event.AT_TARGET; | 2977 var phase = AT_TARGET; |
2824 return invoke(eventPath[0], event, phase); | 2978 var currentTarget = eventPath[0] || win; |
2825 } | 2979 return invoke(currentTarget, event, phase, eventPath, overrideTarget); |
2826 | 2980 } |
2827 function dispatchBubbling(event, eventPath) { | 2981 |
2828 var bubbles = event.bubbles; | 2982 function dispatchBubbling(event, eventPath, win, overrideTarget) { |
2829 var phase; | 2983 var phase = BUBBLING_PHASE; |
2830 | |
2831 for (var i = 1; i < eventPath.length; i++) { | 2984 for (var i = 1; i < eventPath.length; i++) { |
2832 var target = eventPath[i].target; | 2985 if (!invoke(eventPath[i], event, phase, eventPath, overrideTarget)) |
2833 var currentTarget = eventPath[i].currentTarget; | |
2834 if (target === currentTarget) | |
2835 phase = Event.AT_TARGET; | |
2836 else if (bubbles && !stopImmediatePropagationTable.get(event)) | |
2837 phase = Event.BUBBLING_PHASE; | |
2838 else | |
2839 continue; | |
2840 | |
2841 if (!invoke(eventPath[i], event, phase)) | |
2842 return; | 2986 return; |
2843 } | 2987 } |
2844 } | 2988 |
2845 | 2989 if (win && eventPath.length > 0) { |
2846 function invoke(tuple, event, phase) { | 2990 invoke(win, event, phase, eventPath, overrideTarget); |
2847 var target = tuple.target; | 2991 } |
2848 var currentTarget = tuple.currentTarget; | 2992 } |
2849 | 2993 |
| 2994 function invoke(currentTarget, event, phase, eventPath, overrideTarget) { |
2850 var listeners = listenersTable.get(currentTarget); | 2995 var listeners = listenersTable.get(currentTarget); |
2851 if (!listeners) | 2996 if (!listeners) |
2852 return true; | 2997 return true; |
2853 | 2998 |
| 2999 var target = overrideTarget || eventRetargetting(eventPath, currentTarget); |
| 3000 |
| 3001 if (target === currentTarget) { |
| 3002 if (phase === CAPTURING_PHASE) |
| 3003 return true; |
| 3004 |
| 3005 if (phase === BUBBLING_PHASE) |
| 3006 phase = AT_TARGET; |
| 3007 |
| 3008 } else if (phase === BUBBLING_PHASE && !event.bubbles) { |
| 3009 return true; |
| 3010 } |
| 3011 |
2854 if ('relatedTarget' in event) { | 3012 if ('relatedTarget' in event) { |
2855 var originalEvent = unwrap(event); | 3013 var originalEvent = unwrap(event); |
2856 var unwrappedRelatedTarget = originalEvent.relatedTarget; | 3014 var unwrappedRelatedTarget = originalEvent.relatedTarget; |
2857 | 3015 |
2858 // X-Tag sets relatedTarget on a CustomEvent. If they do that there is no | 3016 // X-Tag sets relatedTarget on a CustomEvent. If they do that there is no |
2859 // way to have relatedTarget return the adjusted target but worse is that | 3017 // way to have relatedTarget return the adjusted target but worse is that |
2860 // the originalEvent might not have a relatedTarget so we hit an assert | 3018 // the originalEvent might not have a relatedTarget so we hit an assert |
2861 // when we try to wrap it. | 3019 // when we try to wrap it. |
2862 if (unwrappedRelatedTarget) { | 3020 if (unwrappedRelatedTarget) { |
2863 // In IE we can get objects that are not EventTargets at this point. | 3021 // In IE we can get objects that are not EventTargets at this point. |
2864 // Safari does not have an EventTarget interface so revert to checking | 3022 // Safari does not have an EventTarget interface so revert to checking |
2865 // for addEventListener as an approximation. | 3023 // for addEventListener as an approximation. |
2866 if (unwrappedRelatedTarget instanceof Object && | 3024 if (unwrappedRelatedTarget instanceof Object && |
2867 unwrappedRelatedTarget.addEventListener) { | 3025 unwrappedRelatedTarget.addEventListener) { |
2868 var relatedTarget = wrap(unwrappedRelatedTarget); | 3026 var relatedTarget = wrap(unwrappedRelatedTarget); |
2869 | 3027 |
2870 var adjusted = adjustRelatedTarget(currentTarget, relatedTarget); | 3028 var adjusted = |
| 3029 relatedTargetResolution(event, currentTarget, relatedTarget); |
2871 if (adjusted === target) | 3030 if (adjusted === target) |
2872 return true; | 3031 return true; |
2873 } else { | 3032 } else { |
2874 adjusted = null; | 3033 adjusted = null; |
2875 } | 3034 } |
2876 relatedTargetTable.set(event, adjusted); | 3035 relatedTargetTable.set(event, adjusted); |
2877 } | 3036 } |
2878 } | 3037 } |
2879 | 3038 |
2880 eventPhaseTable.set(event, phase); | 3039 eventPhaseTable.set(event, phase); |
2881 var type = event.type; | 3040 var type = event.type; |
2882 | 3041 |
2883 var anyRemoved = false; | 3042 var anyRemoved = false; |
| 3043 // targetTable.set(event, target); |
2884 targetTable.set(event, target); | 3044 targetTable.set(event, target); |
2885 currentTargetTable.set(event, currentTarget); | 3045 currentTargetTable.set(event, currentTarget); |
2886 | 3046 |
2887 for (var i = 0; i < listeners.length; i++) { | 3047 for (var i = 0; i < listeners.length; i++) { |
2888 var listener = listeners[i]; | 3048 var listener = listeners[i]; |
2889 if (listener.removed) { | 3049 if (listener.removed) { |
2890 anyRemoved = true; | 3050 anyRemoved = true; |
2891 continue; | 3051 continue; |
2892 } | 3052 } |
2893 | 3053 |
2894 if (listener.type !== type || | 3054 if (listener.type !== type || |
2895 !listener.capture && phase === Event.CAPTURING_PHASE || | 3055 !listener.capture && phase === CAPTURING_PHASE || |
2896 listener.capture && phase === Event.BUBBLING_PHASE) { | 3056 listener.capture && phase === BUBBLING_PHASE) { |
2897 continue; | 3057 continue; |
2898 } | 3058 } |
2899 | 3059 |
2900 try { | 3060 try { |
2901 if (typeof listener.handler === 'function') | 3061 if (typeof listener.handler === 'function') |
2902 listener.handler.call(currentTarget, event); | 3062 listener.handler.call(currentTarget, event); |
2903 else | 3063 else |
2904 listener.handler.handleEvent(event); | 3064 listener.handler.handleEvent(event); |
2905 | 3065 |
2906 if (stopImmediatePropagationTable.get(event)) | 3066 if (stopImmediatePropagationTable.get(event)) |
2907 return false; | 3067 return false; |
2908 | 3068 |
2909 } catch (ex) { | 3069 } catch (ex) { |
2910 if (window.onerror) | 3070 if (!pendingError) |
2911 window.onerror(ex.message); | 3071 pendingError = ex; |
2912 else | |
2913 console.error(ex, ex.stack); | |
2914 } | 3072 } |
2915 } | 3073 } |
2916 | 3074 |
2917 if (anyRemoved) { | 3075 if (anyRemoved) { |
2918 var copy = listeners.slice(); | 3076 var copy = listeners.slice(); |
2919 listeners.length = 0; | 3077 listeners.length = 0; |
2920 for (var i = 0; i < copy.length; i++) { | 3078 for (var i = 0; i < copy.length; i++) { |
2921 if (!copy[i].removed) | 3079 if (!copy[i].removed) |
2922 listeners.push(copy[i]); | 3080 listeners.push(copy[i]); |
2923 } | 3081 } |
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2980 }, | 3138 }, |
2981 get path() { | 3139 get path() { |
2982 var nodeList = new wrappers.NodeList(); | 3140 var nodeList = new wrappers.NodeList(); |
2983 var eventPath = eventPathTable.get(this); | 3141 var eventPath = eventPathTable.get(this); |
2984 if (eventPath) { | 3142 if (eventPath) { |
2985 var index = 0; | 3143 var index = 0; |
2986 var lastIndex = eventPath.length - 1; | 3144 var lastIndex = eventPath.length - 1; |
2987 var baseRoot = getTreeScope(currentTargetTable.get(this)); | 3145 var baseRoot = getTreeScope(currentTargetTable.get(this)); |
2988 | 3146 |
2989 for (var i = 0; i <= lastIndex; i++) { | 3147 for (var i = 0; i <= lastIndex; i++) { |
2990 var currentTarget = eventPath[i].currentTarget; | 3148 var currentTarget = eventPath[i]; |
2991 var currentRoot = getTreeScope(currentTarget); | 3149 var currentRoot = getTreeScope(currentTarget); |
2992 if (currentRoot.contains(baseRoot) && | 3150 if (currentRoot.contains(baseRoot) && |
2993 // Make sure we do not add Window to the path. | 3151 // Make sure we do not add Window to the path. |
2994 (i !== lastIndex || currentTarget instanceof wrappers.Node)) { | 3152 (i !== lastIndex || currentTarget instanceof wrappers.Node)) { |
2995 nodeList[index++] = currentTarget; | 3153 nodeList[index++] = currentTarget; |
2996 } | 3154 } |
2997 } | 3155 } |
2998 nodeList.length = index; | 3156 nodeList.length = index; |
2999 } | 3157 } |
3000 return nodeList; | 3158 return nodeList; |
(...skipping 322 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3323 function wrapEventTargetMethods(constructors) { | 3481 function wrapEventTargetMethods(constructors) { |
3324 forwardMethodsToWrapper(constructors, methodNames); | 3482 forwardMethodsToWrapper(constructors, methodNames); |
3325 } | 3483 } |
3326 | 3484 |
3327 var originalElementFromPoint = document.elementFromPoint; | 3485 var originalElementFromPoint = document.elementFromPoint; |
3328 | 3486 |
3329 function elementFromPoint(self, document, x, y) { | 3487 function elementFromPoint(self, document, x, y) { |
3330 scope.renderAllPending(); | 3488 scope.renderAllPending(); |
3331 | 3489 |
3332 var element = wrap(originalElementFromPoint.call(document.impl, x, y)); | 3490 var element = wrap(originalElementFromPoint.call(document.impl, x, y)); |
3333 var targets = retarget(element, this) | 3491 if (!element) |
3334 for (var i = 0; i < targets.length; i++) { | 3492 return null; |
3335 var target = targets[i]; | 3493 var path = getEventPath(element, null); |
3336 if (target.currentTarget === self) | 3494 return eventRetargetting(path, self); |
3337 return target.target; | |
3338 } | |
3339 return null; | |
3340 } | 3495 } |
3341 | 3496 |
3342 /** | 3497 /** |
3343 * Returns a function that is to be used as a getter for `onfoo` properties. | 3498 * Returns a function that is to be used as a getter for `onfoo` properties. |
3344 * @param {string} name | 3499 * @param {string} name |
3345 * @return {Function} | 3500 * @return {Function} |
3346 */ | 3501 */ |
3347 function getEventHandlerGetter(name) { | 3502 function getEventHandlerGetter(name) { |
3348 return function() { | 3503 return function() { |
3349 var inlineEventHandlers = eventHandlersTable.get(this); | 3504 var inlineEventHandlers = eventHandlersTable.get(this); |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3383 | 3538 |
3384 this.addEventListener(eventType, wrapped, false); | 3539 this.addEventListener(eventType, wrapped, false); |
3385 inlineEventHandlers[name] = { | 3540 inlineEventHandlers[name] = { |
3386 value: value, | 3541 value: value, |
3387 wrapped: wrapped | 3542 wrapped: wrapped |
3388 }; | 3543 }; |
3389 } | 3544 } |
3390 }; | 3545 }; |
3391 } | 3546 } |
3392 | 3547 |
3393 scope.adjustRelatedTarget = adjustRelatedTarget; | |
3394 scope.elementFromPoint = elementFromPoint; | 3548 scope.elementFromPoint = elementFromPoint; |
3395 scope.getEventHandlerGetter = getEventHandlerGetter; | 3549 scope.getEventHandlerGetter = getEventHandlerGetter; |
3396 scope.getEventHandlerSetter = getEventHandlerSetter; | 3550 scope.getEventHandlerSetter = getEventHandlerSetter; |
3397 scope.wrapEventTargetMethods = wrapEventTargetMethods; | 3551 scope.wrapEventTargetMethods = wrapEventTargetMethods; |
3398 scope.wrappers.BeforeUnloadEvent = BeforeUnloadEvent; | 3552 scope.wrappers.BeforeUnloadEvent = BeforeUnloadEvent; |
3399 scope.wrappers.CustomEvent = CustomEvent; | 3553 scope.wrappers.CustomEvent = CustomEvent; |
3400 scope.wrappers.Event = Event; | 3554 scope.wrappers.Event = Event; |
3401 scope.wrappers.EventTarget = EventTarget; | 3555 scope.wrappers.EventTarget = EventTarget; |
3402 scope.wrappers.FocusEvent = FocusEvent; | 3556 scope.wrappers.FocusEvent = FocusEvent; |
3403 scope.wrappers.MouseEvent = MouseEvent; | 3557 scope.wrappers.MouseEvent = MouseEvent; |
3404 scope.wrappers.UIEvent = UIEvent; | 3558 scope.wrappers.UIEvent = UIEvent; |
3405 | 3559 |
3406 })(window.ShadowDOMPolyfill); | 3560 })(window.ShadowDOMPolyfill); |
3407 | 3561 |
| 3562 /* |
| 3563 * Copyright 2014 The Polymer Authors. All rights reserved. |
| 3564 * Use of this source code is goverened by a BSD-style |
| 3565 * license that can be found in the LICENSE file. |
| 3566 */ |
| 3567 |
| 3568 (function(scope) { |
| 3569 'use strict'; |
| 3570 |
| 3571 var UIEvent = scope.wrappers.UIEvent; |
| 3572 var mixin = scope.mixin; |
| 3573 var registerWrapper = scope.registerWrapper; |
| 3574 var unwrap = scope.unwrap; |
| 3575 var wrap = scope.wrap; |
| 3576 |
| 3577 // TouchEvent is WebKit/Blink only. |
| 3578 var OriginalTouchEvent = window.TouchEvent; |
| 3579 if (!OriginalTouchEvent) |
| 3580 return; |
| 3581 |
| 3582 var nativeEvent; |
| 3583 try { |
| 3584 nativeEvent = document.createEvent('TouchEvent'); |
| 3585 } catch (ex) { |
| 3586 // In Chrome creating a TouchEvent fails if the feature is not turned on |
| 3587 // which it isn't on desktop Chrome. |
| 3588 return; |
| 3589 } |
| 3590 |
| 3591 var nonEnumDescriptor = {enumerable: false}; |
| 3592 |
| 3593 function nonEnum(obj, prop) { |
| 3594 Object.defineProperty(obj, prop, nonEnumDescriptor); |
| 3595 } |
| 3596 |
| 3597 function Touch(impl) { |
| 3598 this.impl = impl; |
| 3599 } |
| 3600 |
| 3601 Touch.prototype = { |
| 3602 get target() { |
| 3603 return wrap(this.impl.target); |
| 3604 } |
| 3605 }; |
| 3606 |
| 3607 var descr = { |
| 3608 configurable: true, |
| 3609 enumerable: true, |
| 3610 get: null |
| 3611 }; |
| 3612 |
| 3613 [ |
| 3614 'clientX', |
| 3615 'clientY', |
| 3616 'screenX', |
| 3617 'screenY', |
| 3618 'pageX', |
| 3619 'pageY', |
| 3620 'identifier', |
| 3621 'webkitRadiusX', |
| 3622 'webkitRadiusY', |
| 3623 'webkitRotationAngle', |
| 3624 'webkitForce' |
| 3625 ].forEach(function(name) { |
| 3626 descr.get = function() { |
| 3627 return this.impl[name]; |
| 3628 }; |
| 3629 Object.defineProperty(Touch.prototype, name, descr); |
| 3630 }); |
| 3631 |
| 3632 function TouchList() { |
| 3633 this.length = 0; |
| 3634 nonEnum(this, 'length'); |
| 3635 } |
| 3636 |
| 3637 TouchList.prototype = { |
| 3638 item: function(index) { |
| 3639 return this[index]; |
| 3640 } |
| 3641 }; |
| 3642 |
| 3643 function wrapTouchList(nativeTouchList) { |
| 3644 var list = new TouchList(); |
| 3645 for (var i = 0; i < nativeTouchList.length; i++) { |
| 3646 list[i] = new Touch(nativeTouchList[i]); |
| 3647 } |
| 3648 list.length = i; |
| 3649 return list; |
| 3650 } |
| 3651 |
| 3652 function TouchEvent(impl) { |
| 3653 UIEvent.call(this, impl); |
| 3654 } |
| 3655 |
| 3656 TouchEvent.prototype = Object.create(UIEvent.prototype); |
| 3657 |
| 3658 mixin(TouchEvent.prototype, { |
| 3659 get touches() { |
| 3660 return wrapTouchList(unwrap(this).touches); |
| 3661 }, |
| 3662 |
| 3663 get targetTouches() { |
| 3664 return wrapTouchList(unwrap(this).targetTouches); |
| 3665 }, |
| 3666 |
| 3667 get changedTouches() { |
| 3668 return wrapTouchList(unwrap(this).changedTouches); |
| 3669 }, |
| 3670 |
| 3671 initTouchEvent: function() { |
| 3672 // The only way to use this is to reuse the TouchList from an existing |
| 3673 // TouchEvent. Since this is WebKit/Blink proprietary API we will not |
| 3674 // implement this until someone screams. |
| 3675 throw new Error('Not implemented'); |
| 3676 } |
| 3677 }); |
| 3678 |
| 3679 registerWrapper(OriginalTouchEvent, TouchEvent, nativeEvent); |
| 3680 |
| 3681 scope.wrappers.Touch = Touch; |
| 3682 scope.wrappers.TouchEvent = TouchEvent; |
| 3683 scope.wrappers.TouchList = TouchList; |
| 3684 |
| 3685 })(window.ShadowDOMPolyfill); |
| 3686 |
| 3687 |
3408 // Copyright 2012 The Polymer Authors. All rights reserved. | 3688 // Copyright 2012 The Polymer Authors. All rights reserved. |
3409 // Use of this source code is goverened by a BSD-style | 3689 // Use of this source code is goverened by a BSD-style |
3410 // license that can be found in the LICENSE file. | 3690 // license that can be found in the LICENSE file. |
3411 | 3691 |
3412 (function(scope) { | 3692 (function(scope) { |
3413 'use strict'; | 3693 'use strict'; |
3414 | 3694 |
3415 var wrap = scope.wrap; | 3695 var wrap = scope.wrap; |
3416 | 3696 |
| 3697 var nonEnumDescriptor = {enumerable: false}; |
| 3698 |
3417 function nonEnum(obj, prop) { | 3699 function nonEnum(obj, prop) { |
3418 Object.defineProperty(obj, prop, {enumerable: false}); | 3700 Object.defineProperty(obj, prop, nonEnumDescriptor); |
3419 } | 3701 } |
3420 | 3702 |
3421 function NodeList() { | 3703 function NodeList() { |
3422 this.length = 0; | 3704 this.length = 0; |
3423 nonEnum(this, 'length'); | 3705 nonEnum(this, 'length'); |
3424 } | 3706 } |
3425 NodeList.prototype = { | 3707 NodeList.prototype = { |
3426 item: function(index) { | 3708 item: function(index) { |
3427 return this[index]; | 3709 return this[index]; |
3428 } | 3710 } |
(...skipping 435 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3864 else | 4146 else |
3865 nodes = collectNodes(childWrapper, this, previousNode, refWrapper); | 4147 nodes = collectNodes(childWrapper, this, previousNode, refWrapper); |
3866 | 4148 |
3867 if (useNative) { | 4149 if (useNative) { |
3868 ensureSameOwnerDocument(this, childWrapper); | 4150 ensureSameOwnerDocument(this, childWrapper); |
3869 clearChildNodes(this); | 4151 clearChildNodes(this); |
3870 originalInsertBefore.call(this.impl, unwrap(childWrapper), refNode); | 4152 originalInsertBefore.call(this.impl, unwrap(childWrapper), refNode); |
3871 } else { | 4153 } else { |
3872 if (!previousNode) | 4154 if (!previousNode) |
3873 this.firstChild_ = nodes[0]; | 4155 this.firstChild_ = nodes[0]; |
3874 if (!refWrapper) | 4156 if (!refWrapper) { |
3875 this.lastChild_ = nodes[nodes.length - 1]; | 4157 this.lastChild_ = nodes[nodes.length - 1]; |
| 4158 if (this.firstChild_ === undefined) |
| 4159 this.firstChild_ = this.firstChild; |
| 4160 } |
3876 | 4161 |
3877 var parentNode = refNode ? refNode.parentNode : this.impl; | 4162 var parentNode = refNode ? refNode.parentNode : this.impl; |
3878 | 4163 |
3879 // insertBefore refWrapper no matter what the parent is? | 4164 // insertBefore refWrapper no matter what the parent is? |
3880 if (parentNode) { | 4165 if (parentNode) { |
3881 originalInsertBefore.call(parentNode, | 4166 originalInsertBefore.call(parentNode, |
3882 unwrapNodesForInsertion(this, nodes), refNode); | 4167 unwrapNodesForInsertion(this, nodes), refNode); |
3883 } else { | 4168 } else { |
3884 adoptNodesIfNeeded(this, nodes); | 4169 adoptNodesIfNeeded(this, nodes); |
3885 } | 4170 } |
(...skipping 315 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
4201 | 4486 |
4202 })(window.ShadowDOMPolyfill); | 4487 })(window.ShadowDOMPolyfill); |
4203 | 4488 |
4204 // Copyright 2013 The Polymer Authors. All rights reserved. | 4489 // Copyright 2013 The Polymer Authors. All rights reserved. |
4205 // Use of this source code is governed by a BSD-style | 4490 // Use of this source code is governed by a BSD-style |
4206 // license that can be found in the LICENSE file. | 4491 // license that can be found in the LICENSE file. |
4207 | 4492 |
4208 (function(scope) { | 4493 (function(scope) { |
4209 'use strict'; | 4494 'use strict'; |
4210 | 4495 |
| 4496 var HTMLCollection = scope.wrappers.HTMLCollection; |
| 4497 var NodeList = scope.wrappers.NodeList; |
| 4498 |
4211 function findOne(node, selector) { | 4499 function findOne(node, selector) { |
4212 var m, el = node.firstElementChild; | 4500 var m, el = node.firstElementChild; |
4213 while (el) { | 4501 while (el) { |
4214 if (el.matches(selector)) | 4502 if (el.matches(selector)) |
4215 return el; | 4503 return el; |
4216 m = findOne(el, selector); | 4504 m = findOne(el, selector); |
4217 if (m) | 4505 if (m) |
4218 return m; | 4506 return m; |
4219 el = el.nextElementSibling; | 4507 el = el.nextElementSibling; |
4220 } | 4508 } |
4221 return null; | 4509 return null; |
4222 } | 4510 } |
4223 | 4511 |
4224 function findAll(node, selector, results) { | 4512 function matchesSelector(el, selector) { |
| 4513 return el.matches(selector); |
| 4514 } |
| 4515 |
| 4516 var XHTML_NS = 'http://www.w3.org/1999/xhtml'; |
| 4517 |
| 4518 function matchesTagName(el, localName, localNameLowerCase) { |
| 4519 var ln = el.localName; |
| 4520 return ln === localName || |
| 4521 ln === localNameLowerCase && el.namespaceURI === XHTML_NS; |
| 4522 } |
| 4523 |
| 4524 function matchesEveryThing() { |
| 4525 return true; |
| 4526 } |
| 4527 |
| 4528 function matchesLocalName(el, localName) { |
| 4529 return el.localName === localName; |
| 4530 } |
| 4531 |
| 4532 function matchesNameSpace(el, ns) { |
| 4533 return el.namespaceURI === ns; |
| 4534 } |
| 4535 |
| 4536 function matchesLocalNameNS(el, ns, localName) { |
| 4537 return el.namespaceURI === ns && el.localName === localName; |
| 4538 } |
| 4539 |
| 4540 function findElements(node, result, p, arg0, arg1) { |
4225 var el = node.firstElementChild; | 4541 var el = node.firstElementChild; |
4226 while (el) { | 4542 while (el) { |
4227 if (el.matches(selector)) | 4543 if (p(el, arg0, arg1)) |
4228 results[results.length++] = el; | 4544 result[result.length++] = el; |
4229 findAll(el, selector, results); | 4545 findElements(el, result, p, arg0, arg1); |
4230 el = el.nextElementSibling; | 4546 el = el.nextElementSibling; |
4231 } | 4547 } |
4232 return results; | 4548 return result; |
4233 } | 4549 } |
4234 | 4550 |
4235 // find and findAll will only match Simple Selectors, | 4551 // find and findAll will only match Simple Selectors, |
4236 // Structural Pseudo Classes are not guarenteed to be correct | 4552 // Structural Pseudo Classes are not guarenteed to be correct |
4237 // http://www.w3.org/TR/css3-selectors/#simple-selectors | 4553 // http://www.w3.org/TR/css3-selectors/#simple-selectors |
4238 | 4554 |
4239 var SelectorsInterface = { | 4555 var SelectorsInterface = { |
4240 querySelector: function(selector) { | 4556 querySelector: function(selector) { |
4241 return findOne(this, selector); | 4557 return findOne(this, selector); |
4242 }, | 4558 }, |
4243 querySelectorAll: function(selector) { | 4559 querySelectorAll: function(selector) { |
4244 return findAll(this, selector, new NodeList()) | 4560 return findElements(this, new NodeList(), matchesSelector, selector); |
4245 } | 4561 } |
4246 }; | 4562 }; |
4247 | 4563 |
4248 var GetElementsByInterface = { | 4564 var GetElementsByInterface = { |
4249 getElementsByTagName: function(tagName) { | 4565 getElementsByTagName: function(localName) { |
4250 // TODO(arv): Check tagName? | 4566 var result = new HTMLCollection(); |
4251 return this.querySelectorAll(tagName); | 4567 if (localName === '*') |
| 4568 return findElements(this, result, matchesEveryThing); |
| 4569 |
| 4570 return findElements(this, result, |
| 4571 matchesTagName, |
| 4572 localName, |
| 4573 localName.toLowerCase()); |
4252 }, | 4574 }, |
| 4575 |
4253 getElementsByClassName: function(className) { | 4576 getElementsByClassName: function(className) { |
4254 // TODO(arv): Check className? | 4577 // TODO(arv): Check className? |
4255 return this.querySelectorAll('.' + className); | 4578 return this.querySelectorAll('.' + className); |
4256 }, | 4579 }, |
4257 getElementsByTagNameNS: function(ns, tagName) { | |
4258 if (ns === '*') | |
4259 return this.getElementsByTagName(tagName); | |
4260 | 4580 |
4261 // TODO(arv): Check tagName? | 4581 getElementsByTagNameNS: function(ns, localName) { |
4262 var result = new NodeList; | 4582 var result = new HTMLCollection(); |
4263 var els = this.getElementsByTagName(tagName); | 4583 |
4264 for (var i = 0, j = 0; i < els.length; i++) { | 4584 if (ns === '') { |
4265 if (els[i].namespaceURI === ns) | 4585 ns = null; |
4266 result[j++] = els[i]; | 4586 } else if (ns === '*') { |
| 4587 if (localName === '*') |
| 4588 return findElements(this, result, matchesEveryThing); |
| 4589 return findElements(this, result, matchesLocalName, localName); |
4267 } | 4590 } |
4268 result.length = j; | 4591 |
4269 return result; | 4592 if (localName === '*') |
| 4593 return findElements(this, result, matchesNameSpace, ns); |
| 4594 |
| 4595 return findElements(this, result, matchesLocalNameNS, ns, localName); |
4270 } | 4596 } |
4271 }; | 4597 }; |
4272 | 4598 |
4273 scope.GetElementsByInterface = GetElementsByInterface; | 4599 scope.GetElementsByInterface = GetElementsByInterface; |
4274 scope.SelectorsInterface = SelectorsInterface; | 4600 scope.SelectorsInterface = SelectorsInterface; |
4275 | 4601 |
4276 })(window.ShadowDOMPolyfill); | 4602 })(window.ShadowDOMPolyfill); |
4277 | 4603 |
4278 // Copyright 2013 The Polymer Authors. All rights reserved. | 4604 // Copyright 2013 The Polymer Authors. All rights reserved. |
4279 // Use of this source code is goverened by a BSD-style | 4605 // Use of this source code is goverened by a BSD-style |
(...skipping 228 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
4508 var renderer = scope.getRendererForHost(this); | 4834 var renderer = scope.getRendererForHost(this); |
4509 renderer.invalidate(); | 4835 renderer.invalidate(); |
4510 | 4836 |
4511 return newShadowRoot; | 4837 return newShadowRoot; |
4512 }, | 4838 }, |
4513 | 4839 |
4514 get shadowRoot() { | 4840 get shadowRoot() { |
4515 return this.impl.polymerShadowRoot_ || null; | 4841 return this.impl.polymerShadowRoot_ || null; |
4516 }, | 4842 }, |
4517 | 4843 |
| 4844 // getDestinationInsertionPoints added in ShadowRenderer.js |
| 4845 |
4518 setAttribute: function(name, value) { | 4846 setAttribute: function(name, value) { |
4519 var oldValue = this.impl.getAttribute(name); | 4847 var oldValue = this.impl.getAttribute(name); |
4520 this.impl.setAttribute(name, value); | 4848 this.impl.setAttribute(name, value); |
4521 enqueAttributeChange(this, name, oldValue); | 4849 enqueAttributeChange(this, name, oldValue); |
4522 invalidateRendererBasedOnAttribute(this, name); | 4850 invalidateRendererBasedOnAttribute(this, name); |
4523 }, | 4851 }, |
4524 | 4852 |
4525 removeAttribute: function(name) { | 4853 removeAttribute: function(name) { |
4526 var oldValue = this.impl.getAttribute(name); | 4854 var oldValue = this.impl.getAttribute(name); |
4527 this.impl.removeAttribute(name); | 4855 this.impl.removeAttribute(name); |
(...skipping 428 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
4956 this.setAttribute('select', value); | 5284 this.setAttribute('select', value); |
4957 }, | 5285 }, |
4958 | 5286 |
4959 setAttribute: function(n, v) { | 5287 setAttribute: function(n, v) { |
4960 HTMLElement.prototype.setAttribute.call(this, n, v); | 5288 HTMLElement.prototype.setAttribute.call(this, n, v); |
4961 if (String(n).toLowerCase() === 'select') | 5289 if (String(n).toLowerCase() === 'select') |
4962 this.invalidateShadowRenderer(true); | 5290 this.invalidateShadowRenderer(true); |
4963 } | 5291 } |
4964 | 5292 |
4965 // getDistributedNodes is added in ShadowRenderer | 5293 // getDistributedNodes is added in ShadowRenderer |
4966 | |
4967 // TODO: attribute boolean resetStyleInheritance; | |
4968 }); | 5294 }); |
4969 | 5295 |
4970 if (OriginalHTMLContentElement) | 5296 if (OriginalHTMLContentElement) |
4971 registerWrapper(OriginalHTMLContentElement, HTMLContentElement); | 5297 registerWrapper(OriginalHTMLContentElement, HTMLContentElement); |
4972 | 5298 |
4973 scope.wrappers.HTMLContentElement = HTMLContentElement; | 5299 scope.wrappers.HTMLContentElement = HTMLContentElement; |
4974 })(window.ShadowDOMPolyfill); | 5300 })(window.ShadowDOMPolyfill); |
4975 | 5301 |
4976 // Copyright 2013 The Polymer Authors. All rights reserved. | 5302 // Copyright 2013 The Polymer Authors. All rights reserved. |
4977 // Use of this source code is goverened by a BSD-style | 5303 // Use of this source code is goverened by a BSD-style |
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
5019 | 5345 |
5020 // Copyright 2013 The Polymer Authors. All rights reserved. | 5346 // Copyright 2013 The Polymer Authors. All rights reserved. |
5021 // Use of this source code is goverened by a BSD-style | 5347 // Use of this source code is goverened by a BSD-style |
5022 // license that can be found in the LICENSE file. | 5348 // license that can be found in the LICENSE file. |
5023 | 5349 |
5024 (function(scope) { | 5350 (function(scope) { |
5025 'use strict'; | 5351 'use strict'; |
5026 | 5352 |
5027 var HTMLElement = scope.wrappers.HTMLElement; | 5353 var HTMLElement = scope.wrappers.HTMLElement; |
5028 var mixin = scope.mixin; | 5354 var mixin = scope.mixin; |
| 5355 var NodeList = scope.wrappers.NodeList; |
5029 var registerWrapper = scope.registerWrapper; | 5356 var registerWrapper = scope.registerWrapper; |
5030 | 5357 |
5031 var OriginalHTMLShadowElement = window.HTMLShadowElement; | 5358 var OriginalHTMLShadowElement = window.HTMLShadowElement; |
5032 | 5359 |
5033 function HTMLShadowElement(node) { | 5360 function HTMLShadowElement(node) { |
5034 HTMLElement.call(this, node); | 5361 HTMLElement.call(this, node); |
5035 } | 5362 } |
5036 HTMLShadowElement.prototype = Object.create(HTMLElement.prototype); | 5363 HTMLShadowElement.prototype = Object.create(HTMLElement.prototype); |
5037 mixin(HTMLShadowElement.prototype, { | 5364 |
5038 // TODO: attribute boolean resetStyleInheritance; | 5365 // getDistributedNodes is added in ShadowRenderer |
5039 }); | |
5040 | 5366 |
5041 if (OriginalHTMLShadowElement) | 5367 if (OriginalHTMLShadowElement) |
5042 registerWrapper(OriginalHTMLShadowElement, HTMLShadowElement); | 5368 registerWrapper(OriginalHTMLShadowElement, HTMLShadowElement); |
5043 | 5369 |
5044 scope.wrappers.HTMLShadowElement = HTMLShadowElement; | 5370 scope.wrappers.HTMLShadowElement = HTMLShadowElement; |
5045 })(window.ShadowDOMPolyfill); | 5371 })(window.ShadowDOMPolyfill); |
5046 | 5372 |
5047 // Copyright 2013 The Polymer Authors. All rights reserved. | 5373 // Copyright 2013 The Polymer Authors. All rights reserved. |
5048 // Use of this source code is goverened by a BSD-style | 5374 // Use of this source code is goverened by a BSD-style |
5049 // license that can be found in the LICENSE file. | 5375 // license that can be found in the LICENSE file. |
(...skipping 787 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
5837 var spaceCharRe = /[ \t\n\r\f]/; | 6163 var spaceCharRe = /[ \t\n\r\f]/; |
5838 | 6164 |
5839 function ShadowRoot(hostWrapper) { | 6165 function ShadowRoot(hostWrapper) { |
5840 var node = unwrap(hostWrapper.impl.ownerDocument.createDocumentFragment()); | 6166 var node = unwrap(hostWrapper.impl.ownerDocument.createDocumentFragment()); |
5841 DocumentFragment.call(this, node); | 6167 DocumentFragment.call(this, node); |
5842 | 6168 |
5843 // createDocumentFragment associates the node with a wrapper | 6169 // createDocumentFragment associates the node with a wrapper |
5844 // DocumentFragment instance. Override that. | 6170 // DocumentFragment instance. Override that. |
5845 rewrap(node, this); | 6171 rewrap(node, this); |
5846 | 6172 |
5847 this.treeScope_ = new TreeScope(this, getTreeScope(hostWrapper)); | |
5848 | |
5849 var oldShadowRoot = hostWrapper.shadowRoot; | 6173 var oldShadowRoot = hostWrapper.shadowRoot; |
5850 nextOlderShadowTreeTable.set(this, oldShadowRoot); | 6174 nextOlderShadowTreeTable.set(this, oldShadowRoot); |
5851 | 6175 |
| 6176 this.treeScope_ = |
| 6177 new TreeScope(this, getTreeScope(oldShadowRoot || hostWrapper)); |
| 6178 |
5852 shadowHostTable.set(this, hostWrapper); | 6179 shadowHostTable.set(this, hostWrapper); |
5853 } | 6180 } |
5854 ShadowRoot.prototype = Object.create(DocumentFragment.prototype); | 6181 ShadowRoot.prototype = Object.create(DocumentFragment.prototype); |
5855 mixin(ShadowRoot.prototype, { | 6182 mixin(ShadowRoot.prototype, { |
5856 get innerHTML() { | 6183 get innerHTML() { |
5857 return getInnerHTML(this); | 6184 return getInnerHTML(this); |
5858 }, | 6185 }, |
5859 set innerHTML(value) { | 6186 set innerHTML(value) { |
5860 setInnerHTML(this, value); | 6187 setInnerHTML(this, value); |
5861 this.invalidateShadowRenderer(); | 6188 this.invalidateShadowRenderer(); |
(...skipping 118 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
5980 nodeWrapper.nextSibling.previousSibling_ = nodeWrapper; | 6307 nodeWrapper.nextSibling.previousSibling_ = nodeWrapper; |
5981 | 6308 |
5982 if (parentNodeWrapper.lastChild === nodeWrapper) | 6309 if (parentNodeWrapper.lastChild === nodeWrapper) |
5983 parentNodeWrapper.lastChild_ = nodeWrapper; | 6310 parentNodeWrapper.lastChild_ = nodeWrapper; |
5984 if (parentNodeWrapper.firstChild === nodeWrapper) | 6311 if (parentNodeWrapper.firstChild === nodeWrapper) |
5985 parentNodeWrapper.firstChild_ = nodeWrapper; | 6312 parentNodeWrapper.firstChild_ = nodeWrapper; |
5986 | 6313 |
5987 parentNode.removeChild(node); | 6314 parentNode.removeChild(node); |
5988 } | 6315 } |
5989 | 6316 |
5990 var distributedChildNodesTable = new WeakMap(); | 6317 var distributedNodesTable = new WeakMap(); |
5991 var eventParentsTable = new WeakMap(); | 6318 var destinationInsertionPointsTable = new WeakMap(); |
5992 var insertionParentTable = new WeakMap(); | |
5993 var rendererForHostTable = new WeakMap(); | 6319 var rendererForHostTable = new WeakMap(); |
5994 | 6320 |
5995 function distributeChildToInsertionPoint(child, insertionPoint) { | 6321 function resetDistributedNodes(insertionPoint) { |
5996 getDistributedChildNodes(insertionPoint).push(child); | 6322 distributedNodesTable.set(insertionPoint, []); |
5997 assignToInsertionPoint(child, insertionPoint); | |
5998 | |
5999 var eventParents = eventParentsTable.get(child); | |
6000 if (!eventParents) | |
6001 eventParentsTable.set(child, eventParents = []); | |
6002 eventParents.push(insertionPoint); | |
6003 } | 6323 } |
6004 | 6324 |
6005 function resetDistributedChildNodes(insertionPoint) { | 6325 function getDistributedNodes(insertionPoint) { |
6006 distributedChildNodesTable.set(insertionPoint, []); | 6326 var rv = distributedNodesTable.get(insertionPoint); |
6007 } | |
6008 | |
6009 function getDistributedChildNodes(insertionPoint) { | |
6010 var rv = distributedChildNodesTable.get(insertionPoint); | |
6011 if (!rv) | 6327 if (!rv) |
6012 distributedChildNodesTable.set(insertionPoint, rv = []); | 6328 distributedNodesTable.set(insertionPoint, rv = []); |
6013 return rv; | 6329 return rv; |
6014 } | 6330 } |
6015 | 6331 |
6016 function getChildNodesSnapshot(node) { | 6332 function getChildNodesSnapshot(node) { |
6017 var result = [], i = 0; | 6333 var result = [], i = 0; |
6018 for (var child = node.firstChild; child; child = child.nextSibling) { | 6334 for (var child = node.firstChild; child; child = child.nextSibling) { |
6019 result[i++] = child; | 6335 result[i++] = child; |
6020 } | 6336 } |
6021 return result; | 6337 return result; |
6022 } | 6338 } |
6023 | 6339 |
6024 /** | |
6025 * Visits all nodes in the tree that fulfils the |predicate|. If the |visitor| | |
6026 * function returns |false| the traversal is aborted. | |
6027 * @param {!Node} tree | |
6028 * @param {function(!Node) : boolean} predicate | |
6029 * @param {function(!Node) : *} visitor | |
6030 */ | |
6031 function visit(tree, predicate, visitor) { | |
6032 // This operates on logical DOM. | |
6033 for (var node = tree.firstChild; node; node = node.nextSibling) { | |
6034 if (predicate(node)) { | |
6035 if (visitor(node) === false) | |
6036 return; | |
6037 } else { | |
6038 visit(node, predicate, visitor); | |
6039 } | |
6040 } | |
6041 } | |
6042 | |
6043 // Matching Insertion Points | |
6044 // http://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/shadow/index.html#mat
ching-insertion-points | |
6045 | |
6046 // TODO(arv): Verify this... I don't remember why I picked this regexp. | |
6047 var selectorMatchRegExp = /^[*.:#[a-zA-Z_|]/; | |
6048 | |
6049 var allowedPseudoRegExp = new RegExp('^:(' + [ | |
6050 'link', | |
6051 'visited', | |
6052 'target', | |
6053 'enabled', | |
6054 'disabled', | |
6055 'checked', | |
6056 'indeterminate', | |
6057 'nth-child', | |
6058 'nth-last-child', | |
6059 'nth-of-type', | |
6060 'nth-last-of-type', | |
6061 'first-child', | |
6062 'last-child', | |
6063 'first-of-type', | |
6064 'last-of-type', | |
6065 'only-of-type', | |
6066 ].join('|') + ')'); | |
6067 | |
6068 | |
6069 /** | |
6070 * @param {Element} node | |
6071 * @oaram {Element} point The insertion point element. | |
6072 * @return {boolean} Whether the node matches the insertion point. | |
6073 */ | |
6074 function matchesCriteria(node, point) { | |
6075 var select = point.getAttribute('select'); | |
6076 if (!select) | |
6077 return true; | |
6078 | |
6079 // Here we know the select attribute is a non empty string. | |
6080 select = select.trim(); | |
6081 if (!select) | |
6082 return true; | |
6083 | |
6084 if (!(node instanceof Element)) | |
6085 return false; | |
6086 | |
6087 // The native matches function in IE9 does not correctly work with elements | |
6088 // that are not in the document. | |
6089 // TODO(arv): Implement matching in JS. | |
6090 // https://github.com/Polymer/ShadowDOM/issues/361 | |
6091 if (select === '*' || select === node.localName) | |
6092 return true; | |
6093 | |
6094 // TODO(arv): This does not seem right. Need to check for a simple selector. | |
6095 if (!selectorMatchRegExp.test(select)) | |
6096 return false; | |
6097 | |
6098 // TODO(arv): This no longer matches the spec. | |
6099 if (select[0] === ':' && !allowedPseudoRegExp.test(select)) | |
6100 return false; | |
6101 | |
6102 try { | |
6103 return node.matches(select); | |
6104 } catch (ex) { | |
6105 // Invalid selector. | |
6106 return false; | |
6107 } | |
6108 } | |
6109 | |
6110 var request = oneOf(window, [ | 6340 var request = oneOf(window, [ |
6111 'requestAnimationFrame', | 6341 'requestAnimationFrame', |
6112 'mozRequestAnimationFrame', | 6342 'mozRequestAnimationFrame', |
6113 'webkitRequestAnimationFrame', | 6343 'webkitRequestAnimationFrame', |
6114 'setTimeout' | 6344 'setTimeout' |
6115 ]); | 6345 ]); |
6116 | 6346 |
6117 var pendingDirtyRenderers = []; | 6347 var pendingDirtyRenderers = []; |
6118 var renderTimer; | 6348 var renderTimer; |
6119 | 6349 |
(...skipping 124 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
6244 } | 6474 } |
6245 | 6475 |
6246 ShadowRenderer.prototype = { | 6476 ShadowRenderer.prototype = { |
6247 | 6477 |
6248 // http://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/shadow/index.html#r
endering-shadow-trees | 6478 // http://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/shadow/index.html#r
endering-shadow-trees |
6249 render: function(opt_renderNode) { | 6479 render: function(opt_renderNode) { |
6250 if (!this.dirty) | 6480 if (!this.dirty) |
6251 return; | 6481 return; |
6252 | 6482 |
6253 this.invalidateAttributes(); | 6483 this.invalidateAttributes(); |
6254 this.treeComposition(); | |
6255 | 6484 |
6256 var host = this.host; | 6485 var host = this.host; |
6257 var shadowRoot = host.shadowRoot; | |
6258 | 6486 |
6259 this.associateNode(host); | 6487 this.distribution(host); |
6260 var topMostRenderer = !renderNode; | |
6261 var renderNode = opt_renderNode || new RenderNode(host); | 6488 var renderNode = opt_renderNode || new RenderNode(host); |
| 6489 this.buildRenderTree(renderNode, host); |
6262 | 6490 |
6263 for (var node = shadowRoot.firstChild; node; node = node.nextSibling) { | 6491 var topMostRenderer = !opt_renderNode; |
6264 this.renderNode(shadowRoot, renderNode, node, false); | |
6265 } | |
6266 | |
6267 if (topMostRenderer) | 6492 if (topMostRenderer) |
6268 renderNode.sync(); | 6493 renderNode.sync(); |
6269 | 6494 |
6270 this.dirty = false; | 6495 this.dirty = false; |
6271 }, | 6496 }, |
6272 | 6497 |
6273 get parentRenderer() { | 6498 get parentRenderer() { |
6274 return getTreeScope(this.host).renderer; | 6499 return getTreeScope(this.host).renderer; |
6275 }, | 6500 }, |
6276 | 6501 |
6277 invalidate: function() { | 6502 invalidate: function() { |
6278 if (!this.dirty) { | 6503 if (!this.dirty) { |
6279 this.dirty = true; | 6504 this.dirty = true; |
6280 pendingDirtyRenderers.push(this); | 6505 pendingDirtyRenderers.push(this); |
6281 if (renderTimer) | 6506 if (renderTimer) |
6282 return; | 6507 return; |
6283 renderTimer = window[request](handleRequestAnimationFrame, 0); | 6508 renderTimer = window[request](handleRequestAnimationFrame, 0); |
6284 } | 6509 } |
6285 }, | 6510 }, |
6286 | 6511 |
6287 renderNode: function(shadowRoot, renderNode, node, isNested) { | 6512 // http://w3c.github.io/webcomponents/spec/shadow/#distribution-algorithms |
| 6513 distribution: function(root) { |
| 6514 this.resetAll(root); |
| 6515 this.distributionResolution(root); |
| 6516 }, |
| 6517 |
| 6518 resetAll: function(node) { |
| 6519 if (isInsertionPoint(node)) |
| 6520 resetDistributedNodes(node); |
| 6521 else |
| 6522 resetDestinationInsertionPoints(node); |
| 6523 |
| 6524 for (var child = node.firstChild; child; child = child.nextSibling) { |
| 6525 this.resetAll(child); |
| 6526 } |
| 6527 |
| 6528 if (node.shadowRoot) |
| 6529 this.resetAll(node.shadowRoot); |
| 6530 |
| 6531 if (node.olderShadowRoot) |
| 6532 this.resetAll(node.olderShadowRoot); |
| 6533 }, |
| 6534 |
| 6535 // http://w3c.github.io/webcomponents/spec/shadow/#distribution-results |
| 6536 distributionResolution: function(node) { |
6288 if (isShadowHost(node)) { | 6537 if (isShadowHost(node)) { |
6289 renderNode = renderNode.append(node); | 6538 var shadowHost = node; |
6290 var renderer = getRendererForHost(node); | 6539 // 1.1 |
6291 renderer.dirty = true; // Need to rerender due to reprojection. | 6540 var pool = poolPopulation(shadowHost); |
6292 renderer.render(renderNode); | 6541 |
6293 } else if (isInsertionPoint(node)) { | 6542 var shadowTrees = getShadowTrees(shadowHost); |
6294 this.renderInsertionPoint(shadowRoot, renderNode, node, isNested); | 6543 |
6295 } else if (isShadowInsertionPoint(node)) { | 6544 // 1.2 |
6296 this.renderShadowInsertionPoint(shadowRoot, renderNode, node); | 6545 for (var i = 0; i < shadowTrees.length; i++) { |
6297 } else { | 6546 // 1.2.1 |
6298 this.renderAsAnyDomTree(shadowRoot, renderNode, node, isNested); | 6547 this.poolDistribution(shadowTrees[i], pool); |
| 6548 } |
| 6549 |
| 6550 // 1.3 |
| 6551 for (var i = shadowTrees.length - 1; i >= 0; i--) { |
| 6552 var shadowTree = shadowTrees[i]; |
| 6553 |
| 6554 // 1.3.1 |
| 6555 // TODO(arv): We should keep the shadow insertion points on the |
| 6556 // shadow root (or renderer) so we don't have to search the tree |
| 6557 // every time. |
| 6558 var shadow = getShadowInsertionPoint(shadowTree); |
| 6559 |
| 6560 // 1.3.2 |
| 6561 if (shadow) { |
| 6562 |
| 6563 // 1.3.2.1 |
| 6564 var olderShadowRoot = shadowTree.olderShadowRoot; |
| 6565 if (olderShadowRoot) { |
| 6566 // 1.3.2.1.1 |
| 6567 pool = poolPopulation(olderShadowRoot); |
| 6568 } |
| 6569 |
| 6570 // 1.3.2.2 |
| 6571 for (var j = 0; j < pool.length; j++) { |
| 6572 // 1.3.2.2.1 |
| 6573 destributeNodeInto(pool[j], shadow); |
| 6574 } |
| 6575 } |
| 6576 |
| 6577 // 1.3.3 |
| 6578 this.distributionResolution(shadowTree); |
| 6579 } |
| 6580 } |
| 6581 |
| 6582 for (var child = node.firstChild; child; child = child.nextSibling) { |
| 6583 this.distributionResolution(child); |
6299 } | 6584 } |
6300 }, | 6585 }, |
6301 | 6586 |
6302 renderAsAnyDomTree: function(shadowRoot, renderNode, node, isNested) { | 6587 // http://w3c.github.io/webcomponents/spec/shadow/#dfn-pool-distribution-alg
orithm |
6303 renderNode = renderNode.append(node); | 6588 poolDistribution: function (node, pool) { |
| 6589 if (node instanceof HTMLShadowElement) |
| 6590 return; |
| 6591 |
| 6592 if (node instanceof HTMLContentElement) { |
| 6593 var content = node; |
| 6594 this.updateDependentAttributes(content.getAttribute('select')); |
| 6595 |
| 6596 var anyDistributed = false; |
| 6597 |
| 6598 // 1.1 |
| 6599 for (var i = 0; i < pool.length; i++) { |
| 6600 var node = pool[i]; |
| 6601 if (!node) |
| 6602 continue; |
| 6603 if (matches(node, content)) { |
| 6604 destributeNodeInto(node, content); |
| 6605 pool[i] = undefined; |
| 6606 anyDistributed = true; |
| 6607 } |
| 6608 } |
| 6609 |
| 6610 // 1.2 |
| 6611 // Fallback content |
| 6612 if (!anyDistributed) { |
| 6613 for (var child = content.firstChild; |
| 6614 child; |
| 6615 child = child.nextSibling) { |
| 6616 destributeNodeInto(child, content); |
| 6617 } |
| 6618 } |
| 6619 |
| 6620 return; |
| 6621 } |
| 6622 |
| 6623 for (var child = node.firstChild; child; child = child.nextSibling) { |
| 6624 this.poolDistribution(child, pool); |
| 6625 } |
| 6626 }, |
| 6627 |
| 6628 buildRenderTree: function(renderNode, node) { |
| 6629 var children = this.compose(node); |
| 6630 for (var i = 0; i < children.length; i++) { |
| 6631 var child = children[i]; |
| 6632 var childRenderNode = renderNode.append(child); |
| 6633 this.buildRenderTree(childRenderNode, child); |
| 6634 } |
6304 | 6635 |
6305 if (isShadowHost(node)) { | 6636 if (isShadowHost(node)) { |
6306 var renderer = getRendererForHost(node); | 6637 var renderer = getRendererForHost(node); |
6307 renderNode.skip = !renderer.dirty; | 6638 renderer.dirty = false; |
6308 renderer.render(renderNode); | 6639 } |
6309 } else { | 6640 |
6310 for (var child = node.firstChild; child; child = child.nextSibling) { | 6641 }, |
6311 this.renderNode(shadowRoot, renderNode, child, isNested); | 6642 |
| 6643 compose: function(node) { |
| 6644 var children = []; |
| 6645 var p = node.shadowRoot || node; |
| 6646 for (var child = p.firstChild; child; child = child.nextSibling) { |
| 6647 if (isInsertionPoint(child)) { |
| 6648 this.associateNode(p); |
| 6649 var distributedNodes = getDistributedNodes(child); |
| 6650 for (var j = 0; j < distributedNodes.length; j++) { |
| 6651 var distributedNode = distributedNodes[j]; |
| 6652 if (isFinalDestination(child, distributedNode)) |
| 6653 children.push(distributedNode); |
| 6654 } |
| 6655 } else { |
| 6656 children.push(child); |
6312 } | 6657 } |
6313 } | 6658 } |
6314 }, | 6659 return children; |
6315 | |
6316 renderInsertionPoint: function(shadowRoot, renderNode, insertionPoint, | |
6317 isNested) { | |
6318 var distributedChildNodes = getDistributedChildNodes(insertionPoint); | |
6319 if (distributedChildNodes.length) { | |
6320 this.associateNode(insertionPoint); | |
6321 | |
6322 for (var i = 0; i < distributedChildNodes.length; i++) { | |
6323 var child = distributedChildNodes[i]; | |
6324 if (isInsertionPoint(child) && isNested) | |
6325 this.renderInsertionPoint(shadowRoot, renderNode, child, isNested); | |
6326 else | |
6327 this.renderAsAnyDomTree(shadowRoot, renderNode, child, isNested); | |
6328 } | |
6329 } else { | |
6330 this.renderFallbackContent(shadowRoot, renderNode, insertionPoint); | |
6331 } | |
6332 this.associateNode(insertionPoint.parentNode); | |
6333 }, | |
6334 | |
6335 renderShadowInsertionPoint: function(shadowRoot, renderNode, | |
6336 shadowInsertionPoint) { | |
6337 var nextOlderTree = shadowRoot.olderShadowRoot; | |
6338 if (nextOlderTree) { | |
6339 assignToInsertionPoint(nextOlderTree, shadowInsertionPoint); | |
6340 this.associateNode(shadowInsertionPoint.parentNode); | |
6341 for (var node = nextOlderTree.firstChild; | |
6342 node; | |
6343 node = node.nextSibling) { | |
6344 this.renderNode(nextOlderTree, renderNode, node, true); | |
6345 } | |
6346 } else { | |
6347 this.renderFallbackContent(shadowRoot, renderNode, | |
6348 shadowInsertionPoint); | |
6349 } | |
6350 }, | |
6351 | |
6352 renderFallbackContent: function(shadowRoot, renderNode, fallbackHost) { | |
6353 this.associateNode(fallbackHost); | |
6354 this.associateNode(fallbackHost.parentNode); | |
6355 for (var node = fallbackHost.firstChild; node; node = node.nextSibling) { | |
6356 this.renderAsAnyDomTree(shadowRoot, renderNode, node, false); | |
6357 } | |
6358 }, | 6660 }, |
6359 | 6661 |
6360 /** | 6662 /** |
6361 * Invalidates the attributes used to keep track of which attributes may | 6663 * Invalidates the attributes used to keep track of which attributes may |
6362 * cause the renderer to be invalidated. | 6664 * cause the renderer to be invalidated. |
6363 */ | 6665 */ |
6364 invalidateAttributes: function() { | 6666 invalidateAttributes: function() { |
6365 this.attributes = Object.create(null); | 6667 this.attributes = Object.create(null); |
6366 }, | 6668 }, |
6367 | 6669 |
(...skipping 20 matching lines...) Expand all Loading... |
6388 attributes[name] = true; | 6690 attributes[name] = true; |
6389 }); | 6691 }); |
6390 | 6692 |
6391 // Pseudo selectors have been removed from the spec. | 6693 // Pseudo selectors have been removed from the spec. |
6392 }, | 6694 }, |
6393 | 6695 |
6394 dependsOnAttribute: function(name) { | 6696 dependsOnAttribute: function(name) { |
6395 return this.attributes[name]; | 6697 return this.attributes[name]; |
6396 }, | 6698 }, |
6397 | 6699 |
6398 // http://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/shadow/index.html#d
fn-distribution-algorithm | |
6399 distribute: function(tree, pool) { | |
6400 var self = this; | |
6401 | |
6402 visit(tree, isActiveInsertionPoint, | |
6403 function(insertionPoint) { | |
6404 resetDistributedChildNodes(insertionPoint); | |
6405 self.updateDependentAttributes( | |
6406 insertionPoint.getAttribute('select')); | |
6407 | |
6408 for (var i = 0; i < pool.length; i++) { // 1.2 | |
6409 var node = pool[i]; // 1.2.1 | |
6410 if (node === undefined) // removed | |
6411 continue; | |
6412 if (matchesCriteria(node, insertionPoint)) { // 1.2.2 | |
6413 distributeChildToInsertionPoint(node, insertionPoint); // 1.2.2
.1 | |
6414 pool[i] = undefined; // 1.2.2.2 | |
6415 } | |
6416 } | |
6417 }); | |
6418 }, | |
6419 | |
6420 // http://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/shadow/index.html#d
fn-tree-composition | |
6421 treeComposition: function () { | |
6422 var shadowHost = this.host; | |
6423 var tree = shadowHost.shadowRoot; // 1. | |
6424 var pool = []; // 2. | |
6425 | |
6426 for (var child = shadowHost.firstChild; | |
6427 child; | |
6428 child = child.nextSibling) { // 3. | |
6429 if (isInsertionPoint(child)) { // 3.2. | |
6430 var reprojected = getDistributedChildNodes(child); // 3.2.1. | |
6431 // if reprojected is undef... reset it? | |
6432 if (!reprojected || !reprojected.length) // 3.2.2. | |
6433 reprojected = getChildNodesSnapshot(child); | |
6434 pool.push.apply(pool, reprojected); // 3.2.3. | |
6435 } else { | |
6436 pool.push(child); // 3.3. | |
6437 } | |
6438 } | |
6439 | |
6440 var shadowInsertionPoint, point; | |
6441 while (tree) { // 4. | |
6442 // 4.1. | |
6443 shadowInsertionPoint = undefined; // Reset every iteration. | |
6444 visit(tree, isActiveShadowInsertionPoint, function(point) { | |
6445 shadowInsertionPoint = point; | |
6446 return false; | |
6447 }); | |
6448 point = shadowInsertionPoint; | |
6449 | |
6450 this.distribute(tree, pool); // 4.2. | |
6451 if (point) { // 4.3. | |
6452 var nextOlderTree = tree.olderShadowRoot; // 4.3.1. | |
6453 if (!nextOlderTree) { | |
6454 break; // 4.3.1.1. | |
6455 } else { | |
6456 tree = nextOlderTree; // 4.3.2.2. | |
6457 assignToInsertionPoint(tree, point); // 4.3.2.2. | |
6458 continue; // 4.3.2.3. | |
6459 } | |
6460 } else { | |
6461 break; // 4.4. | |
6462 } | |
6463 } | |
6464 }, | |
6465 | |
6466 associateNode: function(node) { | 6700 associateNode: function(node) { |
6467 node.impl.polymerShadowRenderer_ = this; | 6701 node.impl.polymerShadowRenderer_ = this; |
6468 } | 6702 } |
6469 }; | 6703 }; |
6470 | 6704 |
6471 function isInsertionPoint(node) { | 6705 // http://w3c.github.io/webcomponents/spec/shadow/#dfn-pool-population-algorit
hm |
6472 // Should this include <shadow>? | 6706 function poolPopulation(node) { |
6473 return node instanceof HTMLContentElement; | 6707 var pool = []; |
| 6708 for (var child = node.firstChild; child; child = child.nextSibling) { |
| 6709 if (isInsertionPoint(child)) { |
| 6710 pool.push.apply(pool, getDistributedNodes(child)); |
| 6711 } else { |
| 6712 pool.push(child); |
| 6713 } |
| 6714 } |
| 6715 return pool; |
6474 } | 6716 } |
6475 | 6717 |
6476 function isActiveInsertionPoint(node) { | 6718 function getShadowInsertionPoint(node) { |
6477 // <content> inside another <content> or <shadow> is considered inactive. | 6719 if (node instanceof HTMLShadowElement) |
6478 return node instanceof HTMLContentElement; | 6720 return node; |
| 6721 if (node instanceof HTMLContentElement) |
| 6722 return null; |
| 6723 for (var child = node.firstChild; child; child = child.nextSibling) { |
| 6724 var res = getShadowInsertionPoint(child); |
| 6725 if (res) |
| 6726 return res; |
| 6727 } |
| 6728 return null; |
6479 } | 6729 } |
6480 | 6730 |
6481 function isShadowInsertionPoint(node) { | 6731 function destributeNodeInto(child, insertionPoint) { |
6482 return node instanceof HTMLShadowElement; | 6732 getDistributedNodes(insertionPoint).push(child); |
| 6733 var points = destinationInsertionPointsTable.get(child); |
| 6734 if (!points) |
| 6735 destinationInsertionPointsTable.set(child, [insertionPoint]); |
| 6736 else |
| 6737 points.push(insertionPoint); |
6483 } | 6738 } |
6484 | 6739 |
6485 function isActiveShadowInsertionPoint(node) { | 6740 function getDestinationInsertionPoints(node) { |
6486 // <shadow> inside another <content> or <shadow> is considered inactive. | 6741 return destinationInsertionPointsTable.get(node); |
6487 return node instanceof HTMLShadowElement; | 6742 } |
| 6743 |
| 6744 function resetDestinationInsertionPoints(node) { |
| 6745 // IE11 crashes when delete is used. |
| 6746 destinationInsertionPointsTable.set(node, undefined); |
| 6747 } |
| 6748 |
| 6749 // AllowedSelectors : |
| 6750 // TypeSelector |
| 6751 // * |
| 6752 // ClassSelector |
| 6753 // IDSelector |
| 6754 // AttributeSelector |
| 6755 var selectorStartCharRe = /^[*.#[a-zA-Z_|]/; |
| 6756 |
| 6757 function matches(node, contentElement) { |
| 6758 var select = contentElement.getAttribute('select'); |
| 6759 if (!select) |
| 6760 return true; |
| 6761 |
| 6762 // Here we know the select attribute is a non empty string. |
| 6763 select = select.trim(); |
| 6764 if (!select) |
| 6765 return true; |
| 6766 |
| 6767 if (!(node instanceof Element)) |
| 6768 return false; |
| 6769 |
| 6770 if (!selectorStartCharRe.test(select)) |
| 6771 return false; |
| 6772 |
| 6773 try { |
| 6774 return node.matches(select); |
| 6775 } catch (ex) { |
| 6776 // Invalid selector. |
| 6777 return false; |
| 6778 } |
| 6779 } |
| 6780 |
| 6781 function isFinalDestination(insertionPoint, node) { |
| 6782 var points = getDestinationInsertionPoints(node); |
| 6783 return points && points[points.length - 1] === insertionPoint; |
| 6784 } |
| 6785 |
| 6786 function isInsertionPoint(node) { |
| 6787 return node instanceof HTMLContentElement || |
| 6788 node instanceof HTMLShadowElement; |
6488 } | 6789 } |
6489 | 6790 |
6490 function isShadowHost(shadowHost) { | 6791 function isShadowHost(shadowHost) { |
6491 return shadowHost.shadowRoot; | 6792 return shadowHost.shadowRoot; |
6492 } | 6793 } |
6493 | 6794 |
| 6795 // Returns the shadow trees as an array, with the youngest tree at the |
| 6796 // beginning of the array. |
6494 function getShadowTrees(host) { | 6797 function getShadowTrees(host) { |
6495 var trees = []; | 6798 var trees = []; |
6496 | 6799 |
6497 for (var tree = host.shadowRoot; tree; tree = tree.olderShadowRoot) { | 6800 for (var tree = host.shadowRoot; tree; tree = tree.olderShadowRoot) { |
6498 trees.push(tree); | 6801 trees.push(tree); |
6499 } | 6802 } |
6500 return trees; | 6803 return trees; |
6501 } | 6804 } |
6502 | 6805 |
6503 function assignToInsertionPoint(tree, point) { | |
6504 insertionParentTable.set(tree, point); | |
6505 } | |
6506 | |
6507 // http://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/shadow/index.html#ren
dering-shadow-trees | |
6508 function render(host) { | 6806 function render(host) { |
6509 new ShadowRenderer(host).render(); | 6807 new ShadowRenderer(host).render(); |
6510 }; | 6808 }; |
6511 | 6809 |
6512 // Need to rerender shadow host when: | 6810 // Need to rerender shadow host when: |
6513 // | 6811 // |
6514 // - a direct child to the ShadowRoot is added or removed | 6812 // - a direct child to the ShadowRoot is added or removed |
6515 // - a direct child to the host is added or removed | 6813 // - a direct child to the host is added or removed |
6516 // - a new shadow root is created | 6814 // - a new shadow root is created |
6517 // - a direct child to a content/shadow element is added or removed | 6815 // - a direct child to a content/shadow element is added or removed |
6518 // - a sibling to a content/shadow element is added or removed | 6816 // - a sibling to a content/shadow element is added or removed |
6519 // - content[select] is changed | 6817 // - content[select] is changed |
6520 // - an attribute in a direct child to a host is modified | 6818 // - an attribute in a direct child to a host is modified |
6521 | 6819 |
6522 /** | 6820 /** |
6523 * This gets called when a node was added or removed to it. | 6821 * This gets called when a node was added or removed to it. |
6524 */ | 6822 */ |
6525 Node.prototype.invalidateShadowRenderer = function(force) { | 6823 Node.prototype.invalidateShadowRenderer = function(force) { |
6526 var renderer = this.impl.polymerShadowRenderer_; | 6824 var renderer = this.impl.polymerShadowRenderer_; |
6527 if (renderer) { | 6825 if (renderer) { |
6528 renderer.invalidate(); | 6826 renderer.invalidate(); |
6529 return true; | 6827 return true; |
6530 } | 6828 } |
6531 | 6829 |
6532 return false; | 6830 return false; |
6533 }; | 6831 }; |
6534 | 6832 |
6535 HTMLContentElement.prototype.getDistributedNodes = function() { | 6833 HTMLContentElement.prototype.getDistributedNodes = |
| 6834 HTMLShadowElement.prototype.getDistributedNodes = function() { |
6536 // TODO(arv): We should only rerender the dirty ancestor renderers (from | 6835 // TODO(arv): We should only rerender the dirty ancestor renderers (from |
6537 // the root and down). | 6836 // the root and down). |
6538 renderAllPending(); | 6837 renderAllPending(); |
6539 return getDistributedChildNodes(this); | 6838 return getDistributedNodes(this); |
6540 }; | 6839 }; |
6541 | 6840 |
6542 HTMLShadowElement.prototype.nodeIsInserted_ = | 6841 Element.prototype.getDestinationInsertionPoints = function() { |
6543 HTMLContentElement.prototype.nodeIsInserted_ = function() { | 6842 renderAllPending(); |
| 6843 return getDestinationInsertionPoints(this) || []; |
| 6844 }; |
| 6845 |
| 6846 HTMLContentElement.prototype.nodeIsInserted_ = |
| 6847 HTMLShadowElement.prototype.nodeIsInserted_ = function() { |
6544 // Invalidate old renderer if any. | 6848 // Invalidate old renderer if any. |
6545 this.invalidateShadowRenderer(); | 6849 this.invalidateShadowRenderer(); |
6546 | 6850 |
6547 var shadowRoot = getShadowRootAncestor(this); | 6851 var shadowRoot = getShadowRootAncestor(this); |
6548 var renderer; | 6852 var renderer; |
6549 if (shadowRoot) | 6853 if (shadowRoot) |
6550 renderer = getRendererForShadowRoot(shadowRoot); | 6854 renderer = getRendererForShadowRoot(shadowRoot); |
6551 this.impl.polymerShadowRenderer_ = renderer; | 6855 this.impl.polymerShadowRenderer_ = renderer; |
6552 if (renderer) | 6856 if (renderer) |
6553 renderer.invalidate(); | 6857 renderer.invalidate(); |
6554 }; | 6858 }; |
6555 | 6859 |
6556 scope.eventParentsTable = eventParentsTable; | |
6557 scope.getRendererForHost = getRendererForHost; | 6860 scope.getRendererForHost = getRendererForHost; |
6558 scope.getShadowTrees = getShadowTrees; | 6861 scope.getShadowTrees = getShadowTrees; |
6559 scope.insertionParentTable = insertionParentTable; | |
6560 scope.renderAllPending = renderAllPending; | 6862 scope.renderAllPending = renderAllPending; |
6561 | 6863 |
| 6864 scope.getDestinationInsertionPoints = getDestinationInsertionPoints; |
| 6865 |
6562 // Exposed for testing | 6866 // Exposed for testing |
6563 scope.visual = { | 6867 scope.visual = { |
6564 insertBefore: insertBefore, | 6868 insertBefore: insertBefore, |
6565 remove: remove, | 6869 remove: remove, |
6566 }; | 6870 }; |
6567 | 6871 |
6568 })(window.ShadowDOMPolyfill); | 6872 })(window.ShadowDOMPolyfill); |
6569 | 6873 |
6570 // Copyright 2013 The Polymer Authors. All rights reserved. | 6874 // Copyright 2013 The Polymer Authors. All rights reserved. |
6571 // Use of this source code is goverened by a BSD-style | 6875 // Use of this source code is goverened by a BSD-style |
(...skipping 377 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
6949 | 7253 |
6950 mixin(Document.prototype, { | 7254 mixin(Document.prototype, { |
6951 get implementation() { | 7255 get implementation() { |
6952 var implementation = implementationTable.get(this); | 7256 var implementation = implementationTable.get(this); |
6953 if (implementation) | 7257 if (implementation) |
6954 return implementation; | 7258 return implementation; |
6955 implementation = | 7259 implementation = |
6956 new DOMImplementation(unwrap(this).implementation); | 7260 new DOMImplementation(unwrap(this).implementation); |
6957 implementationTable.set(this, implementation); | 7261 implementationTable.set(this, implementation); |
6958 return implementation; | 7262 return implementation; |
| 7263 }, |
| 7264 |
| 7265 get defaultView() { |
| 7266 return wrap(unwrap(this).defaultView); |
6959 } | 7267 } |
6960 }); | 7268 }); |
6961 | 7269 |
6962 registerWrapper(window.Document, Document, | 7270 registerWrapper(window.Document, Document, |
6963 document.implementation.createHTMLDocument('')); | 7271 document.implementation.createHTMLDocument('')); |
6964 | 7272 |
6965 // Both WebKit and Gecko uses HTMLDocument for document. HTML5/DOM only has | 7273 // Both WebKit and Gecko uses HTMLDocument for document. HTML5/DOM only has |
6966 // one Document interface and IE implements the standard correctly. | 7274 // one Document interface and IE implements the standard correctly. |
6967 if (window.HTMLDocument) | 7275 if (window.HTMLDocument) |
6968 registerWrapper(window.HTMLDocument, Document); | 7276 registerWrapper(window.HTMLDocument, Document); |
(...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
7064 mixin(Window.prototype, { | 7372 mixin(Window.prototype, { |
7065 getComputedStyle: function(el, pseudo) { | 7373 getComputedStyle: function(el, pseudo) { |
7066 renderAllPending(); | 7374 renderAllPending(); |
7067 return originalGetComputedStyle.call(unwrap(this), unwrapIfNeeded(el), | 7375 return originalGetComputedStyle.call(unwrap(this), unwrapIfNeeded(el), |
7068 pseudo); | 7376 pseudo); |
7069 }, | 7377 }, |
7070 getSelection: function() { | 7378 getSelection: function() { |
7071 renderAllPending(); | 7379 renderAllPending(); |
7072 return new Selection(originalGetSelection.call(unwrap(this))); | 7380 return new Selection(originalGetSelection.call(unwrap(this))); |
7073 }, | 7381 }, |
| 7382 |
| 7383 get document() { |
| 7384 return wrap(unwrap(this).document); |
| 7385 } |
7074 }); | 7386 }); |
7075 | 7387 |
7076 registerWrapper(OriginalWindow, Window); | 7388 registerWrapper(OriginalWindow, Window, window); |
7077 | 7389 |
7078 scope.wrappers.Window = Window; | 7390 scope.wrappers.Window = Window; |
7079 | 7391 |
7080 })(window.ShadowDOMPolyfill); | 7392 })(window.ShadowDOMPolyfill); |
7081 | 7393 |
7082 /** | 7394 /** |
7083 * Copyright 2014 The Polymer Authors. All rights reserved. | 7395 * Copyright 2014 The Polymer Authors. All rights reserved. |
7084 * Use of this source code is goverened by a BSD-style | 7396 * Use of this source code is goverened by a BSD-style |
7085 * license that can be found in the LICENSE file. | 7397 * license that can be found in the LICENSE file. |
7086 */ | 7398 */ |
(...skipping 120 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
7207 | 7519 |
7208 Object.keys(elements).forEach(overrideConstructor); | 7520 Object.keys(elements).forEach(overrideConstructor); |
7209 | 7521 |
7210 Object.getOwnPropertyNames(scope.wrappers).forEach(function(name) { | 7522 Object.getOwnPropertyNames(scope.wrappers).forEach(function(name) { |
7211 window[name] = scope.wrappers[name] | 7523 window[name] = scope.wrappers[name] |
7212 }); | 7524 }); |
7213 | 7525 |
7214 })(window.ShadowDOMPolyfill); | 7526 })(window.ShadowDOMPolyfill); |
7215 | 7527 |
7216 /* | 7528 /* |
7217 * Copyright 2013 The Polymer Authors. All rights reserved. | 7529 * Copyright (c) 2014 The Polymer Project Authors. All rights reserved. |
7218 * Use of this source code is governed by a BSD-style | 7530 * This code may only be used under the BSD style license found at http://polyme
r.github.io/LICENSE.txt |
7219 * license that can be found in the LICENSE file. | 7531 * The complete set of authors may be found at http://polymer.github.io/AUTHORS.
txt |
| 7532 * The complete set of contributors may be found at http://polymer.github.io/CON
TRIBUTORS.txt |
| 7533 * Code distributed by Google as part of the polymer project is also |
| 7534 * subject to an additional IP rights grant found at http://polymer.github.io/PA
TENTS.txt |
7220 */ | 7535 */ |
| 7536 |
7221 (function() { | 7537 (function() { |
7222 | 7538 |
7223 // convenient global | 7539 // convenient global |
7224 window.wrap = ShadowDOMPolyfill.wrapIfNeeded; | 7540 window.wrap = ShadowDOMPolyfill.wrapIfNeeded; |
7225 window.unwrap = ShadowDOMPolyfill.unwrapIfNeeded; | 7541 window.unwrap = ShadowDOMPolyfill.unwrapIfNeeded; |
7226 | 7542 |
7227 // users may want to customize other types | 7543 // users may want to customize other types |
7228 // TODO(sjmiles): 'button' is now supported by ShadowDOMPolyfill, but | 7544 // TODO(sjmiles): 'button' is now supported by ShadowDOMPolyfill, but |
7229 // I've left this code here in case we need to temporarily patch another | 7545 // I've left this code here in case we need to temporarily patch another |
7230 // type | 7546 // type |
(...skipping 16 matching lines...) Expand all Loading... |
7247 Element.prototype.createShadowRoot = function() { | 7563 Element.prototype.createShadowRoot = function() { |
7248 var root = originalCreateShadowRoot.call(this); | 7564 var root = originalCreateShadowRoot.call(this); |
7249 CustomElements.watchShadow(this); | 7565 CustomElements.watchShadow(this); |
7250 return root; | 7566 return root; |
7251 }; | 7567 }; |
7252 | 7568 |
7253 Element.prototype.webkitCreateShadowRoot = Element.prototype.createShadowRoot; | 7569 Element.prototype.webkitCreateShadowRoot = Element.prototype.createShadowRoot; |
7254 })(); | 7570 })(); |
7255 | 7571 |
7256 /* | 7572 /* |
7257 * Copyright 2012 The Polymer Authors. All rights reserved. | 7573 * Copyright (c) 2014 The Polymer Project Authors. All rights reserved. |
7258 * Use of this source code is governed by a BSD-style | 7574 * This code may only be used under the BSD style license found at http://polyme
r.github.io/LICENSE.txt |
7259 * license that can be found in the LICENSE file. | 7575 * The complete set of authors may be found at http://polymer.github.io/AUTHORS.
txt |
| 7576 * The complete set of contributors may be found at http://polymer.github.io/CON
TRIBUTORS.txt |
| 7577 * Code distributed by Google as part of the polymer project is also |
| 7578 * subject to an additional IP rights grant found at http://polymer.github.io/PA
TENTS.txt |
7260 */ | 7579 */ |
7261 | 7580 |
7262 /* | 7581 /* |
7263 This is a limited shim for ShadowDOM css styling. | 7582 This is a limited shim for ShadowDOM css styling. |
7264 https://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/shadow/index.html#style
s | 7583 https://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/shadow/index.html#style
s |
7265 | 7584 |
7266 The intention here is to support only the styling features which can be | 7585 The intention here is to support only the styling features which can be |
7267 relatively simply implemented. The goal is to allow users to avoid the | 7586 relatively simply implemented. The goal is to allow users to avoid the |
7268 most obvious pitfalls and do so without compromising performance significantly
. | 7587 most obvious pitfalls and do so without compromising performance significantly
. |
7269 For ShadowDOM styling that's not covered here, a set of best practices | 7588 For ShadowDOM styling that's not covered here, a set of best practices |
(...skipping 395 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
7665 } | 7984 } |
7666 return cssText; | 7985 return cssText; |
7667 }, | 7986 }, |
7668 scopeSelector: function(selector, scopeSelector, strict) { | 7987 scopeSelector: function(selector, scopeSelector, strict) { |
7669 var r = [], parts = selector.split(','); | 7988 var r = [], parts = selector.split(','); |
7670 parts.forEach(function(p) { | 7989 parts.forEach(function(p) { |
7671 p = p.trim(); | 7990 p = p.trim(); |
7672 if (this.selectorNeedsScoping(p, scopeSelector)) { | 7991 if (this.selectorNeedsScoping(p, scopeSelector)) { |
7673 p = (strict && !p.match(polyfillHostNoCombinator)) ? | 7992 p = (strict && !p.match(polyfillHostNoCombinator)) ? |
7674 this.applyStrictSelectorScope(p, scopeSelector) : | 7993 this.applyStrictSelectorScope(p, scopeSelector) : |
7675 this.applySimpleSelectorScope(p, scopeSelector); | 7994 this.applySelectorScope(p, scopeSelector); |
7676 } | 7995 } |
7677 r.push(p); | 7996 r.push(p); |
7678 }, this); | 7997 }, this); |
7679 return r.join(', '); | 7998 return r.join(', '); |
7680 }, | 7999 }, |
7681 selectorNeedsScoping: function(selector, scopeSelector) { | 8000 selectorNeedsScoping: function(selector, scopeSelector) { |
| 8001 if (Array.isArray(scopeSelector)) { |
| 8002 return true; |
| 8003 } |
7682 var re = this.makeScopeMatcher(scopeSelector); | 8004 var re = this.makeScopeMatcher(scopeSelector); |
7683 return !selector.match(re); | 8005 return !selector.match(re); |
7684 }, | 8006 }, |
7685 makeScopeMatcher: function(scopeSelector) { | 8007 makeScopeMatcher: function(scopeSelector) { |
7686 scopeSelector = scopeSelector.replace(/\[/g, '\\[').replace(/\[/g, '\\]'); | 8008 scopeSelector = scopeSelector.replace(/\[/g, '\\[').replace(/\[/g, '\\]'); |
7687 return new RegExp('^(' + scopeSelector + ')' + selectorReSuffix, 'm'); | 8009 return new RegExp('^(' + scopeSelector + ')' + selectorReSuffix, 'm'); |
7688 }, | 8010 }, |
| 8011 applySelectorScope: function(selector, selectorScope) { |
| 8012 return Array.isArray(selectorScope) ? |
| 8013 this.applySelectorScopeList(selector, selectorScope) : |
| 8014 this.applySimpleSelectorScope(selector, selectorScope); |
| 8015 }, |
| 8016 // apply an array of selectors |
| 8017 applySelectorScopeList: function(selector, scopeSelectorList) { |
| 8018 var r = []; |
| 8019 for (var i=0, s; (s=scopeSelectorList[i]); i++) { |
| 8020 r.push(this.applySimpleSelectorScope(selector, s)); |
| 8021 } |
| 8022 return r.join(', '); |
| 8023 }, |
7689 // scope via name and [is=name] | 8024 // scope via name and [is=name] |
7690 applySimpleSelectorScope: function(selector, scopeSelector) { | 8025 applySimpleSelectorScope: function(selector, scopeSelector) { |
7691 if (selector.match(polyfillHostRe)) { | 8026 if (selector.match(polyfillHostRe)) { |
7692 selector = selector.replace(polyfillHostNoCombinator, scopeSelector); | 8027 selector = selector.replace(polyfillHostNoCombinator, scopeSelector); |
7693 return selector.replace(polyfillHostRe, scopeSelector + ' '); | 8028 return selector.replace(polyfillHostRe, scopeSelector + ' '); |
7694 } else { | 8029 } else { |
7695 return scopeSelector + ' ' + selector; | 8030 return scopeSelector + ' ' + selector; |
7696 } | 8031 } |
7697 }, | 8032 }, |
7698 // return a selector with [name] suffix on each simple selector | 8033 // return a selector with [name] suffix on each simple selector |
(...skipping 285 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
7984 } | 8319 } |
7985 | 8320 |
7986 } | 8321 } |
7987 }); | 8322 }); |
7988 } | 8323 } |
7989 | 8324 |
7990 // exports | 8325 // exports |
7991 scope.ShadowCSS = ShadowCSS; | 8326 scope.ShadowCSS = ShadowCSS; |
7992 | 8327 |
7993 })(window.Platform); | 8328 })(window.Platform); |
| 8329 |
7994 } else { | 8330 } else { |
7995 /* | 8331 /* |
7996 * Copyright 2013 The Polymer Authors. All rights reserved. | 8332 * Copyright (c) 2014 The Polymer Project Authors. All rights reserved. |
7997 * Use of this source code is governed by a BSD-style | 8333 * This code may only be used under the BSD style license found at http://polyme
r.github.io/LICENSE.txt |
7998 * license that can be found in the LICENSE file. | 8334 * The complete set of authors may be found at http://polymer.github.io/AUTHORS.
txt |
| 8335 * The complete set of contributors may be found at http://polymer.github.io/CON
TRIBUTORS.txt |
| 8336 * Code distributed by Google as part of the polymer project is also |
| 8337 * subject to an additional IP rights grant found at http://polymer.github.io/PA
TENTS.txt |
7999 */ | 8338 */ |
8000 (function() { | |
8001 | 8339 |
8002 // poor man's adapter for template.content on various platform scenarios | 8340 (function(scope) { |
8003 window.templateContent = window.templateContent || function(inTemplate) { | |
8004 return inTemplate.content; | |
8005 }; | |
8006 | 8341 |
8007 // so we can call wrap/unwrap without testing for ShadowDOMPolyfill | 8342 // so we can call wrap/unwrap without testing for ShadowDOMPolyfill |
8008 | |
8009 window.wrap = window.unwrap = function(n){ | 8343 window.wrap = window.unwrap = function(n){ |
8010 return n; | 8344 return n; |
8011 } | 8345 } |
8012 | 8346 |
8013 addEventListener('DOMContentLoaded', function() { | 8347 addEventListener('DOMContentLoaded', function() { |
8014 if (CustomElements.useNative === false) { | 8348 if (CustomElements.useNative === false) { |
8015 var originalCreateShadowRoot = Element.prototype.createShadowRoot; | 8349 var originalCreateShadowRoot = Element.prototype.createShadowRoot; |
8016 Element.prototype.createShadowRoot = function() { | 8350 Element.prototype.createShadowRoot = function() { |
8017 var root = originalCreateShadowRoot.call(this); | 8351 var root = originalCreateShadowRoot.call(this); |
8018 CustomElements.watchShadow(this); | 8352 CustomElements.watchShadow(this); |
8019 return root; | 8353 return root; |
8020 }; | 8354 }; |
8021 } | 8355 } |
8022 }); | 8356 }); |
8023 | 8357 |
8024 window.templateContent = function(inTemplate) { | 8358 Platform.templateContent = function(inTemplate) { |
8025 // if MDV exists, it may need to boostrap this template to reveal content | 8359 // if MDV exists, it may need to boostrap this template to reveal content |
8026 if (window.HTMLTemplateElement && HTMLTemplateElement.bootstrap) { | 8360 if (window.HTMLTemplateElement && HTMLTemplateElement.bootstrap) { |
8027 HTMLTemplateElement.bootstrap(inTemplate); | 8361 HTMLTemplateElement.bootstrap(inTemplate); |
8028 } | 8362 } |
8029 // fallback when there is no Shadow DOM polyfill, no MDV polyfill, and no | 8363 // fallback when there is no Shadow DOM polyfill, no MDV polyfill, and no |
8030 // native template support | 8364 // native template support |
8031 if (!inTemplate.content && !inTemplate._content) { | 8365 if (!inTemplate.content && !inTemplate._content) { |
8032 var frag = document.createDocumentFragment(); | 8366 var frag = document.createDocumentFragment(); |
8033 while (inTemplate.firstChild) { | 8367 while (inTemplate.firstChild) { |
8034 frag.appendChild(inTemplate.firstChild); | 8368 frag.appendChild(inTemplate.firstChild); |
8035 } | 8369 } |
8036 inTemplate._content = frag; | 8370 inTemplate._content = frag; |
8037 } | 8371 } |
8038 return inTemplate.content || inTemplate._content; | 8372 return inTemplate.content || inTemplate._content; |
8039 }; | 8373 }; |
8040 | 8374 |
8041 })(); | 8375 })(window.Platform); |
| 8376 |
8042 } | 8377 } |
8043 /* Any copyright is dedicated to the Public Domain. | 8378 /* Any copyright is dedicated to the Public Domain. |
8044 * http://creativecommons.org/publicdomain/zero/1.0/ */ | 8379 * http://creativecommons.org/publicdomain/zero/1.0/ */ |
8045 | 8380 |
8046 (function(scope) { | 8381 (function(scope) { |
8047 'use strict'; | 8382 'use strict'; |
8048 | 8383 |
8049 // feature detect for URL constructor | 8384 // feature detect for URL constructor |
8050 var hasWorkingUrl = false; | 8385 var hasWorkingUrl = false; |
8051 if (!scope.forceJURL) { | 8386 if (!scope.forceJURL) { |
(...skipping 548 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
8600 hash = hash.slice(1); | 8935 hash = hash.slice(1); |
8601 parse.call(this, hash, 'fragment'); | 8936 parse.call(this, hash, 'fragment'); |
8602 } | 8937 } |
8603 }; | 8938 }; |
8604 | 8939 |
8605 scope.URL = jURL; | 8940 scope.URL = jURL; |
8606 | 8941 |
8607 })(window); | 8942 })(window); |
8608 | 8943 |
8609 /* | 8944 /* |
8610 * Copyright 2013 The Polymer Authors. All rights reserved. | 8945 * Copyright (c) 2014 The Polymer Project Authors. All rights reserved. |
8611 * Use of this source code is governed by a BSD-style | 8946 * This code may only be used under the BSD style license found at http://polyme
r.github.io/LICENSE.txt |
8612 * license that can be found in the LICENSE file. | 8947 * The complete set of authors may be found at http://polymer.github.io/AUTHORS.
txt |
| 8948 * The complete set of contributors may be found at http://polymer.github.io/CON
TRIBUTORS.txt |
| 8949 * Code distributed by Google as part of the polymer project is also |
| 8950 * subject to an additional IP rights grant found at http://polymer.github.io/PA
TENTS.txt |
8613 */ | 8951 */ |
8614 | 8952 |
8615 (function(scope) { | 8953 (function(scope) { |
8616 | 8954 |
8617 // Old versions of iOS do not have bind. | 8955 // Old versions of iOS do not have bind. |
8618 | 8956 |
8619 if (!Function.prototype.bind) { | 8957 if (!Function.prototype.bind) { |
8620 Function.prototype.bind = function(scope) { | 8958 Function.prototype.bind = function(scope) { |
8621 var self = this; | 8959 var self = this; |
8622 var args = Array.prototype.slice.call(arguments, 1); | 8960 var args = Array.prototype.slice.call(arguments, 1); |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
8658 var pd = Object.getOwnPropertyDescriptor(inObject, inName); | 8996 var pd = Object.getOwnPropertyDescriptor(inObject, inName); |
8659 return pd || getPropertyDescriptor(Object.getPrototypeOf(inObject), inName); | 8997 return pd || getPropertyDescriptor(Object.getPrototypeOf(inObject), inName); |
8660 } | 8998 } |
8661 } | 8999 } |
8662 | 9000 |
8663 // export | 9001 // export |
8664 | 9002 |
8665 scope.mixin = mixin; | 9003 scope.mixin = mixin; |
8666 | 9004 |
8667 })(window.Platform); | 9005 })(window.Platform); |
8668 // Copyright 2011 Google Inc. | 9006 |
8669 // | 9007 /* |
8670 // Licensed under the Apache License, Version 2.0 (the "License"); | 9008 * Copyright (c) 2014 The Polymer Project Authors. All rights reserved. |
8671 // you may not use this file except in compliance with the License. | 9009 * This code may only be used under the BSD style license found at http://polyme
r.github.io/LICENSE.txt |
8672 // You may obtain a copy of the License at | 9010 * The complete set of authors may be found at http://polymer.github.io/AUTHORS.
txt |
8673 // | 9011 * The complete set of contributors may be found at http://polymer.github.io/CON
TRIBUTORS.txt |
8674 // http://www.apache.org/licenses/LICENSE-2.0 | 9012 * Code distributed by Google as part of the polymer project is also |
8675 // | 9013 * subject to an additional IP rights grant found at http://polymer.github.io/PA
TENTS.txt |
8676 // Unless required by applicable law or agreed to in writing, software | 9014 */ |
8677 // distributed under the License is distributed on an "AS IS" BASIS, | |
8678 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
8679 // See the License for the specific language governing permissions and | |
8680 // limitations under the License. | |
8681 | 9015 |
8682 (function(scope) { | 9016 (function(scope) { |
8683 | 9017 |
8684 'use strict'; | 9018 'use strict'; |
8685 | 9019 |
8686 // polyfill DOMTokenList | 9020 // polyfill DOMTokenList |
8687 // * add/remove: allow these methods to take multiple classNames | 9021 // * add/remove: allow these methods to take multiple classNames |
8688 // * toggle: add a 2nd argument which forces the given state rather | 9022 // * toggle: add a 2nd argument which forces the given state rather |
8689 // than toggling. | 9023 // than toggling. |
8690 | 9024 |
(...skipping 115 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
8806 }; | 9140 }; |
8807 } | 9141 } |
8808 }); | 9142 }); |
8809 | 9143 |
8810 // exports | 9144 // exports |
8811 scope.createDOM = createDOM; | 9145 scope.createDOM = createDOM; |
8812 | 9146 |
8813 })(window.Platform); | 9147 })(window.Platform); |
8814 | 9148 |
8815 /* | 9149 /* |
8816 * Copyright 2013 The Polymer Authors. All rights reserved. | 9150 * Copyright (c) 2014 The Polymer Project Authors. All rights reserved. |
8817 * Use of this source code is governed by a BSD-style | 9151 * This code may only be used under the BSD style license found at http://polyme
r.github.io/LICENSE.txt |
8818 * license that can be found in the LICENSE file. | 9152 * The complete set of authors may be found at http://polymer.github.io/AUTHORS.
txt |
| 9153 * The complete set of contributors may be found at http://polymer.github.io/CON
TRIBUTORS.txt |
| 9154 * Code distributed by Google as part of the polymer project is also |
| 9155 * subject to an additional IP rights grant found at http://polymer.github.io/PA
TENTS.txt |
8819 */ | 9156 */ |
8820 | 9157 |
8821 // poor man's adapter for template.content on various platform scenarios | 9158 // poor man's adapter for template.content on various platform scenarios |
8822 window.templateContent = window.templateContent || function(inTemplate) { | 9159 (function(scope) { |
8823 return inTemplate.content; | 9160 scope.templateContent = scope.templateContent || function(inTemplate) { |
8824 }; | 9161 return inTemplate.content; |
| 9162 }; |
| 9163 })(window.Platform); |
| 9164 |
| 9165 /* |
| 9166 * Copyright (c) 2014 The Polymer Project Authors. All rights reserved. |
| 9167 * This code may only be used under the BSD style license found at http://polyme
r.github.io/LICENSE.txt |
| 9168 * The complete set of authors may be found at http://polymer.github.io/AUTHORS.
txt |
| 9169 * The complete set of contributors may be found at http://polymer.github.io/CON
TRIBUTORS.txt |
| 9170 * Code distributed by Google as part of the polymer project is also |
| 9171 * subject to an additional IP rights grant found at http://polymer.github.io/PA
TENTS.txt |
| 9172 */ |
| 9173 |
8825 (function(scope) { | 9174 (function(scope) { |
8826 | 9175 |
8827 scope = scope || (window.Inspector = {}); | 9176 scope = scope || (window.Inspector = {}); |
8828 | 9177 |
8829 var inspector; | 9178 var inspector; |
8830 | 9179 |
8831 window.sinspect = function(inNode, inProxy) { | 9180 window.sinspect = function(inNode, inProxy) { |
8832 if (!inspector) { | 9181 if (!inspector) { |
8833 inspector = window.open('', 'ShadowDOM Inspector', null, true); | 9182 inspector = window.open('', 'ShadowDOM Inspector', null, true); |
8834 inspector.document.write(inspectorHTML); | 9183 inspector.document.write(inspectorHTML); |
(...skipping 169 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
9004 console.dir(this); | 9353 console.dir(this); |
9005 } | 9354 } |
9006 }; | 9355 }; |
9007 | 9356 |
9008 // export | 9357 // export |
9009 | 9358 |
9010 scope.output = output; | 9359 scope.output = output; |
9011 | 9360 |
9012 })(window.Inspector); | 9361 })(window.Inspector); |
9013 | 9362 |
| 9363 /* |
| 9364 * Copyright (c) 2014 The Polymer Project Authors. All rights reserved. |
| 9365 * This code may only be used under the BSD style license found at http://polyme
r.github.io/LICENSE.txt |
| 9366 * The complete set of authors may be found at http://polymer.github.io/AUTHORS.
txt |
| 9367 * The complete set of contributors may be found at http://polymer.github.io/CON
TRIBUTORS.txt |
| 9368 * Code distributed by Google as part of the polymer project is also |
| 9369 * subject to an additional IP rights grant found at http://polymer.github.io/PA
TENTS.txt |
| 9370 */ |
9014 | 9371 |
9015 | |
9016 /* | |
9017 * Copyright 2013 The Polymer Authors. All rights reserved. | |
9018 * Use of this source code is governed by a BSD-style | |
9019 * license that can be found in the LICENSE file. | |
9020 */ | |
9021 (function(scope) { | 9372 (function(scope) { |
9022 | 9373 |
9023 // TODO(sorvell): It's desireable to provide a default stylesheet | 9374 // TODO(sorvell): It's desireable to provide a default stylesheet |
9024 // that's convenient for styling unresolved elements, but | 9375 // that's convenient for styling unresolved elements, but |
9025 // it's cumbersome to have to include this manually in every page. | 9376 // it's cumbersome to have to include this manually in every page. |
9026 // It would make sense to put inside some HTMLImport but | 9377 // It would make sense to put inside some HTMLImport but |
9027 // the HTMLImports polyfill does not allow loading of stylesheets | 9378 // the HTMLImports polyfill does not allow loading of stylesheets |
9028 // that block rendering. Therefore this injection is tolerated here. | 9379 // that block rendering. Therefore this injection is tolerated here. |
9029 | 9380 |
9030 var style = document.createElement('style'); | 9381 var style = document.createElement('style'); |
9031 style.textContent = '' | 9382 style.textContent = '' |
9032 + 'body {' | 9383 + 'body {' |
9033 + 'transition: opacity ease-in 0.2s;' | 9384 + 'transition: opacity ease-in 0.2s;' |
9034 + ' } \n' | 9385 + ' } \n' |
9035 + 'body[unresolved] {' | 9386 + 'body[unresolved] {' |
9036 + 'opacity: 0; display: block; overflow: hidden;' | 9387 + 'opacity: 0; display: block; overflow: hidden;' |
9037 + ' } \n' | 9388 + ' } \n' |
9038 ; | 9389 ; |
9039 var head = document.querySelector('head'); | 9390 var head = document.querySelector('head'); |
9040 head.insertBefore(style, head.firstChild); | 9391 head.insertBefore(style, head.firstChild); |
9041 | 9392 |
9042 })(Platform); | 9393 })(Platform); |
9043 | 9394 |
| 9395 /* |
| 9396 * Copyright (c) 2014 The Polymer Project Authors. All rights reserved. |
| 9397 * This code may only be used under the BSD style license found at http://polyme
r.github.io/LICENSE.txt |
| 9398 * The complete set of authors may be found at http://polymer.github.io/AUTHORS.
txt |
| 9399 * The complete set of contributors may be found at http://polymer.github.io/CON
TRIBUTORS.txt |
| 9400 * Code distributed by Google as part of the polymer project is also |
| 9401 * subject to an additional IP rights grant found at http://polymer.github.io/PA
TENTS.txt |
| 9402 */ |
| 9403 |
9044 (function(scope) { | 9404 (function(scope) { |
9045 | 9405 |
9046 function withDependencies(task, depends) { | 9406 function withDependencies(task, depends) { |
9047 depends = depends || []; | 9407 depends = depends || []; |
9048 if (!depends.map) { | 9408 if (!depends.map) { |
9049 depends = [depends]; | 9409 depends = [depends]; |
9050 } | 9410 } |
9051 return task.apply(this, depends.map(marshal)); | 9411 return task.apply(this, depends.map(marshal)); |
9052 } | 9412 } |
9053 | 9413 |
(...skipping 27 matching lines...) Expand all Loading... |
9081 }); | 9441 }); |
9082 }; | 9442 }; |
9083 | 9443 |
9084 // exports | 9444 // exports |
9085 | 9445 |
9086 scope.marshal = marshal; | 9446 scope.marshal = marshal; |
9087 scope.module = module; | 9447 scope.module = module; |
9088 scope.using = using; | 9448 scope.using = using; |
9089 | 9449 |
9090 })(window); | 9450 })(window); |
| 9451 |
9091 /* | 9452 /* |
9092 * Copyright 2013 The Polymer Authors. All rights reserved. | 9453 * Copyright (c) 2014 The Polymer Project Authors. All rights reserved. |
9093 * Use of this source code is governed by a BSD-style | 9454 * This code may only be used under the BSD style license found at http://polyme
r.github.io/LICENSE.txt |
9094 * license that can be found in the LICENSE file. | 9455 * The complete set of authors may be found at http://polymer.github.io/AUTHORS.
txt |
| 9456 * The complete set of contributors may be found at http://polymer.github.io/CON
TRIBUTORS.txt |
| 9457 * Code distributed by Google as part of the polymer project is also |
| 9458 * subject to an additional IP rights grant found at http://polymer.github.io/PA
TENTS.txt |
9095 */ | 9459 */ |
| 9460 |
9096 (function(scope) { | 9461 (function(scope) { |
9097 | 9462 |
9098 var iterations = 0; | 9463 var iterations = 0; |
9099 var callbacks = []; | 9464 var callbacks = []; |
9100 var twiddle = document.createTextNode(''); | 9465 var twiddle = document.createTextNode(''); |
9101 | 9466 |
9102 function endOfMicrotask(callback) { | 9467 function endOfMicrotask(callback) { |
9103 twiddle.textContent = iterations++; | 9468 twiddle.textContent = iterations++; |
9104 callbacks.push(callback); | 9469 callbacks.push(callback); |
9105 } | 9470 } |
9106 | 9471 |
9107 function atEndOfMicrotask() { | 9472 function atEndOfMicrotask() { |
9108 while (callbacks.length) { | 9473 while (callbacks.length) { |
9109 callbacks.shift()(); | 9474 callbacks.shift()(); |
9110 } | 9475 } |
9111 } | 9476 } |
9112 | 9477 |
9113 new (window.MutationObserver || JsMutationObserver)(atEndOfMicrotask) | 9478 new (window.MutationObserver || JsMutationObserver)(atEndOfMicrotask) |
9114 .observe(twiddle, {characterData: true}) | 9479 .observe(twiddle, {characterData: true}) |
9115 ; | 9480 ; |
9116 | 9481 |
9117 // exports | 9482 // exports |
9118 | 9483 |
9119 scope.endOfMicrotask = endOfMicrotask; | 9484 scope.endOfMicrotask = endOfMicrotask; |
9120 | 9485 |
9121 })(Platform); | 9486 })(Platform); |
9122 | 9487 |
9123 | 9488 |
9124 /* | 9489 /* |
9125 * Copyright 2013 The Polymer Authors. All rights reserved. | 9490 * Copyright (c) 2014 The Polymer Project Authors. All rights reserved. |
9126 * Use of this source code is governed by a BSD-style | 9491 * This code may only be used under the BSD style license found at http://polyme
r.github.io/LICENSE.txt |
9127 * license that can be found in the LICENSE file. | 9492 * The complete set of authors may be found at http://polymer.github.io/AUTHORS.
txt |
| 9493 * The complete set of contributors may be found at http://polymer.github.io/CON
TRIBUTORS.txt |
| 9494 * Code distributed by Google as part of the polymer project is also |
| 9495 * subject to an additional IP rights grant found at http://polymer.github.io/PA
TENTS.txt |
9128 */ | 9496 */ |
9129 | 9497 |
9130 (function(scope) { | 9498 (function(scope) { |
9131 | 9499 |
9132 var urlResolver = { | 9500 var urlResolver = { |
9133 resolveDom: function(root, url) { | 9501 resolveDom: function(root, url) { |
9134 url = url || root.ownerDocument.baseURI; | 9502 url = url || root.ownerDocument.baseURI; |
9135 this.resolveAttributes(root, url); | 9503 this.resolveAttributes(root, url); |
9136 this.resolveStyles(root, url); | 9504 this.resolveStyles(root, url); |
9137 // handle template.content | 9505 // handle template.content |
(...skipping 14 matching lines...) Expand all Loading... |
9152 if (styles) { | 9520 if (styles) { |
9153 for (var i = 0, l = styles.length, s; (i < l) && (s = styles[i]); i++) { | 9521 for (var i = 0, l = styles.length, s; (i < l) && (s = styles[i]); i++) { |
9154 this.resolveStyle(s, url); | 9522 this.resolveStyle(s, url); |
9155 } | 9523 } |
9156 } | 9524 } |
9157 }, | 9525 }, |
9158 resolveStyle: function(style, url) { | 9526 resolveStyle: function(style, url) { |
9159 url = url || style.ownerDocument.baseURI; | 9527 url = url || style.ownerDocument.baseURI; |
9160 style.textContent = this.resolveCssText(style.textContent, url); | 9528 style.textContent = this.resolveCssText(style.textContent, url); |
9161 }, | 9529 }, |
9162 resolveCssText: function(cssText, baseUrl) { | 9530 resolveCssText: function(cssText, baseUrl, keepAbsolute) { |
9163 cssText = replaceUrlsInCssText(cssText, baseUrl, CSS_URL_REGEXP); | 9531 cssText = replaceUrlsInCssText(cssText, baseUrl, keepAbsolute, CSS_URL_REGEX
P); |
9164 return replaceUrlsInCssText(cssText, baseUrl, CSS_IMPORT_REGEXP); | 9532 return replaceUrlsInCssText(cssText, baseUrl, keepAbsolute, CSS_IMPORT_REGEX
P); |
9165 }, | 9533 }, |
9166 resolveAttributes: function(root, url) { | 9534 resolveAttributes: function(root, url) { |
9167 if (root.hasAttributes && root.hasAttributes()) { | 9535 if (root.hasAttributes && root.hasAttributes()) { |
9168 this.resolveElementAttributes(root, url); | 9536 this.resolveElementAttributes(root, url); |
9169 } | 9537 } |
9170 // search for attributes that host urls | 9538 // search for attributes that host urls |
9171 var nodes = root && root.querySelectorAll(URL_ATTRS_SELECTOR); | 9539 var nodes = root && root.querySelectorAll(URL_ATTRS_SELECTOR); |
9172 if (nodes) { | 9540 if (nodes) { |
9173 for (var i = 0, l = nodes.length, n; (i < l) && (n = nodes[i]); i++) { | 9541 for (var i = 0, l = nodes.length, n; (i < l) && (n = nodes[i]); i++) { |
9174 this.resolveElementAttributes(n, url); | 9542 this.resolveElementAttributes(n, url); |
9175 } | 9543 } |
9176 } | 9544 } |
9177 }, | 9545 }, |
9178 resolveElementAttributes: function(node, url) { | 9546 resolveElementAttributes: function(node, url) { |
9179 url = url || node.ownerDocument.baseURI; | 9547 url = url || node.ownerDocument.baseURI; |
9180 URL_ATTRS.forEach(function(v) { | 9548 URL_ATTRS.forEach(function(v) { |
9181 var attr = node.attributes[v]; | 9549 var attr = node.attributes[v]; |
9182 if (attr && attr.value && | 9550 var value = attr && attr.value; |
9183 (attr.value.search(URL_TEMPLATE_SEARCH) < 0)) { | 9551 var replacement; |
9184 var urlPath = resolveRelativeUrl(url, attr.value); | 9552 if (value && value.search(URL_TEMPLATE_SEARCH) < 0) { |
9185 attr.value = urlPath; | 9553 if (v === 'style') { |
| 9554 replacement = replaceUrlsInCssText(value, url, CSS_URL_REGEXP); |
| 9555 } else { |
| 9556 replacement = resolveRelativeUrl(url, value); |
| 9557 } |
| 9558 attr.value = replacement; |
9186 } | 9559 } |
9187 }); | 9560 }); |
9188 } | 9561 } |
9189 }; | 9562 }; |
9190 | 9563 |
9191 var CSS_URL_REGEXP = /(url\()([^)]*)(\))/g; | 9564 var CSS_URL_REGEXP = /(url\()([^)]*)(\))/g; |
9192 var CSS_IMPORT_REGEXP = /(@import[\s]+(?!url\())([^;]*)(;)/g; | 9565 var CSS_IMPORT_REGEXP = /(@import[\s]+(?!url\())([^;]*)(;)/g; |
9193 var URL_ATTRS = ['href', 'src', 'action']; | 9566 var URL_ATTRS = ['href', 'src', 'action', 'style']; |
9194 var URL_ATTRS_SELECTOR = '[' + URL_ATTRS.join('],[') + ']'; | 9567 var URL_ATTRS_SELECTOR = '[' + URL_ATTRS.join('],[') + ']'; |
9195 var URL_TEMPLATE_SEARCH = '{{.*}}'; | 9568 var URL_TEMPLATE_SEARCH = '{{.*}}'; |
9196 | 9569 |
9197 function replaceUrlsInCssText(cssText, baseUrl, regexp) { | 9570 function replaceUrlsInCssText(cssText, baseUrl, keepAbsolute, regexp) { |
9198 return cssText.replace(regexp, function(m, pre, url, post) { | 9571 return cssText.replace(regexp, function(m, pre, url, post) { |
9199 var urlPath = url.replace(/["']/g, ''); | 9572 var urlPath = url.replace(/["']/g, ''); |
9200 urlPath = resolveRelativeUrl(baseUrl, urlPath); | 9573 urlPath = resolveRelativeUrl(baseUrl, urlPath, keepAbsolute); |
9201 return pre + '\'' + urlPath + '\'' + post; | 9574 return pre + '\'' + urlPath + '\'' + post; |
9202 }); | 9575 }); |
9203 } | 9576 } |
9204 | 9577 |
9205 function resolveRelativeUrl(baseUrl, url) { | 9578 function resolveRelativeUrl(baseUrl, url, keepAbsolute) { |
| 9579 // do not resolve '/' absolute urls |
| 9580 if (url && url[0] === '/') { |
| 9581 return url; |
| 9582 } |
9206 var u = new URL(url, baseUrl); | 9583 var u = new URL(url, baseUrl); |
9207 return makeDocumentRelPath(u.href); | 9584 return keepAbsolute ? u.href : makeDocumentRelPath(u.href); |
9208 } | 9585 } |
9209 | 9586 |
9210 function makeDocumentRelPath(url) { | 9587 function makeDocumentRelPath(url) { |
9211 var root = document.baseURI; | 9588 var root = new URL(document.baseURI); |
9212 var u = new URL(url, root); | 9589 var u = new URL(url, root); |
9213 if (u.host === root.host && u.port === root.port && | 9590 if (u.host === root.host && u.port === root.port && |
9214 u.protocol === root.protocol) { | 9591 u.protocol === root.protocol) { |
9215 return makeRelPath(root.pathname, u.pathname); | 9592 return makeRelPath(root, u); |
9216 } else { | 9593 } else { |
9217 return url; | 9594 return url; |
9218 } | 9595 } |
9219 } | 9596 } |
9220 | 9597 |
9221 // make a relative path from source to target | 9598 // make a relative path from source to target |
9222 function makeRelPath(source, target) { | 9599 function makeRelPath(sourceUrl, targetUrl) { |
| 9600 var source = sourceUrl.pathname; |
| 9601 var target = targetUrl.pathname; |
9223 var s = source.split('/'); | 9602 var s = source.split('/'); |
9224 var t = target.split('/'); | 9603 var t = target.split('/'); |
9225 while (s.length && s[0] === t[0]){ | 9604 while (s.length && s[0] === t[0]){ |
9226 s.shift(); | 9605 s.shift(); |
9227 t.shift(); | 9606 t.shift(); |
9228 } | 9607 } |
9229 for (var i = 0, l = s.length - 1; i < l; i++) { | 9608 for (var i = 0, l = s.length - 1; i < l; i++) { |
9230 t.unshift('..'); | 9609 t.unshift('..'); |
9231 } | 9610 } |
9232 return t.join('/'); | 9611 return t.join('/') + targetUrl.search + targetUrl.hash; |
9233 } | 9612 } |
9234 | 9613 |
9235 // exports | 9614 // exports |
9236 scope.urlResolver = urlResolver; | 9615 scope.urlResolver = urlResolver; |
9237 | 9616 |
9238 })(Platform); | 9617 })(Platform); |
9239 | 9618 |
9240 /* | 9619 /* |
9241 * Copyright 2012 The Polymer Authors. All rights reserved. | 9620 * Copyright 2012 The Polymer Authors. All rights reserved. |
9242 * Use of this source code is goverened by a BSD-style | 9621 * Use of this source code is goverened by a BSD-style |
(...skipping 1217 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
10460 callback(); | 10839 callback(); |
10461 } | 10840 } |
10462 } | 10841 } |
10463 | 10842 |
10464 // call <callback> when we ensure all imports have loaded | 10843 // call <callback> when we ensure all imports have loaded |
10465 function watchImportsLoad(callback, doc) { | 10844 function watchImportsLoad(callback, doc) { |
10466 var imports = doc.querySelectorAll('link[rel=import]'); | 10845 var imports = doc.querySelectorAll('link[rel=import]'); |
10467 var loaded = 0, l = imports.length; | 10846 var loaded = 0, l = imports.length; |
10468 function checkDone(d) { | 10847 function checkDone(d) { |
10469 if (loaded == l) { | 10848 if (loaded == l) { |
10470 // go async to ensure parser isn't stuck on a script tag | 10849 callback && callback(); |
10471 requestAnimationFrame(callback); | |
10472 } | 10850 } |
10473 } | 10851 } |
10474 function loadedImport(e) { | 10852 function loadedImport(e) { |
10475 loaded++; | 10853 loaded++; |
10476 checkDone(); | 10854 checkDone(); |
10477 } | 10855 } |
10478 if (l) { | 10856 if (l) { |
10479 for (var i=0, imp; (i<l) && (imp=imports[i]); i++) { | 10857 for (var i=0, imp; (i<l) && (imp=imports[i]); i++) { |
10480 if (isImportLoaded(imp)) { | 10858 if (isImportLoaded(imp)) { |
10481 loadedImport.call(imp); | 10859 loadedImport.call(imp); |
10482 } else { | 10860 } else { |
10483 imp.addEventListener('load', loadedImport); | 10861 imp.addEventListener('load', loadedImport); |
10484 imp.addEventListener('error', loadedImport); | 10862 imp.addEventListener('error', loadedImport); |
10485 } | 10863 } |
10486 } | 10864 } |
10487 } else { | 10865 } else { |
10488 checkDone(); | 10866 checkDone(); |
10489 } | 10867 } |
10490 } | 10868 } |
10491 | 10869 |
10492 function isImportLoaded(link) { | 10870 function isImportLoaded(link) { |
10493 return useNative ? (link.import && (link.import.readyState !== 'loading')) : | 10871 return useNative ? (link.import && (link.import.readyState !== 'loading')) ||
link.__loaded : |
10494 link.__importParsed; | 10872 link.__importParsed; |
10495 } | 10873 } |
10496 | 10874 |
| 10875 // TODO(sorvell): install a mutation observer to see if HTMLImports have loaded |
| 10876 // this is a workaround for https://www.w3.org/Bugs/Public/show_bug.cgi?id=25007 |
| 10877 // and should be removed when this bug is addressed. |
| 10878 if (useNative) { |
| 10879 new MutationObserver(function(mxns) { |
| 10880 for (var i=0, l=mxns.length, m; (i < l) && (m=mxns[i]); i++) { |
| 10881 if (m.addedNodes) { |
| 10882 handleImports(m.addedNodes); |
| 10883 } |
| 10884 } |
| 10885 }).observe(document.head, {childList: true}); |
| 10886 |
| 10887 function handleImports(nodes) { |
| 10888 for (var i=0, l=nodes.length, n; (i<l) && (n=nodes[i]); i++) { |
| 10889 if (isImport(n)) { |
| 10890 handleImport(n); |
| 10891 } |
| 10892 } |
| 10893 } |
| 10894 |
| 10895 function isImport(element) { |
| 10896 return element.localName === 'link' && element.rel === 'import'; |
| 10897 } |
| 10898 |
| 10899 function handleImport(element) { |
| 10900 var loaded = element.import; |
| 10901 if (loaded) { |
| 10902 markTargetLoaded({target: element}); |
| 10903 } else { |
| 10904 element.addEventListener('load', markTargetLoaded); |
| 10905 element.addEventListener('error', markTargetLoaded); |
| 10906 } |
| 10907 } |
| 10908 |
| 10909 function markTargetLoaded(event) { |
| 10910 event.target.__loaded = true; |
| 10911 } |
| 10912 |
| 10913 } |
| 10914 |
10497 // exports | 10915 // exports |
10498 scope.hasNative = hasNative; | 10916 scope.hasNative = hasNative; |
10499 scope.useNative = useNative; | 10917 scope.useNative = useNative; |
10500 scope.importer = importer; | 10918 scope.importer = importer; |
10501 scope.whenImportsReady = whenImportsReady; | 10919 scope.whenImportsReady = whenImportsReady; |
10502 scope.IMPORT_LINK_TYPE = IMPORT_LINK_TYPE; | 10920 scope.IMPORT_LINK_TYPE = IMPORT_LINK_TYPE; |
10503 scope.isImportLoaded = isImportLoaded; | 10921 scope.isImportLoaded = isImportLoaded; |
10504 scope.importLoader = importLoader; | 10922 scope.importLoader = importLoader; |
10505 | 10923 |
10506 })(window.HTMLImports); | 10924 })(window.HTMLImports); |
(...skipping 661 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
11168 | 11586 |
11169 function resolvePrototypeChain(definition) { | 11587 function resolvePrototypeChain(definition) { |
11170 // if we don't support __proto__ we need to locate the native level | 11588 // if we don't support __proto__ we need to locate the native level |
11171 // prototype for precise mixing in | 11589 // prototype for precise mixing in |
11172 if (!Object.__proto__) { | 11590 if (!Object.__proto__) { |
11173 // default prototype | 11591 // default prototype |
11174 var nativePrototype = HTMLElement.prototype; | 11592 var nativePrototype = HTMLElement.prototype; |
11175 // work out prototype when using type-extension | 11593 // work out prototype when using type-extension |
11176 if (definition.is) { | 11594 if (definition.is) { |
11177 var inst = document.createElement(definition.tag); | 11595 var inst = document.createElement(definition.tag); |
11178 nativePrototype = Object.getPrototypeOf(inst); | 11596 var expectedPrototype = Object.getPrototypeOf(inst); |
| 11597 // only set nativePrototype if it will actually appear in the definition
's chain |
| 11598 if (expectedPrototype === definition.prototype) { |
| 11599 nativePrototype = expectedPrototype; |
| 11600 } |
11179 } | 11601 } |
11180 // ensure __proto__ reference is installed at each point on the prototype | 11602 // ensure __proto__ reference is installed at each point on the prototype |
11181 // chain. | 11603 // chain. |
11182 // NOTE: On platforms without __proto__, a mixin strategy is used instead | 11604 // NOTE: On platforms without __proto__, a mixin strategy is used instead |
11183 // of prototype swizzling. In this case, this generated __proto__ provides | 11605 // of prototype swizzling. In this case, this generated __proto__ provides |
11184 // limited support for prototype traversal. | 11606 // limited support for prototype traversal. |
11185 var proto = definition.prototype, ancestor; | 11607 var proto = definition.prototype, ancestor; |
11186 while (proto && (proto !== nativePrototype)) { | 11608 while (proto && (proto !== nativePrototype)) { |
11187 var ancestor = Object.getPrototypeOf(proto); | 11609 ancestor = Object.getPrototypeOf(proto); |
11188 proto.__proto__ = ancestor; | 11610 proto.__proto__ = ancestor; |
11189 proto = ancestor; | 11611 proto = ancestor; |
11190 } | 11612 } |
| 11613 // cache this in case of mixin |
| 11614 definition.native = nativePrototype; |
11191 } | 11615 } |
11192 // cache this in case of mixin | |
11193 definition.native = nativePrototype; | |
11194 } | 11616 } |
11195 | 11617 |
11196 // SECTION 4 | 11618 // SECTION 4 |
11197 | 11619 |
11198 function instantiate(definition) { | 11620 function instantiate(definition) { |
11199 // 4.a.1. Create a new object that implements PROTOTYPE | 11621 // 4.a.1. Create a new object that implements PROTOTYPE |
11200 // 4.a.2. Let ELEMENT by this new object | 11622 // 4.a.2. Let ELEMENT by this new object |
11201 // | 11623 // |
11202 // the custom element instantiation algorithm must also ensure that the | 11624 // the custom element instantiation algorithm must also ensure that the |
11203 // output is a valid DOM element with the proper wrapper in place. | 11625 // output is a valid DOM element with the proper wrapper in place. |
(...skipping 370 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
11574 // bootstrap. | 11996 // bootstrap. |
11575 } else { | 11997 } else { |
11576 var loadEvent = window.HTMLImports && !HTMLImports.ready ? | 11998 var loadEvent = window.HTMLImports && !HTMLImports.ready ? |
11577 'HTMLImportsLoaded' : 'DOMContentLoaded'; | 11999 'HTMLImportsLoaded' : 'DOMContentLoaded'; |
11578 window.addEventListener(loadEvent, bootstrap); | 12000 window.addEventListener(loadEvent, bootstrap); |
11579 } | 12001 } |
11580 | 12002 |
11581 })(window.CustomElements); | 12003 })(window.CustomElements); |
11582 | 12004 |
11583 /* | 12005 /* |
11584 * Copyright 2013 The Polymer Authors. All rights reserved. | 12006 * Copyright (c) 2014 The Polymer Project Authors. All rights reserved. |
11585 * Use of this source code is governed by a BSD-style | 12007 * This code may only be used under the BSD style license found at http://polyme
r.github.io/LICENSE.txt |
11586 * license that can be found in the LICENSE file. | 12008 * The complete set of authors may be found at http://polymer.github.io/AUTHORS.
txt |
| 12009 * The complete set of contributors may be found at http://polymer.github.io/CON
TRIBUTORS.txt |
| 12010 * Code distributed by Google as part of the polymer project is also |
| 12011 * subject to an additional IP rights grant found at http://polymer.github.io/PA
TENTS.txt |
11587 */ | 12012 */ |
| 12013 |
11588 (function() { | 12014 (function() { |
11589 | 12015 |
11590 if (window.ShadowDOMPolyfill) { | 12016 if (window.ShadowDOMPolyfill) { |
11591 | 12017 |
11592 // ensure wrapped inputs for these functions | 12018 // ensure wrapped inputs for these functions |
11593 var fns = ['upgradeAll', 'upgradeSubtree', 'observeDocument', | 12019 var fns = ['upgradeAll', 'upgradeSubtree', 'observeDocument', |
11594 'upgradeDocument']; | 12020 'upgradeDocument']; |
11595 | 12021 |
11596 // cache originals | 12022 // cache originals |
11597 var original = {}; | 12023 var original = {}; |
11598 fns.forEach(function(fn) { | 12024 fns.forEach(function(fn) { |
11599 original[fn] = CustomElements[fn]; | 12025 original[fn] = CustomElements[fn]; |
11600 }); | 12026 }); |
11601 | 12027 |
11602 // override | 12028 // override |
11603 fns.forEach(function(fn) { | 12029 fns.forEach(function(fn) { |
11604 CustomElements[fn] = function(inNode) { | 12030 CustomElements[fn] = function(inNode) { |
11605 return original[fn](wrap(inNode)); | 12031 return original[fn](wrap(inNode)); |
11606 }; | 12032 }; |
11607 }); | 12033 }); |
11608 | 12034 |
11609 } | 12035 } |
11610 | 12036 |
11611 })(); | 12037 })(); |
11612 | 12038 |
11613 /* | 12039 /* |
11614 * Copyright 2014 The Polymer Authors. All rights reserved. | 12040 * Copyright (c) 2014 The Polymer Project Authors. All rights reserved. |
11615 * Use of this source code is governed by a BSD-style | 12041 * This code may only be used under the BSD style license found at http://polyme
r.github.io/LICENSE.txt |
11616 * license that can be found in the LICENSE file. | 12042 * The complete set of authors may be found at http://polymer.github.io/AUTHORS.
txt |
| 12043 * The complete set of contributors may be found at http://polymer.github.io/CON
TRIBUTORS.txt |
| 12044 * Code distributed by Google as part of the polymer project is also |
| 12045 * subject to an additional IP rights grant found at http://polymer.github.io/PA
TENTS.txt |
11617 */ | 12046 */ |
| 12047 |
11618 (function(scope) { | 12048 (function(scope) { |
11619 var endOfMicrotask = scope.endOfMicrotask; | 12049 var endOfMicrotask = scope.endOfMicrotask; |
11620 | 12050 |
11621 // Generic url loader | 12051 // Generic url loader |
11622 function Loader(regex) { | 12052 function Loader(regex) { |
| 12053 this.cache = Object.create(null); |
| 12054 this.map = Object.create(null); |
| 12055 this.requests = 0; |
11623 this.regex = regex; | 12056 this.regex = regex; |
11624 } | 12057 } |
11625 Loader.prototype = { | 12058 Loader.prototype = { |
| 12059 |
11626 // TODO(dfreedm): there may be a better factoring here | 12060 // TODO(dfreedm): there may be a better factoring here |
11627 // extract absolute urls from the text (full of relative urls) | 12061 // extract absolute urls from the text (full of relative urls) |
11628 extractUrls: function(text, base) { | 12062 extractUrls: function(text, base) { |
11629 var matches = []; | 12063 var matches = []; |
11630 var matched, u; | 12064 var matched, u; |
11631 while ((matched = this.regex.exec(text))) { | 12065 while ((matched = this.regex.exec(text))) { |
11632 u = new URL(matched[1], base); | 12066 u = new URL(matched[1], base); |
11633 matches.push({matched: matched[0], url: u.href}); | 12067 matches.push({matched: matched[0], url: u.href}); |
11634 } | 12068 } |
11635 return matches; | 12069 return matches; |
11636 }, | 12070 }, |
11637 // take a text blob, a root url, and a callback and load all the urls found
within the text | 12071 // take a text blob, a root url, and a callback and load all the urls found
within the text |
11638 // returns a map of absolute url to text | 12072 // returns a map of absolute url to text |
11639 process: function(text, root, callback) { | 12073 process: function(text, root, callback) { |
11640 var matches = this.extractUrls(text, root); | 12074 var matches = this.extractUrls(text, root); |
11641 this.fetch(matches, {}, callback); | 12075 |
| 12076 // every call to process returns all the text this loader has ever receive
d |
| 12077 var done = callback.bind(null, this.map); |
| 12078 this.fetch(matches, done); |
11642 }, | 12079 }, |
11643 // build a mapping of url -> text from matches | 12080 // build a mapping of url -> text from matches |
11644 fetch: function(matches, map, callback) { | 12081 fetch: function(matches, callback) { |
11645 var inflight = matches.length; | 12082 var inflight = matches.length; |
11646 | 12083 |
11647 // return early if there is no fetching to be done | 12084 // return early if there is no fetching to be done |
11648 if (!inflight) { | 12085 if (!inflight) { |
11649 return callback(map); | 12086 return callback(); |
11650 } | 12087 } |
11651 | 12088 |
| 12089 // wait for all subrequests to return |
11652 var done = function() { | 12090 var done = function() { |
11653 if (--inflight === 0) { | 12091 if (--inflight === 0) { |
11654 callback(map); | 12092 callback(); |
11655 } | 12093 } |
11656 }; | 12094 }; |
11657 | 12095 |
11658 // map url -> responseText | 12096 // start fetching all subrequests |
11659 var handleXhr = function(err, request) { | |
11660 var match = request.match; | |
11661 var key = match.url; | |
11662 // handle errors with an empty string | |
11663 if (err) { | |
11664 map[key] = ''; | |
11665 return done(); | |
11666 } | |
11667 var response = request.response || request.responseText; | |
11668 map[key] = response; | |
11669 this.fetch(this.extractUrls(response, key), map, done); | |
11670 }; | |
11671 | |
11672 var m, req, url; | 12097 var m, req, url; |
11673 for (var i = 0; i < inflight; i++) { | 12098 for (var i = 0; i < inflight; i++) { |
11674 m = matches[i]; | 12099 m = matches[i]; |
11675 url = m.url; | 12100 url = m.url; |
| 12101 req = this.cache[url]; |
11676 // if this url has already been requested, skip requesting it again | 12102 // if this url has already been requested, skip requesting it again |
11677 if (map[url]) { | 12103 if (!req) { |
11678 // Async call to done to simplify the inflight logic | 12104 req = this.xhr(url); |
11679 endOfMicrotask(done); | 12105 req.match = m; |
11680 continue; | 12106 this.cache[url] = req; |
11681 } | 12107 } |
11682 req = this.xhr(url, handleXhr, this); | 12108 // wait for the request to process its subrequests |
11683 req.match = m; | 12109 req.wait(done); |
11684 // tag the map with an XHR request to deduplicate at the same level | |
11685 map[url] = req; | |
11686 } | 12110 } |
11687 }, | 12111 }, |
11688 xhr: function(url, callback, scope) { | 12112 handleXhr: function(request) { |
| 12113 var match = request.match; |
| 12114 var url = match.url; |
| 12115 |
| 12116 // handle errors with an empty string |
| 12117 var response = request.response || request.responseText || ''; |
| 12118 this.map[url] = response; |
| 12119 this.fetch(this.extractUrls(response, url), request.resolve); |
| 12120 }, |
| 12121 xhr: function(url) { |
| 12122 this.requests++; |
11689 var request = new XMLHttpRequest(); | 12123 var request = new XMLHttpRequest(); |
11690 request.open('GET', url, true); | 12124 request.open('GET', url, true); |
11691 request.send(); | 12125 request.send(); |
11692 request.onload = function() { | 12126 request.onerror = request.onload = this.handleXhr.bind(this, request); |
11693 callback.call(scope, null, request); | 12127 |
| 12128 // queue of tasks to run after XHR returns |
| 12129 request.pending = []; |
| 12130 request.resolve = function() { |
| 12131 var pending = request.pending; |
| 12132 for(var i = 0; i < pending.length; i++) { |
| 12133 pending[i](); |
| 12134 } |
| 12135 request.pending = null; |
11694 }; | 12136 }; |
11695 request.onerror = function() { | 12137 |
11696 callback.call(scope, null, request); | 12138 // if we have already resolved, pending is null, async call the callback |
| 12139 request.wait = function(fn) { |
| 12140 if (request.pending) { |
| 12141 request.pending.push(fn); |
| 12142 } else { |
| 12143 endOfMicrotask(fn); |
| 12144 } |
11697 }; | 12145 }; |
| 12146 |
11698 return request; | 12147 return request; |
11699 } | 12148 } |
11700 }; | 12149 }; |
11701 | 12150 |
11702 scope.Loader = Loader; | 12151 scope.Loader = Loader; |
11703 })(window.Platform); | 12152 })(window.Platform); |
11704 | 12153 |
11705 /* | 12154 /* |
11706 * Copyright 2014 The Polymer Authors. All rights reserved. | 12155 * Copyright (c) 2014 The Polymer Project Authors. All rights reserved. |
11707 * Use of this source code is governed by a BSD-style | 12156 * This code may only be used under the BSD style license found at http://polyme
r.github.io/LICENSE.txt |
11708 * license that can be found in the LICENSE file. | 12157 * The complete set of authors may be found at http://polymer.github.io/AUTHORS.
txt |
| 12158 * The complete set of contributors may be found at http://polymer.github.io/CON
TRIBUTORS.txt |
| 12159 * Code distributed by Google as part of the polymer project is also |
| 12160 * subject to an additional IP rights grant found at http://polymer.github.io/PA
TENTS.txt |
11709 */ | 12161 */ |
| 12162 |
11710 (function(scope) { | 12163 (function(scope) { |
11711 | 12164 |
11712 var urlResolver = scope.urlResolver; | 12165 var urlResolver = scope.urlResolver; |
11713 var Loader = scope.Loader; | 12166 var Loader = scope.Loader; |
11714 | 12167 |
11715 function StyleResolver() { | 12168 function StyleResolver() { |
11716 this.loader = new Loader(this.regex); | 12169 this.loader = new Loader(this.regex); |
11717 } | 12170 } |
11718 StyleResolver.prototype = { | 12171 StyleResolver.prototype = { |
11719 regex: /@import\s+(?:url)?["'\(]*([^'"\)]*)['"\)]*;/g, | 12172 regex: /@import\s+(?:url)?["'\(]*([^'"\)]*)['"\)]*;/g, |
11720 // Recursively replace @imports with the text at that url | 12173 // Recursively replace @imports with the text at that url |
11721 resolve: function(text, url, callback) { | 12174 resolve: function(text, url, callback) { |
11722 var done = function(map) { | 12175 var done = function(map) { |
11723 callback(this.flatten(text, url, map)); | 12176 callback(this.flatten(text, url, map)); |
11724 }.bind(this); | 12177 }.bind(this); |
11725 this.loader.process(text, url, done); | 12178 this.loader.process(text, url, done); |
11726 }, | 12179 }, |
11727 // resolve the textContent of a style node | 12180 // resolve the textContent of a style node |
11728 resolveNode: function(style, callback) { | 12181 resolveNode: function(style, url, callback) { |
11729 var text = style.textContent; | 12182 var text = style.textContent; |
11730 var url = style.ownerDocument.baseURI; | |
11731 var done = function(text) { | 12183 var done = function(text) { |
11732 style.textContent = text; | 12184 style.textContent = text; |
11733 callback(style); | 12185 callback(style); |
11734 }; | 12186 }; |
11735 this.resolve(text, url, done); | 12187 this.resolve(text, url, done); |
11736 }, | 12188 }, |
11737 // flatten all the @imports to text | 12189 // flatten all the @imports to text |
11738 flatten: function(text, base, map) { | 12190 flatten: function(text, base, map) { |
11739 var matches = this.loader.extractUrls(text, base); | 12191 var matches = this.loader.extractUrls(text, base); |
11740 var match, url, intermediate; | 12192 var match, url, intermediate; |
11741 for (var i = 0; i < matches.length; i++) { | 12193 for (var i = 0; i < matches.length; i++) { |
11742 match = matches[i]; | 12194 match = matches[i]; |
11743 url = match.url; | 12195 url = match.url; |
11744 // resolve any css text to be relative to the importer | 12196 // resolve any css text to be relative to the importer, keep absolute url |
11745 intermediate = urlResolver.resolveCssText(map[url], url); | 12197 intermediate = urlResolver.resolveCssText(map[url], url, true); |
11746 // flatten intermediate @imports | 12198 // flatten intermediate @imports |
11747 intermediate = this.flatten(intermediate, url, map); | 12199 intermediate = this.flatten(intermediate, base, map); |
11748 text = text.replace(match.matched, intermediate); | 12200 text = text.replace(match.matched, intermediate); |
11749 } | 12201 } |
11750 return text; | 12202 return text; |
11751 }, | 12203 }, |
11752 loadStyles: function(styles, callback) { | 12204 loadStyles: function(styles, base, callback) { |
11753 var loaded=0, l = styles.length; | 12205 var loaded=0, l = styles.length; |
11754 // called in the context of the style | 12206 // called in the context of the style |
11755 function loadedStyle(style) { | 12207 function loadedStyle(style) { |
11756 loaded++; | 12208 loaded++; |
11757 if (loaded === l && callback) { | 12209 if (loaded === l && callback) { |
11758 callback(); | 12210 callback(); |
11759 } | 12211 } |
11760 } | 12212 } |
11761 for (var i=0, s; (i<l) && (s=styles[i]); i++) { | 12213 for (var i=0, s; (i<l) && (s=styles[i]); i++) { |
11762 this.resolveNode(s, loadedStyle); | 12214 this.resolveNode(s, base, loadedStyle); |
11763 } | 12215 } |
11764 } | 12216 } |
11765 }; | 12217 }; |
11766 | 12218 |
11767 var styleResolver = new StyleResolver(); | 12219 var styleResolver = new StyleResolver(); |
11768 | 12220 |
11769 // exports | 12221 // exports |
11770 scope.styleResolver = styleResolver; | 12222 scope.styleResolver = styleResolver; |
11771 | 12223 |
11772 })(window.Platform); | 12224 })(window.Platform); |
11773 | 12225 |
11774 /* | |
11775 * Copyright 2013 The Polymer Authors. All rights reserved. | |
11776 * Use of this source code is governed by a BSD-style | |
11777 * license that can be found in the LICENSE file. | |
11778 */ | |
11779 | |
11780 (function(scope) { | |
11781 scope = scope || {}; | |
11782 scope.external = scope.external || {}; | |
11783 var target = { | |
11784 shadow: function(inEl) { | |
11785 if (inEl) { | |
11786 return inEl.shadowRoot || inEl.webkitShadowRoot; | |
11787 } | |
11788 }, | |
11789 canTarget: function(shadow) { | |
11790 return shadow && Boolean(shadow.elementFromPoint); | |
11791 }, | |
11792 targetingShadow: function(inEl) { | |
11793 var s = this.shadow(inEl); | |
11794 if (this.canTarget(s)) { | |
11795 return s; | |
11796 } | |
11797 }, | |
11798 olderShadow: function(shadow) { | |
11799 var os = shadow.olderShadowRoot; | |
11800 if (!os) { | |
11801 var se = shadow.querySelector('shadow'); | |
11802 if (se) { | |
11803 os = se.olderShadowRoot; | |
11804 } | |
11805 } | |
11806 return os; | |
11807 }, | |
11808 allShadows: function(element) { | |
11809 var shadows = [], s = this.shadow(element); | |
11810 while(s) { | |
11811 shadows.push(s); | |
11812 s = this.olderShadow(s); | |
11813 } | |
11814 return shadows; | |
11815 }, | |
11816 searchRoot: function(inRoot, x, y) { | |
11817 if (inRoot) { | |
11818 var t = inRoot.elementFromPoint(x, y); | |
11819 var st, sr, os; | |
11820 // is element a shadow host? | |
11821 sr = this.targetingShadow(t); | |
11822 while (sr) { | |
11823 // find the the element inside the shadow root | |
11824 st = sr.elementFromPoint(x, y); | |
11825 if (!st) { | |
11826 // check for older shadows | |
11827 sr = this.olderShadow(sr); | |
11828 } else { | |
11829 // shadowed element may contain a shadow root | |
11830 var ssr = this.targetingShadow(st); | |
11831 return this.searchRoot(ssr, x, y) || st; | |
11832 } | |
11833 } | |
11834 // light dom element is the target | |
11835 return t; | |
11836 } | |
11837 }, | |
11838 owner: function(element) { | |
11839 var s = element; | |
11840 // walk up until you hit the shadow root or document | |
11841 while (s.parentNode) { | |
11842 s = s.parentNode; | |
11843 } | |
11844 // the owner element is expected to be a Document or ShadowRoot | |
11845 if (s.nodeType != Node.DOCUMENT_NODE && s.nodeType != Node.DOCUMENT_FRAGME
NT_NODE) { | |
11846 s = document; | |
11847 } | |
11848 return s; | |
11849 }, | |
11850 findTarget: function(inEvent) { | |
11851 var x = inEvent.clientX, y = inEvent.clientY; | |
11852 // if the listener is in the shadow root, it is much faster to start there | |
11853 var s = this.owner(inEvent.target); | |
11854 // if x, y is not in this root, fall back to document search | |
11855 if (!s.elementFromPoint(x, y)) { | |
11856 s = document; | |
11857 } | |
11858 return this.searchRoot(s, x, y); | |
11859 } | |
11860 }; | |
11861 scope.targetFinding = target; | |
11862 scope.findTarget = target.findTarget.bind(target); | |
11863 | |
11864 window.PointerEventsPolyfill = scope; | |
11865 })(window.PointerEventsPolyfill); | |
11866 | |
11867 /* | |
11868 * Copyright 2013 The Polymer Authors. All rights reserved. | |
11869 * Use of this source code is governed by a BSD-style | |
11870 * license that can be found in the LICENSE file. | |
11871 */ | |
11872 (function() { | |
11873 function shadowSelector(v) { | |
11874 return 'body /shadow-deep/ ' + selector(v); | |
11875 } | |
11876 function selector(v) { | |
11877 return '[touch-action="' + v + '"]'; | |
11878 } | |
11879 function rule(v) { | |
11880 return '{ -ms-touch-action: ' + v + '; touch-action: ' + v + '; touch-action
-delay: none; }'; | |
11881 } | |
11882 var attrib2css = [ | |
11883 'none', | |
11884 'auto', | |
11885 'pan-x', | |
11886 'pan-y', | |
11887 { | |
11888 rule: 'pan-x pan-y', | |
11889 selectors: [ | |
11890 'pan-x pan-y', | |
11891 'pan-y pan-x' | |
11892 ] | |
11893 } | |
11894 ]; | |
11895 var styles = ''; | |
11896 // only install stylesheet if the browser has touch action support | |
11897 var head = document.head; | |
11898 var hasNativePE = window.PointerEvent || window.MSPointerEvent; | |
11899 // only add shadow selectors if shadowdom is supported | |
11900 var hasShadowRoot = !window.ShadowDOMPolyfill && document.head.createShadowRoo
t; | |
11901 | |
11902 if (hasNativePE) { | |
11903 attrib2css.forEach(function(r) { | |
11904 if (String(r) === r) { | |
11905 styles += selector(r) + rule(r) + '\n'; | |
11906 if (hasShadowRoot) { | |
11907 styles += shadowSelector(r) + rule(r) + '\n'; | |
11908 } | |
11909 } else { | |
11910 styles += r.selectors.map(selector) + rule(r.rule) + '\n'; | |
11911 if (hasShadowRoot) { | |
11912 styles += r.selectors.map(shadowSelector) + rule(r.rule) + '\n'; | |
11913 } | |
11914 } | |
11915 }); | |
11916 | |
11917 var el = document.createElement('style'); | |
11918 el.textContent = styles; | |
11919 document.head.appendChild(el); | |
11920 } | |
11921 })(); | |
11922 | |
11923 /* | |
11924 * Copyright 2013 The Polymer Authors. All rights reserved. | |
11925 * Use of this source code is governed by a BSD-style | |
11926 * license that can be found in the LICENSE file. | |
11927 */ | |
11928 | |
11929 /** | |
11930 * This is the constructor for new PointerEvents. | |
11931 * | |
11932 * New Pointer Events must be given a type, and an optional dictionary of | |
11933 * initialization properties. | |
11934 * | |
11935 * Due to certain platform requirements, events returned from the constructor | |
11936 * identify as MouseEvents. | |
11937 * | |
11938 * @constructor | |
11939 * @param {String} inType The type of the event to create. | |
11940 * @param {Object} [inDict] An optional dictionary of initial event properties. | |
11941 * @return {Event} A new PointerEvent of type `inType` and initialized with prop
erties from `inDict`. | |
11942 */ | |
11943 (function(scope) { | |
11944 | |
11945 var MOUSE_PROPS = [ | |
11946 'bubbles', | |
11947 'cancelable', | |
11948 'view', | |
11949 'detail', | |
11950 'screenX', | |
11951 'screenY', | |
11952 'clientX', | |
11953 'clientY', | |
11954 'ctrlKey', | |
11955 'altKey', | |
11956 'shiftKey', | |
11957 'metaKey', | |
11958 'button', | |
11959 'relatedTarget', | |
11960 'pageX', | |
11961 'pageY' | |
11962 ]; | |
11963 | |
11964 var MOUSE_DEFAULTS = [ | |
11965 false, | |
11966 false, | |
11967 null, | |
11968 null, | |
11969 0, | |
11970 0, | |
11971 0, | |
11972 0, | |
11973 false, | |
11974 false, | |
11975 false, | |
11976 false, | |
11977 0, | |
11978 null, | |
11979 0, | |
11980 0 | |
11981 ]; | |
11982 | |
11983 function PointerEvent(inType, inDict) { | |
11984 inDict = inDict || Object.create(null); | |
11985 | |
11986 var e = document.createEvent('Event'); | |
11987 e.initEvent(inType, inDict.bubbles || false, inDict.cancelable || false); | |
11988 | |
11989 // define inherited MouseEvent properties | |
11990 for(var i = 0, p; i < MOUSE_PROPS.length; i++) { | |
11991 p = MOUSE_PROPS[i]; | |
11992 e[p] = inDict[p] || MOUSE_DEFAULTS[i]; | |
11993 } | |
11994 e.buttons = inDict.buttons || 0; | |
11995 | |
11996 // Spec requires that pointers without pressure specified use 0.5 for down | |
11997 // state and 0 for up state. | |
11998 var pressure = 0; | |
11999 if (inDict.pressure) { | |
12000 pressure = inDict.pressure; | |
12001 } else { | |
12002 pressure = e.buttons ? 0.5 : 0; | |
12003 } | |
12004 | |
12005 // add x/y properties aliased to clientX/Y | |
12006 e.x = e.clientX; | |
12007 e.y = e.clientY; | |
12008 | |
12009 // define the properties of the PointerEvent interface | |
12010 e.pointerId = inDict.pointerId || 0; | |
12011 e.width = inDict.width || 0; | |
12012 e.height = inDict.height || 0; | |
12013 e.pressure = pressure; | |
12014 e.tiltX = inDict.tiltX || 0; | |
12015 e.tiltY = inDict.tiltY || 0; | |
12016 e.pointerType = inDict.pointerType || ''; | |
12017 e.hwTimestamp = inDict.hwTimestamp || 0; | |
12018 e.isPrimary = inDict.isPrimary || false; | |
12019 return e; | |
12020 } | |
12021 | |
12022 // attach to window | |
12023 if (!scope.PointerEvent) { | |
12024 scope.PointerEvent = PointerEvent; | |
12025 } | |
12026 })(window); | |
12027 | |
12028 /* | |
12029 * Copyright 2013 The Polymer Authors. All rights reserved. | |
12030 * Use of this source code is governed by a BSD-style | |
12031 * license that can be found in the LICENSE file. | |
12032 */ | |
12033 | |
12034 /** | |
12035 * This module implements an map of pointer states | |
12036 */ | |
12037 (function(scope) { | |
12038 var USE_MAP = window.Map && window.Map.prototype.forEach; | |
12039 var POINTERS_FN = function(){ return this.size; }; | |
12040 function PointerMap() { | |
12041 if (USE_MAP) { | |
12042 var m = new Map(); | |
12043 m.pointers = POINTERS_FN; | |
12044 return m; | |
12045 } else { | |
12046 this.keys = []; | |
12047 this.values = []; | |
12048 } | |
12049 } | |
12050 | |
12051 PointerMap.prototype = { | |
12052 set: function(inId, inEvent) { | |
12053 var i = this.keys.indexOf(inId); | |
12054 if (i > -1) { | |
12055 this.values[i] = inEvent; | |
12056 } else { | |
12057 this.keys.push(inId); | |
12058 this.values.push(inEvent); | |
12059 } | |
12060 }, | |
12061 has: function(inId) { | |
12062 return this.keys.indexOf(inId) > -1; | |
12063 }, | |
12064 'delete': function(inId) { | |
12065 var i = this.keys.indexOf(inId); | |
12066 if (i > -1) { | |
12067 this.keys.splice(i, 1); | |
12068 this.values.splice(i, 1); | |
12069 } | |
12070 }, | |
12071 get: function(inId) { | |
12072 var i = this.keys.indexOf(inId); | |
12073 return this.values[i]; | |
12074 }, | |
12075 clear: function() { | |
12076 this.keys.length = 0; | |
12077 this.values.length = 0; | |
12078 }, | |
12079 // return value, key, map | |
12080 forEach: function(callback, thisArg) { | |
12081 this.values.forEach(function(v, i) { | |
12082 callback.call(thisArg, v, this.keys[i], this); | |
12083 }, this); | |
12084 }, | |
12085 pointers: function() { | |
12086 return this.keys.length; | |
12087 } | |
12088 }; | |
12089 | |
12090 scope.PointerMap = PointerMap; | |
12091 })(window.PointerEventsPolyfill); | |
12092 | |
12093 /* | |
12094 * Copyright 2013 The Polymer Authors. All rights reserved. | |
12095 * Use of this source code is governed by a BSD-style | |
12096 * license that can be found in the LICENSE file. | |
12097 */ | |
12098 | |
12099 (function(scope) { | |
12100 var CLONE_PROPS = [ | |
12101 // MouseEvent | |
12102 'bubbles', | |
12103 'cancelable', | |
12104 'view', | |
12105 'detail', | |
12106 'screenX', | |
12107 'screenY', | |
12108 'clientX', | |
12109 'clientY', | |
12110 'ctrlKey', | |
12111 'altKey', | |
12112 'shiftKey', | |
12113 'metaKey', | |
12114 'button', | |
12115 'relatedTarget', | |
12116 // DOM Level 3 | |
12117 'buttons', | |
12118 // PointerEvent | |
12119 'pointerId', | |
12120 'width', | |
12121 'height', | |
12122 'pressure', | |
12123 'tiltX', | |
12124 'tiltY', | |
12125 'pointerType', | |
12126 'hwTimestamp', | |
12127 'isPrimary', | |
12128 // event instance | |
12129 'type', | |
12130 'target', | |
12131 'currentTarget', | |
12132 'which', | |
12133 'pageX', | |
12134 'pageY' | |
12135 ]; | |
12136 | |
12137 var CLONE_DEFAULTS = [ | |
12138 // MouseEvent | |
12139 false, | |
12140 false, | |
12141 null, | |
12142 null, | |
12143 0, | |
12144 0, | |
12145 0, | |
12146 0, | |
12147 false, | |
12148 false, | |
12149 false, | |
12150 false, | |
12151 0, | |
12152 null, | |
12153 // DOM Level 3 | |
12154 0, | |
12155 // PointerEvent | |
12156 0, | |
12157 0, | |
12158 0, | |
12159 0, | |
12160 0, | |
12161 0, | |
12162 '', | |
12163 0, | |
12164 false, | |
12165 // event instance | |
12166 '', | |
12167 null, | |
12168 null, | |
12169 0, | |
12170 0, | |
12171 0 | |
12172 ]; | |
12173 | |
12174 var HAS_SVG_INSTANCE = (typeof SVGElementInstance !== 'undefined'); | |
12175 | |
12176 /** | |
12177 * This module is for normalizing events. Mouse and Touch events will be | |
12178 * collected here, and fire PointerEvents that have the same semantics, no | |
12179 * matter the source. | |
12180 * Events fired: | |
12181 * - pointerdown: a pointing is added | |
12182 * - pointerup: a pointer is removed | |
12183 * - pointermove: a pointer is moved | |
12184 * - pointerover: a pointer crosses into an element | |
12185 * - pointerout: a pointer leaves an element | |
12186 * - pointercancel: a pointer will no longer generate events | |
12187 */ | |
12188 var dispatcher = { | |
12189 pointermap: new scope.PointerMap(), | |
12190 eventMap: Object.create(null), | |
12191 captureInfo: Object.create(null), | |
12192 // Scope objects for native events. | |
12193 // This exists for ease of testing. | |
12194 eventSources: Object.create(null), | |
12195 eventSourceList: [], | |
12196 /** | |
12197 * Add a new event source that will generate pointer events. | |
12198 * | |
12199 * `inSource` must contain an array of event names named `events`, and | |
12200 * functions with the names specified in the `events` array. | |
12201 * @param {string} name A name for the event source | |
12202 * @param {Object} source A new source of platform events. | |
12203 */ | |
12204 registerSource: function(name, source) { | |
12205 var s = source; | |
12206 var newEvents = s.events; | |
12207 if (newEvents) { | |
12208 newEvents.forEach(function(e) { | |
12209 if (s[e]) { | |
12210 this.eventMap[e] = s[e].bind(s); | |
12211 } | |
12212 }, this); | |
12213 this.eventSources[name] = s; | |
12214 this.eventSourceList.push(s); | |
12215 } | |
12216 }, | |
12217 register: function(element) { | |
12218 var l = this.eventSourceList.length; | |
12219 for (var i = 0, es; (i < l) && (es = this.eventSourceList[i]); i++) { | |
12220 // call eventsource register | |
12221 es.register.call(es, element); | |
12222 } | |
12223 }, | |
12224 unregister: function(element) { | |
12225 var l = this.eventSourceList.length; | |
12226 for (var i = 0, es; (i < l) && (es = this.eventSourceList[i]); i++) { | |
12227 // call eventsource register | |
12228 es.unregister.call(es, element); | |
12229 } | |
12230 }, | |
12231 contains: scope.external.contains || function(container, contained) { | |
12232 return container.contains(contained); | |
12233 }, | |
12234 // EVENTS | |
12235 down: function(inEvent) { | |
12236 inEvent.bubbles = true; | |
12237 this.fireEvent('pointerdown', inEvent); | |
12238 }, | |
12239 move: function(inEvent) { | |
12240 inEvent.bubbles = true; | |
12241 this.fireEvent('pointermove', inEvent); | |
12242 }, | |
12243 up: function(inEvent) { | |
12244 inEvent.bubbles = true; | |
12245 this.fireEvent('pointerup', inEvent); | |
12246 }, | |
12247 enter: function(inEvent) { | |
12248 inEvent.bubbles = false; | |
12249 this.fireEvent('pointerenter', inEvent); | |
12250 }, | |
12251 leave: function(inEvent) { | |
12252 inEvent.bubbles = false; | |
12253 this.fireEvent('pointerleave', inEvent); | |
12254 }, | |
12255 over: function(inEvent) { | |
12256 inEvent.bubbles = true; | |
12257 this.fireEvent('pointerover', inEvent); | |
12258 }, | |
12259 out: function(inEvent) { | |
12260 inEvent.bubbles = true; | |
12261 this.fireEvent('pointerout', inEvent); | |
12262 }, | |
12263 cancel: function(inEvent) { | |
12264 inEvent.bubbles = true; | |
12265 this.fireEvent('pointercancel', inEvent); | |
12266 }, | |
12267 leaveOut: function(event) { | |
12268 this.out(event); | |
12269 if (!this.contains(event.target, event.relatedTarget)) { | |
12270 this.leave(event); | |
12271 } | |
12272 }, | |
12273 enterOver: function(event) { | |
12274 this.over(event); | |
12275 if (!this.contains(event.target, event.relatedTarget)) { | |
12276 this.enter(event); | |
12277 } | |
12278 }, | |
12279 // LISTENER LOGIC | |
12280 eventHandler: function(inEvent) { | |
12281 // This is used to prevent multiple dispatch of pointerevents from | |
12282 // platform events. This can happen when two elements in different scopes | |
12283 // are set up to create pointer events, which is relevant to Shadow DOM. | |
12284 if (inEvent._handledByPE) { | |
12285 return; | |
12286 } | |
12287 var type = inEvent.type; | |
12288 var fn = this.eventMap && this.eventMap[type]; | |
12289 if (fn) { | |
12290 fn(inEvent); | |
12291 } | |
12292 inEvent._handledByPE = true; | |
12293 }, | |
12294 // set up event listeners | |
12295 listen: function(target, events) { | |
12296 events.forEach(function(e) { | |
12297 this.addEvent(target, e); | |
12298 }, this); | |
12299 }, | |
12300 // remove event listeners | |
12301 unlisten: function(target, events) { | |
12302 events.forEach(function(e) { | |
12303 this.removeEvent(target, e); | |
12304 }, this); | |
12305 }, | |
12306 addEvent: scope.external.addEvent || function(target, eventName) { | |
12307 target.addEventListener(eventName, this.boundHandler); | |
12308 }, | |
12309 removeEvent: scope.external.removeEvent || function(target, eventName) { | |
12310 target.removeEventListener(eventName, this.boundHandler); | |
12311 }, | |
12312 // EVENT CREATION AND TRACKING | |
12313 /** | |
12314 * Creates a new Event of type `inType`, based on the information in | |
12315 * `inEvent`. | |
12316 * | |
12317 * @param {string} inType A string representing the type of event to create | |
12318 * @param {Event} inEvent A platform event with a target | |
12319 * @return {Event} A PointerEvent of type `inType` | |
12320 */ | |
12321 makeEvent: function(inType, inEvent) { | |
12322 // relatedTarget must be null if pointer is captured | |
12323 if (this.captureInfo[inEvent.pointerId]) { | |
12324 inEvent.relatedTarget = null; | |
12325 } | |
12326 var e = new PointerEvent(inType, inEvent); | |
12327 if (inEvent.preventDefault) { | |
12328 e.preventDefault = inEvent.preventDefault; | |
12329 } | |
12330 e._target = e._target || inEvent.target; | |
12331 return e; | |
12332 }, | |
12333 // make and dispatch an event in one call | |
12334 fireEvent: function(inType, inEvent) { | |
12335 var e = this.makeEvent(inType, inEvent); | |
12336 return this.dispatchEvent(e); | |
12337 }, | |
12338 /** | |
12339 * Returns a snapshot of inEvent, with writable properties. | |
12340 * | |
12341 * @param {Event} inEvent An event that contains properties to copy. | |
12342 * @return {Object} An object containing shallow copies of `inEvent`'s | |
12343 * properties. | |
12344 */ | |
12345 cloneEvent: function(inEvent) { | |
12346 var eventCopy = Object.create(null), p; | |
12347 for (var i = 0; i < CLONE_PROPS.length; i++) { | |
12348 p = CLONE_PROPS[i]; | |
12349 eventCopy[p] = inEvent[p] || CLONE_DEFAULTS[i]; | |
12350 // Work around SVGInstanceElement shadow tree | |
12351 // Return the <use> element that is represented by the instance for Safa
ri, Chrome, IE. | |
12352 // This is the behavior implemented by Firefox. | |
12353 if (HAS_SVG_INSTANCE && (p === 'target' || p === 'relatedTarget')) { | |
12354 if (eventCopy[p] instanceof SVGElementInstance) { | |
12355 eventCopy[p] = eventCopy[p].correspondingUseElement; | |
12356 } | |
12357 } | |
12358 } | |
12359 // keep the semantics of preventDefault | |
12360 if (inEvent.preventDefault) { | |
12361 eventCopy.preventDefault = function() { | |
12362 inEvent.preventDefault(); | |
12363 }; | |
12364 } | |
12365 return eventCopy; | |
12366 }, | |
12367 getTarget: function(inEvent) { | |
12368 // if pointer capture is set, route all events for the specified pointerId | |
12369 // to the capture target | |
12370 return this.captureInfo[inEvent.pointerId] || inEvent._target; | |
12371 }, | |
12372 setCapture: function(inPointerId, inTarget) { | |
12373 if (this.captureInfo[inPointerId]) { | |
12374 this.releaseCapture(inPointerId); | |
12375 } | |
12376 this.captureInfo[inPointerId] = inTarget; | |
12377 var e = document.createEvent('Event'); | |
12378 e.initEvent('gotpointercapture', true, false); | |
12379 e.pointerId = inPointerId; | |
12380 this.implicitRelease = this.releaseCapture.bind(this, inPointerId); | |
12381 document.addEventListener('pointerup', this.implicitRelease); | |
12382 document.addEventListener('pointercancel', this.implicitRelease); | |
12383 e._target = inTarget; | |
12384 this.asyncDispatchEvent(e); | |
12385 }, | |
12386 releaseCapture: function(inPointerId) { | |
12387 var t = this.captureInfo[inPointerId]; | |
12388 if (t) { | |
12389 var e = document.createEvent('Event'); | |
12390 e.initEvent('lostpointercapture', true, false); | |
12391 e.pointerId = inPointerId; | |
12392 this.captureInfo[inPointerId] = undefined; | |
12393 document.removeEventListener('pointerup', this.implicitRelease); | |
12394 document.removeEventListener('pointercancel', this.implicitRelease); | |
12395 e._target = t; | |
12396 this.asyncDispatchEvent(e); | |
12397 } | |
12398 }, | |
12399 /** | |
12400 * Dispatches the event to its target. | |
12401 * | |
12402 * @param {Event} inEvent The event to be dispatched. | |
12403 * @return {Boolean} True if an event handler returns true, false otherwise. | |
12404 */ | |
12405 dispatchEvent: scope.external.dispatchEvent || function(inEvent) { | |
12406 var t = this.getTarget(inEvent); | |
12407 if (t) { | |
12408 return t.dispatchEvent(inEvent); | |
12409 } | |
12410 }, | |
12411 asyncDispatchEvent: function(inEvent) { | |
12412 requestAnimationFrame(this.dispatchEvent.bind(this, inEvent)); | |
12413 } | |
12414 }; | |
12415 dispatcher.boundHandler = dispatcher.eventHandler.bind(dispatcher); | |
12416 scope.dispatcher = dispatcher; | |
12417 scope.register = dispatcher.register.bind(dispatcher); | |
12418 scope.unregister = dispatcher.unregister.bind(dispatcher); | |
12419 })(window.PointerEventsPolyfill); | |
12420 | |
12421 /* | |
12422 * Copyright 2013 The Polymer Authors. All rights reserved. | |
12423 * Use of this source code is governed by a BSD-style | |
12424 * license that can be found in the LICENSE file. | |
12425 */ | |
12426 | |
12427 /** | |
12428 * This module uses Mutation Observers to dynamically adjust which nodes will | |
12429 * generate Pointer Events. | |
12430 * | |
12431 * All nodes that wish to generate Pointer Events must have the attribute | |
12432 * `touch-action` set to `none`. | |
12433 */ | |
12434 (function(scope) { | |
12435 var forEach = Array.prototype.forEach.call.bind(Array.prototype.forEach); | |
12436 var map = Array.prototype.map.call.bind(Array.prototype.map); | |
12437 var toArray = Array.prototype.slice.call.bind(Array.prototype.slice); | |
12438 var filter = Array.prototype.filter.call.bind(Array.prototype.filter); | |
12439 var MO = window.MutationObserver || window.WebKitMutationObserver; | |
12440 var SELECTOR = '[touch-action]'; | |
12441 var OBSERVER_INIT = { | |
12442 subtree: true, | |
12443 childList: true, | |
12444 attributes: true, | |
12445 attributeOldValue: true, | |
12446 attributeFilter: ['touch-action'] | |
12447 }; | |
12448 | |
12449 function Installer(add, remove, changed, binder) { | |
12450 this.addCallback = add.bind(binder); | |
12451 this.removeCallback = remove.bind(binder); | |
12452 this.changedCallback = changed.bind(binder); | |
12453 if (MO) { | |
12454 this.observer = new MO(this.mutationWatcher.bind(this)); | |
12455 } | |
12456 } | |
12457 | |
12458 Installer.prototype = { | |
12459 watchSubtree: function(target) { | |
12460 // Only watch scopes that can target find, as these are top-level. | |
12461 // Otherwise we can see duplicate additions and removals that add noise. | |
12462 // | |
12463 // TODO(dfreedman): For some instances with ShadowDOMPolyfill, we can see | |
12464 // a removal without an insertion when a node is redistributed among | |
12465 // shadows. Since it all ends up correct in the document, watching only | |
12466 // the document will yield the correct mutations to watch. | |
12467 if (scope.targetFinding.canTarget(target)) { | |
12468 this.observer.observe(target, OBSERVER_INIT); | |
12469 } | |
12470 }, | |
12471 enableOnSubtree: function(target) { | |
12472 this.watchSubtree(target); | |
12473 if (target === document && document.readyState !== 'complete') { | |
12474 this.installOnLoad(); | |
12475 } else { | |
12476 this.installNewSubtree(target); | |
12477 } | |
12478 }, | |
12479 installNewSubtree: function(target) { | |
12480 forEach(this.findElements(target), this.addElement, this); | |
12481 }, | |
12482 findElements: function(target) { | |
12483 if (target.querySelectorAll) { | |
12484 return target.querySelectorAll(SELECTOR); | |
12485 } | |
12486 return []; | |
12487 }, | |
12488 removeElement: function(el) { | |
12489 this.removeCallback(el); | |
12490 }, | |
12491 addElement: function(el) { | |
12492 this.addCallback(el); | |
12493 }, | |
12494 elementChanged: function(el, oldValue) { | |
12495 this.changedCallback(el, oldValue); | |
12496 }, | |
12497 concatLists: function(accum, list) { | |
12498 return accum.concat(toArray(list)); | |
12499 }, | |
12500 // register all touch-action = none nodes on document load | |
12501 installOnLoad: function() { | |
12502 document.addEventListener('readystatechange', function() { | |
12503 if (document.readyState === 'complete') { | |
12504 this.installNewSubtree(document); | |
12505 } | |
12506 }.bind(this)); | |
12507 }, | |
12508 isElement: function(n) { | |
12509 return n.nodeType === Node.ELEMENT_NODE; | |
12510 }, | |
12511 flattenMutationTree: function(inNodes) { | |
12512 // find children with touch-action | |
12513 var tree = map(inNodes, this.findElements, this); | |
12514 // make sure the added nodes are accounted for | |
12515 tree.push(filter(inNodes, this.isElement)); | |
12516 // flatten the list | |
12517 return tree.reduce(this.concatLists, []); | |
12518 }, | |
12519 mutationWatcher: function(mutations) { | |
12520 mutations.forEach(this.mutationHandler, this); | |
12521 }, | |
12522 mutationHandler: function(m) { | |
12523 if (m.type === 'childList') { | |
12524 var added = this.flattenMutationTree(m.addedNodes); | |
12525 added.forEach(this.addElement, this); | |
12526 var removed = this.flattenMutationTree(m.removedNodes); | |
12527 removed.forEach(this.removeElement, this); | |
12528 } else if (m.type === 'attributes') { | |
12529 this.elementChanged(m.target, m.oldValue); | |
12530 } | |
12531 } | |
12532 }; | |
12533 | |
12534 if (!MO) { | |
12535 Installer.prototype.watchSubtree = function(){ | |
12536 console.warn('PointerEventsPolyfill: MutationObservers not found, touch-ac
tion will not be dynamically detected'); | |
12537 }; | |
12538 } | |
12539 | |
12540 scope.Installer = Installer; | |
12541 })(window.PointerEventsPolyfill); | |
12542 | |
12543 /* | |
12544 * Copyright 2013 The Polymer Authors. All rights reserved. | |
12545 * Use of this source code is governed by a BSD-style | |
12546 * license that can be found in the LICENSE file. | |
12547 */ | |
12548 | |
12549 (function (scope) { | |
12550 var dispatcher = scope.dispatcher; | |
12551 var pointermap = dispatcher.pointermap; | |
12552 // radius around touchend that swallows mouse events | |
12553 var DEDUP_DIST = 25; | |
12554 | |
12555 var WHICH_TO_BUTTONS = [0, 1, 4, 2]; | |
12556 | |
12557 var HAS_BUTTONS = false; | |
12558 try { | |
12559 HAS_BUTTONS = new MouseEvent('test', {buttons: 1}).buttons === 1; | |
12560 } catch (e) {} | |
12561 | |
12562 // handler block for native mouse events | |
12563 var mouseEvents = { | |
12564 POINTER_ID: 1, | |
12565 POINTER_TYPE: 'mouse', | |
12566 events: [ | |
12567 'mousedown', | |
12568 'mousemove', | |
12569 'mouseup', | |
12570 'mouseover', | |
12571 'mouseout' | |
12572 ], | |
12573 register: function(target) { | |
12574 dispatcher.listen(target, this.events); | |
12575 }, | |
12576 unregister: function(target) { | |
12577 dispatcher.unlisten(target, this.events); | |
12578 }, | |
12579 lastTouches: [], | |
12580 // collide with the global mouse listener | |
12581 isEventSimulatedFromTouch: function(inEvent) { | |
12582 var lts = this.lastTouches; | |
12583 var x = inEvent.clientX, y = inEvent.clientY; | |
12584 for (var i = 0, l = lts.length, t; i < l && (t = lts[i]); i++) { | |
12585 // simulated mouse events will be swallowed near a primary touchend | |
12586 var dx = Math.abs(x - t.x), dy = Math.abs(y - t.y); | |
12587 if (dx <= DEDUP_DIST && dy <= DEDUP_DIST) { | |
12588 return true; | |
12589 } | |
12590 } | |
12591 }, | |
12592 prepareEvent: function(inEvent) { | |
12593 var e = dispatcher.cloneEvent(inEvent); | |
12594 // forward mouse preventDefault | |
12595 var pd = e.preventDefault; | |
12596 e.preventDefault = function() { | |
12597 inEvent.preventDefault(); | |
12598 pd(); | |
12599 }; | |
12600 e.pointerId = this.POINTER_ID; | |
12601 e.isPrimary = true; | |
12602 e.pointerType = this.POINTER_TYPE; | |
12603 if (!HAS_BUTTONS) { | |
12604 e.buttons = WHICH_TO_BUTTONS[e.which] || 0; | |
12605 } | |
12606 return e; | |
12607 }, | |
12608 mousedown: function(inEvent) { | |
12609 if (!this.isEventSimulatedFromTouch(inEvent)) { | |
12610 var p = pointermap.has(this.POINTER_ID); | |
12611 // TODO(dfreedman) workaround for some elements not sending mouseup | |
12612 // http://crbug/149091 | |
12613 if (p) { | |
12614 this.cancel(inEvent); | |
12615 } | |
12616 var e = this.prepareEvent(inEvent); | |
12617 pointermap.set(this.POINTER_ID, inEvent); | |
12618 dispatcher.down(e); | |
12619 } | |
12620 }, | |
12621 mousemove: function(inEvent) { | |
12622 if (!this.isEventSimulatedFromTouch(inEvent)) { | |
12623 var e = this.prepareEvent(inEvent); | |
12624 dispatcher.move(e); | |
12625 } | |
12626 }, | |
12627 mouseup: function(inEvent) { | |
12628 if (!this.isEventSimulatedFromTouch(inEvent)) { | |
12629 var p = pointermap.get(this.POINTER_ID); | |
12630 if (p && p.button === inEvent.button) { | |
12631 var e = this.prepareEvent(inEvent); | |
12632 dispatcher.up(e); | |
12633 this.cleanupMouse(); | |
12634 } | |
12635 } | |
12636 }, | |
12637 mouseover: function(inEvent) { | |
12638 if (!this.isEventSimulatedFromTouch(inEvent)) { | |
12639 var e = this.prepareEvent(inEvent); | |
12640 dispatcher.enterOver(e); | |
12641 } | |
12642 }, | |
12643 mouseout: function(inEvent) { | |
12644 if (!this.isEventSimulatedFromTouch(inEvent)) { | |
12645 var e = this.prepareEvent(inEvent); | |
12646 dispatcher.leaveOut(e); | |
12647 } | |
12648 }, | |
12649 cancel: function(inEvent) { | |
12650 var e = this.prepareEvent(inEvent); | |
12651 dispatcher.cancel(e); | |
12652 this.cleanupMouse(); | |
12653 }, | |
12654 cleanupMouse: function() { | |
12655 pointermap['delete'](this.POINTER_ID); | |
12656 } | |
12657 }; | |
12658 | |
12659 scope.mouseEvents = mouseEvents; | |
12660 })(window.PointerEventsPolyfill); | |
12661 | |
12662 /* | |
12663 * Copyright 2013 The Polymer Authors. All rights reserved. | |
12664 * Use of this source code is governed by a BSD-style | |
12665 * license that can be found in the LICENSE file. | |
12666 */ | |
12667 | |
12668 (function(scope) { | |
12669 var dispatcher = scope.dispatcher; | |
12670 var captureInfo = dispatcher.captureInfo; | |
12671 var findTarget = scope.findTarget; | |
12672 var allShadows = scope.targetFinding.allShadows.bind(scope.targetFinding); | |
12673 var pointermap = dispatcher.pointermap; | |
12674 var touchMap = Array.prototype.map.call.bind(Array.prototype.map); | |
12675 // This should be long enough to ignore compat mouse events made by touch | |
12676 var DEDUP_TIMEOUT = 2500; | |
12677 var CLICK_COUNT_TIMEOUT = 200; | |
12678 var ATTRIB = 'touch-action'; | |
12679 var INSTALLER; | |
12680 // The presence of touch event handlers blocks scrolling, and so we must be ca
reful to | |
12681 // avoid adding handlers unnecessarily. Chrome plans to add a touch-action-de
lay property | |
12682 // (crbug.com/329559) to address this, and once we have that we can opt-in to
a simpler | |
12683 // handler registration mechanism. Rather than try to predict how exactly to
opt-in to | |
12684 // that we'll just leave this disabled until there is a build of Chrome to tes
t. | |
12685 var HAS_TOUCH_ACTION_DELAY = false; | |
12686 | |
12687 // handler block for native touch events | |
12688 var touchEvents = { | |
12689 events: [ | |
12690 'touchstart', | |
12691 'touchmove', | |
12692 'touchend', | |
12693 'touchcancel' | |
12694 ], | |
12695 register: function(target) { | |
12696 if (HAS_TOUCH_ACTION_DELAY) { | |
12697 dispatcher.listen(target, this.events); | |
12698 } else { | |
12699 INSTALLER.enableOnSubtree(target); | |
12700 } | |
12701 }, | |
12702 unregister: function(target) { | |
12703 if (HAS_TOUCH_ACTION_DELAY) { | |
12704 dispatcher.unlisten(target, this.events); | |
12705 } else { | |
12706 // TODO(dfreedman): is it worth it to disconnect the MO? | |
12707 } | |
12708 }, | |
12709 elementAdded: function(el) { | |
12710 var a = el.getAttribute(ATTRIB); | |
12711 var st = this.touchActionToScrollType(a); | |
12712 if (st) { | |
12713 el._scrollType = st; | |
12714 dispatcher.listen(el, this.events); | |
12715 // set touch-action on shadows as well | |
12716 allShadows(el).forEach(function(s) { | |
12717 s._scrollType = st; | |
12718 dispatcher.listen(s, this.events); | |
12719 }, this); | |
12720 } | |
12721 }, | |
12722 elementRemoved: function(el) { | |
12723 el._scrollType = undefined; | |
12724 dispatcher.unlisten(el, this.events); | |
12725 // remove touch-action from shadow | |
12726 allShadows(el).forEach(function(s) { | |
12727 s._scrollType = undefined; | |
12728 dispatcher.unlisten(s, this.events); | |
12729 }, this); | |
12730 }, | |
12731 elementChanged: function(el, oldValue) { | |
12732 var a = el.getAttribute(ATTRIB); | |
12733 var st = this.touchActionToScrollType(a); | |
12734 var oldSt = this.touchActionToScrollType(oldValue); | |
12735 // simply update scrollType if listeners are already established | |
12736 if (st && oldSt) { | |
12737 el._scrollType = st; | |
12738 allShadows(el).forEach(function(s) { | |
12739 s._scrollType = st; | |
12740 }, this); | |
12741 } else if (oldSt) { | |
12742 this.elementRemoved(el); | |
12743 } else if (st) { | |
12744 this.elementAdded(el); | |
12745 } | |
12746 }, | |
12747 scrollTypes: { | |
12748 EMITTER: 'none', | |
12749 XSCROLLER: 'pan-x', | |
12750 YSCROLLER: 'pan-y', | |
12751 SCROLLER: /^(?:pan-x pan-y)|(?:pan-y pan-x)|auto$/ | |
12752 }, | |
12753 touchActionToScrollType: function(touchAction) { | |
12754 var t = touchAction; | |
12755 var st = this.scrollTypes; | |
12756 if (t === 'none') { | |
12757 return 'none'; | |
12758 } else if (t === st.XSCROLLER) { | |
12759 return 'X'; | |
12760 } else if (t === st.YSCROLLER) { | |
12761 return 'Y'; | |
12762 } else if (st.SCROLLER.exec(t)) { | |
12763 return 'XY'; | |
12764 } | |
12765 }, | |
12766 POINTER_TYPE: 'touch', | |
12767 firstTouch: null, | |
12768 isPrimaryTouch: function(inTouch) { | |
12769 return this.firstTouch === inTouch.identifier; | |
12770 }, | |
12771 setPrimaryTouch: function(inTouch) { | |
12772 // set primary touch if there no pointers, or the only pointer is the mous
e | |
12773 if (pointermap.pointers() === 0 || (pointermap.pointers() === 1 && pointer
map.has(1))) { | |
12774 this.firstTouch = inTouch.identifier; | |
12775 this.firstXY = {X: inTouch.clientX, Y: inTouch.clientY}; | |
12776 this.scrolling = false; | |
12777 this.cancelResetClickCount(); | |
12778 } | |
12779 }, | |
12780 removePrimaryPointer: function(inPointer) { | |
12781 if (inPointer.isPrimary) { | |
12782 this.firstTouch = null; | |
12783 this.firstXY = null; | |
12784 this.resetClickCount(); | |
12785 } | |
12786 }, | |
12787 clickCount: 0, | |
12788 resetId: null, | |
12789 resetClickCount: function() { | |
12790 var fn = function() { | |
12791 this.clickCount = 0; | |
12792 this.resetId = null; | |
12793 }.bind(this); | |
12794 this.resetId = setTimeout(fn, CLICK_COUNT_TIMEOUT); | |
12795 }, | |
12796 cancelResetClickCount: function() { | |
12797 if (this.resetId) { | |
12798 clearTimeout(this.resetId); | |
12799 } | |
12800 }, | |
12801 typeToButtons: function(type) { | |
12802 var ret = 0; | |
12803 if (type === 'touchstart' || type === 'touchmove') { | |
12804 ret = 1; | |
12805 } | |
12806 return ret; | |
12807 }, | |
12808 touchToPointer: function(inTouch) { | |
12809 var cte = this.currentTouchEvent; | |
12810 var e = dispatcher.cloneEvent(inTouch); | |
12811 // Spec specifies that pointerId 1 is reserved for Mouse. | |
12812 // Touch identifiers can start at 0. | |
12813 // Add 2 to the touch identifier for compatibility. | |
12814 var id = e.pointerId = inTouch.identifier + 2; | |
12815 e.target = captureInfo[id] || findTarget(e); | |
12816 e.bubbles = true; | |
12817 e.cancelable = true; | |
12818 e.detail = this.clickCount; | |
12819 e.button = 0; | |
12820 e.buttons = this.typeToButtons(cte.type); | |
12821 e.width = inTouch.webkitRadiusX || inTouch.radiusX || 0; | |
12822 e.height = inTouch.webkitRadiusY || inTouch.radiusY || 0; | |
12823 e.pressure = inTouch.webkitForce || inTouch.force || 0.5; | |
12824 e.isPrimary = this.isPrimaryTouch(inTouch); | |
12825 e.pointerType = this.POINTER_TYPE; | |
12826 // forward touch preventDefaults | |
12827 var self = this; | |
12828 e.preventDefault = function() { | |
12829 self.scrolling = false; | |
12830 self.firstXY = null; | |
12831 cte.preventDefault(); | |
12832 }; | |
12833 return e; | |
12834 }, | |
12835 processTouches: function(inEvent, inFunction) { | |
12836 var tl = inEvent.changedTouches; | |
12837 this.currentTouchEvent = inEvent; | |
12838 for (var i = 0, t; i < tl.length; i++) { | |
12839 t = tl[i]; | |
12840 inFunction.call(this, this.touchToPointer(t)); | |
12841 } | |
12842 }, | |
12843 // For single axis scrollers, determines whether the element should emit | |
12844 // pointer events or behave as a scroller | |
12845 shouldScroll: function(inEvent) { | |
12846 if (this.firstXY) { | |
12847 var ret; | |
12848 var scrollAxis = inEvent.currentTarget._scrollType; | |
12849 if (scrollAxis === 'none') { | |
12850 // this element is a touch-action: none, should never scroll | |
12851 ret = false; | |
12852 } else if (scrollAxis === 'XY') { | |
12853 // this element should always scroll | |
12854 ret = true; | |
12855 } else { | |
12856 var t = inEvent.changedTouches[0]; | |
12857 // check the intended scroll axis, and other axis | |
12858 var a = scrollAxis; | |
12859 var oa = scrollAxis === 'Y' ? 'X' : 'Y'; | |
12860 var da = Math.abs(t['client' + a] - this.firstXY[a]); | |
12861 var doa = Math.abs(t['client' + oa] - this.firstXY[oa]); | |
12862 // if delta in the scroll axis > delta other axis, scroll instead of | |
12863 // making events | |
12864 ret = da >= doa; | |
12865 } | |
12866 this.firstXY = null; | |
12867 return ret; | |
12868 } | |
12869 }, | |
12870 findTouch: function(inTL, inId) { | |
12871 for (var i = 0, l = inTL.length, t; i < l && (t = inTL[i]); i++) { | |
12872 if (t.identifier === inId) { | |
12873 return true; | |
12874 } | |
12875 } | |
12876 }, | |
12877 // In some instances, a touchstart can happen without a touchend. This | |
12878 // leaves the pointermap in a broken state. | |
12879 // Therefore, on every touchstart, we remove the touches that did not fire a | |
12880 // touchend event. | |
12881 // To keep state globally consistent, we fire a | |
12882 // pointercancel for this "abandoned" touch | |
12883 vacuumTouches: function(inEvent) { | |
12884 var tl = inEvent.touches; | |
12885 // pointermap.pointers() should be < tl.length here, as the touchstart has
not | |
12886 // been processed yet. | |
12887 if (pointermap.pointers() >= tl.length) { | |
12888 var d = []; | |
12889 pointermap.forEach(function(value, key) { | |
12890 // Never remove pointerId == 1, which is mouse. | |
12891 // Touch identifiers are 2 smaller than their pointerId, which is the | |
12892 // index in pointermap. | |
12893 if (key !== 1 && !this.findTouch(tl, key - 2)) { | |
12894 var p = value.out; | |
12895 d.push(p); | |
12896 } | |
12897 }, this); | |
12898 d.forEach(this.cancelOut, this); | |
12899 } | |
12900 }, | |
12901 touchstart: function(inEvent) { | |
12902 this.vacuumTouches(inEvent); | |
12903 this.setPrimaryTouch(inEvent.changedTouches[0]); | |
12904 this.dedupSynthMouse(inEvent); | |
12905 if (!this.scrolling) { | |
12906 this.clickCount++; | |
12907 this.processTouches(inEvent, this.overDown); | |
12908 } | |
12909 }, | |
12910 overDown: function(inPointer) { | |
12911 var p = pointermap.set(inPointer.pointerId, { | |
12912 target: inPointer.target, | |
12913 out: inPointer, | |
12914 outTarget: inPointer.target | |
12915 }); | |
12916 dispatcher.over(inPointer); | |
12917 dispatcher.enter(inPointer); | |
12918 dispatcher.down(inPointer); | |
12919 }, | |
12920 touchmove: function(inEvent) { | |
12921 if (!this.scrolling) { | |
12922 if (this.shouldScroll(inEvent)) { | |
12923 this.scrolling = true; | |
12924 this.touchcancel(inEvent); | |
12925 } else { | |
12926 inEvent.preventDefault(); | |
12927 this.processTouches(inEvent, this.moveOverOut); | |
12928 } | |
12929 } | |
12930 }, | |
12931 moveOverOut: function(inPointer) { | |
12932 var event = inPointer; | |
12933 var pointer = pointermap.get(event.pointerId); | |
12934 // a finger drifted off the screen, ignore it | |
12935 if (!pointer) { | |
12936 return; | |
12937 } | |
12938 var outEvent = pointer.out; | |
12939 var outTarget = pointer.outTarget; | |
12940 dispatcher.move(event); | |
12941 if (outEvent && outTarget !== event.target) { | |
12942 outEvent.relatedTarget = event.target; | |
12943 event.relatedTarget = outTarget; | |
12944 // recover from retargeting by shadow | |
12945 outEvent.target = outTarget; | |
12946 if (event.target) { | |
12947 dispatcher.leaveOut(outEvent); | |
12948 dispatcher.enterOver(event); | |
12949 } else { | |
12950 // clean up case when finger leaves the screen | |
12951 event.target = outTarget; | |
12952 event.relatedTarget = null; | |
12953 this.cancelOut(event); | |
12954 } | |
12955 } | |
12956 pointer.out = event; | |
12957 pointer.outTarget = event.target; | |
12958 }, | |
12959 touchend: function(inEvent) { | |
12960 this.dedupSynthMouse(inEvent); | |
12961 this.processTouches(inEvent, this.upOut); | |
12962 }, | |
12963 upOut: function(inPointer) { | |
12964 if (!this.scrolling) { | |
12965 dispatcher.up(inPointer); | |
12966 dispatcher.out(inPointer); | |
12967 dispatcher.leave(inPointer); | |
12968 } | |
12969 this.cleanUpPointer(inPointer); | |
12970 }, | |
12971 touchcancel: function(inEvent) { | |
12972 this.processTouches(inEvent, this.cancelOut); | |
12973 }, | |
12974 cancelOut: function(inPointer) { | |
12975 dispatcher.cancel(inPointer); | |
12976 dispatcher.out(inPointer); | |
12977 dispatcher.leave(inPointer); | |
12978 this.cleanUpPointer(inPointer); | |
12979 }, | |
12980 cleanUpPointer: function(inPointer) { | |
12981 pointermap['delete'](inPointer.pointerId); | |
12982 this.removePrimaryPointer(inPointer); | |
12983 }, | |
12984 // prevent synth mouse events from creating pointer events | |
12985 dedupSynthMouse: function(inEvent) { | |
12986 var lts = scope.mouseEvents.lastTouches; | |
12987 var t = inEvent.changedTouches[0]; | |
12988 // only the primary finger will synth mouse events | |
12989 if (this.isPrimaryTouch(t)) { | |
12990 // remember x/y of last touch | |
12991 var lt = {x: t.clientX, y: t.clientY}; | |
12992 lts.push(lt); | |
12993 var fn = (function(lts, lt){ | |
12994 var i = lts.indexOf(lt); | |
12995 if (i > -1) { | |
12996 lts.splice(i, 1); | |
12997 } | |
12998 }).bind(null, lts, lt); | |
12999 setTimeout(fn, DEDUP_TIMEOUT); | |
13000 } | |
13001 } | |
13002 }; | |
13003 | |
13004 if (!HAS_TOUCH_ACTION_DELAY) { | |
13005 INSTALLER = new scope.Installer(touchEvents.elementAdded, touchEvents.elemen
tRemoved, touchEvents.elementChanged, touchEvents); | |
13006 } | |
13007 | |
13008 scope.touchEvents = touchEvents; | |
13009 })(window.PointerEventsPolyfill); | |
13010 | |
13011 /* | |
13012 * Copyright 2013 The Polymer Authors. All rights reserved. | |
13013 * Use of this source code is governed by a BSD-style | |
13014 * license that can be found in the LICENSE file. | |
13015 */ | |
13016 | |
13017 (function(scope) { | |
13018 var dispatcher = scope.dispatcher; | |
13019 var pointermap = dispatcher.pointermap; | |
13020 var HAS_BITMAP_TYPE = window.MSPointerEvent && typeof window.MSPointerEvent.MS
POINTER_TYPE_MOUSE === 'number'; | |
13021 var msEvents = { | |
13022 events: [ | |
13023 'MSPointerDown', | |
13024 'MSPointerMove', | |
13025 'MSPointerUp', | |
13026 'MSPointerOut', | |
13027 'MSPointerOver', | |
13028 'MSPointerCancel', | |
13029 'MSGotPointerCapture', | |
13030 'MSLostPointerCapture' | |
13031 ], | |
13032 register: function(target) { | |
13033 dispatcher.listen(target, this.events); | |
13034 }, | |
13035 unregister: function(target) { | |
13036 dispatcher.unlisten(target, this.events); | |
13037 }, | |
13038 POINTER_TYPES: [ | |
13039 '', | |
13040 'unavailable', | |
13041 'touch', | |
13042 'pen', | |
13043 'mouse' | |
13044 ], | |
13045 prepareEvent: function(inEvent) { | |
13046 var e = inEvent; | |
13047 if (HAS_BITMAP_TYPE) { | |
13048 e = dispatcher.cloneEvent(inEvent); | |
13049 e.pointerType = this.POINTER_TYPES[inEvent.pointerType]; | |
13050 } | |
13051 return e; | |
13052 }, | |
13053 cleanup: function(id) { | |
13054 pointermap['delete'](id); | |
13055 }, | |
13056 MSPointerDown: function(inEvent) { | |
13057 pointermap.set(inEvent.pointerId, inEvent); | |
13058 var e = this.prepareEvent(inEvent); | |
13059 dispatcher.down(e); | |
13060 }, | |
13061 MSPointerMove: function(inEvent) { | |
13062 var e = this.prepareEvent(inEvent); | |
13063 dispatcher.move(e); | |
13064 }, | |
13065 MSPointerUp: function(inEvent) { | |
13066 var e = this.prepareEvent(inEvent); | |
13067 dispatcher.up(e); | |
13068 this.cleanup(inEvent.pointerId); | |
13069 }, | |
13070 MSPointerOut: function(inEvent) { | |
13071 var e = this.prepareEvent(inEvent); | |
13072 dispatcher.leaveOut(e); | |
13073 }, | |
13074 MSPointerOver: function(inEvent) { | |
13075 var e = this.prepareEvent(inEvent); | |
13076 dispatcher.enterOver(e); | |
13077 }, | |
13078 MSPointerCancel: function(inEvent) { | |
13079 var e = this.prepareEvent(inEvent); | |
13080 dispatcher.cancel(e); | |
13081 this.cleanup(inEvent.pointerId); | |
13082 }, | |
13083 MSLostPointerCapture: function(inEvent) { | |
13084 var e = dispatcher.makeEvent('lostpointercapture', inEvent); | |
13085 dispatcher.dispatchEvent(e); | |
13086 }, | |
13087 MSGotPointerCapture: function(inEvent) { | |
13088 var e = dispatcher.makeEvent('gotpointercapture', inEvent); | |
13089 dispatcher.dispatchEvent(e); | |
13090 } | |
13091 }; | |
13092 | |
13093 scope.msEvents = msEvents; | |
13094 })(window.PointerEventsPolyfill); | |
13095 | |
13096 /* | |
13097 * Copyright 2013 The Polymer Authors. All rights reserved. | |
13098 * Use of this source code is governed by a BSD-style | |
13099 * license that can be found in the LICENSE file. | |
13100 */ | |
13101 | |
13102 /** | |
13103 * This module contains the handlers for native platform events. | |
13104 * From here, the dispatcher is called to create unified pointer events. | |
13105 * Included are touch events (v1), mouse events, and MSPointerEvents. | |
13106 */ | |
13107 (function(scope) { | |
13108 var dispatcher = scope.dispatcher; | |
13109 | |
13110 // only activate if this platform does not have pointer events | |
13111 if (window.PointerEvent !== scope.PointerEvent) { | |
13112 | |
13113 if (window.navigator.msPointerEnabled) { | |
13114 var tp = window.navigator.msMaxTouchPoints; | |
13115 Object.defineProperty(window.navigator, 'maxTouchPoints', { | |
13116 value: tp, | |
13117 enumerable: true | |
13118 }); | |
13119 dispatcher.registerSource('ms', scope.msEvents); | |
13120 } else { | |
13121 dispatcher.registerSource('mouse', scope.mouseEvents); | |
13122 if (window.ontouchstart !== undefined) { | |
13123 dispatcher.registerSource('touch', scope.touchEvents); | |
13124 } | |
13125 } | |
13126 | |
13127 dispatcher.register(document); | |
13128 } | |
13129 })(window.PointerEventsPolyfill); | |
13130 | |
13131 /* | |
13132 * Copyright 2013 The Polymer Authors. All rights reserved. | |
13133 * Use of this source code is governed by a BSD-style | |
13134 * license that can be found in the LICENSE file. | |
13135 */ | |
13136 | |
13137 (function(scope) { | |
13138 var dispatcher = scope.dispatcher; | |
13139 var n = window.navigator; | |
13140 var s, r; | |
13141 function assertDown(id) { | |
13142 if (!dispatcher.pointermap.has(id)) { | |
13143 throw new Error('InvalidPointerId'); | |
13144 } | |
13145 } | |
13146 if (n.msPointerEnabled) { | |
13147 s = function(pointerId) { | |
13148 assertDown(pointerId); | |
13149 this.msSetPointerCapture(pointerId); | |
13150 }; | |
13151 r = function(pointerId) { | |
13152 assertDown(pointerId); | |
13153 this.msReleasePointerCapture(pointerId); | |
13154 }; | |
13155 } else { | |
13156 s = function setPointerCapture(pointerId) { | |
13157 assertDown(pointerId); | |
13158 dispatcher.setCapture(pointerId, this); | |
13159 }; | |
13160 r = function releasePointerCapture(pointerId) { | |
13161 assertDown(pointerId); | |
13162 dispatcher.releaseCapture(pointerId, this); | |
13163 }; | |
13164 } | |
13165 if (window.Element && !Element.prototype.setPointerCapture) { | |
13166 Object.defineProperties(Element.prototype, { | |
13167 'setPointerCapture': { | |
13168 value: s | |
13169 }, | |
13170 'releasePointerCapture': { | |
13171 value: r | |
13172 } | |
13173 }); | |
13174 } | |
13175 })(window.PointerEventsPolyfill); | |
13176 | |
13177 /* | |
13178 * Copyright 2013 The Polymer Authors. All rights reserved. | |
13179 * Use of this source code is governed by a BSD-style | |
13180 * license that can be found in the LICENSE file. | |
13181 */ | |
13182 | |
13183 /** | |
13184 * PointerGestureEvent is the constructor for all PointerGesture events. | |
13185 * | |
13186 * @module PointerGestures | |
13187 * @class PointerGestureEvent | |
13188 * @extends UIEvent | |
13189 * @constructor | |
13190 * @param {String} inType Event type | |
13191 * @param {Object} [inDict] Dictionary of properties to initialize on the event | |
13192 */ | |
13193 | |
13194 function PointerGestureEvent(inType, inDict) { | |
13195 var dict = inDict || {}; | |
13196 var e = document.createEvent('Event'); | |
13197 var props = { | |
13198 bubbles: Boolean(dict.bubbles) === dict.bubbles || true, | |
13199 cancelable: Boolean(dict.cancelable) === dict.cancelable || true | |
13200 }; | |
13201 | |
13202 e.initEvent(inType, props.bubbles, props.cancelable); | |
13203 | |
13204 var keys = Object.keys(dict), k; | |
13205 for (var i = 0; i < keys.length; i++) { | |
13206 k = keys[i]; | |
13207 e[k] = dict[k]; | |
13208 } | |
13209 | |
13210 e.preventTap = this.preventTap; | |
13211 | |
13212 return e; | |
13213 } | |
13214 | |
13215 /** | |
13216 * Allows for any gesture to prevent the tap gesture. | |
13217 * | |
13218 * @method preventTap | |
13219 */ | |
13220 PointerGestureEvent.prototype.preventTap = function() { | |
13221 this.tapPrevented = true; | |
13222 }; | |
13223 | |
13224 | |
13225 /* | |
13226 * Copyright 2013 The Polymer Authors. All rights reserved. | |
13227 * Use of this source code is governed by a BSD-style | |
13228 * license that can be found in the LICENSE file. | |
13229 */ | |
13230 | |
13231 (function(scope) { | |
13232 /** | |
13233 * This class contains the gesture recognizers that create the PointerGesture | |
13234 * events. | |
13235 * | |
13236 * @class PointerGestures | |
13237 * @static | |
13238 */ | |
13239 scope = scope || {}; | |
13240 scope.utils = { | |
13241 LCA: { | |
13242 // Determines the lowest node in the ancestor chain of a and b | |
13243 find: function(a, b) { | |
13244 if (a === b) { | |
13245 return a; | |
13246 } | |
13247 // fast case, a is a direct descendant of b or vice versa | |
13248 if (a.contains) { | |
13249 if (a.contains(b)) { | |
13250 return a; | |
13251 } | |
13252 if (b.contains(a)) { | |
13253 return b; | |
13254 } | |
13255 } | |
13256 var adepth = this.depth(a); | |
13257 var bdepth = this.depth(b); | |
13258 var d = adepth - bdepth; | |
13259 if (d > 0) { | |
13260 a = this.walk(a, d); | |
13261 } else { | |
13262 b = this.walk(b, -d); | |
13263 } | |
13264 while(a && b && a !== b) { | |
13265 a = this.walk(a, 1); | |
13266 b = this.walk(b, 1); | |
13267 } | |
13268 return a; | |
13269 }, | |
13270 walk: function(n, u) { | |
13271 for (var i = 0; i < u; i++) { | |
13272 n = n.parentNode; | |
13273 } | |
13274 return n; | |
13275 }, | |
13276 depth: function(n) { | |
13277 var d = 0; | |
13278 while(n) { | |
13279 d++; | |
13280 n = n.parentNode; | |
13281 } | |
13282 return d; | |
13283 } | |
13284 } | |
13285 }; | |
13286 scope.findLCA = function(a, b) { | |
13287 return scope.utils.LCA.find(a, b); | |
13288 } | |
13289 window.PointerGestures = scope; | |
13290 })(window.PointerGestures); | |
13291 | |
13292 /* | |
13293 * Copyright 2013 The Polymer Authors. All rights reserved. | |
13294 * Use of this source code is governed by a BSD-style | |
13295 * license that can be found in the LICENSE file. | |
13296 */ | |
13297 | |
13298 /** | |
13299 * This module implements an map of pointer states | |
13300 */ | |
13301 (function(scope) { | |
13302 var USE_MAP = window.Map && window.Map.prototype.forEach; | |
13303 var POINTERS_FN = function(){ return this.size; }; | |
13304 function PointerMap() { | |
13305 if (USE_MAP) { | |
13306 var m = new Map(); | |
13307 m.pointers = POINTERS_FN; | |
13308 return m; | |
13309 } else { | |
13310 this.keys = []; | |
13311 this.values = []; | |
13312 } | |
13313 } | |
13314 | |
13315 PointerMap.prototype = { | |
13316 set: function(inId, inEvent) { | |
13317 var i = this.keys.indexOf(inId); | |
13318 if (i > -1) { | |
13319 this.values[i] = inEvent; | |
13320 } else { | |
13321 this.keys.push(inId); | |
13322 this.values.push(inEvent); | |
13323 } | |
13324 }, | |
13325 has: function(inId) { | |
13326 return this.keys.indexOf(inId) > -1; | |
13327 }, | |
13328 'delete': function(inId) { | |
13329 var i = this.keys.indexOf(inId); | |
13330 if (i > -1) { | |
13331 this.keys.splice(i, 1); | |
13332 this.values.splice(i, 1); | |
13333 } | |
13334 }, | |
13335 get: function(inId) { | |
13336 var i = this.keys.indexOf(inId); | |
13337 return this.values[i]; | |
13338 }, | |
13339 clear: function() { | |
13340 this.keys.length = 0; | |
13341 this.values.length = 0; | |
13342 }, | |
13343 // return value, key, map | |
13344 forEach: function(callback, thisArg) { | |
13345 this.values.forEach(function(v, i) { | |
13346 callback.call(thisArg, v, this.keys[i], this); | |
13347 }, this); | |
13348 }, | |
13349 pointers: function() { | |
13350 return this.keys.length; | |
13351 } | |
13352 }; | |
13353 | |
13354 scope.PointerMap = PointerMap; | |
13355 })(window.PointerGestures); | |
13356 | |
13357 /* | |
13358 * Copyright 2013 The Polymer Authors. All rights reserved. | |
13359 * Use of this source code is governed by a BSD-style | |
13360 * license that can be found in the LICENSE file. | |
13361 */ | |
13362 | |
13363 (function(scope) { | |
13364 var CLONE_PROPS = [ | |
13365 // MouseEvent | |
13366 'bubbles', | |
13367 'cancelable', | |
13368 'view', | |
13369 'detail', | |
13370 'screenX', | |
13371 'screenY', | |
13372 'clientX', | |
13373 'clientY', | |
13374 'ctrlKey', | |
13375 'altKey', | |
13376 'shiftKey', | |
13377 'metaKey', | |
13378 'button', | |
13379 'relatedTarget', | |
13380 // DOM Level 3 | |
13381 'buttons', | |
13382 // PointerEvent | |
13383 'pointerId', | |
13384 'width', | |
13385 'height', | |
13386 'pressure', | |
13387 'tiltX', | |
13388 'tiltY', | |
13389 'pointerType', | |
13390 'hwTimestamp', | |
13391 'isPrimary', | |
13392 // event instance | |
13393 'type', | |
13394 'target', | |
13395 'currentTarget', | |
13396 'screenX', | |
13397 'screenY', | |
13398 'pageX', | |
13399 'pageY', | |
13400 'tapPrevented' | |
13401 ]; | |
13402 | |
13403 var CLONE_DEFAULTS = [ | |
13404 // MouseEvent | |
13405 false, | |
13406 false, | |
13407 null, | |
13408 null, | |
13409 0, | |
13410 0, | |
13411 0, | |
13412 0, | |
13413 false, | |
13414 false, | |
13415 false, | |
13416 false, | |
13417 0, | |
13418 null, | |
13419 // DOM Level 3 | |
13420 0, | |
13421 // PointerEvent | |
13422 0, | |
13423 0, | |
13424 0, | |
13425 0, | |
13426 0, | |
13427 0, | |
13428 '', | |
13429 0, | |
13430 false, | |
13431 // event instance | |
13432 '', | |
13433 null, | |
13434 null, | |
13435 0, | |
13436 0, | |
13437 0, | |
13438 0 | |
13439 ]; | |
13440 | |
13441 var dispatcher = { | |
13442 handledEvents: new WeakMap(), | |
13443 targets: new WeakMap(), | |
13444 handlers: {}, | |
13445 recognizers: {}, | |
13446 events: {}, | |
13447 // Add a new gesture recognizer to the event listeners. | |
13448 // Recognizer needs an `events` property. | |
13449 registerRecognizer: function(inName, inRecognizer) { | |
13450 var r = inRecognizer; | |
13451 this.recognizers[inName] = r; | |
13452 r.events.forEach(function(e) { | |
13453 if (r[e]) { | |
13454 this.events[e] = true; | |
13455 var f = r[e].bind(r); | |
13456 this.addHandler(e, f); | |
13457 } | |
13458 }, this); | |
13459 }, | |
13460 addHandler: function(inEvent, inFn) { | |
13461 var e = inEvent; | |
13462 if (!this.handlers[e]) { | |
13463 this.handlers[e] = []; | |
13464 } | |
13465 this.handlers[e].push(inFn); | |
13466 }, | |
13467 // add event listeners for inTarget | |
13468 registerTarget: function(inTarget) { | |
13469 this.listen(Object.keys(this.events), inTarget); | |
13470 }, | |
13471 // remove event listeners for inTarget | |
13472 unregisterTarget: function(inTarget) { | |
13473 this.unlisten(Object.keys(this.events), inTarget); | |
13474 }, | |
13475 // LISTENER LOGIC | |
13476 eventHandler: function(inEvent) { | |
13477 if (this.handledEvents.get(inEvent)) { | |
13478 return; | |
13479 } | |
13480 var type = inEvent.type, fns = this.handlers[type]; | |
13481 if (fns) { | |
13482 this.makeQueue(fns, inEvent); | |
13483 } | |
13484 this.handledEvents.set(inEvent, true); | |
13485 }, | |
13486 // queue event for async dispatch | |
13487 makeQueue: function(inHandlerFns, inEvent) { | |
13488 // must clone events to keep the (possibly shadowed) target correct for | |
13489 // async dispatching | |
13490 var e = this.cloneEvent(inEvent); | |
13491 requestAnimationFrame(this.runQueue.bind(this, inHandlerFns, e)); | |
13492 }, | |
13493 // Dispatch the queued events | |
13494 runQueue: function(inHandlers, inEvent) { | |
13495 this.currentPointerId = inEvent.pointerId; | |
13496 for (var i = 0, f, l = inHandlers.length; (i < l) && (f = inHandlers[i]);
i++) { | |
13497 f(inEvent); | |
13498 } | |
13499 this.currentPointerId = 0; | |
13500 }, | |
13501 // set up event listeners | |
13502 listen: function(inEvents, inTarget) { | |
13503 inEvents.forEach(function(e) { | |
13504 this.addEvent(e, this.boundHandler, false, inTarget); | |
13505 }, this); | |
13506 }, | |
13507 // remove event listeners | |
13508 unlisten: function(inEvents) { | |
13509 inEvents.forEach(function(e) { | |
13510 this.removeEvent(e, this.boundHandler, false, inTarget); | |
13511 }, this); | |
13512 }, | |
13513 addEvent: function(inEventName, inEventHandler, inCapture, inTarget) { | |
13514 inTarget.addEventListener(inEventName, inEventHandler, inCapture); | |
13515 }, | |
13516 removeEvent: function(inEventName, inEventHandler, inCapture, inTarget) { | |
13517 inTarget.removeEventListener(inEventName, inEventHandler, inCapture); | |
13518 }, | |
13519 // EVENT CREATION AND TRACKING | |
13520 // Creates a new Event of type `inType`, based on the information in | |
13521 // `inEvent`. | |
13522 makeEvent: function(inType, inDict) { | |
13523 return new PointerGestureEvent(inType, inDict); | |
13524 }, | |
13525 /* | |
13526 * Returns a snapshot of inEvent, with writable properties. | |
13527 * | |
13528 * @method cloneEvent | |
13529 * @param {Event} inEvent An event that contains properties to copy. | |
13530 * @return {Object} An object containing shallow copies of `inEvent`'s | |
13531 * properties. | |
13532 */ | |
13533 cloneEvent: function(inEvent) { | |
13534 var eventCopy = {}, p; | |
13535 for (var i = 0; i < CLONE_PROPS.length; i++) { | |
13536 p = CLONE_PROPS[i]; | |
13537 eventCopy[p] = inEvent[p] || CLONE_DEFAULTS[i]; | |
13538 } | |
13539 return eventCopy; | |
13540 }, | |
13541 // Dispatches the event to its target. | |
13542 dispatchEvent: function(inEvent, inTarget) { | |
13543 var t = inTarget || this.targets.get(inEvent); | |
13544 if (t) { | |
13545 t.dispatchEvent(inEvent); | |
13546 if (inEvent.tapPrevented) { | |
13547 this.preventTap(this.currentPointerId); | |
13548 } | |
13549 } | |
13550 }, | |
13551 asyncDispatchEvent: function(inEvent, inTarget) { | |
13552 requestAnimationFrame(this.dispatchEvent.bind(this, inEvent, inTarget)); | |
13553 }, | |
13554 preventTap: function(inPointerId) { | |
13555 var t = this.recognizers.tap; | |
13556 if (t){ | |
13557 t.preventTap(inPointerId); | |
13558 } | |
13559 } | |
13560 }; | |
13561 dispatcher.boundHandler = dispatcher.eventHandler.bind(dispatcher); | |
13562 // recognizers call into the dispatcher and load later | |
13563 // solve the chicken and egg problem by having registerScopes module run last | |
13564 dispatcher.registerQueue = []; | |
13565 dispatcher.immediateRegister = false; | |
13566 scope.dispatcher = dispatcher; | |
13567 /** | |
13568 * Enable gesture events for a given scope, typically | |
13569 * [ShadowRoots](https://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/shadow
/index.html#shadow-root-object). | |
13570 * | |
13571 * @for PointerGestures | |
13572 * @method register | |
13573 * @param {ShadowRoot} scope A top level scope to enable gesture | |
13574 * support on. | |
13575 */ | |
13576 scope.register = function(inScope) { | |
13577 if (dispatcher.immediateRegister) { | |
13578 var pe = window.PointerEventsPolyfill; | |
13579 if (pe) { | |
13580 pe.register(inScope); | |
13581 } | |
13582 scope.dispatcher.registerTarget(inScope); | |
13583 } else { | |
13584 dispatcher.registerQueue.push(inScope); | |
13585 } | |
13586 }; | |
13587 scope.register(document); | |
13588 })(window.PointerGestures); | |
13589 | |
13590 /* | |
13591 * Copyright 2013 The Polymer Authors. All rights reserved. | |
13592 * Use of this source code is governed by a BSD-style | |
13593 * license that can be found in the LICENSE file. | |
13594 */ | |
13595 | |
13596 /** | |
13597 * This event is fired when a pointer is held down for 200ms. | |
13598 * | |
13599 * @module PointerGestures | |
13600 * @submodule Events | |
13601 * @class hold | |
13602 */ | |
13603 /** | |
13604 * Type of pointer that made the holding event. | |
13605 * @type String | |
13606 * @property pointerType | |
13607 */ | |
13608 /** | |
13609 * Screen X axis position of the held pointer | |
13610 * @type Number | |
13611 * @property clientX | |
13612 */ | |
13613 /** | |
13614 * Screen Y axis position of the held pointer | |
13615 * @type Number | |
13616 * @property clientY | |
13617 */ | |
13618 /** | |
13619 * Type of pointer that made the holding event. | |
13620 * @type String | |
13621 * @property pointerType | |
13622 */ | |
13623 /** | |
13624 * This event is fired every 200ms while a pointer is held down. | |
13625 * | |
13626 * @class holdpulse | |
13627 * @extends hold | |
13628 */ | |
13629 /** | |
13630 * Milliseconds pointer has been held down. | |
13631 * @type Number | |
13632 * @property holdTime | |
13633 */ | |
13634 /** | |
13635 * This event is fired when a held pointer is released or moved. | |
13636 * | |
13637 * @class released | |
13638 */ | |
13639 | |
13640 (function(scope) { | |
13641 var dispatcher = scope.dispatcher; | |
13642 var hold = { | |
13643 // wait at least HOLD_DELAY ms between hold and pulse events | |
13644 HOLD_DELAY: 200, | |
13645 // pointer can move WIGGLE_THRESHOLD pixels before not counting as a hold | |
13646 WIGGLE_THRESHOLD: 16, | |
13647 events: [ | |
13648 'pointerdown', | |
13649 'pointermove', | |
13650 'pointerup', | |
13651 'pointercancel' | |
13652 ], | |
13653 heldPointer: null, | |
13654 holdJob: null, | |
13655 pulse: function() { | |
13656 var hold = Date.now() - this.heldPointer.timeStamp; | |
13657 var type = this.held ? 'holdpulse' : 'hold'; | |
13658 this.fireHold(type, hold); | |
13659 this.held = true; | |
13660 }, | |
13661 cancel: function() { | |
13662 clearInterval(this.holdJob); | |
13663 if (this.held) { | |
13664 this.fireHold('release'); | |
13665 } | |
13666 this.held = false; | |
13667 this.heldPointer = null; | |
13668 this.target = null; | |
13669 this.holdJob = null; | |
13670 }, | |
13671 pointerdown: function(inEvent) { | |
13672 if (inEvent.isPrimary && !this.heldPointer) { | |
13673 this.heldPointer = inEvent; | |
13674 this.target = inEvent.target; | |
13675 this.holdJob = setInterval(this.pulse.bind(this), this.HOLD_DELAY); | |
13676 } | |
13677 }, | |
13678 pointerup: function(inEvent) { | |
13679 if (this.heldPointer && this.heldPointer.pointerId === inEvent.pointerId)
{ | |
13680 this.cancel(); | |
13681 } | |
13682 }, | |
13683 pointercancel: function(inEvent) { | |
13684 this.cancel(); | |
13685 }, | |
13686 pointermove: function(inEvent) { | |
13687 if (this.heldPointer && this.heldPointer.pointerId === inEvent.pointerId)
{ | |
13688 var x = inEvent.clientX - this.heldPointer.clientX; | |
13689 var y = inEvent.clientY - this.heldPointer.clientY; | |
13690 if ((x * x + y * y) > this.WIGGLE_THRESHOLD) { | |
13691 this.cancel(); | |
13692 } | |
13693 } | |
13694 }, | |
13695 fireHold: function(inType, inHoldTime) { | |
13696 var p = { | |
13697 pointerType: this.heldPointer.pointerType, | |
13698 clientX: this.heldPointer.clientX, | |
13699 clientY: this.heldPointer.clientY | |
13700 }; | |
13701 if (inHoldTime) { | |
13702 p.holdTime = inHoldTime; | |
13703 } | |
13704 var e = dispatcher.makeEvent(inType, p); | |
13705 dispatcher.dispatchEvent(e, this.target); | |
13706 if (e.tapPrevented) { | |
13707 dispatcher.preventTap(this.heldPointer.pointerId); | |
13708 } | |
13709 } | |
13710 }; | |
13711 dispatcher.registerRecognizer('hold', hold); | |
13712 })(window.PointerGestures); | |
13713 | |
13714 /* | |
13715 * Copyright 2013 The Polymer Authors. All rights reserved. | |
13716 * Use of this source code is governed by a BSD-style | |
13717 * license that can be found in the LICENSE file. | |
13718 */ | |
13719 | |
13720 /** | |
13721 * This event denotes the beginning of a series of tracking events. | |
13722 * | |
13723 * @module PointerGestures | |
13724 * @submodule Events | |
13725 * @class trackstart | |
13726 */ | |
13727 /** | |
13728 * Pixels moved in the x direction since trackstart. | |
13729 * @type Number | |
13730 * @property dx | |
13731 */ | |
13732 /** | |
13733 * Pixes moved in the y direction since trackstart. | |
13734 * @type Number | |
13735 * @property dy | |
13736 */ | |
13737 /** | |
13738 * Pixels moved in the x direction since the last track. | |
13739 * @type Number | |
13740 * @property ddx | |
13741 */ | |
13742 /** | |
13743 * Pixles moved in the y direction since the last track. | |
13744 * @type Number | |
13745 * @property ddy | |
13746 */ | |
13747 /** | |
13748 * The clientX position of the track gesture. | |
13749 * @type Number | |
13750 * @property clientX | |
13751 */ | |
13752 /** | |
13753 * The clientY position of the track gesture. | |
13754 * @type Number | |
13755 * @property clientY | |
13756 */ | |
13757 /** | |
13758 * The pageX position of the track gesture. | |
13759 * @type Number | |
13760 * @property pageX | |
13761 */ | |
13762 /** | |
13763 * The pageY position of the track gesture. | |
13764 * @type Number | |
13765 * @property pageY | |
13766 */ | |
13767 /** | |
13768 * The screenX position of the track gesture. | |
13769 * @type Number | |
13770 * @property screenX | |
13771 */ | |
13772 /** | |
13773 * The screenY position of the track gesture. | |
13774 * @type Number | |
13775 * @property screenY | |
13776 */ | |
13777 /** | |
13778 * The last x axis direction of the pointer. | |
13779 * @type Number | |
13780 * @property xDirection | |
13781 */ | |
13782 /** | |
13783 * The last y axis direction of the pointer. | |
13784 * @type Number | |
13785 * @property yDirection | |
13786 */ | |
13787 /** | |
13788 * A shared object between all tracking events. | |
13789 * @type Object | |
13790 * @property trackInfo | |
13791 */ | |
13792 /** | |
13793 * The element currently under the pointer. | |
13794 * @type Element | |
13795 * @property relatedTarget | |
13796 */ | |
13797 /** | |
13798 * The type of pointer that make the track gesture. | |
13799 * @type String | |
13800 * @property pointerType | |
13801 */ | |
13802 /** | |
13803 * | |
13804 * This event fires for all pointer movement being tracked. | |
13805 * | |
13806 * @class track | |
13807 * @extends trackstart | |
13808 */ | |
13809 /** | |
13810 * This event fires when the pointer is no longer being tracked. | |
13811 * | |
13812 * @class trackend | |
13813 * @extends trackstart | |
13814 */ | |
13815 | |
13816 (function(scope) { | |
13817 var dispatcher = scope.dispatcher; | |
13818 var pointermap = new scope.PointerMap(); | |
13819 var track = { | |
13820 events: [ | |
13821 'pointerdown', | |
13822 'pointermove', | |
13823 'pointerup', | |
13824 'pointercancel' | |
13825 ], | |
13826 WIGGLE_THRESHOLD: 4, | |
13827 clampDir: function(inDelta) { | |
13828 return inDelta > 0 ? 1 : -1; | |
13829 }, | |
13830 calcPositionDelta: function(inA, inB) { | |
13831 var x = 0, y = 0; | |
13832 if (inA && inB) { | |
13833 x = inB.pageX - inA.pageX; | |
13834 y = inB.pageY - inA.pageY; | |
13835 } | |
13836 return {x: x, y: y}; | |
13837 }, | |
13838 fireTrack: function(inType, inEvent, inTrackingData) { | |
13839 var t = inTrackingData; | |
13840 var d = this.calcPositionDelta(t.downEvent, inEvent); | |
13841 var dd = this.calcPositionDelta(t.lastMoveEvent, inEvent); | |
13842 if (dd.x) { | |
13843 t.xDirection = this.clampDir(dd.x); | |
13844 } | |
13845 if (dd.y) { | |
13846 t.yDirection = this.clampDir(dd.y); | |
13847 } | |
13848 var trackData = { | |
13849 dx: d.x, | |
13850 dy: d.y, | |
13851 ddx: dd.x, | |
13852 ddy: dd.y, | |
13853 clientX: inEvent.clientX, | |
13854 clientY: inEvent.clientY, | |
13855 pageX: inEvent.pageX, | |
13856 pageY: inEvent.pageY, | |
13857 screenX: inEvent.screenX, | |
13858 screenY: inEvent.screenY, | |
13859 xDirection: t.xDirection, | |
13860 yDirection: t.yDirection, | |
13861 trackInfo: t.trackInfo, | |
13862 relatedTarget: inEvent.target, | |
13863 pointerType: inEvent.pointerType | |
13864 }; | |
13865 var e = dispatcher.makeEvent(inType, trackData); | |
13866 t.lastMoveEvent = inEvent; | |
13867 dispatcher.dispatchEvent(e, t.downTarget); | |
13868 }, | |
13869 pointerdown: function(inEvent) { | |
13870 if (inEvent.isPrimary && (inEvent.pointerType === 'mouse' ? inEvent.butto
ns === 1 : true)) { | |
13871 var p = { | |
13872 downEvent: inEvent, | |
13873 downTarget: inEvent.target, | |
13874 trackInfo: {}, | |
13875 lastMoveEvent: null, | |
13876 xDirection: 0, | |
13877 yDirection: 0, | |
13878 tracking: false | |
13879 }; | |
13880 pointermap.set(inEvent.pointerId, p); | |
13881 } | |
13882 }, | |
13883 pointermove: function(inEvent) { | |
13884 var p = pointermap.get(inEvent.pointerId); | |
13885 if (p) { | |
13886 if (!p.tracking) { | |
13887 var d = this.calcPositionDelta(p.downEvent, inEvent); | |
13888 var move = d.x * d.x + d.y * d.y; | |
13889 // start tracking only if finger moves more than WIGGLE_THRESHOLD | |
13890 if (move > this.WIGGLE_THRESHOLD) { | |
13891 p.tracking = true; | |
13892 this.fireTrack('trackstart', p.downEvent, p); | |
13893 this.fireTrack('track', inEvent, p); | |
13894 } | |
13895 } else { | |
13896 this.fireTrack('track', inEvent, p); | |
13897 } | |
13898 } | |
13899 }, | |
13900 pointerup: function(inEvent) { | |
13901 var p = pointermap.get(inEvent.pointerId); | |
13902 if (p) { | |
13903 if (p.tracking) { | |
13904 this.fireTrack('trackend', inEvent, p); | |
13905 } | |
13906 pointermap.delete(inEvent.pointerId); | |
13907 } | |
13908 }, | |
13909 pointercancel: function(inEvent) { | |
13910 this.pointerup(inEvent); | |
13911 } | |
13912 }; | |
13913 dispatcher.registerRecognizer('track', track); | |
13914 })(window.PointerGestures); | |
13915 | |
13916 /* | |
13917 * Copyright 2013 The Polymer Authors. All rights reserved. | |
13918 * Use of this source code is governed by a BSD-style | |
13919 * license that can be found in the LICENSE file. | |
13920 */ | |
13921 | |
13922 /** | |
13923 * This event denotes a rapid down/move/up sequence from a pointer. | |
13924 * | |
13925 * The event is sent to the first element the pointer went down on. | |
13926 * | |
13927 * @module PointerGestures | |
13928 * @submodule Events | |
13929 * @class flick | |
13930 */ | |
13931 /** | |
13932 * Signed velocity of the flick in the x direction. | |
13933 * @property xVelocity | |
13934 * @type Number | |
13935 */ | |
13936 /** | |
13937 * Signed velocity of the flick in the y direction. | |
13938 * @type Number | |
13939 * @property yVelocity | |
13940 */ | |
13941 /** | |
13942 * Unsigned total velocity of the flick. | |
13943 * @type Number | |
13944 * @property velocity | |
13945 */ | |
13946 /** | |
13947 * Angle of the flick in degrees, with 0 along the | |
13948 * positive x axis. | |
13949 * @type Number | |
13950 * @property angle | |
13951 */ | |
13952 /** | |
13953 * Axis with the greatest absolute velocity. Denoted | |
13954 * with 'x' or 'y'. | |
13955 * @type String | |
13956 * @property majorAxis | |
13957 */ | |
13958 /** | |
13959 * Type of the pointer that made the flick. | |
13960 * @type String | |
13961 * @property pointerType | |
13962 */ | |
13963 | |
13964 (function(scope) { | |
13965 var dispatcher = scope.dispatcher; | |
13966 var flick = { | |
13967 // TODO(dfreedman): value should be low enough for low speed flicks, but | |
13968 // high enough to remove accidental flicks | |
13969 MIN_VELOCITY: 0.5 /* px/ms */, | |
13970 MAX_QUEUE: 4, | |
13971 moveQueue: [], | |
13972 target: null, | |
13973 pointerId: null, | |
13974 events: [ | |
13975 'pointerdown', | |
13976 'pointermove', | |
13977 'pointerup', | |
13978 'pointercancel' | |
13979 ], | |
13980 pointerdown: function(inEvent) { | |
13981 if (inEvent.isPrimary && !this.pointerId) { | |
13982 this.pointerId = inEvent.pointerId; | |
13983 this.target = inEvent.target; | |
13984 this.addMove(inEvent); | |
13985 } | |
13986 }, | |
13987 pointermove: function(inEvent) { | |
13988 if (inEvent.pointerId === this.pointerId) { | |
13989 this.addMove(inEvent); | |
13990 } | |
13991 }, | |
13992 pointerup: function(inEvent) { | |
13993 if (inEvent.pointerId === this.pointerId) { | |
13994 this.fireFlick(inEvent); | |
13995 } | |
13996 this.cleanup(); | |
13997 }, | |
13998 pointercancel: function(inEvent) { | |
13999 this.cleanup(); | |
14000 }, | |
14001 cleanup: function() { | |
14002 this.moveQueue = []; | |
14003 this.target = null; | |
14004 this.pointerId = null; | |
14005 }, | |
14006 addMove: function(inEvent) { | |
14007 if (this.moveQueue.length >= this.MAX_QUEUE) { | |
14008 this.moveQueue.shift(); | |
14009 } | |
14010 this.moveQueue.push(inEvent); | |
14011 }, | |
14012 fireFlick: function(inEvent) { | |
14013 var e = inEvent; | |
14014 var l = this.moveQueue.length; | |
14015 var dt, dx, dy, tx, ty, tv, x = 0, y = 0, v = 0; | |
14016 // flick based off the fastest segment of movement | |
14017 for (var i = 0, m; i < l && (m = this.moveQueue[i]); i++) { | |
14018 dt = e.timeStamp - m.timeStamp; | |
14019 dx = e.clientX - m.clientX, dy = e.clientY - m.clientY; | |
14020 tx = dx / dt, ty = dy / dt, tv = Math.sqrt(tx * tx + ty * ty); | |
14021 if (tv > v) { | |
14022 x = tx, y = ty, v = tv; | |
14023 } | |
14024 } | |
14025 var ma = Math.abs(x) > Math.abs(y) ? 'x' : 'y'; | |
14026 var a = this.calcAngle(x, y); | |
14027 if (Math.abs(v) >= this.MIN_VELOCITY) { | |
14028 var ev = dispatcher.makeEvent('flick', { | |
14029 xVelocity: x, | |
14030 yVelocity: y, | |
14031 velocity: v, | |
14032 angle: a, | |
14033 majorAxis: ma, | |
14034 pointerType: inEvent.pointerType | |
14035 }); | |
14036 dispatcher.dispatchEvent(ev, this.target); | |
14037 } | |
14038 }, | |
14039 calcAngle: function(inX, inY) { | |
14040 return (Math.atan2(inY, inX) * 180 / Math.PI); | |
14041 } | |
14042 }; | |
14043 dispatcher.registerRecognizer('flick', flick); | |
14044 })(window.PointerGestures); | |
14045 | |
14046 /* | |
14047 * Copyright 2013 The Polymer Authors. All rights reserved. | |
14048 * Use of this source code is governed by a BSD-style | |
14049 * license that can be found in the LICENSE file. | |
14050 */ | |
14051 | |
14052 /* | |
14053 * Basic strategy: find the farthest apart points, use as diameter of circle | |
14054 * react to size change and rotation of the chord | |
14055 */ | |
14056 | |
14057 /** | |
14058 * @module PointerGestures | |
14059 * @submodule Events | |
14060 * @class pinch | |
14061 */ | |
14062 /** | |
14063 * Scale of the pinch zoom gesture | |
14064 * @property scale | |
14065 * @type Number | |
14066 */ | |
14067 /** | |
14068 * Center X position of pointers causing pinch | |
14069 * @property centerX | |
14070 * @type Number | |
14071 */ | |
14072 /** | |
14073 * Center Y position of pointers causing pinch | |
14074 * @property centerY | |
14075 * @type Number | |
14076 */ | |
14077 | |
14078 /** | |
14079 * @module PointerGestures | |
14080 * @submodule Events | |
14081 * @class rotate | |
14082 */ | |
14083 /** | |
14084 * Angle (in degrees) of rotation. Measured from starting positions of pointers. | |
14085 * @property angle | |
14086 * @type Number | |
14087 */ | |
14088 /** | |
14089 * Center X position of pointers causing rotation | |
14090 * @property centerX | |
14091 * @type Number | |
14092 */ | |
14093 /** | |
14094 * Center Y position of pointers causing rotation | |
14095 * @property centerY | |
14096 * @type Number | |
14097 */ | |
14098 (function(scope) { | |
14099 var dispatcher = scope.dispatcher; | |
14100 var pointermap = new scope.PointerMap(); | |
14101 var RAD_TO_DEG = 180 / Math.PI; | |
14102 var pinch = { | |
14103 events: [ | |
14104 'pointerdown', | |
14105 'pointermove', | |
14106 'pointerup', | |
14107 'pointercancel' | |
14108 ], | |
14109 reference: {}, | |
14110 pointerdown: function(ev) { | |
14111 pointermap.set(ev.pointerId, ev); | |
14112 if (pointermap.pointers() == 2) { | |
14113 var points = this.calcChord(); | |
14114 var angle = this.calcAngle(points); | |
14115 this.reference = { | |
14116 angle: angle, | |
14117 diameter: points.diameter, | |
14118 target: scope.findLCA(points.a.target, points.b.target) | |
14119 }; | |
14120 } | |
14121 }, | |
14122 pointerup: function(ev) { | |
14123 pointermap.delete(ev.pointerId); | |
14124 }, | |
14125 pointermove: function(ev) { | |
14126 if (pointermap.has(ev.pointerId)) { | |
14127 pointermap.set(ev.pointerId, ev); | |
14128 if (pointermap.pointers() > 1) { | |
14129 this.calcPinchRotate(); | |
14130 } | |
14131 } | |
14132 }, | |
14133 pointercancel: function(ev) { | |
14134 this.pointerup(ev); | |
14135 }, | |
14136 dispatchPinch: function(diameter, points) { | |
14137 var zoom = diameter / this.reference.diameter; | |
14138 var ev = dispatcher.makeEvent('pinch', { | |
14139 scale: zoom, | |
14140 centerX: points.center.x, | |
14141 centerY: points.center.y | |
14142 }); | |
14143 dispatcher.dispatchEvent(ev, this.reference.target); | |
14144 }, | |
14145 dispatchRotate: function(angle, points) { | |
14146 var diff = Math.round((angle - this.reference.angle) % 360); | |
14147 var ev = dispatcher.makeEvent('rotate', { | |
14148 angle: diff, | |
14149 centerX: points.center.x, | |
14150 centerY: points.center.y | |
14151 }); | |
14152 dispatcher.dispatchEvent(ev, this.reference.target); | |
14153 }, | |
14154 calcPinchRotate: function() { | |
14155 var points = this.calcChord(); | |
14156 var diameter = points.diameter; | |
14157 var angle = this.calcAngle(points); | |
14158 if (diameter != this.reference.diameter) { | |
14159 this.dispatchPinch(diameter, points); | |
14160 } | |
14161 if (angle != this.reference.angle) { | |
14162 this.dispatchRotate(angle, points); | |
14163 } | |
14164 }, | |
14165 calcChord: function() { | |
14166 var pointers = []; | |
14167 pointermap.forEach(function(p) { | |
14168 pointers.push(p); | |
14169 }); | |
14170 var dist = 0; | |
14171 // start with at least two pointers | |
14172 var points = {a: pointers[0], b: pointers[1]}; | |
14173 var x, y, d; | |
14174 for (var i = 0; i < pointers.length; i++) { | |
14175 var a = pointers[i]; | |
14176 for (var j = i + 1; j < pointers.length; j++) { | |
14177 var b = pointers[j]; | |
14178 x = Math.abs(a.clientX - b.clientX); | |
14179 y = Math.abs(a.clientY - b.clientY); | |
14180 d = x + y; | |
14181 if (d > dist) { | |
14182 dist = d; | |
14183 points = {a: a, b: b}; | |
14184 } | |
14185 } | |
14186 } | |
14187 x = Math.abs(points.a.clientX + points.b.clientX) / 2; | |
14188 y = Math.abs(points.a.clientY + points.b.clientY) / 2; | |
14189 points.center = { x: x, y: y }; | |
14190 points.diameter = dist; | |
14191 return points; | |
14192 }, | |
14193 calcAngle: function(points) { | |
14194 var x = points.a.clientX - points.b.clientX; | |
14195 var y = points.a.clientY - points.b.clientY; | |
14196 return (360 + Math.atan2(y, x) * RAD_TO_DEG) % 360; | |
14197 }, | |
14198 }; | |
14199 dispatcher.registerRecognizer('pinch', pinch); | |
14200 })(window.PointerGestures); | |
14201 | |
14202 /* | |
14203 * Copyright 2013 The Polymer Authors. All rights reserved. | |
14204 * Use of this source code is governed by a BSD-style | |
14205 * license that can be found in the LICENSE file. | |
14206 */ | |
14207 | |
14208 /** | |
14209 * This event is fired when a pointer quickly goes down and up, and is used to | |
14210 * denote activation. | |
14211 * | |
14212 * Any gesture event can prevent the tap event from being created by calling | |
14213 * `event.preventTap`. | |
14214 * | |
14215 * Any pointer event can prevent the tap by setting the `tapPrevented` property | |
14216 * on itself. | |
14217 * | |
14218 * @module PointerGestures | |
14219 * @submodule Events | |
14220 * @class tap | |
14221 */ | |
14222 /** | |
14223 * X axis position of the tap. | |
14224 * @property x | |
14225 * @type Number | |
14226 */ | |
14227 /** | |
14228 * Y axis position of the tap. | |
14229 * @property y | |
14230 * @type Number | |
14231 */ | |
14232 /** | |
14233 * Type of the pointer that made the tap. | |
14234 * @property pointerType | |
14235 * @type String | |
14236 */ | |
14237 (function(scope) { | |
14238 var dispatcher = scope.dispatcher; | |
14239 var pointermap = new scope.PointerMap(); | |
14240 var tap = { | |
14241 events: [ | |
14242 'pointerdown', | |
14243 'pointermove', | |
14244 'pointerup', | |
14245 'pointercancel', | |
14246 'keyup' | |
14247 ], | |
14248 pointerdown: function(inEvent) { | |
14249 if (inEvent.isPrimary && !inEvent.tapPrevented) { | |
14250 pointermap.set(inEvent.pointerId, { | |
14251 target: inEvent.target, | |
14252 buttons: inEvent.buttons, | |
14253 x: inEvent.clientX, | |
14254 y: inEvent.clientY | |
14255 }); | |
14256 } | |
14257 }, | |
14258 pointermove: function(inEvent) { | |
14259 if (inEvent.isPrimary) { | |
14260 var start = pointermap.get(inEvent.pointerId); | |
14261 if (start) { | |
14262 if (inEvent.tapPrevented) { | |
14263 pointermap.delete(inEvent.pointerId); | |
14264 } | |
14265 } | |
14266 } | |
14267 }, | |
14268 shouldTap: function(e, downState) { | |
14269 if (!e.tapPrevented) { | |
14270 if (e.pointerType === 'mouse') { | |
14271 // only allow left click to tap for mouse | |
14272 return downState.buttons === 1; | |
14273 } else { | |
14274 return true; | |
14275 } | |
14276 } | |
14277 }, | |
14278 pointerup: function(inEvent) { | |
14279 var start = pointermap.get(inEvent.pointerId); | |
14280 if (start && this.shouldTap(inEvent, start)) { | |
14281 var t = scope.findLCA(start.target, inEvent.target); | |
14282 if (t) { | |
14283 var e = dispatcher.makeEvent('tap', { | |
14284 x: inEvent.clientX, | |
14285 y: inEvent.clientY, | |
14286 detail: inEvent.detail, | |
14287 pointerType: inEvent.pointerType | |
14288 }); | |
14289 dispatcher.dispatchEvent(e, t); | |
14290 } | |
14291 } | |
14292 pointermap.delete(inEvent.pointerId); | |
14293 }, | |
14294 pointercancel: function(inEvent) { | |
14295 pointermap.delete(inEvent.pointerId); | |
14296 }, | |
14297 keyup: function(inEvent) { | |
14298 var code = inEvent.keyCode; | |
14299 // 32 == spacebar | |
14300 if (code === 32) { | |
14301 var t = inEvent.target; | |
14302 if (!(t instanceof HTMLInputElement || t instanceof HTMLTextAreaElement)
) { | |
14303 dispatcher.dispatchEvent(dispatcher.makeEvent('tap', { | |
14304 x: 0, | |
14305 y: 0, | |
14306 detail: 0, | |
14307 pointerType: 'unavailable' | |
14308 }), t); | |
14309 } | |
14310 } | |
14311 }, | |
14312 preventTap: function(inPointerId) { | |
14313 pointermap.delete(inPointerId); | |
14314 } | |
14315 }; | |
14316 dispatcher.registerRecognizer('tap', tap); | |
14317 })(window.PointerGestures); | |
14318 | |
14319 /* | |
14320 * Copyright 2014 The Polymer Authors. All rights reserved. | |
14321 * Use of this source code is governed by a BSD-style | |
14322 * license that can be found in the LICENSE file. | |
14323 */ | |
14324 | |
14325 /** | |
14326 * Because recognizers are loaded after dispatcher, we have to wait to register | |
14327 * scopes until after all the recognizers. | |
14328 */ | |
14329 (function(scope) { | |
14330 var dispatcher = scope.dispatcher; | |
14331 function registerScopes() { | |
14332 dispatcher.immediateRegister = true; | |
14333 var rq = dispatcher.registerQueue; | |
14334 rq.forEach(scope.register); | |
14335 rq.length = 0; | |
14336 } | |
14337 if (document.readyState === 'complete') { | |
14338 registerScopes(); | |
14339 } else { | |
14340 // register scopes after a steadystate is reached | |
14341 // less MutationObserver churn | |
14342 document.addEventListener('readystatechange', function() { | |
14343 if (document.readyState === 'complete') { | |
14344 registerScopes(); | |
14345 } | |
14346 }); | |
14347 } | |
14348 })(window.PointerGestures); | |
14349 | |
14350 // Copyright (c) 2014 The Polymer Project Authors. All rights reserved. | 12226 // Copyright (c) 2014 The Polymer Project Authors. All rights reserved. |
14351 // This code may only be used under the BSD style license found at http://polyme
r.github.io/LICENSE.txt | 12227 // This code may only be used under the BSD style license found at http://polyme
r.github.io/LICENSE.txt |
14352 // The complete set of authors may be found at http://polymer.github.io/AUTHORS.
txt | 12228 // The complete set of authors may be found at http://polymer.github.io/AUTHORS.
txt |
14353 // The complete set of contributors may be found at http://polymer.github.io/CON
TRIBUTORS.txt | 12229 // The complete set of contributors may be found at http://polymer.github.io/CON
TRIBUTORS.txt |
14354 // Code distributed by Google as part of the polymer project is also | 12230 // Code distributed by Google as part of the polymer project is also |
14355 // subject to an additional IP rights grant found at http://polymer.github.io/PA
TENTS.txt | 12231 // subject to an additional IP rights grant found at http://polymer.github.io/PA
TENTS.txt |
14356 | 12232 |
14357 (function(global) { | 12233 (function(global) { |
14358 'use strict'; | 12234 'use strict'; |
14359 | 12235 |
14360 var filter = Array.prototype.filter.call.bind(Array.prototype.filter); | 12236 var filter = Array.prototype.filter.call.bind(Array.prototype.filter); |
14361 | 12237 |
14362 function getTreeScope(node) { | 12238 function getTreeScope(node) { |
14363 while (node.parentNode) { | 12239 while (node.parentNode) { |
14364 node = node.parentNode; | 12240 node = node.parentNode; |
14365 } | 12241 } |
14366 | 12242 |
14367 return typeof node.getElementById === 'function' ? node : null; | 12243 return typeof node.getElementById === 'function' ? node : null; |
14368 } | 12244 } |
14369 | 12245 |
14370 Node.prototype.bind = function(name, observable) { | 12246 Node.prototype.bind = function(name, observable) { |
14371 console.error('Unhandled binding to Node: ', this, name, observable); | 12247 console.error('Unhandled binding to Node: ', this, name, observable); |
14372 }; | 12248 }; |
14373 | 12249 |
| 12250 Node.prototype.bindFinished = function() {}; |
| 12251 |
14374 function updateBindings(node, name, binding) { | 12252 function updateBindings(node, name, binding) { |
14375 var bindings = node.bindings_; | 12253 var bindings = node.bindings_; |
14376 if (!bindings) | 12254 if (!bindings) |
14377 bindings = node.bindings_ = {}; | 12255 bindings = node.bindings_ = {}; |
14378 | 12256 |
14379 if (bindings[name]) | 12257 if (bindings[name]) |
14380 binding[name].close(); | 12258 binding[name].close(); |
14381 | 12259 |
14382 return bindings[name] = binding; | 12260 return bindings[name] = binding; |
14383 } | 12261 } |
(...skipping 564 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
14948 doc.templateContentsOwner_ = d; | 12826 doc.templateContentsOwner_ = d; |
14949 } | 12827 } |
14950 return d; | 12828 return d; |
14951 } | 12829 } |
14952 | 12830 |
14953 function getTemplateStagingDocument(template) { | 12831 function getTemplateStagingDocument(template) { |
14954 if (!template.stagingDocument_) { | 12832 if (!template.stagingDocument_) { |
14955 var owner = template.ownerDocument; | 12833 var owner = template.ownerDocument; |
14956 if (!owner.stagingDocument_) { | 12834 if (!owner.stagingDocument_) { |
14957 owner.stagingDocument_ = owner.implementation.createHTMLDocument(''); | 12835 owner.stagingDocument_ = owner.implementation.createHTMLDocument(''); |
14958 | 12836 owner.stagingDocument_.isStagingDocument = true; |
14959 // TODO(rafaelw): Remove when fix for | 12837 // TODO(rafaelw): Remove when fix for |
14960 // https://codereview.chromium.org/164803002/ | 12838 // https://codereview.chromium.org/164803002/ |
14961 // makes it to Chrome release. | 12839 // makes it to Chrome release. |
14962 var base = owner.stagingDocument_.createElement('base'); | 12840 var base = owner.stagingDocument_.createElement('base'); |
14963 base.href = document.baseURI; | 12841 base.href = document.baseURI; |
14964 owner.stagingDocument_.head.appendChild(base); | 12842 owner.stagingDocument_.head.appendChild(base); |
14965 | 12843 |
14966 owner.stagingDocument_.stagingDocument_ = owner.stagingDocument_; | 12844 owner.stagingDocument_.stagingDocument_ = owner.stagingDocument_; |
14967 } | 12845 } |
14968 | 12846 |
(...skipping 222 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
15191 templateObserver.observe(this, { attributes: true, | 13069 templateObserver.observe(this, { attributes: true, |
15192 attributeFilter: ['ref'] }); | 13070 attributeFilter: ['ref'] }); |
15193 } | 13071 } |
15194 | 13072 |
15195 return this.iterator_; | 13073 return this.iterator_; |
15196 }, | 13074 }, |
15197 | 13075 |
15198 createInstance: function(model, bindingDelegate, delegate_) { | 13076 createInstance: function(model, bindingDelegate, delegate_) { |
15199 if (bindingDelegate) | 13077 if (bindingDelegate) |
15200 delegate_ = this.newDelegate_(bindingDelegate); | 13078 delegate_ = this.newDelegate_(bindingDelegate); |
| 13079 else if (!delegate_) |
| 13080 delegate_ = this.delegate_; |
15201 | 13081 |
15202 if (!this.refContent_) | 13082 if (!this.refContent_) |
15203 this.refContent_ = this.ref_.content; | 13083 this.refContent_ = this.ref_.content; |
15204 var content = this.refContent_; | 13084 var content = this.refContent_; |
15205 if (content.firstChild === null) | 13085 if (content.firstChild === null) |
15206 return emptyInstance; | 13086 return emptyInstance; |
15207 | 13087 |
15208 var map = this.bindingMap_; | 13088 var map = getInstanceBindingMap(content, delegate_); |
15209 if (!map || map.content !== content) { | |
15210 // TODO(rafaelw): Setup a MutationObserver on content to detect | |
15211 // when the instanceMap is invalid. | |
15212 map = createInstanceBindingMap(content, | |
15213 delegate_ && delegate_.prepareBinding) || []; | |
15214 map.content = content; | |
15215 this.bindingMap_ = map; | |
15216 } | |
15217 | |
15218 var stagingDocument = getTemplateStagingDocument(this); | 13089 var stagingDocument = getTemplateStagingDocument(this); |
15219 var instance = stagingDocument.createDocumentFragment(); | 13090 var instance = stagingDocument.createDocumentFragment(); |
15220 instance.templateCreator_ = this; | 13091 instance.templateCreator_ = this; |
15221 instance.protoContent_ = content; | 13092 instance.protoContent_ = content; |
15222 instance.bindings_ = []; | 13093 instance.bindings_ = []; |
15223 instance.terminator_ = null; | 13094 instance.terminator_ = null; |
15224 var instanceRecord = instance.templateInstance_ = { | 13095 var instanceRecord = instance.templateInstance_ = { |
15225 firstNode: null, | 13096 firstNode: null, |
15226 lastNode: null, | 13097 lastNode: null, |
15227 model: model | 13098 model: model |
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
15293 this.delegate_ = delegate; | 13164 this.delegate_ = delegate; |
15294 this.bindingMap_ = undefined; | 13165 this.bindingMap_ = undefined; |
15295 if (this.iterator_) { | 13166 if (this.iterator_) { |
15296 this.iterator_.instancePositionChangedFn_ = undefined; | 13167 this.iterator_.instancePositionChangedFn_ = undefined; |
15297 this.iterator_.instanceModelFn_ = undefined; | 13168 this.iterator_.instanceModelFn_ = undefined; |
15298 } | 13169 } |
15299 }, | 13170 }, |
15300 | 13171 |
15301 newDelegate_: function(bindingDelegate) { | 13172 newDelegate_: function(bindingDelegate) { |
15302 if (!bindingDelegate) | 13173 if (!bindingDelegate) |
15303 return {}; | 13174 return; |
15304 | 13175 |
15305 function delegateFn(name) { | 13176 function delegateFn(name) { |
15306 var fn = bindingDelegate && bindingDelegate[name]; | 13177 var fn = bindingDelegate && bindingDelegate[name]; |
15307 if (typeof fn != 'function') | 13178 if (typeof fn != 'function') |
15308 return; | 13179 return; |
15309 | 13180 |
15310 return function() { | 13181 return function() { |
15311 return fn.apply(bindingDelegate, arguments); | 13182 return fn.apply(bindingDelegate, arguments); |
15312 }; | 13183 }; |
15313 } | 13184 } |
15314 | 13185 |
15315 return { | 13186 return { |
| 13187 bindingMaps: {}, |
15316 raw: bindingDelegate, | 13188 raw: bindingDelegate, |
15317 prepareBinding: delegateFn('prepareBinding'), | 13189 prepareBinding: delegateFn('prepareBinding'), |
15318 prepareInstanceModel: delegateFn('prepareInstanceModel'), | 13190 prepareInstanceModel: delegateFn('prepareInstanceModel'), |
15319 prepareInstancePositionChanged: | 13191 prepareInstancePositionChanged: |
15320 delegateFn('prepareInstancePositionChanged') | 13192 delegateFn('prepareInstancePositionChanged') |
15321 }; | 13193 }; |
15322 }, | 13194 }, |
15323 | 13195 |
15324 // TODO(rafaelw): Assigning .bindingDelegate always succeeds. It may | 13196 // TODO(rafaelw): Assigning .bindingDelegate always succeeds. It may |
15325 // make sense to issue a warning or even throw if the template is already | 13197 // make sense to issue a warning or even throw if the template is already |
(...skipping 158 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
15484 function processBindings(node, bindings, model, instanceBindings) { | 13356 function processBindings(node, bindings, model, instanceBindings) { |
15485 for (var i = 0; i < bindings.length; i += 2) { | 13357 for (var i = 0; i < bindings.length; i += 2) { |
15486 var name = bindings[i] | 13358 var name = bindings[i] |
15487 var tokens = bindings[i + 1]; | 13359 var tokens = bindings[i + 1]; |
15488 var value = processBinding(name, tokens, node, model); | 13360 var value = processBinding(name, tokens, node, model); |
15489 var binding = node.bind(name, value, tokens.onlyOneTime); | 13361 var binding = node.bind(name, value, tokens.onlyOneTime); |
15490 if (binding && instanceBindings) | 13362 if (binding && instanceBindings) |
15491 instanceBindings.push(binding); | 13363 instanceBindings.push(binding); |
15492 } | 13364 } |
15493 | 13365 |
| 13366 node.bindFinished(); |
15494 if (!bindings.isTemplate) | 13367 if (!bindings.isTemplate) |
15495 return; | 13368 return; |
15496 | 13369 |
15497 node.model_ = model; | 13370 node.model_ = model; |
15498 var iter = node.processBindingDirectives_(bindings); | 13371 var iter = node.processBindingDirectives_(bindings); |
15499 if (instanceBindings && iter) | 13372 if (instanceBindings && iter) |
15500 instanceBindings.push(iter); | 13373 instanceBindings.push(iter); |
15501 } | 13374 } |
15502 | 13375 |
15503 function parseWithDefault(el, name, prepareBindingFn) { | 13376 function parseWithDefault(el, name, prepareBindingFn) { |
(...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
15595 var map = getBindings(node, prepareBindingFn); | 13468 var map = getBindings(node, prepareBindingFn); |
15596 map.children = {}; | 13469 map.children = {}; |
15597 var index = 0; | 13470 var index = 0; |
15598 for (var child = node.firstChild; child; child = child.nextSibling) { | 13471 for (var child = node.firstChild; child; child = child.nextSibling) { |
15599 map.children[index++] = createInstanceBindingMap(child, prepareBindingFn); | 13472 map.children[index++] = createInstanceBindingMap(child, prepareBindingFn); |
15600 } | 13473 } |
15601 | 13474 |
15602 return map; | 13475 return map; |
15603 } | 13476 } |
15604 | 13477 |
| 13478 var contentUidCounter = 1; |
| 13479 |
| 13480 // TODO(rafaelw): Setup a MutationObserver on content which clears the id |
| 13481 // so that bindingMaps regenerate when the template.content changes. |
| 13482 function getContentUid(content) { |
| 13483 var id = content.id_; |
| 13484 if (!id) |
| 13485 id = content.id_ = contentUidCounter++; |
| 13486 return id; |
| 13487 } |
| 13488 |
| 13489 // Each delegate is associated with a set of bindingMaps, one for each |
| 13490 // content which may be used by a template. The intent is that each binding |
| 13491 // delegate gets the opportunity to prepare the instance (via the prepare* |
| 13492 // delegate calls) once across all uses. |
| 13493 // TODO(rafaelw): Separate out the parse map from the binding map. In the |
| 13494 // current implementation, if two delegates need a binding map for the same |
| 13495 // content, the second will have to reparse. |
| 13496 function getInstanceBindingMap(content, delegate_) { |
| 13497 var contentId = getContentUid(content); |
| 13498 if (delegate_) { |
| 13499 var map = delegate_.bindingMaps[contentId]; |
| 13500 if (!map) { |
| 13501 map = delegate_.bindingMaps[contentId] = |
| 13502 createInstanceBindingMap(content, delegate_.prepareBinding) || []; |
| 13503 } |
| 13504 return map; |
| 13505 } |
| 13506 |
| 13507 var map = content.bindingMap_; |
| 13508 if (!map) { |
| 13509 map = content.bindingMap_ = |
| 13510 createInstanceBindingMap(content, undefined) || []; |
| 13511 } |
| 13512 return map; |
| 13513 } |
| 13514 |
15605 Object.defineProperty(Node.prototype, 'templateInstance', { | 13515 Object.defineProperty(Node.prototype, 'templateInstance', { |
15606 get: function() { | 13516 get: function() { |
15607 var instance = this.templateInstance_; | 13517 var instance = this.templateInstance_; |
15608 return instance ? instance : | 13518 return instance ? instance : |
15609 (this.parentNode ? this.parentNode.templateInstance : undefined); | 13519 (this.parentNode ? this.parentNode.templateInstance : undefined); |
15610 } | 13520 } |
15611 }); | 13521 }); |
15612 | 13522 |
15613 var emptyInstance = document.createDocumentFragment(); | 13523 var emptyInstance = document.createDocumentFragment(); |
15614 emptyInstance.bindings_ = []; | 13524 emptyInstance.bindings_ = []; |
(...skipping 295 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
15910 this.templateElement_.iterator_ = undefined; | 13820 this.templateElement_.iterator_ = undefined; |
15911 this.closed = true; | 13821 this.closed = true; |
15912 } | 13822 } |
15913 }; | 13823 }; |
15914 | 13824 |
15915 // Polyfill-specific API. | 13825 // Polyfill-specific API. |
15916 HTMLTemplateElement.forAllTemplatesFrom_ = forAllTemplatesFrom; | 13826 HTMLTemplateElement.forAllTemplatesFrom_ = forAllTemplatesFrom; |
15917 })(this); | 13827 })(this); |
15918 | 13828 |
15919 /* | 13829 /* |
15920 Copyright (C) 2013 Ariya Hidayat <ariya.hidayat@gmail.com> | 13830 * Copyright (c) 2014 The Polymer Project Authors. All rights reserved. |
15921 Copyright (C) 2013 Thaddee Tyl <thaddee.tyl@gmail.com> | 13831 * This code may only be used under the BSD style license found at http://polyme
r.github.io/LICENSE.txt |
15922 Copyright (C) 2012 Ariya Hidayat <ariya.hidayat@gmail.com> | 13832 * The complete set of authors may be found at http://polymer.github.io/AUTHORS.
txt |
15923 Copyright (C) 2012 Mathias Bynens <mathias@qiwi.be> | 13833 * The complete set of contributors may be found at http://polymer.github.io/CON
TRIBUTORS.txt |
15924 Copyright (C) 2012 Joost-Wim Boekesteijn <joost-wim@boekesteijn.nl> | 13834 * Code distributed by Google as part of the polymer project is also |
15925 Copyright (C) 2012 Kris Kowal <kris.kowal@cixar.com> | 13835 * subject to an additional IP rights grant found at http://polymer.github.io/PA
TENTS.txt |
15926 Copyright (C) 2012 Yusuke Suzuki <utatane.tea@gmail.com> | 13836 */ |
15927 Copyright (C) 2012 Arpad Borsos <arpad.borsos@googlemail.com> | |
15928 Copyright (C) 2011 Ariya Hidayat <ariya.hidayat@gmail.com> | |
15929 | 13837 |
15930 Redistribution and use in source and binary forms, with or without | |
15931 modification, are permitted provided that the following conditions are met: | |
15932 | |
15933 * Redistributions of source code must retain the above copyright | |
15934 notice, this list of conditions and the following disclaimer. | |
15935 * Redistributions in binary form must reproduce the above copyright | |
15936 notice, this list of conditions and the following disclaimer in the | |
15937 documentation and/or other materials provided with the distribution. | |
15938 | |
15939 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | |
15940 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
15941 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
15942 ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY | |
15943 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |
15944 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |
15945 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |
15946 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
15947 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | |
15948 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
15949 */ | |
15950 | |
15951 (function (global) { | |
15952 'use strict'; | |
15953 | |
15954 var Token, | |
15955 TokenName, | |
15956 Syntax, | |
15957 Messages, | |
15958 source, | |
15959 index, | |
15960 length, | |
15961 delegate, | |
15962 lookahead, | |
15963 state; | |
15964 | |
15965 Token = { | |
15966 BooleanLiteral: 1, | |
15967 EOF: 2, | |
15968 Identifier: 3, | |
15969 Keyword: 4, | |
15970 NullLiteral: 5, | |
15971 NumericLiteral: 6, | |
15972 Punctuator: 7, | |
15973 StringLiteral: 8 | |
15974 }; | |
15975 | |
15976 TokenName = {}; | |
15977 TokenName[Token.BooleanLiteral] = 'Boolean'; | |
15978 TokenName[Token.EOF] = '<end>'; | |
15979 TokenName[Token.Identifier] = 'Identifier'; | |
15980 TokenName[Token.Keyword] = 'Keyword'; | |
15981 TokenName[Token.NullLiteral] = 'Null'; | |
15982 TokenName[Token.NumericLiteral] = 'Numeric'; | |
15983 TokenName[Token.Punctuator] = 'Punctuator'; | |
15984 TokenName[Token.StringLiteral] = 'String'; | |
15985 | |
15986 Syntax = { | |
15987 ArrayExpression: 'ArrayExpression', | |
15988 BinaryExpression: 'BinaryExpression', | |
15989 CallExpression: 'CallExpression', | |
15990 ConditionalExpression: 'ConditionalExpression', | |
15991 EmptyStatement: 'EmptyStatement', | |
15992 ExpressionStatement: 'ExpressionStatement', | |
15993 Identifier: 'Identifier', | |
15994 Literal: 'Literal', | |
15995 LabeledStatement: 'LabeledStatement', | |
15996 LogicalExpression: 'LogicalExpression', | |
15997 MemberExpression: 'MemberExpression', | |
15998 ObjectExpression: 'ObjectExpression', | |
15999 Program: 'Program', | |
16000 Property: 'Property', | |
16001 ThisExpression: 'ThisExpression', | |
16002 UnaryExpression: 'UnaryExpression' | |
16003 }; | |
16004 | |
16005 // Error messages should be identical to V8. | |
16006 Messages = { | |
16007 UnexpectedToken: 'Unexpected token %0', | |
16008 UnknownLabel: 'Undefined label \'%0\'', | |
16009 Redeclaration: '%0 \'%1\' has already been declared' | |
16010 }; | |
16011 | |
16012 // Ensure the condition is true, otherwise throw an error. | |
16013 // This is only to have a better contract semantic, i.e. another safety net | |
16014 // to catch a logic error. The condition shall be fulfilled in normal case. | |
16015 // Do NOT use this to enforce a certain condition on any user input. | |
16016 | |
16017 function assert(condition, message) { | |
16018 if (!condition) { | |
16019 throw new Error('ASSERT: ' + message); | |
16020 } | |
16021 } | |
16022 | |
16023 function isDecimalDigit(ch) { | |
16024 return (ch >= 48 && ch <= 57); // 0..9 | |
16025 } | |
16026 | |
16027 | |
16028 // 7.2 White Space | |
16029 | |
16030 function isWhiteSpace(ch) { | |
16031 return (ch === 32) || // space | |
16032 (ch === 9) || // tab | |
16033 (ch === 0xB) || | |
16034 (ch === 0xC) || | |
16035 (ch === 0xA0) || | |
16036 (ch >= 0x1680 && '\u1680\u180E\u2000\u2001\u2002\u2003\u2004\u2005\u
2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000\uFEFF'.indexOf(String.fromCharCod
e(ch)) > 0); | |
16037 } | |
16038 | |
16039 // 7.3 Line Terminators | |
16040 | |
16041 function isLineTerminator(ch) { | |
16042 return (ch === 10) || (ch === 13) || (ch === 0x2028) || (ch === 0x2029); | |
16043 } | |
16044 | |
16045 // 7.6 Identifier Names and Identifiers | |
16046 | |
16047 function isIdentifierStart(ch) { | |
16048 return (ch === 36) || (ch === 95) || // $ (dollar) and _ (underscore) | |
16049 (ch >= 65 && ch <= 90) || // A..Z | |
16050 (ch >= 97 && ch <= 122); // a..z | |
16051 } | |
16052 | |
16053 function isIdentifierPart(ch) { | |
16054 return (ch === 36) || (ch === 95) || // $ (dollar) and _ (underscore) | |
16055 (ch >= 65 && ch <= 90) || // A..Z | |
16056 (ch >= 97 && ch <= 122) || // a..z | |
16057 (ch >= 48 && ch <= 57); // 0..9 | |
16058 } | |
16059 | |
16060 // 7.6.1.1 Keywords | |
16061 | |
16062 function isKeyword(id) { | |
16063 return (id === 'this') | |
16064 } | |
16065 | |
16066 // 7.4 Comments | |
16067 | |
16068 function skipWhitespace() { | |
16069 while (index < length && isWhiteSpace(source.charCodeAt(index))) { | |
16070 ++index; | |
16071 } | |
16072 } | |
16073 | |
16074 function getIdentifier() { | |
16075 var start, ch; | |
16076 | |
16077 start = index++; | |
16078 while (index < length) { | |
16079 ch = source.charCodeAt(index); | |
16080 if (isIdentifierPart(ch)) { | |
16081 ++index; | |
16082 } else { | |
16083 break; | |
16084 } | |
16085 } | |
16086 | |
16087 return source.slice(start, index); | |
16088 } | |
16089 | |
16090 function scanIdentifier() { | |
16091 var start, id, type; | |
16092 | |
16093 start = index; | |
16094 | |
16095 id = getIdentifier(); | |
16096 | |
16097 // There is no keyword or literal with only one character. | |
16098 // Thus, it must be an identifier. | |
16099 if (id.length === 1) { | |
16100 type = Token.Identifier; | |
16101 } else if (isKeyword(id)) { | |
16102 type = Token.Keyword; | |
16103 } else if (id === 'null') { | |
16104 type = Token.NullLiteral; | |
16105 } else if (id === 'true' || id === 'false') { | |
16106 type = Token.BooleanLiteral; | |
16107 } else { | |
16108 type = Token.Identifier; | |
16109 } | |
16110 | |
16111 return { | |
16112 type: type, | |
16113 value: id, | |
16114 range: [start, index] | |
16115 }; | |
16116 } | |
16117 | |
16118 | |
16119 // 7.7 Punctuators | |
16120 | |
16121 function scanPunctuator() { | |
16122 var start = index, | |
16123 code = source.charCodeAt(index), | |
16124 code2, | |
16125 ch1 = source[index], | |
16126 ch2; | |
16127 | |
16128 switch (code) { | |
16129 | |
16130 // Check for most common single-character punctuators. | |
16131 case 46: // . dot | |
16132 case 40: // ( open bracket | |
16133 case 41: // ) close bracket | |
16134 case 59: // ; semicolon | |
16135 case 44: // , comma | |
16136 case 123: // { open curly brace | |
16137 case 125: // } close curly brace | |
16138 case 91: // [ | |
16139 case 93: // ] | |
16140 case 58: // : | |
16141 case 63: // ? | |
16142 ++index; | |
16143 return { | |
16144 type: Token.Punctuator, | |
16145 value: String.fromCharCode(code), | |
16146 range: [start, index] | |
16147 }; | |
16148 | |
16149 default: | |
16150 code2 = source.charCodeAt(index + 1); | |
16151 | |
16152 // '=' (char #61) marks an assignment or comparison operator. | |
16153 if (code2 === 61) { | |
16154 switch (code) { | |
16155 case 37: // % | |
16156 case 38: // & | |
16157 case 42: // *: | |
16158 case 43: // + | |
16159 case 45: // - | |
16160 case 47: // / | |
16161 case 60: // < | |
16162 case 62: // > | |
16163 case 124: // | | |
16164 index += 2; | |
16165 return { | |
16166 type: Token.Punctuator, | |
16167 value: String.fromCharCode(code) + String.fromCharCode(c
ode2), | |
16168 range: [start, index] | |
16169 }; | |
16170 | |
16171 case 33: // ! | |
16172 case 61: // = | |
16173 index += 2; | |
16174 | |
16175 // !== and === | |
16176 if (source.charCodeAt(index) === 61) { | |
16177 ++index; | |
16178 } | |
16179 return { | |
16180 type: Token.Punctuator, | |
16181 value: source.slice(start, index), | |
16182 range: [start, index] | |
16183 }; | |
16184 default: | |
16185 break; | |
16186 } | |
16187 } | |
16188 break; | |
16189 } | |
16190 | |
16191 // Peek more characters. | |
16192 | |
16193 ch2 = source[index + 1]; | |
16194 | |
16195 // Other 2-character punctuators: && || | |
16196 | |
16197 if (ch1 === ch2 && ('&|'.indexOf(ch1) >= 0)) { | |
16198 index += 2; | |
16199 return { | |
16200 type: Token.Punctuator, | |
16201 value: ch1 + ch2, | |
16202 range: [start, index] | |
16203 }; | |
16204 } | |
16205 | |
16206 if ('<>=!+-*%&|^/'.indexOf(ch1) >= 0) { | |
16207 ++index; | |
16208 return { | |
16209 type: Token.Punctuator, | |
16210 value: ch1, | |
16211 range: [start, index] | |
16212 }; | |
16213 } | |
16214 | |
16215 throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); | |
16216 } | |
16217 | |
16218 // 7.8.3 Numeric Literals | |
16219 function scanNumericLiteral() { | |
16220 var number, start, ch; | |
16221 | |
16222 ch = source[index]; | |
16223 assert(isDecimalDigit(ch.charCodeAt(0)) || (ch === '.'), | |
16224 'Numeric literal must start with a decimal digit or a decimal point'
); | |
16225 | |
16226 start = index; | |
16227 number = ''; | |
16228 if (ch !== '.') { | |
16229 number = source[index++]; | |
16230 ch = source[index]; | |
16231 | |
16232 // Hex number starts with '0x'. | |
16233 // Octal number starts with '0'. | |
16234 if (number === '0') { | |
16235 // decimal number starts with '0' such as '09' is illegal. | |
16236 if (ch && isDecimalDigit(ch.charCodeAt(0))) { | |
16237 throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); | |
16238 } | |
16239 } | |
16240 | |
16241 while (isDecimalDigit(source.charCodeAt(index))) { | |
16242 number += source[index++]; | |
16243 } | |
16244 ch = source[index]; | |
16245 } | |
16246 | |
16247 if (ch === '.') { | |
16248 number += source[index++]; | |
16249 while (isDecimalDigit(source.charCodeAt(index))) { | |
16250 number += source[index++]; | |
16251 } | |
16252 ch = source[index]; | |
16253 } | |
16254 | |
16255 if (ch === 'e' || ch === 'E') { | |
16256 number += source[index++]; | |
16257 | |
16258 ch = source[index]; | |
16259 if (ch === '+' || ch === '-') { | |
16260 number += source[index++]; | |
16261 } | |
16262 if (isDecimalDigit(source.charCodeAt(index))) { | |
16263 while (isDecimalDigit(source.charCodeAt(index))) { | |
16264 number += source[index++]; | |
16265 } | |
16266 } else { | |
16267 throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); | |
16268 } | |
16269 } | |
16270 | |
16271 if (isIdentifierStart(source.charCodeAt(index))) { | |
16272 throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); | |
16273 } | |
16274 | |
16275 return { | |
16276 type: Token.NumericLiteral, | |
16277 value: parseFloat(number), | |
16278 range: [start, index] | |
16279 }; | |
16280 } | |
16281 | |
16282 // 7.8.4 String Literals | |
16283 | |
16284 function scanStringLiteral() { | |
16285 var str = '', quote, start, ch, octal = false; | |
16286 | |
16287 quote = source[index]; | |
16288 assert((quote === '\'' || quote === '"'), | |
16289 'String literal must starts with a quote'); | |
16290 | |
16291 start = index; | |
16292 ++index; | |
16293 | |
16294 while (index < length) { | |
16295 ch = source[index++]; | |
16296 | |
16297 if (ch === quote) { | |
16298 quote = ''; | |
16299 break; | |
16300 } else if (ch === '\\') { | |
16301 ch = source[index++]; | |
16302 if (!ch || !isLineTerminator(ch.charCodeAt(0))) { | |
16303 switch (ch) { | |
16304 case 'n': | |
16305 str += '\n'; | |
16306 break; | |
16307 case 'r': | |
16308 str += '\r'; | |
16309 break; | |
16310 case 't': | |
16311 str += '\t'; | |
16312 break; | |
16313 case 'b': | |
16314 str += '\b'; | |
16315 break; | |
16316 case 'f': | |
16317 str += '\f'; | |
16318 break; | |
16319 case 'v': | |
16320 str += '\x0B'; | |
16321 break; | |
16322 | |
16323 default: | |
16324 str += ch; | |
16325 break; | |
16326 } | |
16327 } else { | |
16328 if (ch === '\r' && source[index] === '\n') { | |
16329 ++index; | |
16330 } | |
16331 } | |
16332 } else if (isLineTerminator(ch.charCodeAt(0))) { | |
16333 break; | |
16334 } else { | |
16335 str += ch; | |
16336 } | |
16337 } | |
16338 | |
16339 if (quote !== '') { | |
16340 throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); | |
16341 } | |
16342 | |
16343 return { | |
16344 type: Token.StringLiteral, | |
16345 value: str, | |
16346 octal: octal, | |
16347 range: [start, index] | |
16348 }; | |
16349 } | |
16350 | |
16351 function isIdentifierName(token) { | |
16352 return token.type === Token.Identifier || | |
16353 token.type === Token.Keyword || | |
16354 token.type === Token.BooleanLiteral || | |
16355 token.type === Token.NullLiteral; | |
16356 } | |
16357 | |
16358 function advance() { | |
16359 var ch; | |
16360 | |
16361 skipWhitespace(); | |
16362 | |
16363 if (index >= length) { | |
16364 return { | |
16365 type: Token.EOF, | |
16366 range: [index, index] | |
16367 }; | |
16368 } | |
16369 | |
16370 ch = source.charCodeAt(index); | |
16371 | |
16372 // Very common: ( and ) and ; | |
16373 if (ch === 40 || ch === 41 || ch === 58) { | |
16374 return scanPunctuator(); | |
16375 } | |
16376 | |
16377 // String literal starts with single quote (#39) or double quote (#34). | |
16378 if (ch === 39 || ch === 34) { | |
16379 return scanStringLiteral(); | |
16380 } | |
16381 | |
16382 if (isIdentifierStart(ch)) { | |
16383 return scanIdentifier(); | |
16384 } | |
16385 | |
16386 // Dot (.) char #46 can also start a floating-point number, hence the ne
ed | |
16387 // to check the next character. | |
16388 if (ch === 46) { | |
16389 if (isDecimalDigit(source.charCodeAt(index + 1))) { | |
16390 return scanNumericLiteral(); | |
16391 } | |
16392 return scanPunctuator(); | |
16393 } | |
16394 | |
16395 if (isDecimalDigit(ch)) { | |
16396 return scanNumericLiteral(); | |
16397 } | |
16398 | |
16399 return scanPunctuator(); | |
16400 } | |
16401 | |
16402 function lex() { | |
16403 var token; | |
16404 | |
16405 token = lookahead; | |
16406 index = token.range[1]; | |
16407 | |
16408 lookahead = advance(); | |
16409 | |
16410 index = token.range[1]; | |
16411 | |
16412 return token; | |
16413 } | |
16414 | |
16415 function peek() { | |
16416 var pos; | |
16417 | |
16418 pos = index; | |
16419 lookahead = advance(); | |
16420 index = pos; | |
16421 } | |
16422 | |
16423 // Throw an exception | |
16424 | |
16425 function throwError(token, messageFormat) { | |
16426 var error, | |
16427 args = Array.prototype.slice.call(arguments, 2), | |
16428 msg = messageFormat.replace( | |
16429 /%(\d)/g, | |
16430 function (whole, index) { | |
16431 assert(index < args.length, 'Message reference must be in ra
nge'); | |
16432 return args[index]; | |
16433 } | |
16434 ); | |
16435 | |
16436 error = new Error(msg); | |
16437 error.index = index; | |
16438 error.description = msg; | |
16439 throw error; | |
16440 } | |
16441 | |
16442 // Throw an exception because of the token. | |
16443 | |
16444 function throwUnexpected(token) { | |
16445 throwError(token, Messages.UnexpectedToken, token.value); | |
16446 } | |
16447 | |
16448 // Expect the next token to match the specified punctuator. | |
16449 // If not, an exception will be thrown. | |
16450 | |
16451 function expect(value) { | |
16452 var token = lex(); | |
16453 if (token.type !== Token.Punctuator || token.value !== value) { | |
16454 throwUnexpected(token); | |
16455 } | |
16456 } | |
16457 | |
16458 // Return true if the next token matches the specified punctuator. | |
16459 | |
16460 function match(value) { | |
16461 return lookahead.type === Token.Punctuator && lookahead.value === value; | |
16462 } | |
16463 | |
16464 // Return true if the next token matches the specified keyword | |
16465 | |
16466 function matchKeyword(keyword) { | |
16467 return lookahead.type === Token.Keyword && lookahead.value === keyword; | |
16468 } | |
16469 | |
16470 function consumeSemicolon() { | |
16471 // Catch the very common case first: immediately a semicolon (char #59). | |
16472 if (source.charCodeAt(index) === 59) { | |
16473 lex(); | |
16474 return; | |
16475 } | |
16476 | |
16477 skipWhitespace(); | |
16478 | |
16479 if (match(';')) { | |
16480 lex(); | |
16481 return; | |
16482 } | |
16483 | |
16484 if (lookahead.type !== Token.EOF && !match('}')) { | |
16485 throwUnexpected(lookahead); | |
16486 } | |
16487 } | |
16488 | |
16489 // 11.1.4 Array Initialiser | |
16490 | |
16491 function parseArrayInitialiser() { | |
16492 var elements = []; | |
16493 | |
16494 expect('['); | |
16495 | |
16496 while (!match(']')) { | |
16497 if (match(',')) { | |
16498 lex(); | |
16499 elements.push(null); | |
16500 } else { | |
16501 elements.push(parseExpression()); | |
16502 | |
16503 if (!match(']')) { | |
16504 expect(','); | |
16505 } | |
16506 } | |
16507 } | |
16508 | |
16509 expect(']'); | |
16510 | |
16511 return delegate.createArrayExpression(elements); | |
16512 } | |
16513 | |
16514 // 11.1.5 Object Initialiser | |
16515 | |
16516 function parseObjectPropertyKey() { | |
16517 var token; | |
16518 | |
16519 skipWhitespace(); | |
16520 token = lex(); | |
16521 | |
16522 // Note: This function is called only from parseObjectProperty(), where | |
16523 // EOF and Punctuator tokens are already filtered out. | |
16524 if (token.type === Token.StringLiteral || token.type === Token.NumericLi
teral) { | |
16525 return delegate.createLiteral(token); | |
16526 } | |
16527 | |
16528 return delegate.createIdentifier(token.value); | |
16529 } | |
16530 | |
16531 function parseObjectProperty() { | |
16532 var token, key; | |
16533 | |
16534 token = lookahead; | |
16535 skipWhitespace(); | |
16536 | |
16537 if (token.type === Token.EOF || token.type === Token.Punctuator) { | |
16538 throwUnexpected(token); | |
16539 } | |
16540 | |
16541 key = parseObjectPropertyKey(); | |
16542 expect(':'); | |
16543 return delegate.createProperty('init', key, parseExpression()); | |
16544 } | |
16545 | |
16546 function parseObjectInitialiser() { | |
16547 var properties = []; | |
16548 | |
16549 expect('{'); | |
16550 | |
16551 while (!match('}')) { | |
16552 properties.push(parseObjectProperty()); | |
16553 | |
16554 if (!match('}')) { | |
16555 expect(','); | |
16556 } | |
16557 } | |
16558 | |
16559 expect('}'); | |
16560 | |
16561 return delegate.createObjectExpression(properties); | |
16562 } | |
16563 | |
16564 // 11.1.6 The Grouping Operator | |
16565 | |
16566 function parseGroupExpression() { | |
16567 var expr; | |
16568 | |
16569 expect('('); | |
16570 | |
16571 expr = parseExpression(); | |
16572 | |
16573 expect(')'); | |
16574 | |
16575 return expr; | |
16576 } | |
16577 | |
16578 | |
16579 // 11.1 Primary Expressions | |
16580 | |
16581 function parsePrimaryExpression() { | |
16582 var type, token, expr; | |
16583 | |
16584 if (match('(')) { | |
16585 return parseGroupExpression(); | |
16586 } | |
16587 | |
16588 type = lookahead.type; | |
16589 | |
16590 if (type === Token.Identifier) { | |
16591 expr = delegate.createIdentifier(lex().value); | |
16592 } else if (type === Token.StringLiteral || type === Token.NumericLiteral
) { | |
16593 expr = delegate.createLiteral(lex()); | |
16594 } else if (type === Token.Keyword) { | |
16595 if (matchKeyword('this')) { | |
16596 lex(); | |
16597 expr = delegate.createThisExpression(); | |
16598 } | |
16599 } else if (type === Token.BooleanLiteral) { | |
16600 token = lex(); | |
16601 token.value = (token.value === 'true'); | |
16602 expr = delegate.createLiteral(token); | |
16603 } else if (type === Token.NullLiteral) { | |
16604 token = lex(); | |
16605 token.value = null; | |
16606 expr = delegate.createLiteral(token); | |
16607 } else if (match('[')) { | |
16608 expr = parseArrayInitialiser(); | |
16609 } else if (match('{')) { | |
16610 expr = parseObjectInitialiser(); | |
16611 } | |
16612 | |
16613 if (expr) { | |
16614 return expr; | |
16615 } | |
16616 | |
16617 throwUnexpected(lex()); | |
16618 } | |
16619 | |
16620 // 11.2 Left-Hand-Side Expressions | |
16621 | |
16622 function parseArguments() { | |
16623 var args = []; | |
16624 | |
16625 expect('('); | |
16626 | |
16627 if (!match(')')) { | |
16628 while (index < length) { | |
16629 args.push(parseExpression()); | |
16630 if (match(')')) { | |
16631 break; | |
16632 } | |
16633 expect(','); | |
16634 } | |
16635 } | |
16636 | |
16637 expect(')'); | |
16638 | |
16639 return args; | |
16640 } | |
16641 | |
16642 function parseNonComputedProperty() { | |
16643 var token; | |
16644 | |
16645 token = lex(); | |
16646 | |
16647 if (!isIdentifierName(token)) { | |
16648 throwUnexpected(token); | |
16649 } | |
16650 | |
16651 return delegate.createIdentifier(token.value); | |
16652 } | |
16653 | |
16654 function parseNonComputedMember() { | |
16655 expect('.'); | |
16656 | |
16657 return parseNonComputedProperty(); | |
16658 } | |
16659 | |
16660 function parseComputedMember() { | |
16661 var expr; | |
16662 | |
16663 expect('['); | |
16664 | |
16665 expr = parseExpression(); | |
16666 | |
16667 expect(']'); | |
16668 | |
16669 return expr; | |
16670 } | |
16671 | |
16672 function parseLeftHandSideExpression() { | |
16673 var expr, property; | |
16674 | |
16675 expr = parsePrimaryExpression(); | |
16676 | |
16677 while (match('.') || match('[')) { | |
16678 if (match('[')) { | |
16679 property = parseComputedMember(); | |
16680 expr = delegate.createMemberExpression('[', expr, property); | |
16681 } else { | |
16682 property = parseNonComputedMember(); | |
16683 expr = delegate.createMemberExpression('.', expr, property); | |
16684 } | |
16685 } | |
16686 | |
16687 return expr; | |
16688 } | |
16689 | |
16690 // 11.3 Postfix Expressions | |
16691 | |
16692 var parsePostfixExpression = parseLeftHandSideExpression; | |
16693 | |
16694 // 11.4 Unary Operators | |
16695 | |
16696 function parseUnaryExpression() { | |
16697 var token, expr; | |
16698 | |
16699 if (lookahead.type !== Token.Punctuator && lookahead.type !== Token.Keyw
ord) { | |
16700 expr = parsePostfixExpression(); | |
16701 } else if (match('+') || match('-') || match('!')) { | |
16702 token = lex(); | |
16703 expr = parseUnaryExpression(); | |
16704 expr = delegate.createUnaryExpression(token.value, expr); | |
16705 } else if (matchKeyword('delete') || matchKeyword('void') || matchKeywor
d('typeof')) { | |
16706 throwError({}, Messages.UnexpectedToken); | |
16707 } else { | |
16708 expr = parsePostfixExpression(); | |
16709 } | |
16710 | |
16711 return expr; | |
16712 } | |
16713 | |
16714 function binaryPrecedence(token) { | |
16715 var prec = 0; | |
16716 | |
16717 if (token.type !== Token.Punctuator && token.type !== Token.Keyword) { | |
16718 return 0; | |
16719 } | |
16720 | |
16721 switch (token.value) { | |
16722 case '||': | |
16723 prec = 1; | |
16724 break; | |
16725 | |
16726 case '&&': | |
16727 prec = 2; | |
16728 break; | |
16729 | |
16730 case '==': | |
16731 case '!=': | |
16732 case '===': | |
16733 case '!==': | |
16734 prec = 6; | |
16735 break; | |
16736 | |
16737 case '<': | |
16738 case '>': | |
16739 case '<=': | |
16740 case '>=': | |
16741 case 'instanceof': | |
16742 prec = 7; | |
16743 break; | |
16744 | |
16745 case 'in': | |
16746 prec = 7; | |
16747 break; | |
16748 | |
16749 case '+': | |
16750 case '-': | |
16751 prec = 9; | |
16752 break; | |
16753 | |
16754 case '*': | |
16755 case '/': | |
16756 case '%': | |
16757 prec = 11; | |
16758 break; | |
16759 | |
16760 default: | |
16761 break; | |
16762 } | |
16763 | |
16764 return prec; | |
16765 } | |
16766 | |
16767 // 11.5 Multiplicative Operators | |
16768 // 11.6 Additive Operators | |
16769 // 11.7 Bitwise Shift Operators | |
16770 // 11.8 Relational Operators | |
16771 // 11.9 Equality Operators | |
16772 // 11.10 Binary Bitwise Operators | |
16773 // 11.11 Binary Logical Operators | |
16774 | |
16775 function parseBinaryExpression() { | |
16776 var expr, token, prec, stack, right, operator, left, i; | |
16777 | |
16778 left = parseUnaryExpression(); | |
16779 | |
16780 token = lookahead; | |
16781 prec = binaryPrecedence(token); | |
16782 if (prec === 0) { | |
16783 return left; | |
16784 } | |
16785 token.prec = prec; | |
16786 lex(); | |
16787 | |
16788 right = parseUnaryExpression(); | |
16789 | |
16790 stack = [left, token, right]; | |
16791 | |
16792 while ((prec = binaryPrecedence(lookahead)) > 0) { | |
16793 | |
16794 // Reduce: make a binary expression from the three topmost entries. | |
16795 while ((stack.length > 2) && (prec <= stack[stack.length - 2].prec))
{ | |
16796 right = stack.pop(); | |
16797 operator = stack.pop().value; | |
16798 left = stack.pop(); | |
16799 expr = delegate.createBinaryExpression(operator, left, right); | |
16800 stack.push(expr); | |
16801 } | |
16802 | |
16803 // Shift. | |
16804 token = lex(); | |
16805 token.prec = prec; | |
16806 stack.push(token); | |
16807 expr = parseUnaryExpression(); | |
16808 stack.push(expr); | |
16809 } | |
16810 | |
16811 // Final reduce to clean-up the stack. | |
16812 i = stack.length - 1; | |
16813 expr = stack[i]; | |
16814 while (i > 1) { | |
16815 expr = delegate.createBinaryExpression(stack[i - 1].value, stack[i -
2], expr); | |
16816 i -= 2; | |
16817 } | |
16818 | |
16819 return expr; | |
16820 } | |
16821 | |
16822 | |
16823 // 11.12 Conditional Operator | |
16824 | |
16825 function parseConditionalExpression() { | |
16826 var expr, consequent, alternate; | |
16827 | |
16828 expr = parseBinaryExpression(); | |
16829 | |
16830 if (match('?')) { | |
16831 lex(); | |
16832 consequent = parseConditionalExpression(); | |
16833 expect(':'); | |
16834 alternate = parseConditionalExpression(); | |
16835 | |
16836 expr = delegate.createConditionalExpression(expr, consequent, altern
ate); | |
16837 } | |
16838 | |
16839 return expr; | |
16840 } | |
16841 | |
16842 // Simplification since we do not support AssignmentExpression. | |
16843 var parseExpression = parseConditionalExpression; | |
16844 | |
16845 // Polymer Syntax extensions | |
16846 | |
16847 // Filter :: | |
16848 // Identifier | |
16849 // Identifier "(" ")" | |
16850 // Identifier "(" FilterArguments ")" | |
16851 | |
16852 function parseFilter() { | |
16853 var identifier, args; | |
16854 | |
16855 identifier = lex(); | |
16856 | |
16857 if (identifier.type !== Token.Identifier) { | |
16858 throwUnexpected(identifier); | |
16859 } | |
16860 | |
16861 args = match('(') ? parseArguments() : []; | |
16862 | |
16863 return delegate.createFilter(identifier.value, args); | |
16864 } | |
16865 | |
16866 // Filters :: | |
16867 // "|" Filter | |
16868 // Filters "|" Filter | |
16869 | |
16870 function parseFilters() { | |
16871 while (match('|')) { | |
16872 lex(); | |
16873 parseFilter(); | |
16874 } | |
16875 } | |
16876 | |
16877 // TopLevel :: | |
16878 // LabelledExpressions | |
16879 // AsExpression | |
16880 // InExpression | |
16881 // FilterExpression | |
16882 | |
16883 // AsExpression :: | |
16884 // FilterExpression as Identifier | |
16885 | |
16886 // InExpression :: | |
16887 // Identifier, Identifier in FilterExpression | |
16888 // Identifier in FilterExpression | |
16889 | |
16890 // FilterExpression :: | |
16891 // Expression | |
16892 // Expression Filters | |
16893 | |
16894 function parseTopLevel() { | |
16895 skipWhitespace(); | |
16896 peek(); | |
16897 | |
16898 var expr = parseExpression(); | |
16899 if (expr) { | |
16900 if (lookahead.value === ',' || lookahead.value == 'in' && | |
16901 expr.type === Syntax.Identifier) { | |
16902 parseInExpression(expr); | |
16903 } else { | |
16904 parseFilters(); | |
16905 if (lookahead.value === 'as') { | |
16906 parseAsExpression(expr); | |
16907 } else { | |
16908 delegate.createTopLevel(expr); | |
16909 } | |
16910 } | |
16911 } | |
16912 | |
16913 if (lookahead.type !== Token.EOF) { | |
16914 throwUnexpected(lookahead); | |
16915 } | |
16916 } | |
16917 | |
16918 function parseAsExpression(expr) { | |
16919 lex(); // as | |
16920 var identifier = lex().value; | |
16921 delegate.createAsExpression(expr, identifier); | |
16922 } | |
16923 | |
16924 function parseInExpression(identifier) { | |
16925 var indexName; | |
16926 if (lookahead.value === ',') { | |
16927 lex(); | |
16928 if (lookahead.type !== Token.Identifier) | |
16929 throwUnexpected(lookahead); | |
16930 indexName = lex().value; | |
16931 } | |
16932 | |
16933 lex(); // in | |
16934 var expr = parseExpression(); | |
16935 parseFilters(); | |
16936 delegate.createInExpression(identifier.name, indexName, expr); | |
16937 } | |
16938 | |
16939 function parse(code, inDelegate) { | |
16940 delegate = inDelegate; | |
16941 source = code; | |
16942 index = 0; | |
16943 length = source.length; | |
16944 lookahead = null; | |
16945 state = { | |
16946 labelSet: {} | |
16947 }; | |
16948 | |
16949 return parseTopLevel(); | |
16950 } | |
16951 | |
16952 global.esprima = { | |
16953 parse: parse | |
16954 }; | |
16955 })(this); | |
16956 | |
16957 // Copyright (c) 2014 The Polymer Project Authors. All rights reserved. | |
16958 // This code may only be used under the BSD style license found at http://polyme
r.github.io/LICENSE.txt | |
16959 // The complete set of authors may be found at http://polymer.github.io/AUTHORS.
txt | |
16960 // The complete set of contributors may be found at http://polymer.github.io/CON
TRIBUTORS.txt | |
16961 // Code distributed by Google as part of the polymer project is also | |
16962 // subject to an additional IP rights grant found at http://polymer.github.io/PA
TENTS.txt | |
16963 | |
16964 (function (global) { | |
16965 'use strict'; | |
16966 | |
16967 // JScript does not have __proto__. We wrap all object literals with | |
16968 // createObject which uses Object.create, Object.defineProperty and | |
16969 // Object.getOwnPropertyDescriptor to create a new object that does the exact | |
16970 // same thing. The main downside to this solution is that we have to extract | |
16971 // all those property descriptors for IE. | |
16972 var createObject = ('__proto__' in {}) ? | |
16973 function(obj) { return obj; } : | |
16974 function(obj) { | |
16975 var proto = obj.__proto__; | |
16976 if (!proto) | |
16977 return obj; | |
16978 var newObject = Object.create(proto); | |
16979 Object.getOwnPropertyNames(obj).forEach(function(name) { | |
16980 Object.defineProperty(newObject, name, | |
16981 Object.getOwnPropertyDescriptor(obj, name)); | |
16982 }); | |
16983 return newObject; | |
16984 }; | |
16985 | |
16986 function prepareBinding(expressionText, name, node, filterRegistry) { | |
16987 var expression; | |
16988 try { | |
16989 expression = getExpression(expressionText); | |
16990 if (expression.scopeIdent && | |
16991 (node.nodeType !== Node.ELEMENT_NODE || | |
16992 node.tagName !== 'TEMPLATE' || | |
16993 (name !== 'bind' && name !== 'repeat'))) { | |
16994 throw Error('as and in can only be used within <template bind/repeat>'); | |
16995 } | |
16996 } catch (ex) { | |
16997 console.error('Invalid expression syntax: ' + expressionText, ex); | |
16998 return; | |
16999 } | |
17000 | |
17001 return function(model, node, oneTime) { | |
17002 var binding = expression.getBinding(model, filterRegistry, oneTime); | |
17003 if (expression.scopeIdent && binding) { | |
17004 node.polymerExpressionScopeIdent_ = expression.scopeIdent; | |
17005 if (expression.indexIdent) | |
17006 node.polymerExpressionIndexIdent_ = expression.indexIdent; | |
17007 } | |
17008 | |
17009 return binding; | |
17010 } | |
17011 } | |
17012 | |
17013 // TODO(rafaelw): Implement simple LRU. | |
17014 var expressionParseCache = Object.create(null); | |
17015 | |
17016 function getExpression(expressionText) { | |
17017 var expression = expressionParseCache[expressionText]; | |
17018 if (!expression) { | |
17019 var delegate = new ASTDelegate(); | |
17020 esprima.parse(expressionText, delegate); | |
17021 expression = new Expression(delegate); | |
17022 expressionParseCache[expressionText] = expression; | |
17023 } | |
17024 return expression; | |
17025 } | |
17026 | |
17027 function Literal(value) { | |
17028 this.value = value; | |
17029 this.valueFn_ = undefined; | |
17030 } | |
17031 | |
17032 Literal.prototype = { | |
17033 valueFn: function() { | |
17034 if (!this.valueFn_) { | |
17035 var value = this.value; | |
17036 this.valueFn_ = function() { | |
17037 return value; | |
17038 } | |
17039 } | |
17040 | |
17041 return this.valueFn_; | |
17042 } | |
17043 } | |
17044 | |
17045 function IdentPath(name) { | |
17046 this.name = name; | |
17047 this.path = Path.get(name); | |
17048 } | |
17049 | |
17050 IdentPath.prototype = { | |
17051 valueFn: function() { | |
17052 if (!this.valueFn_) { | |
17053 var name = this.name; | |
17054 var path = this.path; | |
17055 this.valueFn_ = function(model, observer) { | |
17056 if (observer) | |
17057 observer.addPath(model, path); | |
17058 | |
17059 return path.getValueFrom(model); | |
17060 } | |
17061 } | |
17062 | |
17063 return this.valueFn_; | |
17064 }, | |
17065 | |
17066 setValue: function(model, newValue) { | |
17067 if (this.path.length == 1); | |
17068 model = findScope(model, this.path[0]); | |
17069 | |
17070 return this.path.setValueFrom(model, newValue); | |
17071 } | |
17072 }; | |
17073 | |
17074 function MemberExpression(object, property, accessor) { | |
17075 // convert literal computed property access where literal value is a value | |
17076 // path to ident dot-access. | |
17077 if (accessor == '[' && | |
17078 property instanceof Literal && | |
17079 Path.get(property.value).valid) { | |
17080 accessor = '.'; | |
17081 property = new IdentPath(property.value); | |
17082 } | |
17083 | |
17084 this.dynamicDeps = typeof object == 'function' || object.dynamic; | |
17085 | |
17086 this.dynamic = typeof property == 'function' || | |
17087 property.dynamic || | |
17088 accessor == '['; | |
17089 | |
17090 this.simplePath = | |
17091 !this.dynamic && | |
17092 !this.dynamicDeps && | |
17093 property instanceof IdentPath && | |
17094 (object instanceof MemberExpression || object instanceof IdentPath); | |
17095 | |
17096 this.object = this.simplePath ? object : getFn(object); | |
17097 this.property = accessor == '.' ? property : getFn(property); | |
17098 } | |
17099 | |
17100 MemberExpression.prototype = { | |
17101 get fullPath() { | |
17102 if (!this.fullPath_) { | |
17103 var last = this.object instanceof IdentPath ? | |
17104 this.object.name : this.object.fullPath; | |
17105 this.fullPath_ = Path.get(last + '.' + this.property.name); | |
17106 } | |
17107 | |
17108 return this.fullPath_; | |
17109 }, | |
17110 | |
17111 valueFn: function() { | |
17112 if (!this.valueFn_) { | |
17113 var object = this.object; | |
17114 | |
17115 if (this.simplePath) { | |
17116 var path = this.fullPath; | |
17117 | |
17118 this.valueFn_ = function(model, observer) { | |
17119 if (observer) | |
17120 observer.addPath(model, path); | |
17121 | |
17122 return path.getValueFrom(model); | |
17123 }; | |
17124 } else if (this.property instanceof IdentPath) { | |
17125 var path = Path.get(this.property.name); | |
17126 | |
17127 this.valueFn_ = function(model, observer) { | |
17128 var context = object(model, observer); | |
17129 | |
17130 if (observer) | |
17131 observer.addPath(context, path); | |
17132 | |
17133 return path.getValueFrom(context); | |
17134 } | |
17135 } else { | |
17136 // Computed property. | |
17137 var property = this.property; | |
17138 | |
17139 this.valueFn_ = function(model, observer) { | |
17140 var context = object(model, observer); | |
17141 var propName = property(model, observer); | |
17142 if (observer) | |
17143 observer.addPath(context, propName); | |
17144 | |
17145 return context ? context[propName] : undefined; | |
17146 }; | |
17147 } | |
17148 } | |
17149 return this.valueFn_; | |
17150 }, | |
17151 | |
17152 setValue: function(model, newValue) { | |
17153 if (this.simplePath) { | |
17154 this.fullPath.setValueFrom(model, newValue); | |
17155 return newValue; | |
17156 } | |
17157 | |
17158 var object = this.object(model); | |
17159 var propName = this.property instanceof IdentPath ? this.property.name : | |
17160 this.property(model); | |
17161 return object[propName] = newValue; | |
17162 } | |
17163 }; | |
17164 | |
17165 function Filter(name, args) { | |
17166 this.name = name; | |
17167 this.args = []; | |
17168 for (var i = 0; i < args.length; i++) { | |
17169 this.args[i] = getFn(args[i]); | |
17170 } | |
17171 } | |
17172 | |
17173 Filter.prototype = { | |
17174 transform: function(value, toModelDirection, filterRegistry, model, | |
17175 observer) { | |
17176 var fn = filterRegistry[this.name]; | |
17177 var context = model; | |
17178 if (fn) { | |
17179 context = undefined; | |
17180 } else { | |
17181 fn = context[this.name]; | |
17182 if (!fn) { | |
17183 console.error('Cannot find filter: ' + this.name); | |
17184 return; | |
17185 } | |
17186 } | |
17187 | |
17188 // If toModelDirection is falsey, then the "normal" (dom-bound) direction | |
17189 // is used. Otherwise, it looks for a 'toModel' property function on the | |
17190 // object. | |
17191 if (toModelDirection) { | |
17192 fn = fn.toModel; | |
17193 } else if (typeof fn.toDOM == 'function') { | |
17194 fn = fn.toDOM; | |
17195 } | |
17196 | |
17197 if (typeof fn != 'function') { | |
17198 console.error('No ' + (toModelDirection ? 'toModel' : 'toDOM') + | |
17199 ' found on' + this.name); | |
17200 return; | |
17201 } | |
17202 | |
17203 var args = [value]; | |
17204 for (var i = 0; i < this.args.length; i++) { | |
17205 args[i + 1] = getFn(this.args[i])(model, observer); | |
17206 } | |
17207 | |
17208 return fn.apply(context, args); | |
17209 } | |
17210 }; | |
17211 | |
17212 function notImplemented() { throw Error('Not Implemented'); } | |
17213 | |
17214 var unaryOperators = { | |
17215 '+': function(v) { return +v; }, | |
17216 '-': function(v) { return -v; }, | |
17217 '!': function(v) { return !v; } | |
17218 }; | |
17219 | |
17220 var binaryOperators = { | |
17221 '+': function(l, r) { return l+r; }, | |
17222 '-': function(l, r) { return l-r; }, | |
17223 '*': function(l, r) { return l*r; }, | |
17224 '/': function(l, r) { return l/r; }, | |
17225 '%': function(l, r) { return l%r; }, | |
17226 '<': function(l, r) { return l<r; }, | |
17227 '>': function(l, r) { return l>r; }, | |
17228 '<=': function(l, r) { return l<=r; }, | |
17229 '>=': function(l, r) { return l>=r; }, | |
17230 '==': function(l, r) { return l==r; }, | |
17231 '!=': function(l, r) { return l!=r; }, | |
17232 '===': function(l, r) { return l===r; }, | |
17233 '!==': function(l, r) { return l!==r; }, | |
17234 '&&': function(l, r) { return l&&r; }, | |
17235 '||': function(l, r) { return l||r; }, | |
17236 }; | |
17237 | |
17238 function getFn(arg) { | |
17239 return typeof arg == 'function' ? arg : arg.valueFn(); | |
17240 } | |
17241 | |
17242 function ASTDelegate() { | |
17243 this.expression = null; | |
17244 this.filters = []; | |
17245 this.deps = {}; | |
17246 this.currentPath = undefined; | |
17247 this.scopeIdent = undefined; | |
17248 this.indexIdent = undefined; | |
17249 this.dynamicDeps = false; | |
17250 } | |
17251 | |
17252 ASTDelegate.prototype = { | |
17253 createUnaryExpression: function(op, argument) { | |
17254 if (!unaryOperators[op]) | |
17255 throw Error('Disallowed operator: ' + op); | |
17256 | |
17257 argument = getFn(argument); | |
17258 | |
17259 return function(model, observer) { | |
17260 return unaryOperators[op](argument(model, observer)); | |
17261 }; | |
17262 }, | |
17263 | |
17264 createBinaryExpression: function(op, left, right) { | |
17265 if (!binaryOperators[op]) | |
17266 throw Error('Disallowed operator: ' + op); | |
17267 | |
17268 left = getFn(left); | |
17269 right = getFn(right); | |
17270 | |
17271 return function(model, observer) { | |
17272 return binaryOperators[op](left(model, observer), | |
17273 right(model, observer)); | |
17274 }; | |
17275 }, | |
17276 | |
17277 createConditionalExpression: function(test, consequent, alternate) { | |
17278 test = getFn(test); | |
17279 consequent = getFn(consequent); | |
17280 alternate = getFn(alternate); | |
17281 | |
17282 return function(model, observer) { | |
17283 return test(model, observer) ? | |
17284 consequent(model, observer) : alternate(model, observer); | |
17285 } | |
17286 }, | |
17287 | |
17288 createIdentifier: function(name) { | |
17289 var ident = new IdentPath(name); | |
17290 ident.type = 'Identifier'; | |
17291 return ident; | |
17292 }, | |
17293 | |
17294 createMemberExpression: function(accessor, object, property) { | |
17295 var ex = new MemberExpression(object, property, accessor); | |
17296 if (ex.dynamicDeps) | |
17297 this.dynamicDeps = true; | |
17298 return ex; | |
17299 }, | |
17300 | |
17301 createLiteral: function(token) { | |
17302 return new Literal(token.value); | |
17303 }, | |
17304 | |
17305 createArrayExpression: function(elements) { | |
17306 for (var i = 0; i < elements.length; i++) | |
17307 elements[i] = getFn(elements[i]); | |
17308 | |
17309 return function(model, observer) { | |
17310 var arr = [] | |
17311 for (var i = 0; i < elements.length; i++) | |
17312 arr.push(elements[i](model, observer)); | |
17313 return arr; | |
17314 } | |
17315 }, | |
17316 | |
17317 createProperty: function(kind, key, value) { | |
17318 return { | |
17319 key: key instanceof IdentPath ? key.name : key.value, | |
17320 value: value | |
17321 }; | |
17322 }, | |
17323 | |
17324 createObjectExpression: function(properties) { | |
17325 for (var i = 0; i < properties.length; i++) | |
17326 properties[i].value = getFn(properties[i].value); | |
17327 | |
17328 return function(model, observer) { | |
17329 var obj = {}; | |
17330 for (var i = 0; i < properties.length; i++) | |
17331 obj[properties[i].key] = properties[i].value(model, observer); | |
17332 return obj; | |
17333 } | |
17334 }, | |
17335 | |
17336 createFilter: function(name, args) { | |
17337 this.filters.push(new Filter(name, args)); | |
17338 }, | |
17339 | |
17340 createAsExpression: function(expression, scopeIdent) { | |
17341 this.expression = expression; | |
17342 this.scopeIdent = scopeIdent; | |
17343 }, | |
17344 | |
17345 createInExpression: function(scopeIdent, indexIdent, expression) { | |
17346 this.expression = expression; | |
17347 this.scopeIdent = scopeIdent; | |
17348 this.indexIdent = indexIdent; | |
17349 }, | |
17350 | |
17351 createTopLevel: function(expression) { | |
17352 this.expression = expression; | |
17353 }, | |
17354 | |
17355 createThisExpression: notImplemented | |
17356 } | |
17357 | |
17358 function ConstantObservable(value) { | |
17359 this.value_ = value; | |
17360 } | |
17361 | |
17362 ConstantObservable.prototype = { | |
17363 open: function() { return this.value_; }, | |
17364 discardChanges: function() { return this.value_; }, | |
17365 deliver: function() {}, | |
17366 close: function() {}, | |
17367 } | |
17368 | |
17369 function Expression(delegate) { | |
17370 this.scopeIdent = delegate.scopeIdent; | |
17371 this.indexIdent = delegate.indexIdent; | |
17372 | |
17373 if (!delegate.expression) | |
17374 throw Error('No expression found.'); | |
17375 | |
17376 this.expression = delegate.expression; | |
17377 getFn(this.expression); // forces enumeration of path dependencies | |
17378 | |
17379 this.filters = delegate.filters; | |
17380 this.dynamicDeps = delegate.dynamicDeps; | |
17381 } | |
17382 | |
17383 Expression.prototype = { | |
17384 getBinding: function(model, filterRegistry, oneTime) { | |
17385 if (oneTime) | |
17386 return this.getValue(model, undefined, filterRegistry); | |
17387 | |
17388 var observer = new CompoundObserver(); | |
17389 // captures deps. | |
17390 var firstValue = this.getValue(model, observer, filterRegistry); | |
17391 var firstTime = true; | |
17392 var self = this; | |
17393 | |
17394 function valueFn() { | |
17395 // deps cannot have changed on first value retrieval. | |
17396 if (firstTime) { | |
17397 firstTime = false; | |
17398 return firstValue; | |
17399 } | |
17400 | |
17401 if (self.dynamicDeps) | |
17402 observer.startReset(); | |
17403 | |
17404 var value = self.getValue(model, | |
17405 self.dynamicDeps ? observer : undefined, | |
17406 filterRegistry); | |
17407 if (self.dynamicDeps) | |
17408 observer.finishReset(); | |
17409 | |
17410 return value; | |
17411 } | |
17412 | |
17413 function setValueFn(newValue) { | |
17414 self.setValue(model, newValue, filterRegistry); | |
17415 return newValue; | |
17416 } | |
17417 | |
17418 return new ObserverTransform(observer, valueFn, setValueFn, true); | |
17419 }, | |
17420 | |
17421 getValue: function(model, observer, filterRegistry) { | |
17422 var value = getFn(this.expression)(model, observer); | |
17423 for (var i = 0; i < this.filters.length; i++) { | |
17424 value = this.filters[i].transform(value, false, filterRegistry, model, | |
17425 observer); | |
17426 } | |
17427 | |
17428 return value; | |
17429 }, | |
17430 | |
17431 setValue: function(model, newValue, filterRegistry) { | |
17432 var count = this.filters ? this.filters.length : 0; | |
17433 while (count-- > 0) { | |
17434 newValue = this.filters[count].transform(newValue, true, filterRegistry, | |
17435 model); | |
17436 } | |
17437 | |
17438 if (this.expression.setValue) | |
17439 return this.expression.setValue(model, newValue); | |
17440 } | |
17441 } | |
17442 | |
17443 /** | |
17444 * Converts a style property name to a css property name. For example: | |
17445 * "WebkitUserSelect" to "-webkit-user-select" | |
17446 */ | |
17447 function convertStylePropertyName(name) { | |
17448 return String(name).replace(/[A-Z]/g, function(c) { | |
17449 return '-' + c.toLowerCase(); | |
17450 }); | |
17451 } | |
17452 | |
17453 function isEventHandler(name) { | |
17454 return name[0] === 'o' && | |
17455 name[1] === 'n' && | |
17456 name[2] === '-'; | |
17457 } | |
17458 | |
17459 var mixedCaseEventTypes = {}; | |
17460 [ | |
17461 'webkitAnimationStart', | |
17462 'webkitAnimationEnd', | |
17463 'webkitTransitionEnd', | |
17464 'DOMFocusOut', | |
17465 'DOMFocusIn', | |
17466 'DOMMouseScroll' | |
17467 ].forEach(function(e) { | |
17468 mixedCaseEventTypes[e.toLowerCase()] = e; | |
17469 }); | |
17470 | |
17471 var parentScopeName = '@' + Math.random().toString(36).slice(2); | |
17472 | |
17473 // Single ident paths must bind directly to the appropriate scope object. | |
17474 // I.e. Pushed values in two-bindings need to be assigned to the actual model | |
17475 // object. | |
17476 function findScope(model, prop) { | |
17477 while (model[parentScopeName] && | |
17478 !Object.prototype.hasOwnProperty.call(model, prop)) { | |
17479 model = model[parentScopeName]; | |
17480 } | |
17481 | |
17482 return model; | |
17483 } | |
17484 | |
17485 function resolveEventReceiver(model, path, node) { | |
17486 if (path.length == 0) | |
17487 return undefined; | |
17488 | |
17489 if (path.length == 1) | |
17490 return findScope(model, path[0]); | |
17491 | |
17492 for (var i = 0; model != null && i < path.length - 1; i++) { | |
17493 model = model[path[i]]; | |
17494 } | |
17495 | |
17496 return model; | |
17497 } | |
17498 | |
17499 function prepareEventBinding(path, name, polymerExpressions) { | |
17500 var eventType = name.substring(3); | |
17501 eventType = mixedCaseEventTypes[eventType] || eventType; | |
17502 | |
17503 return function(model, node, oneTime) { | |
17504 var fn, receiver, handler; | |
17505 if (typeof polymerExpressions.resolveEventHandler == 'function') { | |
17506 handler = function(e) { | |
17507 fn = fn || polymerExpressions.resolveEventHandler(model, path, node); | |
17508 fn(e, e.detail, e.currentTarget); | |
17509 | |
17510 if (Platform && typeof Platform.flush == 'function') | |
17511 Platform.flush(); | |
17512 }; | |
17513 } else { | |
17514 handler = function(e) { | |
17515 fn = fn || path.getValueFrom(model); | |
17516 receiver = receiver || resolveEventReceiver(model, path, node); | |
17517 | |
17518 fn.apply(receiver, [e, e.detail, e.currentTarget]); | |
17519 | |
17520 if (Platform && typeof Platform.flush == 'function') | |
17521 Platform.flush(); | |
17522 }; | |
17523 } | |
17524 | |
17525 node.addEventListener(eventType, handler); | |
17526 | |
17527 if (oneTime) | |
17528 return; | |
17529 | |
17530 function bindingValue() { | |
17531 return '{{ ' + path + ' }}'; | |
17532 } | |
17533 | |
17534 return { | |
17535 open: bindingValue, | |
17536 discardChanges: bindingValue, | |
17537 close: function() { | |
17538 node.removeEventListener(eventType, handler); | |
17539 } | |
17540 }; | |
17541 } | |
17542 } | |
17543 | |
17544 function isLiteralExpression(pathString) { | |
17545 switch (pathString) { | |
17546 case '': | |
17547 return false; | |
17548 | |
17549 case 'false': | |
17550 case 'null': | |
17551 case 'true': | |
17552 return true; | |
17553 } | |
17554 | |
17555 if (!isNaN(Number(pathString))) | |
17556 return true; | |
17557 | |
17558 return false; | |
17559 }; | |
17560 | |
17561 function PolymerExpressions() {} | |
17562 | |
17563 PolymerExpressions.prototype = { | |
17564 // "built-in" filters | |
17565 styleObject: function(value) { | |
17566 var parts = []; | |
17567 for (var key in value) { | |
17568 parts.push(convertStylePropertyName(key) + ': ' + value[key]); | |
17569 } | |
17570 return parts.join('; '); | |
17571 }, | |
17572 | |
17573 tokenList: function(value) { | |
17574 var tokens = []; | |
17575 for (var key in value) { | |
17576 if (value[key]) | |
17577 tokens.push(key); | |
17578 } | |
17579 return tokens.join(' '); | |
17580 }, | |
17581 | |
17582 // binding delegate API | |
17583 prepareInstancePositionChanged: function(template) { | |
17584 var indexIdent = template.polymerExpressionIndexIdent_; | |
17585 if (!indexIdent) | |
17586 return; | |
17587 | |
17588 return function(templateInstance, index) { | |
17589 templateInstance.model[indexIdent] = index; | |
17590 }; | |
17591 }, | |
17592 | |
17593 prepareBinding: function(pathString, name, node) { | |
17594 var path = Path.get(pathString); | |
17595 if (isEventHandler(name)) { | |
17596 if (!path.valid) { | |
17597 console.error('on-* bindings must be simple path expressions'); | |
17598 return; | |
17599 } | |
17600 | |
17601 return prepareEventBinding(path, name, this); | |
17602 } | |
17603 | |
17604 if (!isLiteralExpression(pathString) && path.valid) { | |
17605 if (path.length == 1) { | |
17606 return function(model, node, oneTime) { | |
17607 if (oneTime) | |
17608 return path.getValueFrom(model); | |
17609 | |
17610 var scope = findScope(model, path[0]); | |
17611 return new PathObserver(scope, path); | |
17612 }; | |
17613 } | |
17614 return; // bail out early if pathString is simple path. | |
17615 } | |
17616 | |
17617 return prepareBinding(pathString, name, node, this); | |
17618 }, | |
17619 | |
17620 prepareInstanceModel: function(template) { | |
17621 var scopeName = template.polymerExpressionScopeIdent_; | |
17622 if (!scopeName) | |
17623 return; | |
17624 | |
17625 var parentScope = template.templateInstance ? | |
17626 template.templateInstance.model : | |
17627 template.model; | |
17628 | |
17629 var indexName = template.polymerExpressionIndexIdent_; | |
17630 | |
17631 return function(model) { | |
17632 var scope = Object.create(parentScope); | |
17633 scope[scopeName] = model; | |
17634 scope[indexName] = undefined; | |
17635 scope[parentScopeName] = parentScope; | |
17636 return scope; | |
17637 }; | |
17638 } | |
17639 }; | |
17640 | |
17641 global.PolymerExpressions = PolymerExpressions; | |
17642 if (global.exposeGetExpression) | |
17643 global.getExpression_ = getExpression; | |
17644 | |
17645 global.PolymerExpressions.prepareEventBinding = prepareEventBinding; | |
17646 })(this); | |
17647 | |
17648 /* | |
17649 * Copyright 2013 The Polymer Authors. All rights reserved. | |
17650 * Use of this source code is governed by a BSD-style | |
17651 * license that can be found in the LICENSE file. | |
17652 */ | |
17653 (function(scope) { | 13838 (function(scope) { |
17654 | 13839 |
17655 // inject style sheet | 13840 // inject style sheet |
17656 var style = document.createElement('style'); | 13841 var style = document.createElement('style'); |
17657 style.textContent = 'template {display: none !important;} /* injected by platfor
m.js */'; | 13842 style.textContent = 'template {display: none !important;} /* injected by platfor
m.js */'; |
17658 var head = document.querySelector('head'); | 13843 var head = document.querySelector('head'); |
17659 head.insertBefore(style, head.firstChild); | 13844 head.insertBefore(style, head.firstChild); |
17660 | 13845 |
17661 // flush (with logging) | 13846 // flush (with logging) |
17662 var flushing; | 13847 var flushing; |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
17694 } | 13879 } |
17695 } | 13880 } |
17696 | 13881 |
17697 // exports | 13882 // exports |
17698 scope.flush = flush; | 13883 scope.flush = flush; |
17699 | 13884 |
17700 })(window.Platform); | 13885 })(window.Platform); |
17701 | 13886 |
17702 | 13887 |
17703 //# sourceMappingURL=platform.concat.js.map | 13888 //# sourceMappingURL=platform.concat.js.map |
OLD | NEW |