| OLD | NEW |
| 1 // Copyright (c) 2012 The Polymer Authors. All rights reserved. | 1 // Copyright (c) 2012 The Polymer Authors. All rights reserved. |
| 2 // | 2 // |
| 3 // Redistribution and use in source and binary forms, with or without | 3 // Redistribution and use in source and binary forms, with or without |
| 4 // modification, are permitted provided that the following conditions are | 4 // modification, are permitted provided that the following conditions are |
| 5 // met: | 5 // met: |
| 6 // | 6 // |
| 7 // * Redistributions of source code must retain the above copyright | 7 // * Redistributions of source code must retain the above copyright |
| 8 // notice, this list of conditions and the following disclaimer. | 8 // notice, this list of conditions and the following disclaimer. |
| 9 // * Redistributions in binary form must reproduce the above | 9 // * Redistributions in binary form must reproduce the above |
| 10 // copyright notice, this list of conditions and the following disclaimer | 10 // copyright notice, this list of conditions and the following disclaimer |
| 11 // in the documentation and/or other materials provided with the | 11 // in the documentation and/or other materials provided with the |
| 12 // distribution. | 12 // distribution. |
| 13 // * Neither the name of Google Inc. nor the names of its | 13 // * Neither the name of Google Inc. nor the names of its |
| 14 // contributors may be used to endorse or promote products derived from | 14 // contributors may be used to endorse or promote products derived from |
| 15 // this software without specific prior written permission. | 15 // this software without specific prior written permission. |
| 16 // | 16 // |
| 17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | 17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| 18 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | 18 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| 19 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | 19 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| 20 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | 20 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| 21 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | 21 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| 22 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | 22 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| 23 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | 23 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| 24 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 24 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| 25 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 25 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 26 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 26 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| 27 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 27 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 28 if (typeof WeakMap === 'undefined') { | 28 window.CustomElements = {flags:{}}; |
| 29 // SideTable is a weak map where possible. If WeakMap is not available the |
| 30 // association is stored as an expando property. |
| 31 var SideTable; |
| 32 // TODO(arv): WeakMap does not allow for Node etc to be keys in Firefox |
| 33 if (typeof WeakMap !== 'undefined' && navigator.userAgent.indexOf('Firefox/') <
0) { |
| 34 SideTable = WeakMap; |
| 35 } else { |
| 29 (function() { | 36 (function() { |
| 30 var defineProperty = Object.defineProperty; | 37 var defineProperty = Object.defineProperty; |
| 31 var counter = Date.now() % 1e9; | 38 var counter = Date.now() % 1e9; |
| 32 | 39 |
| 33 var WeakMap = function() { | 40 SideTable = function() { |
| 34 this.name = '__st' + (Math.random() * 1e9 >>> 0) + (counter++ + '__'); | 41 this.name = '__st' + (Math.random() * 1e9 >>> 0) + (counter++ + '__'); |
| 35 }; | 42 }; |
| 36 | 43 |
| 37 WeakMap.prototype = { | 44 SideTable.prototype = { |
| 38 set: function(key, value) { | 45 set: function(key, value) { |
| 39 var entry = key[this.name]; | 46 var entry = key[this.name]; |
| 40 if (entry && entry[0] === key) | 47 if (entry && entry[0] === key) |
| 41 entry[1] = value; | 48 entry[1] = value; |
| 42 else | 49 else |
| 43 defineProperty(key, this.name, {value: [key, value], writable: true}); | 50 defineProperty(key, this.name, {value: [key, value], writable: true}); |
| 44 }, | 51 }, |
| 45 get: function(key) { | 52 get: function(key) { |
| 46 var entry; | 53 var entry; |
| 47 return (entry = key[this.name]) && entry[0] === key ? | 54 return (entry = key[this.name]) && entry[0] === key ? |
| 48 entry[1] : undefined; | 55 entry[1] : undefined; |
| 49 }, | 56 }, |
| 50 delete: function(key) { | 57 delete: function(key) { |
| 51 this.set(key, undefined); | 58 this.set(key, undefined); |
| 52 } | 59 } |
| 53 }; | 60 } |
| 54 | |
| 55 window.WeakMap = WeakMap; | |
| 56 })(); | 61 })(); |
| 57 } | 62 } |
| 58 | 63 |
| 59 (function(global) { | 64 (function(global) { |
| 60 | 65 |
| 61 var registrationsTable = new WeakMap(); | 66 var registrationsTable = new SideTable(); |
| 62 | 67 |
| 63 // We use setImmediate or postMessage for our future callback. | 68 // We use setImmediate or postMessage for our future callback. |
| 64 var setImmediate = window.msSetImmediate; | 69 var setImmediate = window.msSetImmediate; |
| 65 | 70 |
| 66 // Use post message to emulate setImmediate. | 71 // Use post message to emulate setImmediate. |
| 67 if (!setImmediate) { | 72 if (!setImmediate) { |
| 68 var setImmediateQueue = []; | 73 var setImmediateQueue = []; |
| 69 var sentinel = String(Math.random()); | 74 var sentinel = String(Math.random()); |
| 70 window.addEventListener('message', function(e) { | 75 window.addEventListener('message', function(e) { |
| 71 if (e.data === sentinel) { | 76 if (e.data === sentinel) { |
| (...skipping 512 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 584 }); | 589 }); |
| 585 | 590 |
| 586 } | 591 } |
| 587 | 592 |
| 588 clearRecords(); | 593 clearRecords(); |
| 589 } | 594 } |
| 590 }; | 595 }; |
| 591 | 596 |
| 592 global.JsMutationObserver = JsMutationObserver; | 597 global.JsMutationObserver = JsMutationObserver; |
| 593 | 598 |
| 594 // Provide unprefixed MutationObserver with native or JS implementation | |
| 595 if (!global.MutationObserver && global.WebKitMutationObserver) | |
| 596 global.MutationObserver = global.WebKitMutationObserver; | |
| 597 | |
| 598 if (!global.MutationObserver) | |
| 599 global.MutationObserver = JsMutationObserver; | |
| 600 | |
| 601 | |
| 602 })(this); | 599 })(this); |
| 603 | 600 |
| 604 window.CustomElements = window.CustomElements || {flags:{}}; | 601 if (!window.MutationObserver) { |
| 602 window.MutationObserver = |
| 603 window.WebKitMutationObserver || |
| 604 window.JsMutationObserver; |
| 605 if (!MutationObserver) { |
| 606 throw new Error("no mutation observer support"); |
| 607 } |
| 608 } |
| 609 |
| 605 (function(scope){ | 610 (function(scope){ |
| 606 | 611 |
| 607 var logFlags = window.logFlags || {}; | 612 var logFlags = window.logFlags || {}; |
| 608 | 613 |
| 609 // walk the subtree rooted at node, applying 'find(element, data)' function | 614 // walk the subtree rooted at node, applying 'find(element, data)' function |
| 610 // to each element | 615 // to each element |
| 611 // if 'find' returns true for 'element', do not search element's subtree | 616 // if 'find' returns true for 'element', do not search element's subtree |
| 612 function findAll(node, find, data) { | 617 function findAll(node, find, data) { |
| 613 var e = node.firstElementChild; | 618 var e = node.firstElementChild; |
| 614 if (!e) { | 619 if (!e) { |
| (...skipping 155 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 770 } | 775 } |
| 771 } | 776 } |
| 772 | 777 |
| 773 function removedNode(node) { | 778 function removedNode(node) { |
| 774 removed(node); | 779 removed(node); |
| 775 forSubtree(node, function(e) { | 780 forSubtree(node, function(e) { |
| 776 removed(e); | 781 removed(e); |
| 777 }); | 782 }); |
| 778 } | 783 } |
| 779 | 784 |
| 785 |
| 780 function removed(element) { | 786 function removed(element) { |
| 781 if (hasPolyfillMutations) { | 787 if (hasPolyfillMutations) { |
| 782 deferMutation(function() { | 788 deferMutation(function() { |
| 783 _removed(element); | 789 _removed(element); |
| 784 }); | 790 }); |
| 785 } else { | 791 } else { |
| 786 _removed(element); | 792 _removed(element); |
| 787 } | 793 } |
| 788 } | 794 } |
| 789 | 795 |
| 790 function _removed(element) { | 796 function removed(element) { |
| 791 // TODO(sjmiles): temporary: do work on all custom elements so we can track | 797 // TODO(sjmiles): temporary: do work on all custom elements so we can track |
| 792 // behavior even when callbacks not defined | 798 // behavior even when callbacks not defined |
| 793 if (element.leftViewCallback || (element.__upgraded__ && logFlags.dom)) { | 799 if (element.leftViewCallback || (element.__upgraded__ && logFlags.dom)) { |
| 794 logFlags.dom && console.log('removed:', element.localName); | 800 logFlags.dom && console.log('removed:', element.localName); |
| 795 if (!inDocument(element)) { | 801 if (!inDocument(element)) { |
| 796 element.__inserted = (element.__inserted || 0) - 1; | 802 element.__inserted = (element.__inserted || 0) - 1; |
| 797 // if we are in a 'inserted' state, bluntly adjust to an 'removed' state | 803 // if we are in a 'inserted' state, bluntly adjust to an 'removed' state |
| 798 if (element.__inserted > 0) { | 804 if (element.__inserted > 0) { |
| 799 element.__inserted = 0; | 805 element.__inserted = 0; |
| 800 } | 806 } |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 833 } | 839 } |
| 834 } | 840 } |
| 835 | 841 |
| 836 function watchRoot(root) { | 842 function watchRoot(root) { |
| 837 if (!root.__watched) { | 843 if (!root.__watched) { |
| 838 observe(root); | 844 observe(root); |
| 839 root.__watched = true; | 845 root.__watched = true; |
| 840 } | 846 } |
| 841 } | 847 } |
| 842 | 848 |
| 849 function filter(inNode) { |
| 850 switch (inNode.localName) { |
| 851 case 'style': |
| 852 case 'script': |
| 853 case 'template': |
| 854 case undefined: |
| 855 return true; |
| 856 } |
| 857 } |
| 858 |
| 843 function handler(mutations) { | 859 function handler(mutations) { |
| 844 // | 860 // |
| 845 if (logFlags.dom) { | 861 if (logFlags.dom) { |
| 846 var mx = mutations[0]; | 862 var mx = mutations[0]; |
| 847 if (mx && mx.type === 'childList' && mx.addedNodes) { | 863 if (mx && mx.type === 'childList' && mx.addedNodes) { |
| 848 if (mx.addedNodes) { | 864 if (mx.addedNodes) { |
| 849 var d = mx.addedNodes[0]; | 865 var d = mx.addedNodes[0]; |
| 850 while (d && d !== document && !d.host) { | 866 while (d && d !== document && !d.host) { |
| 851 d = d.parentNode; | 867 d = d.parentNode; |
| 852 } | 868 } |
| 853 var u = d && (d.URL || d._URL || (d.host && d.host.localName)) || ''; | 869 var u = d && (d.URL || d._URL || (d.host && d.host.localName)) || ''; |
| 854 u = u.split('/?').shift().split('/').pop(); | 870 u = u.split('/?').shift().split('/').pop(); |
| 855 } | 871 } |
| 856 } | 872 } |
| 857 console.group('mutations (%d) [%s]', mutations.length, u || ''); | 873 console.group('mutations (%d) [%s]', mutations.length, u || ''); |
| 858 } | 874 } |
| 859 // | 875 // |
| 860 mutations.forEach(function(mx) { | 876 mutations.forEach(function(mx) { |
| 861 //logFlags.dom && console.group('mutation'); | 877 //logFlags.dom && console.group('mutation'); |
| 862 if (mx.type === 'childList') { | 878 if (mx.type === 'childList') { |
| 863 forEach(mx.addedNodes, function(n) { | 879 forEach(mx.addedNodes, function(n) { |
| 864 //logFlags.dom && console.log(n.localName); | 880 //logFlags.dom && console.log(n.localName); |
| 865 if (!n.localName) { | 881 if (filter(n)) { |
| 866 return; | 882 return; |
| 867 } | 883 } |
| 868 // nodes added may need lifecycle management | 884 // nodes added may need lifecycle management |
| 869 addedNode(n); | 885 addedNode(n); |
| 870 }); | 886 }); |
| 871 // removed nodes may need lifecycle management | 887 // removed nodes may need lifecycle management |
| 872 forEach(mx.removedNodes, function(n) { | 888 forEach(mx.removedNodes, function(n) { |
| 873 //logFlags.dom && console.log(n.localName); | 889 //logFlags.dom && console.log(n.localName); |
| 874 if (!n.localName) { | 890 if (filter(n)) { |
| 875 return; | 891 return; |
| 876 } | 892 } |
| 877 removedNode(n); | 893 removedNode(n); |
| 878 }); | 894 }); |
| 879 } | 895 } |
| 880 //logFlags.dom && console.groupEnd(); | 896 //logFlags.dom && console.groupEnd(); |
| 881 }); | 897 }); |
| 882 logFlags.dom && console.groupEnd(); | 898 logFlags.dom && console.groupEnd(); |
| 883 }; | 899 }; |
| 884 | 900 |
| (...skipping 123 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1008 if (!name) { | 1024 if (!name) { |
| 1009 // TODO(sjmiles): replace with more appropriate error (EricB can probably | 1025 // TODO(sjmiles): replace with more appropriate error (EricB can probably |
| 1010 // offer guidance) | 1026 // offer guidance) |
| 1011 throw new Error('document.register: first argument `name` must not be empt
y'); | 1027 throw new Error('document.register: first argument `name` must not be empt
y'); |
| 1012 } | 1028 } |
| 1013 if (name.indexOf('-') < 0) { | 1029 if (name.indexOf('-') < 0) { |
| 1014 // TODO(sjmiles): replace with more appropriate error (EricB can probably | 1030 // TODO(sjmiles): replace with more appropriate error (EricB can probably |
| 1015 // offer guidance) | 1031 // offer guidance) |
| 1016 throw new Error('document.register: first argument (\'name\') must contain
a dash (\'-\'). Argument provided was \'' + String(name) + '\'.'); | 1032 throw new Error('document.register: first argument (\'name\') must contain
a dash (\'-\'). Argument provided was \'' + String(name) + '\'.'); |
| 1017 } | 1033 } |
| 1018 // elements may only be registered once | 1034 // record name |
| 1019 if (getRegisteredDefinition(name)) { | 1035 definition.name = name; |
| 1020 throw new Error('DuplicateDefinitionError: a type with name \'' + String(n
ame) + '\' is already registered'); | |
| 1021 } | |
| 1022 // must have a prototype, default to an extension of HTMLElement | 1036 // must have a prototype, default to an extension of HTMLElement |
| 1023 // TODO(sjmiles): probably should throw if no prototype, check spec | 1037 // TODO(sjmiles): probably should throw if no prototype, check spec |
| 1024 if (!definition.prototype) { | 1038 if (!definition.prototype) { |
| 1025 // TODO(sjmiles): replace with more appropriate error (EricB can probably | 1039 // TODO(sjmiles): replace with more appropriate error (EricB can probably |
| 1026 // offer guidance) | 1040 // offer guidance) |
| 1027 throw new Error('Options missing required prototype property'); | 1041 throw new Error('Options missing required prototype property'); |
| 1028 } | 1042 } |
| 1029 // record name | |
| 1030 definition.name = name.toLowerCase(); | |
| 1031 // ensure a lifecycle object so we don't have to null test it | 1043 // ensure a lifecycle object so we don't have to null test it |
| 1032 definition.lifecycle = definition.lifecycle || {}; | 1044 definition.lifecycle = definition.lifecycle || {}; |
| 1033 // build a list of ancestral custom elements (for native base detection) | 1045 // build a list of ancestral custom elements (for native base detection) |
| 1034 // TODO(sjmiles): we used to need to store this, but current code only | 1046 // TODO(sjmiles): we used to need to store this, but current code only |
| 1035 // uses it in 'resolveTagName': it should probably be inlined | 1047 // uses it in 'resolveTagName': it should probably be inlined |
| 1036 definition.ancestry = ancestry(definition.extends); | 1048 definition.ancestry = ancestry(definition.extends); |
| 1037 // extensions of native specializations of HTMLElement require localName | 1049 // extensions of native specializations of HTMLElement require localName |
| 1038 // to remain native, and use secondary 'is' specifier for extension type | 1050 // to remain native, and use secondary 'is' specifier for extension type |
| 1039 resolveTagName(definition); | 1051 resolveTagName(definition); |
| 1040 // some platforms require modifications to the user-supplied prototype | 1052 // some platforms require modifications to the user-supplied prototype |
| 1041 // chain | 1053 // chain |
| 1042 resolvePrototypeChain(definition); | 1054 resolvePrototypeChain(definition); |
| 1043 // overrides to implement attributeChanged callback | 1055 // overrides to implement attributeChanged callback |
| 1044 overrideAttributeApi(definition.prototype); | 1056 overrideAttributeApi(definition.prototype); |
| 1045 // 7.1.5: Register the DEFINITION with DOCUMENT | 1057 // 7.1.5: Register the DEFINITION with DOCUMENT |
| 1046 registerDefinition(definition.name, definition); | 1058 registerDefinition(name, definition); |
| 1047 // 7.1.7. Run custom element constructor generation algorithm with PROTOTYPE | 1059 // 7.1.7. Run custom element constructor generation algorithm with PROTOTYPE |
| 1048 // 7.1.8. Return the output of the previous step. | 1060 // 7.1.8. Return the output of the previous step. |
| 1049 definition.ctor = generateConstructor(definition); | 1061 definition.ctor = generateConstructor(definition); |
| 1050 definition.ctor.prototype = definition.prototype; | 1062 definition.ctor.prototype = definition.prototype; |
| 1051 // force our .constructor to be our actual constructor | 1063 // force our .constructor to be our actual constructor |
| 1052 definition.prototype.constructor = definition.ctor; | 1064 definition.prototype.constructor = definition.ctor; |
| 1053 // if initial parsing is complete | 1065 // if initial parsing is complete |
| 1054 if (scope.ready || scope.performedInitialDocumentUpgrade) { | 1066 if (scope.ready || scope.performedInitialDocumentUpgrade) { |
| 1055 // upgrade any pre-existing nodes of this type | 1067 // upgrade any pre-existing nodes of this type |
| 1056 scope.upgradeAll(document); | 1068 scope.upgradeAll(document); |
| 1057 } | 1069 } |
| 1058 return definition.ctor; | 1070 return definition.ctor; |
| 1059 } | 1071 } |
| 1060 | 1072 |
| 1061 function ancestry(extnds) { | 1073 function ancestry(extnds) { |
| 1062 var extendee = getRegisteredDefinition(extnds); | 1074 var extendee = registry[extnds]; |
| 1063 if (extendee) { | 1075 if (extendee) { |
| 1064 return ancestry(extendee.extends).concat([extendee]); | 1076 return ancestry(extendee.extends).concat([extendee]); |
| 1065 } | 1077 } |
| 1066 return []; | 1078 return []; |
| 1067 } | 1079 } |
| 1068 | 1080 |
| 1069 function resolveTagName(definition) { | 1081 function resolveTagName(definition) { |
| 1070 // if we are explicitly extending something, that thing is our | 1082 // if we are explicitly extending something, that thing is our |
| 1071 // baseTag, unless it represents a custom component | 1083 // baseTag, unless it represents a custom component |
| 1072 var baseTag = definition.extends; | 1084 var baseTag = definition.extends; |
| (...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1120 // output is a valid DOM element with the proper wrapper in place. | 1132 // output is a valid DOM element with the proper wrapper in place. |
| 1121 // | 1133 // |
| 1122 return upgrade(domCreateElement(definition.tag), definition); | 1134 return upgrade(domCreateElement(definition.tag), definition); |
| 1123 } | 1135 } |
| 1124 | 1136 |
| 1125 function upgrade(element, definition) { | 1137 function upgrade(element, definition) { |
| 1126 // some definitions specify an 'is' attribute | 1138 // some definitions specify an 'is' attribute |
| 1127 if (definition.is) { | 1139 if (definition.is) { |
| 1128 element.setAttribute('is', definition.is); | 1140 element.setAttribute('is', definition.is); |
| 1129 } | 1141 } |
| 1130 // remove 'unresolved' attr, which is a standin for :unresolved. | |
| 1131 element.removeAttribute('unresolved'); | |
| 1132 // make 'element' implement definition.prototype | 1142 // make 'element' implement definition.prototype |
| 1133 implement(element, definition); | 1143 implement(element, definition); |
| 1134 // flag as upgraded | 1144 // flag as upgraded |
| 1135 element.__upgraded__ = true; | 1145 element.__upgraded__ = true; |
| 1136 // there should never be a shadow root on element at this point | 1146 // there should never be a shadow root on element at this point |
| 1137 // we require child nodes be upgraded before `created` | 1147 // we require child nodes be upgraded before `created` |
| 1138 scope.upgradeSubtree(element); | 1148 scope.upgradeSubtree(element); |
| 1139 // lifecycle management | 1149 // lifecycle management |
| 1140 created(element); | 1150 created(element); |
| 1141 // OUTPUT | 1151 // OUTPUT |
| (...skipping 17 matching lines...) Expand all Loading... |
| 1159 // TODO(sjmiles): 'used' allows us to only copy the 'youngest' version of | 1169 // TODO(sjmiles): 'used' allows us to only copy the 'youngest' version of |
| 1160 // any property. This set should be precalculated. We also need to | 1170 // any property. This set should be precalculated. We also need to |
| 1161 // consider this for supporting 'super'. | 1171 // consider this for supporting 'super'. |
| 1162 var used = {}; | 1172 var used = {}; |
| 1163 // start with inSrc | 1173 // start with inSrc |
| 1164 var p = inSrc; | 1174 var p = inSrc; |
| 1165 // sometimes the default is HTMLUnknownElement.prototype instead of | 1175 // sometimes the default is HTMLUnknownElement.prototype instead of |
| 1166 // HTMLElement.prototype, so we add a test | 1176 // HTMLElement.prototype, so we add a test |
| 1167 // the idea is to avoid mixing in native prototypes, so adding | 1177 // the idea is to avoid mixing in native prototypes, so adding |
| 1168 // the second test is WLOG | 1178 // the second test is WLOG |
| 1169 while (p !== inNative && p !== HTMLUnknownElement.prototype) { | 1179 while (p && p !== inNative && p !== HTMLUnknownElement.prototype) { |
| 1170 var keys = Object.getOwnPropertyNames(p); | 1180 var keys = Object.getOwnPropertyNames(p); |
| 1171 for (var i=0, k; k=keys[i]; i++) { | 1181 for (var i=0, k; k=keys[i]; i++) { |
| 1172 if (!used[k]) { | 1182 if (!used[k]) { |
| 1173 Object.defineProperty(inTarget, k, | 1183 Object.defineProperty(inTarget, k, |
| 1174 Object.getOwnPropertyDescriptor(p, k)); | 1184 Object.getOwnPropertyDescriptor(p, k)); |
| 1175 used[k] = 1; | 1185 used[k] = 1; |
| 1176 } | 1186 } |
| 1177 } | 1187 } |
| 1178 p = Object.getPrototypeOf(p); | 1188 p = Object.getPrototypeOf(p); |
| 1179 } | 1189 } |
| 1180 } | 1190 } |
| 1181 | 1191 |
| 1182 function created(element) { | 1192 function created(element) { |
| 1183 // invoke createdCallback | 1193 // invoke createdCallback |
| 1184 if (element.createdCallback) { | 1194 if (element.createdCallback) { |
| 1185 element.createdCallback(); | 1195 element.createdCallback(); |
| 1186 } | 1196 } |
| 1187 } | 1197 } |
| 1188 | 1198 |
| 1189 // attribute watching | 1199 // attribute watching |
| 1190 | 1200 |
| 1191 function overrideAttributeApi(prototype) { | 1201 function overrideAttributeApi(prototype) { |
| 1192 // overrides to implement callbacks | 1202 // overrides to implement callbacks |
| 1193 // TODO(sjmiles): should support access via .attributes NamedNodeMap | 1203 // TODO(sjmiles): should support access via .attributes NamedNodeMap |
| 1194 // TODO(sjmiles): preserves user defined overrides, if any | 1204 // TODO(sjmiles): preserves user defined overrides, if any |
| 1195 if (prototype.setAttribute._polyfilled) { | |
| 1196 return; | |
| 1197 } | |
| 1198 var setAttribute = prototype.setAttribute; | 1205 var setAttribute = prototype.setAttribute; |
| 1199 prototype.setAttribute = function(name, value) { | 1206 prototype.setAttribute = function(name, value) { |
| 1200 changeAttribute.call(this, name, value, setAttribute); | 1207 changeAttribute.call(this, name, value, setAttribute); |
| 1201 } | 1208 } |
| 1202 var removeAttribute = prototype.removeAttribute; | 1209 var removeAttribute = prototype.removeAttribute; |
| 1203 prototype.removeAttribute = function(name) { | 1210 prototype.removeAttribute = function(name) { |
| 1204 changeAttribute.call(this, name, null, removeAttribute); | 1211 changeAttribute.call(this, name, null, removeAttribute); |
| 1205 } | 1212 } |
| 1206 prototype.setAttribute._polyfilled = true; | |
| 1207 } | 1213 } |
| 1208 | 1214 |
| 1209 // https://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/custom/ | |
| 1210 // index.html#dfn-attribute-changed-callback | |
| 1211 function changeAttribute(name, value, operation) { | 1215 function changeAttribute(name, value, operation) { |
| 1212 var oldValue = this.getAttribute(name); | 1216 var oldValue = this.getAttribute(name); |
| 1213 operation.apply(this, arguments); | 1217 operation.apply(this, arguments); |
| 1214 var newValue = this.getAttribute(name); | |
| 1215 if (this.attributeChangedCallback | 1218 if (this.attributeChangedCallback |
| 1216 && (newValue !== oldValue)) { | 1219 && (this.getAttribute(name) !== oldValue)) { |
| 1217 this.attributeChangedCallback(name, oldValue, newValue); | 1220 this.attributeChangedCallback(name, oldValue, value); |
| 1218 } | 1221 } |
| 1219 } | 1222 } |
| 1220 | 1223 |
| 1221 // element registry (maps tag names to definitions) | 1224 // element registry (maps tag names to definitions) |
| 1222 | 1225 |
| 1223 var registry = {}; | 1226 var registry = {}; |
| 1224 | 1227 |
| 1225 function getRegisteredDefinition(name) { | 1228 function registerDefinition(name, definition) { |
| 1226 if (name) { | 1229 if (registry[name]) { |
| 1227 return registry[name.toLowerCase()]; | 1230 throw new Error('Cannot register a tag more than once'); |
| 1228 } | 1231 } |
| 1229 } | |
| 1230 | |
| 1231 function registerDefinition(name, definition) { | |
| 1232 registry[name] = definition; | 1232 registry[name] = definition; |
| 1233 } | 1233 } |
| 1234 | 1234 |
| 1235 function generateConstructor(definition) { | 1235 function generateConstructor(definition) { |
| 1236 return function() { | 1236 return function() { |
| 1237 return instantiate(definition); | 1237 return instantiate(definition); |
| 1238 }; | 1238 }; |
| 1239 } | 1239 } |
| 1240 | 1240 |
| 1241 function createElement(tag, typeExtension) { | 1241 function createElement(tag, typeExtension) { |
| 1242 // TODO(sjmiles): ignore 'tag' when using 'typeExtension', we could | 1242 var definition = registry[typeExtension || tag]; |
| 1243 // error check it, or perhaps there should only ever be one argument | |
| 1244 var definition = getRegisteredDefinition(typeExtension || tag); | |
| 1245 if (definition) { | 1243 if (definition) { |
| 1246 if (tag == definition.tag && typeExtension == definition.is) { | 1244 if (tag == definition.tag && typeExtension == definition.is) { |
| 1247 return new definition.ctor(); | 1245 return new definition.ctor(); |
| 1248 } | 1246 } |
| 1249 // Handle empty string for type extension. | 1247 // Handle empty string for type extension. |
| 1250 if (!typeExtension && !definition.is) { | 1248 if (!typeExtension && !definition.is) { |
| 1251 return new definition.ctor(); | 1249 return new definition.ctor(); |
| 1252 } | 1250 } |
| 1253 } | 1251 } |
| 1254 | 1252 |
| 1255 if (typeExtension) { | 1253 if (typeExtension) { |
| 1256 var element = createElement(tag); | 1254 var element = createElement(tag); |
| 1257 element.setAttribute('is', typeExtension); | 1255 element.setAttribute('is', typeExtension); |
| 1258 return element; | 1256 return element; |
| 1259 } | 1257 } |
| 1260 var element = domCreateElement(tag); | 1258 var element = domCreateElement(tag); |
| 1261 // Custom tags should be HTMLElements even if not upgraded. | 1259 // Custom tags should be HTMLElements even if not upgraded. |
| 1262 if (tag.indexOf('-') >= 0) { | 1260 if (tag.indexOf('-') >= 0) { |
| 1263 implement(element, HTMLElement); | 1261 implement(element, HTMLElement); |
| 1264 } | 1262 } |
| 1265 return element; | 1263 return element; |
| 1266 } | 1264 } |
| 1267 | 1265 |
| 1268 function upgradeElement(element) { | 1266 function upgradeElement(element) { |
| 1269 if (!element.__upgraded__ && (element.nodeType === Node.ELEMENT_NODE)) { | 1267 if (!element.__upgraded__ && (element.nodeType === Node.ELEMENT_NODE)) { |
| 1270 var type = element.getAttribute('is') || element.localName; | 1268 var is = element.getAttribute('is'); |
| 1271 var definition = getRegisteredDefinition(type); | 1269 var definition = registry[is || element.localName]; |
| 1272 return definition && upgrade(element, definition); | 1270 if (definition) { |
| 1271 if (is && definition.tag == element.localName) { |
| 1272 return upgrade(element, definition); |
| 1273 } else if (!is && !definition.extends) { |
| 1274 return upgrade(element, definition); |
| 1275 } |
| 1276 } |
| 1273 } | 1277 } |
| 1274 } | 1278 } |
| 1275 | 1279 |
| 1276 function cloneNode(deep) { | 1280 function cloneNode(deep) { |
| 1277 // call original clone | 1281 // call original clone |
| 1278 var n = domCloneNode.call(this, deep); | 1282 var n = domCloneNode.call(this, deep); |
| 1279 // upgrade the element and subtree | 1283 // upgrade the element and subtree |
| 1280 scope.upgradeAll(n); | 1284 scope.upgradeAll(n); |
| 1281 // return the clone | 1285 // return the clone |
| 1282 return n; | 1286 return n; |
| (...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1366 && inElt.getAttribute('rel') === IMPORT_LINK_TYPE); | 1370 && inElt.getAttribute('rel') === IMPORT_LINK_TYPE); |
| 1367 } | 1371 } |
| 1368 | 1372 |
| 1369 var forEach = Array.prototype.forEach.call.bind(Array.prototype.forEach); | 1373 var forEach = Array.prototype.forEach.call.bind(Array.prototype.forEach); |
| 1370 | 1374 |
| 1371 // exports | 1375 // exports |
| 1372 | 1376 |
| 1373 CustomElements.parser = parser; | 1377 CustomElements.parser = parser; |
| 1374 | 1378 |
| 1375 })(); | 1379 })(); |
| 1376 (function(scope){ | 1380 (function(){ |
| 1377 | 1381 |
| 1378 // bootstrap parsing | 1382 // bootstrap parsing |
| 1379 function bootstrap() { | 1383 function bootstrap() { |
| 1380 // parse document | 1384 // parse document |
| 1381 CustomElements.parser.parse(document); | 1385 CustomElements.parser.parse(document); |
| 1382 // one more pass before register is 'live' | 1386 // one more pass before register is 'live' |
| 1383 CustomElements.upgradeDocument(document); | 1387 CustomElements.upgradeDocument(document); |
| 1384 CustomElements.performedInitialDocumentUpgrade = true; | 1388 CustomElements.performedInitialDocumentUpgrade = true; |
| 1385 // choose async | 1389 // choose async |
| 1386 var async = window.Platform && Platform.endOfMicrotask ? | 1390 var async = window.Platform && Platform.endOfMicrotask ? |
| (...skipping 11 matching lines...) Expand all Loading... |
| 1398 // notify the system that we are bootstrapped | 1402 // notify the system that we are bootstrapped |
| 1399 document.body.dispatchEvent( | 1403 document.body.dispatchEvent( |
| 1400 new CustomEvent('WebComponentsReady', {bubbles: true}) | 1404 new CustomEvent('WebComponentsReady', {bubbles: true}) |
| 1401 ); | 1405 ); |
| 1402 }); | 1406 }); |
| 1403 } | 1407 } |
| 1404 | 1408 |
| 1405 // CustomEvent shim for IE | 1409 // CustomEvent shim for IE |
| 1406 if (typeof window.CustomEvent !== 'function') { | 1410 if (typeof window.CustomEvent !== 'function') { |
| 1407 window.CustomEvent = function(inType) { | 1411 window.CustomEvent = function(inType) { |
| 1408 var e = document.createEvent('HTMLEvents'); | 1412 var e = document.createEvent('HTMLEvents'); |
| 1409 e.initEvent(inType, true, true); | 1413 e.initEvent(inType, true, true); |
| 1410 return e; | 1414 return e; |
| 1411 }; | 1415 }; |
| 1412 } | 1416 } |
| 1413 | 1417 |
| 1414 // When loading at readyState complete time (or via flag), boot custom elements | 1418 if (document.readyState === 'complete') { |
| 1415 // immediately. | |
| 1416 // If relevant, HTMLImports must already be loaded. | |
| 1417 if (document.readyState === 'complete' || scope.flags.eager) { | |
| 1418 bootstrap(); | 1419 bootstrap(); |
| 1419 // When loading at readyState interactive time, bootstrap only if HTMLImports | |
| 1420 // are not pending. Also avoid IE as the semantics of this state are unreliable. | |
| 1421 } else if (document.readyState === 'interactive' && !window.attachEvent && | |
| 1422 (!window.HTMLImports || window.HTMLImports.ready)) { | |
| 1423 bootstrap(); | |
| 1424 // When loading at other readyStates, wait for the appropriate DOM event to | |
| 1425 // bootstrap. | |
| 1426 } else { | 1420 } else { |
| 1427 var loadEvent = window.HTMLImports ? 'HTMLImportsLoaded' : | 1421 var loadEvent = window.HTMLImports ? 'HTMLImportsLoaded' : |
| 1428 document.readyState == 'loading' ? 'DOMContentLoaded' : 'load'; | 1422 document.readyState == 'loading' ? 'DOMContentLoaded' : 'load'; |
| 1429 window.addEventListener(loadEvent, bootstrap); | 1423 window.addEventListener(loadEvent, bootstrap); |
| 1430 } | 1424 } |
| 1431 | 1425 |
| 1432 })(window.CustomElements); | 1426 })(); |
| 1433 | 1427 |
| 1434 (function() { | 1428 (function() { |
| 1435 // Patch to allow custom element and shadow dom to work together, from: | 1429 // Patch to allow custom element and shadow dom to work together, from: |
| 1436 // https://github.com/Polymer/platform/blob/master/src/patches-shadowdom-polyfil
l.js | 1430 // https://github.com/Polymer/platform/blob/master/src/patches-shadowdom-polyfil
l.js |
| 1437 // include .host reference | 1431 // include .host reference |
| 1438 if (HTMLElement.prototype.createShadowRoot) { | 1432 if (HTMLElement.prototype.createShadowRoot) { |
| 1439 var originalCreateShadowRoot = HTMLElement.prototype.createShadowRoot; | 1433 var originalCreateShadowRoot = HTMLElement.prototype.createShadowRoot; |
| 1440 HTMLElement.prototype.createShadowRoot = function() { | 1434 HTMLElement.prototype.createShadowRoot = function() { |
| 1441 var root = originalCreateShadowRoot.call(this); | 1435 var root = originalCreateShadowRoot.call(this); |
| 1442 root.host = this; | 1436 root.host = this; |
| 1443 return root; | 1437 return root; |
| 1444 } | 1438 } |
| 1445 } | 1439 } |
| 1446 | 1440 |
| 1447 | 1441 |
| 1448 // Patch to allow custom elements and shadow dom to work together, from: | 1442 // Patch to allow custom elements and shadow dom to work together, from: |
| 1449 // https://github.com/Polymer/platform-dev/blob/2bb9c56d90f9ac19c2e65cdad368668a
ff514f14/src/patches-custom-elements.js | 1443 // https://github.com/Polymer/platform/blob/master/src/patches-custom-elements.j
s |
| 1450 if (window.ShadowDOMPolyfill) { | 1444 if (window.ShadowDOMPolyfill) { |
| 1445 function nop() {}; |
| 1446 |
| 1447 // disable shadow dom watching |
| 1448 CustomElements.watchShadow = nop; |
| 1449 CustomElements.watchAllShadows = nop; |
| 1451 | 1450 |
| 1452 // ensure wrapped inputs for these functions | 1451 // ensure wrapped inputs for these functions |
| 1453 var fns = ['upgradeAll', 'upgradeSubtree', 'observeDocument', | 1452 var fns = ['upgradeAll', 'upgradeSubtree', 'observeDocument', |
| 1454 'upgradeDocument']; | 1453 'upgradeDocument']; |
| 1455 | 1454 |
| 1456 // cache originals | 1455 // cache originals |
| 1457 var original = {}; | 1456 var original = {}; |
| 1458 fns.forEach(function(fn) { | 1457 fns.forEach(function(fn) { |
| 1459 original[fn] = CustomElements[fn]; | 1458 original[fn] = CustomElements[fn]; |
| 1460 }); | 1459 }); |
| 1461 | 1460 |
| 1462 // override | 1461 // override |
| 1463 fns.forEach(function(fn) { | 1462 fns.forEach(function(fn) { |
| 1464 CustomElements[fn] = function(inNode) { | 1463 CustomElements[fn] = function(inNode) { |
| 1465 return original[fn](window.ShadowDOMPolyfill.wrapIfNeeded(inNode)); | 1464 return original[fn](ShadowDOMPolyfill.wrapIfNeeded(inNode)); |
| 1466 }; | 1465 }; |
| 1467 }); | 1466 }); |
| 1468 | |
| 1469 } | |
| 1470 | |
| 1471 // Patch to make importNode work. | |
| 1472 // https://github.com/Polymer/platform-dev/blob/64a92f273462f04a84abbe2f054294f2
b62dbcd6/src/patches-mdv.js | |
| 1473 if (window.CustomElements && !CustomElements.useNative) { | |
| 1474 var originalImportNode = Document.prototype.importNode; | |
| 1475 Document.prototype.importNode = function(node, deep) { | |
| 1476 var imported = originalImportNode.call(this, node, deep); | |
| 1477 CustomElements.upgradeAll(imported); | |
| 1478 return imported; | |
| 1479 } | |
| 1480 } | 1467 } |
| 1481 | 1468 |
| 1482 })(); | 1469 })(); |
| OLD | NEW |