Index: third_party/polymer/components-chromium/core-list/core-list-extracted.js |
diff --git a/third_party/polymer/components-chromium/core-list/core-list-extracted.js b/third_party/polymer/components-chromium/core-list/core-list-extracted.js |
new file mode 100644 |
index 0000000000000000000000000000000000000000..bd4c8c66aea93689cb73dfccc7f477a80b155023 |
--- /dev/null |
+++ b/third_party/polymer/components-chromium/core-list/core-list-extracted.js |
@@ -0,0 +1,361 @@ |
+ |
+(function() { |
+ |
+ Polymer('core-list', { |
+ |
+ publish: { |
+ /** |
+ * Fired when an item element is tapped. |
+ * |
+ * @event core-activate |
+ * @param {Object} detail |
+ * @param {Object} detail.item the item element |
+ */ |
+ |
+ /** |
+ * |
+ * An array of source data for the list to display. |
+ * |
+ * @attribute data |
+ * @type array |
+ * @default null |
+ */ |
+ data: null, |
+ |
+ /** |
+ * |
+ * An optional element on which to listen for scroll events. |
+ * |
+ * @attribute scrollTarget |
+ * @type Element |
+ * @default core-list |
+ */ |
+ scrollTarget: null, |
+ |
+ /** |
+ * |
+ * The height of a list item. `core-list` currently supports only fixed-height |
+ * list items. This height must be specified via the height property. |
+ * |
+ * @attribute height |
+ * @type number |
+ * @default 80 |
+ */ |
+ height: 80, |
+ |
+ /** |
+ * |
+ * The number of extra items rendered above the minimum set required to |
+ * fill the list's height. |
+ * |
+ * @attribute extraItems |
+ * @type number |
+ * @default 30 |
+ */ |
+ extraItems: 30, |
+ |
+ /** |
+ * |
+ * The property set on the list view data to represent selection state. |
+ * This should set so that it does not conflict with other data properties. |
+ * Note, selection data is not stored on the data in given in the data property. |
+ * |
+ * @attribute selectedProperty |
+ * @type string |
+ * @default 'selected' |
+ */ |
+ selectedProperty: 'selected', |
+ |
+ // TODO(sorvell): experimental |
+ /** |
+ * |
+ * If true, data is sync'd from the list back to the list's data. |
+ * |
+ * @attribute sync |
+ * @type boolean |
+ * @default false |
+ */ |
+ sync: false, |
+ |
+ /** |
+ * |
+ * Set to true to support multiple selection. |
+ * |
+ * @attribute multi |
+ * @type boolean |
+ * @default false |
+ */ |
+ multi: false |
+ |
+ }, |
+ |
+ observe: { |
+ 'data template scrollTarget': 'initialize' |
+ }, |
+ |
+ ready: function() { |
+ this.clearSelection(); |
+ this._boundScrollHandler = this.scrollHandler.bind(this); |
+ }, |
+ |
+ attached: function() { |
+ this.template = this.querySelector('template'); |
+ }, |
+ |
+ // TODO(sorvell): it'd be nice to dispense with 'data' and just use |
+ // template repeat's model. However, we need tighter integration |
+ // with TemplateBinding for this. |
+ initialize: function() { |
+ if (!this.data || !this.template) { |
+ return; |
+ } |
+ var target = this.scrollTarget || this; |
+ if (this._target !== target) { |
+ if (this._target) { |
+ this._target.removeEventListener('scroll', this._boundScrollHandler, false); |
+ } |
+ this._target = target; |
+ this._target.addEventListener('scroll', this._boundScrollHandler, false); |
+ } |
+ |
+ this.initializeViewport(); |
+ this.initalizeData(); |
+ this.onMutation(this, this.initializeItems); |
+ }, |
+ |
+ // TODO(sorvell): need to handle resizing |
+ initializeViewport: function() { |
+ this.$.viewport.style.height = this.height * this.data.length + 'px'; |
+ this._visibleCount = Math.ceil(this._target.offsetHeight / this.height); |
+ this._physicalCount = Math.min(this._visibleCount + this.extraItems, |
+ this.data.length); |
+ this._physicalHeight = this.height * this._physicalCount; |
+ }, |
+ |
+ // TODO(sorvell): selection currently cannot be maintained when |
+ // items are added or deleted. |
+ initalizeData: function() { |
+ var exampleDatum = this.data[0] || {}; |
+ this._propertyNames = Object.getOwnPropertyNames(exampleDatum); |
+ this._physicalData = new Array(this._physicalCount); |
+ for (var i = 0; i < this._physicalCount; ++i) { |
+ this._physicalData[i] = {}; |
+ this.updateItem(i, i); |
+ } |
+ this.template.model = this._physicalData; |
+ this.template.setAttribute('repeat', ''); |
+ }, |
+ |
+ initializeItems: function() { |
+ this._physicalItems = new Array(this._physicalCount); |
+ for (var i = 0, item = this.template.nextElementSibling; |
+ item && i < this._physicalCount; |
+ ++i, item = item.nextElementSibling) { |
+ this._physicalItems[i] = item; |
+ item._transformValue = 0; |
+ } |
+ this.refresh(false); |
+ }, |
+ |
+ updateItem: function(virtualIndex, physicalIndex) { |
+ var virtualDatum = this.data[virtualIndex]; |
+ var physicalDatum = this._physicalData[physicalIndex]; |
+ this.pushItemData(virtualDatum, physicalDatum); |
+ physicalDatum._physicalIndex = physicalIndex; |
+ physicalDatum._virtualIndex = virtualIndex; |
+ if (this.selectedProperty) { |
+ physicalDatum[this.selectedProperty] = this._selectedData.get(virtualDatum); |
+ } |
+ }, |
+ |
+ pushItemData: function(source, dest) { |
+ for (var i = 0; i < this._propertyNames.length; ++i) { |
+ var propertyName = this._propertyNames[i]; |
+ dest[propertyName] = source[propertyName]; |
+ } |
+ }, |
+ |
+ // experimental: push physical data back to this.data. |
+ // this is optional when scrolling and needs to be called at other times. |
+ syncData: function() { |
+ if (this.firstPhysicalIndex === undefined || |
+ this.baseVirtualIndex === undefined) { |
+ return; |
+ } |
+ var p, v; |
+ for (var i = 0; i < this.firstPhysicalIndex; ++i) { |
+ p = this._physicalData[i]; |
+ v = this.data[this.baseVirtualIndex + this._physicalCount + i]; |
+ this.pushItemData(p, v); |
+ } |
+ for (var i = this.firstPhysicalIndex; i < this._physicalCount; ++i) { |
+ p = this._physicalData[i]; |
+ v = this.data[this.baseVirtualIndex + i]; |
+ this.pushItemData(p, v); |
+ } |
+ }, |
+ |
+ scrollHandler: function(e, detail) { |
+ this._scrollTop = e.detail ? e.detail.target.scrollTop : e.target.scrollTop; |
+ this.refresh(false); |
+ }, |
+ |
+ /** |
+ * Refresh the list at the current scroll position. |
+ * |
+ * @method refresh |
+ */ |
+ refresh: function(force) { |
+ var firstVisibleIndex = Math.floor(this._scrollTop / this.height); |
+ var visibleMidpoint = firstVisibleIndex + this._visibleCount / 2; |
+ |
+ var firstReifiedIndex = Math.max(0, Math.floor(visibleMidpoint - |
+ this._physicalCount / 2)); |
+ firstReifiedIndex = Math.min(firstReifiedIndex, this.data.length - |
+ this._physicalCount); |
+ |
+ var firstPhysicalIndex = firstReifiedIndex % this._physicalCount; |
+ var baseVirtualIndex = firstReifiedIndex - firstPhysicalIndex; |
+ |
+ var baseTransformValue = Math.floor(this.height * baseVirtualIndex); |
+ var nextTransformValue = Math.floor(baseTransformValue + |
+ this._physicalHeight); |
+ |
+ var baseTransformString = 'translate3d(0,' + baseTransformValue + 'px,0)'; |
+ var nextTransformString = 'translate3d(0,' + nextTransformValue + 'px,0)'; |
+ // TODO(sorvell): experiemental for sync'ing back to virtual data. |
+ if (this.sync) { |
+ this.syncData(); |
+ } |
+ this.firstPhysicalIndex = firstPhysicalIndex; |
+ this.baseVirtualIndex = baseVirtualIndex; |
+ |
+ for (var i = 0; i < firstPhysicalIndex; ++i) { |
+ var item = this._physicalItems[i]; |
+ if (force || item._transformValue != nextTransformValue) { |
+ this.updateItem(baseVirtualIndex + this._physicalCount + i, i); |
+ setTransform(item, nextTransformString, nextTransformValue); |
+ } |
+ } |
+ for (var i = firstPhysicalIndex; i < this._physicalCount; ++i) { |
+ var item = this._physicalItems[i]; |
+ if (force || item._transformValue != baseTransformValue) { |
+ this.updateItem(baseVirtualIndex + i, i); |
+ setTransform(item, baseTransformString, baseTransformValue); |
+ } |
+ } |
+ }, |
+ |
+ // list selection |
+ tapHandler: function(e) { |
+ if (e.target === this) { |
+ return; |
+ } |
+ if (this.sync) { |
+ this.syncData(); |
+ } |
+ var n = e.target; |
+ var model = n.templateInstance && n.templateInstance.model; |
+ if (model) { |
+ var vi = model._virtualIndex, pi = model._physicalIndex; |
+ var data = this.data[vi], item = this._physicalItems[pi]; |
+ this.$.selection.select(data); |
+ this.asyncFire('core-activate', {data: data, item: item}); |
+ } |
+ }, |
+ |
+ selectedHandler: function(e, detail) { |
+ if (this.selectedProperty) { |
+ var i$ = this.indexesForData(detail.item); |
+ // TODO(sorvell): we should be relying on selection to store the |
+ // selected data but we want to optimize for lookup. |
+ this._selectedData.set(detail.item, detail.isSelected); |
+ if (i$.physical >= 0) { |
+ this.updateItem(i$.virtual, i$.physical); |
+ } |
+ } |
+ }, |
+ |
+ /** |
+ * Select the list item at the given index. |
+ * |
+ * @method selectItem |
+ * @param {number} index |
+ */ |
+ selectItem: function(index) { |
+ var data = this.data[index]; |
+ if (data) { |
+ this.$.selection.select(data); |
+ } |
+ }, |
+ |
+ /** |
+ * Set the selected state of the list item at the given index. |
+ * |
+ * @method setItemSelected |
+ * @param {number} index |
+ * @param {boolean} isSelected |
+ */ |
+ setItemSelected: function(index, isSelected) { |
+ var data = this.data[index]; |
+ if (data) { |
+ this.$.selection.setItemSelected(data, isSelected); |
+ } |
+ }, |
+ |
+ indexesForData: function(data) { |
+ var virtual = this.data.indexOf(data); |
+ var physical = this.virtualToPhysicalIndex(virtual); |
+ return { virtual: virtual, physical: physical }; |
+ }, |
+ |
+ virtualToPhysicalIndex: function(index) { |
+ for (var i=0, l=this._physicalData.length; i<l; i++) { |
+ if (this._physicalData[i]._virtualIndex === index) { |
+ return i; |
+ } |
+ } |
+ return -1; |
+ }, |
+ |
+ get selection() { |
+ return this.$.selection.getSelection(); |
+ }, |
+ |
+ selectedChanged: function() { |
+ this.$.selection.select(this.selected); |
+ }, |
+ |
+ clearSelection: function() { |
+ this._selectedData = new WeakMap(); |
+ if (this.multi) { |
+ var s$ = this.selection; |
+ for (var i=0, l=s$.length, s; (i<l) && (s=s$[i]); i++) { |
+ this.$.selection.setItemSelected(s, false); |
+ } |
+ } else { |
+ this.$.selection.setItemSelected(this.selection, false); |
+ } |
+ this.$.selection.clear(); |
+ }, |
+ |
+ scrollToItem: function(index) { |
+ this.scrollTop = index * this.height; |
+ } |
+ |
+ }); |
+ |
+ // determine proper transform mechanizm |
+ if (document.documentElement.style.transform !== undefined) { |
+ function setTransform(element, string, value) { |
+ element.style.transform = string; |
+ element._transformValue = value; |
+ } |
+ } else { |
+ function setTransform(element, string, value) { |
+ element.style.webkitTransform = string; |
+ element._transformValue = value; |
+ } |
+ } |
+ |
+})(); |