var MutationObserver = window.MutationObserver 
 | 
  || window.WebKitMutationObserver 
 | 
  || window.MozMutationObserver; 
 | 
  
 | 
/* 
 | 
 * Copyright 2012 The Polymer Authors. All rights reserved. 
 | 
 * Use of this source code is goverened by a BSD-style 
 | 
 * license that can be found in the LICENSE file. 
 | 
 */ 
 | 
  
 | 
var WeakMap = window.WeakMap; 
 | 
  
 | 
if (typeof WeakMap === 'undefined') { 
 | 
  var defineProperty = Object.defineProperty; 
 | 
  var counter = Date.now() % 1e9; 
 | 
  
 | 
  WeakMap = function() { 
 | 
    this.name = '__st' + (Math.random() * 1e9 >>> 0) + (counter++ + '__'); 
 | 
  }; 
 | 
  
 | 
  WeakMap.prototype = { 
 | 
    set: function(key, value) { 
 | 
      var entry = key[this.name]; 
 | 
      if (entry && entry[0] === key) 
 | 
        entry[1] = value; 
 | 
      else 
 | 
        defineProperty(key, this.name, {value: [key, value], writable: true}); 
 | 
      return this; 
 | 
    }, 
 | 
    get: function(key) { 
 | 
      var entry; 
 | 
      return (entry = key[this.name]) && entry[0] === key ? 
 | 
          entry[1] : undefined; 
 | 
    }, 
 | 
    'delete': function(key) { 
 | 
      var entry = key[this.name]; 
 | 
      if (!entry) return false; 
 | 
      var hasValue = entry[0] === key; 
 | 
      entry[0] = entry[1] = undefined; 
 | 
      return hasValue; 
 | 
    }, 
 | 
    has: function(key) { 
 | 
      var entry = key[this.name]; 
 | 
      if (!entry) return false; 
 | 
      return entry[0] === key; 
 | 
    } 
 | 
  }; 
 | 
} 
 | 
  
 | 
var registrationsTable = new WeakMap(); 
 | 
  
 | 
// We use setImmediate or postMessage for our future callback. 
 | 
var setImmediate = window.msSetImmediate; 
 | 
  
 | 
// Use post message to emulate setImmediate. 
 | 
if (!setImmediate) { 
 | 
  var setImmediateQueue = []; 
 | 
  var sentinel = String(Math.random()); 
 | 
  window.addEventListener('message', function(e) { 
 | 
    if (e.data === sentinel) { 
 | 
      var queue = setImmediateQueue; 
 | 
      setImmediateQueue = []; 
 | 
      queue.forEach(function(func) { 
 | 
        func(); 
 | 
      }); 
 | 
    } 
 | 
  }); 
 | 
  setImmediate = function(func) { 
 | 
    setImmediateQueue.push(func); 
 | 
    window.postMessage(sentinel, '*'); 
 | 
  }; 
 | 
} 
 | 
  
 | 
// This is used to ensure that we never schedule 2 callas to setImmediate 
 | 
var isScheduled = false; 
 | 
  
 | 
// Keep track of observers that needs to be notified next time. 
 | 
var scheduledObservers = []; 
 | 
  
 | 
/** 
 | 
 * Schedules |dispatchCallback| to be called in the future. 
 | 
 * @param {MutationObserver} observer 
 | 
 */ 
 | 
function scheduleCallback(observer) { 
 | 
  scheduledObservers.push(observer); 
 | 
  if (!isScheduled) { 
 | 
    isScheduled = true; 
 | 
    setImmediate(dispatchCallbacks); 
 | 
  } 
 | 
} 
 | 
  
 | 
function wrapIfNeeded(node) { 
 | 
  return window.ShadowDOMPolyfill && 
 | 
      window.ShadowDOMPolyfill.wrapIfNeeded(node) || 
 | 
      node; 
 | 
} 
 | 
  
 | 
function dispatchCallbacks() { 
 | 
  // http://dom.spec.whatwg.org/#mutation-observers 
 | 
  
 | 
  isScheduled = false; // Used to allow a new setImmediate call above. 
 | 
  
 | 
  var observers = scheduledObservers; 
 | 
  scheduledObservers = []; 
 | 
  // Sort observers based on their creation UID (incremental). 
 | 
  observers.sort(function(o1, o2) { 
 | 
    return o1.uid_ - o2.uid_; 
 | 
  }); 
 | 
  
 | 
  var anyNonEmpty = false; 
 | 
  observers.forEach(function(observer) { 
 | 
  
 | 
    // 2.1, 2.2 
 | 
    var queue = observer.takeRecords(); 
 | 
    // 2.3. Remove all transient registered observers whose observer is mo. 
 | 
    removeTransientObserversFor(observer); 
 | 
  
 | 
    // 2.4 
 | 
    if (queue.length) { 
 | 
      observer.callback_(queue, observer); 
 | 
      anyNonEmpty = true; 
 | 
    } 
 | 
  }); 
 | 
  
 | 
  // 3. 
 | 
  if (anyNonEmpty) 
 | 
    dispatchCallbacks(); 
 | 
} 
 | 
  
 | 
