Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(177)

Side by Side Diff: third_party/polymer/v0_8/components-chromium/polymer/src/lib/dom-api-extracted.js

Issue 1082403004: Import Polymer 0.8 and several key elements. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Also remove polymer/explainer Created 5 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1
2
3 Polymer.DomApi = (function() {
4
5 var Debounce = Polymer.Debounce;
6 var Settings = Polymer.Settings;
7
8 var nativeInsertBefore = Element.prototype.insertBefore;
9 var nativeRemoveChild = Element.prototype.removeChild;
10 var nativeAppendChild = Element.prototype.appendChild;
11
12 var dirtyRoots = [];
13
14 var DomApi = function(node, patch) {
15 this.node = node;
16 if (patch) {
17 this.patch();
18 }
19 };
20
21 DomApi.prototype = {
22
23 // experimental: support patching selected native api.
24 patch: function() {
25 var self = this;
26 this.node.appendChild = function(node) {
27 return self.appendChild(node);
28 };
29 this.node.insertBefore = function(node, ref_node) {
30 return self.insertBefore(node, ref_node);
31 };
32 this.node.removeChild = function(node) {
33 return self.removeChild(node);
34 };
35 },
36
37 get childNodes() {
38 var c$ = getLightChildren(this.node);
39 return Array.isArray(c$) ? c$ : Array.prototype.slice.call(c$);
40 },
41
42 get children() {
43 return Array.prototype.filter.call(this.childNodes, function(n) {
44 return (n.nodeType === Node.ELEMENT_NODE);
45 });
46 },
47
48 get parentNode() {
49 return this.node.lightParent || this.node.parentNode;
50 },
51
52 flush: function() {
53 for (var i=0, host; i<dirtyRoots.length; i++) {
54 host = dirtyRoots[i];
55 host.flushDebouncer('_distribute');
56 }
57 dirtyRoots = [];
58 },
59
60 _lazyDistribute: function(host) {
61 if (host.shadyRoot) {
62 host.shadyRoot._distributionClean = false;
63 }
64 // TODO(sorvell): optimize debounce so it does less work by default
65 // and then remove these checks...
66 // need to dirty distribution once.
67 if (!host.isDebouncerActive('_distribute')) {
68 host.debounce('_distribute', host._distributeContent);
69 dirtyRoots.push(host);
70 }
71 },
72
73 // cases in which we may not be able to just do standard appendChild
74 // 1. container has a shadyRoot (needsDistribution IFF the shadyRoot
75 // has an insertion point)
76 // 2. container is a shadyRoot (don't distribute, instead set
77 // container to container.host.
78 // 3. node is <content> (host of container needs distribution)
79 appendChild: function(node) {
80 var distributed;
81 this._removeNodeFromHost(node);
82 if (this._nodeIsInLogicalTree(this.node)) {
83 var host = this._hostForNode(this.node);
84 this._addLogicalInfo(node, this.node, host && host.shadyRoot);
85 this._addNodeToHost(node);
86 if (host) {
87 distributed = this._maybeDistribute(node, this.node, host);
88 }
89 }
90 if (!distributed) {
91 // if adding to a shadyRoot, add to host instead
92 var container = this.node._isShadyRoot ? this.node.host : this.node;
93 nativeAppendChild.call(container, node);
94 }
95 return node;
96 },
97
98 insertBefore: function(node, ref_node) {
99 if (!ref_node) {
100 return this.appendChild(node);
101 }
102 var distributed;
103 this._removeNodeFromHost(node);
104 if (this._nodeIsInLogicalTree(this.node)) {
105 saveLightChildrenIfNeeded(this.node);
106 var children = this.childNodes;
107 var index = children.indexOf(ref_node);
108 if (index < 0) {
109 throw Error('The ref_node to be inserted before is not a child ' +
110 'of this node');
111 }
112 var host = this._hostForNode(this.node);
113 this._addLogicalInfo(node, this.node, host && host.shadyRoot, index);
114 this._addNodeToHost(node);
115 if (host) {
116 distributed = this._maybeDistribute(node, this.node, host);
117 }
118 }
119 if (!distributed) {
120 // if ref_node is <content> replace with first distributed node
121 ref_node = ref_node.localName === CONTENT ?
122 this._firstComposedNode(ref_node) : ref_node;
123 // if adding to a shadyRoot, add to host instead
124 var container = this.node._isShadyRoot ? this.node.host : this.node;
125 nativeInsertBefore.call(container, node, ref_node);
126 }
127 return node;
128 },
129
130 /**
131 Removes the given `node` from the element's `lightChildren`.
132 This method also performs dom composition.
133 */
134 removeChild: function(node) {
135 var distributed;
136 if (this._nodeIsInLogicalTree(this.node)) {
137 var host = this._hostForNode(this.node);
138 distributed = this._maybeDistribute(node, this.node, host);
139 this._removeNodeFromHost(node);
140 }
141 if (!distributed) {
142 // if removing from a shadyRoot, remove form host instead
143 var container = this.node._isShadyRoot ? this.node.host : this.node;
144 nativeRemoveChild.call(container, node);
145 }
146 return node;
147 },
148
149 replaceChild: function(node, ref_node) {
150 this.insertBefore(node, ref_node);
151 this.removeChild(ref_node);
152 return node;
153 },
154
155 getOwnerRoot: function() {
156 return this._ownerShadyRootForNode(this.node);
157 },
158
159 _ownerShadyRootForNode: function(node) {
160 if (node._ownerShadyRoot === undefined) {
161 var root;
162 if (node._isShadyRoot) {
163 root = node;
164 } else {
165 var parent = Polymer.dom(node).parentNode;
166 if (parent) {
167 root = parent._isShadyRoot ? parent :
168 this._ownerShadyRootForNode(parent);
169 } else {
170 root = null;
171 }
172 }
173 node._ownerShadyRoot = root;
174 }
175 return node._ownerShadyRoot;
176
177 },
178
179 _maybeDistribute: function(node, parent, host) {
180 var nodeNeedsDistribute = this._nodeNeedsDistribution(node);
181 var distribute = this._parentNeedsDistribution(parent) ||
182 nodeNeedsDistribute;
183 if (nodeNeedsDistribute) {
184 this._updateInsertionPoints(host);
185 }
186 if (distribute) {
187 this._lazyDistribute(host);
188 }
189 return distribute;
190 },
191
192 _updateInsertionPoints: function(host) {
193 host.shadyRoot._insertionPoints =
194 factory(host.shadyRoot).querySelectorAll(CONTENT);
195 },
196
197 _nodeIsInLogicalTree: function(node) {
198 return Boolean(node._isShadyRoot ||
199 this._ownerShadyRootForNode(node) ||
200 node.shadyRoot);
201 },
202
203 // note: a node is its own host
204 _hostForNode: function(node) {
205 var root = node.shadyRoot || (node._isShadyRoot ?
206 node : this._ownerShadyRootForNode(node));
207 return root && root.host;
208 },
209
210 _parentNeedsDistribution: function(parent) {
211 return parent.shadyRoot && hasInsertionPoint(parent.shadyRoot);
212 },
213
214 // TODO(sorvell): technically we should check non-fragment nodes for
215 // <content> children but since this case is assumed to be exceedingly
216 // rare, we avoid the cost and will address with some specific api
217 // when the need arises.
218 _nodeNeedsDistribution: function(node) {
219 return (node.localName === CONTENT) ||
220 ((node.nodeType === Node.DOCUMENT_FRAGMENT_NODE) &&
221 node.querySelector(CONTENT));
222 },
223
224 _removeNodeFromHost: function(node) {
225 if (node.lightParent) {
226 var root = this._ownerShadyRootForNode(node);
227 if (root) {
228 root.host._elementRemove(node);
229 }
230 this._removeLogicalInfo(node, node.lightParent);
231 }
232 this._removeOwnerShadyRoot(node);
233 },
234
235 _addNodeToHost: function(node) {
236 var checkNode = node.nodeType === Node.DOCUMENT_FRAGMENT_NODE ?
237 node.firstChild : node;
238 var root = this._ownerShadyRootForNode(checkNode);
239 if (root) {
240 root.host._elementAdd(node);
241 }
242 },
243
244 _addLogicalInfo: function(node, container, root, index) {
245 saveLightChildrenIfNeeded(container);
246 var children = factory(container).childNodes;
247 index = index === undefined ? children.length : index;
248 // handle document fragments
249 if (node.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
250 var n = node.firstChild;
251 while (n) {
252 children.splice(index++, 0, n);
253 n.lightParent = container;
254 n = n.nextSibling;
255 }
256 } else {
257 children.splice(index, 0, node);
258 node.lightParent = container;
259 }
260 },
261
262 // NOTE: in general, we expect contents of the lists here to be small-ish
263 // and therefore indexOf to be nbd. Other optimizations can be made
264 // for larger lists (linked list)
265 _removeLogicalInfo: function(node, container) {
266 var children = factory(container).childNodes;
267 var index = children.indexOf(node);
268 if ((index < 0) || (container !== node.lightParent)) {
269 throw Error('The node to be removed is not a child of this node');
270 }
271 children.splice(index, 1);
272 node.lightParent = null;
273 },
274
275 _removeOwnerShadyRoot: function(node) {
276 // TODO(sorvell): need to clear any children of element?
277 node._ownerShadyRoot = undefined;
278 },
279
280 // TODO(sorvell): This will fail if distribution that affects this
281 // question is pending; this is expected to be exceedingly rare, but if
282 // the issue comes up, we can force a flush in this case.
283 _firstComposedNode: function(content) {
284 var n$ = factory(content).getDistributedNodes();
285 for (var i=0, l=n$.length, n, p$; (i<l) && (n=n$[i]); i++) {
286 p$ = factory(n).getDestinationInsertionPoints();
287 // means that we're composed to this spot.
288 if (p$[p$.length-1] === content) {
289 return n;
290 }
291 }
292 },
293
294 // TODO(sorvell): consider doing native QSA and filtering results.
295 querySelector: function(selector) {
296 return this.querySelectorAll(selector)[0];
297 },
298
299 querySelectorAll: function(selector) {
300 return this._query(function(n) {
301 return matchesSelector.call(n, selector);
302 }, this.node);
303 },
304
305 _query: function(matcher, node) {
306 var list = [];
307 this._queryElements(factory(node).childNodes, matcher, list);
308 return list;
309 },
310
311 _queryElements: function(elements, matcher, list) {
312 for (var i=0, l=elements.length, c; (i<l) && (c=elements[i]); i++) {
313 if (c.nodeType === Node.ELEMENT_NODE) {
314 this._queryElement(c, matcher, list);
315 }
316 }
317 },
318
319 _queryElement: function(node, matcher, list) {
320 if (matcher(node)) {
321 list.push(node);
322 }
323 this._queryElements(factory(node).childNodes, matcher, list);
324 },
325
326 getDestinationInsertionPoints: function() {
327 return this.node._destinationInsertionPoints || [];
328 },
329
330 getDistributedNodes: function() {
331 return this.node._distributedNodes || [];
332 },
333
334 /*
335 Returns a list of nodes distributed within this element. These can be
336 dom children or elements distributed to children that are insertion
337 points.
338 */
339 queryDistributedElements: function(selector) {
340 var c$ = this.childNodes;
341 var list = [];
342 this._distributedFilter(selector, c$, list);
343 for (var i=0, l=c$.length, c; (i<l) && (c=c$[i]); i++) {
344 if (c.localName === CONTENT) {
345 this._distributedFilter(selector, factory(c).getDistributedNodes(),
346 list);
347 }
348 }
349 return list;
350 },
351
352 _distributedFilter: function(selector, list, results) {
353 results = results || [];
354 for (var i=0, l=list.length, d; (i<l) && (d=list[i]); i++) {
355 if ((d.nodeType === Node.ELEMENT_NODE) &&
356 (d.localName !== CONTENT) &&
357 matchesSelector.call(d, selector)) {
358 results.push(d);
359 }
360 }
361 return results;
362 }
363
364 };
365
366 if (Settings.useShadow) {
367
368 DomApi.prototype.querySelectorAll = function(selector) {
369 return Array.prototype.slice.call(this.node.querySelectorAll(selector));
370 };
371
372 DomApi.prototype.patch = function() {};
373
374 DomApi.prototype.getOwnerRoot = function() {
375 var n = this.node;
376 while (n) {
377 if (n.nodeType === Node.DOCUMENT_FRAGMENT_NODE && n.host) {
378 return n;
379 }
380 n = n.parentNode;
381 }
382 };
383
384 DomApi.prototype.getDestinationInsertionPoints = function() {
385 var n$ = this.node.getDestinationInsertionPoints();
386 return n$ ? Array.prototype.slice.call(n$) : [];
387 };
388
389 DomApi.prototype.getDistributedNodes = function() {
390 var n$ = this.node.getDistributedNodes();
391 return n$ ? Array.prototype.slice.call(n$) : [];
392 };
393
394
395 }
396
397 var CONTENT = 'content';
398
399 var factory = function(node, patch) {
400 node = node || document;
401 if (!node.__domApi) {
402 node.__domApi = new DomApi(node, patch);
403 }
404 return node.__domApi;
405 };
406
407 Polymer.dom = function(obj, patch) {
408 if (obj instanceof Event) {
409 return Polymer.EventApi.factory(obj);
410 } else {
411 return factory(obj, patch);
412 }
413 };
414
415 // make flush available directly.
416 Polymer.dom.flush = DomApi.prototype.flush;
417
418 function getLightChildren(node) {
419 var children = node.lightChildren;
420 return children ? children : node.childNodes;
421 }
422
423 function saveLightChildrenIfNeeded(node) {
424 // Capture the list of light children. It's important to do this before we
425 // start transforming the DOM into "rendered" state.
426 //
427 // Children may be added to this list dynamically. It will be treated as t he
428 // source of truth for the light children of the element. This element's
429 // actual children will be treated as the rendered state once lightChildre n
430 // is populated.
431 if (!node.lightChildren) {
432 var children = [];
433 for (var child = node.firstChild; child; child = child.nextSibling) {
434 children.push(child);
435 child.lightParent = child.lightParent || node;
436 }
437 node.lightChildren = children;
438 }
439 }
440
441 function hasInsertionPoint(root) {
442 return Boolean(root._insertionPoints.length);
443 }
444
445 var p = Element.prototype;
446 var matchesSelector = p.matches || p.matchesSelector ||
447 p.mozMatchesSelector || p.msMatchesSelector ||
448 p.oMatchesSelector || p.webkitMatchesSelector;
449
450 return {
451 getLightChildren: getLightChildren,
452 saveLightChildrenIfNeeded: saveLightChildrenIfNeeded,
453 matchesSelector: matchesSelector,
454 hasInsertionPoint: hasInsertionPoint,
455 factory: factory
456 };
457
458 })();
459
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698