| Index: third_party/polymer/v0_8/components-chromium/polymer/src/lib/dom-api-extracted.js
|
| diff --git a/third_party/polymer/v0_8/components-chromium/polymer/src/lib/dom-api-extracted.js b/third_party/polymer/v0_8/components-chromium/polymer/src/lib/dom-api-extracted.js
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..e7dfa41c82d44e8a36ac46d1b524001dec4f2f87
|
| --- /dev/null
|
| +++ b/third_party/polymer/v0_8/components-chromium/polymer/src/lib/dom-api-extracted.js
|
| @@ -0,0 +1,459 @@
|
| +
|
| +
|
| + Polymer.DomApi = (function() {
|
| +
|
| + var Debounce = Polymer.Debounce;
|
| + var Settings = Polymer.Settings;
|
| +
|
| + var nativeInsertBefore = Element.prototype.insertBefore;
|
| + var nativeRemoveChild = Element.prototype.removeChild;
|
| + var nativeAppendChild = Element.prototype.appendChild;
|
| +
|
| + var dirtyRoots = [];
|
| +
|
| + var DomApi = function(node, patch) {
|
| + this.node = node;
|
| + if (patch) {
|
| + this.patch();
|
| + }
|
| + };
|
| +
|
| + DomApi.prototype = {
|
| +
|
| + // experimental: support patching selected native api.
|
| + patch: function() {
|
| + var self = this;
|
| + this.node.appendChild = function(node) {
|
| + return self.appendChild(node);
|
| + };
|
| + this.node.insertBefore = function(node, ref_node) {
|
| + return self.insertBefore(node, ref_node);
|
| + };
|
| + this.node.removeChild = function(node) {
|
| + return self.removeChild(node);
|
| + };
|
| + },
|
| +
|
| + get childNodes() {
|
| + var c$ = getLightChildren(this.node);
|
| + return Array.isArray(c$) ? c$ : Array.prototype.slice.call(c$);
|
| + },
|
| +
|
| + get children() {
|
| + return Array.prototype.filter.call(this.childNodes, function(n) {
|
| + return (n.nodeType === Node.ELEMENT_NODE);
|
| + });
|
| + },
|
| +
|
| + get parentNode() {
|
| + return this.node.lightParent || this.node.parentNode;
|
| + },
|
| +
|
| + flush: function() {
|
| + for (var i=0, host; i<dirtyRoots.length; i++) {
|
| + host = dirtyRoots[i];
|
| + host.flushDebouncer('_distribute');
|
| + }
|
| + dirtyRoots = [];
|
| + },
|
| +
|
| + _lazyDistribute: function(host) {
|
| + if (host.shadyRoot) {
|
| + host.shadyRoot._distributionClean = false;
|
| + }
|
| + // TODO(sorvell): optimize debounce so it does less work by default
|
| + // and then remove these checks...
|
| + // need to dirty distribution once.
|
| + if (!host.isDebouncerActive('_distribute')) {
|
| + host.debounce('_distribute', host._distributeContent);
|
| + dirtyRoots.push(host);
|
| + }
|
| + },
|
| +
|
| + // cases in which we may not be able to just do standard appendChild
|
| + // 1. container has a shadyRoot (needsDistribution IFF the shadyRoot
|
| + // has an insertion point)
|
| + // 2. container is a shadyRoot (don't distribute, instead set
|
| + // container to container.host.
|
| + // 3. node is <content> (host of container needs distribution)
|
| + appendChild: function(node) {
|
| + var distributed;
|
| + this._removeNodeFromHost(node);
|
| + if (this._nodeIsInLogicalTree(this.node)) {
|
| + var host = this._hostForNode(this.node);
|
| + this._addLogicalInfo(node, this.node, host && host.shadyRoot);
|
| + this._addNodeToHost(node);
|
| + if (host) {
|
| + distributed = this._maybeDistribute(node, this.node, host);
|
| + }
|
| + }
|
| + if (!distributed) {
|
| + // if adding to a shadyRoot, add to host instead
|
| + var container = this.node._isShadyRoot ? this.node.host : this.node;
|
| + nativeAppendChild.call(container, node);
|
| + }
|
| + return node;
|
| + },
|
| +
|
| + insertBefore: function(node, ref_node) {
|
| + if (!ref_node) {
|
| + return this.appendChild(node);
|
| + }
|
| + var distributed;
|
| + this._removeNodeFromHost(node);
|
| + if (this._nodeIsInLogicalTree(this.node)) {
|
| + saveLightChildrenIfNeeded(this.node);
|
| + var children = this.childNodes;
|
| + var index = children.indexOf(ref_node);
|
| + if (index < 0) {
|
| + throw Error('The ref_node to be inserted before is not a child ' +
|
| + 'of this node');
|
| + }
|
| + var host = this._hostForNode(this.node);
|
| + this._addLogicalInfo(node, this.node, host && host.shadyRoot, index);
|
| + this._addNodeToHost(node);
|
| + if (host) {
|
| + distributed = this._maybeDistribute(node, this.node, host);
|
| + }
|
| + }
|
| + if (!distributed) {
|
| + // if ref_node is <content> replace with first distributed node
|
| + ref_node = ref_node.localName === CONTENT ?
|
| + this._firstComposedNode(ref_node) : ref_node;
|
| + // if adding to a shadyRoot, add to host instead
|
| + var container = this.node._isShadyRoot ? this.node.host : this.node;
|
| + nativeInsertBefore.call(container, node, ref_node);
|
| + }
|
| + return node;
|
| + },
|
| +
|
| + /**
|
| + Removes the given `node` from the element's `lightChildren`.
|
| + This method also performs dom composition.
|
| + */
|
| + removeChild: function(node) {
|
| + var distributed;
|
| + if (this._nodeIsInLogicalTree(this.node)) {
|
| + var host = this._hostForNode(this.node);
|
| + distributed = this._maybeDistribute(node, this.node, host);
|
| + this._removeNodeFromHost(node);
|
| + }
|
| + if (!distributed) {
|
| + // if removing from a shadyRoot, remove form host instead
|
| + var container = this.node._isShadyRoot ? this.node.host : this.node;
|
| + nativeRemoveChild.call(container, node);
|
| + }
|
| + return node;
|
| + },
|
| +
|
| + replaceChild: function(node, ref_node) {
|
| + this.insertBefore(node, ref_node);
|
| + this.removeChild(ref_node);
|
| + return node;
|
| + },
|
| +
|
| + getOwnerRoot: function() {
|
| + return this._ownerShadyRootForNode(this.node);
|
| + },
|
| +
|
| + _ownerShadyRootForNode: function(node) {
|
| + if (node._ownerShadyRoot === undefined) {
|
| + var root;
|
| + if (node._isShadyRoot) {
|
| + root = node;
|
| + } else {
|
| + var parent = Polymer.dom(node).parentNode;
|
| + if (parent) {
|
| + root = parent._isShadyRoot ? parent :
|
| + this._ownerShadyRootForNode(parent);
|
| + } else {
|
| + root = null;
|
| + }
|
| + }
|
| + node._ownerShadyRoot = root;
|
| + }
|
| + return node._ownerShadyRoot;
|
| +
|
| + },
|
| +
|
| + _maybeDistribute: function(node, parent, host) {
|
| + var nodeNeedsDistribute = this._nodeNeedsDistribution(node);
|
| + var distribute = this._parentNeedsDistribution(parent) ||
|
| + nodeNeedsDistribute;
|
| + if (nodeNeedsDistribute) {
|
| + this._updateInsertionPoints(host);
|
| + }
|
| + if (distribute) {
|
| + this._lazyDistribute(host);
|
| + }
|
| + return distribute;
|
| + },
|
| +
|
| + _updateInsertionPoints: function(host) {
|
| + host.shadyRoot._insertionPoints =
|
| + factory(host.shadyRoot).querySelectorAll(CONTENT);
|
| + },
|
| +
|
| + _nodeIsInLogicalTree: function(node) {
|
| + return Boolean(node._isShadyRoot ||
|
| + this._ownerShadyRootForNode(node) ||
|
| + node.shadyRoot);
|
| + },
|
| +
|
| + // note: a node is its own host
|
| + _hostForNode: function(node) {
|
| + var root = node.shadyRoot || (node._isShadyRoot ?
|
| + node : this._ownerShadyRootForNode(node));
|
| + return root && root.host;
|
| + },
|
| +
|
| + _parentNeedsDistribution: function(parent) {
|
| + return parent.shadyRoot && hasInsertionPoint(parent.shadyRoot);
|
| + },
|
| +
|
| + // TODO(sorvell): technically we should check non-fragment nodes for
|
| + // <content> children but since this case is assumed to be exceedingly
|
| + // rare, we avoid the cost and will address with some specific api
|
| + // when the need arises.
|
| + _nodeNeedsDistribution: function(node) {
|
| + return (node.localName === CONTENT) ||
|
| + ((node.nodeType === Node.DOCUMENT_FRAGMENT_NODE) &&
|
| + node.querySelector(CONTENT));
|
| + },
|
| +
|
| + _removeNodeFromHost: function(node) {
|
| + if (node.lightParent) {
|
| + var root = this._ownerShadyRootForNode(node);
|
| + if (root) {
|
| + root.host._elementRemove(node);
|
| + }
|
| + this._removeLogicalInfo(node, node.lightParent);
|
| + }
|
| + this._removeOwnerShadyRoot(node);
|
| + },
|
| +
|
| + _addNodeToHost: function(node) {
|
| + var checkNode = node.nodeType === Node.DOCUMENT_FRAGMENT_NODE ?
|
| + node.firstChild : node;
|
| + var root = this._ownerShadyRootForNode(checkNode);
|
| + if (root) {
|
| + root.host._elementAdd(node);
|
| + }
|
| + },
|
| +
|
| + _addLogicalInfo: function(node, container, root, index) {
|
| + saveLightChildrenIfNeeded(container);
|
| + var children = factory(container).childNodes;
|
| + index = index === undefined ? children.length : index;
|
| + // handle document fragments
|
| + if (node.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
|
| + var n = node.firstChild;
|
| + while (n) {
|
| + children.splice(index++, 0, n);
|
| + n.lightParent = container;
|
| + n = n.nextSibling;
|
| + }
|
| + } else {
|
| + children.splice(index, 0, node);
|
| + node.lightParent = container;
|
| + }
|
| + },
|
| +
|
| + // NOTE: in general, we expect contents of the lists here to be small-ish
|
| + // and therefore indexOf to be nbd. Other optimizations can be made
|
| + // for larger lists (linked list)
|
| + _removeLogicalInfo: function(node, container) {
|
| + var children = factory(container).childNodes;
|
| + var index = children.indexOf(node);
|
| + if ((index < 0) || (container !== node.lightParent)) {
|
| + throw Error('The node to be removed is not a child of this node');
|
| + }
|
| + children.splice(index, 1);
|
| + node.lightParent = null;
|
| + },
|
| +
|
| + _removeOwnerShadyRoot: function(node) {
|
| + // TODO(sorvell): need to clear any children of element?
|
| + node._ownerShadyRoot = undefined;
|
| + },
|
| +
|
| + // TODO(sorvell): This will fail if distribution that affects this
|
| + // question is pending; this is expected to be exceedingly rare, but if
|
| + // the issue comes up, we can force a flush in this case.
|
| + _firstComposedNode: function(content) {
|
| + var n$ = factory(content).getDistributedNodes();
|
| + for (var i=0, l=n$.length, n, p$; (i<l) && (n=n$[i]); i++) {
|
| + p$ = factory(n).getDestinationInsertionPoints();
|
| + // means that we're composed to this spot.
|
| + if (p$[p$.length-1] === content) {
|
| + return n;
|
| + }
|
| + }
|
| + },
|
| +
|
| + // TODO(sorvell): consider doing native QSA and filtering results.
|
| + querySelector: function(selector) {
|
| + return this.querySelectorAll(selector)[0];
|
| + },
|
| +
|
| + querySelectorAll: function(selector) {
|
| + return this._query(function(n) {
|
| + return matchesSelector.call(n, selector);
|
| + }, this.node);
|
| + },
|
| +
|
| + _query: function(matcher, node) {
|
| + var list = [];
|
| + this._queryElements(factory(node).childNodes, matcher, list);
|
| + return list;
|
| + },
|
| +
|
| + _queryElements: function(elements, matcher, list) {
|
| + for (var i=0, l=elements.length, c; (i<l) && (c=elements[i]); i++) {
|
| + if (c.nodeType === Node.ELEMENT_NODE) {
|
| + this._queryElement(c, matcher, list);
|
| + }
|
| + }
|
| + },
|
| +
|
| + _queryElement: function(node, matcher, list) {
|
| + if (matcher(node)) {
|
| + list.push(node);
|
| + }
|
| + this._queryElements(factory(node).childNodes, matcher, list);
|
| + },
|
| +
|
| + getDestinationInsertionPoints: function() {
|
| + return this.node._destinationInsertionPoints || [];
|
| + },
|
| +
|
| + getDistributedNodes: function() {
|
| + return this.node._distributedNodes || [];
|
| + },
|
| +
|
| + /*
|
| + Returns a list of nodes distributed within this element. These can be
|
| + dom children or elements distributed to children that are insertion
|
| + points.
|
| + */
|
| + queryDistributedElements: function(selector) {
|
| + var c$ = this.childNodes;
|
| + var list = [];
|
| + this._distributedFilter(selector, c$, list);
|
| + for (var i=0, l=c$.length, c; (i<l) && (c=c$[i]); i++) {
|
| + if (c.localName === CONTENT) {
|
| + this._distributedFilter(selector, factory(c).getDistributedNodes(),
|
| + list);
|
| + }
|
| + }
|
| + return list;
|
| + },
|
| +
|
| + _distributedFilter: function(selector, list, results) {
|
| + results = results || [];
|
| + for (var i=0, l=list.length, d; (i<l) && (d=list[i]); i++) {
|
| + if ((d.nodeType === Node.ELEMENT_NODE) &&
|
| + (d.localName !== CONTENT) &&
|
| + matchesSelector.call(d, selector)) {
|
| + results.push(d);
|
| + }
|
| + }
|
| + return results;
|
| + }
|
| +
|
| + };
|
| +
|
| + if (Settings.useShadow) {
|
| +
|
| + DomApi.prototype.querySelectorAll = function(selector) {
|
| + return Array.prototype.slice.call(this.node.querySelectorAll(selector));
|
| + };
|
| +
|
| + DomApi.prototype.patch = function() {};
|
| +
|
| + DomApi.prototype.getOwnerRoot = function() {
|
| + var n = this.node;
|
| + while (n) {
|
| + if (n.nodeType === Node.DOCUMENT_FRAGMENT_NODE && n.host) {
|
| + return n;
|
| + }
|
| + n = n.parentNode;
|
| + }
|
| + };
|
| +
|
| + DomApi.prototype.getDestinationInsertionPoints = function() {
|
| + var n$ = this.node.getDestinationInsertionPoints();
|
| + return n$ ? Array.prototype.slice.call(n$) : [];
|
| + };
|
| +
|
| + DomApi.prototype.getDistributedNodes = function() {
|
| + var n$ = this.node.getDistributedNodes();
|
| + return n$ ? Array.prototype.slice.call(n$) : [];
|
| + };
|
| +
|
| +
|
| + }
|
| +
|
| + var CONTENT = 'content';
|
| +
|
| + var factory = function(node, patch) {
|
| + node = node || document;
|
| + if (!node.__domApi) {
|
| + node.__domApi = new DomApi(node, patch);
|
| + }
|
| + return node.__domApi;
|
| + };
|
| +
|
| + Polymer.dom = function(obj, patch) {
|
| + if (obj instanceof Event) {
|
| + return Polymer.EventApi.factory(obj);
|
| + } else {
|
| + return factory(obj, patch);
|
| + }
|
| + };
|
| +
|
| + // make flush available directly.
|
| + Polymer.dom.flush = DomApi.prototype.flush;
|
| +
|
| + function getLightChildren(node) {
|
| + var children = node.lightChildren;
|
| + return children ? children : node.childNodes;
|
| + }
|
| +
|
| + function saveLightChildrenIfNeeded(node) {
|
| + // Capture the list of light children. It's important to do this before we
|
| + // start transforming the DOM into "rendered" state.
|
| + //
|
| + // Children may be added to this list dynamically. It will be treated as the
|
| + // source of truth for the light children of the element. This element's
|
| + // actual children will be treated as the rendered state once lightChildren
|
| + // is populated.
|
| + if (!node.lightChildren) {
|
| + var children = [];
|
| + for (var child = node.firstChild; child; child = child.nextSibling) {
|
| + children.push(child);
|
| + child.lightParent = child.lightParent || node;
|
| + }
|
| + node.lightChildren = children;
|
| + }
|
| + }
|
| +
|
| + function hasInsertionPoint(root) {
|
| + return Boolean(root._insertionPoints.length);
|
| + }
|
| +
|
| + var p = Element.prototype;
|
| + var matchesSelector = p.matches || p.matchesSelector ||
|
| + p.mozMatchesSelector || p.msMatchesSelector ||
|
| + p.oMatchesSelector || p.webkitMatchesSelector;
|
| +
|
| + return {
|
| + getLightChildren: getLightChildren,
|
| + saveLightChildrenIfNeeded: saveLightChildrenIfNeeded,
|
| + matchesSelector: matchesSelector,
|
| + hasInsertionPoint: hasInsertionPoint,
|
| + factory: factory
|
| + };
|
| +
|
| + })();
|
| +
|
|
|