function removeTransientObserversFor(observer) { 
 | 
  observer.nodes_.forEach(function(node) { 
 | 
    var registrations = registrationsTable.get(node); 
 | 
    if (!registrations) 
 | 
      return; 
 | 
    registrations.forEach(function(registration) { 
 | 
      if (registration.observer === observer) 
 | 
        registration.removeTransientObservers(); 
 | 
    }); 
 | 
  }); 
 | 
} 
 | 
  
 | 
/** 
 | 
 * This function is used for the "For each registered observer observer (with 
 | 
 * observer's options as options) in target's list of registered observers, 
 | 
 * run these substeps:" and the "For each ancestor ancestor of target, and for 
 | 
 * each registered observer observer (with options options) in ancestor's list 
 | 
 * of registered observers, run these substeps:" part of the algorithms. The 
 | 
 * |options.subtree| is checked to ensure that the callback is called 
 | 
 * correctly. 
 | 
 * 
 | 
 * @param {Node} target 
 | 
 * @param {function(MutationObserverInit):MutationRecord} callback 
 | 
 */ 
 | 
function forEachAncestorAndObserverEnqueueRecord(target, callback) { 
 | 
  for (var node = target; node; node = node.parentNode) { 
 | 
    var registrations = registrationsTable.get(node); 
 | 
  
 | 
    if (registrations) { 
 | 
      for (var j = 0; j < registrations.length; j++) { 
 | 
        var registration = registrations[j]; 
 | 
        var options = registration.options; 
 | 
  
 | 
        // Only target ignores subtree. 
 | 
        if (node !== target && !options.subtree) 
 | 
          continue; 
 | 
  
 | 
        var record = callback(options); 
 | 
        if (record) 
 | 
          registration.enqueue(record); 
 | 
      } 
 | 
    } 
 | 
  } 
 | 
} 
 | 
  
 | 
var uidCounter = 0; 
 | 
  
 | 
/** 
 | 
 * The class that maps to the DOM MutationObserver interface. 
 | 
 * @param {Function} callback. 
 | 
 * @constructor 
 | 
 */ 
 | 
function JsMutationObserver(callback) { 
 | 
  this.callback_ = callback; 
 | 
  this.nodes_ = []; 
 | 
  this.records_ = []; 
 | 
  this.uid_ = ++uidCounter; 
 | 
} 
 | 
  
 | 
JsMutationObserver.prototype = { 
 | 
  observe: function(target, options) { 
 | 
    target = wrapIfNeeded(target); 
 | 
  
 | 
    // 1.1 
 | 
    if (!options.childList && !options.attributes && !options.characterData || 
 | 
  
 | 
        // 1.2 
 | 
        options.attributeOldValue && !options.attributes || 
 | 
  
 | 
        // 1.3 
 | 
        options.attributeFilter && options.attributeFilter.length && 
 | 
            !options.attributes || 
 | 
  
 | 
        // 1.4 
 | 
        options.characterDataOldValue && !options.characterData) { 
 | 
  
 | 
      throw new SyntaxError(); 
 | 
    } 
 | 
  
 | 
    var registrations = registrationsTable.get(target); 
 | 
    if (!registrations) 
 | 
      registrationsTable.set(target, registrations = []); 
 | 
  
 | 
    // 2 
 | 
    // If target's list of registered observers already includes a registered 
 | 
    // observer associated with the context object, replace that registered 
 | 
    // observer's options with options. 
 | 
    var registration; 
 | 
    for (var i = 0; i < registrations.length; i++) { 
 | 
      if (registrations[i].observer === this) { 
 | 
        registration = registrations[i]; 
 | 
        registration.removeListeners(); 
 | 
        registration.options = options; 
 | 
        break; 
 | 
      } 
 | 
    } 
 | 
  
 | 
    // 3. 
 | 
    // Otherwise, add a new registered observer to target's list of registered 
 | 
    // observers with the context object as the observer and options as the 
 | 
    // options, and add target to context object's list of nodes on which it 
 | 
    // is registered. 
 | 
    if (!registration) { 
 | 
      registration = new Registration(this, target, options); 
 | 
      registrations.push(registration); 
 | 
      this.nodes_.push(target); 
 | 
    } 
 | 
  
 | 
    registration.addListeners(); 
 | 
  }, 
 | 
  
 | 
  disconnect: function() { 
 | 
    this.nodes_.forEach(function(node) { 
 | 
      var registrations = registrationsTable.get(node); 
 | 
      for (var i = 0; i < registrations.length; i++) { 
 | 
        var registration = registrations[i]; 
 | 
        if (registration.observer === this) { 
 | 
          registration.removeListeners(); 
 | 
          registrations.splice(i, 1); 
 | 
          // Each node can only have one registered observer associated with 
 | 
          // this observer. 
 | 
          break; 
 | 
        } 
 | 
      } 
 | 
    }, this); 
 | 
    this.records_ = []; 
 | 
  }, 
 | 
  
 | 
  takeRecords: function() { 
 | 
    var copyOfRecords = this.records_; 
 | 
    this.records_ = []; 
 | 
    return copyOfRecords; 
 | 
  } 
 | 
}; 
 | 
  
 | 
