PageSourceSearch

https://staging.cdn-v3.conductrics.com/js/mutation-summary.js

js conductrics.com collected 2026-10-02 05:53:21 UTC 56,638 bytes, 1,396 lines download raw bytes

1// Copyright 2011 Google Inc.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14var __extends = this.__extends || function (d, b) {
15    for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
16    function __() { this.constructor = d; }
17    __.prototype = b.prototype;
18    d.prototype = new __();
19};
20var MutationObserverCtor;
21if (typeof WebKitMutationObserver !== 'undefined')
22    MutationObserverCtor = WebKitMutationObserver;
23else
24    MutationObserverCtor = MutationObserver;
25if (MutationObserverCtor === undefined) {
26    console.error('DOM Mutation Observers are required.');
27    console.error('https://developer.mozilla.org/en-US/docs/DOM/MutationObserver');
28    throw Error('DOM Mutation Observers are required');
29}
30var NodeMap = (function () {
31    function NodeMap() {
32        this.nodes = [];
33        this.values = [];
34    }
35    NodeMap.prototype.isIndex = function (s) {
36        return +s === s >>> 0;
37    };
38    NodeMap.prototype.nodeId = function (node) {
39        var id = node[NodeMap.ID_PROP];
40        if (!id)
41            id = node[NodeMap.ID_PROP] = NodeMap.nextId_++;
42        return id;
43    };
44    NodeMap.prototype.set = function (node, value) {
45        var id = this.nodeId(node);
46        this.nodes[id] = node;
47        this.values[id] = value;
48    };
49    NodeMap.prototype.get = function (node) {
50        var id = this.nodeId(node);
51        return this.values[id];
52    };
53    NodeMap.prototype.has = function (node) {
54        return this.nodeId(node) in this.nodes;
55    };
56    NodeMap.prototype.delete = function (node) {
57        var id = this.nodeId(node);
58        delete this.nodes[id];
59        this.values[id] = undefined;
60    };
61    NodeMap.prototype.keys = function () {
62        var nodes = [];
63        for (var id in this.nodes) {
64            if (!this.isIndex(id))
65                continue;
66            nodes.push(this.nodes[id]);
67        }
68        return nodes;
69    };
70    NodeMap.ID_PROP = '__mutation_summary_node_map_id__';
71    NodeMap.nextId_ = 1;
72    return NodeMap;
73})();
74/**
75 *  var reachableMatchableProduct = [
76 *  //  STAYED_OUT,  ENTERED,     STAYED_IN,   EXITED
77 *    [ STAYED_OUT,  STAYED_OUT,  STAYED_OUT,  STAYED_OUT ], // STAYED_OUT
78 *    [ STAYED_OUT,  ENTERED,     ENTERED,     STAYED_OUT ], // ENTERED
79 *    [ STAYED_OUT,  ENTERED,     STAYED_IN,   EXITED     ], // STAYED_IN
80 *    [ STAYED_OUT,  STAYED_OUT,  EXITED,      EXITED     ]  // EXITED
81 *  ];
82 */
83var Movement;
84(function (Movement) {
85    Movement[Movement["STAYED_OUT"] = 0] = "STAYED_OUT";
86    Movement[Movement["ENTERED"] = 1] = "ENTERED";
87    Movement[Movement["STAYED_IN"] = 2] = "STAYED_IN";
88    Movement[Movement["REPARENTED"] = 3] = "REPARENTED";
89    Movement[Movement["REORDERED"] = 4] = "REORDERED";
90    Movement[Movement["EXITED"] = 5] = "EXITED";
91})(Movement || (Movement = {}));
92function enteredOrExited(changeType) {
93    return changeType === Movement.ENTERED || changeType === Movement.EXITED;
94}
95var NodeChange = (function () {
96    function NodeChange(node, childList, attributes, characterData, oldParentNode, added, attributeOldValues, characterDataOldValue) {
97        if (childList === void 0) { childList = false; }
98        if (attributes === void 0) { attributes = false; }
99        if (characterData === void 0) { characterData = false; }
100        if (oldParentNode === void 0) { oldParentNode = null; }
101        if (added === void 0) { added = false; }
102        if (attributeOldValues === void 0) { attributeOldValues = null; }
103        if (characterDataOldValue === void 0) { characterDataOldValue = null; }
104        this.node = node;
105        this.childList = childList;
106        this.attributes = attributes;
107        this.characterData = characterData;
108        this.oldParentNode = oldParentNode;
109        this.added = added;
110        this.attributeOldValues = attributeOldValues;
111        this.characterDataOldValue = characterDataOldValue;
112        this.isCaseInsensitive =
113            this.node.nodeType === Node.ELEMENT_NODE &&
114                this.node instanceof HTMLElement &&
115                this.node.ownerDocument instanceof HTMLDocument;
116    }
117    NodeChange.prototype.getAttributeOldValue = function (name) {
118        if (!this.attributeOldValues)
119            return undefined;
120        if (this.isCaseInsensitive)
121            name = name.toLowerCase();
122        return this.attributeOldValues[name];
123    };
124    NodeChange.prototype.getAttributeNamesMutated = function () {
125        var names = [];
126        if (!this.attributeOldValues)
127            return names;
128        for (var name in this.attributeOldValues) {
129            names.push(name);
130        }
131        return names;
132    };
133    NodeChange.prototype.attributeMutated = function (name, oldValue) {
134        this.attributes = true;
135        this.attributeOldValues = this.attributeOldValues || {};
136        if (name in this.attributeOldValues)
137            return;
138        this.attributeOldValues[name] = oldValue;
139    };
140    NodeChange.prototype.characterDataMutated = function (oldValue) {
141        if (this.characterData)
142            return;
143        this.characterData = true;
144        this.characterDataOldValue = oldValue;
145    };
146    // Note: is it possible to receive a removal followed by a removal. This
147    // can occur if the removed node is added to an non-observed node, that
148    // node is added to the observed area, and then the node removed from
149    // it.
150    NodeChange.prototype.removedFromParent = function (parent) {
151        this.childList = true;
152        if (this.added || this.oldParentNode)
153            this.added = false;
154        else
155            this.oldParentNode = parent;
156    };
157    NodeChange.prototype.insertedIntoParent = function () {
158        this.childList = true;
159        this.added = true;
160    };
161    // An node's oldParent is
162    //   -its present parent, if its parentNode was not changed.
163    //   -null if the first thing that happened to it was an add.
164    //   -the node it was removed from if the first thing that happened to it
165    //      was a remove.
166    NodeChange.prototype.getOldParent = function () {
167        if (this.childList) {
168            if (this.oldParentNode)
169                return this.oldParentNode;
170            if (this.added)
171                return null;
172        }
173        return this.node.parentNode;
174    };
175    return NodeChange;
176})();
177var ChildListChange = (function () {
178    function ChildListChange() {
179        this.added = new NodeMap();
180        this.removed = new NodeMap();
181        this.maybeMoved = new NodeMap();
182        this.oldPrevious = new NodeMap();
183        this.moved = undefined;
184    }
185    return ChildListChange;
186})();
187var TreeChanges = (function (_super) {
188    __extends(TreeChanges, _super);
189    function TreeChanges(rootNode, mutations) {
190        _super.call(this);
191        this.rootNode = rootNode;
192        this.reachableCache = undefined;
193        this.wasReachableCache = undefined;
194        this.anyParentsChanged = false;
195        this.anyAttributesChanged = false;
196        this.anyCharacterDataChanged = false;
197        for (var m = 0; m < mutations.length; m++) {
198            var mutation = mutations[m];
199            switch (mutation.type) {
200                case 'childList':
201                    this.anyParentsChanged = true;
202                    for (var i = 0; i < mutation.removedNodes.length; i++) {
203                        var node = mutation.removedNodes[i];
204                        this.getChange(node).removedFromParent(mutation.target);
205                    }
206                    for (var i = 0; i < mutation.addedNodes.length; i++) {
207                        var node = mutation.addedNodes[i];
208                        this.getChange(node).insertedIntoParent();
209                    }
210                    break;
211                case 'attributes':
212                    this.anyAttributesChanged = true;
213                    var change = this.getChange(mutation.target);
214                    change.attributeMutated(mutation.attributeName, mutation.oldValue);
215                    break;
216                case 'characterData':
217                    this.anyCharacterDataChanged = true;
218                    var change = this.getChange(mutation.target);
219                    change.characterDataMutated(mutation.oldValue);
220                    break;
221            }
222        }
223    }
224    TreeChanges.prototype.getChange = function (node) {
225        var change = this.get(node);
226        if (!change) {
227            change = new NodeChange(node);
228            this.set(node, change);
229        }
230        return change;
231    };
232    TreeChanges.prototype.getOldParent = function (node) {
233        var change = this.get(node);
234        return change ? change.getOldParent() : node.parentNode;
235    };
236    TreeChanges.prototype.getIsReachable = function (node) {
237        if (node === this.rootNode)
238            return true;
239        if (!node)
240            return false;
241        this.reachableCache = this.reachableCache || new NodeMap();
242        var isReachable = this.reachableCache.get(node);
243        if (isReachable === undefined) {
244            isReachable = this.getIsReachable(node.parentNode);
245            this.reachableCache.set(node, isReachable);
246        }
247        return isReachable;
248    };
249    // A node wasReachable if its oldParent wasReachable.
250    TreeChanges.prototype.getWasReachable = function (node) {
251        if (node === this.rootNode)
252            return true;
253        if (!node)
254            return false;
255        this.wasReachableCache = this.wasReachableCache || new NodeMap();
256        var wasReachable = this.wasReachableCache.get(node);
257        if (wasReachable === undefined) {
258            wasReachable = this.getWasReachable(this.getOldParent(node));
259            this.wasReachableCache.set(node, wasReachable);
260        }
261        return wasReachable;
262    };
263    TreeChanges.prototype.reachabilityChange = function (node) {
264        if (this.getIsReachable(node)) {
265            return this.getWasReachable(node) ?
266                Movement.STAYED_IN : Movement.ENTERED;
267        }
268        return this.getWasReachable(node) ?
269            Movement.EXITED : Movement.STAYED_OUT;
270    };
271    return TreeChanges;
272})(NodeMap);
273var MutationProjection = (function () {
274    // TOOD(any)
275    function MutationProjection(rootNode, mutations, selectors, calcReordered, calcOldPreviousSibling) {
276        this.rootNode = rootNode;
277        this.mutations = mutations;
278        this.selectors = selectors;
279        this.calcReordered = calcReordered;
280        this.calcOldPreviousSibling = calcOldPreviousSibling;
281        this.treeChanges = new TreeChanges(rootNode, mutations);
282        this.entered = [];
283        this.exited = [];
284        this.stayedIn = new NodeMap();
285        this.visited = new NodeMap();
286        this.childListChangeMap = undefined;
287        this.characterDataOnly = undefined;
288        this.matchCache = undefined;
289        this.processMutations();
290    }
291    MutationProjection.prototype.processMutations = function () {
292        if (!this.treeChanges.anyParentsChanged &&
293            !this.treeChanges.anyAttributesChanged)
294            return;
295        var changedNodes = this.treeChanges.keys();
296        for (var i = 0; i < changedNodes.length; i++) {
297            this.visitNode(changedNodes[i], undefined);
298        }
299    };
300    MutationProjection.prototype.visitNode = function (node, parentReachable) {
301        if (this.visited.has(node))
302            return;
303        this.visited.set(node, true);
304        var change = this.treeChanges.get(node);
305        var reachable = parentReachable;
306        // node inherits its parent's reachability change unless
307        // its parentNode was mutated.
308        if ((change && change.childList) || reachable == undefined)
309            reachable = this.treeChanges.reachabilityChange(node);
310        if (reachable === Movement.STAYED_OUT)
311            return;
312        // Cache match results for sub-patterns.
313        this.matchabilityChange(node);
314        if (reachable === Movement.ENTERED) {
315            this.entered.push(node);
316        }
317        else if (reachable === Movement.EXITED) {
318            this.exited.push(node);
319            this.ensureHasOldPreviousSiblingIfNeeded(node);
320        }
321        else if (reachable === Movement.STAYED_IN) {
322            var movement = Movement.STAYED_IN;
323            if (change && change.childList) {
324                if (change.oldParentNode !== node.parentNode) {
325                    movement = Movement.REPARENTED;
326                    this.ensureHasOldPreviousSiblingIfNeeded(node);
327                }
328                else if (this.calcReordered && this.wasReordered(node)) {
329                    movement = Movement.REORDERED;
330                }
331            }
332            this.stayedIn.set(node, movement);
333        }
334        if (reachable === Movement.STAYED_IN)
335            return;
336        // reachable === ENTERED || reachable === EXITED.
337        for (var child = node.firstChild; child; child = child.nextSibling) {
338            this.visitNode(child, reachable);
339        }
340    };
341    MutationProjection.prototype.ensureHasOldPreviousSiblingIfNeeded = function (node) {
342        if (!this.calcOldPreviousSibling)
343            return;
344        this.processChildlistChanges();
345        var parentNode = node.parentNode;
346        var nodeChange = this.treeChanges.get(node);
347        if (nodeChange && nodeChange.oldParentNode)
348            parentNode = nodeChange.oldParentNode;
349        var change = this.childListChangeMap.get(parentNode);
350        if (!change) {
351            change = new ChildListChange();
352            this.childListChangeMap.set(parentNode, change);
353        }
354        if (!change.oldPrevious.has(node)) {
355            change.oldPrevious.set(node, node.previousSibling);
356        }
357    };
358    MutationProjection.prototype.getChanged = function (summary, selectors, characterDataOnly) {
359        this.selectors = selectors;
360        this.characterDataOnly = characterDataOnly;
361        for (var i = 0; i < this.entered.length; i++) {
362            var node = this.entered[i];
363            var matchable = this.matchabilityChange(node);
364            if (matchable === Movement.ENTERED || matchable === Movement.STAYED_IN)
365                summary.added.push(node);
366        }
367        var stayedInNodes = this.stayedIn.keys();
368        for (var i = 0; i < stayedInNodes.length; i++) {
369            var node = stayedInNodes[i];
370            var matchable = this.matchabilityChange(node);
371            if (matchable === Movement.ENTERED) {
372                summary.added.push(node);
373            }
374            else if (matchable === Movement.EXITED) {
375                summary.removed.push(node);
376            }
377            else if (matchable === Movement.STAYED_IN && (summary.reparented || summary.reordered)) {
378                var movement = this.stayedIn.get(node);
379                if (summary.reparented && movement === Movement.REPARENTED)
380                    summary.reparented.push(node);
381                else if (summary.reordered && movement === Movement.REORDERED)
382                    summary.reordered.push(node);
383            }
384        }
385        for (var i = 0; i < this.exited.length; i++) {
386            var node = this.exited[i];
387            var matchable = this.matchabilityChange(node);
388            if (matchable === Movement.EXITED || matchable === Movement.STAYED_IN)
389                summary.removed.push(node);
390        }
391    };
392    MutationProjection.prototype.getOldParentNode = function (node) {
393        var change = this.treeChanges.get(node);
394        if (change && change.childList)
395            return change.oldParentNode ? change.oldParentNode : null;
396        var reachabilityChange = this.treeChanges.reachabilityChange(node);
397        if (reachabilityChange === Movement.STAYED_OUT || reachabilityChange === Movement.ENTERED)
398            throw Error('getOldParentNode requested on invalid node.');
399        return node.parentNode;
400    };
401    MutationProjection.prototype.getOldPreviousSibling = function (node) {
402        var parentNode = node.parentNode;
403        var nodeChange = this.treeChanges.get(node);
404        if (nodeChange && nodeChange.oldParentNode)
405            parentNode = nodeChange.oldParentNode;
406        var change = this.childListChangeMap.get(parentNode);
407        if (!change)
408            throw Error('getOldPreviousSibling requested on invalid node.');
409        return change.oldPrevious.get(node);
410    };
411    MutationProjection.prototype.getOldAttribute = function (element, attrName) {
412        var change = this.treeChanges.get(element);
413        if (!change || !change.attributes)
414            throw Error('getOldAttribute requested on invalid node.');
415        var value = change.getAttributeOldValue(attrName);
416        if (value === undefined)
417            throw Error('getOldAttribute requested for unchanged attribute name.');
418        return value;
419    };
420    MutationProjection.prototype.attributeChangedNodes = function (includeAttributes) {
421        if (!this.treeChanges.anyAttributesChanged)
422            return {}; // No attributes mutations occurred.
423        var attributeFilter;
424        var caseInsensitiveFilter;
425        if (includeAttributes) {
426            attributeFilter = {};
427            caseInsensitiveFilter = {};
428            for (var i = 0; i < includeAttributes.length; i++) {
429                var attrName = includeAttributes[i];
430                attributeFilter[attrName] = true;
431                caseInsensitiveFilter[attrName.toLowerCase()] = attrName;
432            }
433        }
434        var result = {};
435        var nodes = this.treeChanges.keys();
436        for (var i = 0; i < nodes.length; i++) {
437            var node = nodes[i];
438            var change = this.treeChanges.get(node);
439            if (!change.attributes)
440                continue;
441            if (Movement.STAYED_IN !== this.treeChanges.reachabilityChange(node) ||
442                Movement.STAYED_IN !== this.matchabilityChange(node)) {
443                continue;
444            }
445            var element = node;
446            var changedAttrNames = change.getAttributeNamesMutated();
447            for (var j = 0; j < changedAttrNames.length; j++) {
448                var attrName = changedAttrNames[j];
449                if (attributeFilter &&
450                    !attributeFilter[attrName] &&
451                    !(change.isCaseInsensitive && caseInsensitiveFilter[attrName])) {
452                    continue;
453                }
454                var oldValue = change.getAttributeOldValue(attrName);
455                if (oldValue === element.getAttribute(attrName))
456                    continue;
457                if (caseInsensitiveFilter && change.isCaseInsensitive)
458                    attrName = caseInsensitiveFilter[attrName];
459                result[attrName] = result[attrName] || [];
460                result[attrName].push(element);
461            }
462        }
463        return result;
464    };
465    MutationProjection.prototype.getOldCharacterData = function (node) {
466        var change = this.treeChanges.get(node);
467        if (!change || !change.characterData)
468            throw Error('getOldCharacterData requested on invalid node.');
469        return change.characterDataOldValue;
470    };
471    MutationProjection.prototype.getCharacterDataChanged = function () {
472        if (!this.treeChanges.anyCharacterDataChanged)
473            return []; // No characterData mutations occurred.
474        var nodes = this.treeChanges.keys();
475        var result = [];
476        for (var i = 0; i < nodes.length; i++) {
477            var target = nodes[i];
478            if (Movement.STAYED_IN !== this.treeChanges.reachabilityChange(target))
479                continue;
480            var change = this.treeChanges.get(target);
481            if (!change.characterData ||
482                target.textContent == change.characterDataOldValue)
483                continue;
484            result.push(target);
485        }
486        return result;
487    };
488    MutationProjection.prototype.computeMatchabilityChange = function (selector, el) {
489        if (!this.matchCache)
490            this.matchCache = [];
491        if (!this.matchCache[selector.uid])
492            this.matchCache[selector.uid] = new NodeMap();
493        var cache = this.matchCache[selector.uid];
494        var result = cache.get(el);
495        if (result === undefined) {
496            result = selector.matchabilityChange(el, this.treeChanges.get(el));
497            cache.set(el, result);
498        }
499        return result;
500    };
501    MutationProjection.prototype.matchabilityChange = function (node) {
502        var _this = this;
503        // TODO(rafaelw): Include PI, CDATA?
504        // Only include text nodes.
505        if (this.characterDataOnly) {
506            switch (node.nodeType) {
507                case Node.COMMENT_NODE:
508                case Node.TEXT_NODE:
509                    return Movement.STAYED_IN;
510                default:
511                    return Movement.STAYED_OUT;
512            }
513        }
514        // No element filter. Include all nodes.
515        if (!this.selectors)
516            return Movement.STAYED_IN;
517        // Element filter. Exclude non-elements.
518        if (node.nodeType !== Node.ELEMENT_NODE)
519            return Movement.STAYED_OUT;
520        var el = node;
521        var matchChanges = this.selectors.map(function (selector) {
522            return _this.computeMatchabilityChange(selector, el);
523        });
524        var accum = Movement.STAYED_OUT;
525        var i = 0;
526        while (accum !== Movement.STAYED_IN && i < matchChanges.length) {
527            switch (matchChanges[i]) {
528                case Movement.STAYED_IN:
529                    accum = Movement.STAYED_IN;
530                    break;
531                case Movement.ENTERED:
532                    if (accum === Movement.EXITED)
533                        accum = Movement.STAYED_IN;
534                    else
535                        accum = Movement.ENTERED;
536                    break;
537                case Movement.EXITED:
538                    if (accum === Movement.ENTERED)
539                        accum = Movement.STAYED_IN;
540                    else
541                        accum = Movement.EXITED;
542                    break;
543            }
544            i++;
545        }
546        return accum;
547    };
548    MutationProjection.prototype.getChildlistChange = function (el) {
549        var change = this.childListChangeMap.get(el);
550        if (!change) {
551            change = new ChildListChange();
552            this.childListChangeMap.set(el, change);
553        }
554        return change;
555    };
556    MutationProjection.prototype.processChildlistChanges = function () {
557        if (this.childListChangeMap)
558            return;
559        this.childListChangeMap = new NodeMap();
560        for (var i = 0; i < this.mutations.length; i++) {
561            var mutation = this.mutations[i];
562            if (mutation.type != 'childList')
563                continue;
564            if (this.treeChanges.reachabilityChange(mutation.target) !== Movement.STAYED_IN &&
565                !this.calcOldPreviousSibling)
566                continue;
567            var change = this.getChildlistChange(mutation.target);
568            var oldPrevious = mutation.previousSibling;
569            function recordOldPrevious(node, previous) {
570                if (!node ||
571                    change.oldPrevious.has(node) ||
572                    change.added.has(node) ||
573                    change.maybeMoved.has(node))
574                    return;
575                if (previous &&
576                    (change.added.has(previous) ||
577                        change.maybeMoved.has(previous)))
578                    return;
579                change.oldPrevious.set(node, previous);
580            }
581            for (var j = 0; j < mutation.removedNodes.length; j++) {
582                var node = mutation.removedNodes[j];
583                recordOldPrevious(node, oldPrevious);
584                if (change.added.has(node)) {
585                    change.added.delete(node);
586                }
587                else {
588                    change.removed.set(node, true);
589                    change.maybeMoved.delete(node);
590                }
591                oldPrevious = node;
592            }
593            recordOldPrevious(mutation.nextSibling, oldPrevious);
594            for (var j = 0; j < mutation.addedNodes.length; j++) {
595                var node = mutation.addedNodes[j];
596                if (change.removed.has(node)) {
597                    change.removed.delete(node);
598                    change.maybeMoved.set(node, true);
599                }
600                else {
601                    change.added.set(node, true);
602                }
603            }
604        }
605    };
606    MutationProjection.prototype.wasReordered = function (node) {
607        if (!this.treeChanges.anyParentsChanged)
608            return false;
609        this.processChildlistChanges();
610        var parentNode = node.parentNode;
611        var nodeChange = this.treeChanges.get(node);
612        if (nodeChange && nodeChange.oldParentNode)
613            parentNode = nodeChange.oldParentNode;
614        var change = this.childListChangeMap.get(parentNode);
615        if (!change)
616            return false;
617        if (change.moved)
618            return change.moved.get(node);
619        change.moved = new NodeMap();
620        var pendingMoveDecision = new NodeMap();
621        function isMoved(node) {
622            if (!node)
623                return false;
624            if (!change.maybeMoved.has(node))
625                return false;
626            var didMove = change.moved.get(node);
627            if (didMove !== undefined)
628                return didMove;
629            if (pendingMoveDecision.has(node)) {
630                didMove = true;
631            }
632            else {
633                pendingMoveDecision.set(node, true);
634                didMove = getPrevious(node) !== getOldPrevious(node);
635            }
636            if (pendingMoveDecision.has(node)) {
637                pendingMoveDecision.delete(node);
638                change.moved.set(node, didMove);
639            }
640            else {
641                didMove = change.moved.get(node);
642            }
643            return didMove;
644        }
645        var oldPreviousCache = new NodeMap();
646        function getOldPrevious(node) {
647            var oldPrevious = oldPreviousCache.get(node);
648            if (oldPrevious !== undefined)
649                return oldPrevious;
650            oldPrevious = change.oldPrevious.get(node);
651            while (oldPrevious &&
652                (change.removed.has(oldPrevious) || isMoved(oldPrevious))) {
653                oldPrevious = getOldPrevious(oldPrevious);
654            }
655            if (oldPrevious === undefined)
656                oldPrevious = node.previousSibling;
657            oldPreviousCache.set(node, oldPrevious);
658            return oldPrevious;
659        }
660        var previousCache = new NodeMap();
661        function getPrevious(node) {
662            if (previousCache.has(node))
663                return previousCache.get(node);
664            var previous = node.previousSibling;
665            while (previous && (change.added.has(previous) || isMoved(previous)))
666                previous = previous.previousSibling;
667            previousCache.set(node, previous);
668            return previous;
669        }
670        change.maybeMoved.keys().forEach(isMoved);
671        return change.moved.get(node);
672    };
673    return MutationProjection;
674})();
675var Summary = (function () {
676    function Summary(projection, query) {
677        var _this = this;
678        this.projection = projection;
679        this.added = [];
680        this.removed = [];
681        this.reparented = query.all || query.element || query.characterData ? [] : undefined;
682        this.reordered = query.all ? [] : undefined;
683        projection.getChanged(this, query.elementFilter, query.characterData);
684        if (query.all || query.attribute || query.attributeList) {
685            var filter = query.attribute ? [query.attribute] : query.attributeList;
686            var attributeChanged = projection.attributeChangedNodes(filter);
687            if (query.attribute) {
688                this.valueChanged = attributeChanged[query.attribute] || [];
689            }
690            else {
691                this.attributeChanged = attributeChanged;
692                if (query.attributeList) {
693                    query.attributeList.forEach(function (attrName) {
694                        if (!_this.attributeChanged.hasOwnProperty(attrName))
695                            _this.attributeChanged[attrName] = [];
696                    });
697                }
698            }
699        }
700        if (query.all || query.characterData) {
701            var characterDataChanged = projection.getCharacterDataChanged();
702            if (query.characterData)
703                this.valueChanged = characterDataChanged;
704            else
705                this.characterDataChanged = characterDataChanged;
706        }
707        if (this.reordered)
708            this.getOldPreviousSibling = projection.getOldPreviousSibling.bind(projection);
709    }
710    Summary.prototype.getOldParentNode = function (node) {
711        return this.projection.getOldParentNode(node);
712    };
713    Summary.prototype.getOldAttribute = function (node, name) {
714        return this.projection.getOldAttribute(node, name);
715    };
716    Summary.prototype.getOldCharacterData = function (node) {
717        return this.projection.getOldCharacterData(node);
718    };
719    Summary.prototype.getOldPreviousSibling = function (node) {
720        return this.projection.getOldPreviousSibling(node);
721    };
722    return Summary;
723})();
724// TODO(rafaelw): Allow ':' and '.' as valid name characters.
725var validNameInitialChar = /[a-zA-Z_]+/;
726var validNameNonInitialChar = /[a-zA-Z0-9_\-]+/;
727// TODO(rafaelw): Consider allowing backslash in the attrValue.
728// TODO(rafaelw): There's got a to be way to represent this state machine
729// more compactly???
730function escapeQuotes(value) {
731    return '"' + value.replace(/"/, '\\\"') + '"';
732}
733var Qualifier = (function () {
734    function Qualifier() {
735    }
736    Qualifier.prototype.matches = function (oldValue) {
737        if (oldValue === null)
738            return false;
739        if (this.attrValue === undefined)
740            return true;
741        if (!this.contains)
742            return this.attrValue == oldValue;
743        var tokens = oldValue.split(' ');
744        for (var i = 0; i < tokens.length; i++) {
745            if (this.attrValue === tokens[i])
746                return true;
747        }
748        return false;
749    };
750    Qualifier.prototype.toString = function () {
751        if (this.attrName === 'class' && this.contains)
752            return '.' + this.attrValue;
753        if (this.attrName === 'id' && !this.contains)
754            return '#' + this.attrValue;
755        if (this.contains)
756            return '[' + this.attrName + '~=' + escapeQuotes(this.attrValue) + ']';
757        if ('attrValue' in this)
758            return '[' + this.attrName + '=' + escapeQuotes(this.attrValue) + ']';
759        return '[' + this.attrName + ']';
760    };
761    return Qualifier;
762})();
763var Selector = (function () {
764    function Selector() {
765        this.uid = Selector.nextUid++;
766        this.qualifiers = [];
767    }
768    Object.defineProperty(Selector.prototype, "caseInsensitiveTagName", {
769        get: function () {
770            return this.tagName.toUpperCase();
771        },
772        enumerable: true,
773        configurable: true
774    });
775    Object.defineProperty(Selector.prototype, "selectorString", {
776        get: function () {
777            return this.tagName + this.qualifiers.join('');
778        },
779        enumerable: true,
780        configurable: true
781    });
782    Selector.prototype.isMatching = function (el) {
783        return el[Selector.matchesSelector](this.selectorString);
784    };
785    Selector.prototype.wasMatching = function (el, change, isMatching) {
786        if (!change || !change.attributes)
787            return isMatching;
788        var tagName = change.isCaseInsensitive ? this.caseInsensitiveTagName : this.tagName;
789        if (tagName !== '*' && tagName !== el.tagName)
790            return false;
791        var attributeOldValues = [];
792        var anyChanged = false;
793        for (var i = 0; i < this.qualifiers.length; i++) {
794            var qualifier = this.qualifiers[i];
795            var oldValue = change.getAttributeOldValue(qualifier.attrName);
796            attributeOldValues.push(oldValue);
797            anyChanged = anyChanged || (oldValue !== undefined);
798        }
799        if (!anyChanged)
800            return isMatching;
801        for (var i = 0; i < this.qualifiers.length; i++) {
802            var qualifier = this.qualifiers[i];
803            var oldValue = attributeOldValues[i];
804            if (oldValue === undefined)
805                oldValue = el.getAttribute(qualifier.attrName);
806            if (!qualifier.matches(oldValue))
807                return false;
808        }
809        return true;
810    };
811    Selector.prototype.matchabilityChange = function (el, change) {
812        var isMatching = this.isMatching(el);
813        if (isMatching)
814            return this.wasMatching(el, change, isMatching) ? Movement.STAYED_IN : Movement.ENTERED;
815        else
816            return this.wasMatching(el, change, isMatching) ? Movement.EXITED : Movement.STAYED_OUT;
817    };
818    Selector.parseSelectors = function (input) {
819        var selectors = [];
820        var currentSelector;
821        var currentQualifier;
822        function newSelector() {
823            if (currentSelector) {
824                if (currentQualifier) {
825                    currentSelector.qualifiers.push(currentQualifier);
826                    currentQualifier = undefined;
827                }
828                selectors.push(currentSelector);
829            }
830            currentSelector = new Selector();
831        }
832        function newQualifier() {
833            if (currentQualifier)
834                currentSelector.qualifiers.push(currentQualifier);
835            currentQualifier = new Qualifier();
836        }
837        var WHITESPACE = /\s/;
838        var valueQuoteChar;
839        var SYNTAX_ERROR = 'Invalid or unsupported selector syntax.';
840        var SELECTOR = 1;
841        var TAG_NAME = 2;
842        var QUALIFIER = 3;
843        var QUALIFIER_NAME_FIRST_CHAR = 4;
844        var QUALIFIER_NAME = 5;
845        var ATTR_NAME_FIRST_CHAR = 6;
846        var ATTR_NAME = 7;
847        var EQUIV_OR_ATTR_QUAL_END = 8;
848        var EQUAL = 9;
849        var ATTR_QUAL_END = 10;
850        var VALUE_FIRST_CHAR = 11;
851        var VALUE = 12;
852        var QUOTED_VALUE = 13;
853        var SELECTOR_SEPARATOR = 14;
854        var state = SELECTOR;
855        var i = 0;
856        while (i < input.length) {
857            var c = input[i++];
858            switch (state) {
859                case SELECTOR:
860                    if (c.match(validNameInitialChar)) {
861                        newSelector();
862                        currentSelector.tagName = c;
863                        state = TAG_NAME;
864                        break;
865                    }
866                    if (c == '*') {
867                        newSelector();
868                        currentSelector.tagName = '*';
869                        state = QUALIFIER;
870                        break;
871                    }
872                    if (c == '.') {
873                        newSelector();
874                        newQualifier();
875                        currentSelector.tagName = '*';
876                        currentQualifier.attrName = 'class';
877                        currentQualifier.contains = true;
878                        state = QUALIFIER_NAME_FIRST_CHAR;
879                        break;
880                    }
881                    if (c == '#') {
882                        newSelector();
883                        newQualifier();
884                        currentSelector.tagName = '*';
885                        currentQualifier.attrName = 'id';
886                        state = QUALIFIER_NAME_FIRST_CHAR;
887                        break;
888                    }
889                    if (c == '[') {
890                        newSelector();
891                        newQualifier();
892                        currentSelector.tagName = '*';
893                        currentQualifier.attrName = '';
894                        state = ATTR_NAME_FIRST_CHAR;
895                        break;
896                    }
897                    if (c.match(WHITESPACE))
898                        break;
899                    throw Error(SYNTAX_ERROR);
900                case TAG_NAME:
901                    if (c.match(validNameNonInitialChar)) {
902                        currentSelector.tagName += c;
903                        break;
904                    }
905                    if (c == '.') {
906                        newQualifier();
907                        currentQualifier.attrName = 'class';
908                        currentQualifier.contains = true;
909                        state = QUALIFIER_NAME_FIRST_CHAR;
910                        break;
911                    }
912                    if (c == '#') {
913                        newQualifier();
914                        currentQualifier.attrName = 'id';
915                        state = QUALIFIER_NAME_FIRST_CHAR;
916                        break;
917                    }
918                    if (c == '[') {
919                        newQualifier();
920                        currentQualifier.attrName = '';
921                        state = ATTR_NAME_FIRST_CHAR;
922                        break;
923                    }
924                    if (c.match(WHITESPACE)) {
925                        state = SELECTOR_SEPARATOR;
926                        break;
927                    }
928                    if (c == ',') {
929                        state = SELECTOR;
930                        break;
931                    }
932                    throw Error(SYNTAX_ERROR);
933                case QUALIFIER:
934                    if (c == '.') {
935                        newQualifier();
936                        currentQualifier.attrName = 'class';
937                        currentQualifier.contains = true;
938                        state = QUALIFIER_NAME_FIRST_CHAR;
939                        break;
940                    }
941                    if (c == '#') {
942                        newQualifier();
943                        currentQualifier.attrName = 'id';
944                        state = QUALIFIER_NAME_FIRST_CHAR;
945                        break;
946                    }
947                    if (c == '[') {
948                        newQualifier();
949                        currentQualifier.attrName = '';
950                        state = ATTR_NAME_FIRST_CHAR;
951                        break;
952                    }
953                    if (c.match(WHITESPACE)) {
954                        state = SELECTOR_SEPARATOR;
955                        break;
956                    }
957                    if (c == ',') {
958                        state = SELECTOR;
959                        break;
960                    }
961                    throw Error(SYNTAX_ERROR);
962                case QUALIFIER_NAME_FIRST_CHAR:
963                    if (c.match(validNameInitialChar)) {
964                        currentQualifier.attrValue = c;
965                        state = QUALIFIER_NAME;
966                        break;
967                    }
968                    throw Error(SYNTAX_ERROR);
969                case QUALIFIER_NAME:
970                    if (c.match(validNameNonInitialChar)) {
971                        currentQualifier.attrValue += c;
972                        break;
973                    }
974                    if (c == '.') {
975                        newQualifier();
976                        currentQualifier.attrName = 'class';
977                        currentQualifier.contains = true;
978                        state = QUALIFIER_NAME_FIRST_CHAR;
979                        break;
980                    }
981                    if (c == '#') {
982                        newQualifier();
983                        currentQualifier.attrName = 'id';
984                        state = QUALIFIER_NAME_FIRST_CHAR;
985                        break;
986                    }
987                    if (c == '[') {
988                        newQualifier();
989                        state = ATTR_NAME_FIRST_CHAR;
990                        break;
991                    }
992                    if (c.match(WHITESPACE)) {
993                        state = SELECTOR_SEPARATOR;
994                        break;
995                    }
996                    if (c == ',') {
997                        state = SELECTOR;
998                        break;
999                    }
1000                    throw Error(SYNTAX_ERROR);
1001                case ATTR_NAME_FIRST_CHAR:
1002                    if (c.match(validNameInitialChar)) {
1003                        currentQualifier.attrName = c;
1004                        state = ATTR_NAME;
1005                        break;
1006                    }
1007                    if (c.match(WHITESPACE))
1008                        break;
1009                    throw Error(SYNTAX_ERROR);
1010                case ATTR_NAME:
1011                    if (c.match(validNameNonInitialChar)) {
1012                        currentQualifier.attrName += c;
1013                        break;
1014                    }
1015                    if (c.match(WHITESPACE)) {
1016                        state = EQUIV_OR_ATTR_QUAL_END;
1017                        break;
1018                    }
1019                    if (c == '~') {
1020                        currentQualifier.contains = true;
1021                        state = EQUAL;
1022                        break;
1023                    }
1024                    if (c == '=') {
1025                        currentQualifier.attrValue = '';
1026                        state = VALUE_FIRST_CHAR;
1027                        break;
1028                    }
1029                    if (c == ']') {
1030                        state = QUALIFIER;
1031                        break;
1032                    }
1033                    throw Error(SYNTAX_ERROR);
1034                case EQUIV_OR_ATTR_QUAL_END:
1035                    if (c == '~') {
1036                        currentQualifier.contains = true;
1037                        state = EQUAL;
1038                        break;
1039                    }
1040                    if (c == '=') {
1041                        currentQualifier.attrValue = '';
1042                        state = VALUE_FIRST_CHAR;
1043                        break;
1044                    }
1045                    if (c == ']') {
1046                        state = QUALIFIER;
1047                        break;
1048                    }
1049                    if (c.match(WHITESPACE))
1050                        break;
1051                    throw Error(SYNTAX_ERROR);
1052                case EQUAL:
1053                    if (c == '=') {
1054                        currentQualifier.attrValue = '';
1055                        state = VALUE_FIRST_CHAR;
1056                        break;
1057                    }
1058                    throw Error(SYNTAX_ERROR);
1059                case ATTR_QUAL_END:
1060                    if (c == ']') {
1061                        state = QUALIFIER;
1062                        break;
1063                    }
1064                    if (c.match(WHITESPACE))
1065                        break;
1066                    throw Error(SYNTAX_ERROR);
1067                case VALUE_FIRST_CHAR:
1068                    if (c.match(WHITESPACE))
1069                        break;
1070                    if (c == '"' || c == "'") {
1071                        valueQuoteChar = c;
1072                        state = QUOTED_VALUE;
1073                        break;
1074                    }
1075                    currentQualifier.attrValue += c;
1076                    state = VALUE;
1077                    break;
1078                case VALUE:
1079                    if (c.match(WHITESPACE)) {
1080                        state = ATTR_QUAL_END;
1081                        break;
1082                    }
1083                    if (c == ']') {
1084                        state = QUALIFIER;
1085                        break;
1086                    }
1087                    if (c == "'" || c == '"')
1088                        throw Error(SYNTAX_ERROR);
1089                    currentQualifier.attrValue += c;
1090                    break;
1091                case QUOTED_VALUE:
1092                    if (c == valueQuoteChar) {
1093                        state = ATTR_QUAL_END;
1094                        break;
1095                    }
1096                    currentQualifier.attrValue += c;
1097                    break;
1098                case SELECTOR_SEPARATOR:
1099                    if (c.match(WHITESPACE))
1100                        break;
1101                    if (c == ',') {
1102                        state = SELECTOR;
1103                        break;
1104                    }
1105                    throw Error(SYNTAX_ERROR);
1106            }
1107        }
1108        switch (state) {
1109            case SELECTOR:
1110            case TAG_NAME:
1111            case QUALIFIER:
1112            case QUALIFIER_NAME:
1113            case SELECTOR_SEPARATOR:
1114                // Valid end states.
1115                newSelector();
1116                break;
1117            default:
1118                throw Error(SYNTAX_ERROR);
1119        }
1120        if (!selectors.length)
1121            throw Error(SYNTAX_ERROR);
1122        return selectors;
1123    };
1124    Selector.nextUid = 1;
1125    Selector.matchesSelector = (function () {
1126        var element = document.createElement('div');
1127        if (typeof element['webkitMatchesSelector'] === 'function')
1128            return 'webkitMatchesSelector';
1129        if (typeof element['mozMatchesSelector'] === 'function')
1130            return 'mozMatchesSelector';
1131        if (typeof element['msMatchesSelector'] === 'function')
1132            return 'msMatchesSelector';
1133        return 'matchesSelector';
1134    })();
1135    return Selector;
1136})();
1137var attributeFilterPattern = /^([a-zA-Z:_]+[a-zA-Z0-9_\-:\.]*)$/;
1138function validateAttribute(attribute) {
1139    if (typeof attribute != 'string')
1140        throw Error('Invalid request opion. attribute must be a non-zero length string.');
1141    attribute = attribute.trim();
1142    if (!attribute)
1143        throw Error('Invalid request opion. attribute must be a non-zero length string.');
1144    if (!attribute.match(attributeFilterPattern))
1145        throw Error('Invalid request option. invalid attribute name: ' + attribute);
1146    return attribute;
1147}
1148function validateElementAttributes(attribs) {
1149    if (!attribs.trim().length)
1150        throw Error('Invalid request option: elementAttributes must contain at least one attribute.');
1151    var lowerAttributes = {};
1152    var attributes = {};
1153    var tokens = attribs.split(/\s+/);
1154    for (var i = 0; i < tokens.length; i++) {
1155        var name = tokens[i];
1156        if (!name)
1157            continue;
1158        var name = validateAttribute(name);
1159        var nameLower = name.toLowerCase();
1160        if (lowerAttributes[nameLower])
1161            throw Error('Invalid request option: observing multiple case variations of the same attribute is not supported.');
1162        attributes[name] = true;
1163        lowerAttributes[nameLower] = true;
1164    }
1165    return Object.keys(attributes);
1166}
1167function elementFilterAttributes(selectors) {
1168    var attributes = {};
1169    selectors.forEach(function (selector) {
1170        selector.qualifiers.forEach(function (qualifier) {
1171            attributes[qualifier.attrName] = true;
1172        });
1173    });
1174    return Object.keys(attributes);
1175}
1176var MutationSummary = (function () {
1177    function MutationSummary(opts) {
1178        var _this = this;
1179        this.connected = false;
1180        this.options = MutationSummary.validateOptions(opts);
1181        this.observerOptions = MutationSummary.createObserverOptions(this.options.queries);
1182        this.root = this.options.rootNode;
1183        this.callback = this.options.callback;
1184        this.elementFilter = Array.prototype.concat.apply([], this.options.queries.map(function (query) {
1185            return query.elementFilter ? query.elementFilter : [];
1186        }));
1187        if (!this.elementFilter.length)
1188            this.elementFilter = undefined;
1189        this.calcReordered = this.options.queries.some(function (query) {
1190            return query.all;
1191        });
1192        this.queryValidators = []; // TODO(rafaelw): Shouldn't always define this.
1193        if (MutationSummary.createQueryValidator) {
1194            this.queryValidators = this.options.queries.map(function (query) {
1195                return MutationSummary.createQueryValidator(_this.root, query);
1196            });
1197        }
1198        this.observer = new MutationObserverCtor(function (mutations) {
1199            _this.observerCallback(mutations);
1200        });
1201        this.reconnect();
1202    }
1203    MutationSummary.createObserverOptions = function (queries) {
1204        var observerOptions = {
1205            childList: true,
1206            subtree: true
1207        };
1208        var attributeFilter;
1209        function observeAttributes(attributes) {
1210            if (observerOptions.attributes && !attributeFilter)
1211                return; // already observing all.
1212            observerOptions.attributes = true;
1213            observerOptions.attributeOldValue = true;
1214            if (!attributes) {
1215                // observe all.
1216                attributeFilter = undefined;
1217                return;
1218            }
1219            // add to observed.
1220            attributeFilter = attributeFilter || {};
1221            attributes.forEach(function (attribute) {
1222                attributeFilter[attribute] = true;
1223                attributeFilter[attribute.toLowerCase()] = true;
1224            });
1225        }
1226        queries.forEach(function (query) {
1227            if (query.characterData) {
1228                observerOptions.characterData = true;
1229                observerOptions.characterDataOldValue = true;
1230                return;
1231            }
1232            if (query.all) {
1233                observeAttributes();
1234                observerOptions.characterData = true;
1235                observerOptions.characterDataOldValue = true;
1236                return;
1237            }
1238            if (query.attribute) {
1239                observeAttributes([query.attribute.trim()]);
1240                return;
1241            }
1242            var attributes = elementFilterAttributes(query.elementFilter).concat(query.attributeList || []);
1243            if (attributes.length)
1244                observeAttributes(attributes);
1245        });
1246        if (attributeFilter)
1247            observerOptions.attributeFilter = Object.keys(attributeFilter);
1248        return observerOptions;
1249    };
1250    MutationSummary.validateOptions = function (options) {
1251        for (var prop in options) {
1252            if (!(prop in MutationSummary.optionKeys))
1253                throw Error('Invalid option: ' + prop);
1254        }
1255        if (typeof options.callback !== 'function')
1256            throw Error('Invalid options: callback is required and must be a function');
1257        if (!options.queries || !options.queries.length)
1258            throw Error('Invalid options: queries must contain at least one query request object.');
1259        var opts = {
1260            callback: options.callback,
1261            rootNode: options.rootNode || document,
1262            observeOwnChanges: !!options.observeOwnChanges,
1263            oldPreviousSibling: !!options.oldPreviousSibling,
1264            queries: []
1265        };
1266        for (var i = 0; i < options.queries.length; i++) {
1267            var request = options.queries[i];
1268            // all
1269            if (request.all) {
1270                if (Object.keys(request).length > 1)
1271                    throw Error('Invalid request option. all has no options.');
1272                opts.queries.push({ all: true });
1273                continue;
1274            }
1275            // attribute
1276            if ('attribute' in request) {
1277                var query = {
1278                    attribute: validateAttribute(request.attribute)
1279                };
1280                query.elementFilter = Selector.parseSelectors('*[' + query.attribute + ']');
1281                if (Object.keys(request).length > 1)
1282                    throw Error('Invalid request option. attribute has no options.');
1283                opts.queries.push(query);
1284                continue;
1285            }
1286            // element
1287            if ('element' in request) {
1288                var requestOptionCount = Object.keys(request).length;
1289                var query = {
1290                    element: request.element,
1291                    elementFilter: Selector.parseSelectors(request.element)
1292                };
1293                if (request.hasOwnProperty('elementAttributes')) {
1294                    query.attributeList = validateElementAttributes(request.elementAttributes);
1295                    requestOptionCount--;
1296                }
1297                if (requestOptionCount > 1)
1298                    throw Error('Invalid request option. element only allows elementAttributes option.');
1299                opts.queries.push(query);
1300                continue;
1301            }
1302            // characterData
1303            if (request.characterData) {
1304                if (Object.keys(request).length > 1)
1305                    throw Error('Invalid request option. characterData has no options.');
1306                opts.queries.push({ characterData: true });
1307                continue;
1308            }
1309            throw Error('Invalid request option. Unknown query request.');
1310        }
1311        return opts;
1312    };
1313    MutationSummary.prototype.createSummaries = function (mutations) {
1314        if (!mutations || !mutations.length)
1315            return [];
1316        var projection = new MutationProjection(this.root, mutations, this.elementFilter, this.calcReordered, this.options.oldPreviousSibling);
1317        var summaries = [];
1318        for (var i = 0; i < this.options.queries.length; i++) {
1319            summaries.push(new Summary(projection, this.options.queries[i])
1319);
1320        }
1321        return summaries;
1322    };
1323    MutationSummary.prototype.checkpointQueryValidators = function () {
1324        this.queryValidators.forEach(function (validator) {
1325            if (validator)
1326                validator.recordPreviousState();
1327        });
1328    };
1329    MutationSummary.prototype.runQueryValidators = function (summaries) {
1330        this.queryValidators.forEach(function (validator, index) {
1331            if (validator)
1332                validator.validate(summaries[index]);
1333        });
1334    };
1335    MutationSummary.prototype.changesToReport = function (summaries) {
1336        return summaries.some(function (summary) {
1337            var summaryProps = ['added', 'removed', 'reordered', 'reparented',
1338                'valueChanged', 'characterDataChanged'];
1339            if (summaryProps.some(function (prop) { return summary[prop] && summary[prop].length; }))
1340                return true;
1341            if (summary.attributeChanged) {
1342                var attrNames = Object.keys(summary.attributeChanged);
1343                var attrsChanged = attrNames.some(function (attrName) {
1344                    return !!summary.attributeChanged[attrName].length;
1345                });
1346                if (attrsChanged)
1347                    return true;
1348            }
1349            return false;
1350        });
1351    };
1352    MutationSummary.prototype.observerCallback = function (mutations) {
1353        if (!this.options.observeOwnChanges)
1354            this.observer.disconnect();
1355        var summaries = this.createSummaries(mutations);
1356        this.runQueryValidators(summaries);
1357        if (this.options.observeOwnChanges)
1358            this.checkpointQueryValidators();
1359        if (this.changesToReport(summaries))
1360            this.callback(summaries);
1361        // disconnect() may have been called during the callback.
1362        if (!this.options.observeOwnChanges && this.connected) {
1363            this.checkpointQueryValidators();
1364            this.observer.observe(this.root, this.observerOptions);
1365        }
1366    };
1367    MutationSummary.prototype.reconnect = function () {
1368        if (this.connected)
1369            throw Error('Already connected');
1370        this.observer.observe(this.root, this.observerOptions);
1371        this.connected = true;
1372        this.checkpointQueryValidators();
1373    };
1374    MutationSummary.prototype.takeSummaries = function () {
1375        if (!this.connected)
1376            throw Error('Not connected');
1377        var summaries = this.createSummaries(this.observer.takeRecords());
1378        return this.changesToReport(summaries) ? summaries : undefined;
1379    };
1380    MutationSummary.prototype.disconnect = function () {
1381        var summaries = this.takeSummaries();
1382        this.observer.disconnect();
1383        this.connected = false;
1384        return summaries;
1385    };
1386    MutationSummary.NodeMap = NodeMap; // exposed for use in TreeMirror.
1387    MutationSummary.parseElementFilter = Selector.parseSelectors; // exposed for testing.
1388    MutationSummary.optionKeys = {
1389        'callback': true,
1390        'queries': true,
1391        'rootNode': true,
1392        'oldPreviousSibling': true,
1393        'observeOwnChanges': true
1394    };
1395    return MutationSummary;
1396})();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.