| Index: appengine/config_service/ui/bower_components/polymer/lib/utils/flattened-nodes-observer.html
|
| diff --git a/appengine/config_service/ui/bower_components/polymer/lib/utils/flattened-nodes-observer.html b/appengine/config_service/ui/bower_components/polymer/lib/utils/flattened-nodes-observer.html
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..66ad7795f70e32b58e8d8edd02888dcc6a1d888a
|
| --- /dev/null
|
| +++ b/appengine/config_service/ui/bower_components/polymer/lib/utils/flattened-nodes-observer.html
|
| @@ -0,0 +1,242 @@
|
| +<!--
|
| +@license
|
| +Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
|
| +This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
|
| +The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
|
| +The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
|
| +Code distributed by Google as part of the polymer project is also
|
| +subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
|
| +-->
|
| +<link rel="import" href="../utils/boot.html">
|
| +<link rel="import" href="../utils/array-splice.html">
|
| +<link rel="import" href="../utils/async.html">
|
| +<script>
|
| +(function() {
|
| + 'use strict';
|
| +
|
| + function isSlot(node) {
|
| + return (node.localName === 'slot');
|
| + }
|
| +
|
| + /**
|
| + * Class that listens for changes (additions or removals) to
|
| + * "flattened nodes" on a given `node`. The list of flattened nodes consists
|
| + * of a node's children and, for any children that are `<slot>` elements,
|
| + * the expanded flattened list of `assignedNodes`.
|
| + * For example, if the observed node has children `<a></a><slot></slot><b></b>`
|
| + * and the `<slot>` has one `<div>` assigned to it, then the flattened
|
| + * nodes list is `<a></a><div></div><b></b>`. If the `<slot>` has other
|
| + * `<slot>` elements assigned to it, these are flattened as well.
|
| + *
|
| + * The provided `callback` is called whenever any change to this list
|
| + * of flattened nodes occurs, where an addition or removal of a node is
|
| + * considered a change. The `callback` is called with one argument, an object
|
| + * containing an array of any `addedNodes` and `removedNodes`.
|
| + *
|
| + * Note: the callback is called asynchronous to any changes
|
| + * at a microtask checkpoint. This is because observation is performed using
|
| + * `MutationObserver` and the `<slot>` element's `slotchange` event which
|
| + * are asynchronous.
|
| + *
|
| + * @memberof Polymer
|
| + * @param {Node} target Node on which to listen for changes.
|
| + * @param {Function} callback Function called when there are additions
|
| + * or removals from the target's list of flattened nodes.
|
| + * @summary Class that listens for changes (additions or removals) to
|
| + * "flattened nodes" on a given `node`.
|
| + */
|
| + class FlattenedNodesObserver {
|
| +
|
| + /**
|
| + * Returns the list of flattened nodes for the given `node`.
|
| + * This list consists of a node's children and, for any children
|
| + * that are `<slot>` elements, the expanded flattened list of `assignedNodes`.
|
| + * For example, if the observed node has children `<a></a><slot></slot><b></b>`
|
| + * and the `<slot>` has one `<div>` assigned to it, then the flattened
|
| + * nodes list is `<a></a><div></div><b></b>`. If the `<slot>` has other
|
| + * `<slot>` elements assigned to it, these are flattened as well.
|
| + *
|
| + * @param {Node} node The node for which to return the list of flattened nodes.
|
| + * @return {Array} The list of flattened nodes for the given `node`.
|
| + */
|
| + static getFlattenedNodes(node) {
|
| + if (isSlot(node)) {
|
| + return node.assignedNodes({flatten: true});
|
| + } else {
|
| + return Array.from(node.childNodes).map(node => {
|
| + if (isSlot(node)) {
|
| + return node.assignedNodes({flatten: true});
|
| + } else {
|
| + return [node];
|
| + }
|
| + }).reduce((a, b) => a.concat(b), []);
|
| + }
|
| + }
|
| +
|
| + constructor(target, callback) {
|
| + /** @type {MutationObserver} */
|
| + this._shadyChildrenObserver = null;
|
| + /** @type {MutationObserver} */
|
| + this._nativeChildrenObserver = null;
|
| + this._connected = false;
|
| + this._target = target;
|
| + this.callback = callback;
|
| + this._effectiveNodes = [];
|
| + this._observer = null;
|
| + this._scheduled = false;
|
| + this._boundSchedule = () => {
|
| + this._schedule();
|
| + }
|
| + this.connect();
|
| + this._schedule();
|
| + }
|
| +
|
| + /**
|
| + * Activates an observer. This method is automatically called when
|
| + * a `FlattenedNodesObserver` is created. It should only be called to
|
| + * re-activate an observer that has been deactivated via the `disconnect` method.
|
| + */
|
| + connect() {
|
| + if (isSlot(this._target)) {
|
| + this._listenSlots([this._target]);
|
| + } else {
|
| + this._listenSlots(this._target.children);
|
| + if (window.ShadyDOM) {
|
| + this._shadyChildrenObserver =
|
| + ShadyDOM.observeChildren(this._target, (mutations) => {
|
| + this._processMutations(mutations);
|
| + });
|
| + } else {
|
| + this._nativeChildrenObserver =
|
| + new MutationObserver((mutations) => {
|
| + this._processMutations(mutations);
|
| + });
|
| + this._nativeChildrenObserver.observe(this._target, {childList: true});
|
| + }
|
| + }
|
| + this._connected = true;
|
| + }
|
| +
|
| + /**
|
| + * Deactivates the flattened nodes observer. After calling this method
|
| + * the observer callback will not be called when changes to flattened nodes
|
| + * occur. The `connect` method may be subsequently called to reactivate
|
| + * the observer.
|
| + */
|
| + disconnect() {
|
| + if (isSlot(this._target)) {
|
| + this._unlistenSlots([this._target]);
|
| + } else {
|
| + this._unlistenSlots(this._target.children);
|
| + if (window.ShadyDOM && this._shadyChildrenObserver) {
|
| + ShadyDOM.unobserveChildren(this._shadyChildrenObserver);
|
| + this._shadyChildrenObserver = null;
|
| + } else if (this._nativeChildrenObserver) {
|
| + this._nativeChildrenObserver.disconnect();
|
| + this._nativeChildrenObserver = null;
|
| + }
|
| + }
|
| + this._connected = false;
|
| + }
|
| +
|
| + _schedule() {
|
| + if (!this._scheduled) {
|
| + this._scheduled = true;
|
| + Polymer.Async.microTask.run(() => this.flush());
|
| + }
|
| + }
|
| +
|
| + _processMutations(mutations) {
|
| + this._processSlotMutations(mutations);
|
| + this.flush();
|
| + }
|
| +
|
| + _processSlotMutations(mutations) {
|
| + if (mutations) {
|
| + for (let i=0; i < mutations.length; i++) {
|
| + let mutation = mutations[i];
|
| + if (mutation.addedNodes) {
|
| + this._listenSlots(mutation.addedNodes);
|
| + }
|
| + if (mutation.removedNodes) {
|
| + this._unlistenSlots(mutation.removedNodes);
|
| + }
|
| + }
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * Flushes the observer causing any pending changes to be immediately
|
| + * delivered the observer callback. By default these changes are delivered
|
| + * asynchronously at the next microtask checkpoint.
|
| + *
|
| + * @return {boolean} Returns true if any pending changes caused the observer
|
| + * callback to run.
|
| + */
|
| + flush() {
|
| + if (!this._connected) {
|
| + return;
|
| + }
|
| + if (window.ShadyDOM) {
|
| + ShadyDOM.flush();
|
| + }
|
| + if (this._nativeChildrenObserver) {
|
| + this._processSlotMutations(this._nativeChildrenObserver.takeRecords());
|
| + } else if (this.shadyChildrenObserver) {
|
| + this._processSlotMutations(this._shadyChildrenObserver.takeRecords());
|
| + }
|
| + this._scheduled = false;
|
| + let info = {
|
| + target: this._target,
|
| + addedNodes: [],
|
| + removedNodes: []
|
| + };
|
| + let newNodes = this.constructor.getFlattenedNodes(this._target);
|
| + let splices = Polymer.ArraySplice.calculateSplices(newNodes,
|
| + this._effectiveNodes);
|
| + // process removals
|
| + for (let i=0, s; (i<splices.length) && (s=splices[i]); i++) {
|
| + for (let j=0, n; (j < s.removed.length) && (n=s.removed[j]); j++) {
|
| + info.removedNodes.push(n);
|
| + }
|
| + }
|
| + // process adds
|
| + for (let i=0, s; (i<splices.length) && (s=splices[i]); i++) {
|
| + for (let j=s.index; j < s.index + s.addedCount; j++) {
|
| + info.addedNodes.push(newNodes[j]);
|
| + }
|
| + }
|
| + // update cache
|
| + this._effectiveNodes = newNodes;
|
| + let didFlush = false;
|
| + if (info.addedNodes.length || info.removedNodes.length) {
|
| + didFlush = true;
|
| + this.callback.call(this._target, info);
|
| + }
|
| + return didFlush;
|
| + }
|
| +
|
| + _listenSlots(nodeList) {
|
| + for (let i=0; i < nodeList.length; i++) {
|
| + let n = nodeList[i];
|
| + if (isSlot(n)) {
|
| + n.addEventListener('slotchange', this._boundSchedule);
|
| + }
|
| + }
|
| + }
|
| +
|
| + _unlistenSlots(nodeList) {
|
| + for (let i=0; i < nodeList.length; i++) {
|
| + let n = nodeList[i];
|
| + if (isSlot(n)) {
|
| + n.removeEventListener('slotchange', this._boundSchedule);
|
| + }
|
| + }
|
| + }
|
| +
|
| + }
|
| +
|
| + Polymer.FlattenedNodesObserver = FlattenedNodesObserver;
|
| +
|
| +})();
|
| +</script>
|
|
|