/** 
 | 
 * @param {string} type 
 | 
 * @param {Node} target 
 | 
 * @constructor 
 | 
 */ 
 | 
function MutationRecord(type, target) { 
 | 
  this.type = type; 
 | 
  this.target = target; 
 | 
  this.addedNodes = []; 
 | 
  this.removedNodes = []; 
 | 
  this.previousSibling = null; 
 | 
  this.nextSibling = null; 
 | 
  this.attributeName = null; 
 | 
  this.attributeNamespace = null; 
 | 
  this.oldValue = null; 
 | 
} 
 | 
  
 | 
function copyMutationRecord(original) { 
 | 
  var record = new MutationRecord(original.type, original.target); 
 | 
  record.addedNodes = original.addedNodes.slice(); 
 | 
  record.removedNodes = original.removedNodes.slice(); 
 | 
  record.previousSibling = original.previousSibling; 
 | 
  record.nextSibling = original.nextSibling; 
 | 
  record.attributeName = original.attributeName; 
 | 
  record.attributeNamespace = original.attributeNamespace; 
 | 
  record.oldValue = original.oldValue; 
 | 
  return record; 
 | 
}; 
 | 
  
 | 
// We keep track of the two (possibly one) records used in a single mutation. 
 | 
var currentRecord, recordWithOldValue; 
 | 
  
 | 
/** 
 | 
 * Creates a record without |oldValue| and caches it as |currentRecord| for 
 | 
 * later use. 
 | 
 * @param {string} oldValue 
 | 
 * @return {MutationRecord} 
 | 
 */ 
 | 
function getRecord(type, target) { 
 | 
  return currentRecord = new MutationRecord(type, target); 
 | 
} 
 | 
  
 | 
/** 
 | 
 * Gets or creates a record with |oldValue| based in the |currentRecord| 
 | 
 * @param {string} oldValue 
 | 
 * @return {MutationRecord} 
 | 
 */ 
 | 
function getRecordWithOldValue(oldValue) { 
 | 
  if (recordWithOldValue) 
 | 
    return recordWithOldValue; 
 | 
  recordWithOldValue = copyMutationRecord(currentRecord); 
 | 
  recordWithOldValue.oldValue = oldValue; 
 | 
  return recordWithOldValue; 
 | 
} 
 | 
  
 | 
function clearRecords() { 
 | 
  currentRecord = recordWithOldValue = undefined; 
 | 
} 
 | 
  
 | 
/** 
 | 
 * @param {MutationRecord} record 
 | 
 * @return {boolean} Whether the record represents a record from the current 
 | 
 * mutation event. 
 | 
 */ 
 | 
function recordRepresentsCurrentMutation(record) { 
 | 
  return record === recordWithOldValue || record === currentRecord; 
 | 
} 
 | 
  
 | 
/** 
 | 
 * Selects which record, if any, to replace the last record in the queue. 
 | 
 * This returns |null| if no record should be replaced. 
 | 
 * 
 | 
 * @param {MutationRecord} lastRecord 
 | 
 * @param {MutationRecord} newRecord 
 | 
 * @param {MutationRecord} 
 | 
 */ 
 | 
function selectRecord(lastRecord, newRecord) { 
 | 
  if (lastRecord === newRecord) 
 | 
    return lastRecord; 
 | 
  
 | 
  // Check if the the record we are adding represents the same record. If 
 | 
  // so, we keep the one with the oldValue in it. 
 | 
  if (recordWithOldValue && recordRepresentsCurrentMutation(lastRecord)) 
 | 
    return recordWithOldValue; 
 | 
  
 | 
  return null; 
 | 
} 
 | 
  
 | 
