| OLD | NEW | 
|---|
| (Empty) |  | 
|  | 1 /* | 
|  | 2  * Copyright 2013 The Polymer Authors. All rights reserved. | 
|  | 3  * Use of this source code is goverened by a BSD-style | 
|  | 4  * license that can be found in the LICENSE file. | 
|  | 5  */ | 
|  | 6 | 
|  | 7 // TODO(jmesserly): polyfill does not have feature testing or the definition of | 
|  | 8 // SideTable. The extra code is from: | 
|  | 9 // https://github.com/Polymer/CustomElements/blob/master/src/MutationObserver.js | 
|  | 10 // https://github.com/Polymer/CustomElements/blob/master/src/sidetable.js | 
|  | 11 // I also renamed JsMutationObserver -> MutationObserver to correctly interact | 
|  | 12 // with dart2js interceptors. | 
|  | 13 | 
|  | 14 if (!window.MutationObserver && !window.WebKitMutationObserver) { | 
|  | 15 | 
|  | 16 (function(global) { | 
|  | 17   // SideTable is a weak map where possible. If WeakMap is not available the | 
|  | 18   // association is stored as an expando property. | 
|  | 19   var SideTable; | 
|  | 20   // TODO(arv): WeakMap does not allow for Node etc to be keys in Firefox | 
|  | 21   if (typeof WeakMap !== 'undefined' && navigator.userAgent.indexOf('Firefox/') 
     < 0) { | 
|  | 22     SideTable = WeakMap; | 
|  | 23   } else { | 
|  | 24     (function() { | 
|  | 25       var defineProperty = Object.defineProperty; | 
|  | 26       var hasOwnProperty = Object.hasOwnProperty; | 
|  | 27       var counter = new Date().getTime() % 1e9; | 
|  | 28 | 
|  | 29       SideTable = function() { | 
|  | 30         this.name = '__st' + (Math.random() * 1e9 >>> 0) + (counter++ + '__'); | 
|  | 31       }; | 
|  | 32 | 
|  | 33       SideTable.prototype = { | 
|  | 34         set: function(key, value) { | 
|  | 35           defineProperty(key, this.name, {value: value, writable: true}); | 
|  | 36         }, | 
|  | 37         get: function(key) { | 
|  | 38           return hasOwnProperty.call(key, this.name) ? key[this.name] : undefine
     d; | 
|  | 39         }, | 
|  | 40         delete: function(key) { | 
|  | 41           this.set(key, undefined); | 
|  | 42         } | 
|  | 43       } | 
|  | 44     })(); | 
|  | 45   } | 
|  | 46 | 
|  | 47   var registrationsTable = new SideTable(); | 
|  | 48 | 
|  | 49   // We use setImmediate or postMessage for our future callback. | 
|  | 50   var setImmediate = window.msSetImmediate; | 
|  | 51 | 
|  | 52   // Use post message to emulate setImmediate. | 
|  | 53   if (!setImmediate) { | 
|  | 54     var setImmediateQueue = []; | 
|  | 55     var sentinel = String(Math.random()); | 
|  | 56     window.addEventListener('message', function(e) { | 
|  | 57       if (e.data === sentinel) { | 
|  | 58         var queue = setImmediateQueue; | 
|  | 59         setImmediateQueue = []; | 
|  | 60         queue.forEach(function(func) { | 
|  | 61           func(); | 
|  | 62         }); | 
|  | 63       } | 
|  | 64     }); | 
|  | 65     setImmediate = function(func) { | 
|  | 66       setImmediateQueue.push(func); | 
|  | 67       window.postMessage(sentinel, '*'); | 
|  | 68     }; | 
|  | 69   } | 
|  | 70 | 
|  | 71   // This is used to ensure that we never schedule 2 callas to setImmediate | 
|  | 72   var isScheduled = false; | 
|  | 73 | 
|  | 74   // Keep track of observers that needs to be notified next time. | 
|  | 75   var scheduledObservers = []; | 
|  | 76 | 
|  | 77   /** | 
|  | 78    * Schedules |dispatchCallback| to be called in the future. | 
|  | 79    * @param {MutationObserver} observer | 
|  | 80    */ | 
|  | 81   function scheduleCallback(observer) { | 
|  | 82     scheduledObservers.push(observer); | 
|  | 83     if (!isScheduled) { | 
|  | 84       isScheduled = true; | 
|  | 85       setImmediate(dispatchCallbacks); | 
|  | 86     } | 
|  | 87   } | 
|  | 88 | 
|  | 89   function wrapIfNeeded(node) { | 
|  | 90     return window.ShadowDOMPolyfill && | 
|  | 91         window.ShadowDOMPolyfill.wrapIfNeeded(node) || | 
|  | 92         node; | 
|  | 93   } | 
|  | 94 | 
|  | 95   function dispatchCallbacks() { | 
|  | 96     // http://dom.spec.whatwg.org/#mutation-observers | 
|  | 97 | 
|  | 98     isScheduled = false; // Used to allow a new setImmediate call above. | 
|  | 99 | 
|  | 100     var observers = scheduledObservers; | 
|  | 101     scheduledObservers = []; | 
|  | 102     // Sort observers based on their creation UID (incremental). | 
|  | 103     observers.sort(function(o1, o2) { | 
|  | 104       return o1.uid_ - o2.uid_; | 
|  | 105     }); | 
|  | 106 | 
|  | 107     var anyNonEmpty = false; | 
|  | 108     observers.forEach(function(observer) { | 
|  | 109 | 
|  | 110       // 2.1, 2.2 | 
|  | 111       var queue = observer.takeRecords(); | 
|  | 112       // 2.3. Remove all transient registered observers whose observer is mo. | 
|  | 113       removeTransientObserversFor(observer); | 
|  | 114 | 
|  | 115       // 2.4 | 
|  | 116       if (queue.length) { | 
|  | 117         observer.callback_(queue, observer); | 
|  | 118         anyNonEmpty = true; | 
|  | 119       } | 
|  | 120     }); | 
|  | 121 | 
|  | 122     // 3. | 
|  | 123     if (anyNonEmpty) | 
|  | 124       dispatchCallbacks(); | 
|  | 125   } | 
|  | 126 | 
|  | 127   function removeTransientObserversFor(observer) { | 
|  | 128     observer.nodes_.forEach(function(node) { | 
|  | 129       var registrations = registrationsTable.get(node); | 
|  | 130       if (!registrations) | 
|  | 131         return; | 
|  | 132       registrations.forEach(function(registration) { | 
|  | 133         if (registration.observer === observer) | 
|  | 134           registration.removeTransientObservers(); | 
|  | 135       }); | 
|  | 136     }); | 
|  | 137   } | 
|  | 138 | 
|  | 139   /** | 
|  | 140    * This function is used for the "For each registered observer observer (with | 
|  | 141    * observer's options as options) in target's list of registered observers, | 
|  | 142    * run these substeps:" and the "For each ancestor ancestor of target, and for | 
|  | 143    * each registered observer observer (with options options) in ancestor's list | 
|  | 144    * of registered observers, run these substeps:" part of the algorithms. The | 
|  | 145    * |options.subtree| is checked to ensure that the callback is called | 
|  | 146    * correctly. | 
|  | 147    * | 
|  | 148    * @param {Node} target | 
|  | 149    * @param {function(MutationObserverInit):MutationRecord} callback | 
|  | 150    */ | 
|  | 151   function forEachAncestorAndObserverEnqueueRecord(target, callback) { | 
|  | 152     for (var node = target; node; node = node.parentNode) { | 
|  | 153       var registrations = registrationsTable.get(node); | 
|  | 154 | 
|  | 155       if (registrations) { | 
|  | 156         for (var j = 0; j < registrations.length; j++) { | 
|  | 157           var registration = registrations[j]; | 
|  | 158           var options = registration.options; | 
|  | 159 | 
|  | 160           // Only target ignores subtree. | 
|  | 161           if (node !== target && !options.subtree) | 
|  | 162             continue; | 
|  | 163 | 
|  | 164           var record = callback(options); | 
|  | 165           if (record) | 
|  | 166             registration.enqueue(record); | 
|  | 167         } | 
|  | 168       } | 
|  | 169     } | 
|  | 170   } | 
|  | 171 | 
|  | 172   var uidCounter = 0; | 
|  | 173 | 
|  | 174   /** | 
|  | 175    * The class that maps to the DOM MutationObserver interface. | 
|  | 176    * @param {Function} callback. | 
|  | 177    * @constructor | 
|  | 178    */ | 
|  | 179   function MutationObserver(callback) { | 
|  | 180     this.callback_ = callback; | 
|  | 181     this.nodes_ = []; | 
|  | 182     this.records_ = []; | 
|  | 183     this.uid_ = ++uidCounter; | 
|  | 184   } | 
|  | 185 | 
|  | 186   MutationObserver.prototype = { | 
|  | 187     observe: function(target, options) { | 
|  | 188       target = wrapIfNeeded(target); | 
|  | 189 | 
|  | 190       // 1.1 | 
|  | 191       if (!options.childList && !options.attributes && !options.characterData || | 
|  | 192 | 
|  | 193           // 1.2 | 
|  | 194           options.attributeOldValue && !options.attributes || | 
|  | 195 | 
|  | 196           // 1.3 | 
|  | 197           options.attributeFilter && options.attributeFilter.length && | 
|  | 198               !options.attributes || | 
|  | 199 | 
|  | 200           // 1.4 | 
|  | 201           options.characterDataOldValue && !options.characterData) { | 
|  | 202 | 
|  | 203         throw new SyntaxError(); | 
|  | 204       } | 
|  | 205 | 
|  | 206       var registrations = registrationsTable.get(target); | 
|  | 207       if (!registrations) | 
|  | 208         registrationsTable.set(target, registrations = []); | 
|  | 209 | 
|  | 210       // 2 | 
|  | 211       // If target's list of registered observers already includes a registered | 
|  | 212       // observer associated with the context object, replace that registered | 
|  | 213       // observer's options with options. | 
|  | 214       var registration; | 
|  | 215       for (var i = 0; i < registrations.length; i++) { | 
|  | 216         if (registrations[i].observer === this) { | 
|  | 217           registration = registrations[i]; | 
|  | 218           registration.removeListeners(); | 
|  | 219           registration.options = options; | 
|  | 220           break; | 
|  | 221         } | 
|  | 222       } | 
|  | 223 | 
|  | 224       // 3. | 
|  | 225       // Otherwise, add a new registered observer to target's list of registered | 
|  | 226       // observers with the context object as the observer and options as the | 
|  | 227       // options, and add target to context object's list of nodes on which it | 
|  | 228       // is registered. | 
|  | 229       if (!registration) { | 
|  | 230         registration = new Registration(this, target, options); | 
|  | 231         registrations.push(registration); | 
|  | 232         this.nodes_.push(target); | 
|  | 233       } | 
|  | 234 | 
|  | 235       registration.addListeners(); | 
|  | 236     }, | 
|  | 237 | 
|  | 238     disconnect: function() { | 
|  | 239       this.nodes_.forEach(function(node) { | 
|  | 240         var registrations = registrationsTable.get(node); | 
|  | 241         for (var i = 0; i < registrations.length; i++) { | 
|  | 242           var registration = registrations[i]; | 
|  | 243           if (registration.observer === this) { | 
|  | 244             registration.removeListeners(); | 
|  | 245             registrations.splice(i, 1); | 
|  | 246             // Each node can only have one registered observer associated with | 
|  | 247             // this observer. | 
|  | 248             break; | 
|  | 249           } | 
|  | 250         } | 
|  | 251       }, this); | 
|  | 252       this.records_ = []; | 
|  | 253     }, | 
|  | 254 | 
|  | 255     takeRecords: function() { | 
|  | 256       var copyOfRecords = this.records_; | 
|  | 257       this.records_ = []; | 
|  | 258       return copyOfRecords; | 
|  | 259     } | 
|  | 260   }; | 
|  | 261 | 
|  | 262   /** | 
|  | 263    * @param {string} type | 
|  | 264    * @param {Node} target | 
|  | 265    * @constructor | 
|  | 266    */ | 
|  | 267   function MutationRecord(type, target) { | 
|  | 268     this.type = type; | 
|  | 269     this.target = target; | 
|  | 270     this.addedNodes = []; | 
|  | 271     this.removedNodes = []; | 
|  | 272     this.previousSibling = null; | 
|  | 273     this.nextSibling = null; | 
|  | 274     this.attributeName = null; | 
|  | 275     this.attributeNamespace = null; | 
|  | 276     this.oldValue = null; | 
|  | 277   } | 
|  | 278 | 
|  | 279   // TODO(jmesserly): this fixes the interceptor dispatch on IE. | 
|  | 280   // Not sure why this is necessary. | 
|  | 281   MutationObserver.prototype.constructor = MutationObserver; | 
|  | 282   MutationObserver.name = 'MutationObserver'; | 
|  | 283   MutationRecord.prototype.constructor = MutationRecord; | 
|  | 284   MutationRecord.name = 'MutationRecord'; | 
|  | 285 | 
|  | 286   function copyMutationRecord(original) { | 
|  | 287     var record = new MutationRecord(original.type, original.target); | 
|  | 288     record.addedNodes = original.addedNodes.slice(); | 
|  | 289     record.removedNodes = original.removedNodes.slice(); | 
|  | 290     record.previousSibling = original.previousSibling; | 
|  | 291     record.nextSibling = original.nextSibling; | 
|  | 292     record.attributeName = original.attributeName; | 
|  | 293     record.attributeNamespace = original.attributeNamespace; | 
|  | 294     record.oldValue = original.oldValue; | 
|  | 295     return record; | 
|  | 296   }; | 
|  | 297 | 
|  | 298   // We keep track of the two (possibly one) records used in a single mutation. | 
|  | 299   var currentRecord, recordWithOldValue; | 
|  | 300 | 
|  | 301   /** | 
|  | 302    * Creates a record without |oldValue| and caches it as |currentRecord| for | 
|  | 303    * later use. | 
|  | 304    * @param {string} oldValue | 
|  | 305    * @return {MutationRecord} | 
|  | 306    */ | 
|  | 307   function getRecord(type, target) { | 
|  | 308     return currentRecord = new MutationRecord(type, target); | 
|  | 309   } | 
|  | 310 | 
|  | 311   /** | 
|  | 312    * Gets or creates a record with |oldValue| based in the |currentRecord| | 
|  | 313    * @param {string} oldValue | 
|  | 314    * @return {MutationRecord} | 
|  | 315    */ | 
|  | 316   function getRecordWithOldValue(oldValue) { | 
|  | 317     if (recordWithOldValue) | 
|  | 318       return recordWithOldValue; | 
|  | 319     recordWithOldValue = copyMutationRecord(currentRecord); | 
|  | 320     recordWithOldValue.oldValue = oldValue; | 
|  | 321     return recordWithOldValue; | 
|  | 322   } | 
|  | 323 | 
|  | 324   function clearRecords() { | 
|  | 325     currentRecord = recordWithOldValue = undefined; | 
|  | 326   } | 
|  | 327 | 
|  | 328   /** | 
|  | 329    * @param {MutationRecord} record | 
|  | 330    * @return {boolean} Whether the record represents a record from the current | 
|  | 331    * mutation event. | 
|  | 332    */ | 
|  | 333   function recordRepresentsCurrentMutation(record) { | 
|  | 334     return record === recordWithOldValue || record === currentRecord; | 
|  | 335   } | 
|  | 336 | 
|  | 337   /** | 
|  | 338    * Selects which record, if any, to replace the last record in the queue. | 
|  | 339    * This returns |null| if no record should be replaced. | 
|  | 340    * | 
|  | 341    * @param {MutationRecord} lastRecord | 
|  | 342    * @param {MutationRecord} newRecord | 
|  | 343    * @param {MutationRecord} | 
|  | 344    */ | 
|  | 345   function selectRecord(lastRecord, newRecord) { | 
|  | 346     if (lastRecord === newRecord) | 
|  | 347       return lastRecord; | 
|  | 348 | 
|  | 349     // Check if the the record we are adding represents the same record. If | 
|  | 350     // so, we keep the one with the oldValue in it. | 
|  | 351     if (recordWithOldValue && recordRepresentsCurrentMutation(lastRecord)) | 
|  | 352       return recordWithOldValue; | 
|  | 353 | 
|  | 354     return null; | 
|  | 355   } | 
|  | 356 | 
|  | 357   /** | 
|  | 358    * Class used to represent a registered observer. | 
|  | 359    * @param {MutationObserver} observer | 
|  | 360    * @param {Node} target | 
|  | 361    * @param {MutationObserverInit} options | 
|  | 362    * @constructor | 
|  | 363    */ | 
|  | 364   function Registration(observer, target, options) { | 
|  | 365     this.observer = observer; | 
|  | 366     this.target = target; | 
|  | 367     this.options = options; | 
|  | 368     this.transientObservedNodes = []; | 
|  | 369   } | 
|  | 370 | 
|  | 371   Registration.prototype = { | 
|  | 372     enqueue: function(record) { | 
|  | 373       var records = this.observer.records_; | 
|  | 374       var length = records.length; | 
|  | 375 | 
|  | 376       // There are cases where we replace the last record with the new record. | 
|  | 377       // For example if the record represents the same mutation we need to use | 
|  | 378       // the one with the oldValue. If we get same record (this can happen as we | 
|  | 379       // walk up the tree) we ignore the new record. | 
|  | 380       if (records.length > 0) { | 
|  | 381         var lastRecord = records[length - 1]; | 
|  | 382         var recordToReplaceLast = selectRecord(lastRecord, record); | 
|  | 383         if (recordToReplaceLast) { | 
|  | 384           records[length - 1] = recordToReplaceLast; | 
|  | 385           return; | 
|  | 386         } | 
|  | 387       } else { | 
|  | 388         scheduleCallback(this.observer); | 
|  | 389       } | 
|  | 390 | 
|  | 391       records[length] = record; | 
|  | 392     }, | 
|  | 393 | 
|  | 394     addListeners: function() { | 
|  | 395       this.addListeners_(this.target); | 
|  | 396     }, | 
|  | 397 | 
|  | 398     addListeners_: function(node) { | 
|  | 399       var options = this.options; | 
|  | 400       if (options.attributes) | 
|  | 401         node.addEventListener('DOMAttrModified', this, true); | 
|  | 402 | 
|  | 403       if (options.characterData) | 
|  | 404         node.addEventListener('DOMCharacterDataModified', this, true); | 
|  | 405 | 
|  | 406       if (options.childList) | 
|  | 407         node.addEventListener('DOMNodeInserted', this, true); | 
|  | 408 | 
|  | 409       if (options.childList || options.subtree) | 
|  | 410         node.addEventListener('DOMNodeRemoved', this, true); | 
|  | 411     }, | 
|  | 412 | 
|  | 413     removeListeners: function() { | 
|  | 414       this.removeListeners_(this.target); | 
|  | 415     }, | 
|  | 416 | 
|  | 417     removeListeners_: function(node) { | 
|  | 418       var options = this.options; | 
|  | 419       if (options.attributes) | 
|  | 420         node.removeEventListener('DOMAttrModified', this, true); | 
|  | 421 | 
|  | 422       if (options.characterData) | 
|  | 423         node.removeEventListener('DOMCharacterDataModified', this, true); | 
|  | 424 | 
|  | 425       if (options.childList) | 
|  | 426         node.removeEventListener('DOMNodeInserted', this, true); | 
|  | 427 | 
|  | 428       if (options.childList || options.subtree) | 
|  | 429         node.removeEventListener('DOMNodeRemoved', this, true); | 
|  | 430     }, | 
|  | 431 | 
|  | 432     /** | 
|  | 433      * Adds a transient observer on node. The transient observer gets removed | 
|  | 434      * next time we deliver the change records. | 
|  | 435      * @param {Node} node | 
|  | 436      */ | 
|  | 437     addTransientObserver: function(node) { | 
|  | 438       // Don't add transient observers on the target itself. We already have all | 
|  | 439       // the required listeners set up on the target. | 
|  | 440       if (node === this.target) | 
|  | 441         return; | 
|  | 442 | 
|  | 443       this.addListeners_(node); | 
|  | 444       this.transientObservedNodes.push(node); | 
|  | 445       var registrations = registrationsTable.get(node); | 
|  | 446       if (!registrations) | 
|  | 447         registrationsTable.set(node, registrations = []); | 
|  | 448 | 
|  | 449       // We know that registrations does not contain this because we already | 
|  | 450       // checked if node === this.target. | 
|  | 451       registrations.push(this); | 
|  | 452     }, | 
|  | 453 | 
|  | 454     removeTransientObservers: function() { | 
|  | 455       var transientObservedNodes = this.transientObservedNodes; | 
|  | 456       this.transientObservedNodes = []; | 
|  | 457 | 
|  | 458       transientObservedNodes.forEach(function(node) { | 
|  | 459         // Transient observers are never added to the target. | 
|  | 460         this.removeListeners_(node); | 
|  | 461 | 
|  | 462         var registrations = registrationsTable.get(node); | 
|  | 463         for (var i = 0; i < registrations.length; i++) { | 
|  | 464           if (registrations[i] === this) { | 
|  | 465             registrations.splice(i, 1); | 
|  | 466             // Each node can only have one registered observer associated with | 
|  | 467             // this observer. | 
|  | 468             break; | 
|  | 469           } | 
|  | 470         } | 
|  | 471       }, this); | 
|  | 472     }, | 
|  | 473 | 
|  | 474     handleEvent: function(e) { | 
|  | 475       // Stop propagation since we are managing the propagation manually. | 
|  | 476       // This means that other mutation events on the page will not work | 
|  | 477       // correctly but that is by design. | 
|  | 478       e.stopImmediatePropagation(); | 
|  | 479 | 
|  | 480       switch (e.type) { | 
|  | 481         case 'DOMAttrModified': | 
|  | 482           // http://dom.spec.whatwg.org/#concept-mo-queue-attributes | 
|  | 483 | 
|  | 484           var name = e.attrName; | 
|  | 485           var namespace = e.relatedNode.namespaceURI; | 
|  | 486           var target = e.target; | 
|  | 487 | 
|  | 488           // 1. | 
|  | 489           var record = new getRecord('attributes', target); | 
|  | 490           record.attributeName = name; | 
|  | 491           record.attributeNamespace = namespace; | 
|  | 492 | 
|  | 493           // 2. | 
|  | 494           var oldValue = | 
|  | 495               e.attrChange === MutationEvent.ADDITION ? null : e.prevValue; | 
|  | 496 | 
|  | 497           forEachAncestorAndObserverEnqueueRecord(target, function(options) { | 
|  | 498             // 3.1, 4.2 | 
|  | 499             if (!options.attributes) | 
|  | 500               return; | 
|  | 501 | 
|  | 502             // 3.2, 4.3 | 
|  | 503             if (options.attributeFilter && options.attributeFilter.length && | 
|  | 504                 options.attributeFilter.indexOf(name) === -1 && | 
|  | 505                 options.attributeFilter.indexOf(namespace) === -1) { | 
|  | 506               return; | 
|  | 507             } | 
|  | 508             // 3.3, 4.4 | 
|  | 509             if (options.attributeOldValue) | 
|  | 510               return getRecordWithOldValue(oldValue); | 
|  | 511 | 
|  | 512             // 3.4, 4.5 | 
|  | 513             return record; | 
|  | 514           }); | 
|  | 515 | 
|  | 516           break; | 
|  | 517 | 
|  | 518         case 'DOMCharacterDataModified': | 
|  | 519           // http://dom.spec.whatwg.org/#concept-mo-queue-characterdata | 
|  | 520           var target = e.target; | 
|  | 521 | 
|  | 522           // 1. | 
|  | 523           var record = getRecord('characterData', target); | 
|  | 524 | 
|  | 525           // 2. | 
|  | 526           var oldValue = e.prevValue; | 
|  | 527 | 
|  | 528 | 
|  | 529           forEachAncestorAndObserverEnqueueRecord(target, function(options) { | 
|  | 530             // 3.1, 4.2 | 
|  | 531             if (!options.characterData) | 
|  | 532               return; | 
|  | 533 | 
|  | 534             // 3.2, 4.3 | 
|  | 535             if (options.characterDataOldValue) | 
|  | 536               return getRecordWithOldValue(oldValue); | 
|  | 537 | 
|  | 538             // 3.3, 4.4 | 
|  | 539             return record; | 
|  | 540           }); | 
|  | 541 | 
|  | 542           break; | 
|  | 543 | 
|  | 544         case 'DOMNodeRemoved': | 
|  | 545           this.addTransientObserver(e.target); | 
|  | 546           // Fall through. | 
|  | 547         case 'DOMNodeInserted': | 
|  | 548           // http://dom.spec.whatwg.org/#concept-mo-queue-childlist | 
|  | 549           var target = e.relatedNode; | 
|  | 550           var changedNode = e.target; | 
|  | 551           var addedNodes, removedNodes; | 
|  | 552           if (e.type === 'DOMNodeInserted') { | 
|  | 553             addedNodes = [changedNode]; | 
|  | 554             removedNodes = []; | 
|  | 555           } else { | 
|  | 556 | 
|  | 557             addedNodes = []; | 
|  | 558             removedNodes = [changedNode]; | 
|  | 559           } | 
|  | 560           var previousSibling = changedNode.previousSibling; | 
|  | 561           var nextSibling = changedNode.nextSibling; | 
|  | 562 | 
|  | 563           // 1. | 
|  | 564           var record = getRecord('childList', target); | 
|  | 565           record.addedNodes = addedNodes; | 
|  | 566           record.removedNodes = removedNodes; | 
|  | 567           record.previousSibling = previousSibling; | 
|  | 568           record.nextSibling = nextSibling; | 
|  | 569 | 
|  | 570           forEachAncestorAndObserverEnqueueRecord(target, function(options) { | 
|  | 571             // 2.1, 3.2 | 
|  | 572             if (!options.childList) | 
|  | 573               return; | 
|  | 574 | 
|  | 575             // 2.2, 3.3 | 
|  | 576             return record; | 
|  | 577           }); | 
|  | 578 | 
|  | 579       } | 
|  | 580 | 
|  | 581       clearRecords(); | 
|  | 582     } | 
|  | 583   }; | 
|  | 584 | 
|  | 585   global.MutationObserver = MutationObserver; | 
|  | 586 })(window); | 
|  | 587 | 
|  | 588 } | 
| OLD | NEW | 
|---|