text: /*
   * 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.
   */
  
  if (typeof window != 'undefined')
  (function(global) {
  
    var registrationsTable = new WeakMap();
  
    // We use setImmediate or postMessage for our future callback.
    var setImmediate = window.msSetImmediate || window.setImmediate;
  
    // 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 =
                e.attrChange === MutationEvent.ADDITION ? null : 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();
      }
    };
  
    global.JsMutationObserver = JsMutationObserver;
  
    if (!global.MutationObserver)
      global.MutationObserver = JsMutationObserver;
  
  
  })(this);