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

Side by Side Diff: third_party/polymer/v0_8/components-chromium/polymer/src/lib/template/x-repeat-extracted.js

Issue 1162563004: Upgrade to 1.0 and switch clients to dom-repeat where needed. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Fix a layout import and remove the gzipped webanimation in reproduce.sh Created 5 years, 6 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({
4
5 is: 'x-repeat',
6 extends: 'template',
7
8 properties: {
9
10 /**
11 * An array containing items determining how many instances of the templat e
12 * to stamp and that that each template instance should bind to.
13 */
14 items: {
15 type: Array
16 },
17
18 /**
19 * A function that should determine the sort order of the items. This
20 * property should either be provided as a string, indicating a method
21 * name on the element's host, or else be an actual function. The
22 * function should match the sort function passed to `Array.sort`.
23 * Using a sort function has no effect on the underlying `items` array.
24 */
25 sort: {
26 type: Function,
27 observer: '_sortChanged'
28 },
29
30 /**
31 * A function that can be used to filter items out of the view. This
32 * property should either be provided as a string, indicating a method
33 * name on the element's host, or else be an actual function. The
34 * function should match the sort function passed to `Array.filter`.
35 * Using a filter function has no effect on the underlying `items` array.
36 */
37 filter: {
38 type: Function,
39 observer: '_filterChanged'
40 },
41
42 /**
43 * When using a `filter` or `sort` function, the `observe` property
44 * should be set to a space-separated list of the names of item
45 * sub-fields that should trigger a re-sort or re-filter when changed.
46 * These should generally be fields of `item` that the sort or filter
47 * function depends on.
48 */
49 observe: {
50 type: String,
51 observer: '_observeChanged'
52 },
53
54 /**
55 * When using a `filter` or `sort` function, the `delay` property
56 * determines a debounce time after a change to observed item
57 * properties that must pass before the filter or sort is re-run.
58 * This is useful in rate-limiting shuffing of the view when
59 * item changes may be frequent.
60 */
61 delay: Number
62 },
63
64 behaviors: [
65 Polymer.Templatizer
66 ],
67
68 observers: [
69 '_itemsChanged(items.*)'
70 ],
71
72 created: function() {
73 this.boundCollectionObserver = this.render.bind(this);
74 },
75
76 ready: function() {
77 // Templatizing (generating the instance constructor) needs to wait
78 // until attached, since it may not have its template content handed
79 // back to it until then, following its host template stamping
80 if (!this.ctor) {
81 this.templatize(this);
82 }
83 },
84
85 _sortChanged: function() {
86 var dataHost = this._getRootDataHost();
87 this._sortFn = this.sort && (typeof this.sort == 'function' ?
88 this.sort : dataHost[this.sort].bind(this.host));
89 if (this.items) {
90 this.debounce('render', this.render);
91 }
92 },
93
94 _filterChanged: function() {
95 var dataHost = this._getRootDataHost();
96 this._filterFn = this.filter && (typeof this.filter == 'function' ?
97 this.filter : dataHost[this.filter].bind(this.host));
98 if (this.items) {
99 this.debounce('render', this.render);
100 }
101 },
102
103 _observeChanged: function() {
104 this._observePaths = this.observe &&
105 this.observe.replace('.*', '.').split(' ');
106 },
107
108 _itemsChanged: function(change) {
109 if (change.path == 'items') {
110 this._unobserveCollection();
111 if (change.value) {
112 this._observeCollection(change.value);
113 this.debounce('render', this.render);
114 }
115 } else {
116 this._forwardItemPath(change.path, change.value);
117 this._checkObservedPaths(change.path);
118 }
119 },
120
121 _checkObservedPaths: function(path) {
122 if (this._observePaths && path.indexOf('items.') === 0) {
123 path = path.substring(path.indexOf('.', 6) + 1);
124 var paths = this._observePaths;
125 for (var i=0; i<paths.length; i++) {
126 if (path.indexOf(paths[i]) === 0) {
127 this.debounce('render', this.render, this.delay);
128 return;
129 }
130 }
131 }
132 },
133
134 _observeCollection: function(items) {
135 this.collection = Array.isArray(items) ? Polymer.Collection.get(items) : i tems;
136 this.collection.observe(this.boundCollectionObserver);
137 },
138
139 _unobserveCollection: function() {
140 if (this.collection) {
141 this.collection.unobserve(this.boundCollectionObserver);
142 }
143 },
144
145 render: function(splices) {
146 this.flushDebouncer('render');
147 var c = this.collection;
148 if (splices) {
149 if (this._sortFn || splices[0].index == null) {
150 this._applySplicesViewSort(splices);
151 } else {
152 this._applySplicesArraySort(splices);
153 }
154 } else {
155 this._sortAndFilter();
156 }
157 var rowForKey = this._rowForKey = {};
158 var keys = this._orderedKeys;
159 // Assign items and keys
160 this.rows = this.rows || [];
161 for (var i=0; i<keys.length; i++) {
162 var key = keys[i];
163 var item = c.getItem(key);
164 var row = this.rows[i];
165 rowForKey[key] = i;
166 if (!row) {
167 this.rows.push(row = this._insertRow(i, null, item));
168 }
169 row.item = item;
170 row.key = key;
171 row.index = i;
172 }
173 // Remove extra
174 for (; i<this.rows.length; i++) {
175 this._detachRow(i);
176 }
177 this.rows.splice(keys.length, this.rows.length-keys.length);
178 },
179
180 _sortAndFilter: function() {
181 var c = this.collection;
182 this._orderedKeys = c.getKeys();
183 // Filter
184 if (this._filterFn) {
185 this._orderedKeys = this._orderedKeys.filter(function(a) {
186 return this._filterFn(c.getItem(a));
187 }, this);
188 }
189 // Sort
190 if (this._sortFn) {
191 this._orderedKeys.sort(function(a, b) {
192 return this._sortFn(c.getItem(a), c.getItem(b));
193 }.bind(this));
194 }
195 },
196
197 _keySort: function(a, b) {
198 return this.collection.getKey(a) - this.collection.getKey(b);
199 },
200
201 _applySplicesViewSort: function(splices) {
202 var c = this.collection;
203 var keys = this._orderedKeys;
204 var rows = this.rows;
205 var removedRows = [];
206 var addedKeys = [];
207 var pool = [];
208 var sortFn = this._sortFn || this._keySort.bind(this);
209 splices.forEach(function(s) {
210 // Collect all removed row idx's
211 for (var i=0; i<s.removed.length; i++) {
212 var idx = this._rowForKey[s.removed[i]];
213 if (idx != null) {
214 removedRows.push(idx);
215 }
216 }
217 // Collect all added keys
218 for (i=0; i<s.added.length; i++) {
219 addedKeys.push(s.added[i]);
220 }
221 }, this);
222 if (removedRows.length) {
223 // Sort removed rows idx's
224 removedRows.sort();
225 // Remove keys and pool rows (backwards, so we don't invalidate rowForKe y)
226 for (i=removedRows.length-1; i>=0 ; i--) {
227 var idx = removedRows[i];
228 pool.push(this._detachRow(idx));
229 rows.splice(idx, 1);
230 keys.splice(idx, 1);
231 }
232 }
233 if (addedKeys.length) {
234 // Filter added keys
235 if (this._filterFn) {
236 addedKeys = addedKeys.filter(function(a) {
237 return this._filterFn(c.getItem(a));
238 }, this);
239 }
240 // Sort added keys
241 addedKeys.sort(function(a, b) {
242 return this.sortFn(c.getItem(a), c.getItem(b));
243 }, this);
244 // Insert new rows using sort (from pool or newly created)
245 var start = 0;
246 for (i=0; i<addedKeys.length; i++) {
247 start = this._insertRowIntoViewSort(start, addedKeys[i], pool);
248 }
249 }
250 },
251
252 _insertRowIntoViewSort: function(start, key, pool) {
253 var c = this.collection;
254 var item = c.getItem(key);
255 var end = this.rows.length - 1;
256 var idx = -1;
257 var sortFn = this._sortFn || this._keySort.bind(this);
258 // Binary search for insertion point
259 while (start <= end) {
260 var mid = (start + end) >> 1;
261 var midKey = this._orderedKeys[mid];
262 var cmp = sortFn(c.getItem(midKey), item);
263 if (cmp < 0) {
264 start = mid + 1;
265 } else if (cmp > 0) {
266 end = mid - 1;
267 } else {
268 idx = mid;
269 break;
270 }
271 }
272 if (idx < 0) {
273 idx = end + 1;
274 }
275 // Insert key & row at insertion point
276 this._orderedKeys.splice(idx, 0, key);
277 this.rows.splice(idx, 0, this._insertRow(idx, pool));
278 return idx;
279 },
280
281 _applySplicesArraySort: function(splices) {
282 var keys = this._orderedKeys;
283 var pool = [];
284 splices.forEach(function(s) {
285 // Remove & pool rows first, to ensure we can fully reuse removed rows
286 for (var i=0; i<s.removed.length; i++) {
287 pool.push(this._detachRow(s.index + i));
288 }
289 this.rows.splice(s.index, s.removed.length);
290 }, this);
291 var c = this.collection;
292 var filterDelta = 0;
293 splices.forEach(function(s) {
294 // Filter added keys
295 var addedKeys = s.added;
296 if (this._filterFn) {
297 addedKeys = addedKeys.filter(function(a) {
298 return this._filterFn(c.getItem(a));
299 }, this);
300 filterDelta += (s.added.length - addedKeys.length);
301 }
302 var idx = s.index - filterDelta;
303 // Apply splices to keys
304 var args = [idx, s.removed.length].concat(addedKeys);
305 keys.splice.apply(keys, args);
306 // Insert new rows (from pool or newly created)
307 var addedRows = [];
308 for (i=0; i<s.added.length; i++) {
309 addedRows.push(this._insertRow(idx + i, pool));
310 }
311 args = [s.index, 0].concat(addedRows);
312 this.rows.splice.apply(this.rows, args);
313 }, this);
314 },
315
316 _detachRow: function(idx) {
317 var row = this.rows[idx];
318 var parentNode = Polymer.dom(this).parentNode;
319 for (var i=0; i<row._children.length; i++) {
320 var el = row._children[i];
321 Polymer.dom(row.root).appendChild(el);
322 }
323 return row;
324 },
325
326 _insertRow: function(idx, pool, item) {
327 var row = (pool && pool.pop()) || this._generateRow(idx, item);
328 var beforeRow = this.rows[idx];
329 var beforeNode = beforeRow ? beforeRow._children[0] : this;
330 var parentNode = Polymer.dom(this).parentNode;
331 Polymer.dom(parentNode).insertBefore(row.root, beforeNode);
332 return row;
333 },
334
335 _generateRow: function(idx, item) {
336 var row = this.stamp({
337 index: idx,
338 key: this.collection.getKey(item),
339 item: item
340 });
341 // each row is a document fragment which is lost when we appendChild,
342 // so we have to track each child individually
343 var children = [];
344 for (var n = row.root.firstChild; n; n=n.nextSibling) {
345 children.push(n);
346 n._templateInstance = row;
347 }
348 // Since archetype overrides Base/HTMLElement, Safari complains
349 // when accessing `children`
350 row._children = children;
351 return row;
352 },
353
354 // Implements extension point from Templatizer mixin
355 _getStampedChildren: function() {
356 var children = [];
357 if (this.rows) {
358 for (var i=0; i<this.rows.length; i++) {
359 var c = this.rows[i]._children;
360 for (var j=0; j<c.length; j++)
361 children.push(c[j]);
362 }
363 }
364 return children;
365 },
366
367 // Implements extension point from Templatizer
368 // Called as a side effect of a template instance path change, responsible
369 // for notifying items.<key-for-row>.<path> change up to host
370 _forwardInstancePath: function(row, root, subPath, value) {
371 if (root == 'item') {
372 this.notifyPath('items.' + row.key + '.' + subPath, value);
373 }
374 },
375
376 // Implements extension point from Templatizer mixin
377 // Called as side-effect of a host property change, responsible for
378 // notifying parent.<prop> path change on each row
379 _forwardParentProp: function(prop, value) {
380 if (this.rows) {
381 this.rows.forEach(function(row) {
382 row.parent[prop] = value;
383 row.notifyPath('parent.' + prop, value, true);
384 }, this);
385 }
386 },
387
388 // Implements extension point from Templatizer
389 // Called as side-effect of a host path change, responsible for
390 // notifying parent.<path> path change on each row
391 _forwardParentPath: function(path, value) {
392 if (this.rows) {
393 this.rows.forEach(function(row) {
394 row.notifyPath('parent.' + path, value, true);
395 }, this);
396 }
397 },
398
399 // Called as a side effect of a host items.<key>.<path> path change,
400 // responsible for notifying item.<path> changes to row for key
401 _forwardItemPath: function(path, value) {
402 if (this._rowForKey) {
403 // 'items.'.length == 6
404 var dot = path.indexOf('.', 6);
405 var key = path.substring(6, dot < 0 ? path.length : dot);
406 var idx = this._rowForKey[key];
407 var row = this.rows[idx];
408 if (row) {
409 if (dot >= 0) {
410 path = 'item.' + path.substring(dot+1);
411 row.notifyPath(path, value, true);
412 } else {
413 row.item = value;
414 }
415 }
416 }
417 },
418
419 _instanceForElement: function(el) {
420 while (el && !el._templateInstance) {
421 el = el.parentNode;
422 }
423 return el && el._templateInstance;
424 },
425
426 /**
427 * Returns the item associated with a given element stamped by
428 * this `x-repeat`.
429 */
430 itemForElement: function(el) {
431 var instance = this._instanceForElement(el);
432 return instance && instance.item;
433 },
434
435 /**
436 * Returns the `Polymer.Collection` key associated with a given
437 * element stamped by this `x-repeat`.
438 */
439 keyForElement: function(el) {
440 var instance = this._instanceForElement(el);
441 return instance && instance.key;
442 },
443
444 /**
445 * Returns the index in `items` associated with a given element
446 * stamped by this `x-repeat`.
447 */
448 indexForElement: function(el) {
449 var instance = this._instanceForElement(el);
450 return this.rows.indexOf(instance);
451 }
452
453 });
454
455
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698