OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 // require: array_data_model.js | 5 // require: array_data_model.js |
6 // require: list_selection_model.js | 6 // require: list_selection_model.js |
7 // require: list_selection_controller.js | 7 // require: list_selection_controller.js |
8 // require: list_item.js | 8 // require: list_item.js |
9 | 9 |
10 /** | 10 /** |
(...skipping 10 matching lines...) Expand all Loading... |
21 * false if the mouseevent was generated over a border or a scrollbar. | 21 * false if the mouseevent was generated over a border or a scrollbar. |
22 * @param {!HTMLElement} el The element to test the event with. | 22 * @param {!HTMLElement} el The element to test the event with. |
23 * @param {!Event} e The mouse event. | 23 * @param {!Event} e The mouse event. |
24 * @return {boolean} Whether the mouse event was inside the viewport. | 24 * @return {boolean} Whether the mouse event was inside the viewport. |
25 */ | 25 */ |
26 function inViewport(el, e) { | 26 function inViewport(el, e) { |
27 var rect = el.getBoundingClientRect(); | 27 var rect = el.getBoundingClientRect(); |
28 var x = e.clientX; | 28 var x = e.clientX; |
29 var y = e.clientY; | 29 var y = e.clientY; |
30 return x >= rect.left + el.clientLeft && | 30 return x >= rect.left + el.clientLeft && |
31 x < rect.left + el.clientLeft + el.clientWidth && | 31 x < rect.left + el.clientLeft + el.clientWidth && |
32 y >= rect.top + el.clientTop && | 32 y >= rect.top + el.clientTop && |
33 y < rect.top + el.clientTop + el.clientHeight; | 33 y < rect.top + el.clientTop + el.clientHeight; |
34 } | 34 } |
35 | 35 |
36 function getComputedStyle(el) { | 36 function getComputedStyle(el) { |
37 return el.ownerDocument.defaultView.getComputedStyle(el); | 37 return el.ownerDocument.defaultView.getComputedStyle(el); |
38 } | 38 } |
39 | 39 |
40 /** | 40 /** |
41 * Creates a new list element. | 41 * Creates a new list element. |
42 * @param {Object=} opt_propertyBag Optional properties. | 42 * @param {Object=} opt_propertyBag Optional properties. |
43 * @constructor | 43 * @constructor |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
87 * Function used to create grid items. | 87 * Function used to create grid items. |
88 * @type {function(new:cr.ui.ListItem, *)} | 88 * @type {function(new:cr.ui.ListItem, *)} |
89 * @private | 89 * @private |
90 */ | 90 */ |
91 itemConstructor_: cr.ui.ListItem, | 91 itemConstructor_: cr.ui.ListItem, |
92 | 92 |
93 /** | 93 /** |
94 * Function used to create grid items. | 94 * Function used to create grid items. |
95 * @return {function(new:cr.ui.ListItem, *)} | 95 * @return {function(new:cr.ui.ListItem, *)} |
96 */ | 96 */ |
97 get itemConstructor() { | 97 get itemConstructor() { return this.itemConstructor_; }, |
98 return this.itemConstructor_; | |
99 }, | |
100 set itemConstructor(func) { | 98 set itemConstructor(func) { |
101 if (func != this.itemConstructor_) { | 99 if (func != this.itemConstructor_) { |
102 this.itemConstructor_ = func; | 100 this.itemConstructor_ = func; |
103 this.cachedItems_ = {}; | 101 this.cachedItems_ = {}; |
104 this.redraw(); | 102 this.redraw(); |
105 } | 103 } |
106 }, | 104 }, |
107 | 105 |
108 dataModel_: null, | 106 dataModel_: null, |
109 | 107 |
110 /** | 108 /** |
111 * The data model driving the list. | 109 * The data model driving the list. |
112 * @type {ArrayDataModel} | 110 * @type {ArrayDataModel} |
113 */ | 111 */ |
114 set dataModel(dataModel) { | 112 set dataModel(dataModel) { |
115 if (this.dataModel_ == dataModel) | 113 if (this.dataModel_ == dataModel) |
116 return; | 114 return; |
117 | 115 |
118 if (!this.boundHandleDataModelPermuted_) { | 116 if (!this.boundHandleDataModelPermuted_) { |
119 this.boundHandleDataModelPermuted_ = | 117 this.boundHandleDataModelPermuted_ = |
120 this.handleDataModelPermuted_.bind(this); | 118 this.handleDataModelPermuted_.bind(this); |
121 this.boundHandleDataModelChange_ = | 119 this.boundHandleDataModelChange_ = |
122 this.handleDataModelChange_.bind(this); | 120 this.handleDataModelChange_.bind(this); |
123 } | 121 } |
124 | 122 |
125 if (this.dataModel_) { | 123 if (this.dataModel_) { |
126 this.dataModel_.removeEventListener( | 124 this.dataModel_.removeEventListener( |
127 'permuted', | 125 'permuted', this.boundHandleDataModelPermuted_); |
128 this.boundHandleDataModelPermuted_); | 126 this.dataModel_.removeEventListener( |
129 this.dataModel_.removeEventListener('change', | 127 'change', this.boundHandleDataModelChange_); |
130 this.boundHandleDataModelChange_); | |
131 } | 128 } |
132 | 129 |
133 this.dataModel_ = dataModel; | 130 this.dataModel_ = dataModel; |
134 | 131 |
135 this.cachedItems_ = {}; | 132 this.cachedItems_ = {}; |
136 this.cachedItemHeights_ = {}; | 133 this.cachedItemHeights_ = {}; |
137 this.selectionModel.clear(); | 134 this.selectionModel.clear(); |
138 if (dataModel) | 135 if (dataModel) |
139 this.selectionModel.adjustLength(dataModel.length); | 136 this.selectionModel.adjustLength(dataModel.length); |
140 | 137 |
141 if (this.dataModel_) { | 138 if (this.dataModel_) { |
142 this.dataModel_.addEventListener( | 139 this.dataModel_.addEventListener( |
143 'permuted', | 140 'permuted', this.boundHandleDataModelPermuted_); |
144 this.boundHandleDataModelPermuted_); | 141 this.dataModel_.addEventListener( |
145 this.dataModel_.addEventListener('change', | 142 'change', this.boundHandleDataModelChange_); |
146 this.boundHandleDataModelChange_); | |
147 } | 143 } |
148 | 144 |
149 this.redraw(); | 145 this.redraw(); |
150 this.onSetDataModelComplete(); | 146 this.onSetDataModelComplete(); |
151 }, | 147 }, |
152 | 148 |
153 get dataModel() { | 149 get dataModel() { return this.dataModel_; }, |
154 return this.dataModel_; | |
155 }, | |
156 | 150 |
157 /** | 151 /** |
158 * Override to be notified when |this.dataModel| is set. | 152 * Override to be notified when |this.dataModel| is set. |
159 * @protected | 153 * @protected |
160 */ | 154 */ |
161 onSetDataModelComplete: function() { | 155 onSetDataModelComplete: function() {}, |
162 }, | |
163 | 156 |
164 /** | 157 /** |
165 * Cached item for measuring the default item size by measureItem(). | 158 * Cached item for measuring the default item size by measureItem(). |
166 * @type {cr.ui.ListItem} | 159 * @type {cr.ui.ListItem} |
167 */ | 160 */ |
168 cachedMeasuredItem_: null, | 161 cachedMeasuredItem_: null, |
169 | 162 |
170 /** | 163 /** |
171 * The selection model to use. | 164 * The selection model to use. |
172 * @type {cr.ui.ListSelectionModel} | 165 * @type {cr.ui.ListSelectionModel} |
173 */ | 166 */ |
174 get selectionModel() { | 167 get selectionModel() { return this.selectionModel_; }, |
175 return this.selectionModel_; | |
176 }, | |
177 set selectionModel(sm) { | 168 set selectionModel(sm) { |
178 var oldSm = this.selectionModel_; | 169 var oldSm = this.selectionModel_; |
179 if (oldSm == sm) | 170 if (oldSm == sm) |
180 return; | 171 return; |
181 | 172 |
182 if (!this.boundHandleOnChange_) { | 173 if (!this.boundHandleOnChange_) { |
183 this.boundHandleOnChange_ = this.handleOnChange_.bind(this); | 174 this.boundHandleOnChange_ = this.handleOnChange_.bind(this); |
184 this.boundHandleLeadChange_ = this.handleLeadChange.bind(this); | 175 this.boundHandleLeadChange_ = this.handleLeadChange.bind(this); |
185 } | 176 } |
186 | 177 |
187 if (oldSm) { | 178 if (oldSm) { |
188 oldSm.removeEventListener('change', this.boundHandleOnChange_); | 179 oldSm.removeEventListener('change', this.boundHandleOnChange_); |
189 oldSm.removeEventListener('leadIndexChange', | 180 oldSm.removeEventListener( |
190 this.boundHandleLeadChange_); | 181 'leadIndexChange', this.boundHandleLeadChange_); |
191 } | 182 } |
192 | 183 |
193 this.selectionModel_ = sm; | 184 this.selectionModel_ = sm; |
194 this.selectionController_ = this.createSelectionController(sm); | 185 this.selectionController_ = this.createSelectionController(sm); |
195 | 186 |
196 if (sm) { | 187 if (sm) { |
197 sm.addEventListener('change', this.boundHandleOnChange_); | 188 sm.addEventListener('change', this.boundHandleOnChange_); |
198 sm.addEventListener('leadIndexChange', this.boundHandleLeadChange_); | 189 sm.addEventListener('leadIndexChange', this.boundHandleLeadChange_); |
199 } | 190 } |
200 }, | 191 }, |
201 | 192 |
202 /** | 193 /** |
203 * Whether or not the list auto-expands. | 194 * Whether or not the list auto-expands. |
204 * @type {boolean} | 195 * @type {boolean} |
205 */ | 196 */ |
206 get autoExpands() { | 197 get autoExpands() { return this.autoExpands_; }, |
207 return this.autoExpands_; | |
208 }, | |
209 set autoExpands(autoExpands) { | 198 set autoExpands(autoExpands) { |
210 if (this.autoExpands_ == autoExpands) | 199 if (this.autoExpands_ == autoExpands) |
211 return; | 200 return; |
212 this.autoExpands_ = autoExpands; | 201 this.autoExpands_ = autoExpands; |
213 this.redraw(); | 202 this.redraw(); |
214 }, | 203 }, |
215 | 204 |
216 /** | 205 /** |
217 * Whether or not the rows on list have various heights. | 206 * Whether or not the rows on list have various heights. |
218 * @type {boolean} | 207 * @type {boolean} |
219 */ | 208 */ |
220 get fixedHeight() { | 209 get fixedHeight() { return this.fixedHeight_; }, |
221 return this.fixedHeight_; | |
222 }, | |
223 set fixedHeight(fixedHeight) { | 210 set fixedHeight(fixedHeight) { |
224 if (this.fixedHeight_ == fixedHeight) | 211 if (this.fixedHeight_ == fixedHeight) |
225 return; | 212 return; |
226 this.fixedHeight_ = fixedHeight; | 213 this.fixedHeight_ = fixedHeight; |
227 this.redraw(); | 214 this.redraw(); |
228 }, | 215 }, |
229 | 216 |
230 /** | 217 /** |
231 * Convenience alias for selectionModel.selectedItem | 218 * Convenience alias for selectionModel.selectedItem |
232 * @type {*} | 219 * @type {*} |
(...skipping 16 matching lines...) Expand all Loading... |
249 }, | 236 }, |
250 | 237 |
251 /** | 238 /** |
252 * Convenience alias for selectionModel.selectedItems | 239 * Convenience alias for selectionModel.selectedItems |
253 * @type {!Array<*>} | 240 * @type {!Array<*>} |
254 */ | 241 */ |
255 get selectedItems() { | 242 get selectedItems() { |
256 var indexes = this.selectionModel.selectedIndexes; | 243 var indexes = this.selectionModel.selectedIndexes; |
257 var dataModel = this.dataModel; | 244 var dataModel = this.dataModel; |
258 if (dataModel) { | 245 if (dataModel) { |
259 return indexes.map(function(i) { | 246 return indexes.map(function(i) { return dataModel.item(i); }); |
260 return dataModel.item(i); | |
261 }); | |
262 } | 247 } |
263 return []; | 248 return []; |
264 }, | 249 }, |
265 | 250 |
266 /** | 251 /** |
267 * The HTML elements representing the items. | 252 * The HTML elements representing the items. |
268 * @type {HTMLCollection} | 253 * @type {HTMLCollection} |
269 */ | 254 */ |
270 get items() { | 255 get items() { |
271 return Array.prototype.filter.call(this.children, | 256 return Array.prototype.filter.call(this.children, this.isItem, this); |
272 this.isItem, this); | |
273 }, | 257 }, |
274 | 258 |
275 /** | 259 /** |
276 * Returns true if the child is a list item. Subclasses may override this | 260 * Returns true if the child is a list item. Subclasses may override this |
277 * to filter out certain elements. | 261 * to filter out certain elements. |
278 * @param {Node} child Child of the list. | 262 * @param {Node} child Child of the list. |
279 * @return {boolean} True if a list item. | 263 * @return {boolean} True if a list item. |
280 */ | 264 */ |
281 isItem: function(child) { | 265 isItem: function(child) { |
282 return child.nodeType == Node.ELEMENT_NODE && | 266 return child.nodeType == Node.ELEMENT_NODE && |
283 child != this.beforeFiller_ && child != this.afterFiller_; | 267 child != this.beforeFiller_ && child != this.afterFiller_; |
284 }, | 268 }, |
285 | 269 |
286 batchCount_: 0, | 270 batchCount_: 0, |
287 | 271 |
288 /** | 272 /** |
289 * When making a lot of updates to the list, the code could be wrapped in | 273 * When making a lot of updates to the list, the code could be wrapped in |
290 * the startBatchUpdates and finishBatchUpdates to increase performance. Be | 274 * the startBatchUpdates and finishBatchUpdates to increase performance. Be |
291 * sure that the code will not return without calling endBatchUpdates or the | 275 * sure that the code will not return without calling endBatchUpdates or the |
292 * list will not be correctly updated. | 276 * list will not be correctly updated. |
293 */ | 277 */ |
294 startBatchUpdates: function() { | 278 startBatchUpdates: function() { this.batchCount_++; }, |
295 this.batchCount_++; | |
296 }, | |
297 | 279 |
298 /** | 280 /** |
299 * See startBatchUpdates. | 281 * See startBatchUpdates. |
300 */ | 282 */ |
301 endBatchUpdates: function() { | 283 endBatchUpdates: function() { |
302 this.batchCount_--; | 284 this.batchCount_--; |
303 if (this.batchCount_ == 0) | 285 if (this.batchCount_ == 0) |
304 this.redraw(); | 286 this.redraw(); |
305 }, | 287 }, |
306 | 288 |
(...skipping 87 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
394 * the first value in the model. | 376 * the first value in the model. |
395 * @return {{height: number, marginTop: number, marginBottom: number, | 377 * @return {{height: number, marginTop: number, marginBottom: number, |
396 * width: number, marginLeft: number, marginRight: number}} | 378 * width: number, marginLeft: number, marginRight: number}} |
397 * The height and width of the item, taking | 379 * The height and width of the item, taking |
398 * margins into account, and the top, bottom, left and right margins | 380 * margins into account, and the top, bottom, left and right margins |
399 * themselves. | 381 * themselves. |
400 */ | 382 */ |
401 measureItem: function(opt_item) { | 383 measureItem: function(opt_item) { |
402 var dataModel = this.dataModel; | 384 var dataModel = this.dataModel; |
403 if (!dataModel || !dataModel.length) { | 385 if (!dataModel || !dataModel.length) { |
404 return {height: 0, marginTop: 0, marginBottom: 0, | 386 return { |
405 width: 0, marginLeft: 0, marginRight: 0}; | 387 height: 0, |
| 388 marginTop: 0, |
| 389 marginBottom: 0, |
| 390 width: 0, |
| 391 marginLeft: 0, |
| 392 marginRight: 0 |
| 393 }; |
406 } | 394 } |
407 var item = opt_item || this.cachedMeasuredItem_ || | 395 var item = opt_item || this.cachedMeasuredItem_ || |
408 this.createItem(dataModel.item(0)); | 396 this.createItem(dataModel.item(0)); |
409 if (!opt_item) { | 397 if (!opt_item) { |
410 this.cachedMeasuredItem_ = item; | 398 this.cachedMeasuredItem_ = item; |
411 this.appendChild(item); | 399 this.appendChild(item); |
412 } | 400 } |
413 | 401 |
414 var rect = item.getBoundingClientRect(); | 402 var rect = item.getBoundingClientRect(); |
415 var cs = getComputedStyle(item); | 403 var cs = getComputedStyle(item); |
(...skipping 21 matching lines...) Expand all Loading... |
437 } else if (ml >= 0 && mr >= 0) { | 425 } else if (ml >= 0 && mr >= 0) { |
438 mh = Math.max(ml, mr); | 426 mh = Math.max(ml, mr); |
439 } else { | 427 } else { |
440 mh = ml + mr; | 428 mh = ml + mr; |
441 } | 429 } |
442 w += mh; | 430 w += mh; |
443 | 431 |
444 if (!opt_item) | 432 if (!opt_item) |
445 this.removeChild(item); | 433 this.removeChild(item); |
446 return { | 434 return { |
447 height: Math.max(0, h), | 435 height: Math.max(0, h), |
448 marginTop: mt, marginBottom: mb, | 436 marginTop: mt, |
449 width: Math.max(0, w), | 437 marginBottom: mb, |
450 marginLeft: ml, marginRight: mr}; | 438 width: Math.max(0, w), |
| 439 marginLeft: ml, |
| 440 marginRight: mr |
| 441 }; |
451 }, | 442 }, |
452 | 443 |
453 /** | 444 /** |
454 * Callback for the double click event. | 445 * Callback for the double click event. |
455 * @param {Event} e The mouse event object. | 446 * @param {Event} e The mouse event object. |
456 * @private | 447 * @private |
457 */ | 448 */ |
458 handleDoubleClick_: function(e) { | 449 handleDoubleClick_: function(e) { |
459 if (this.disabled) | 450 if (this.disabled) |
460 return; | 451 return; |
461 | 452 |
462 var target = /** @type {HTMLElement} */(e.target); | 453 var target = /** @type {HTMLElement} */ (e.target); |
463 | 454 |
464 var ancestor = this.getListItemAncestor(target); | 455 var ancestor = this.getListItemAncestor(target); |
465 var index = -1; | 456 var index = -1; |
466 if (ancestor) { | 457 if (ancestor) { |
467 index = this.getIndexOfListItem(ancestor); | 458 index = this.getIndexOfListItem(ancestor); |
468 this.activateItemAtIndex(index); | 459 this.activateItemAtIndex(index); |
469 } | 460 } |
470 | 461 |
471 var sm = this.selectionModel; | 462 var sm = this.selectionModel; |
472 var indexSelected = sm.getIndexSelected(index); | 463 var indexSelected = sm.getIndexSelected(index); |
473 if (!indexSelected) | 464 if (!indexSelected) |
474 this.handlePointerDownUp_(e); | 465 this.handlePointerDownUp_(e); |
475 }, | 466 }, |
476 | 467 |
477 /** | 468 /** |
478 * Callback for mousedown and mouseup events. | 469 * Callback for mousedown and mouseup events. |
479 * @param {Event} e The mouse event object. | 470 * @param {Event} e The mouse event object. |
480 * @private | 471 * @private |
481 */ | 472 */ |
482 handlePointerDownUp_: function(e) { | 473 handlePointerDownUp_: function(e) { |
483 if (this.disabled) | 474 if (this.disabled) |
484 return; | 475 return; |
485 | 476 |
486 var target = /** @type {HTMLElement} */(e.target); | 477 var target = /** @type {HTMLElement} */ (e.target); |
487 | 478 |
488 // If the target was this element we need to make sure that the user did | 479 // If the target was this element we need to make sure that the user did |
489 // not click on a border or a scrollbar. | 480 // not click on a border or a scrollbar. |
490 if (target == this) { | 481 if (target == this) { |
491 if (inViewport(target, e)) | 482 if (inViewport(target, e)) |
492 this.selectionController_.handlePointerDownUp(e, -1); | 483 this.selectionController_.handlePointerDownUp(e, -1); |
493 return; | 484 return; |
494 } | 485 } |
495 | 486 |
496 target = this.getListItemAncestor(target); | 487 target = this.getListItemAncestor(target); |
(...skipping 189 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
686 } | 677 } |
687 return top; | 678 return top; |
688 } | 679 } |
689 }, | 680 }, |
690 | 681 |
691 /** | 682 /** |
692 * @param {number} index The index of the item. | 683 * @param {number} index The index of the item. |
693 * @return {number} The row of the item. May vary in the case | 684 * @return {number} The row of the item. May vary in the case |
694 * of multiple columns. | 685 * of multiple columns. |
695 */ | 686 */ |
696 getItemRow: function(index) { | 687 getItemRow: function(index) { return index; }, |
697 return index; | |
698 }, | |
699 | 688 |
700 /** | 689 /** |
701 * @param {number} row The row. | 690 * @param {number} row The row. |
702 * @return {number} The index of the first item in the row. | 691 * @return {number} The index of the first item in the row. |
703 */ | 692 */ |
704 getFirstItemInRow: function(row) { | 693 getFirstItemInRow: function(row) { return row; }, |
705 return row; | |
706 }, | |
707 | 694 |
708 /** | 695 /** |
709 * Ensures that a given index is inside the viewport. | 696 * Ensures that a given index is inside the viewport. |
710 * @param {number} index The index of the item to scroll into view. | 697 * @param {number} index The index of the item to scroll into view. |
711 */ | 698 */ |
712 scrollIndexIntoView: function(index) { | 699 scrollIndexIntoView: function(index) { |
713 var dataModel = this.dataModel; | 700 var dataModel = this.dataModel; |
714 if (!dataModel || index < 0 || index >= dataModel.length) | 701 if (!dataModel || index < 0 || index >= dataModel.length) |
715 return; | 702 return; |
716 | 703 |
717 var itemHeight = this.getItemHeightByIndex_(index); | 704 var itemHeight = this.getItemHeightByIndex_(index); |
718 var scrollTop = this.scrollTop; | 705 var scrollTop = this.scrollTop; |
719 var top = this.getItemTop(index); | 706 var top = this.getItemTop(index); |
720 var clientHeight = this.clientHeight; | 707 var clientHeight = this.clientHeight; |
721 | 708 |
722 var cs = getComputedStyle(this); | 709 var cs = getComputedStyle(this); |
723 var paddingY = parseInt(cs.paddingTop, 10) + | 710 var paddingY = |
724 parseInt(cs.paddingBottom, 10); | 711 parseInt(cs.paddingTop, 10) + parseInt(cs.paddingBottom, 10); |
725 var availableHeight = clientHeight - paddingY; | 712 var availableHeight = clientHeight - paddingY; |
726 | 713 |
727 var self = this; | 714 var self = this; |
728 // Function to adjust the tops of viewport and row. | 715 // Function to adjust the tops of viewport and row. |
729 function scrollToAdjustTop() { | 716 function scrollToAdjustTop() { self.scrollTop = top; } |
730 self.scrollTop = top; | |
731 } | |
732 // Function to adjust the bottoms of viewport and row. | 717 // Function to adjust the bottoms of viewport and row. |
733 function scrollToAdjustBottom() { | 718 function scrollToAdjustBottom() { |
734 self.scrollTop = top + itemHeight - availableHeight; | 719 self.scrollTop = top + itemHeight - availableHeight; |
735 } | 720 } |
736 | 721 |
737 // Check if the entire of given indexed row can be shown in the viewport. | 722 // Check if the entire of given indexed row can be shown in the viewport. |
738 if (itemHeight <= availableHeight) { | 723 if (itemHeight <= availableHeight) { |
739 if (top < scrollTop) | 724 if (top < scrollTop) |
740 scrollToAdjustTop(); | 725 scrollToAdjustTop(); |
741 else if (scrollTop + availableHeight < top + itemHeight) | 726 else if (scrollTop + availableHeight < top + itemHeight) |
(...skipping 113 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
855 // If offset exceeds the height of list. | 840 // If offset exceeds the height of list. |
856 var lastHeight = 0; | 841 var lastHeight = 0; |
857 if (this.dataModel.length) { | 842 if (this.dataModel.length) { |
858 var h = this.getHeightsForIndex_(this.dataModel.length - 1); | 843 var h = this.getHeightsForIndex_(this.dataModel.length - 1); |
859 lastHeight = h.top + h.height; | 844 lastHeight = h.top + h.height; |
860 } | 845 } |
861 if (lastHeight < offset) | 846 if (lastHeight < offset) |
862 return this.dataModel.length; | 847 return this.dataModel.length; |
863 | 848 |
864 // Estimates index. | 849 // Estimates index. |
865 var estimatedIndex = Math.min(Math.floor(offset / itemHeight), | 850 var estimatedIndex = |
866 this.dataModel.length - 1); | 851 Math.min(Math.floor(offset / itemHeight), this.dataModel.length - 1); |
867 var isIncrementing = this.getItemTop(estimatedIndex) < offset; | 852 var isIncrementing = this.getItemTop(estimatedIndex) < offset; |
868 | 853 |
869 // Searchs the correct index. | 854 // Searchs the correct index. |
870 do { | 855 do { |
871 var heights = this.getHeightsForIndex_(estimatedIndex); | 856 var heights = this.getHeightsForIndex_(estimatedIndex); |
872 var top = heights.top; | 857 var top = heights.top; |
873 var height = heights.height; | 858 var height = heights.height; |
874 | 859 |
875 if (top <= offset && offset <= (top + height)) | 860 if (top <= offset && offset <= (top + height)) |
876 break; | 861 break; |
(...skipping 22 matching lines...) Expand all Loading... |
899 * @param {number} scrollTop The scroll top position. | 884 * @param {number} scrollTop The scroll top position. |
900 * @param {number} clientHeight The height of viewport. | 885 * @param {number} clientHeight The height of viewport. |
901 * @return {{first: number, length: number, last: number}} The index of | 886 * @return {{first: number, length: number, last: number}} The index of |
902 * first item in view port, The number of items, The item past the last. | 887 * first item in view port, The number of items, The item past the last. |
903 */ | 888 */ |
904 getItemsInViewPort: function(scrollTop, clientHeight) { | 889 getItemsInViewPort: function(scrollTop, clientHeight) { |
905 if (this.autoExpands_) { | 890 if (this.autoExpands_) { |
906 return { | 891 return { |
907 first: 0, | 892 first: 0, |
908 length: this.dataModel.length, | 893 length: this.dataModel.length, |
909 last: this.dataModel.length}; | 894 last: this.dataModel.length |
| 895 }; |
910 } else { | 896 } else { |
911 var firstIndex = this.getIndexForListOffset_(scrollTop); | 897 var firstIndex = this.getIndexForListOffset_(scrollTop); |
912 var lastIndex = this.getIndexForListOffset_(scrollTop + clientHeight); | 898 var lastIndex = this.getIndexForListOffset_(scrollTop + clientHeight); |
913 | 899 |
914 return { | 900 return { |
915 first: firstIndex, | 901 first: firstIndex, |
916 length: lastIndex - firstIndex + 1, | 902 length: lastIndex - firstIndex + 1, |
917 last: lastIndex + 1}; | 903 last: lastIndex + 1 |
| 904 }; |
918 } | 905 } |
919 }, | 906 }, |
920 | 907 |
921 /** | 908 /** |
922 * Merges list items currently existing in the list with items in the range | 909 * Merges list items currently existing in the list with items in the range |
923 * [firstIndex, lastIndex). Removes or adds items if needed. | 910 * [firstIndex, lastIndex). Removes or adds items if needed. |
924 * Doesn't delete {@code this.pinnedItem_} if it is present (instead hides | 911 * Doesn't delete {@code this.pinnedItem_} if it is present (instead hides |
925 * it if it is out of the range). | 912 * it if it is out of the range). |
926 * @param {number} firstIndex The index of first item, inclusively. | 913 * @param {number} firstIndex The index of first item, inclusively. |
927 * @param {number} lastIndex The index of last item, exclusively. | 914 * @param {number} lastIndex The index of last item, exclusively. |
928 */ | 915 */ |
929 mergeItems: function(firstIndex, lastIndex) { | 916 mergeItems: function(firstIndex, lastIndex) { |
930 var self = this; | 917 var self = this; |
931 var dataModel = this.dataModel; | 918 var dataModel = this.dataModel; |
932 var currentIndex = firstIndex; | 919 var currentIndex = firstIndex; |
933 | 920 |
934 function insert() { | 921 function insert() { |
935 var dataItem = dataModel.item(currentIndex); | 922 var dataItem = dataModel.item(currentIndex); |
936 var newItem = self.cachedItems_[currentIndex] || | 923 var newItem = |
937 self.createItem(dataItem); | 924 self.cachedItems_[currentIndex] || self.createItem(dataItem); |
938 newItem.listIndex = currentIndex; | 925 newItem.listIndex = currentIndex; |
939 self.cachedItems_[currentIndex] = newItem; | 926 self.cachedItems_[currentIndex] = newItem; |
940 self.insertBefore(newItem, item); | 927 self.insertBefore(newItem, item); |
941 currentIndex++; | 928 currentIndex++; |
942 } | 929 } |
943 | 930 |
944 function remove() { | 931 function remove() { |
945 var next = item.nextSibling; | 932 var next = item.nextSibling; |
946 if (item != self.pinnedItem_) | 933 if (item != self.pinnedItem_) |
947 self.removeChild(item); | 934 self.removeChild(item); |
(...skipping 128 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1076 // interleave measuring with mutating dom. | 1063 // interleave measuring with mutating dom. |
1077 if (!this.fixedHeight_) | 1064 if (!this.fixedHeight_) |
1078 this.ensureAllItemSizesInCache(); | 1065 this.ensureAllItemSizesInCache(); |
1079 | 1066 |
1080 var autoExpands = this.autoExpands_; | 1067 var autoExpands = this.autoExpands_; |
1081 | 1068 |
1082 var itemsInViewPort = this.getItemsInViewPort(scrollTop, clientHeight); | 1069 var itemsInViewPort = this.getItemsInViewPort(scrollTop, clientHeight); |
1083 // Draws the hidden rows just above/below the viewport to prevent | 1070 // Draws the hidden rows just above/below the viewport to prevent |
1084 // flashing in scroll. | 1071 // flashing in scroll. |
1085 var firstIndex = Math.max( | 1072 var firstIndex = Math.max( |
1086 0, | 1073 0, Math.min(dataModel.length - 1, itemsInViewPort.first - 1)); |
1087 Math.min(dataModel.length - 1, itemsInViewPort.first - 1)); | |
1088 var lastIndex = Math.min(itemsInViewPort.last + 1, dataModel.length); | 1074 var lastIndex = Math.min(itemsInViewPort.last + 1, dataModel.length); |
1089 | 1075 |
1090 var beforeFillerHeight = | 1076 var beforeFillerHeight = |
1091 this.autoExpands ? 0 : this.getItemTop(firstIndex); | 1077 this.autoExpands ? 0 : this.getItemTop(firstIndex); |
1092 var afterFillerHeight = | 1078 var afterFillerHeight = |
1093 this.autoExpands ? 0 : this.getAfterFillerHeight(lastIndex); | 1079 this.autoExpands ? 0 : this.getAfterFillerHeight(lastIndex); |
1094 | 1080 |
1095 this.beforeFiller_.style.height = beforeFillerHeight + 'px'; | 1081 this.beforeFiller_.style.height = beforeFillerHeight + 'px'; |
1096 | 1082 |
1097 var sm = this.selectionModel; | 1083 var sm = this.selectionModel; |
(...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1181 delete this.cachedItems_[index]; | 1167 delete this.cachedItems_[index]; |
1182 this.redraw(); | 1168 this.redraw(); |
1183 } | 1169 } |
1184 }, | 1170 }, |
1185 | 1171 |
1186 /** | 1172 /** |
1187 * Called when a list item is activated, currently only by a double click | 1173 * Called when a list item is activated, currently only by a double click |
1188 * event. | 1174 * event. |
1189 * @param {number} index The index of the activated item. | 1175 * @param {number} index The index of the activated item. |
1190 */ | 1176 */ |
1191 activateItemAtIndex: function(index) { | 1177 activateItemAtIndex: function(index) {}, |
1192 }, | |
1193 | 1178 |
1194 /** | 1179 /** |
1195 * Returns a ListItem for the leadIndex. If the item isn't present in the | 1180 * Returns a ListItem for the leadIndex. If the item isn't present in the |
1196 * list creates it and inserts to the list (may be invisible if it's out of | 1181 * list creates it and inserts to the list (may be invisible if it's out of |
1197 * the visible range). | 1182 * the visible range). |
1198 * | 1183 * |
1199 * Item returned from this method won't be removed until it remains a lead | 1184 * Item returned from this method won't be removed until it remains a lead |
1200 * item or till the data model changes (unlike other items that could be | 1185 * item or till the data model changes (unlike other items that could be |
1201 * removed when they go out of the visible range). | 1186 * removed when they go out of the visible range). |
1202 * | 1187 * |
1203 * @return {cr.ui.ListItem} The lead item for the list. | 1188 * @return {cr.ui.ListItem} The lead item for the list. |
1204 */ | 1189 */ |
1205 ensureLeadItemExists: function() { | 1190 ensureLeadItemExists: function() { |
1206 var index = this.selectionModel.leadIndex; | 1191 var index = this.selectionModel.leadIndex; |
1207 if (index < 0) | 1192 if (index < 0) |
1208 return null; | 1193 return null; |
1209 var cachedItems = this.cachedItems_ || {}; | 1194 var cachedItems = this.cachedItems_ || {}; |
1210 | 1195 |
1211 var item = cachedItems[index] || | 1196 var item = |
1212 this.createItem(this.dataModel.item(index)); | 1197 cachedItems[index] || this.createItem(this.dataModel.item(index)); |
1213 if (this.pinnedItem_ != item && this.pinnedItem_ && | 1198 if (this.pinnedItem_ != item && this.pinnedItem_ && |
1214 this.pinnedItem_.hidden) { | 1199 this.pinnedItem_.hidden) { |
1215 this.removeChild(this.pinnedItem_); | 1200 this.removeChild(this.pinnedItem_); |
1216 } | 1201 } |
1217 this.pinnedItem_ = item; | 1202 this.pinnedItem_ = item; |
1218 cachedItems[index] = item; | 1203 cachedItems[index] = item; |
1219 item.listIndex = index; | 1204 item.listIndex = index; |
1220 if (item.parentNode == this) | 1205 if (item.parentNode == this) |
1221 return item; | 1206 return item; |
1222 | 1207 |
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1275 * that point even though it doesn't actually have the page focus. | 1260 * that point even though it doesn't actually have the page focus. |
1276 */ | 1261 */ |
1277 cr.defineProperty(List, 'hasElementFocus', cr.PropertyKind.BOOL_ATTR); | 1262 cr.defineProperty(List, 'hasElementFocus', cr.PropertyKind.BOOL_ATTR); |
1278 | 1263 |
1279 /** | 1264 /** |
1280 * Mousedown event handler. | 1265 * Mousedown event handler. |
1281 * @this {cr.ui.List} | 1266 * @this {cr.ui.List} |
1282 * @param {Event} e The mouse event object. | 1267 * @param {Event} e The mouse event object. |
1283 */ | 1268 */ |
1284 function handleMouseDown(e) { | 1269 function handleMouseDown(e) { |
1285 e.target = /** @type {!HTMLElement} */(e.target); | 1270 e.target = /** @type {!HTMLElement} */ (e.target); |
1286 var listItem = this.getListItemAncestor(e.target); | 1271 var listItem = this.getListItemAncestor(e.target); |
1287 var wasSelected = listItem && listItem.selected; | 1272 var wasSelected = listItem && listItem.selected; |
1288 this.handlePointerDownUp_(e); | 1273 this.handlePointerDownUp_(e); |
1289 | 1274 |
1290 if (e.defaultPrevented || e.button != 0) | 1275 if (e.defaultPrevented || e.button != 0) |
1291 return; | 1276 return; |
1292 | 1277 |
1293 // The following hack is required only if the listItem gets selected. | 1278 // The following hack is required only if the listItem gets selected. |
1294 if (!listItem || wasSelected || !listItem.selected) | 1279 if (!listItem || wasSelected || !listItem.selected) |
1295 return; | 1280 return; |
(...skipping 11 matching lines...) Expand all Loading... |
1307 } | 1292 } |
1308 | 1293 |
1309 /** | 1294 /** |
1310 * Dragstart event handler. | 1295 * Dragstart event handler. |
1311 * If there is an item at starting position of drag operation and the item | 1296 * If there is an item at starting position of drag operation and the item |
1312 * is not selected, select it. | 1297 * is not selected, select it. |
1313 * @this {cr.ui.List} | 1298 * @this {cr.ui.List} |
1314 * @param {Event} e The event object for 'dragstart'. | 1299 * @param {Event} e The event object for 'dragstart'. |
1315 */ | 1300 */ |
1316 function handleDragStart(e) { | 1301 function handleDragStart(e) { |
1317 e = /** @type {MouseEvent} */(e); | 1302 e = /** @type {MouseEvent} */ (e); |
1318 var element = e.target.ownerDocument.elementFromPoint(e.clientX, e.clientY); | 1303 var element = e.target.ownerDocument.elementFromPoint(e.clientX, e.clientY); |
1319 var listItem = this.getListItemAncestor(element); | 1304 var listItem = this.getListItemAncestor(element); |
1320 if (!listItem) | 1305 if (!listItem) |
1321 return; | 1306 return; |
1322 | 1307 |
1323 var index = this.getIndexOfListItem(listItem); | 1308 var index = this.getIndexOfListItem(listItem); |
1324 if (index == -1) | 1309 if (index == -1) |
1325 return; | 1310 return; |
1326 | 1311 |
1327 var isAlreadySelected = this.selectionModel_.getIndexSelected(index); | 1312 var isAlreadySelected = this.selectionModel_.getIndexSelected(index); |
1328 if (!isAlreadySelected) | 1313 if (!isAlreadySelected) |
1329 this.selectionModel_.selectedIndex = index; | 1314 this.selectionModel_.selectedIndex = index; |
1330 } | 1315 } |
1331 | 1316 |
1332 /** | 1317 /** |
1333 * Check if |start| or its ancestor under |root| is focusable. | 1318 * Check if |start| or its ancestor under |root| is focusable. |
1334 * This is a helper for handleMouseDown. | 1319 * This is a helper for handleMouseDown. |
1335 * @param {!Element} start An element which we start to check. | 1320 * @param {!Element} start An element which we start to check. |
1336 * @param {!Element} root An element which we finish to check. | 1321 * @param {!Element} root An element which we finish to check. |
1337 * @return {boolean} True if we found a focusable element. | 1322 * @return {boolean} True if we found a focusable element. |
1338 */ | 1323 */ |
1339 function containsFocusableElement(start, root) { | 1324 function containsFocusableElement(start, root) { |
1340 for (var element = start; element && element != root; | 1325 for (var element = start; element && element != root; |
1341 element = element.parentElement) { | 1326 element = element.parentElement) { |
1342 if (element.tabIndex >= 0 && !element.disabled) | 1327 if (element.tabIndex >= 0 && !element.disabled) |
1343 return true; | 1328 return true; |
1344 } | 1329 } |
1345 return false; | 1330 return false; |
1346 } | 1331 } |
1347 | 1332 |
1348 return { | 1333 return {List: List}; |
1349 List: List | |
1350 }; | |
1351 }); | 1334 }); |
OLD | NEW |