mirror of
https://github.com/Polymer/polymer.git
synced 2025-02-25 18:55:30 -06:00
Merge pull request #3112 from Polymer/shady-linked
ShadyDOM: use a linked-list for tree accessors
This commit is contained in:
commit
273ab0fbe6
@ -57,12 +57,16 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
},
|
||||
|
||||
_initFeatures: function() {
|
||||
// setup gestures
|
||||
this._setupGestures();
|
||||
// manage configuration
|
||||
this._setupConfigure();
|
||||
// setup style properties
|
||||
this._setupStyleProperties();
|
||||
// setup debouncers
|
||||
this._setupDebouncers();
|
||||
// setup shady
|
||||
this._setupShady();
|
||||
this._registerHost();
|
||||
if (this._template) {
|
||||
// manage local dom
|
||||
|
@ -366,18 +366,22 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
|
||||
// instance-time
|
||||
|
||||
_localSubTree: function(node, host) {
|
||||
return (node === host) ? node.childNodes :
|
||||
(node._lightChildren || node.childNodes);
|
||||
},
|
||||
|
||||
findAnnotatedNode: function(root, annote) {
|
||||
// recursively ascend tree until we hit root
|
||||
var parent = annote.parent &&
|
||||
Polymer.Annotations.findAnnotatedNode(root, annote.parent);
|
||||
// unwind the stack, returning the indexed node at each level
|
||||
return !parent ? root :
|
||||
Polymer.Annotations._localSubTree(parent, root)[annote.index];
|
||||
if (parent) {
|
||||
// note: marginally faster than indexing via childNodes
|
||||
// (http://jsperf.com/childnodes-lookup)
|
||||
for (var n=parent.firstChild, i=0; n; n=n.nextSibling) {
|
||||
if (annote.index === i++) {
|
||||
return n;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return root;
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
@ -33,6 +33,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
|
||||
createdCallback: function() {
|
||||
Polymer.telemetry.instanceCount++;
|
||||
this.isAttached = false;
|
||||
this.root = this;
|
||||
this._doBehavior('created'); // abstract
|
||||
this._initFeatures(); // abstract
|
||||
|
@ -14,6 +14,8 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
|
||||
var DomApi = Polymer.DomApi.ctor;
|
||||
|
||||
var useShadow = Polymer.Settings.useShadow;
|
||||
|
||||
/**
|
||||
* DomApi.classList allows maniuplation of `classList` compatible with
|
||||
* Polymer.dom. The general usage is
|
||||
@ -36,20 +38,28 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
}
|
||||
|
||||
DomApi.ClassList.prototype = {
|
||||
|
||||
add: function() {
|
||||
this.node.classList.add.apply(this.node.classList, arguments);
|
||||
this.domApi._distributeParent();
|
||||
this._distributeParent();
|
||||
},
|
||||
|
||||
remove: function() {
|
||||
this.node.classList.remove.apply(this.node.classList, arguments);
|
||||
this.domApi._distributeParent();
|
||||
this._distributeParent();
|
||||
},
|
||||
|
||||
toggle: function() {
|
||||
this.node.classList.toggle.apply(this.node.classList, arguments);
|
||||
this.domApi._distributeParent();
|
||||
this._distributeParent();
|
||||
},
|
||||
|
||||
_distributeParent: function() {
|
||||
if (!useShadow) {
|
||||
this.domApi._maybeDistributeParent();
|
||||
}
|
||||
},
|
||||
|
||||
contains: function() {
|
||||
return this.node.classList.contains.apply(this.node.classList,
|
||||
arguments);
|
||||
|
134
src/lib/dom-api-shadow.html
Normal file
134
src/lib/dom-api-shadow.html
Normal file
@ -0,0 +1,134 @@
|
||||
<!--
|
||||
@license
|
||||
Copyright (c) 2014 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
|
||||
-->
|
||||
<script>
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
var Settings = Polymer.Settings;
|
||||
var TreeApi = Polymer.TreeApi;
|
||||
var DomApi = Polymer.DomApi;
|
||||
|
||||
// *************** Configure DomApi for Shadow DOM!! ***************
|
||||
if (!Settings.useShadow) {
|
||||
return;
|
||||
}
|
||||
|
||||
Polymer.Base.extend(DomApi.prototype, {
|
||||
|
||||
querySelectorAll: function(selector) {
|
||||
return TreeApi.arrayCopy(this.node.querySelectorAll(selector));
|
||||
},
|
||||
|
||||
getOwnerRoot: function() {
|
||||
var n = this.node;
|
||||
while (n) {
|
||||
if (n.nodeType === Node.DOCUMENT_FRAGMENT_NODE && n.host) {
|
||||
return n;
|
||||
}
|
||||
n = n.parentNode;
|
||||
}
|
||||
},
|
||||
|
||||
importNode: function(externalNode, deep) {
|
||||
var doc = this.node instanceof Document ? this.node :
|
||||
this.node.ownerDocument;
|
||||
return doc.importNode(externalNode, deep);
|
||||
},
|
||||
|
||||
getDestinationInsertionPoints: function() {
|
||||
var n$ = this.node.getDestinationInsertionPoints &&
|
||||
this.node.getDestinationInsertionPoints();
|
||||
return n$ ? TreeApi.arrayCopy(n$) : [];
|
||||
},
|
||||
|
||||
getDistributedNodes: function() {
|
||||
var n$ = this.node.getDistributedNodes &&
|
||||
this.node.getDistributedNodes();
|
||||
return n$ ? TreeApi.arrayCopy(n$) : [];
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
Object.defineProperties(DomApi.prototype, {
|
||||
|
||||
childNodes: {
|
||||
get: function() {
|
||||
return TreeApi.arrayCopyChildNodes(this.node);
|
||||
},
|
||||
configurable: true
|
||||
},
|
||||
|
||||
children: {
|
||||
get: function() {
|
||||
return TreeApi.arrayCopyChildren(this.node);
|
||||
},
|
||||
configurable: true
|
||||
},
|
||||
|
||||
// textContent / innerHTML
|
||||
textContent: {
|
||||
get: function() {
|
||||
return this.node.textContent;
|
||||
},
|
||||
set: function(value) {
|
||||
return this.node.textContent = value;
|
||||
},
|
||||
configurable: true
|
||||
},
|
||||
|
||||
innerHTML: {
|
||||
get: function() {
|
||||
return this.node.innerHTML;
|
||||
},
|
||||
set: function(value) {
|
||||
return this.node.innerHTML = value;
|
||||
},
|
||||
configurable: true
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
var forwardMethods = function(m$) {
|
||||
for (var i=0; i < m$.length; i++) {
|
||||
forwardMethod(m$[i]);
|
||||
}
|
||||
};
|
||||
|
||||
var forwardMethod = function(method) {
|
||||
DomApi.prototype[method] = function() {
|
||||
return this.node[method].apply(this.node, arguments);
|
||||
}
|
||||
};
|
||||
|
||||
forwardMethods(['cloneNode', 'appendChild', 'insertBefore',
|
||||
'removeChild', 'replaceChild', 'setAttribute', 'removeAttribute',
|
||||
'querySelector']);
|
||||
|
||||
var forwardProperties = function(f$) {
|
||||
for (var i=0; i < f$.length; i++) {
|
||||
forwardProperty(f$[i]);
|
||||
}
|
||||
};
|
||||
|
||||
var forwardProperty = function(name) {
|
||||
Object.defineProperty(DomApi.prototype, name, {
|
||||
get: function() {
|
||||
return this.node[name];
|
||||
},
|
||||
configurable: true
|
||||
});
|
||||
};
|
||||
|
||||
forwardProperties(['parentNode', 'firstChild', 'lastChild',
|
||||
'nextSibling', 'previousSibling', 'firstElementChild',
|
||||
'lastElementChild', 'nextElementSibling', 'previousElementSibling']);
|
||||
|
||||
})();
|
||||
</script>
|
594
src/lib/dom-api-shady.html
Normal file
594
src/lib/dom-api-shady.html
Normal file
@ -0,0 +1,594 @@
|
||||
<!--
|
||||
@license
|
||||
Copyright (c) 2014 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="settings.html">
|
||||
<link rel="import" href="dom-innerHTML.html">
|
||||
<script>
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
var Settings = Polymer.Settings;
|
||||
var DomApi = Polymer.DomApi;
|
||||
var dom = DomApi.factory;
|
||||
var TreeApi = Polymer.TreeApi;
|
||||
var getInnerHTML = Polymer.domInnerHTML.getInnerHTML;
|
||||
var CONTENT = DomApi.CONTENT;
|
||||
|
||||
// *************** Configure DomApi for Shady DOM!! ***************
|
||||
if (Settings.useShadow) {
|
||||
return;
|
||||
}
|
||||
|
||||
var nativeCloneNode = Element.prototype.cloneNode;
|
||||
var nativeImportNode = Document.prototype.importNode;
|
||||
|
||||
Polymer.Base.extend(DomApi.prototype, {
|
||||
|
||||
_lazyDistribute: function(host) {
|
||||
// note: only try to distribute if the root is not clean; this ensures
|
||||
// we don't distribute before initial distribution
|
||||
if (host.shadyRoot && host.shadyRoot._distributionClean) {
|
||||
host.shadyRoot._distributionClean = false;
|
||||
Polymer.dom.addDebouncer(host.debounce('_distribute',
|
||||
host._distributeContent));
|
||||
}
|
||||
},
|
||||
|
||||
appendChild: function(node) {
|
||||
return this.insertBefore(node);
|
||||
},
|
||||
|
||||
// cases in which we may not be able to just do standard native call
|
||||
// 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)
|
||||
insertBefore: function(node, ref_node) {
|
||||
if (ref_node && TreeApi.Logical.getParentNode(ref_node) !== this.node) {
|
||||
throw Error('The ref_node to be inserted before is not a child ' +
|
||||
'of this node');
|
||||
}
|
||||
// remove node from its current position iff it's in a tree.
|
||||
if (node.nodeType !== Node.DOCUMENT_FRAGMENT_NODE) {
|
||||
var parent = TreeApi.Logical.getParentNode(node);
|
||||
// notify existing parent that this node is being removed.
|
||||
if (parent) {
|
||||
if (DomApi.hasApi(parent)) {
|
||||
dom(parent).notifyObserver();
|
||||
}
|
||||
this._removeNode(node);
|
||||
} else {
|
||||
this._removeOwnerShadyRoot(node);
|
||||
}
|
||||
}
|
||||
if (!this._addNode(node, ref_node)) {
|
||||
if (ref_node) {
|
||||
// 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;
|
||||
if (ref_node) {
|
||||
TreeApi.Composed.insertBefore(container, node, ref_node);
|
||||
} else {
|
||||
TreeApi.Composed.appendChild(container, node);
|
||||
}
|
||||
}
|
||||
this.notifyObserver();
|
||||
return node;
|
||||
},
|
||||
|
||||
// Try to add node. Record logical info, track insertion points, perform
|
||||
// distribution iff needed. Return true if the add is handled.
|
||||
_addNode: function(node, ref_node) {
|
||||
var root = this.getOwnerRoot();
|
||||
if (root) {
|
||||
root._invalidInsertionPoints =
|
||||
this._maybeAddInsertionPoint(node, this.node);
|
||||
this._addNodeToHost(root.host, node);
|
||||
}
|
||||
if (TreeApi.Logical.hasChildNodes(this.node)) {
|
||||
TreeApi.Logical.recordInsertBefore(node, this.node, ref_node);
|
||||
}
|
||||
// if not distributing and not adding to host, do a fast path addition
|
||||
return (this._maybeDistribute(node) ||
|
||||
this._tryRemoveUndistributedNode(node));
|
||||
},
|
||||
|
||||
/**
|
||||
Removes the given `node` from the element's `lightChildren`.
|
||||
This method also performs dom composition.
|
||||
*/
|
||||
removeChild: function(node) {
|
||||
if (TreeApi.Logical.getParentNode(node) !== this.node) {
|
||||
throw Error('The node to be removed is not a child of this node: ' +
|
||||
node);
|
||||
}
|
||||
if (!this._removeNode(node)) {
|
||||
// if removing from a shadyRoot, remove form host instead
|
||||
var container = this.node._isShadyRoot ? this.node.host : this.node;
|
||||
// not guaranteed to physically be in container; e.g.
|
||||
// undistributed nodes.
|
||||
if (container === node.parentNode) {
|
||||
TreeApi.Composed.removeChild(container, node);
|
||||
}
|
||||
}
|
||||
this.notifyObserver();
|
||||
return node;
|
||||
},
|
||||
|
||||
// Try to remove node: update logical info and perform distribution iff
|
||||
// needed. Return true if the removal has been handled.
|
||||
// note that it's possible for both the node's host and its parent
|
||||
// to require distribution... both cases are handled here.
|
||||
_removeNode: function(node) {
|
||||
// important that we want to do this only if the node has a logical parent
|
||||
var logicalParent = TreeApi.Logical.hasParentNode(node) &&
|
||||
TreeApi.Logical.getParentNode(node);
|
||||
var distributed;
|
||||
var root = this._ownerShadyRootForNode(node);
|
||||
if (logicalParent) {
|
||||
// distribute node's parent iff needed
|
||||
distributed = dom(node)._maybeDistributeParent();
|
||||
TreeApi.Logical.recordRemoveChild(node, logicalParent);
|
||||
// remove node from root and distribute it iff needed
|
||||
if (root && this._removeDistributedChildren(root, node)) {
|
||||
root._invalidInsertionPoints = true;
|
||||
this._lazyDistribute(root.host);
|
||||
}
|
||||
}
|
||||
this._removeOwnerShadyRoot(node);
|
||||
if (root) {
|
||||
this._removeNodeFromHost(root.host, node);
|
||||
}
|
||||
return distributed;
|
||||
},
|
||||
|
||||
replaceChild: function(node, ref_node) {
|
||||
this.insertBefore(node, ref_node);
|
||||
this.removeChild(ref_node);
|
||||
return node;
|
||||
},
|
||||
|
||||
_hasCachedOwnerRoot: function(node) {
|
||||
return Boolean(node._ownerShadyRoot !== undefined);
|
||||
},
|
||||
|
||||
getOwnerRoot: function() {
|
||||
return this._ownerShadyRootForNode(this.node);
|
||||
},
|
||||
|
||||
_ownerShadyRootForNode: function(node) {
|
||||
if (!node) {
|
||||
return;
|
||||
}
|
||||
if (node._ownerShadyRoot === undefined) {
|
||||
var root;
|
||||
if (node._isShadyRoot) {
|
||||
root = node;
|
||||
} else {
|
||||
var parent = TreeApi.Logical.getParentNode(node);
|
||||
if (parent) {
|
||||
root = parent._isShadyRoot ? parent :
|
||||
this._ownerShadyRootForNode(parent);
|
||||
} else {
|
||||
root = null;
|
||||
}
|
||||
}
|
||||
node._ownerShadyRoot = root;
|
||||
}
|
||||
return node._ownerShadyRoot;
|
||||
},
|
||||
|
||||
_maybeDistribute: function(node) {
|
||||
// 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. For now, the user must call
|
||||
// distributeContent(true), which updates insertion points manually
|
||||
// and forces distribution.
|
||||
var fragContent = (node.nodeType === Node.DOCUMENT_FRAGMENT_NODE) &&
|
||||
!node.__noContent && dom(node).querySelector(CONTENT);
|
||||
var wrappedContent = fragContent &&
|
||||
(TreeApi.Logical.getParentNode(fragContent).nodeType !==
|
||||
Node.DOCUMENT_FRAGMENT_NODE);
|
||||
var hasContent = fragContent || (node.localName === CONTENT);
|
||||
// There are 2 possible cases where a distribution may need to occur:
|
||||
// 1. <content> being inserted (the host of the shady root where
|
||||
// content is inserted needs distribution)
|
||||
// 2. children being inserted into parent with a shady root (parent
|
||||
// needs distribution)
|
||||
if (hasContent) {
|
||||
var root = this.getOwnerRoot();
|
||||
if (root) {
|
||||
// note, insertion point list update is handled after node
|
||||
// mutations are complete
|
||||
this._lazyDistribute(root.host);
|
||||
}
|
||||
}
|
||||
var needsDist = this._nodeNeedsDistribution(this.node);
|
||||
if (needsDist) {
|
||||
this._lazyDistribute(this.node);
|
||||
}
|
||||
// Return true when distribution will fully handle the composition
|
||||
// Note that if a content was being inserted that was wrapped by a node,
|
||||
// and the parent does not need distribution, return false to allow
|
||||
// the nodes to be added directly, after which children may be
|
||||
// distributed and composed into the wrapping node(s)
|
||||
return needsDist || (hasContent && !wrappedContent);
|
||||
},
|
||||
|
||||
/* note: parent argument is required since node may have an out
|
||||
of date parent at this point; returns true if a <content> is being added */
|
||||
_maybeAddInsertionPoint: function(node, parent) {
|
||||
var added;
|
||||
if (node.nodeType === Node.DOCUMENT_FRAGMENT_NODE &&
|
||||
!node.__noContent) {
|
||||
var c$ = dom(node).querySelectorAll(CONTENT);
|
||||
for (var i=0, n, np, na; (i<c$.length) && (n=c$[i]); i++) {
|
||||
np = TreeApi.Logical.getParentNode(n);
|
||||
// don't allow node's parent to be fragment itself
|
||||
if (np === node) {
|
||||
np = parent;
|
||||
}
|
||||
na = this._maybeAddInsertionPoint(n, np);
|
||||
added = added || na;
|
||||
}
|
||||
} else if (node.localName === CONTENT) {
|
||||
TreeApi.Logical.saveChildNodes(parent);
|
||||
TreeApi.Logical.saveChildNodes(node);
|
||||
added = true;
|
||||
}
|
||||
return added;
|
||||
},
|
||||
|
||||
_tryRemoveUndistributedNode: function(node) {
|
||||
if (this.node.shadyRoot) {
|
||||
var parent = TreeApi.Composed.getParentNode(node);
|
||||
if (parent) {
|
||||
TreeApi.Composed.removeChild(parent, node);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
},
|
||||
|
||||
_updateInsertionPoints: function(host) {
|
||||
var i$ = host.shadyRoot._insertionPoints =
|
||||
dom(host.shadyRoot).querySelectorAll(CONTENT);
|
||||
// ensure <content>'s and their parents have logical dom info.
|
||||
for (var i=0, c; i < i$.length; i++) {
|
||||
c = i$[i];
|
||||
TreeApi.Logical.saveChildNodes(c);
|
||||
TreeApi.Logical.saveChildNodes(TreeApi.Logical.getParentNode(c));
|
||||
}
|
||||
},
|
||||
|
||||
_nodeNeedsDistribution: function(node) {
|
||||
return node && node.shadyRoot &&
|
||||
DomApi.hasInsertionPoint(node.shadyRoot);
|
||||
},
|
||||
|
||||
// a node being added is always in this same host as this.node.
|
||||
_addNodeToHost: function(host, node) {
|
||||
if (host._elementAdd) {
|
||||
host._elementAdd(node);
|
||||
}
|
||||
},
|
||||
|
||||
_removeNodeFromHost: function(host, node) {
|
||||
if (host._elementRemove) {
|
||||
host._elementRemove(node);
|
||||
}
|
||||
},
|
||||
|
||||
_removeDistributedChildren: function(root, container) {
|
||||
var hostNeedsDist;
|
||||
var ip$ = root._insertionPoints;
|
||||
for (var i=0; i<ip$.length; i++) {
|
||||
var content = ip$[i];
|
||||
if (this._contains(container, content)) {
|
||||
var dc$ = dom(content).getDistributedNodes();
|
||||
for (var j=0; j<dc$.length; j++) {
|
||||
hostNeedsDist = true;
|
||||
var node = dc$[j];
|
||||
var parent = node.parentNode;
|
||||
if (parent) {
|
||||
TreeApi.Composed.removeChild(parent, node);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return hostNeedsDist;
|
||||
},
|
||||
|
||||
_contains: function(container, node) {
|
||||
while (node) {
|
||||
if (node == container) {
|
||||
return true;
|
||||
}
|
||||
node = TreeApi.Logical.getParentNode(node);
|
||||
}
|
||||
},
|
||||
|
||||
_removeOwnerShadyRoot: function(node) {
|
||||
// optimization: only reset the tree if node is actually in a root
|
||||
if (this._hasCachedOwnerRoot(node)) {
|
||||
var c$ = TreeApi.Logical.getChildNodes(node);
|
||||
for (var i=0, l=c$.length, n; (i<l) && (n=c$[i]); i++) {
|
||||
this._removeOwnerShadyRoot(n);
|
||||
}
|
||||
}
|
||||
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$ = dom(content).getDistributedNodes();
|
||||
for (var i=0, l=n$.length, n, p$; (i<l) && (n=n$[i]); i++) {
|
||||
p$ = dom(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 DomApi.matchesSelector.call(n, selector);
|
||||
}, this.node);
|
||||
},
|
||||
|
||||
_query: function(matcher, node) {
|
||||
node = node || this.node;
|
||||
var list = [];
|
||||
this._queryElements(TreeApi.Logical.getChildNodes(node), 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(TreeApi.Logical.getChildNodes(node), matcher, list);
|
||||
},
|
||||
|
||||
getDestinationInsertionPoints: function() {
|
||||
return this.node._destinationInsertionPoints || [];
|
||||
},
|
||||
|
||||
getDistributedNodes: function() {
|
||||
return this.node._distributedNodes || [];
|
||||
},
|
||||
|
||||
_clear: function() {
|
||||
while (this.childNodes.length) {
|
||||
this.removeChild(this.childNodes[0]);
|
||||
}
|
||||
},
|
||||
|
||||
setAttribute: function(name, value) {
|
||||
this.node.setAttribute(name, value);
|
||||
this._maybeDistributeParent();
|
||||
},
|
||||
|
||||
removeAttribute: function(name) {
|
||||
this.node.removeAttribute(name);
|
||||
this._maybeDistributeParent();
|
||||
},
|
||||
|
||||
_maybeDistributeParent: function() {
|
||||
if (this._nodeNeedsDistribution(this.parentNode)) {
|
||||
this._lazyDistribute(this.parentNode);
|
||||
return true;
|
||||
}
|
||||
},
|
||||
|
||||
cloneNode: function(deep) {
|
||||
var n = nativeCloneNode.call(this.node, false);
|
||||
if (deep) {
|
||||
var c$ = this.childNodes;
|
||||
var d = dom(n);
|
||||
for (var i=0, nc; i < c$.length; i++) {
|
||||
nc = dom(c$[i]).cloneNode(true);
|
||||
d.appendChild(nc);
|
||||
}
|
||||
}
|
||||
return n;
|
||||
},
|
||||
|
||||
importNode: function(externalNode, deep) {
|
||||
// for convenience use this node's ownerDoc if the node isn't a document
|
||||
var doc = this.node instanceof Document ? this.node :
|
||||
this.node.ownerDocument;
|
||||
var n = nativeImportNode.call(doc, externalNode, false);
|
||||
if (deep) {
|
||||
var c$ = TreeApi.Logical.getChildNodes(externalNode);
|
||||
var d = dom(n);
|
||||
for (var i=0, nc; i < c$.length; i++) {
|
||||
nc = dom(doc).importNode(c$[i], true);
|
||||
d.appendChild(nc);
|
||||
}
|
||||
}
|
||||
return n;
|
||||
},
|
||||
|
||||
_getComposedInnerHTML: function() {
|
||||
return getInnerHTML(this.node, true);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
Object.defineProperties(DomApi.prototype, {
|
||||
|
||||
childNodes: {
|
||||
get: function() {
|
||||
var c$ = TreeApi.Logical.getChildNodes(this.node);
|
||||
return Array.isArray(c$) ? c$ : TreeApi.arrayCopyChildNodes(this.node);
|
||||
},
|
||||
configurable: true
|
||||
},
|
||||
|
||||
children: {
|
||||
get: function() {
|
||||
if (TreeApi.Logical.hasChildNodes(this.node)) {
|
||||
return Array.prototype.filter.call(this.childNodes, function(n) {
|
||||
return (n.nodeType === Node.ELEMENT_NODE);
|
||||
});
|
||||
} else {
|
||||
return TreeApi.arrayCopyChildren(this.node);
|
||||
}
|
||||
},
|
||||
configurable: true
|
||||
},
|
||||
|
||||
parentNode: {
|
||||
get: function() {
|
||||
return TreeApi.Logical.getParentNode(this.node);
|
||||
},
|
||||
configurable: true
|
||||
},
|
||||
|
||||
firstChild: {
|
||||
get: function() {
|
||||
return TreeApi.Logical.getFirstChild(this.node);
|
||||
},
|
||||
configurable: true
|
||||
},
|
||||
|
||||
lastChild: {
|
||||
get: function() {
|
||||
return TreeApi.Logical.getLastChild(this.node);
|
||||
},
|
||||
configurable: true
|
||||
},
|
||||
|
||||
nextSibling: {
|
||||
get: function() {
|
||||
return TreeApi.Logical.getNextSibling(this.node);
|
||||
},
|
||||
configurable: true
|
||||
},
|
||||
|
||||
previousSibling: {
|
||||
get: function() {
|
||||
return TreeApi.Logical.getPreviousSibling(this.node);
|
||||
},
|
||||
configurable: true
|
||||
},
|
||||
|
||||
firstElementChild: {
|
||||
get: function() {
|
||||
return TreeApi.Logical.getFirstElementChild(this.node);
|
||||
},
|
||||
configurable: true
|
||||
},
|
||||
|
||||
lastElementChild: {
|
||||
get: function() {
|
||||
return TreeApi.Logical.getLastElementChild(this.node);
|
||||
},
|
||||
configurable: true
|
||||
},
|
||||
|
||||
nextElementSibling: {
|
||||
get: function() {
|
||||
return TreeApi.Logical.getNextElementSibling(this.node);
|
||||
},
|
||||
configurable: true
|
||||
},
|
||||
|
||||
previousElementSibling: {
|
||||
get: function() {
|
||||
return TreeApi.Logical.getPreviousElementSibling(this.node);
|
||||
},
|
||||
configurable: true
|
||||
},
|
||||
|
||||
// textContent / innerHTML
|
||||
textContent: {
|
||||
get: function() {
|
||||
var nt = this.node.nodeType;
|
||||
if (nt === Node.TEXT_NODE || nt === Node.COMMENT_NODE) {
|
||||
return this.node.textContent;
|
||||
} else {
|
||||
var tc = [];
|
||||
for (var i = 0, cn = this.childNodes, c; c = cn[i]; i++) {
|
||||
if (c.nodeType !== Node.COMMENT_NODE) {
|
||||
tc.push(c.textContent);
|
||||
}
|
||||
}
|
||||
return tc.join('');
|
||||
}
|
||||
},
|
||||
set: function(text) {
|
||||
var nt = this.node.nodeType;
|
||||
if (nt === Node.TEXT_NODE || nt === Node.COMMENT_NODE) {
|
||||
this.node.textContent = text;
|
||||
} else {
|
||||
this._clear();
|
||||
if (text) {
|
||||
this.appendChild(document.createTextNode(text));
|
||||
}
|
||||
}
|
||||
},
|
||||
configurable: true
|
||||
},
|
||||
|
||||
innerHTML: {
|
||||
get: function() {
|
||||
var nt = this.node.nodeType;
|
||||
if (nt === Node.TEXT_NODE || nt === Node.COMMENT_NODE) {
|
||||
return null;
|
||||
} else {
|
||||
return getInnerHTML(this.node);
|
||||
}
|
||||
},
|
||||
set: function(text) {
|
||||
var nt = this.node.nodeType;
|
||||
if (nt !== Node.TEXT_NODE || nt !== Node.COMMENT_NODE) {
|
||||
this._clear();
|
||||
var d = document.createElement('div');
|
||||
d.innerHTML = text;
|
||||
// here, appendChild may move nodes async so we cannot rely
|
||||
// on node position when copying
|
||||
var c$ = TreeApi.arrayCopyChildNodes(d);
|
||||
for (var i=0; i < c$.length; i++) {
|
||||
this.appendChild(c$[i]);
|
||||
}
|
||||
}
|
||||
},
|
||||
configurable: true
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
DomApi.hasInsertionPoint = function(root) {
|
||||
return Boolean(root && root._insertionPoints.length);
|
||||
};
|
||||
|
||||
})();
|
||||
</script>
|
@ -21,24 +21,14 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
'use strict';
|
||||
|
||||
var Settings = Polymer.Settings;
|
||||
var getInnerHTML = Polymer.domInnerHTML.getInnerHTML;
|
||||
|
||||
var nativeInsertBefore = Element.prototype.insertBefore;
|
||||
var nativeRemoveChild = Element.prototype.removeChild;
|
||||
var nativeAppendChild = Element.prototype.appendChild;
|
||||
var nativeCloneNode = Element.prototype.cloneNode;
|
||||
var nativeImportNode = Document.prototype.importNode;
|
||||
var DomApi = function(node) {
|
||||
this.node = needsToWrap ? DomApi.wrap(node) : node;
|
||||
};
|
||||
|
||||
// ensure nodes are wrapped if SD polyfill is present
|
||||
var needsToWrap = Settings.hasShadow && !Settings.nativeShadow;
|
||||
var wrap = window.wrap ? window.wrap : function(node) { return node; };
|
||||
|
||||
var DomApi = function(node) {
|
||||
this.node = needsToWrap ? wrap(node) : node;
|
||||
if (this.patch) {
|
||||
this.patch();
|
||||
}
|
||||
};
|
||||
DomApi.wrap = window.wrap ? window.wrap : function(node) { return node; };
|
||||
|
||||
DomApi.prototype = {
|
||||
|
||||
@ -68,400 +58,6 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
return n === this.node;
|
||||
},
|
||||
|
||||
_lazyDistribute: function(host) {
|
||||
// note: only try to distribute if the root is not clean; this ensures
|
||||
// we don't distribute before initial distribution
|
||||
if (host.shadyRoot && host.shadyRoot._distributionClean) {
|
||||
host.shadyRoot._distributionClean = false;
|
||||
Polymer.dom.addDebouncer(host.debounce('_distribute',
|
||||
host._distributeContent));
|
||||
}
|
||||
},
|
||||
|
||||
appendChild: function(node) {
|
||||
return this._addNode(node);
|
||||
},
|
||||
|
||||
insertBefore: function(node, ref_node) {
|
||||
return this._addNode(node, ref_node);
|
||||
},
|
||||
|
||||
// cases in which we may not be able to just do standard native call
|
||||
// 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)
|
||||
_addNode: function(node, ref_node) {
|
||||
this._removeNodeFromParent(node);
|
||||
var addedInsertionPoint;
|
||||
var root = this.getOwnerRoot();
|
||||
// if a <content> is added, make sure it's parent has logical info.
|
||||
if (root) {
|
||||
addedInsertionPoint = this._maybeAddInsertionPoint(node, this.node);
|
||||
}
|
||||
if (this._nodeHasLogicalChildren(this.node)) {
|
||||
if (ref_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');
|
||||
}
|
||||
}
|
||||
this._addLogicalInfo(node, this.node, index);
|
||||
}
|
||||
this._addNodeToHost(node);
|
||||
// if not distributing and not adding to host, do a fast path addition
|
||||
if (!this._maybeDistribute(node, this.node) &&
|
||||
!this._tryRemoveUndistributedNode(node)) {
|
||||
if (ref_node) {
|
||||
// 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;
|
||||
addToComposedParent(container, node, ref_node);
|
||||
if (ref_node) {
|
||||
nativeInsertBefore.call(container, node, ref_node);
|
||||
} else {
|
||||
nativeAppendChild.call(container, node);
|
||||
}
|
||||
}
|
||||
if (addedInsertionPoint) {
|
||||
this._updateInsertionPoints(root.host);
|
||||
}
|
||||
this.notifyObserver();
|
||||
return node;
|
||||
},
|
||||
|
||||
/**
|
||||
Removes the given `node` from the element's `lightChildren`.
|
||||
This method also performs dom composition.
|
||||
*/
|
||||
removeChild: function(node) {
|
||||
if (factory(node).parentNode !== this.node) {
|
||||
console.warn('The node to be removed is not a child of this node',
|
||||
node);
|
||||
}
|
||||
this._removeNodeFromHost(node);
|
||||
if (!this._maybeDistribute(node, this.node)) {
|
||||
// if removing from a shadyRoot, remove form host instead
|
||||
var container = this.node._isShadyRoot ? this.node.host : this.node;
|
||||
// not guaranteed to physically be in container; e.g.
|
||||
// undistributed nodes.
|
||||
if (container === node.parentNode) {
|
||||
removeFromComposedParent(container, node);
|
||||
nativeRemoveChild.call(container, node);
|
||||
}
|
||||
}
|
||||
this.notifyObserver();
|
||||
return node;
|
||||
},
|
||||
|
||||
replaceChild: function(node, ref_node) {
|
||||
this.insertBefore(node, ref_node);
|
||||
this.removeChild(ref_node);
|
||||
return node;
|
||||
},
|
||||
|
||||
_hasCachedOwnerRoot: function(node) {
|
||||
return Boolean(node._ownerShadyRoot !== undefined);
|
||||
},
|
||||
|
||||
getOwnerRoot: function() {
|
||||
return this._ownerShadyRootForNode(this.node);
|
||||
},
|
||||
|
||||
_ownerShadyRootForNode: function(node) {
|
||||
if (!node) {
|
||||
return;
|
||||
}
|
||||
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) {
|
||||
// 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. For now, the user must call
|
||||
// distributeContent(true), which updates insertion points manually
|
||||
// and forces distribution.
|
||||
var fragContent = (node.nodeType === Node.DOCUMENT_FRAGMENT_NODE) &&
|
||||
!node.__noContent && Polymer.dom(node).querySelector(CONTENT);
|
||||
var wrappedContent = fragContent &&
|
||||
(Polymer.dom(fragContent).parentNode.nodeType !==
|
||||
Node.DOCUMENT_FRAGMENT_NODE);
|
||||
var hasContent = fragContent || (node.localName === CONTENT);
|
||||
// There are 2 possible cases where a distribution may need to occur:
|
||||
// 1. <content> being inserted (the host of the shady root where
|
||||
// content is inserted needs distribution)
|
||||
// 2. children being inserted into parent with a shady root (parent
|
||||
// needs distribution)
|
||||
if (hasContent) {
|
||||
var root = this._ownerShadyRootForNode(parent);
|
||||
if (root) {
|
||||
var host = root.host;
|
||||
// note, insertion point list update is handled after node
|
||||
// mutations are complete
|
||||
this._lazyDistribute(host);
|
||||
}
|
||||
}
|
||||
var parentNeedsDist = this._parentNeedsDistribution(parent);
|
||||
if (parentNeedsDist) {
|
||||
this._lazyDistribute(parent);
|
||||
}
|
||||
// Return true when distribution will fully handle the composition
|
||||
// Note that if a content was being inserted that was wrapped by a node,
|
||||
// and the parent does not need distribution, return false to allow
|
||||
// the nodes to be added directly, after which children may be
|
||||
// distributed and composed into the wrapping node(s)
|
||||
return parentNeedsDist || (hasContent && !wrappedContent);
|
||||
},
|
||||
|
||||
/* note: parent argument is required since node may have an out
|
||||
of date parent at this point; returns true if a <content> is being added */
|
||||
_maybeAddInsertionPoint: function(node, parent) {
|
||||
var added;
|
||||
if (node.nodeType === Node.DOCUMENT_FRAGMENT_NODE &&
|
||||
!node.__noContent) {
|
||||
var c$ = factory(node).querySelectorAll(CONTENT);
|
||||
for (var i=0, n, np, na; (i<c$.length) && (n=c$[i]); i++) {
|
||||
np = factory(n).parentNode;
|
||||
// don't allow node's parent to be fragment itself
|
||||
if (np === node) {
|
||||
np = parent;
|
||||
}
|
||||
na = this._maybeAddInsertionPoint(n, np);
|
||||
added = added || na;
|
||||
}
|
||||
} else if (node.localName === CONTENT) {
|
||||
saveLightChildrenIfNeeded(parent);
|
||||
saveLightChildrenIfNeeded(node);
|
||||
added = true;
|
||||
}
|
||||
return added;
|
||||
},
|
||||
|
||||
_tryRemoveUndistributedNode: function(node) {
|
||||
if (this.node.shadyRoot) {
|
||||
var parent = getComposedParent(node);
|
||||
if (parent) {
|
||||
nativeRemoveChild.call(parent, node);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
},
|
||||
|
||||
_updateInsertionPoints: function(host) {
|
||||
var i$ = host.shadyRoot._insertionPoints =
|
||||
factory(host.shadyRoot).querySelectorAll(CONTENT);
|
||||
// ensure <content>'s and their parents have logical dom info.
|
||||
for (var i=0, c; i < i$.length; i++) {
|
||||
c = i$[i];
|
||||
saveLightChildrenIfNeeded(c);
|
||||
saveLightChildrenIfNeeded(factory(c).parentNode);
|
||||
}
|
||||
},
|
||||
|
||||
// a node has logical children
|
||||
_nodeHasLogicalChildren: function(node) {
|
||||
return Boolean(node._lightChildren !== undefined);
|
||||
},
|
||||
|
||||
_parentNeedsDistribution: function(parent) {
|
||||
return parent && parent.shadyRoot && hasInsertionPoint(parent.shadyRoot);
|
||||
},
|
||||
|
||||
_removeNodeFromParent: function(node) {
|
||||
// note: we may need to notify and not have logical info so fallback
|
||||
// to composed parentNode.
|
||||
var parent = node._lightParent || node.parentNode;
|
||||
if (parent && hasDomApi(parent)) {
|
||||
factory(parent).notifyObserver();
|
||||
}
|
||||
this._removeNodeFromHost(node, true);
|
||||
},
|
||||
|
||||
// NOTE: if `ensureComposedRemoval` is true then the node should be
|
||||
// removed from its composed parent.
|
||||
_removeNodeFromHost: function(node, ensureComposedRemoval) {
|
||||
// note that it's possible for both the node's host and its parent
|
||||
// to require distribution... both cases are handled here.
|
||||
var hostNeedsDist;
|
||||
var root;
|
||||
var parent = node._lightParent;
|
||||
if (parent) {
|
||||
// distribute node's parent iff needed
|
||||
factory(node)._distributeParent();
|
||||
root = this._ownerShadyRootForNode(node);
|
||||
// remove node from root and distribute it iff needed
|
||||
if (root) {
|
||||
root.host._elementRemove(node);
|
||||
hostNeedsDist = this._removeDistributedChildren(root, node);
|
||||
}
|
||||
this._removeLogicalInfo(node, parent);
|
||||
}
|
||||
this._removeOwnerShadyRoot(node);
|
||||
if (root && hostNeedsDist) {
|
||||
this._updateInsertionPoints(root.host);
|
||||
this._lazyDistribute(root.host);
|
||||
} else if (ensureComposedRemoval) {
|
||||
removeFromComposedParent(getComposedParent(node), node);
|
||||
}
|
||||
},
|
||||
|
||||
_removeDistributedChildren: function(root, container) {
|
||||
var hostNeedsDist;
|
||||
var ip$ = root._insertionPoints;
|
||||
for (var i=0; i<ip$.length; i++) {
|
||||
var content = ip$[i];
|
||||
if (this._contains(container, content)) {
|
||||
var dc$ = factory(content).getDistributedNodes();
|
||||
for (var j=0; j<dc$.length; j++) {
|
||||
hostNeedsDist = true;
|
||||
var node = dc$[j];
|
||||
var parent = node.parentNode;
|
||||
if (parent) {
|
||||
removeFromComposedParent(parent, node);
|
||||
nativeRemoveChild.call(parent, node);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return hostNeedsDist;
|
||||
},
|
||||
|
||||
_contains: function(container, node) {
|
||||
while (node) {
|
||||
if (node == container) {
|
||||
return true;
|
||||
}
|
||||
node = factory(node).parentNode;
|
||||
}
|
||||
},
|
||||
|
||||
// a node being added is always in this same host as this.node.
|
||||
_addNodeToHost: function(node) {
|
||||
var root = this.getOwnerRoot();
|
||||
if (root) {
|
||||
root.host._elementAdd(node);
|
||||
}
|
||||
},
|
||||
|
||||
_addLogicalInfo: function(node, container, index) {
|
||||
var children = factory(container).childNodes;
|
||||
index = index === undefined ? children.length : index;
|
||||
// handle document fragments
|
||||
if (node.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
|
||||
// NOTE: the act of setting this info can affect patched nodes
|
||||
// getters; therefore capture childNodes before patching.
|
||||
var c$ = arrayCopyChildNodes(node);
|
||||
for (var i=0, n; (i<c$.length) && (n=c$[i]); i++) {
|
||||
children.splice(index++, 0, n);
|
||||
n._lightParent = container;
|
||||
}
|
||||
} 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) {
|
||||
// optimization: only reset the tree if node is actually in a root
|
||||
if (this._hasCachedOwnerRoot(node)) {
|
||||
var c$ = factory(node).childNodes;
|
||||
for (var i=0, l=c$.length, n; (i<l) && (n=c$[i]); i++) {
|
||||
this._removeOwnerShadyRoot(n);
|
||||
}
|
||||
}
|
||||
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) {
|
||||
node = node || this.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
|
||||
@ -472,7 +68,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
var list = [];
|
||||
for (var i=0, l=c$.length, c; (i<l) && (c=c$[i]); i++) {
|
||||
if ((c.nodeType === Node.ELEMENT_NODE) &&
|
||||
matchesSelector.call(c, selector)) {
|
||||
DomApi.matchesSelector.call(c, selector)) {
|
||||
list.push(c);
|
||||
}
|
||||
}
|
||||
@ -489,7 +85,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
var c$ = this.childNodes;
|
||||
for (var i=0, l=c$.length, c; (i<l) && (c=c$[i]); i++) {
|
||||
if (c.localName === CONTENT) {
|
||||
var d$ = factory(c).getDistributedNodes();
|
||||
var d$ = dom(c).getDistributedNodes();
|
||||
for (var j=0; j < d$.length; j++) {
|
||||
list.push(d$[j]);
|
||||
}
|
||||
@ -500,57 +96,6 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
return list;
|
||||
},
|
||||
|
||||
_clear: function() {
|
||||
while (this.childNodes.length) {
|
||||
this.removeChild(this.childNodes[0]);
|
||||
}
|
||||
},
|
||||
|
||||
setAttribute: function(name, value) {
|
||||
this.node.setAttribute(name, value);
|
||||
this._distributeParent();
|
||||
},
|
||||
|
||||
removeAttribute: function(name) {
|
||||
this.node.removeAttribute(name);
|
||||
this._distributeParent();
|
||||
},
|
||||
|
||||
_distributeParent: function() {
|
||||
if (this._parentNeedsDistribution(this.parentNode)) {
|
||||
this._lazyDistribute(this.parentNode);
|
||||
}
|
||||
},
|
||||
|
||||
cloneNode: function(deep) {
|
||||
var n = nativeCloneNode.call(this.node, false);
|
||||
if (deep) {
|
||||
var c$ = this.childNodes;
|
||||
var d = factory(n);
|
||||
for (var i=0, nc; i < c$.length; i++) {
|
||||
nc = factory(c$[i]).cloneNode(true);
|
||||
d.appendChild(nc);
|
||||
}
|
||||
}
|
||||
return n;
|
||||
},
|
||||
|
||||
importNode: function(externalNode, deep) {
|
||||
// for convenience use this node's ownerDoc if the node isn't a document
|
||||
var doc = this.node instanceof Document ? this.node :
|
||||
this.node.ownerDocument;
|
||||
var n = nativeImportNode.call(doc, externalNode, false);
|
||||
if (deep) {
|
||||
var c$ = factory(externalNode).childNodes;
|
||||
var d = factory(n);
|
||||
for (var i=0, nc; i < c$.length; i++) {
|
||||
nc = factory(doc).importNode(c$[i], true);
|
||||
d.appendChild(nc);
|
||||
}
|
||||
}
|
||||
return n;
|
||||
},
|
||||
|
||||
/**
|
||||
* Notifies callers about changes to the element's effective child nodes,
|
||||
* the same list as returned by `getEffectiveChildNodes`.
|
||||
@ -593,420 +138,37 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
|
||||
};
|
||||
|
||||
// changes and accessors...
|
||||
if (!Settings.useShadow) {
|
||||
var CONTENT = DomApi.CONTENT = 'content';
|
||||
|
||||
Object.defineProperties(DomApi.prototype, {
|
||||
|
||||
childNodes: {
|
||||
get: function() {
|
||||
var c$ = getLightChildren(this.node);
|
||||
return Array.isArray(c$) ? c$ : arrayCopyChildNodes(this.node);
|
||||
},
|
||||
configurable: true
|
||||
},
|
||||
|
||||
children: {
|
||||
get: function() {
|
||||
return Array.prototype.filter.call(this.childNodes, function(n) {
|
||||
return (n.nodeType === Node.ELEMENT_NODE);
|
||||
});
|
||||
},
|
||||
configurable: true
|
||||
},
|
||||
|
||||
parentNode: {
|
||||
get: function() {
|
||||
return this.node._lightParent ||
|
||||
getComposedParent(this.node);
|
||||
},
|
||||
configurable: true
|
||||
},
|
||||
|
||||
firstChild: {
|
||||
get: function() {
|
||||
return this.childNodes[0];
|
||||
},
|
||||
configurable: true
|
||||
},
|
||||
|
||||
lastChild: {
|
||||
get: function() {
|
||||
var c$ = this.childNodes;
|
||||
return c$[c$.length-1];
|
||||
},
|
||||
configurable: true
|
||||
},
|
||||
|
||||
nextSibling: {
|
||||
get: function() {
|
||||
var c$ = this.parentNode && factory(this.parentNode).childNodes;
|
||||
if (c$) {
|
||||
return c$[Array.prototype.indexOf.call(c$, this.node) + 1];
|
||||
}
|
||||
},
|
||||
configurable: true
|
||||
},
|
||||
|
||||
previousSibling: {
|
||||
get: function() {
|
||||
var c$ = this.parentNode && factory(this.parentNode).childNodes;
|
||||
if (c$) {
|
||||
return c$[Array.prototype.indexOf.call(c$, this.node) - 1];
|
||||
}
|
||||
},
|
||||
configurable: true
|
||||
},
|
||||
|
||||
firstElementChild: {
|
||||
get: function() {
|
||||
return this.children[0];
|
||||
},
|
||||
configurable: true
|
||||
},
|
||||
|
||||
lastElementChild: {
|
||||
get: function() {
|
||||
var c$ = this.children;
|
||||
return c$[c$.length-1];
|
||||
},
|
||||
configurable: true
|
||||
},
|
||||
|
||||
nextElementSibling: {
|
||||
get: function() {
|
||||
var c$ = this.parentNode && factory(this.parentNode).children;
|
||||
if (c$) {
|
||||
return c$[Array.prototype.indexOf.call(c$, this.node) + 1];
|
||||
}
|
||||
},
|
||||
configurable: true
|
||||
},
|
||||
|
||||
previousElementSibling: {
|
||||
get: function() {
|
||||
var c$ = this.parentNode && factory(this.parentNode).children;
|
||||
if (c$) {
|
||||
return c$[Array.prototype.indexOf.call(c$, this.node) - 1];
|
||||
}
|
||||
},
|
||||
configurable: true
|
||||
},
|
||||
|
||||
// textContent / innerHTML
|
||||
textContent: {
|
||||
get: function() {
|
||||
var nt = this.node.nodeType;
|
||||
if (nt === Node.TEXT_NODE || nt === Node.COMMENT_NODE) {
|
||||
return this.node.textContent;
|
||||
} else {
|
||||
var tc = [];
|
||||
for (var i = 0, cn = this.childNodes, c; c = cn[i]; i++) {
|
||||
if (c.nodeType !== Node.COMMENT_NODE) {
|
||||
tc.push(c.textContent);
|
||||
}
|
||||
}
|
||||
return tc.join('');
|
||||
}
|
||||
},
|
||||
set: function(text) {
|
||||
var nt = this.node.nodeType;
|
||||
if (nt === Node.TEXT_NODE || nt === Node.COMMENT_NODE) {
|
||||
this.node.textContent = text;
|
||||
} else {
|
||||
this._clear();
|
||||
if (text) {
|
||||
this.appendChild(document.createTextNode(text));
|
||||
}
|
||||
}
|
||||
},
|
||||
configurable: true
|
||||
},
|
||||
|
||||
innerHTML: {
|
||||
get: function() {
|
||||
var nt = this.node.nodeType;
|
||||
if (nt === Node.TEXT_NODE || nt === Node.COMMENT_NODE) {
|
||||
return null;
|
||||
} else {
|
||||
return getInnerHTML(this.node);
|
||||
}
|
||||
},
|
||||
set: function(text) {
|
||||
var nt = this.node.nodeType;
|
||||
if (nt !== Node.TEXT_NODE || nt !== Node.COMMENT_NODE) {
|
||||
this._clear();
|
||||
var d = document.createElement('div');
|
||||
d.innerHTML = text;
|
||||
// here, appendChild may move nodes async so we cannot rely
|
||||
// on node position when copying
|
||||
var c$ = arrayCopyChildNodes(d);
|
||||
for (var i=0; i < c$.length; i++) {
|
||||
this.appendChild(c$[i]);
|
||||
}
|
||||
}
|
||||
},
|
||||
configurable: true
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
DomApi.prototype._getComposedInnerHTML = function() {
|
||||
return getInnerHTML(this.node, true);
|
||||
};
|
||||
|
||||
|
||||
} else {
|
||||
|
||||
var forwardMethods = function(m$) {
|
||||
for (var i=0; i < m$.length; i++) {
|
||||
forwardMethod(m$[i]);
|
||||
}
|
||||
};
|
||||
|
||||
var forwardMethod = function(method) {
|
||||
DomApi.prototype[method] = function() {
|
||||
return this.node[method].apply(this.node, arguments);
|
||||
};
|
||||
};
|
||||
|
||||
forwardMethods(['cloneNode', 'appendChild', 'insertBefore',
|
||||
'removeChild', 'replaceChild']);
|
||||
|
||||
DomApi.prototype.querySelectorAll = function(selector) {
|
||||
return arrayCopy(this.node.querySelectorAll(selector));
|
||||
};
|
||||
|
||||
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.importNode = function(externalNode, deep) {
|
||||
var doc = this.node instanceof Document ? this.node :
|
||||
this.node.ownerDocument;
|
||||
return doc.importNode(externalNode, deep);
|
||||
};
|
||||
|
||||
DomApi.prototype.getDestinationInsertionPoints = function() {
|
||||
var n$ = this.node.getDestinationInsertionPoints &&
|
||||
this.node.getDestinationInsertionPoints();
|
||||
return n$ ? arrayCopy(n$) : [];
|
||||
};
|
||||
|
||||
DomApi.prototype.getDistributedNodes = function() {
|
||||
var n$ = this.node.getDistributedNodes &&
|
||||
this.node.getDistributedNodes();
|
||||
return n$ ? arrayCopy(n$) : [];
|
||||
};
|
||||
|
||||
DomApi.prototype._distributeParent = function() {};
|
||||
|
||||
Object.defineProperties(DomApi.prototype, {
|
||||
|
||||
childNodes: {
|
||||
get: function() {
|
||||
return arrayCopyChildNodes(this.node);
|
||||
},
|
||||
configurable: true
|
||||
},
|
||||
|
||||
children: {
|
||||
get: function() {
|
||||
return arrayCopyChildren(this.node);
|
||||
},
|
||||
configurable: true
|
||||
},
|
||||
|
||||
// textContent / innerHTML
|
||||
textContent: {
|
||||
get: function() {
|
||||
return this.node.textContent;
|
||||
},
|
||||
set: function(value) {
|
||||
return this.node.textContent = value;
|
||||
},
|
||||
configurable: true
|
||||
},
|
||||
|
||||
innerHTML: {
|
||||
get: function() {
|
||||
return this.node.innerHTML;
|
||||
},
|
||||
set: function(value) {
|
||||
return this.node.innerHTML = value;
|
||||
},
|
||||
configurable: true
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
var forwardProperties = function(f$) {
|
||||
for (var i=0; i < f$.length; i++) {
|
||||
forwardProperty(f$[i]);
|
||||
}
|
||||
};
|
||||
|
||||
var forwardProperty = function(name) {
|
||||
Object.defineProperty(DomApi.prototype, name, {
|
||||
get: function() {
|
||||
return this.node[name];
|
||||
},
|
||||
configurable: true
|
||||
});
|
||||
};
|
||||
|
||||
forwardProperties(['parentNode', 'firstChild', 'lastChild',
|
||||
'nextSibling', 'previousSibling', 'firstElementChild',
|
||||
'lastElementChild', 'nextElementSibling', 'previousElementSibling']);
|
||||
|
||||
}
|
||||
|
||||
var CONTENT = 'content';
|
||||
|
||||
function factory(node, patch) {
|
||||
var dom = DomApi.factory = function(node) {
|
||||
node = node || document;
|
||||
if (!node.__domApi) {
|
||||
node.__domApi = new DomApi(node, patch);
|
||||
node.__domApi = new DomApi.ctor(node);
|
||||
}
|
||||
return node.__domApi;
|
||||
}
|
||||
};
|
||||
|
||||
function hasDomApi(node) {
|
||||
DomApi.hasApi = function(node) {
|
||||
return Boolean(node.__domApi);
|
||||
}
|
||||
};
|
||||
|
||||
DomApi.ctor = DomApi;
|
||||
|
||||
Polymer.dom = function(obj, patch) {
|
||||
if (obj instanceof Event) {
|
||||
return Polymer.EventApi.factory(obj);
|
||||
} else {
|
||||
return factory(obj, patch);
|
||||
return DomApi.factory(obj, patch);
|
||||
}
|
||||
};
|
||||
|
||||
function getLightChildren(node) {
|
||||
var children = node._lightChildren;
|
||||
// TODO(sorvell): it's more correct to use _composedChildren instead of
|
||||
// childNodes here but any trivial failure to use Polymer.dom
|
||||
// will result in an error so we avoid using _composedChildren
|
||||
return children ? children : node.childNodes;
|
||||
}
|
||||
|
||||
function getComposedChildren(node) {
|
||||
if (!node._composedChildren) {
|
||||
node._composedChildren = arrayCopyChildNodes(node);
|
||||
}
|
||||
return node._composedChildren;
|
||||
}
|
||||
|
||||
function addToComposedParent(parent, node, ref_node) {
|
||||
var children = getComposedChildren(parent);
|
||||
var i = ref_node ? children.indexOf(ref_node) : -1;
|
||||
if (node.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
|
||||
var fragChildren = getComposedChildren(node);
|
||||
for (var j=0; j < fragChildren.length; j++) {
|
||||
addNodeToComposedChildren(fragChildren[j], parent, children, i + j);
|
||||
}
|
||||
node._composedChildren = null;
|
||||
} else {
|
||||
addNodeToComposedChildren(node, parent, children, i);
|
||||
}
|
||||
}
|
||||
|
||||
function getComposedParent(node) {
|
||||
return node.__patched ? node._composedParent : node.parentNode;
|
||||
}
|
||||
|
||||
function addNodeToComposedChildren(node, parent, children, i) {
|
||||
node._composedParent = parent;
|
||||
children.splice(i >= 0 ? i : children.length, 0, node);
|
||||
}
|
||||
|
||||
function removeFromComposedParent(parent, node) {
|
||||
node._composedParent = null;
|
||||
if (parent) {
|
||||
var children = getComposedChildren(parent);
|
||||
var i = children.indexOf(node);
|
||||
if (i >= 0) {
|
||||
children.splice(i, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 c$ = arrayCopyChildNodes(node);
|
||||
for (var i=0, l=c$.length, child; (i<l) && (child=c$[i]); i++) {
|
||||
child._lightParent = child._lightParent || node;
|
||||
}
|
||||
node._lightChildren = c$;
|
||||
}
|
||||
}
|
||||
|
||||
// sad but faster than slice...
|
||||
function arrayCopyChildNodes(parent) {
|
||||
var copy=[], i=0;
|
||||
for (var n=parent.firstChild; n; n=n.nextSibling) {
|
||||
copy[i++] = n;
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
|
||||
function arrayCopyChildren(parent) {
|
||||
var copy=[], i=0;
|
||||
for (var n=parent.firstElementChild; n; n=n.nextElementSibling) {
|
||||
copy[i++] = n;
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
|
||||
function arrayCopy(a$) {
|
||||
var l = a$.length;
|
||||
var copy = new Array(l);
|
||||
for (var i=0; i < l; i++) {
|
||||
copy[i] = a$[i];
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
|
||||
function hasInsertionPoint(root) {
|
||||
return Boolean(root && root._insertionPoints.length);
|
||||
}
|
||||
|
||||
var p = Element.prototype;
|
||||
var matchesSelector = p.matches || p.matchesSelector ||
|
||||
p.mozMatchesSelector || p.msMatchesSelector ||
|
||||
p.oMatchesSelector || p.webkitMatchesSelector;
|
||||
|
||||
return {
|
||||
getLightChildren: getLightChildren,
|
||||
getComposedParent: getComposedParent,
|
||||
getComposedChildren: getComposedChildren,
|
||||
removeFromComposedParent: removeFromComposedParent,
|
||||
saveLightChildrenIfNeeded: saveLightChildrenIfNeeded,
|
||||
matchesSelector: matchesSelector,
|
||||
hasInsertionPoint: hasInsertionPoint,
|
||||
ctor: DomApi,
|
||||
factory: factory,
|
||||
hasDomApi: hasDomApi,
|
||||
arrayCopy: arrayCopy,
|
||||
arrayCopyChildNodes: arrayCopyChildNodes,
|
||||
arrayCopyChildren: arrayCopyChildren,
|
||||
wrap: wrap
|
||||
};
|
||||
DomApi.matchesSelector = p.matches || p.matchesSelector ||
|
||||
p.mozMatchesSelector || p.msMatchesSelector ||
|
||||
p.oMatchesSelector || p.webkitMatchesSelector;
|
||||
|
||||
return DomApi;
|
||||
|
||||
})();
|
||||
|
||||
</script>
|
||||
</script>
|
288
src/lib/dom-tree-api.html
Normal file
288
src/lib/dom-tree-api.html
Normal file
@ -0,0 +1,288 @@
|
||||
<!--
|
||||
@license
|
||||
Copyright (c) 2014 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="settings.html">
|
||||
<link rel="import" href="dom-innerHTML.html">
|
||||
<script>
|
||||
(function() {
|
||||
|
||||
'use strict';
|
||||
|
||||
// native add/remove
|
||||
var nativeInsertBefore = Element.prototype.insertBefore;
|
||||
var nativeAppendChild = Element.prototype.appendChild;
|
||||
var nativeRemoveChild = Element.prototype.removeChild;
|
||||
|
||||
/**
|
||||
* TreeApi is a dom manipulation library used by Shady/Polymer.dom to
|
||||
* manipulate composed and logical trees.
|
||||
*/
|
||||
var TreeApi = Polymer.TreeApi = {
|
||||
|
||||
// sad but faster than slice...
|
||||
arrayCopyChildNodes: function(parent) {
|
||||
var copy=[], i=0;
|
||||
for (var n=parent.firstChild; n; n=n.nextSibling) {
|
||||
copy[i++] = n;
|
||||
}
|
||||
return copy;
|
||||
},
|
||||
|
||||
arrayCopyChildren: function(parent) {
|
||||
var copy=[], i=0;
|
||||
for (var n=parent.firstElementChild; n; n=n.nextElementSibling) {
|
||||
copy[i++] = n;
|
||||
}
|
||||
return copy;
|
||||
},
|
||||
|
||||
arrayCopy: function(a$) {
|
||||
var l = a$.length;
|
||||
var copy = new Array(l);
|
||||
for (var i=0; i < l; i++) {
|
||||
copy[i] = a$[i];
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
Polymer.TreeApi.Logical = {
|
||||
|
||||
hasParentNode: function(node) {
|
||||
return Boolean(node.__dom && node.__dom.parentNode);
|
||||
},
|
||||
|
||||
hasChildNodes: function(node) {
|
||||
return Boolean(node.__dom && node.__dom.childNodes !== undefined);
|
||||
},
|
||||
|
||||
getChildNodes: function(node) {
|
||||
// note: we're distinguishing here between undefined and false-y:
|
||||
// hasChildNodes uses undefined check to see if this element has logical
|
||||
// children; the false-y check indicates whether or not we should rebuild
|
||||
// the cached childNodes array.
|
||||
return this.hasChildNodes(node) ? this._getChildNodes(node) :
|
||||
node.childNodes;
|
||||
},
|
||||
|
||||
_getChildNodes: function(node) {
|
||||
if (!node.__dom.childNodes) {
|
||||
node.__dom.childNodes = [];
|
||||
for (var n=node.__dom.firstChild; n; n=n.__dom.nextSibling) {
|
||||
node.__dom.childNodes.push(n);
|
||||
}
|
||||
}
|
||||
return node.__dom.childNodes;
|
||||
},
|
||||
|
||||
getParentNode: function(node) {
|
||||
return node.__dom && node.__dom.parentNode || node.parentNode;
|
||||
},
|
||||
|
||||
getFirstChild: function(node) {
|
||||
return node.__dom && node.__dom.firstChild || node.firstChild;
|
||||
},
|
||||
|
||||
getLastChild: function(node) {
|
||||
return node.__dom && node.__dom.lastChild || node.lastChild;
|
||||
},
|
||||
|
||||
getNextSibling: function(node) {
|
||||
return node.__dom && node.__dom.nextSibling || node.nextSibling;
|
||||
},
|
||||
|
||||
getPreviousSibling: function(node) {
|
||||
return node.__dom && node.__dom.previousSibling || node.previousSibling;
|
||||
},
|
||||
|
||||
getFirstElementChild: function(node) {
|
||||
return node.__dom && node.__dom.firstChild ?
|
||||
this._getFirstElementChild(node) : node.firstElementChild;
|
||||
},
|
||||
|
||||
_getFirstElementChild: function(node) {
|
||||
var n = node.__dom.firstChild;
|
||||
while (n && n.nodeType !== Node.ELEMENT_NODE) {
|
||||
n = n.__dom.nextSibling;
|
||||
}
|
||||
return n;
|
||||
},
|
||||
|
||||
getLastElementChild: function(node) {
|
||||
return node.__dom && node.__dom.lastChild ?
|
||||
this._getLastElementChild(node) : node.firstElementChild;
|
||||
},
|
||||
|
||||
_getLastElementChild: function(node) {
|
||||
var n = node.__dom.lastChild;
|
||||
while (n && n.nodeType !== Node.ELEMENT_NODE) {
|
||||
n = n.__dom.previousSibling;
|
||||
}
|
||||
return n;
|
||||
},
|
||||
|
||||
getNextElementSibling: function(node) {
|
||||
return node.__dom && node.__dom.nextSibling ?
|
||||
this._getNextElementSibling(node) : node.nextElementSibling;
|
||||
},
|
||||
|
||||
_getNextElementSibling: function(node) {
|
||||
var n = node.__dom.nextSibling;
|
||||
while (n && n.nodeType !== Node.ELEMENT_NODE) {
|
||||
n = n.__dom.nextSibling;
|
||||
}
|
||||
return n;
|
||||
},
|
||||
|
||||
getPreviousElementSibling: function(node) {
|
||||
return node.__dom && node.__dom.previousSibling ?
|
||||
this._getPreviousElementSibling(node) : node.previousElementSibling;
|
||||
},
|
||||
|
||||
_getPreviousElementSibling: function(node) {
|
||||
var n = node.__dom.previousSibling;
|
||||
while (n && n.nodeType !== Node.ELEMENT_NODE) {
|
||||
n = n.__dom.previousSibling;
|
||||
}
|
||||
return n;
|
||||
},
|
||||
|
||||
// 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 this function
|
||||
// has been called.
|
||||
saveChildNodes: function(node) {
|
||||
if (!this.hasChildNodes(node)) {
|
||||
node.__dom = node.__dom || {};
|
||||
node.__dom.firstChild = node.firstChild;
|
||||
node.__dom.lastChild = node.lastChild;
|
||||
node.__dom.childNodes = [];
|
||||
for (var n=node.firstChild; n; n=n.nextSibling) {
|
||||
n.__dom = n.__dom || {};
|
||||
n.__dom.parentNode = node;
|
||||
node.__dom.childNodes.push(n);
|
||||
n.__dom.nextSibling = n.nextSibling;
|
||||
n.__dom.previousSibling = n.previousSibling;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
recordInsertBefore: function(node, container, ref_node) {
|
||||
container.__dom.childNodes = null;
|
||||
// handle document fragments
|
||||
if (node.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
|
||||
// TODO(sorvell): remember this for patching:
|
||||
// the act of setting this info can affect patched nodes
|
||||
// getters; therefore capture childNodes before patching.
|
||||
for (var n=node.firstChild; n; n=n.nextSibling) {
|
||||
this._linkNode(n, container, ref_node);
|
||||
}
|
||||
} else {
|
||||
this._linkNode(node, container, ref_node);
|
||||
}
|
||||
},
|
||||
|
||||
_linkNode: function(node, container, ref_node) {
|
||||
node.__dom = node.__dom || {};
|
||||
container.__dom = container.__dom || {};
|
||||
if (ref_node) {
|
||||
ref_node.__dom = ref_node.__dom || {};
|
||||
}
|
||||
// update ref_node.previousSibling <-> node
|
||||
node.__dom.previousSibling = ref_node ? ref_node.__dom.previousSibling :
|
||||
container.__dom.lastChild;
|
||||
if (node.__dom.previousSibling) {
|
||||
node.__dom.previousSibling.__dom.nextSibling = node;
|
||||
}
|
||||
// update node <-> ref_node
|
||||
node.__dom.nextSibling = ref_node;
|
||||
if (node.__dom.nextSibling) {
|
||||
node.__dom.nextSibling.__dom.previousSibling = node;
|
||||
}
|
||||
// update node <-> container
|
||||
node.__dom.parentNode = container;
|
||||
if (ref_node) {
|
||||
if (ref_node === container.__dom.firstChild) {
|
||||
container.__dom.firstChild = node;
|
||||
}
|
||||
} else {
|
||||
container.__dom.lastChild = node;
|
||||
if (!container.__dom.firstChild) {
|
||||
container.__dom.firstChild = node;
|
||||
}
|
||||
}
|
||||
// remove caching of childNodes
|
||||
container.__dom.childNodes = null;
|
||||
},
|
||||
|
||||
recordRemoveChild: function(node, container) {
|
||||
node.__dom = node.__dom || {};
|
||||
container.__dom = container.__dom || {};
|
||||
if (node === container.__dom.firstChild) {
|
||||
container.__dom.firstChild = node.__dom.nextSibling;
|
||||
}
|
||||
if (node === container.__dom.lastChild) {
|
||||
container.__dom.lastChild = node.__dom.previousSibling;
|
||||
}
|
||||
var p = node.__dom.previousSibling;
|
||||
var n = node.__dom.nextSibling;
|
||||
if (p) {
|
||||
p.__dom.nextSibling = n;
|
||||
}
|
||||
if (n) {
|
||||
n.__dom.previousSibling = p;
|
||||
}
|
||||
node.__dom.parentNode = node.__dom.previousSibling =
|
||||
node.__dom.nextSibling = null;
|
||||
// remove caching of childNodes
|
||||
container.__dom.childNodes = null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// TODO(sorvell): composed tree manipulation is made available
|
||||
// (1) to maninpulate the composed tree, and (2) to track changes
|
||||
// to the tree for optional patching pluggability.
|
||||
Polymer.TreeApi.Composed = {
|
||||
|
||||
getChildNodes: function(node) {
|
||||
return Polymer.TreeApi.arrayCopyChildNodes(node);
|
||||
},
|
||||
|
||||
getParentNode: function(node) {
|
||||
return node.parentNode;
|
||||
},
|
||||
|
||||
// composed tracking needs to reset composed children here in case
|
||||
// they may have already been set (this shouldn't happen but can
|
||||
// if dependency ordering is incorrect and as a result upgrade order
|
||||
// is unexpected)
|
||||
clearChildNodes: function(node) {
|
||||
node.textContent = '';
|
||||
},
|
||||
|
||||
insertBefore: function(parentNode, newChild, refChild) {
|
||||
return nativeInsertBefore.call(parentNode, newChild, refChild || null);
|
||||
},
|
||||
|
||||
appendChild: function(parentNode, newChild) {
|
||||
return nativeAppendChild.call(parentNode, newChild);
|
||||
},
|
||||
|
||||
removeChild: function(parentNode, node) {
|
||||
return nativeRemoveChild.call(parentNode, node);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
})();
|
||||
</script>
|
@ -22,7 +22,19 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
|
||||
(function() {
|
||||
|
||||
// ******* Only patch if we're using Shady DOM *******
|
||||
if (Polymer.Settings.useShadow) {
|
||||
return;
|
||||
}
|
||||
|
||||
var baseFinishDistribute = Polymer.Base._finishDistribute;
|
||||
var TreeApi = Polymer.TreeApi;
|
||||
var DomApi = Polymer.DomApi;
|
||||
var dom = Polymer.dom;
|
||||
|
||||
var nativeInsertBefore = Element.prototype.insertBefore;
|
||||
var nativeAppendChild = Element.prototype.appendChild;
|
||||
var nativeRemoveChild = Element.prototype.removeChild;
|
||||
|
||||
// NOTE: any manipulation of a node must occur in a patched parent
|
||||
// so that the parent can cleanup the node's composed and logical
|
||||
@ -30,13 +42,14 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
// likely to be satisfied.
|
||||
// Also note that any use of qS/qSA must be done in a patched node.
|
||||
Polymer.Base._finishDistribute = function() {
|
||||
var hasDistributed = this.root._hasDistributed;
|
||||
baseFinishDistribute.call(this);
|
||||
if (!this.__patched) {
|
||||
if (!hasDistributed) {
|
||||
for (var n=this.firstChild; n; n=n.nextSibling) {
|
||||
Polymer.dom(n);
|
||||
};
|
||||
Polymer.dom(this);
|
||||
Polymer.dom(this.root);
|
||||
Array.prototype.forEach.call(this.childNodes, function(c) {
|
||||
Polymer.dom(c);
|
||||
});
|
||||
// TODO(sorvell): ensure top element's parents are wrapped (helped A2
|
||||
// since it uses qSA on the fragment containing stamped custom elements)
|
||||
// note that getOwnerRoot will patch all parents but there should be an
|
||||
@ -48,34 +61,31 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
};
|
||||
|
||||
|
||||
var saveLightChildrenIfNeeded = Polymer.DomApi.saveLightChildrenIfNeeded;
|
||||
var getComposedChildren = Polymer.DomApi.getComposedChildren;
|
||||
|
||||
var nativeShadow = Polymer.Settings.useShadow;
|
||||
|
||||
var excluded = ['head'];
|
||||
|
||||
Polymer.telemetry.patched = 0;
|
||||
|
||||
// experimental: support patching selected native api.
|
||||
Polymer.DomApi.ctor.prototype.patch = function(force) {
|
||||
if (nativeShadow || this.node.__patched ||
|
||||
(this.node.localName && excluded.indexOf(this.node.localName) >= 0)) {
|
||||
return;
|
||||
}
|
||||
var ctor = Polymer.DomApi.ctor;
|
||||
Polymer.DomApi.ctor = function(node) {
|
||||
Polymer.DomApi.patch(node);
|
||||
return new ctor(node);
|
||||
}
|
||||
|
||||
getComposedChildren(this.node);
|
||||
saveLightChildrenIfNeeded(this.node);
|
||||
if (!this.node._lightParent) {
|
||||
this.node._lightParent = this.node.parentNode;
|
||||
}
|
||||
if (!this.node._composedParent) {
|
||||
this.node._composedParent = this.node.parentNode;
|
||||
}
|
||||
// TODO(sorvell): correctly patch non-element nodes.
|
||||
if (this.node.nodeType !== Node.TEXT_NODE) {
|
||||
this.node.__patched = true;
|
||||
patchImpl.patch(this.node);
|
||||
Polymer.DomApi.ctor.prototype = ctor.prototype;
|
||||
|
||||
Polymer.DomApi.patch = function(node) {
|
||||
if (!node.__patched &&
|
||||
(!node.localName || !excluded.indexOf(node.localName) >= 0)) {
|
||||
TreeApi.Logical.saveChildNodes(node);
|
||||
if (!TreeApi.Composed.hasParentNode(node)) {
|
||||
TreeApi.Composed.saveParentNode(node);
|
||||
}
|
||||
TreeApi.Composed.saveChildNodes(node);
|
||||
// TODO(sorvell): correctly patch non-element nodes.
|
||||
if (node.nodeType !== Node.TEXT_NODE) {
|
||||
node.__patched = true;
|
||||
patchImpl.patch(node);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -90,9 +100,21 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
this.unpatch();
|
||||
};
|
||||
|
||||
var log = false;
|
||||
// allows attribute setting to be patched
|
||||
var nativeSetAttribute = Element.prototype.setAttribute;
|
||||
Polymer.DomApi.ctor.prototype.setAttribute = function(name, value) {
|
||||
nativeSetAttribute.call(this.node, name, value);
|
||||
this._maybeDistributeParent();
|
||||
};
|
||||
|
||||
var factory = Polymer.DomApi.factory;
|
||||
var nativeRemoveAttribute = Element.prototype.removeAttribute;
|
||||
Polymer.DomApi.ctor.prototype.removeAttribute = function(name, value) {
|
||||
nativeRemoveAttribute.call(this.node, name);
|
||||
this._maybeDistributeParent();
|
||||
};
|
||||
|
||||
|
||||
var log = false;
|
||||
|
||||
var patchImpl = {
|
||||
|
||||
@ -101,7 +123,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
|
||||
methods: ['appendChild', 'insertBefore', 'removeChild', 'replaceChild',
|
||||
'querySelector', 'querySelectorAll', 'getDestinationInsertionPoints',
|
||||
'cloneNode', 'importNode'],
|
||||
'cloneNode', 'setAttribute', 'removeAttribute'],
|
||||
// <content>: getDistributedNodes
|
||||
|
||||
accessors: ['parentNode', 'childNodes',
|
||||
@ -160,7 +182,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
obj['_$' + name + '$_'] = orig;
|
||||
obj[name] = function() {
|
||||
log && console.log(this, name, arguments);
|
||||
return factory(this)[name].apply(this.__domApi, arguments);
|
||||
return dom(this)[name].apply(this.__domApi, arguments);
|
||||
};
|
||||
},
|
||||
|
||||
@ -170,14 +192,13 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
}
|
||||
var info = {
|
||||
get: function() {
|
||||
log && console.log(this, name);
|
||||
return factory(this)[name];
|
||||
return dom(this)[name];
|
||||
},
|
||||
configurable: true
|
||||
};
|
||||
if (writable) {
|
||||
info.set = function(value) {
|
||||
factory(this)[name] = value;
|
||||
dom(this)[name] = value;
|
||||
};
|
||||
}
|
||||
Object.defineProperty(obj, name, info);
|
||||
@ -216,20 +237,365 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
|
||||
};
|
||||
|
||||
Polymer.DomApi.getLightChildren = function(node) {
|
||||
var children = node._lightChildren;
|
||||
return children ? children : Polymer.DomApi.getComposedChildren(node);
|
||||
}
|
||||
|
||||
Polymer.Base.instanceTemplate = function(template) {
|
||||
var m = document._$importNode$_ || document.importNode;
|
||||
var dom =
|
||||
m.call(document, template._content || template.content, true);
|
||||
return dom;
|
||||
}
|
||||
|
||||
Polymer.DomApi.patchImpl = patchImpl;
|
||||
|
||||
// NOTE: patch logical implementations here so we can use
|
||||
// composed getters
|
||||
// TODO(sorvell): may need to patch saveChildNodes iff the tree has
|
||||
// already been distributed.
|
||||
TreeApi.Logical.recordInsertBefore = function(node, container, ref_node) {
|
||||
container.__dom.childNodes = null;
|
||||
// handle document fragments
|
||||
if (node.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
|
||||
// TODO(sorvell): remember this for patching:
|
||||
// the act of setting this info can affect patched nodes
|
||||
// getters; therefore capture childNodes before patching.
|
||||
for (var n=TreeApi.Composed.getFirstChild(node); n;
|
||||
n=TreeApi.Composed.getNextSibling(n)) {
|
||||
this._linkNode(n, container, ref_node);
|
||||
}
|
||||
} else {
|
||||
this._linkNode(node, container, ref_node);
|
||||
}
|
||||
}
|
||||
|
||||
TreeApi.Logical.getParentNode = function(node) {
|
||||
return node.__dom && node.__dom.parentNode ||
|
||||
TreeApi.Composed.getParentNode(node);
|
||||
};
|
||||
|
||||
TreeApi.Logical.getFirstChild = function(node) {
|
||||
return node.__dom && node.__dom.firstChild ||
|
||||
TreeApi.Composed.getFirstChild(node);
|
||||
};
|
||||
|
||||
TreeApi.Logical.getLastChild = function(node) {
|
||||
return node.__dom && node.__dom.lastChild ||
|
||||
TreeApi.Composed.getLastChild(node);
|
||||
};
|
||||
|
||||
TreeApi.Logical.getNextSibling = function(node) {
|
||||
return node.__dom && node.__dom.nextSibling ||
|
||||
TreeApi.Composed.getNextSibling(node);
|
||||
};
|
||||
|
||||
TreeApi.Logical.getPreviousSibling = function(node) {
|
||||
return node.__dom && node.__dom.previousSibling ||
|
||||
TreeApi.Composed.getPreviousSibling(node);
|
||||
};
|
||||
|
||||
TreeApi.Logical.getFirstElementChild = function(node) {
|
||||
return node.__dom && node.__dom.firstChild ?
|
||||
this._getFirstElementChild(node) :
|
||||
TreeApi.Composed.getFirstElementChild(node);
|
||||
};
|
||||
|
||||
TreeApi.Logical.getLastElementChild = function(node) {
|
||||
return node.__dom && node.__dom.lastChild ?
|
||||
this._getLastElementChild(node) :
|
||||
TreeApi.Composed.getLastElementChild(node);
|
||||
};
|
||||
|
||||
TreeApi.Logical.getNextElementSibling = function(node) {
|
||||
return node.__dom && node.__dom.nextSibling ?
|
||||
this._getNextElementSibling(node) :
|
||||
TreeApi.Composed.getNextElementSibling(node);
|
||||
};
|
||||
|
||||
TreeApi.Logical.getPreviousElementSibling = function(node) {
|
||||
return node.__dom && node.__dom.previousSibling ?
|
||||
this._getPreviousElementSibling(node) :
|
||||
TreeApi.Composed.getPreviousElementSibling(node);
|
||||
};
|
||||
|
||||
|
||||
// TODO(sorvell): This is largely copy/pasted from the Logical tree
|
||||
// implementation. The code could be factored such that it could be shared
|
||||
// but there are perf trade offs to consider.
|
||||
TreeApi.Composed = {
|
||||
|
||||
hasParentNode: function(node) {
|
||||
return Boolean(node.__dom && node.__dom.$parentNode !== undefined);
|
||||
},
|
||||
|
||||
hasChildNodes: function(node) {
|
||||
return Boolean(node.__dom && node.__dom.$childNodes !== undefined);
|
||||
},
|
||||
|
||||
getChildNodes: function(node) {
|
||||
return this.hasChildNodes(node) ? this._getChildNodes(node) :
|
||||
(!node.__patched && TreeApi.arrayCopy(node.childNodes));
|
||||
},
|
||||
|
||||
_getChildNodes: function(node) {
|
||||
if (!node.__dom.$childNodes) {
|
||||
node.__dom.$childNodes = [];
|
||||
for (var n=node.__dom.$firstChild; n; n=n.__dom.$nextSibling) {
|
||||
node.__dom.$childNodes.push(n);
|
||||
}
|
||||
}
|
||||
return node.__dom.$childNodes;
|
||||
},
|
||||
|
||||
getComposedChildNodes: function(node) {
|
||||
return node.__dom.$childNodes;
|
||||
},
|
||||
|
||||
getParentNode: function(node) {
|
||||
return this.hasParentNode(node) ? node.__dom.$parentNode :
|
||||
(!node.__patched && node.parentNode);
|
||||
},
|
||||
|
||||
getFirstChild: function(node) {
|
||||
return node.__patched ? node.__dom.$firstChild : node.firstChild;
|
||||
},
|
||||
|
||||
getLastChild: function(node) {
|
||||
return node.__patched ? node.__dom.$lastChild : node.lastChild;
|
||||
},
|
||||
|
||||
getNextSibling: function(node) {
|
||||
return node.__patched ? node.__dom.$nextSibling : node.nextSibling;
|
||||
},
|
||||
|
||||
getPreviousSibling: function(node) {
|
||||
return node.__patched ? node.__dom.$previousSibling : node.previousSibling;
|
||||
},
|
||||
|
||||
getFirstElementChild: function(node) {
|
||||
return node.__patched ? this._getFirstElementChild(node) :
|
||||
node.firstElementChild;
|
||||
},
|
||||
|
||||
_getFirstElementChild: function(node) {
|
||||
var n = node.__dom.$firstChild;
|
||||
while (n && n.nodeType !== Node.ELEMENT_NODE) {
|
||||
n = n.__dom.$nextSibling;
|
||||
}
|
||||
return n;
|
||||
},
|
||||
|
||||
getLastElementChild: function(node) {
|
||||
return node.__patched ? this._getLastElementChild(node) :
|
||||
node.firstElementChild;
|
||||
},
|
||||
|
||||
_getLastElementChild: function(node) {
|
||||
var n = node.__dom.$lastChild;
|
||||
while (n && n.nodeType !== Node.ELEMENT_NODE) {
|
||||
n = n.__dom.$previousSibling;
|
||||
}
|
||||
return n;
|
||||
},
|
||||
|
||||
getNextElementSibling: function(node) {
|
||||
return node.__patched ? this._getNextElementSibling(node) :
|
||||
node.nextElementSibling;
|
||||
},
|
||||
|
||||
_getNextElementSibling: function(node) {
|
||||
var n = node.__dom.$nextSibling;
|
||||
while (n && n.nodeType !== Node.ELEMENT_NODE) {
|
||||
n = n.__dom.$nextSibling;
|
||||
}
|
||||
return n;
|
||||
},
|
||||
|
||||
getPreviousElementSibling: function(node) {
|
||||
return node.__patched ? this._getPreviousElementSibling(node) :
|
||||
node.previousElementSibling;
|
||||
},
|
||||
|
||||
_getPreviousElementSibling: function(node) {
|
||||
var n = node.__dom.$previousSibling;
|
||||
while (n && n.nodeType !== Node.ELEMENT_NODE) {
|
||||
n = n.__dom.$previousSibling;
|
||||
}
|
||||
return n;
|
||||
},
|
||||
|
||||
saveChildNodes: function(node) {
|
||||
if (!this.hasChildNodes(node)) {
|
||||
node.__dom = node.__dom || {};
|
||||
node.__dom.$firstChild = node.firstChild;
|
||||
node.__dom.$lastChild = node.lastChild;
|
||||
node.__dom.$childNodes = [];
|
||||
for (var n=node.firstChild; n; n=n.nextSibling) {
|
||||
n.__dom = n.__dom || {};
|
||||
n.__dom.$parentNode = node;
|
||||
node.__dom.$childNodes.push(n);
|
||||
n.__dom.$nextSibling = n.nextSibling;
|
||||
n.__dom.$previousSibling = n.previousSibling;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
recordInsertBefore: function(node, container, ref_node) {
|
||||
container.__dom.$childNodes = null;
|
||||
// handle document fragments
|
||||
if (node.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
|
||||
// TODO(sorvell): remember this for patching:
|
||||
// the act of setting this info can affect patched nodes
|
||||
// getters; therefore capture childNodes before patching.
|
||||
for (var n=this.getFirstChild(node); n; n=this.getNextSibling(n)) {
|
||||
this._linkNode(n, container, ref_node);
|
||||
}
|
||||
} else {
|
||||
this._linkNode(node, container, ref_node);
|
||||
}
|
||||
},
|
||||
|
||||
_linkNode: function(node, container, ref_node) {
|
||||
node.__dom = node.__dom || {};
|
||||
container.__dom = container.__dom || {};
|
||||
if (ref_node) {
|
||||
ref_node.__dom = ref_node.__dom || {};
|
||||
}
|
||||
// update ref_node.previousSibling <-> node
|
||||
node.__dom.$previousSibling = ref_node ? ref_node.__dom.$previousSibling :
|
||||
container.__dom.$lastChild;
|
||||
if (node.__dom.$previousSibling) {
|
||||
node.__dom.$previousSibling.__dom.$nextSibling = node;
|
||||
}
|
||||
// update node <-> ref_node
|
||||
node.__dom.$nextSibling = ref_node;
|
||||
if (node.__dom.$nextSibling) {
|
||||
node.__dom.$nextSibling.__dom.$previousSibling = node;
|
||||
}
|
||||
// update node <-> container
|
||||
node.__dom.$parentNode = container;
|
||||
if (ref_node) {
|
||||
if (ref_node === container.__dom.$firstChild) {
|
||||
container.__dom.$firstChild = node;
|
||||
}
|
||||
} else {
|
||||
container.__dom.$lastChild = node;
|
||||
if (!container.__dom.$firstChild) {
|
||||
container.__dom.$firstChild = node;
|
||||
}
|
||||
}
|
||||
// remove caching of childNodes
|
||||
container.__dom.$childNodes = null;
|
||||
},
|
||||
|
||||
recordRemoveChild: function(node, container) {
|
||||
node.__dom = node.__dom || {};
|
||||
container.__dom = container.__dom || {};
|
||||
if (node === container.__dom.$firstChild) {
|
||||
container.__dom.$firstChild = node.__dom.$nextSibling;
|
||||
}
|
||||
if (node === container.__dom.$lastChild) {
|
||||
container.__dom.$lastChild = node.__dom.$previousSibling;
|
||||
}
|
||||
var p = node.__dom.$previousSibling;
|
||||
var n = node.__dom.$nextSibling;
|
||||
if (p) {
|
||||
p.__dom.$nextSibling = n;
|
||||
}
|
||||
if (n) {
|
||||
n.__dom.$previousSibling = p;
|
||||
}
|
||||
node.__dom.$parentNode = node.__dom.$previousSibling =
|
||||
node.__dom.$nextSibling = null;
|
||||
// remove caching of childNodes
|
||||
container.__dom.$childNodes = null;
|
||||
},
|
||||
|
||||
// composed tracking needs to reset composed children here in case
|
||||
// they may have already been set (this shouldn't happen but can
|
||||
// if dependency ordering is incorrect and as a result upgrade order
|
||||
// is unexpected)
|
||||
clearChildNodes: function(node) {
|
||||
if (node.__dom && node.__dom.$parentNode) {
|
||||
node.__dom.$parentNode.__dom.$childNodes = [];
|
||||
}
|
||||
node.textContent = '';
|
||||
},
|
||||
|
||||
saveParentNode: function(node) {
|
||||
node.__dom = node.__dom || {};
|
||||
node.__dom.$parentNode = node.parentNode;
|
||||
},
|
||||
|
||||
insertBefore: function(parentNode, newChild, refChild) {
|
||||
this.saveChildNodes(parentNode);
|
||||
// remove from current location.
|
||||
if (this.hasParentNode(newChild)) {
|
||||
var oldParent = this.getParentNode(newChild);
|
||||
if (oldParent) {
|
||||
this._removeChild(oldParent, newChild);
|
||||
}
|
||||
}
|
||||
this._addChild(parentNode, newChild, refChild);
|
||||
return nativeInsertBefore.call(parentNode, newChild, refChild || null);
|
||||
},
|
||||
|
||||
appendChild: function(parentNode, newChild) {
|
||||
this.saveChildNodes(parentNode);
|
||||
// remove from current location.
|
||||
if (this.hasParentNode(newChild)) {
|
||||
var oldParent = this.getParentNode(newChild);
|
||||
if (oldParent) {
|
||||
this._removeChild(oldParent, newChild);
|
||||
}
|
||||
}
|
||||
this._addChild(parentNode, newChild);
|
||||
return nativeAppendChild.call(parentNode, newChild);
|
||||
},
|
||||
|
||||
removeChild: function(parentNode, node) {
|
||||
var currentParent = this.getParentNode(node);
|
||||
this.saveChildNodes(parentNode);
|
||||
this._removeChild(parentNode, node);
|
||||
if (currentParent === parentNode) {
|
||||
// TODO(sorvell); abort if the composedParent is not expected...
|
||||
if (!node.__patched && node.parentNode !== node.__dom.$parentNode) {
|
||||
return;
|
||||
}
|
||||
return nativeRemoveChild.call(parentNode, node);
|
||||
}
|
||||
},
|
||||
|
||||
_addChild: function(parentNode, newChild, refChild) {
|
||||
if (newChild.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
|
||||
var c$ = this.getChildNodes(newChild);
|
||||
for (var j=0; j < c$.length; j++) {
|
||||
this._addChild(parentNode, c$[j], refChild);
|
||||
}
|
||||
} else {
|
||||
newChild.__dom = newChild.__dom || {};
|
||||
newChild.__dom.$parentNode = parentNode;
|
||||
var c$ = this.getComposedChildNodes(parentNode);
|
||||
if (c$) {
|
||||
var i = c$.indexOf(refChild);
|
||||
i = i === -1 ? c$.length : i;
|
||||
c$.splice(i, 0, newChild);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_removeChild: function(parentNode, node) {
|
||||
node.__dom = node.__dom || {};
|
||||
node.__dom.$parentNode = null;
|
||||
var c$ = this.getComposedChildNodes(parentNode);
|
||||
if (c$) {
|
||||
var i = c$.indexOf(node);
|
||||
if (i >= 0) {
|
||||
c$.splice(i, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
// patch important nodes
|
||||
if (window.document) {
|
||||
Polymer.dom(document);
|
||||
if (document.body) {
|
||||
Polymer.dom(document.body);
|
||||
}
|
||||
}
|
||||
|
||||
})();
|
||||
|
||||
</script>
|
||||
|
@ -107,7 +107,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
var cssText = '';
|
||||
// if element is a template, get content from its .content
|
||||
var content = element.content || element;
|
||||
var e$ = Polymer.DomApi.arrayCopy(
|
||||
var e$ = Polymer.TreeApi.arrayCopy(
|
||||
content.querySelectorAll(this.MODULE_STYLES_SELECTOR));
|
||||
for (var i=0, e; i < e$.length; i++) {
|
||||
e = e$[i];
|
||||
|
@ -154,7 +154,7 @@ elements to the template itself as the binding scope.
|
||||
this._prepBindings();
|
||||
this._prepPropertyInfo();
|
||||
Polymer.Base._initFeatures.call(this);
|
||||
this._children = Polymer.DomApi.arrayCopyChildNodes(this.root);
|
||||
this._children = Polymer.TreeApi.arrayCopyChildNodes(this.root);
|
||||
}
|
||||
this._insertChildren();
|
||||
this.fire('dom-change');
|
||||
|
@ -69,6 +69,8 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
if (host && host._clients) {
|
||||
host._clients.push(this);
|
||||
}
|
||||
this._clients = null;
|
||||
this._clientsReadied = false;
|
||||
},
|
||||
|
||||
// establish this element as the current hosting element (allows
|
||||
@ -86,6 +88,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
},
|
||||
|
||||
_tryReady: function() {
|
||||
this._readied = false;
|
||||
if (this._canReady()) {
|
||||
this._ready();
|
||||
}
|
||||
|
@ -8,7 +8,10 @@ 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="../lib/array-splice.html">
|
||||
<link rel="import" href="../lib/dom-tree-api.html">
|
||||
<link rel="import" href="../lib/dom-api.html">
|
||||
<link rel="import" href="../lib/dom-api-shady.html">
|
||||
<link rel="import" href="../lib/dom-api-shadow.html">
|
||||
<link rel="import" href="../lib/dom-api-flush.html">
|
||||
<link rel="import" href="../lib/dom-api-event.html">
|
||||
<link rel="import" href="../lib/dom-api-classlist.html">
|
||||
@ -21,7 +24,8 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
Implements a pared down version of ShadowDOM's scoping, which is easy to
|
||||
polyfill across browsers.
|
||||
*/
|
||||
var hasDomApi = Polymer.DomApi.hasDomApi;
|
||||
var DomApi = Polymer.DomApi;
|
||||
var TreeApi = Polymer.TreeApi;
|
||||
|
||||
Polymer.Base._addFeature({
|
||||
|
||||
@ -30,11 +34,25 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
this._useContent = this._useContent || Boolean(this._template);
|
||||
},
|
||||
|
||||
_setupShady: function() {
|
||||
// object shaping...
|
||||
this.shadyRoot = null;
|
||||
if (!this.__domApi) {
|
||||
this.__domApi = null;
|
||||
}
|
||||
if (!this.__dom) {
|
||||
this.__dom = null;
|
||||
}
|
||||
if (!this._ownerShadyRoot) {
|
||||
this._ownerShadyRoot = undefined;
|
||||
}
|
||||
},
|
||||
|
||||
// called as part of content initialization, prior to template stamping
|
||||
_poolContent: function() {
|
||||
if (this._useContent) {
|
||||
// capture lightChildren to help reify dom scoping
|
||||
saveLightChildrenIfNeeded(this);
|
||||
TreeApi.Logical.saveChildNodes(this);
|
||||
}
|
||||
},
|
||||
|
||||
@ -47,7 +65,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
// removed from document by shadyDOM distribution
|
||||
// so we ensure this here
|
||||
if (!this.dataHost) {
|
||||
upgradeLightChildren(this._lightChildren);
|
||||
upgradeLogicalChildren(TreeApi.Logical.getChildNodes(this));
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -66,11 +84,11 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
// a. for shadyRoot
|
||||
// b. for insertion points (fallback)
|
||||
// c. for parents of insertion points
|
||||
saveLightChildrenIfNeeded(this.shadyRoot);
|
||||
TreeApi.Logical.saveChildNodes(this.shadyRoot);
|
||||
for (var i=0, c; i < i$.length; i++) {
|
||||
c = i$[i];
|
||||
saveLightChildrenIfNeeded(c);
|
||||
saveLightChildrenIfNeeded(c.parentNode);
|
||||
TreeApi.Logical.saveChildNodes(c);
|
||||
TreeApi.Logical.saveChildNodes(c.parentNode);
|
||||
}
|
||||
this.shadyRoot.host = this;
|
||||
},
|
||||
@ -101,19 +119,21 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
*/
|
||||
distributeContent: function(updateInsertionPoints) {
|
||||
if (this.shadyRoot) {
|
||||
var dom = Polymer.dom(this);
|
||||
if (updateInsertionPoints) {
|
||||
dom._updateInsertionPoints(this);
|
||||
}
|
||||
this.shadyRoot._invalidInsertionPoints =
|
||||
this.shadyRoot._invalidInsertionPoints || updateInsertionPoints;
|
||||
// Distribute the host that's the top of this element's distribution
|
||||
// tree. Distributing that host will *always* distibute this element.
|
||||
var host = getTopDistributingHost(this);
|
||||
dom._lazyDistribute(host);
|
||||
Polymer.dom(this)._lazyDistribute(host);
|
||||
}
|
||||
},
|
||||
|
||||
_distributeContent: function() {
|
||||
if (this._useContent && !this.shadyRoot._distributionClean) {
|
||||
if (this.shadyRoot._invalidInsertionPoints) {
|
||||
Polymer.dom(this)._updateInsertionPoints(this);
|
||||
this.shadyRoot._invalidInsertionPoints = false;
|
||||
}
|
||||
// logically distribute self
|
||||
this._beginDistribute();
|
||||
this._distributeDirtyRoots();
|
||||
@ -122,7 +142,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
},
|
||||
|
||||
_beginDistribute: function() {
|
||||
if (this._useContent && hasInsertionPoint(this.shadyRoot)) {
|
||||
if (this._useContent && DomApi.hasInsertionPoint(this.shadyRoot)) {
|
||||
// reset distributions
|
||||
this._resetDistribution();
|
||||
// compute which nodes should be distributed where
|
||||
@ -147,18 +167,14 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
// so that attachment that provokes additional distribution (e.g.
|
||||
// adding something to your parentNode) works
|
||||
this.shadyRoot._distributionClean = true;
|
||||
if (hasInsertionPoint(this.shadyRoot)) {
|
||||
if (DomApi.hasInsertionPoint(this.shadyRoot)) {
|
||||
this._composeTree();
|
||||
// NOTE: send a signal to insertion points that we have distributed
|
||||
// which informs effective children observers
|
||||
notifyContentObservers(this.shadyRoot);
|
||||
} else {
|
||||
if (!this.shadyRoot._hasDistributed) {
|
||||
this.textContent = '';
|
||||
// reset composed children here in case they may have already
|
||||
// been set (this shouldn't happen but can if dependency ordering
|
||||
// is incorrect and as a result upgrade order is unexpected)
|
||||
this._composedChildren = null;
|
||||
TreeApi.Composed.clearChildNodes(this);
|
||||
this.appendChild(this.shadyRoot);
|
||||
} else {
|
||||
// simplified non-tree walk composition
|
||||
@ -188,7 +204,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
// Alternatively we could just polyfill it somewhere.
|
||||
// Note that the arguments are reversed from what you might expect.
|
||||
node = node || this;
|
||||
return matchesSelector.call(node, selector);
|
||||
return DomApi.matchesSelector.call(node, selector);
|
||||
},
|
||||
|
||||
// Many of the following methods are all conceptually static, but they are
|
||||
@ -196,7 +212,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
|
||||
_resetDistribution: function() {
|
||||
// light children
|
||||
var children = getLightChildren(this);
|
||||
var children = TreeApi.Logical.getChildNodes(this);
|
||||
for (var i = 0; i < children.length; i++) {
|
||||
var child = children[i];
|
||||
if (child._destinationInsertionPoints) {
|
||||
@ -218,7 +234,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
// these with the "content root" to arrive at the composed tree.
|
||||
_collectPool: function() {
|
||||
var pool = [];
|
||||
var children = getLightChildren(this);
|
||||
var children = TreeApi.Logical.getChildNodes(this);
|
||||
for (var i = 0; i < children.length; i++) {
|
||||
var child = children[i];
|
||||
if (isInsertionPoint(child)) {
|
||||
@ -264,7 +280,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
}
|
||||
// Fallback content if nothing was distributed here
|
||||
if (!anyDistributed) {
|
||||
var children = getLightChildren(content);
|
||||
var children = TreeApi.Logical.getChildNodes(content);
|
||||
for (var j = 0; j < children.length; j++) {
|
||||
distributeNodeInto(children[j], content);
|
||||
}
|
||||
@ -277,7 +293,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
this._updateChildNodes(this, this._composeNode(this));
|
||||
var p$ = this.shadyRoot._insertionPoints;
|
||||
for (var i=0, l=p$.length, p, parent; (i<l) && (p=p$[i]); i++) {
|
||||
parent = p._lightParent || p.parentNode;
|
||||
parent = TreeApi.Logical.getParentNode(p);
|
||||
if (!parent._useContent && (parent !== this) &&
|
||||
(parent !== this.shadyRoot)) {
|
||||
this._updateChildNodes(parent, this._composeNode(parent));
|
||||
@ -288,7 +304,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
// Returns the list of nodes which should be rendered inside `node`.
|
||||
_composeNode: function(node) {
|
||||
var children = [];
|
||||
var c$ = getLightChildren(node.shadyRoot || node);
|
||||
var c$ = TreeApi.Logical.getChildNodes(node.shadyRoot || node);
|
||||
for (var i = 0; i < c$.length; i++) {
|
||||
var child = c$[i];
|
||||
if (isInsertionPoint(child)) {
|
||||
@ -308,7 +324,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
|
||||
// Ensures that the rendered node list inside `container` is `children`.
|
||||
_updateChildNodes: function(container, children) {
|
||||
var composed = getComposedChildren(container);
|
||||
var composed = TreeApi.Composed.getChildNodes(container);
|
||||
var splices =
|
||||
Polymer.ArraySplice.calculateSplices(children, composed);
|
||||
// process removals
|
||||
@ -318,8 +334,8 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
// to remove it; this can happen if Polymer.dom moves a node and
|
||||
// then schedules its previous host for distribution resulting in
|
||||
// the node being removed here.
|
||||
if (getComposedParent(n) === container) {
|
||||
remove(n);
|
||||
if (TreeApi.Composed.getParentNode(n) === container) {
|
||||
TreeApi.Composed.removeChild(container, n);
|
||||
}
|
||||
composed.splice(s.index + d, 1);
|
||||
}
|
||||
@ -330,12 +346,11 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
next = composed[s.index];
|
||||
for (var j=s.index, n; j < s.index + s.addedCount; j++) {
|
||||
n = children[j];
|
||||
insertBefore(container, n, next);
|
||||
TreeApi.Composed.insertBefore(container, n, next);
|
||||
// TODO(sorvell): is this splice strictly needed?
|
||||
composed.splice(j, 0, n);
|
||||
}
|
||||
}
|
||||
// ensure composed parent is set
|
||||
ensureComposedParent(container, children);
|
||||
},
|
||||
|
||||
_matchesContentSelect: function(node, contentElement) {
|
||||
@ -375,14 +390,6 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
|
||||
});
|
||||
|
||||
var saveLightChildrenIfNeeded = Polymer.DomApi.saveLightChildrenIfNeeded;
|
||||
var getLightChildren = Polymer.DomApi.getLightChildren;
|
||||
var matchesSelector = Polymer.DomApi.matchesSelector;
|
||||
var hasInsertionPoint = Polymer.DomApi.hasInsertionPoint;
|
||||
var getComposedChildren = Polymer.DomApi.getComposedChildren;
|
||||
var getComposedParent = Polymer.DomApi.getComposedParent;
|
||||
var removeFromComposedParent = Polymer.DomApi.removeFromComposedParent;
|
||||
|
||||
function distributeNodeInto(child, insertionPoint) {
|
||||
insertionPoint._distributedNodes.push(child);
|
||||
var points = child._destinationInsertionPoints;
|
||||
@ -408,9 +415,10 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
|
||||
// dirty a shadyRoot if a change may trigger reprojection!
|
||||
function maybeRedistributeParent(content, host) {
|
||||
var parent = content._lightParent;
|
||||
// only get logical parent.
|
||||
var parent = TreeApi.Logical.getParentNode(content);
|
||||
if (parent && parent.shadyRoot &&
|
||||
hasInsertionPoint(parent.shadyRoot) &&
|
||||
DomApi.hasInsertionPoint(parent.shadyRoot) &&
|
||||
parent.shadyRoot._distributionClean) {
|
||||
parent.shadyRoot._distributionClean = false;
|
||||
host.shadyRoot._dirtyRoots.push(parent);
|
||||
@ -427,36 +435,6 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
return node.localName == 'content';
|
||||
}
|
||||
|
||||
var nativeInsertBefore = Element.prototype.insertBefore;
|
||||
var nativeRemoveChild = Element.prototype.removeChild;
|
||||
|
||||
function insertBefore(parentNode, newChild, refChild) {
|
||||
var newChildParent = getComposedParent(newChild);
|
||||
if (newChildParent !== parentNode) {
|
||||
removeFromComposedParent(newChildParent, newChild);
|
||||
}
|
||||
// remove child from its old parent first
|
||||
remove(newChild);
|
||||
// insert it into the real DOM
|
||||
nativeInsertBefore.call(parentNode, newChild, refChild || null);
|
||||
newChild._composedParent = parentNode;
|
||||
}
|
||||
|
||||
function remove(node) {
|
||||
var parentNode = getComposedParent(node);
|
||||
if (parentNode) {
|
||||
node._composedParent = null;
|
||||
// remove it from the real DOM
|
||||
nativeRemoveChild.call(parentNode, node);
|
||||
}
|
||||
}
|
||||
|
||||
function ensureComposedParent(parent, children) {
|
||||
for (var i=0; i < children.length; i++) {
|
||||
children[i]._composedParent = parent;
|
||||
}
|
||||
}
|
||||
|
||||
// returns the host that's the top of this host's distribution tree
|
||||
function getTopDistributingHost(host) {
|
||||
while (host && hostNeedsRedistribution(host)) {
|
||||
@ -468,10 +446,10 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
// Return true if a host's children includes
|
||||
// an insertion point that selects selectively
|
||||
function hostNeedsRedistribution(host) {
|
||||
var c$ = Polymer.dom(host).children;
|
||||
var c$ = TreeApi.Logical.getChildNodes(host);
|
||||
for (var i=0, c; i < c$.length; i++) {
|
||||
c = c$[i];
|
||||
if (c.localName === 'content') {
|
||||
if (c.localName && c.localName === 'content') {
|
||||
return host.domHost;
|
||||
}
|
||||
}
|
||||
@ -480,21 +458,21 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
function notifyContentObservers(root) {
|
||||
for (var i=0, c; i < root._insertionPoints.length; i++) {
|
||||
c = root._insertionPoints[i];
|
||||
if (hasDomApi(c)) {
|
||||
if (DomApi.hasApi(c)) {
|
||||
Polymer.dom(c).notifyObserver();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function notifyInitialDistribution(host) {
|
||||
if (hasDomApi(host)) {
|
||||
if (DomApi.hasApi(host)) {
|
||||
Polymer.dom(host).notifyObserver();
|
||||
}
|
||||
}
|
||||
|
||||
var needsUpgrade = window.CustomElements && !CustomElements.useNative;
|
||||
|
||||
function upgradeLightChildren(children) {
|
||||
function upgradeLogicalChildren(children) {
|
||||
if (needsUpgrade && children) {
|
||||
for (var i=0; i < children.length; i++) {
|
||||
CustomElements.upgrade(children[i]);
|
||||
|
@ -47,6 +47,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
_setupConfigure: function(initialConfig) {
|
||||
this._config = {};
|
||||
this._handlers = [];
|
||||
this._aboveConfig = null;
|
||||
if (initialConfig) {
|
||||
// don't accept undefined values in intialConfig
|
||||
for (var i in initialConfig) {
|
||||
|
@ -676,6 +676,11 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
};
|
||||
|
||||
Polymer.Base._addFeature({
|
||||
|
||||
_setupGestures: function() {
|
||||
this.__polymerGestures = null;
|
||||
},
|
||||
|
||||
// override _listen to handle gestures
|
||||
_listen: function(node, eventName, handler) {
|
||||
if (Gestures.gestures[eventName]) {
|
||||
|
@ -55,6 +55,11 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
// here we have an instance time spot to put custom property data
|
||||
_setupStyleProperties: function() {
|
||||
this.customStyle = {};
|
||||
this._styleCache = null;
|
||||
this._styleProperties = null;
|
||||
this._scopeSelector = null;
|
||||
this._ownStyleProperties = null;
|
||||
this._customStyle = null;
|
||||
},
|
||||
|
||||
_needsStyleProperties: function() {
|
||||
|
42
test/smoke/nextSibling.html
Normal file
42
test/smoke/nextSibling.html
Normal file
@ -0,0 +1,42 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
|
||||
<title>annotations</title>
|
||||
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
|
||||
<script src="../../../webcomponentsjs/webcomponents-lite.js"></script>
|
||||
<script>//Polymer = {dom: 'shadow'}</script>
|
||||
<link rel="import" href="../../polymer.html">
|
||||
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
<script>
|
||||
var ul = document.createElement("ul");
|
||||
for (i=0; i < 1000; i++) {
|
||||
var li = document.createElement("li");
|
||||
ul.appendChild(li);
|
||||
}
|
||||
|
||||
var start = window.performance.now();
|
||||
var e = ul.firstElementChild;
|
||||
for (i=0; i <999; i++)
|
||||
e = e.nextSibling;
|
||||
|
||||
console.log(e);
|
||||
console.log("Native performance: "+(window.performance.now()-start)+"ms");
|
||||
|
||||
var start = window.performance.now();
|
||||
var e = ul.firstElementChild;
|
||||
for (i=0; i <999; i++)
|
||||
e = Polymer.dom(e).nextSibling;
|
||||
|
||||
console.log(e);
|
||||
console.log("Polymer performance: "+(window.performance.now()-start)+"ms");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@ -247,25 +247,23 @@ suite('Polymer.dom (patch)', function() {
|
||||
s.id = 'light';
|
||||
s.textContent = 'Light';
|
||||
rere.appendChild(s);
|
||||
// TODO(sorvell); patch
|
||||
Polymer.dom(s);
|
||||
assert.equal(rere.querySelector('#light'), s);
|
||||
assert.equal(s.parentNode, rere);
|
||||
Polymer.dom.flush();
|
||||
if (rere.shadyRoot) {
|
||||
assert.notEqual(s._composedParent, rere);
|
||||
assert.notEqual(s.__dom.$parentNode, rere);
|
||||
}
|
||||
Polymer.dom.flush();
|
||||
if (rere.shadyRoot) {
|
||||
assert.equal(s._composedParent, p);
|
||||
assert.equal(s.__dom.$parentNode, p);
|
||||
}
|
||||
rere.removeChild(s);
|
||||
if (rere.shadyRoot) {
|
||||
assert.equal(s._composedParent, p);
|
||||
assert.equal(s.__dom.$parentNode, p);
|
||||
}
|
||||
Polymer.dom.flush();
|
||||
if (rere.shadyRoot) {
|
||||
assert.equal(s._composedParent, null);
|
||||
assert.equal(s.__dom.$parentNode, null);
|
||||
}
|
||||
});
|
||||
|
||||
@ -298,35 +296,6 @@ suite('Polymer.dom (patch)', function() {
|
||||
assert.notOk(projected);
|
||||
});
|
||||
|
||||
test('Polymer.dom event', function() {
|
||||
var test = document.querySelector('x-test');
|
||||
var rere = test.root.querySelector('x-rereproject');
|
||||
var re = rere.root.querySelector('x-reproject');
|
||||
var p = re.root.querySelector('x-project');
|
||||
var eventHandled = 0;
|
||||
test.addEventListener('test-event', function(e) {
|
||||
eventHandled++;
|
||||
assert.equal(Polymer.dom(e).rootTarget, p);
|
||||
assert.equal(Polymer.dom(e).localTarget, test);
|
||||
var path = Polymer.dom(e).path;
|
||||
// path includes window only on more recent Shadow DOM implementations
|
||||
// account for that here.
|
||||
assert.ok(path.length >= 10);
|
||||
assert.equal(path[0], p);
|
||||
assert.equal(path[2], re);
|
||||
assert.equal(path[4], rere);
|
||||
assert.equal(path[6], test);
|
||||
});
|
||||
|
||||
rere.addEventListener('test-event', function(e) {
|
||||
eventHandled++;
|
||||
assert.equal(Polymer.dom(e).localTarget, rere);
|
||||
});
|
||||
|
||||
p.fire('test-event');
|
||||
assert.equal(eventHandled, 2);
|
||||
});
|
||||
|
||||
test('Polymer.dom.childNodes is an array', function() {
|
||||
assert.isTrue(Array.isArray(Polymer.dom(document.body).childNodes));
|
||||
});
|
||||
@ -374,13 +343,13 @@ suite('Polymer.dom non-distributed elements', function() {
|
||||
function testNoAttr() {
|
||||
assert.equal(Polymer.dom(child).getDestinationInsertionPoints()[0], d.$.notTestContent, 'child not distributed logically');
|
||||
if (shady) {
|
||||
assert.equal(child._composedParent, d.$.notTestContainer, 'child not rendered in composed dom');
|
||||
assert.equal(child.__dom.$parentNode, d.$.notTestContainer, 'child not rendered in composed dom');
|
||||
}
|
||||
}
|
||||
function testWithAttr() {
|
||||
assert.equal(Polymer.dom(child).getDestinationInsertionPoints()[0], d.$.testContent, 'child not distributed logically');
|
||||
if (shady) {
|
||||
assert.equal(child._composedParent, d.$.testContainer, 'child not rendered in composed dom');
|
||||
assert.equal(child.__dom.$parentNode, d.$.testContainer, 'child not rendered in composed dom');
|
||||
}
|
||||
}
|
||||
// test with x-distribute
|
||||
@ -395,17 +364,12 @@ suite('Polymer.dom non-distributed elements', function() {
|
||||
testNoAttr();
|
||||
// set / unset `test` attr and see if it distributes properly
|
||||
child.setAttribute('test', '');
|
||||
d.distributeContent();
|
||||
Polymer.dom.flush();
|
||||
testWithAttr();
|
||||
//
|
||||
child.removeAttribute('test');
|
||||
d.distributeContent();
|
||||
Polymer.dom.flush();
|
||||
testNoAttr();
|
||||
//
|
||||
child.setAttribute('test', '');
|
||||
d.distributeContent();
|
||||
Polymer.dom.flush();
|
||||
testWithAttr();
|
||||
});
|
||||
|
79
test/smoke/patch/smoke.html
Normal file
79
test/smoke/patch/smoke.html
Normal file
@ -0,0 +1,79 @@
|
||||
<!doctype html>
|
||||
<!--
|
||||
@license
|
||||
Copyright (c) 2014 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
|
||||
-->
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<script src="../../../../webcomponentsjs/webcomponents-lite.js"></script>
|
||||
<script src="../../../../web-component-tester/browser.js"></script>
|
||||
<link rel="import" href="../../../polymer.html">
|
||||
<link rel="import" href="../../../src/lib/experimental/patch-dom.html">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<x-test>
|
||||
<header>header 1</header>
|
||||
<footer>footer 1</footer>
|
||||
</x-test>
|
||||
|
||||
<dom-module id="x-test">
|
||||
<template>
|
||||
<style>
|
||||
:host {
|
||||
display: block;
|
||||
border: 2px solid black;
|
||||
padding: 8px;
|
||||
margin: 8px;
|
||||
}
|
||||
|
||||
.header {
|
||||
background: steelblue;
|
||||
}
|
||||
|
||||
.content {
|
||||
margin: 10px;
|
||||
background: beige;
|
||||
}
|
||||
|
||||
.footer {
|
||||
background: tomato;
|
||||
}
|
||||
</style>
|
||||
<div>User headers...</div>
|
||||
<div class="header"><content select="header"></content></div>
|
||||
<div class="content">Element content...</div>
|
||||
<div>User footers...</div>
|
||||
<div class="footer"><content select="footer"></content></div>
|
||||
</template>
|
||||
<script>
|
||||
HTMLImports.whenReady(function() {
|
||||
Polymer({is: 'x-test'});
|
||||
});
|
||||
</script>
|
||||
</dom-module>
|
||||
|
||||
<script>
|
||||
addEventListener('WebComponentsReady', function() {
|
||||
var test = document.querySelector('x-test');
|
||||
console.log('should not find anything:',
|
||||
document.querySelector('.content'),
|
||||
test.querySelector('.content'));
|
||||
var f = document.createElement('footer');
|
||||
f.textContent = 'dynamic footer';
|
||||
test.insertBefore(f, test.lastElementChild);
|
||||
var h = document.createElement('header');
|
||||
h.textContent = 'dynamic header';
|
||||
test.appendChild(h);
|
||||
|
||||
})
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
@ -948,7 +948,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
assert.equal(host.$.distWrapper.children[1], s2);
|
||||
assert.equal(host.$.distWrapper.children[2], s3);
|
||||
assert.equal(host.$.distWrapper.children[3], s0);
|
||||
var composedChildren = host.$.distWrapper._composedChildren;
|
||||
var composedChildren = Polymer.TreeApi.Composed.getChildNodes(host.$.distWrapper);
|
||||
assert.equal(composedChildren.length, 4);
|
||||
assert.equal(composedChildren[0], s1);
|
||||
assert.equal(composedChildren[1], s2);
|
||||
@ -961,7 +961,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
Polymer.dom.flush();
|
||||
if (host.shadyRoot) {
|
||||
assert.equal(host.$.distWrapper.children.length, 1);
|
||||
var composedChildren = host.$.distWrapper._composedChildren;
|
||||
var composedChildren = Polymer.TreeApi.Composed.getChildNodes(host.$.distWrapper);
|
||||
assert.equal(composedChildren.length, 1);
|
||||
assert.equal(composedChildren[0], s0);
|
||||
}
|
||||
@ -987,7 +987,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
assert.equal(host.$.distWrapper.children[1], s2);
|
||||
assert.equal(host.$.distWrapper.children[2], s3);
|
||||
assert.equal(host.$.distWrapper.children[3], s0);
|
||||
var composedChildren = host.$.distWrapper._composedChildren;
|
||||
var composedChildren = Polymer.TreeApi.Composed.getChildNodes(host.$.distWrapper);
|
||||
assert.equal(composedChildren.length, 4);
|
||||
assert.equal(composedChildren[0], s1);
|
||||
assert.equal(composedChildren[1], s2);
|
||||
@ -1000,7 +1000,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
Polymer.dom.flush();
|
||||
if (host.shadyRoot) {
|
||||
assert.equal(host.$.distWrapper.children.length, 1);
|
||||
var composedChildren = host.$.distWrapper._composedChildren;
|
||||
var composedChildren = Polymer.TreeApi.Composed.getChildNodes(host.$.distWrapper);
|
||||
assert.equal(composedChildren.length, 1);
|
||||
assert.equal(composedChildren[0], s0);
|
||||
}
|
||||
@ -1100,7 +1100,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
Polymer.dom(h2).appendChild(d);
|
||||
Polymer.dom.flush();
|
||||
assert.equal(Polymer.dom(h2).childNodes.length, 1);
|
||||
assert.equal(Polymer.dom(h2).firstElementChild, d);
|
||||
assert.equal(Polymer.dom(h2).firstChild, d);
|
||||
assert.equal(Polymer.dom(h1).childNodes.length, 0);
|
||||
assert.deepEqual(Polymer.dom(h1.$.content).getDistributedNodes().length, 0);
|
||||
Polymer.dom(h1).appendChild(d);
|
||||
@ -1112,7 +1112,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
Polymer.dom(h2).appendChild(d);
|
||||
Polymer.dom.flush();
|
||||
assert.equal(Polymer.dom(h2).childNodes.length, 1);
|
||||
assert.equal(Polymer.dom(h2).firstElementChild, d);
|
||||
assert.equal(Polymer.dom(h2).firstChild, d);
|
||||
assert.equal(Polymer.dom(h1).childNodes.length, 0);
|
||||
assert.deepEqual(Polymer.dom(h1.$.content).getDistributedNodes().length, 0);
|
||||
document.body.removeChild(h1);
|
||||
@ -1134,7 +1134,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
Polymer.dom(h2).appendChild(d);
|
||||
Polymer.dom.flush();
|
||||
assert.equal(Polymer.dom(h2).childNodes.length, 1);
|
||||
assert.equal(Polymer.dom(h2).firstElementChild, d);
|
||||
assert.equal(Polymer.dom(h2).firstChild, d);
|
||||
assert.equal(Polymer.dom(h1).childNodes.length, 0);
|
||||
assert.deepEqual(Polymer.dom(h1.$.content).getDistributedNodes().length, 0);
|
||||
Polymer.dom(h1).appendChild(d);
|
||||
@ -1146,7 +1146,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
Polymer.dom(h2).appendChild(d);
|
||||
Polymer.dom.flush();
|
||||
assert.equal(Polymer.dom(h2).childNodes.length, 1);
|
||||
assert.equal(Polymer.dom(h2).firstElementChild, d);
|
||||
assert.equal(Polymer.dom(h2).firstChild, d);
|
||||
assert.equal(Polymer.dom(h1).childNodes.length, 0);
|
||||
assert.deepEqual(Polymer.dom(h1.$.content).getDistributedNodes().length, 0);
|
||||
document.body.removeChild(h1);
|
||||
@ -1246,7 +1246,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
Polymer.dom(h2).appendChild(d);
|
||||
Polymer.dom.flush();
|
||||
assert.equal(Polymer.dom(h2).childNodes.length, 1);
|
||||
assert.equal(Polymer.dom(h2).firstElementChild, d);
|
||||
assert.equal(Polymer.dom(h2).firstChild, d);
|
||||
assert.equal(Polymer.dom(h1).childNodes.length, 0);
|
||||
assert.deepEqual(Polymer.dom(h1.$.content).getDistributedNodes().length, 0);
|
||||
Polymer.dom(h1).appendChild(d);
|
||||
@ -1258,7 +1258,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
Polymer.dom(h2).appendChild(d);
|
||||
Polymer.dom.flush();
|
||||
assert.equal(Polymer.dom(h2).childNodes.length, 1);
|
||||
assert.equal(Polymer.dom(h2).firstElementChild, d);
|
||||
assert.equal(Polymer.dom(h2).firstChild, d);
|
||||
assert.equal(Polymer.dom(h1).childNodes.length, 0);
|
||||
assert.deepEqual(Polymer.dom(h1.$.content).getDistributedNodes().length, 0);
|
||||
document.body.removeChild(h1);
|
||||
@ -1280,7 +1280,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
Polymer.dom(h2).appendChild(d);
|
||||
Polymer.dom.flush();
|
||||
assert.equal(Polymer.dom(h2).childNodes.length, 1);
|
||||
assert.equal(Polymer.dom(h2).firstElementChild, d);
|
||||
assert.equal(Polymer.dom(h2).firstChild, d);
|
||||
assert.equal(Polymer.dom(h1).childNodes.length, 0);
|
||||
assert.deepEqual(Polymer.dom(h1.$.content).getDistributedNodes().length, 0);
|
||||
Polymer.dom(h1).appendChild(d);
|
||||
@ -1292,7 +1292,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
Polymer.dom(h2).appendChild(d);
|
||||
Polymer.dom.flush();
|
||||
assert.equal(Polymer.dom(h2).childNodes.length, 1);
|
||||
assert.equal(Polymer.dom(h2).firstElementChild, d);
|
||||
assert.equal(Polymer.dom(h2).firstChild, d);
|
||||
assert.equal(Polymer.dom(h1).childNodes.length, 0);
|
||||
assert.deepEqual(Polymer.dom(h1.$.content).getDistributedNodes().length, 0);
|
||||
document.body.removeChild(h1);
|
||||
@ -1313,7 +1313,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
Polymer.dom(h2).appendChild(d);
|
||||
Polymer.dom.flush();
|
||||
assert.equal(Polymer.dom(h2).childNodes.length, 1);
|
||||
assert.equal(Polymer.dom(h2).firstElementChild, d);
|
||||
assert.equal(Polymer.dom(h2).firstChild, d);
|
||||
assert.equal(Polymer.dom(h1).childNodes.length, 0);
|
||||
assert.deepEqual(Polymer.dom(h1.$.content).getDistributedNodes().length, 0);
|
||||
Polymer.dom(h1).appendChild(d);
|
||||
@ -1325,7 +1325,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
Polymer.dom(h2).appendChild(d);
|
||||
Polymer.dom.flush();
|
||||
assert.equal(Polymer.dom(h2).childNodes.length, 1);
|
||||
assert.equal(Polymer.dom(h2).firstElementChild, d);
|
||||
assert.equal(Polymer.dom(h2).firstChild, d);
|
||||
assert.equal(Polymer.dom(h1).childNodes.length, 0);
|
||||
assert.deepEqual(Polymer.dom(h1.$.content).getDistributedNodes().length, 0);
|
||||
document.body.removeChild(h1);
|
||||
@ -1335,7 +1335,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
var div = document.createElement('div');
|
||||
div.innerHTML = '<x-dist-simple><div></div></x-dist-simple>';
|
||||
var h1 = div.firstChild;
|
||||
var h2 = document.createDocumentFragment();;
|
||||
var h2 = document.createDocumentFragment();
|
||||
document.body.appendChild(h1);
|
||||
Polymer.dom.flush();
|
||||
var d = Polymer.dom(h1).firstElementChild;
|
||||
@ -1347,7 +1347,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
Polymer.dom(h2).appendChild(d);
|
||||
Polymer.dom.flush();
|
||||
assert.equal(Polymer.dom(h2).childNodes.length, 1);
|
||||
assert.equal(Polymer.dom(h2).firstElementChild, d);
|
||||
assert.equal(Polymer.dom(h2).firstChild, d);
|
||||
assert.equal(Polymer.dom(h1).childNodes.length, 0);
|
||||
assert.deepEqual(Polymer.dom(h1.$.content).getDistributedNodes().length, 0);
|
||||
Polymer.dom(h1).appendChild(d);
|
||||
@ -1359,7 +1359,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
Polymer.dom(h2).appendChild(d);
|
||||
Polymer.dom.flush();
|
||||
assert.equal(Polymer.dom(h2).childNodes.length, 1);
|
||||
assert.equal(Polymer.dom(h2).firstElementChild, d);
|
||||
assert.equal(Polymer.dom(h2).firstChild, d);
|
||||
assert.equal(Polymer.dom(h1).childNodes.length, 0);
|
||||
assert.deepEqual(Polymer.dom(h1.$.content).getDistributedNodes().length, 0);
|
||||
document.body.removeChild(h1);
|
||||
|
@ -489,19 +489,19 @@ suite('Polymer.dom', function() {
|
||||
assert.equal(Polymer.dom(rere).querySelector('#light'), s);
|
||||
assert.equal(Polymer.dom(s).parentNode, rere);
|
||||
if (rere.shadyRoot) {
|
||||
assert.notEqual(s._composedParent, rere);
|
||||
assert.notEqual(Polymer.TreeApi.Composed.getParentNode(s), rere);
|
||||
}
|
||||
Polymer.dom(testElement).flush();
|
||||
if (rere.shadyRoot) {
|
||||
assert.equal(s._composedParent, p);
|
||||
assert.equal(Polymer.TreeApi.Composed.getParentNode(s), p);
|
||||
}
|
||||
Polymer.dom(rere).removeChild(s);
|
||||
if (rere.shadyRoot) {
|
||||
assert.equal(s._composedParent, p);
|
||||
assert.equal(Polymer.TreeApi.Composed.getParentNode(s), p);
|
||||
}
|
||||
Polymer.dom(testElement).flush();
|
||||
if (rere.shadyRoot) {
|
||||
assert.equal(s._composedParent, null);
|
||||
assert.equal(Polymer.TreeApi.Composed.getParentNode(s), null);
|
||||
}
|
||||
});
|
||||
|
||||
@ -760,7 +760,7 @@ suite('Polymer.dom accessors', function() {
|
||||
assert.equal(Polymer.dom(testElement).textContent, 'Hello World', 'textContent getter incorrect');
|
||||
if (testElement.shadyRoot) {
|
||||
Polymer.dom.flush();
|
||||
assert.equal(testElement._composedChildren[1].textContent, 'Hello World', 'text content setter incorrect');
|
||||
assert.equal(Polymer.TreeApi.Composed.getChildNodes(testElement)[1].textContent, 'Hello World', 'text content setter incorrect');
|
||||
}
|
||||
testElement = document.createElement('x-commented');
|
||||
assert.equal(Polymer.dom(testElement.root).textContent, '[]', 'text content getter with comment incorrect');
|
||||
@ -784,9 +784,10 @@ suite('Polymer.dom accessors', function() {
|
||||
assert.equal(Polymer.dom(testElement).innerHTML , '<div>Hello World</div><div>2</div><div>3</div>', 'innerHTML getter incorrect');
|
||||
if (testElement.shadyRoot) {
|
||||
Polymer.dom.flush();
|
||||
assert.equal(testElement._composedChildren[1], added, 'innerHTML setter composed incorrectly');
|
||||
assert.equal(testElement._composedChildren[2].textContent, '2', 'innerHTML setter composed incorrectly');
|
||||
assert.equal(testElement._composedChildren[3].textContent, '3', 'innerHTML setter composed incorrectly');
|
||||
var children = Polymer.TreeApi.Composed.getChildNodes(testElement);
|
||||
assert.equal(children[1], added, 'innerHTML setter composed incorrectly');
|
||||
assert.equal(children[2].textContent, '2', 'innerHTML setter composed incorrectly');
|
||||
assert.equal(children[3].textContent, '3', 'innerHTML setter composed incorrectly');
|
||||
}
|
||||
});
|
||||
|
||||
@ -851,13 +852,13 @@ suite('Polymer.dom non-distributed elements', function() {
|
||||
function testNoAttr() {
|
||||
assert.equal(Polymer.dom(child).getDestinationInsertionPoints()[0], d.$.notTestContent, 'child not distributed logically');
|
||||
if (shady) {
|
||||
assert.equal(child._composedParent, d.$.notTestContainer, 'child not rendered in composed dom');
|
||||
assert.equal(Polymer.TreeApi.Composed.getParentNode(child), d.$.notTestContainer, 'child not rendered in composed dom');
|
||||
}
|
||||
}
|
||||
function testWithAttr() {
|
||||
assert.equal(Polymer.dom(child).getDestinationInsertionPoints()[0], d.$.testContent, 'child not distributed logically');
|
||||
if (shady) {
|
||||
assert.equal(child._composedParent, d.$.testContainer, 'child not rendered in composed dom');
|
||||
assert.equal(Polymer.TreeApi.Composed.getParentNode(child), d.$.testContainer, 'child not rendered in composed dom');
|
||||
}
|
||||
}
|
||||
// test with x-distribute
|
||||
|
@ -135,14 +135,14 @@ test('Reproject', function() {
|
||||
assert.strictEqual(getComposedHTML(host),
|
||||
'<x-content-test id="p">a: <a></a>b: <b></b></x-content-test>');
|
||||
|
||||
assertArrayEqual(host._lightChildren, [a]);
|
||||
assert.strictEqual(a._lightParent, host);
|
||||
assertArrayEqual(host.shadyRoot._lightChildren, [p]);
|
||||
assert.strictEqual(p._lightParent, host.shadyRoot);
|
||||
assertArrayEqual(p._lightChildren, [b, content]);
|
||||
assert.strictEqual(b._lightParent, p);
|
||||
assert.strictEqual(content._lightParent, p);
|
||||
assertArrayEqual(p.shadyRoot._lightChildren,
|
||||
assertArrayEqual(host.__dom.childNodes, [a]);
|
||||
assert.strictEqual(a.__dom.parentNode, host);
|
||||
assertArrayEqual(host.shadyRoot.__dom.childNodes, [p]);
|
||||
assert.strictEqual(p.__dom.parentNode, host.shadyRoot);
|
||||
assertArrayEqual(p.__dom.childNodes, [b, content]);
|
||||
assert.strictEqual(b.__dom.parentNode, p);
|
||||
assert.strictEqual(content.__dom.parentNode, p);
|
||||
assertArrayEqual(p.shadyRoot.__dom.childNodes,
|
||||
[textNodeA, contentA, textNodeB, contentB]);
|
||||
}
|
||||
|
||||
@ -165,7 +165,7 @@ suite('Mutate light DOM', function() {
|
||||
distributeContentNow(host);
|
||||
assert.strictEqual(getComposedHTML(host), '<a></a>');
|
||||
|
||||
host._lightChildren = [];
|
||||
host.__dom.childNodes = [];
|
||||
distributeContentNow(host);
|
||||
assert.strictEqual(getComposedHTML(host), 'fallback');
|
||||
});
|
||||
@ -180,11 +180,11 @@ suite('Mutate light DOM', function() {
|
||||
distributeContentNow(host);
|
||||
assert.strictEqual(getComposedHTML(host), '<a>Hello</a><b>after</b>');
|
||||
|
||||
host.shadyRoot._lightChildren[1].textContent = '';
|
||||
host.shadyRoot.__dom.childNodes[1].textContent = '';
|
||||
distributeContentNow(host);
|
||||
assert.strictEqual(getComposedHTML(host), '<a>Hello</a><b></b>');
|
||||
|
||||
host.shadyRoot._lightChildren = [];
|
||||
host.shadyRoot.__dom.childNodes = [];
|
||||
distributeContentNow(host);
|
||||
assert.strictEqual(getComposedHTML(host), '');
|
||||
});
|
||||
@ -203,11 +203,11 @@ suite('Mutate light DOM', function() {
|
||||
distributeContentNow(host);
|
||||
assert.strictEqual(getComposedHTML(host), '<b></b>');
|
||||
|
||||
host.shadyRoot.firstChild._lightChildren = [];
|
||||
host.shadyRoot.firstChild.__dom.childNodes = [];
|
||||
distributeContentNow(host);
|
||||
assert.strictEqual(getComposedHTML(host), '');
|
||||
|
||||
host.shadyRoot._lightChildren = [];
|
||||
host.shadyRoot.__dom.childNodes = [];
|
||||
distributeContentNow(host);
|
||||
assert.strictEqual(getComposedHTML(host), '');
|
||||
});
|
||||
@ -225,7 +225,7 @@ suite('Mutate light DOM', function() {
|
||||
distributeContentNow(host);
|
||||
assert.strictEqual(getComposedHTML(host), '<a></a>');
|
||||
|
||||
host._lightChildren = [];
|
||||
host.__dom.childNodes = [];
|
||||
distributeContentNow(host);
|
||||
assert.strictEqual(getComposedHTML(host), 'fallback');
|
||||
});
|
||||
@ -265,11 +265,11 @@ suite('Mutate light DOM', function() {
|
||||
distributeContentNow(host);
|
||||
assert.strictEqual(getComposedHTML(host), '<a>Hello</a><b></b>');
|
||||
|
||||
host.shadyRoot._lightChildren.splice(1, 1); // remove b
|
||||
host.shadyRoot.__dom.childNodes.splice(1, 1); // remove b
|
||||
distributeContentNow(host);
|
||||
assert.strictEqual(getComposedHTML(host), '<a>Hello</a>');
|
||||
|
||||
host.shadyRoot._lightChildren = []; // remove a
|
||||
host.shadyRoot.__dom.childNodes = []; // remove a
|
||||
distributeContentNow(host);
|
||||
assert.strictEqual(getComposedHTML(host), '');
|
||||
});
|
||||
@ -368,7 +368,7 @@ suite('Mutate light DOM', function() {
|
||||
assert.strictEqual(getComposedHTML(host), '<a>Hello</a>');
|
||||
|
||||
var b = document.createElement('b');
|
||||
host._lightChildren[0] = b;
|
||||
host.__dom.childNodes[0] = b;
|
||||
distributeContentNow(host);
|
||||
assert.strictEqual(getComposedHTML(host), '<b></b>');
|
||||
});
|
||||
@ -437,12 +437,17 @@ suite('Mutate light DOM', function() {
|
||||
});
|
||||
|
||||
function syncLightDOM(n) {
|
||||
if (n._lightChildren) {
|
||||
var c$ = n.__patched ? n._composedChildren || [] : Array.prototype.slice.call(n.childNodes);
|
||||
c$.forEach(function(c) {
|
||||
if (n._lightChildren.indexOf(c) < 0) {
|
||||
c._lightParent = n;
|
||||
n._lightChildren.push(c);
|
||||
if (n.__dom.childNodes !== undefined) {
|
||||
var c$ = Array.prototype.slice.call(n.childNodes);
|
||||
n.__dom.firstChild = c$[0];
|
||||
n.__dom.lastChild = c$[c$.length-1];
|
||||
c$.forEach(function(c, i) {
|
||||
if (n.__dom.childNodes.indexOf(c) < 0) {
|
||||
c.__dom = c.__dom || {};
|
||||
c.__dom.parentNode = n;
|
||||
c.__dom.previousSibling = c$[i-1];
|
||||
c.__dom.nextSibling = c$[i+1];
|
||||
n.__dom.childNodes.push(c);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -452,17 +457,11 @@ var nativeAppendChild = Element.prototype.appendChild;
|
||||
|
||||
function setInnerHTML(node, value) {
|
||||
node.textContent = '';
|
||||
if (node._composedChildren) {
|
||||
node._composedChildren = [];
|
||||
}
|
||||
var temp = node.ownerDocument.createElement('div');
|
||||
temp.innerHTML = value;
|
||||
var firstChild;
|
||||
while (firstChild = temp.firstChild) {
|
||||
nativeAppendChild.call(node, firstChild);
|
||||
if (node._composedChildren) {
|
||||
node._composedChildren.push(firstChild);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -477,11 +476,11 @@ function updateRootInsertionPoints(root) {
|
||||
}
|
||||
|
||||
function getComposedHTML(node) {
|
||||
return node.__patched ? Polymer.domInnerHTML.getInnerHTML(node, true) : node.innerHTML;
|
||||
return node.innerHTML;
|
||||
}
|
||||
|
||||
function getComposedChildAtIndex(node, index) {
|
||||
var c$ = node._composedChildren || node.childNodes;
|
||||
var c$ = node.childNodes;
|
||||
if (c$) {
|
||||
index = index || 0;
|
||||
index = Math.max(0, Math.min(index, c$.length-1));
|
||||
|
Loading…
Reference in New Issue
Block a user