1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 cr.define('cr.ui', function() {
6 /** @const */ var EventTarget = cr.EventTarget;
9 * Creates a new selection model that is to be used with lists.
11 * @param {number=} opt_length The number items in the selection.
14 * @extends {!cr.EventTarget}
16 function ListSelectionModel(opt_length) {
17 this.length_ = opt_length || 0;
18 // Even though selectedIndexes_ is really a map we use an array here to get
19 // iteration in the order of the indexes.
20 this.selectedIndexes_ = [];
23 ListSelectionModel.prototype = {
24 __proto__: EventTarget.prototype,
27 * The number of items in the model.
35 * @type {!Array} The selected indexes.
37 get selectedIndexes() {
38 return Object.keys(this.selectedIndexes_).map(Number);
40 set selectedIndexes(selectedIndexes) {
43 for (var i = 0; i < selectedIndexes.length; i++) {
44 this.setIndexSelected(selectedIndexes[i], true);
46 if (selectedIndexes.length) {
47 this.leadIndex = this.anchorIndex = selectedIndexes[0];
49 this.leadIndex = this.anchorIndex = -1;
55 * Convenience getter which returns the first selected index.
59 for (var i in this.selectedIndexes_) {
64 set selectedIndex(selectedIndex) {
67 if (selectedIndex != -1) {
68 this.selectedIndexes = [selectedIndex];
70 this.leadIndex = this.anchorIndex = -1;
76 * Selects a range of indexes, starting with {@code start} and ends with
78 * @param {number} start The first index to select.
79 * @param {number} end The last index to select.
81 selectRange: function(start, end) {
82 // Swap if starts comes after end.
91 for (var index = start; index != end; index++) {
92 this.setIndexSelected(index, true);
94 this.setIndexSelected(end, true);
100 * Selects all indexes.
102 selectAll: function() {
103 this.selectRange(0, this.length - 1);
107 * Clears the selection
112 this.anchorIndex = this.leadIndex = -1;
118 * Unselects all selected items.
120 unselectAll: function() {
122 for (var i in this.selectedIndexes_) {
123 this.setIndexSelected(i, false);
129 * Sets the selected state for an index.
130 * @param {number} index The index to set the selected state for.
131 * @param {boolean} b Whether to select the index or not.
133 setIndexSelected: function(index, b) {
134 var oldSelected = index in this.selectedIndexes_;
135 if (oldSelected == b)
139 this.selectedIndexes_[index] = true;
141 delete this.selectedIndexes_[index];
146 if (index in this.changedIndexes_ && this.changedIndexes_[index] == !b) {
147 delete this.changedIndexes_[index];
149 this.changedIndexes_[index] = b;
152 // End change dispatches an event which in turn may update the view.
157 * Whether a given index is selected or not.
158 * @param {number} index The index to check.
159 * @return {boolean} Whether an index is selected.
161 getIndexSelected: function(index) {
162 return index in this.selectedIndexes_;
166 * This is used to begin batching changes. Call {@code endChange} when you
167 * are done making changes.
169 beginChange: function() {
170 if (!this.changeCount_) {
171 this.changeCount_ = 0;
172 this.changedIndexes_ = {};
178 * Call this after changes are done and it will dispatch a change event if
179 * any changes were actually done.
181 endChange: function() {
183 if (!this.changeCount_) {
184 var indexes = Object.keys(this.changedIndexes_);
185 if (indexes.length) {
186 var e = new Event('change');
187 e.changes = indexes.map(function(index) {
190 selected: this.changedIndexes_[index]
193 this.dispatchEvent(e);
195 this.changedIndexes_ = {};
202 * The leadIndex is used with multiple selection and it is the index that
203 * the user is moving using the arrow keys.
207 return this.leadIndex_;
209 set leadIndex(leadIndex) {
210 var li = Math.max(-1, Math.min(this.length_ - 1, leadIndex));
211 if (li != this.leadIndex_) {
212 var oldLeadIndex = this.leadIndex_;
213 this.leadIndex_ = li;
214 cr.dispatchPropertyChange(this, 'leadIndex', li, oldLeadIndex);
221 * The anchorIndex is used with multiple selection.
225 return this.anchorIndex_;
227 set anchorIndex(anchorIndex) {
228 var ai = Math.max(-1, Math.min(this.length_ - 1, anchorIndex));
229 if (ai != this.anchorIndex_) {
230 var oldAnchorIndex = this.anchorIndex_;
231 this.anchorIndex_ = ai;
232 cr.dispatchPropertyChange(this, 'anchorIndex', ai, oldAnchorIndex);
237 * Whether the selection model supports multiple selected items.
245 * Adjusts the selection after reordering of items in the table.
246 * @param {!Array.<number>} permutation The reordering permutation.
248 adjustToReordering: function(permutation) {
249 var oldLeadIndex = this.leadIndex;
251 var oldSelectedIndexes = this.selectedIndexes;
252 this.selectedIndexes = oldSelectedIndexes.map(function(oldIndex) {
253 return permutation[oldIndex];
254 }).filter(function(index) {
258 if (oldLeadIndex != -1)
259 this.leadIndex = permutation[oldLeadIndex];
263 * Adjusts selection model length.
264 * @param {number} length New selection model length.
266 adjustLength: function(length) {
267 this.length_ = length;
272 ListSelectionModel: ListSelectionModel