/** 
 | 
 * Class used to represent a registered observer. 
 | 
 * @param {MutationObserver} observer 
 | 
 * @param {Node} target 
 | 
 * @param {MutationObserverInit} options 
 | 
 * @constructor 
 | 
 */ 
 | 
function Registration(observer, target, options) { 
 | 
  this.observer = observer; 
 | 
  this.target = target; 
 | 
  this.options = options; 
 | 
  this.transientObservedNodes = []; 
 | 
} 
 | 
  
 | 
Registration.prototype = { 
 | 
  enqueue: function(record) { 
 | 
    var records = this.observer.records_; 
 | 
    var length = records.length; 
 | 
  
 | 
    // There are cases where we replace the last record with the new record. 
 | 
    // For example if the record represents the same mutation we need to use 
 | 
    // the one with the oldValue. If we get same record (this can happen as we 
 | 
    // walk up the tree) we ignore the new record. 
 | 
    if (records.length > 0) { 
 | 
      var lastRecord = records[length - 1]; 
 | 
      var recordToReplaceLast = selectRecord(lastRecord, record); 
 | 
      if (recordToReplaceLast) { 
 | 
        records[length - 1] = recordToReplaceLast; 
 | 
        return; 
 | 
      } 
 | 
    } else { 
 | 
      scheduleCallback(this.observer); 
 | 
    } 
 | 
  
 | 
    records[length] = record; 
 | 
  }, 
 | 
  
 | 
  addListeners: function() { 
 | 
    this.addListeners_(this.target); 
 | 
  }, 
 | 
  
 | 
  addListeners_: function(node) { 
 | 
    var options = this.options; 
 | 
    if (options.attributes) 
 | 
      node.addEventListener('DOMAttrModified', this, true); 
 | 
  
 | 
    if (options.characterData) 
 | 
      node.addEventListener('DOMCharacterDataModified', this, true); 
 | 
  
 | 
    if (options.childList) 
 | 
      node.addEventListener('DOMNodeInserted', this, true); 
 | 
  
 | 
    if (options.childList || options.subtree) 
 | 
      node.addEventListener('DOMNodeRemoved', this, true); 
 | 
  }, 
 | 
  
 | 
  removeListeners: function() { 
 | 
    this.removeListeners_(this.target); 
 | 
  }, 
 | 
  
 | 
  removeListeners_: function(node) { 
 | 
    var options = this.options; 
 | 
    if (options.attributes) 
 | 
      node.removeEventListener('DOMAttrModified', this, true); 
 | 
  
 | 
    if (options.characterData) 
 | 
      node.removeEventListener('DOMCharacterDataModified', this, true); 
 | 
  
 | 
    if (options.childList) 
 | 
      node.removeEventListener('DOMNodeInserted', this, true); 
 | 
  
 | 
    if (options.childList || options.subtree) 
 | 
      node.removeEventListener('DOMNodeRemoved', this, true); 
 | 
  }, 
 | 
  
 | 
  /** 
 | 
   * Adds a transient observer on node. The transient observer gets removed 
 | 
   * next time we deliver the change records. 
 | 
   * @param {Node} node 
 | 
   */ 
 | 
  addTransientObserver: function(node) { 
 | 
    // Don't add transient observers on the target itself. We already have all 
 | 
    // the required listeners set up on the target. 
 | 
    if (node === this.target) 
 | 
      return; 
 | 
  
 | 
    this.addListeners_(node); 
 | 
    this.transientObservedNodes.push(node); 
 | 
    var registrations = registrationsTable.get(node); 
 | 
    if (!registrations) 
 | 
      registrationsTable.set(node, registrations = []); 
 | 
  
 | 
    // We know that registrations does not contain this because we already 
 | 
    // checked if node === this.target. 
 | 
    registrations.push(this); 
 | 
  }, 
 | 
  
 | 
  removeTransientObservers: function() { 
 | 
    var transientObservedNodes = this.transientObservedNodes; 
 | 
    this.transientObservedNodes = []; 
 | 
  
 | 
    transientObservedNodes.forEach(function(node) { 
 | 
      // Transient observers are never added to the target. 
 | 
      this.removeListeners_(node); 
 | 
  
 | 
      var registrations = registrationsTable.get(node); 
 | 
      for (var i = 0; i < registrations.length; i++) { 
 | 
        if (registrations[i] === this) { 
 | 
          registrations.splice(i, 1); 
 | 
          // Each node can only have one registered observer associated with 
 | 
          // this observer. 
 | 
          break; 
 | 
        } 
 | 
      } 
 | 
    }, this); 
 | 
  }, 
 | 
  
 | 
  handleEvent: function(e) { 
 | 
    // Stop propagation since we are managing the propagation manually. 
 | 
    // This means that other mutation events on the page will not work 
 | 
    // correctly but that is by design. 
 | 
    e.stopImmediatePropagation(); 
 | 
  
 | 
    switch (e.type) { 
 | 
      case 'DOMAttrModified': 
 | 
        // http://dom.spec.whatwg.org/#concept-mo-queue-attributes 
 | 
  
 | 
        var name = e.attrName; 
 | 
        var namespace = e.relatedNode.namespaceURI; 
 | 
        var target = e.target; 
 | 
  
 | 
        // 1. 
 | 
        var record = new getRecord('attributes', target); 
 | 
        record.attributeName = name; 
 | 
        record.attributeNamespace = namespace; 
 | 
  
 | 
        // 2. 
 | 
        var oldValue = null; 
 | 
        if (!(typeof MutationEvent !== 'undefined' && e.attrChange === MutationEvent.ADDITION)) 
 | 
          oldValue = e.prevValue; 
 | 
  
 | 
        forEachAncestorAndObserverEnqueueRecord(target, function(options) { 
 | 
          // 3.1, 4.2 
 | 
          if (!options.attributes) 
 | 
            return; 
 | 
  
 | 
          // 3.2, 4.3 
 | 
          if (options.attributeFilter && options.attributeFilter.length && 
 | 
              options.attributeFilter.indexOf(name) === -1 && 
 | 
              options.attributeFilter.indexOf(namespace) === -1) { 
 | 
            return; 
 | 
          } 
 | 
          // 3.3, 4.4 
 | 
          if (options.attributeOldValue) 
 | 
            return getRecordWithOldValue(oldValue); 
 | 
  
 | 
          // 3.4, 4.5 
 | 
          return record; 
 | 
        }); 
 | 
  
 | 
        break; 
 | 
  
 | 
      case 'DOMCharacterDataModified': 
 | 
        // http://dom.spec.whatwg.org/#concept-mo-queue-characterdata 
 | 
        var target = e.target; 
 | 
  
 | 
        // 1. 
 | 
        var record = getRecord('characterData', target); 
 | 
  
 | 
        // 2. 
 | 
        var oldValue = e.prevValue; 
 | 
  
 | 
  
 | 
        forEachAncestorAndObserverEnqueueRecord(target, function(options) { 
 | 
          // 3.1, 4.2 
 | 
          if (!options.characterData) 
 | 
            return; 
 | 
  
 | 
          // 3.2, 4.3 
 | 
          if (options.characterDataOldValue) 
 | 
            return getRecordWithOldValue(oldValue); 
 | 
  
 | 
          // 3.3, 4.4 
 | 
          return record; 
 | 
        }); 
 | 
  
 | 
        break; 
 | 
  
 | 
      case 'DOMNodeRemoved': 
 | 
        this.addTransientObserver(e.target); 
 | 
        // Fall through. 
 | 
      case 'DOMNodeInserted': 
 | 
        // http://dom.spec.whatwg.org/#concept-mo-queue-childlist 
 | 
        var target = e.relatedNode; 
 | 
        var changedNode = e.target; 
 | 
        var addedNodes, removedNodes; 
 | 
        if (e.type === 'DOMNodeInserted') { 
 | 
          addedNodes = [changedNode]; 
 | 
          removedNodes = []; 
 | 
        } else { 
 | 
  
 | 
          addedNodes = []; 
 | 
          removedNodes = [changedNode]; 
 | 
        } 
 | 
        var previousSibling = changedNode.previousSibling; 
 | 
        var nextSibling = changedNode.nextSibling; 
 | 
  
 | 
        // 1. 
 | 
        var record = getRecord('childList', target); 
 | 
        record.addedNodes = addedNodes; 
 | 
        record.removedNodes = removedNodes; 
 | 
        record.previousSibling = previousSibling; 
 | 
        record.nextSibling = nextSibling; 
 | 
  
 | 
        forEachAncestorAndObserverEnqueueRecord(target, function(options) { 
 | 
          // 2.1, 3.2 
 | 
          if (!options.childList) 
 | 
            return; 
 | 
  
 | 
          // 2.2, 3.3 
 | 
          return record; 
 | 
        }); 
 | 
  
 | 
    } 
 | 
  
 | 
    clearRecords(); 
 | 
  } 
 | 
}; 
 | 
  
 | 
if (!MutationObserver) { 
 | 
  MutationObserver = JsMutationObserver; 
 | 
} 
 | 
  
 | 
module.exports = MutationObserver; 
 |