| OLD | NEW | 
|---|
|  | (Empty) | 
| 1 // Copyright (c) 2012 The Polymer Authors. All rights reserved. |  | 
| 2 // |  | 
| 3 // Redistribution and use in source and binary forms, with or without |  | 
| 4 // modification, are permitted provided that the following conditions are |  | 
| 5 // met: |  | 
| 6 // |  | 
| 7 //    * Redistributions of source code must retain the above copyright |  | 
| 8 // notice, this list of conditions and the following disclaimer. |  | 
| 9 //    * Redistributions in binary form must reproduce the above |  | 
| 10 // copyright notice, this list of conditions and the following disclaimer |  | 
| 11 // in the documentation and/or other materials provided with the |  | 
| 12 // distribution. |  | 
| 13 //    * Neither the name of Google Inc. nor the names of its |  | 
| 14 // contributors may be used to endorse or promote products derived from |  | 
| 15 // this software without specific prior written permission. |  | 
| 16 // |  | 
| 17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |  | 
| 18 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |  | 
| 19 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |  | 
| 20 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |  | 
| 21 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |  | 
| 22 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |  | 
| 23 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |  | 
| 24 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |  | 
| 25 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |  | 
| 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. |  | 
| 28 if (typeof WeakMap === 'undefined') { |  | 
| 29   (function() { |  | 
| 30     var defineProperty = Object.defineProperty; |  | 
| 31     var counter = Date.now() % 1e9; |  | 
| 32 |  | 
| 33     var WeakMap = function() { |  | 
| 34       this.name = '__st' + (Math.random() * 1e9 >>> 0) + (counter++ + '__'); |  | 
| 35     }; |  | 
| 36 |  | 
| 37     WeakMap.prototype = { |  | 
| 38       set: function(key, value) { |  | 
| 39         var entry = key[this.name]; |  | 
| 40         if (entry && entry[0] === key) |  | 
| 41           entry[1] = value; |  | 
| 42         else |  | 
| 43           defineProperty(key, this.name, {value: [key, value], writable: true}); |  | 
| 44       }, |  | 
| 45       get: function(key) { |  | 
| 46         var entry; |  | 
| 47         return (entry = key[this.name]) && entry[0] === key ? |  | 
| 48             entry[1] : undefined; |  | 
| 49       }, |  | 
| 50       delete: function(key) { |  | 
| 51         this.set(key, undefined); |  | 
| 52       } |  | 
| 53     }; |  | 
| 54 |  | 
| 55     window.WeakMap = WeakMap; |  | 
| 56   })(); |  | 
| 57 } |  | 
| 58 |  | 
| 59 (function(global) { |  | 
| 60 |  | 
| 61   var registrationsTable = new WeakMap(); |  | 
| 62 |  | 
| 63   // We use setImmediate or postMessage for our future callback. |  | 
| 64   var setImmediate = window.msSetImmediate; |  | 
| 65 |  | 
| 66   // Use post message to emulate setImmediate. |  | 
| 67   if (!setImmediate) { |  | 
| 68     var setImmediateQueue = []; |  | 
| 69     var sentinel = String(Math.random()); |  | 
| 70     window.addEventListener('message', function(e) { |  | 
| 71       if (e.data === sentinel) { |  | 
| 72         var queue = setImmediateQueue; |  | 
| 73         setImmediateQueue = []; |  | 
| 74         queue.forEach(function(func) { |  | 
| 75           func(); |  | 
| 76         }); |  | 
| 77       } |  | 
| 78     }); |  | 
| 79     setImmediate = function(func) { |  | 
| 80       setImmediateQueue.push(func); |  | 
| 81       window.postMessage(sentinel, '*'); |  | 
| 82     }; |  | 
| 83   } |  | 
| 84 |  | 
| 85   // This is used to ensure that we never schedule 2 callas to setImmediate |  | 
| 86   var isScheduled = false; |  | 
| 87 |  | 
| 88   // Keep track of observers that needs to be notified next time. |  | 
| 89   var scheduledObservers = []; |  | 
| 90 |  | 
| 91   /** |  | 
| 92    * Schedules |dispatchCallback| to be called in the future. |  | 
| 93    * @param {MutationObserver} observer |  | 
| 94    */ |  | 
| 95   function scheduleCallback(observer) { |  | 
| 96     scheduledObservers.push(observer); |  | 
| 97     if (!isScheduled) { |  | 
| 98       isScheduled = true; |  | 
| 99       setImmediate(dispatchCallbacks); |  | 
| 100     } |  | 
| 101   } |  | 
| 102 |  | 
| 103   function wrapIfNeeded(node) { |  | 
| 104     return window.ShadowDOMPolyfill && |  | 
| 105         window.ShadowDOMPolyfill.wrapIfNeeded(node) || |  | 
| 106         node; |  | 
| 107   } |  | 
| 108 |  | 
| 109   function dispatchCallbacks() { |  | 
| 110     // http://dom.spec.whatwg.org/#mutation-observers |  | 
| 111 |  | 
| 112     isScheduled = false; // Used to allow a new setImmediate call above. |  | 
| 113 |  | 
| 114     var observers = scheduledObservers; |  | 
| 115     scheduledObservers = []; |  | 
| 116     // Sort observers based on their creation UID (incremental). |  | 
| 117     observers.sort(function(o1, o2) { |  | 
| 118       return o1.uid_ - o2.uid_; |  | 
| 119     }); |  | 
| 120 |  | 
| 121     var anyNonEmpty = false; |  | 
| 122     observers.forEach(function(observer) { |  | 
| 123 |  | 
| 124       // 2.1, 2.2 |  | 
| 125       var queue = observer.takeRecords(); |  | 
| 126       // 2.3. Remove all transient registered observers whose observer is mo. |  | 
| 127       removeTransientObserversFor(observer); |  | 
| 128 |  | 
| 129       // 2.4 |  | 
| 130       if (queue.length) { |  | 
| 131         observer.callback_(queue, observer); |  | 
| 132         anyNonEmpty = true; |  | 
| 133       } |  | 
| 134     }); |  | 
| 135 |  | 
| 136     // 3. |  | 
| 137     if (anyNonEmpty) |  | 
| 138       dispatchCallbacks(); |  | 
| 139   } |  | 
| 140 |  | 
| 141   function removeTransientObserversFor(observer) { |  | 
| 142     observer.nodes_.forEach(function(node) { |  | 
| 143       var registrations = registrationsTable.get(node); |  | 
| 144       if (!registrations) |  | 
| 145         return; |  | 
| 146       registrations.forEach(function(registration) { |  | 
| 147         if (registration.observer === observer) |  | 
| 148           registration.removeTransientObservers(); |  | 
| 149       }); |  | 
| 150     }); |  | 
| 151   } |  | 
| 152 |  | 
| 153   /** |  | 
| 154    * This function is used for the "For each registered observer observer (with |  | 
| 155    * observer's options as options) in target's list of registered observers, |  | 
| 156    * run these substeps:" and the "For each ancestor ancestor of target, and for |  | 
| 157    * each registered observer observer (with options options) in ancestor's list |  | 
| 158    * of registered observers, run these substeps:" part of the algorithms. The |  | 
| 159    * |options.subtree| is checked to ensure that the callback is called |  | 
| 160    * correctly. |  | 
| 161    * |  | 
| 162    * @param {Node} target |  | 
| 163    * @param {function(MutationObserverInit):MutationRecord} callback |  | 
| 164    */ |  | 
| 165   function forEachAncestorAndObserverEnqueueRecord(target, callback) { |  | 
| 166     for (var node = target; node; node = node.parentNode) { |  | 
| 167       var registrations = registrationsTable.get(node); |  | 
| 168 |  | 
| 169       if (registrations) { |  | 
| 170         for (var j = 0; j < registrations.length; j++) { |  | 
| 171           var registration = registrations[j]; |  | 
| 172           var options = registration.options; |  | 
| 173 |  | 
| 174           // Only target ignores subtree. |  | 
| 175           if (node !== target && !options.subtree) |  | 
| 176             continue; |  | 
| 177 |  | 
| 178           var record = callback(options); |  | 
| 179           if (record) |  | 
| 180             registration.enqueue(record); |  | 
| 181         } |  | 
| 182       } |  | 
| 183     } |  | 
| 184   } |  | 
| 185 |  | 
| 186   var uidCounter = 0; |  | 
| 187 |  | 
| 188   /** |  | 
| 189    * The class that maps to the DOM MutationObserver interface. |  | 
| 190    * @param {Function} callback. |  | 
| 191    * @constructor |  | 
| 192    */ |  | 
| 193   function JsMutationObserver(callback) { |  | 
| 194     this.callback_ = callback; |  | 
| 195     this.nodes_ = []; |  | 
| 196     this.records_ = []; |  | 
| 197     this.uid_ = ++uidCounter; |  | 
| 198   } |  | 
| 199 |  | 
| 200   JsMutationObserver.prototype = { |  | 
| 201     observe: function(target, options) { |  | 
| 202       target = wrapIfNeeded(target); |  | 
| 203 |  | 
| 204       // 1.1 |  | 
| 205       if (!options.childList && !options.attributes && !options.characterData || |  | 
| 206 |  | 
| 207           // 1.2 |  | 
| 208           options.attributeOldValue && !options.attributes || |  | 
| 209 |  | 
| 210           // 1.3 |  | 
| 211           options.attributeFilter && options.attributeFilter.length && |  | 
| 212               !options.attributes || |  | 
| 213 |  | 
| 214           // 1.4 |  | 
| 215           options.characterDataOldValue && !options.characterData) { |  | 
| 216 |  | 
| 217         throw new SyntaxError(); |  | 
| 218       } |  | 
| 219 |  | 
| 220       var registrations = registrationsTable.get(target); |  | 
| 221       if (!registrations) |  | 
| 222         registrationsTable.set(target, registrations = []); |  | 
| 223 |  | 
| 224       // 2 |  | 
| 225       // If target's list of registered observers already includes a registered |  | 
| 226       // observer associated with the context object, replace that registered |  | 
| 227       // observer's options with options. |  | 
| 228       var registration; |  | 
| 229       for (var i = 0; i < registrations.length; i++) { |  | 
| 230         if (registrations[i].observer === this) { |  | 
| 231           registration = registrations[i]; |  | 
| 232           registration.removeListeners(); |  | 
| 233           registration.options = options; |  | 
| 234           break; |  | 
| 235         } |  | 
| 236       } |  | 
| 237 |  | 
| 238       // 3. |  | 
| 239       // Otherwise, add a new registered observer to target's list of registered |  | 
| 240       // observers with the context object as the observer and options as the |  | 
| 241       // options, and add target to context object's list of nodes on which it |  | 
| 242       // is registered. |  | 
| 243       if (!registration) { |  | 
| 244         registration = new Registration(this, target, options); |  | 
| 245         registrations.push(registration); |  | 
| 246         this.nodes_.push(target); |  | 
| 247       } |  | 
| 248 |  | 
| 249       registration.addListeners(); |  | 
| 250     }, |  | 
| 251 |  | 
| 252     disconnect: function() { |  | 
| 253       this.nodes_.forEach(function(node) { |  | 
| 254         var registrations = registrationsTable.get(node); |  | 
| 255         for (var i = 0; i < registrations.length; i++) { |  | 
| 256           var registration = registrations[i]; |  | 
| 257           if (registration.observer === this) { |  | 
| 258             registration.removeListeners(); |  | 
| 259             registrations.splice(i, 1); |  | 
| 260             // Each node can only have one registered observer associated with |  | 
| 261             // this observer. |  | 
| 262             break; |  | 
| 263           } |  | 
| 264         } |  | 
| 265       }, this); |  | 
| 266       this.records_ = []; |  | 
| 267     }, |  | 
| 268 |  | 
| 269     takeRecords: function() { |  | 
| 270       var copyOfRecords = this.records_; |  | 
| 271       this.records_ = []; |  | 
| 272       return copyOfRecords; |  | 
| 273     } |  | 
| 274   }; |  | 
| 275 |  | 
| 276   /** |  | 
| 277    * @param {string} type |  | 
| 278    * @param {Node} target |  | 
| 279    * @constructor |  | 
| 280    */ |  | 
| 281   function MutationRecord(type, target) { |  | 
| 282     this.type = type; |  | 
| 283     this.target = target; |  | 
| 284     this.addedNodes = []; |  | 
| 285     this.removedNodes = []; |  | 
| 286     this.previousSibling = null; |  | 
| 287     this.nextSibling = null; |  | 
| 288     this.attributeName = null; |  | 
| 289     this.attributeNamespace = null; |  | 
| 290     this.oldValue = null; |  | 
| 291   } |  | 
| 292 |  | 
| 293   function copyMutationRecord(original) { |  | 
| 294     var record = new MutationRecord(original.type, original.target); |  | 
| 295     record.addedNodes = original.addedNodes.slice(); |  | 
| 296     record.removedNodes = original.removedNodes.slice(); |  | 
| 297     record.previousSibling = original.previousSibling; |  | 
| 298     record.nextSibling = original.nextSibling; |  | 
| 299     record.attributeName = original.attributeName; |  | 
| 300     record.attributeNamespace = original.attributeNamespace; |  | 
| 301     record.oldValue = original.oldValue; |  | 
| 302     return record; |  | 
| 303   }; |  | 
| 304 |  | 
| 305   // We keep track of the two (possibly one) records used in a single mutation. |  | 
| 306   var currentRecord, recordWithOldValue; |  | 
| 307 |  | 
| 308   /** |  | 
| 309    * Creates a record without |oldValue| and caches it as |currentRecord| for |  | 
| 310    * later use. |  | 
| 311    * @param {string} oldValue |  | 
| 312    * @return {MutationRecord} |  | 
| 313    */ |  | 
| 314   function getRecord(type, target) { |  | 
| 315     return currentRecord = new MutationRecord(type, target); |  | 
| 316   } |  | 
| 317 |  | 
| 318   /** |  | 
| 319    * Gets or creates a record with |oldValue| based in the |currentRecord| |  | 
| 320    * @param {string} oldValue |  | 
| 321    * @return {MutationRecord} |  | 
| 322    */ |  | 
| 323   function getRecordWithOldValue(oldValue) { |  | 
| 324     if (recordWithOldValue) |  | 
| 325       return recordWithOldValue; |  | 
| 326     recordWithOldValue = copyMutationRecord(currentRecord); |  | 
| 327     recordWithOldValue.oldValue = oldValue; |  | 
| 328     return recordWithOldValue; |  | 
| 329   } |  | 
| 330 |  | 
| 331   function clearRecords() { |  | 
| 332     currentRecord = recordWithOldValue = undefined; |  | 
| 333   } |  | 
| 334 |  | 
| 335   /** |  | 
| 336    * @param {MutationRecord} record |  | 
| 337    * @return {boolean} Whether the record represents a record from the current |  | 
| 338    * mutation event. |  | 
| 339    */ |  | 
| 340   function recordRepresentsCurrentMutation(record) { |  | 
| 341     return record === recordWithOldValue || record === currentRecord; |  | 
| 342   } |  | 
| 343 |  | 
| 344   /** |  | 
| 345    * Selects which record, if any, to replace the last record in the queue. |  | 
| 346    * This returns |null| if no record should be replaced. |  | 
| 347    * |  | 
| 348    * @param {MutationRecord} lastRecord |  | 
| 349    * @param {MutationRecord} newRecord |  | 
| 350    * @param {MutationRecord} |  | 
| 351    */ |  | 
| 352   function selectRecord(lastRecord, newRecord) { |  | 
| 353     if (lastRecord === newRecord) |  | 
| 354       return lastRecord; |  | 
| 355 |  | 
| 356     // Check if the the record we are adding represents the same record. If |  | 
| 357     // so, we keep the one with the oldValue in it. |  | 
| 358     if (recordWithOldValue && recordRepresentsCurrentMutation(lastRecord)) |  | 
| 359       return recordWithOldValue; |  | 
| 360 |  | 
| 361     return null; |  | 
| 362   } |  | 
| 363 |  | 
| 364   /** |  | 
| 365    * Class used to represent a registered observer. |  | 
| 366    * @param {MutationObserver} observer |  | 
| 367    * @param {Node} target |  | 
| 368    * @param {MutationObserverInit} options |  | 
| 369    * @constructor |  | 
| 370    */ |  | 
| 371   function Registration(observer, target, options) { |  | 
| 372     this.observer = observer; |  | 
| 373     this.target = target; |  | 
| 374     this.options = options; |  | 
| 375     this.transientObservedNodes = []; |  | 
| 376   } |  | 
| 377 |  | 
| 378   Registration.prototype = { |  | 
| 379     enqueue: function(record) { |  | 
| 380       var records = this.observer.records_; |  | 
| 381       var length = records.length; |  | 
| 382 |  | 
| 383       // There are cases where we replace the last record with the new record. |  | 
| 384       // For example if the record represents the same mutation we need to use |  | 
| 385       // the one with the oldValue. If we get same record (this can happen as we |  | 
| 386       // walk up the tree) we ignore the new record. |  | 
| 387       if (records.length > 0) { |  | 
| 388         var lastRecord = records[length - 1]; |  | 
| 389         var recordToReplaceLast = selectRecord(lastRecord, record); |  | 
| 390         if (recordToReplaceLast) { |  | 
| 391           records[length - 1] = recordToReplaceLast; |  | 
| 392           return; |  | 
| 393         } |  | 
| 394       } else { |  | 
| 395         scheduleCallback(this.observer); |  | 
| 396       } |  | 
| 397 |  | 
| 398       records[length] = record; |  | 
| 399     }, |  | 
| 400 |  | 
| 401     addListeners: function() { |  | 
| 402       this.addListeners_(this.target); |  | 
| 403     }, |  | 
| 404 |  | 
| 405     addListeners_: function(node) { |  | 
| 406       var options = this.options; |  | 
| 407       if (options.attributes) |  | 
| 408         node.addEventListener('DOMAttrModified', this, true); |  | 
| 409 |  | 
| 410       if (options.characterData) |  | 
| 411         node.addEventListener('DOMCharacterDataModified', this, true); |  | 
| 412 |  | 
| 413       if (options.childList) |  | 
| 414         node.addEventListener('DOMNodeInserted', this, true); |  | 
| 415 |  | 
| 416       if (options.childList || options.subtree) |  | 
| 417         node.addEventListener('DOMNodeRemoved', this, true); |  | 
| 418     }, |  | 
| 419 |  | 
| 420     removeListeners: function() { |  | 
| 421       this.removeListeners_(this.target); |  | 
| 422     }, |  | 
| 423 |  | 
| 424     removeListeners_: function(node) { |  | 
| 425       var options = this.options; |  | 
| 426       if (options.attributes) |  | 
| 427         node.removeEventListener('DOMAttrModified', this, true); |  | 
| 428 |  | 
| 429       if (options.characterData) |  | 
| 430         node.removeEventListener('DOMCharacterDataModified', this, true); |  | 
| 431 |  | 
| 432       if (options.childList) |  | 
| 433         node.removeEventListener('DOMNodeInserted', this, true); |  | 
| 434 |  | 
| 435       if (options.childList || options.subtree) |  | 
| 436         node.removeEventListener('DOMNodeRemoved', this, true); |  | 
| 437     }, |  | 
| 438 |  | 
| 439     /** |  | 
| 440      * Adds a transient observer on node. The transient observer gets removed |  | 
| 441      * next time we deliver the change records. |  | 
| 442      * @param {Node} node |  | 
| 443      */ |  | 
| 444     addTransientObserver: function(node) { |  | 
| 445       // Don't add transient observers on the target itself. We already have all |  | 
| 446       // the required listeners set up on the target. |  | 
| 447       if (node === this.target) |  | 
| 448         return; |  | 
| 449 |  | 
| 450       this.addListeners_(node); |  | 
| 451       this.transientObservedNodes.push(node); |  | 
| 452       var registrations = registrationsTable.get(node); |  | 
| 453       if (!registrations) |  | 
| 454         registrationsTable.set(node, registrations = []); |  | 
| 455 |  | 
| 456       // We know that registrations does not contain this because we already |  | 
| 457       // checked if node === this.target. |  | 
| 458       registrations.push(this); |  | 
| 459     }, |  | 
| 460 |  | 
| 461     removeTransientObservers: function() { |  | 
| 462       var transientObservedNodes = this.transientObservedNodes; |  | 
| 463       this.transientObservedNodes = []; |  | 
| 464 |  | 
| 465       transientObservedNodes.forEach(function(node) { |  | 
| 466         // Transient observers are never added to the target. |  | 
| 467         this.removeListeners_(node); |  | 
| 468 |  | 
| 469         var registrations = registrationsTable.get(node); |  | 
| 470         for (var i = 0; i < registrations.length; i++) { |  | 
| 471           if (registrations[i] === this) { |  | 
| 472             registrations.splice(i, 1); |  | 
| 473             // Each node can only have one registered observer associated with |  | 
| 474             // this observer. |  | 
| 475             break; |  | 
| 476           } |  | 
| 477         } |  | 
| 478       }, this); |  | 
| 479     }, |  | 
| 480 |  | 
| 481     handleEvent: function(e) { |  | 
| 482       // Stop propagation since we are managing the propagation manually. |  | 
| 483       // This means that other mutation events on the page will not work |  | 
| 484       // correctly but that is by design. |  | 
| 485       e.stopImmediatePropagation(); |  | 
| 486 |  | 
| 487       switch (e.type) { |  | 
| 488         case 'DOMAttrModified': |  | 
| 489           // http://dom.spec.whatwg.org/#concept-mo-queue-attributes |  | 
| 490 |  | 
| 491           var name = e.attrName; |  | 
| 492           var namespace = e.relatedNode.namespaceURI; |  | 
| 493           var target = e.target; |  | 
| 494 |  | 
| 495           // 1. |  | 
| 496           var record = new getRecord('attributes', target); |  | 
| 497           record.attributeName = name; |  | 
| 498           record.attributeNamespace = namespace; |  | 
| 499 |  | 
| 500           // 2. |  | 
| 501           var oldValue = |  | 
| 502               e.attrChange === MutationEvent.ADDITION ? null : e.prevValue; |  | 
| 503 |  | 
| 504           forEachAncestorAndObserverEnqueueRecord(target, function(options) { |  | 
| 505             // 3.1, 4.2 |  | 
| 506             if (!options.attributes) |  | 
| 507               return; |  | 
| 508 |  | 
| 509             // 3.2, 4.3 |  | 
| 510             if (options.attributeFilter && options.attributeFilter.length && |  | 
| 511                 options.attributeFilter.indexOf(name) === -1 && |  | 
| 512                 options.attributeFilter.indexOf(namespace) === -1) { |  | 
| 513               return; |  | 
| 514             } |  | 
| 515             // 3.3, 4.4 |  | 
| 516             if (options.attributeOldValue) |  | 
| 517               return getRecordWithOldValue(oldValue); |  | 
| 518 |  | 
| 519             // 3.4, 4.5 |  | 
| 520             return record; |  | 
| 521           }); |  | 
| 522 |  | 
| 523           break; |  | 
| 524 |  | 
| 525         case 'DOMCharacterDataModified': |  | 
| 526           // http://dom.spec.whatwg.org/#concept-mo-queue-characterdata |  | 
| 527           var target = e.target; |  | 
| 528 |  | 
| 529           // 1. |  | 
| 530           var record = getRecord('characterData', target); |  | 
| 531 |  | 
| 532           // 2. |  | 
| 533           var oldValue = e.prevValue; |  | 
| 534 |  | 
| 535 |  | 
| 536           forEachAncestorAndObserverEnqueueRecord(target, function(options) { |  | 
| 537             // 3.1, 4.2 |  | 
| 538             if (!options.characterData) |  | 
| 539               return; |  | 
| 540 |  | 
| 541             // 3.2, 4.3 |  | 
| 542             if (options.characterDataOldValue) |  | 
| 543               return getRecordWithOldValue(oldValue); |  | 
| 544 |  | 
| 545             // 3.3, 4.4 |  | 
| 546             return record; |  | 
| 547           }); |  | 
| 548 |  | 
| 549           break; |  | 
| 550 |  | 
| 551         case 'DOMNodeRemoved': |  | 
| 552           this.addTransientObserver(e.target); |  | 
| 553           // Fall through. |  | 
| 554         case 'DOMNodeInserted': |  | 
| 555           // http://dom.spec.whatwg.org/#concept-mo-queue-childlist |  | 
| 556           var target = e.relatedNode; |  | 
| 557           var changedNode = e.target; |  | 
| 558           var addedNodes, removedNodes; |  | 
| 559           if (e.type === 'DOMNodeInserted') { |  | 
| 560             addedNodes = [changedNode]; |  | 
| 561             removedNodes = []; |  | 
| 562           } else { |  | 
| 563 |  | 
| 564             addedNodes = []; |  | 
| 565             removedNodes = [changedNode]; |  | 
| 566           } |  | 
| 567           var previousSibling = changedNode.previousSibling; |  | 
| 568           var nextSibling = changedNode.nextSibling; |  | 
| 569 |  | 
| 570           // 1. |  | 
| 571           var record = getRecord('childList', target); |  | 
| 572           record.addedNodes = addedNodes; |  | 
| 573           record.removedNodes = removedNodes; |  | 
| 574           record.previousSibling = previousSibling; |  | 
| 575           record.nextSibling = nextSibling; |  | 
| 576 |  | 
| 577           forEachAncestorAndObserverEnqueueRecord(target, function(options) { |  | 
| 578             // 2.1, 3.2 |  | 
| 579             if (!options.childList) |  | 
| 580               return; |  | 
| 581 |  | 
| 582             // 2.2, 3.3 |  | 
| 583             return record; |  | 
| 584           }); |  | 
| 585 |  | 
| 586       } |  | 
| 587 |  | 
| 588       clearRecords(); |  | 
| 589     } |  | 
| 590   }; |  | 
| 591 |  | 
| 592   global.JsMutationObserver = JsMutationObserver; |  | 
| 593 |  | 
| 594   if (!global.MutationObserver) |  | 
| 595     global.MutationObserver = JsMutationObserver; |  | 
| 596 |  | 
| 597 |  | 
| 598 })(this); |  | 
| 599 |  | 
| 600 window.CustomElements = window.CustomElements || {flags:{}}; |  | 
| 601 (function(scope){ |  | 
| 602 |  | 
| 603 var logFlags = window.logFlags || {}; |  | 
| 604 var IMPORT_LINK_TYPE = window.HTMLImports ? HTMLImports.IMPORT_LINK_TYPE : 'none
      '; |  | 
| 605 |  | 
| 606 // walk the subtree rooted at node, applying 'find(element, data)' function |  | 
| 607 // to each element |  | 
| 608 // if 'find' returns true for 'element', do not search element's subtree |  | 
| 609 function findAll(node, find, data) { |  | 
| 610   var e = node.firstElementChild; |  | 
| 611   if (!e) { |  | 
| 612     e = node.firstChild; |  | 
| 613     while (e && e.nodeType !== Node.ELEMENT_NODE) { |  | 
| 614       e = e.nextSibling; |  | 
| 615     } |  | 
| 616   } |  | 
| 617   while (e) { |  | 
| 618     if (find(e, data) !== true) { |  | 
| 619       findAll(e, find, data); |  | 
| 620     } |  | 
| 621     e = e.nextElementSibling; |  | 
| 622   } |  | 
| 623   return null; |  | 
| 624 } |  | 
| 625 |  | 
| 626 // walk all shadowRoots on a given node. |  | 
| 627 function forRoots(node, cb) { |  | 
| 628   var root = node.shadowRoot; |  | 
| 629   while(root) { |  | 
| 630     forSubtree(root, cb); |  | 
| 631     root = root.olderShadowRoot; |  | 
| 632   } |  | 
| 633 } |  | 
| 634 |  | 
| 635 // walk the subtree rooted at node, including descent into shadow-roots, |  | 
| 636 // applying 'cb' to each element |  | 
| 637 function forSubtree(node, cb) { |  | 
| 638   //logFlags.dom && node.childNodes && node.childNodes.length && console.group('
      subTree: ', node); |  | 
| 639   findAll(node, function(e) { |  | 
| 640     if (cb(e)) { |  | 
| 641       return true; |  | 
| 642     } |  | 
| 643     forRoots(e, cb); |  | 
| 644   }); |  | 
| 645   forRoots(node, cb); |  | 
| 646   //logFlags.dom && node.childNodes && node.childNodes.length && console.groupEn
      d(); |  | 
| 647 } |  | 
| 648 |  | 
| 649 // manage lifecycle on added node |  | 
| 650 function added(node) { |  | 
| 651   if (upgrade(node)) { |  | 
| 652     insertedNode(node); |  | 
| 653     return true; |  | 
| 654   } |  | 
| 655   inserted(node); |  | 
| 656 } |  | 
| 657 |  | 
| 658 // manage lifecycle on added node's subtree only |  | 
| 659 function addedSubtree(node) { |  | 
| 660   forSubtree(node, function(e) { |  | 
| 661     if (added(e)) { |  | 
| 662       return true; |  | 
| 663     } |  | 
| 664   }); |  | 
| 665 } |  | 
| 666 |  | 
| 667 // manage lifecycle on added node and it's subtree |  | 
| 668 function addedNode(node) { |  | 
| 669   return added(node) || addedSubtree(node); |  | 
| 670 } |  | 
| 671 |  | 
| 672 // upgrade custom elements at node, if applicable |  | 
| 673 function upgrade(node) { |  | 
| 674   if (!node.__upgraded__ && node.nodeType === Node.ELEMENT_NODE) { |  | 
| 675     var type = node.getAttribute('is') || node.localName; |  | 
| 676     var definition = scope.registry[type]; |  | 
| 677     if (definition) { |  | 
| 678       logFlags.dom && console.group('upgrade:', node.localName); |  | 
| 679       scope.upgrade(node); |  | 
| 680       logFlags.dom && console.groupEnd(); |  | 
| 681       return true; |  | 
| 682     } |  | 
| 683   } |  | 
| 684 } |  | 
| 685 |  | 
| 686 function insertedNode(node) { |  | 
| 687   inserted(node); |  | 
| 688   if (inDocument(node)) { |  | 
| 689     forSubtree(node, function(e) { |  | 
| 690       inserted(e); |  | 
| 691     }); |  | 
| 692   } |  | 
| 693 } |  | 
| 694 |  | 
| 695 |  | 
| 696 // TODO(sorvell): on platforms without MutationObserver, mutations may not be |  | 
| 697 // reliable and therefore attached/detached are not reliable. |  | 
| 698 // To make these callbacks less likely to fail, we defer all inserts and removes |  | 
| 699 // to give a chance for elements to be inserted into dom. |  | 
| 700 // This ensures attachedCallback fires for elements that are created and |  | 
| 701 // immediately added to dom. |  | 
| 702 var hasPolyfillMutations = (!window.MutationObserver || |  | 
| 703     (window.MutationObserver === window.JsMutationObserver)); |  | 
| 704 scope.hasPolyfillMutations = hasPolyfillMutations; |  | 
| 705 |  | 
| 706 var isPendingMutations = false; |  | 
| 707 var pendingMutations = []; |  | 
| 708 function deferMutation(fn) { |  | 
| 709   pendingMutations.push(fn); |  | 
| 710   if (!isPendingMutations) { |  | 
| 711     isPendingMutations = true; |  | 
| 712     var async = (window.Platform && window.Platform.endOfMicrotask) || |  | 
| 713         setTimeout; |  | 
| 714     async(takeMutations); |  | 
| 715   } |  | 
| 716 } |  | 
| 717 |  | 
| 718 function takeMutations() { |  | 
| 719   isPendingMutations = false; |  | 
| 720   var $p = pendingMutations; |  | 
| 721   for (var i=0, l=$p.length, p; (i<l) && (p=$p[i]); i++) { |  | 
| 722     p(); |  | 
| 723   } |  | 
| 724   pendingMutations = []; |  | 
| 725 } |  | 
| 726 |  | 
| 727 function inserted(element) { |  | 
| 728   if (hasPolyfillMutations) { |  | 
| 729     deferMutation(function() { |  | 
| 730       _inserted(element); |  | 
| 731     }); |  | 
| 732   } else { |  | 
| 733     _inserted(element); |  | 
| 734   } |  | 
| 735 } |  | 
| 736 |  | 
| 737 // TODO(sjmiles): if there are descents into trees that can never have inDocumen
      t(*) true, fix this |  | 
| 738 function _inserted(element) { |  | 
| 739   // TODO(sjmiles): it's possible we were inserted and removed in the space |  | 
| 740   // of one microtask, in which case we won't be 'inDocument' here |  | 
| 741   // But there are other cases where we are testing for inserted without |  | 
| 742   // specific knowledge of mutations, and must test 'inDocument' to determine |  | 
| 743   // whether to call inserted |  | 
| 744   // If we can factor these cases into separate code paths we can have |  | 
| 745   // better diagnostics. |  | 
| 746   // TODO(sjmiles): when logging, do work on all custom elements so we can |  | 
| 747   // track behavior even when callbacks not defined |  | 
| 748   //console.log('inserted: ', element.localName); |  | 
| 749   if (element.attachedCallback || element.detachedCallback || (element.__upgrade
      d__ && logFlags.dom)) { |  | 
| 750     logFlags.dom && console.group('inserted:', element.localName); |  | 
| 751     if (inDocument(element)) { |  | 
| 752       element.__inserted = (element.__inserted || 0) + 1; |  | 
| 753       // if we are in a 'removed' state, bluntly adjust to an 'inserted' state |  | 
| 754       if (element.__inserted < 1) { |  | 
| 755         element.__inserted = 1; |  | 
| 756       } |  | 
| 757       // if we are 'over inserted', squelch the callback |  | 
| 758       if (element.__inserted > 1) { |  | 
| 759         logFlags.dom && console.warn('inserted:', element.localName, |  | 
| 760           'insert/remove count:', element.__inserted) |  | 
| 761       } else if (element.attachedCallback) { |  | 
| 762         logFlags.dom && console.log('inserted:', element.localName); |  | 
| 763         element.attachedCallback(); |  | 
| 764       } |  | 
| 765     } |  | 
| 766     logFlags.dom && console.groupEnd(); |  | 
| 767   } |  | 
| 768 } |  | 
| 769 |  | 
| 770 function removedNode(node) { |  | 
| 771   removed(node); |  | 
| 772   forSubtree(node, function(e) { |  | 
| 773     removed(e); |  | 
| 774   }); |  | 
| 775 } |  | 
| 776 |  | 
| 777 function removed(element) { |  | 
| 778   if (hasPolyfillMutations) { |  | 
| 779     deferMutation(function() { |  | 
| 780       _removed(element); |  | 
| 781     }); |  | 
| 782   } else { |  | 
| 783     _removed(element); |  | 
| 784   } |  | 
| 785 } |  | 
| 786 |  | 
| 787 function _removed(element) { |  | 
| 788   // TODO(sjmiles): temporary: do work on all custom elements so we can track |  | 
| 789   // behavior even when callbacks not defined |  | 
| 790   if (element.attachedCallback || element.detachedCallback || (element.__upgrade
      d__ && logFlags.dom)) { |  | 
| 791     logFlags.dom && console.group('removed:', element.localName); |  | 
| 792     if (!inDocument(element)) { |  | 
| 793       element.__inserted = (element.__inserted || 0) - 1; |  | 
| 794       // if we are in a 'inserted' state, bluntly adjust to an 'removed' state |  | 
| 795       if (element.__inserted > 0) { |  | 
| 796         element.__inserted = 0; |  | 
| 797       } |  | 
| 798       // if we are 'over removed', squelch the callback |  | 
| 799       if (element.__inserted < 0) { |  | 
| 800         logFlags.dom && console.warn('removed:', element.localName, |  | 
| 801             'insert/remove count:', element.__inserted) |  | 
| 802       } else if (element.detachedCallback) { |  | 
| 803         element.detachedCallback(); |  | 
| 804       } |  | 
| 805     } |  | 
| 806     logFlags.dom && console.groupEnd(); |  | 
| 807   } |  | 
| 808 } |  | 
| 809 |  | 
| 810 // SD polyfill intrustion due mainly to the fact that 'document' |  | 
| 811 // is not entirely wrapped |  | 
| 812 function wrapIfNeeded(node) { |  | 
| 813   return window.ShadowDOMPolyfill ? ShadowDOMPolyfill.wrapIfNeeded(node) |  | 
| 814       : node; |  | 
| 815 } |  | 
| 816 |  | 
| 817 function inDocument(element) { |  | 
| 818   var p = element; |  | 
| 819   var doc = wrapIfNeeded(document); |  | 
| 820   while (p) { |  | 
| 821     if (p == doc) { |  | 
| 822       return true; |  | 
| 823     } |  | 
| 824     p = p.parentNode || p.host; |  | 
| 825   } |  | 
| 826 } |  | 
| 827 |  | 
| 828 function watchShadow(node) { |  | 
| 829   if (node.shadowRoot && !node.shadowRoot.__watched) { |  | 
| 830     logFlags.dom && console.log('watching shadow-root for: ', node.localName); |  | 
| 831     // watch all unwatched roots... |  | 
| 832     var root = node.shadowRoot; |  | 
| 833     while (root) { |  | 
| 834       watchRoot(root); |  | 
| 835       root = root.olderShadowRoot; |  | 
| 836     } |  | 
| 837   } |  | 
| 838 } |  | 
| 839 |  | 
| 840 function watchRoot(root) { |  | 
| 841   if (!root.__watched) { |  | 
| 842     observe(root); |  | 
| 843     root.__watched = true; |  | 
| 844   } |  | 
| 845 } |  | 
| 846 |  | 
| 847 function handler(mutations) { |  | 
| 848   // |  | 
| 849   if (logFlags.dom) { |  | 
| 850     var mx = mutations[0]; |  | 
| 851     if (mx && mx.type === 'childList' && mx.addedNodes) { |  | 
| 852         if (mx.addedNodes) { |  | 
| 853           var d = mx.addedNodes[0]; |  | 
| 854           while (d && d !== document && !d.host) { |  | 
| 855             d = d.parentNode; |  | 
| 856           } |  | 
| 857           var u = d && (d.URL || d._URL || (d.host && d.host.localName)) || ''; |  | 
| 858           u = u.split('/?').shift().split('/').pop(); |  | 
| 859         } |  | 
| 860     } |  | 
| 861     console.group('mutations (%d) [%s]', mutations.length, u || ''); |  | 
| 862   } |  | 
| 863   // |  | 
| 864   mutations.forEach(function(mx) { |  | 
| 865     //logFlags.dom && console.group('mutation'); |  | 
| 866     if (mx.type === 'childList') { |  | 
| 867       forEach(mx.addedNodes, function(n) { |  | 
| 868         //logFlags.dom && console.log(n.localName); |  | 
| 869         if (!n.localName) { |  | 
| 870           return; |  | 
| 871         } |  | 
| 872         // nodes added may need lifecycle management |  | 
| 873         addedNode(n); |  | 
| 874       }); |  | 
| 875       // removed nodes may need lifecycle management |  | 
| 876       forEach(mx.removedNodes, function(n) { |  | 
| 877         //logFlags.dom && console.log(n.localName); |  | 
| 878         if (!n.localName) { |  | 
| 879           return; |  | 
| 880         } |  | 
| 881         removedNode(n); |  | 
| 882       }); |  | 
| 883     } |  | 
| 884     //logFlags.dom && console.groupEnd(); |  | 
| 885   }); |  | 
| 886   logFlags.dom && console.groupEnd(); |  | 
| 887 }; |  | 
| 888 |  | 
| 889 var observer = new MutationObserver(handler); |  | 
| 890 |  | 
| 891 function takeRecords() { |  | 
| 892   // TODO(sjmiles): ask Raf why we have to call handler ourselves |  | 
| 893   handler(observer.takeRecords()); |  | 
| 894   takeMutations(); |  | 
| 895 } |  | 
| 896 |  | 
| 897 var forEach = Array.prototype.forEach.call.bind(Array.prototype.forEach); |  | 
| 898 |  | 
| 899 function observe(inRoot) { |  | 
| 900   observer.observe(inRoot, {childList: true, subtree: true}); |  | 
| 901 } |  | 
| 902 |  | 
| 903 function observeDocument(doc) { |  | 
| 904   observe(doc); |  | 
| 905 } |  | 
| 906 |  | 
| 907 function upgradeDocument(doc) { |  | 
| 908   logFlags.dom && console.group('upgradeDocument: ', (doc.baseURI).split('/').po
      p()); |  | 
| 909   addedNode(doc); |  | 
| 910   logFlags.dom && console.groupEnd(); |  | 
| 911 } |  | 
| 912 |  | 
| 913 function upgradeDocumentTree(doc) { |  | 
| 914   doc = wrapIfNeeded(doc); |  | 
| 915   upgradeDocument(doc); |  | 
| 916   //console.log('upgradeDocumentTree: ', (doc.baseURI).split('/').pop()); |  | 
| 917   // upgrade contained imported documents |  | 
| 918   var imports = doc.querySelectorAll('link[rel=' + IMPORT_LINK_TYPE + ']'); |  | 
| 919   for (var i=0, l=imports.length, n; (i<l) && (n=imports[i]); i++) { |  | 
| 920     if (n.import && n.import.__parsed) { |  | 
| 921       upgradeDocumentTree(n.import); |  | 
| 922     } |  | 
| 923   } |  | 
| 924 } |  | 
| 925 |  | 
| 926 // exports |  | 
| 927 scope.IMPORT_LINK_TYPE = IMPORT_LINK_TYPE; |  | 
| 928 scope.watchShadow = watchShadow; |  | 
| 929 scope.upgradeDocumentTree = upgradeDocumentTree; |  | 
| 930 scope.upgradeAll = addedNode; |  | 
| 931 scope.upgradeSubtree = addedSubtree; |  | 
| 932 |  | 
| 933 scope.observeDocument = observeDocument; |  | 
| 934 scope.upgradeDocument = upgradeDocument; |  | 
| 935 |  | 
| 936 scope.takeRecords = takeRecords; |  | 
| 937 |  | 
| 938 })(window.CustomElements); |  | 
| 939 |  | 
| 940 /** |  | 
| 941  * Implements `document.register` |  | 
| 942  * @module CustomElements |  | 
| 943 */ |  | 
| 944 |  | 
| 945 /** |  | 
| 946  * Polyfilled extensions to the `document` object. |  | 
| 947  * @class Document |  | 
| 948 */ |  | 
| 949 |  | 
| 950 (function(scope) { |  | 
| 951 |  | 
| 952 // imports |  | 
| 953 |  | 
| 954 if (!scope) { |  | 
| 955   scope = window.CustomElements = {flags:{}}; |  | 
| 956 } |  | 
| 957 var flags = scope.flags; |  | 
| 958 |  | 
| 959 // native document.registerElement? |  | 
| 960 |  | 
| 961 var hasNative = Boolean(document.registerElement); |  | 
| 962 // TODO(sorvell): See https://github.com/Polymer/polymer/issues/399 |  | 
| 963 // we'll address this by defaulting to CE polyfill in the presence of the SD |  | 
| 964 // polyfill. This will avoid spamming excess attached/detached callbacks. |  | 
| 965 // If there is a compelling need to run CE native with SD polyfill, |  | 
| 966 // we'll need to fix this issue. |  | 
| 967 var useNative = !flags.register && hasNative && !window.ShadowDOMPolyfill; |  | 
| 968 |  | 
| 969 if (useNative) { |  | 
| 970 |  | 
| 971   // stub |  | 
| 972   var nop = function() {}; |  | 
| 973 |  | 
| 974   // exports |  | 
| 975   scope.registry = {}; |  | 
| 976   scope.upgradeElement = nop; |  | 
| 977 |  | 
| 978   scope.watchShadow = nop; |  | 
| 979   scope.upgrade = nop; |  | 
| 980   scope.upgradeAll = nop; |  | 
| 981   scope.upgradeSubtree = nop; |  | 
| 982   scope.observeDocument = nop; |  | 
| 983   scope.upgradeDocument = nop; |  | 
| 984   scope.upgradeDocumentTree = nop; |  | 
| 985   scope.takeRecords = nop; |  | 
| 986 |  | 
| 987 } else { |  | 
| 988 |  | 
| 989   /** |  | 
| 990    * Registers a custom tag name with the document. |  | 
| 991    * |  | 
| 992    * When a registered element is created, a `readyCallback` method is called |  | 
| 993    * in the scope of the element. The `readyCallback` method can be specified on |  | 
| 994    * either `options.prototype` or `options.lifecycle` with the latter taking |  | 
| 995    * precedence. |  | 
| 996    * |  | 
| 997    * @method register |  | 
| 998    * @param {String} name The tag name to register. Must include a dash ('-'), |  | 
| 999    *    for example 'x-component'. |  | 
| 1000    * @param {Object} options |  | 
| 1001    *    @param {String} [options.extends] |  | 
| 1002    *      (_off spec_) Tag name of an element to extend (or blank for a new |  | 
| 1003    *      element). This parameter is not part of the specification, but instead |  | 
| 1004    *      is a hint for the polyfill because the extendee is difficult to infer. |  | 
| 1005    *      Remember that the input prototype must chain to the extended element's |  | 
| 1006    *      prototype (or HTMLElement.prototype) regardless of the value of |  | 
| 1007    *      `extends`. |  | 
| 1008    *    @param {Object} options.prototype The prototype to use for the new |  | 
| 1009    *      element. The prototype must inherit from HTMLElement. |  | 
| 1010    *    @param {Object} [options.lifecycle] |  | 
| 1011    *      Callbacks that fire at important phases in the life of the custom |  | 
| 1012    *      element. |  | 
| 1013    * |  | 
| 1014    * @example |  | 
| 1015    *      FancyButton = document.registerElement("fancy-button", { |  | 
| 1016    *        extends: 'button', |  | 
| 1017    *        prototype: Object.create(HTMLButtonElement.prototype, { |  | 
| 1018    *          readyCallback: { |  | 
| 1019    *            value: function() { |  | 
| 1020    *              console.log("a fancy-button was created", |  | 
| 1021    *            } |  | 
| 1022    *          } |  | 
| 1023    *        }) |  | 
| 1024    *      }); |  | 
| 1025    * @return {Function} Constructor for the newly registered type. |  | 
| 1026    */ |  | 
| 1027   function register(name, options) { |  | 
| 1028     //console.warn('document.registerElement("' + name + '", ', options, ')'); |  | 
| 1029     // construct a defintion out of options |  | 
| 1030     // TODO(sjmiles): probably should clone options instead of mutating it |  | 
| 1031     var definition = options || {}; |  | 
| 1032     if (!name) { |  | 
| 1033       // TODO(sjmiles): replace with more appropriate error (EricB can probably |  | 
| 1034       // offer guidance) |  | 
| 1035       throw new Error('document.registerElement: first argument `name` must not 
      be empty'); |  | 
| 1036     } |  | 
| 1037     if (name.indexOf('-') < 0) { |  | 
| 1038       // TODO(sjmiles): replace with more appropriate error (EricB can probably |  | 
| 1039       // offer guidance) |  | 
| 1040       throw new Error('document.registerElement: first argument (\'name\') must 
      contain a dash (\'-\'). Argument provided was \'' + String(name) + '\'.'); |  | 
| 1041     } |  | 
| 1042     // elements may only be registered once |  | 
| 1043     if (getRegisteredDefinition(name)) { |  | 
| 1044       throw new Error('DuplicateDefinitionError: a type with name \'' + String(n
      ame) + '\' is already registered'); |  | 
| 1045     } |  | 
| 1046     // must have a prototype, default to an extension of HTMLElement |  | 
| 1047     // TODO(sjmiles): probably should throw if no prototype, check spec |  | 
| 1048     if (!definition.prototype) { |  | 
| 1049       // TODO(sjmiles): replace with more appropriate error (EricB can probably |  | 
| 1050       // offer guidance) |  | 
| 1051       throw new Error('Options missing required prototype property'); |  | 
| 1052     } |  | 
| 1053     // record name |  | 
| 1054     definition.__name = name.toLowerCase(); |  | 
| 1055     // ensure a lifecycle object so we don't have to null test it |  | 
| 1056     definition.lifecycle = definition.lifecycle || {}; |  | 
| 1057     // build a list of ancestral custom elements (for native base detection) |  | 
| 1058     // TODO(sjmiles): we used to need to store this, but current code only |  | 
| 1059     // uses it in 'resolveTagName': it should probably be inlined |  | 
| 1060     definition.ancestry = ancestry(definition.extends); |  | 
| 1061     // extensions of native specializations of HTMLElement require localName |  | 
| 1062     // to remain native, and use secondary 'is' specifier for extension type |  | 
| 1063     resolveTagName(definition); |  | 
| 1064     // some platforms require modifications to the user-supplied prototype |  | 
| 1065     // chain |  | 
| 1066     resolvePrototypeChain(definition); |  | 
| 1067     // overrides to implement attributeChanged callback |  | 
| 1068     overrideAttributeApi(definition.prototype); |  | 
| 1069     // 7.1.5: Register the DEFINITION with DOCUMENT |  | 
| 1070     registerDefinition(definition.__name, definition); |  | 
| 1071     // 7.1.7. Run custom element constructor generation algorithm with PROTOTYPE |  | 
| 1072     // 7.1.8. Return the output of the previous step. |  | 
| 1073     definition.ctor = generateConstructor(definition); |  | 
| 1074     definition.ctor.prototype = definition.prototype; |  | 
| 1075     // force our .constructor to be our actual constructor |  | 
| 1076     definition.prototype.constructor = definition.ctor; |  | 
| 1077     // if initial parsing is complete |  | 
| 1078     if (scope.ready || scope.performedInitialDocumentUpgrade) { |  | 
| 1079       // upgrade any pre-existing nodes of this type |  | 
| 1080       scope.upgradeDocumentTree(document); |  | 
| 1081     } |  | 
| 1082     return definition.ctor; |  | 
| 1083   } |  | 
| 1084 |  | 
| 1085   function ancestry(extnds) { |  | 
| 1086     var extendee = getRegisteredDefinition(extnds); |  | 
| 1087     if (extendee) { |  | 
| 1088       return ancestry(extendee.extends).concat([extendee]); |  | 
| 1089     } |  | 
| 1090     return []; |  | 
| 1091   } |  | 
| 1092 |  | 
| 1093   function resolveTagName(definition) { |  | 
| 1094     // if we are explicitly extending something, that thing is our |  | 
| 1095     // baseTag, unless it represents a custom component |  | 
| 1096     var baseTag = definition.extends; |  | 
| 1097     // if our ancestry includes custom components, we only have a |  | 
| 1098     // baseTag if one of them does |  | 
| 1099     for (var i=0, a; (a=definition.ancestry[i]); i++) { |  | 
| 1100       baseTag = a.is && a.tag; |  | 
| 1101     } |  | 
| 1102     // our tag is our baseTag, if it exists, and otherwise just our name |  | 
| 1103     definition.tag = baseTag || definition.__name; |  | 
| 1104     if (baseTag) { |  | 
| 1105       // if there is a base tag, use secondary 'is' specifier |  | 
| 1106       definition.is = definition.__name; |  | 
| 1107     } |  | 
| 1108   } |  | 
| 1109 |  | 
| 1110   function resolvePrototypeChain(definition) { |  | 
| 1111     // if we don't support __proto__ we need to locate the native level |  | 
| 1112     // prototype for precise mixing in |  | 
| 1113     if (!Object.__proto__) { |  | 
| 1114       // default prototype |  | 
| 1115       var nativePrototype = HTMLElement.prototype; |  | 
| 1116       // work out prototype when using type-extension |  | 
| 1117       if (definition.is) { |  | 
| 1118         var inst = document.createElement(definition.tag); |  | 
| 1119         nativePrototype = Object.getPrototypeOf(inst); |  | 
| 1120       } |  | 
| 1121       // ensure __proto__ reference is installed at each point on the prototype |  | 
| 1122       // chain. |  | 
| 1123       // NOTE: On platforms without __proto__, a mixin strategy is used instead |  | 
| 1124       // of prototype swizzling. In this case, this generated __proto__ provides |  | 
| 1125       // limited support for prototype traversal. |  | 
| 1126       var proto = definition.prototype, ancestor; |  | 
| 1127       while (proto && (proto !== nativePrototype)) { |  | 
| 1128         var ancestor = Object.getPrototypeOf(proto); |  | 
| 1129         proto.__proto__ = ancestor; |  | 
| 1130         proto = ancestor; |  | 
| 1131       } |  | 
| 1132     } |  | 
| 1133     // cache this in case of mixin |  | 
| 1134     definition.native = nativePrototype; |  | 
| 1135   } |  | 
| 1136 |  | 
| 1137   // SECTION 4 |  | 
| 1138 |  | 
| 1139   function instantiate(definition) { |  | 
| 1140     // 4.a.1. Create a new object that implements PROTOTYPE |  | 
| 1141     // 4.a.2. Let ELEMENT by this new object |  | 
| 1142     // |  | 
| 1143     // the custom element instantiation algorithm must also ensure that the |  | 
| 1144     // output is a valid DOM element with the proper wrapper in place. |  | 
| 1145     // |  | 
| 1146     return upgrade(domCreateElement(definition.tag), definition); |  | 
| 1147   } |  | 
| 1148 |  | 
| 1149   function upgrade(element, definition) { |  | 
| 1150     // some definitions specify an 'is' attribute |  | 
| 1151     if (definition.is) { |  | 
| 1152       element.setAttribute('is', definition.is); |  | 
| 1153     } |  | 
| 1154     // remove 'unresolved' attr, which is a standin for :unresolved. |  | 
| 1155     element.removeAttribute('unresolved'); |  | 
| 1156     // make 'element' implement definition.prototype |  | 
| 1157     implement(element, definition); |  | 
| 1158     // flag as upgraded |  | 
| 1159     element.__upgraded__ = true; |  | 
| 1160     // lifecycle management |  | 
| 1161     created(element); |  | 
| 1162     // there should never be a shadow root on element at this point |  | 
| 1163     // we require child nodes be upgraded before `created` |  | 
| 1164     scope.upgradeSubtree(element); |  | 
| 1165     // OUTPUT |  | 
| 1166     return element; |  | 
| 1167   } |  | 
| 1168 |  | 
| 1169   function implement(element, definition) { |  | 
| 1170     // prototype swizzling is best |  | 
| 1171     if (Object.__proto__) { |  | 
| 1172       element.__proto__ = definition.prototype; |  | 
| 1173     } else { |  | 
| 1174       // where above we can re-acquire inPrototype via |  | 
| 1175       // getPrototypeOf(Element), we cannot do so when |  | 
| 1176       // we use mixin, so we install a magic reference |  | 
| 1177       customMixin(element, definition.prototype, definition.native); |  | 
| 1178       element.__proto__ = definition.prototype; |  | 
| 1179     } |  | 
| 1180   } |  | 
| 1181 |  | 
| 1182   function customMixin(inTarget, inSrc, inNative) { |  | 
| 1183     // TODO(sjmiles): 'used' allows us to only copy the 'youngest' version of |  | 
| 1184     // any property. This set should be precalculated. We also need to |  | 
| 1185     // consider this for supporting 'super'. |  | 
| 1186     var used = {}; |  | 
| 1187     // start with inSrc |  | 
| 1188     var p = inSrc; |  | 
| 1189     // sometimes the default is HTMLUnknownElement.prototype instead of |  | 
| 1190     // HTMLElement.prototype, so we add a test |  | 
| 1191     // the idea is to avoid mixing in native prototypes, so adding |  | 
| 1192     // the second test is WLOG |  | 
| 1193     while (p !== inNative && p !== HTMLUnknownElement.prototype) { |  | 
| 1194       var keys = Object.getOwnPropertyNames(p); |  | 
| 1195       for (var i=0, k; k=keys[i]; i++) { |  | 
| 1196         if (!used[k]) { |  | 
| 1197           Object.defineProperty(inTarget, k, |  | 
| 1198               Object.getOwnPropertyDescriptor(p, k)); |  | 
| 1199           used[k] = 1; |  | 
| 1200         } |  | 
| 1201       } |  | 
| 1202       p = Object.getPrototypeOf(p); |  | 
| 1203     } |  | 
| 1204   } |  | 
| 1205 |  | 
| 1206   function created(element) { |  | 
| 1207     // invoke createdCallback |  | 
| 1208     if (element.createdCallback) { |  | 
| 1209       element.createdCallback(); |  | 
| 1210     } |  | 
| 1211   } |  | 
| 1212 |  | 
| 1213   // attribute watching |  | 
| 1214 |  | 
| 1215   function overrideAttributeApi(prototype) { |  | 
| 1216     // overrides to implement callbacks |  | 
| 1217     // TODO(sjmiles): should support access via .attributes NamedNodeMap |  | 
| 1218     // TODO(sjmiles): preserves user defined overrides, if any |  | 
| 1219     if (prototype.setAttribute._polyfilled) { |  | 
| 1220       return; |  | 
| 1221     } |  | 
| 1222     var setAttribute = prototype.setAttribute; |  | 
| 1223     prototype.setAttribute = function(name, value) { |  | 
| 1224       changeAttribute.call(this, name, value, setAttribute); |  | 
| 1225     } |  | 
| 1226     var removeAttribute = prototype.removeAttribute; |  | 
| 1227     prototype.removeAttribute = function(name) { |  | 
| 1228       changeAttribute.call(this, name, null, removeAttribute); |  | 
| 1229     } |  | 
| 1230     prototype.setAttribute._polyfilled = true; |  | 
| 1231   } |  | 
| 1232 |  | 
| 1233   // https://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/custom/ |  | 
| 1234   // index.html#dfn-attribute-changed-callback |  | 
| 1235   function changeAttribute(name, value, operation) { |  | 
| 1236     var oldValue = this.getAttribute(name); |  | 
| 1237     operation.apply(this, arguments); |  | 
| 1238     var newValue = this.getAttribute(name); |  | 
| 1239     if (this.attributeChangedCallback |  | 
| 1240         && (newValue !== oldValue)) { |  | 
| 1241       this.attributeChangedCallback(name, oldValue, newValue); |  | 
| 1242     } |  | 
| 1243   } |  | 
| 1244 |  | 
| 1245   // element registry (maps tag names to definitions) |  | 
| 1246 |  | 
| 1247   var registry = {}; |  | 
| 1248 |  | 
| 1249   function getRegisteredDefinition(name) { |  | 
| 1250     if (name) { |  | 
| 1251       return registry[name.toLowerCase()]; |  | 
| 1252     } |  | 
| 1253   } |  | 
| 1254 |  | 
| 1255   function registerDefinition(name, definition) { |  | 
| 1256     if (registry[name]) { |  | 
| 1257       throw new Error('a type with that name is already registered.'); |  | 
| 1258     } |  | 
| 1259     registry[name] = definition; |  | 
| 1260   } |  | 
| 1261 |  | 
| 1262   function generateConstructor(definition) { |  | 
| 1263     return function() { |  | 
| 1264       return instantiate(definition); |  | 
| 1265     }; |  | 
| 1266   } |  | 
| 1267 |  | 
| 1268   function createElement(tag, typeExtension) { |  | 
| 1269     // TODO(sjmiles): ignore 'tag' when using 'typeExtension', we could |  | 
| 1270     // error check it, or perhaps there should only ever be one argument |  | 
| 1271     var definition = getRegisteredDefinition(typeExtension || tag); |  | 
| 1272     if (definition) { |  | 
| 1273       if (tag == definition.tag && typeExtension == definition.is) { |  | 
| 1274         return new definition.ctor(); |  | 
| 1275       } |  | 
| 1276       // Handle empty string for type extension. |  | 
| 1277       if (!typeExtension && !definition.is) { |  | 
| 1278         return new definition.ctor(); |  | 
| 1279       } |  | 
| 1280     } |  | 
| 1281 |  | 
| 1282     if (typeExtension) { |  | 
| 1283       var element = createElement(tag); |  | 
| 1284       element.setAttribute('is', typeExtension); |  | 
| 1285       return element; |  | 
| 1286     } |  | 
| 1287     var element = domCreateElement(tag); |  | 
| 1288     // Custom tags should be HTMLElements even if not upgraded. |  | 
| 1289     if (tag.indexOf('-') >= 0) { |  | 
| 1290       implement(element, HTMLElement); |  | 
| 1291     } |  | 
| 1292     return element; |  | 
| 1293   } |  | 
| 1294 |  | 
| 1295   function upgradeElement(element) { |  | 
| 1296     if (!element.__upgraded__ && (element.nodeType === Node.ELEMENT_NODE)) { |  | 
| 1297       var is = element.getAttribute('is'); |  | 
| 1298       var definition = registry[is || element.localName]; |  | 
| 1299       if (definition) { |  | 
| 1300         if (is && definition.tag == element.localName) { |  | 
| 1301           return upgrade(element, definition); |  | 
| 1302         } else if (!is && !definition.extends) { |  | 
| 1303           return upgrade(element, definition); |  | 
| 1304         } |  | 
| 1305       } |  | 
| 1306     } |  | 
| 1307   } |  | 
| 1308 |  | 
| 1309   function cloneNode(deep) { |  | 
| 1310     // call original clone |  | 
| 1311     var n = domCloneNode.call(this, deep); |  | 
| 1312     // upgrade the element and subtree |  | 
| 1313     scope.upgradeAll(n); |  | 
| 1314     // return the clone |  | 
| 1315     return n; |  | 
| 1316   } |  | 
| 1317   // capture native createElement before we override it |  | 
| 1318 |  | 
| 1319   var domCreateElement = document.createElement.bind(document); |  | 
| 1320 |  | 
| 1321   // capture native cloneNode before we override it |  | 
| 1322 |  | 
| 1323   var domCloneNode = Node.prototype.cloneNode; |  | 
| 1324 |  | 
| 1325   // exports |  | 
| 1326 |  | 
| 1327   document.registerElement = register; |  | 
| 1328   document.createElement = createElement; // override |  | 
| 1329   Node.prototype.cloneNode = cloneNode; // override |  | 
| 1330 |  | 
| 1331   scope.registry = registry; |  | 
| 1332 |  | 
| 1333   /** |  | 
| 1334    * Upgrade an element to a custom element. Upgrading an element |  | 
| 1335    * causes the custom prototype to be applied, an `is` attribute |  | 
| 1336    * to be attached (as needed), and invocation of the `readyCallback`. |  | 
| 1337    * `upgrade` does nothing if the element is already upgraded, or |  | 
| 1338    * if it matches no registered custom tag name. |  | 
| 1339    * |  | 
| 1340    * @method ugprade |  | 
| 1341    * @param {Element} element The element to upgrade. |  | 
| 1342    * @return {Element} The upgraded element. |  | 
| 1343    */ |  | 
| 1344   scope.upgrade = upgradeElement; |  | 
| 1345 } |  | 
| 1346 |  | 
| 1347 // bc |  | 
| 1348 document.register = document.registerElement; |  | 
| 1349 |  | 
| 1350 scope.hasNative = hasNative; |  | 
| 1351 scope.useNative = useNative; |  | 
| 1352 |  | 
| 1353 })(window.CustomElements); |  | 
| 1354 |  | 
| 1355 (function(scope) { |  | 
| 1356 |  | 
| 1357 // import |  | 
| 1358 |  | 
| 1359 var IMPORT_LINK_TYPE = scope.IMPORT_LINK_TYPE; |  | 
| 1360 |  | 
| 1361 // highlander object for parsing a document tree |  | 
| 1362 |  | 
| 1363 var parser = { |  | 
| 1364   selectors: [ |  | 
| 1365     'link[rel=' + IMPORT_LINK_TYPE + ']' |  | 
| 1366   ], |  | 
| 1367   map: { |  | 
| 1368     link: 'parseLink' |  | 
| 1369   }, |  | 
| 1370   parse: function(inDocument) { |  | 
| 1371     if (!inDocument.__parsed) { |  | 
| 1372       // only parse once |  | 
| 1373       inDocument.__parsed = true; |  | 
| 1374       // all parsable elements in inDocument (depth-first pre-order traversal) |  | 
| 1375       var elts = inDocument.querySelectorAll(parser.selectors); |  | 
| 1376       // for each parsable node type, call the mapped parsing method |  | 
| 1377       forEach(elts, function(e) { |  | 
| 1378         parser[parser.map[e.localName]](e); |  | 
| 1379       }); |  | 
| 1380       // upgrade all upgradeable static elements, anything dynamically |  | 
| 1381       // created should be caught by observer |  | 
| 1382       CustomElements.upgradeDocument(inDocument); |  | 
| 1383       // observe document for dom changes |  | 
| 1384       CustomElements.observeDocument(inDocument); |  | 
| 1385     } |  | 
| 1386   }, |  | 
| 1387   parseLink: function(linkElt) { |  | 
| 1388     // imports |  | 
| 1389     if (isDocumentLink(linkElt)) { |  | 
| 1390       this.parseImport(linkElt); |  | 
| 1391     } |  | 
| 1392   }, |  | 
| 1393   parseImport: function(linkElt) { |  | 
| 1394     if (linkElt.import) { |  | 
| 1395       parser.parse(linkElt.import); |  | 
| 1396     } |  | 
| 1397   } |  | 
| 1398 }; |  | 
| 1399 |  | 
| 1400 function isDocumentLink(inElt) { |  | 
| 1401   return (inElt.localName === 'link' |  | 
| 1402       && inElt.getAttribute('rel') === IMPORT_LINK_TYPE); |  | 
| 1403 } |  | 
| 1404 |  | 
| 1405 var forEach = Array.prototype.forEach.call.bind(Array.prototype.forEach); |  | 
| 1406 |  | 
| 1407 // exports |  | 
| 1408 |  | 
| 1409 scope.parser = parser; |  | 
| 1410 scope.IMPORT_LINK_TYPE = IMPORT_LINK_TYPE; |  | 
| 1411 |  | 
| 1412 })(window.CustomElements); |  | 
| 1413 (function(scope){ |  | 
| 1414 |  | 
| 1415 // bootstrap parsing |  | 
| 1416 function bootstrap() { |  | 
| 1417   // parse document |  | 
| 1418   CustomElements.parser.parse(document); |  | 
| 1419   // one more pass before register is 'live' |  | 
| 1420   CustomElements.upgradeDocument(document); |  | 
| 1421   CustomElements.performedInitialDocumentUpgrade = true; |  | 
| 1422   // choose async |  | 
| 1423   var async = window.Platform && Platform.endOfMicrotask ? |  | 
| 1424     Platform.endOfMicrotask : |  | 
| 1425     setTimeout; |  | 
| 1426   async(function() { |  | 
| 1427     // set internal 'ready' flag, now document.registerElement will trigger |  | 
| 1428     // synchronous upgrades |  | 
| 1429     CustomElements.ready = true; |  | 
| 1430     // capture blunt profiling data |  | 
| 1431     CustomElements.readyTime = Date.now(); |  | 
| 1432     if (window.HTMLImports) { |  | 
| 1433       CustomElements.elapsed = CustomElements.readyTime - HTMLImports.readyTime; |  | 
| 1434     } |  | 
| 1435     // notify the system that we are bootstrapped |  | 
| 1436     document.dispatchEvent( |  | 
| 1437       new CustomEvent('WebComponentsReady', {bubbles: true}) |  | 
| 1438     ); |  | 
| 1439   }); |  | 
| 1440 } |  | 
| 1441 |  | 
| 1442 // CustomEvent shim for IE |  | 
| 1443 if (typeof window.CustomEvent !== 'function') { |  | 
| 1444   window.CustomEvent = function(inType) { |  | 
| 1445     var e = document.createEvent('HTMLEvents'); |  | 
| 1446     e.initEvent(inType, true, true); |  | 
| 1447     return e; |  | 
| 1448   }; |  | 
| 1449 } |  | 
| 1450 |  | 
| 1451 // When loading at readyState complete time (or via flag), boot custom elements |  | 
| 1452 // immediately. |  | 
| 1453 // If relevant, HTMLImports must already be loaded. |  | 
| 1454 if (document.readyState === 'complete' || scope.flags.eager) { |  | 
| 1455   bootstrap(); |  | 
| 1456 // When loading at readyState interactive time, bootstrap only if HTMLImports |  | 
| 1457 // are not pending. Also avoid IE as the semantics of this state are unreliable. |  | 
| 1458 } else if (document.readyState === 'interactive' && !window.attachEvent && |  | 
| 1459     (!window.HTMLImports || window.HTMLImports.ready)) { |  | 
| 1460   bootstrap(); |  | 
| 1461 // When loading at other readyStates, wait for the appropriate DOM event to |  | 
| 1462 // bootstrap. |  | 
| 1463 } else { |  | 
| 1464   var loadEvent = window.HTMLImports && !HTMLImports.ready ? |  | 
| 1465       'HTMLImportsLoaded' : document.readyState == 'loading' ? |  | 
| 1466         'DOMContentLoaded' : 'load'; |  | 
| 1467   window.addEventListener(loadEvent, bootstrap); |  | 
| 1468 } |  | 
| 1469 |  | 
| 1470 })(window.CustomElements); |  | 
| 1471 |  | 
| 1472 (function() { |  | 
| 1473 // Patch to allow custom element and shadow dom to work together, from: |  | 
| 1474 // https://github.com/Polymer/platform-dev/blob/60ece8c323c5d9325cbfdfd6e8cd180d
      4f38a3bc/src/patches-shadowdom-polyfill.js |  | 
| 1475 // include .host reference |  | 
| 1476 if (HTMLElement.prototype.createShadowRoot) { |  | 
| 1477   var originalCreateShadowRoot = HTMLElement.prototype.createShadowRoot; |  | 
| 1478   HTMLElement.prototype.createShadowRoot = function() { |  | 
| 1479     var root = originalCreateShadowRoot.call(this); |  | 
| 1480     root.host = this; |  | 
| 1481     CustomElements.watchShadow(this); |  | 
| 1482     return root; |  | 
| 1483   } |  | 
| 1484 } |  | 
| 1485 |  | 
| 1486 |  | 
| 1487 // Patch to allow custom elements and shadow dom to work together, from: |  | 
| 1488 // https://github.com/Polymer/platform-dev/blob/2bb9c56d90f9ac19c2e65cdad368668a
      ff514f14/src/patches-custom-elements.js |  | 
| 1489 if (window.ShadowDOMPolyfill) { |  | 
| 1490 |  | 
| 1491   // ensure wrapped inputs for these functions |  | 
| 1492   var fns = ['upgradeAll', 'upgradeSubtree', 'observeDocument', |  | 
| 1493       'upgradeDocument']; |  | 
| 1494 |  | 
| 1495   // cache originals |  | 
| 1496   var original = {}; |  | 
| 1497   fns.forEach(function(fn) { |  | 
| 1498     original[fn] = CustomElements[fn]; |  | 
| 1499   }); |  | 
| 1500 |  | 
| 1501   // override |  | 
| 1502   fns.forEach(function(fn) { |  | 
| 1503     CustomElements[fn] = function(inNode) { |  | 
| 1504       return original[fn](window.ShadowDOMPolyfill.wrapIfNeeded(inNode)); |  | 
| 1505     }; |  | 
| 1506   }); |  | 
| 1507 |  | 
| 1508 } |  | 
| 1509 |  | 
| 1510 // Patch to make importNode work. |  | 
| 1511 // https://github.com/Polymer/platform-dev/blob/64a92f273462f04a84abbe2f054294f2
      b62dbcd6/src/patches-mdv.js |  | 
| 1512 if (window.CustomElements && !CustomElements.useNative) { |  | 
| 1513   var originalImportNode = Document.prototype.importNode; |  | 
| 1514   Document.prototype.importNode = function(node, deep) { |  | 
| 1515     var imported = originalImportNode.call(this, node, deep); |  | 
| 1516     CustomElements.upgradeAll(imported); |  | 
| 1517     return imported; |  | 
| 1518   } |  | 
| 1519 } |  | 
| 1520 |  | 
| 1521 })(); |  | 
| OLD | NEW | 
|---|