- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / common / extensions / docs / examples / extensions / plugin_settings / domui / js / cr / ui / list_selection_model.js
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.
4
5 cr.define('cr.ui', function() {
6   /** @const */ var EventTarget = cr.EventTarget;
7
8   /**
9    * Creates a new selection model that is to be used with lists.
10    *
11    * @param {number=} opt_length The number items in the selection.
12    *
13    * @constructor
14    * @extends {!cr.EventTarget}
15    */
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_ = [];
21   }
22
23   ListSelectionModel.prototype = {
24     __proto__: EventTarget.prototype,
25
26     /**
27      * The number of items in the model.
28      * @type {number}
29      */
30     get length() {
31       return this.length_;
32     },
33
34     /**
35      * @type {!Array} The selected indexes.
36      */
37     get selectedIndexes() {
38       return Object.keys(this.selectedIndexes_).map(Number);
39     },
40     set selectedIndexes(selectedIndexes) {
41       this.beginChange();
42       this.unselectAll();
43       for (var i = 0; i < selectedIndexes.length; i++) {
44         this.setIndexSelected(selectedIndexes[i], true);
45       }
46       if (selectedIndexes.length) {
47         this.leadIndex = this.anchorIndex = selectedIndexes[0];
48       } else {
49         this.leadIndex = this.anchorIndex = -1;
50       }
51       this.endChange();
52     },
53
54     /**
55      * Convenience getter which returns the first selected index.
56      * @type {number}
57      */
58     get selectedIndex() {
59       for (var i in this.selectedIndexes_) {
60         return Number(i);
61       }
62       return -1;
63     },
64     set selectedIndex(selectedIndex) {
65       this.beginChange();
66       this.unselectAll();
67       if (selectedIndex != -1) {
68         this.selectedIndexes = [selectedIndex];
69       } else {
70         this.leadIndex = this.anchorIndex = -1;
71       }
72       this.endChange();
73     },
74
75     /**
76      * Selects a range of indexes, starting with {@code start} and ends with
77      * {@code end}.
78      * @param {number} start The first index to select.
79      * @param {number} end The last index to select.
80      */
81     selectRange: function(start, end) {
82       // Swap if starts comes after end.
83       if (start > end) {
84         var tmp = start;
85         start = end;
86         end = tmp;
87       }
88
89       this.beginChange();
90
91       for (var index = start; index != end; index++) {
92         this.setIndexSelected(index, true);
93       }
94       this.setIndexSelected(end, true);
95
96       this.endChange();
97     },
98
99     /**
100      * Selects all indexes.
101      */
102     selectAll: function() {
103       this.selectRange(0, this.length - 1);
104     },
105
106     /**
107      * Clears the selection
108      */
109     clear: function() {
110       this.beginChange();
111       this.length_ = 0;
112       this.anchorIndex = this.leadIndex = -1;
113       this.unselectAll();
114       this.endChange();
115     },
116
117     /**
118      * Unselects all selected items.
119      */
120     unselectAll: function() {
121       this.beginChange();
122       for (var i in this.selectedIndexes_) {
123         this.setIndexSelected(i, false);
124       }
125       this.endChange();
126     },
127
128     /**
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.
132      */
133     setIndexSelected: function(index, b) {
134       var oldSelected = index in this.selectedIndexes_;
135       if (oldSelected == b)
136         return;
137
138       if (b)
139         this.selectedIndexes_[index] = true;
140       else
141         delete this.selectedIndexes_[index];
142
143       this.beginChange();
144
145       // Changing back?
146       if (index in this.changedIndexes_ && this.changedIndexes_[index] == !b) {
147         delete this.changedIndexes_[index];
148       } else {
149         this.changedIndexes_[index] = b;
150       }
151
152       // End change dispatches an event which in turn may update the view.
153       this.endChange();
154     },
155
156     /**
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.
160      */
161     getIndexSelected: function(index) {
162       return index in this.selectedIndexes_;
163     },
164
165     /**
166      * This is used to begin batching changes. Call {@code endChange} when you
167      * are done making changes.
168      */
169     beginChange: function() {
170       if (!this.changeCount_) {
171         this.changeCount_ = 0;
172         this.changedIndexes_ = {};
173       }
174       this.changeCount_++;
175     },
176
177     /**
178      * Call this after changes are done and it will dispatch a change event if
179      * any changes were actually done.
180      */
181     endChange: function() {
182       this.changeCount_--;
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) {
188             return {
189               index: index,
190               selected: this.changedIndexes_[index]
191             };
192           }, this);
193           this.dispatchEvent(e);
194         }
195         this.changedIndexes_ = {};
196       }
197     },
198
199     leadIndex_: -1,
200
201     /**
202      * The leadIndex is used with multiple selection and it is the index that
203      * the user is moving using the arrow keys.
204      * @type {number}
205      */
206     get leadIndex() {
207       return this.leadIndex_;
208     },
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);
215       }
216     },
217
218     anchorIndex_: -1,
219
220     /**
221      * The anchorIndex is used with multiple selection.
222      * @type {number}
223      */
224     get anchorIndex() {
225       return this.anchorIndex_;
226     },
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);
233       }
234     },
235
236     /**
237      * Whether the selection model supports multiple selected items.
238      * @type {boolean}
239      */
240     get multiple() {
241       return true;
242     },
243
244     /**
245      * Adjusts the selection after reordering of items in the table.
246      * @param {!Array.<number>} permutation The reordering permutation.
247      */
248     adjustToReordering: function(permutation) {
249       var oldLeadIndex = this.leadIndex;
250
251       var oldSelectedIndexes = this.selectedIndexes;
252       this.selectedIndexes = oldSelectedIndexes.map(function(oldIndex) {
253         return permutation[oldIndex];
254       }).filter(function(index) {
255         return index != -1;
256       });
257
258       if (oldLeadIndex != -1)
259         this.leadIndex = permutation[oldLeadIndex];
260     },
261
262     /**
263      * Adjusts selection model length.
264      * @param {number} length New selection model length.
265      */
266     adjustLength: function(length) {
267       this.length_ = length;
268     }
269   };
270
271   return {
272     ListSelectionModel: ListSelectionModel
273   };
274 });