• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
5cr.define('cr.ui', function() {
6  const Event = cr.Event;
7  const EventTarget = cr.EventTarget;
8
9  /**
10   * Creates a new selection model that is to be used with lists. This only
11   * allows a single index to be selected.
12   *
13   * @param {number=} opt_length The number items in the selection.
14   *
15   * @constructor
16   * @extends {!cr.EventTarget}
17   */
18  function ListSingleSelectionModel(opt_length) {
19    this.length_ = opt_length || 0;
20    this.selectedIndex = -1;
21  }
22
23  ListSingleSelectionModel.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      var i = this.selectedIndex;
39      return i != -1 ? [this.selectedIndex] : [];
40    },
41
42    /**
43     * Convenience getter which returns the first selected index.
44     * @type {number}
45     */
46    get selectedIndex() {
47      return this.selectedIndex_;
48    },
49    set selectedIndex(selectedIndex) {
50      var oldSelectedIndex = this.selectedIndex;
51      var i = Math.max(-1, Math.min(this.length_ - 1, selectedIndex));
52
53      if (i != oldSelectedIndex) {
54        this.beginChange();
55        this.selectedIndex_ = i
56        this.endChange();
57      }
58    },
59
60    /**
61     * Selects a range of indexes, starting with {@code start} and ends with
62     * {@code end}.
63     * @param {number} start The first index to select.
64     * @param {number} end The last index to select.
65     */
66    selectRange: function(start, end) {
67      // Only select first index.
68      this.selectedIndex = Math.min(start, end);
69    },
70
71    /**
72     * Selects all indexes.
73     */
74    selectAll: function() {
75      // Select all is not allowed on a single selection model
76    },
77
78    /**
79     * Clears the selection
80     */
81    clear: function() {
82      this.beginChange();
83      this.length_ = 0;
84      this.selectedIndex = this.anchorIndex = this.leadIndex = -1;
85      this.endChange();
86    },
87
88    /**
89     * Unselects all selected items.
90     */
91    unselectAll: function() {
92      this.selectedIndex = -1;
93    },
94
95    /**
96     * Sets the selected state for an index.
97     * @param {number} index The index to set the selected state for.
98     * @param {boolean} b Whether to select the index or not.
99     */
100    setIndexSelected: function(index, b) {
101      // Only allow selection
102      var oldSelected = index == this.selectedIndex_;
103      if (oldSelected == b)
104        return;
105
106      if (b)
107        this.selectedIndex = index;
108      else if (index == this.selectedIndex_)
109        this.selectedIndex = -1;
110    },
111
112    /**
113     * Whether a given index is selected or not.
114     * @param {number} index The index to check.
115     * @return {boolean} Whether an index is selected.
116     */
117    getIndexSelected: function(index) {
118      return index == this.selectedIndex_;
119    },
120
121    /**
122     * This is used to begin batching changes. Call {@code endChange} when you
123     * are done making changes.
124     */
125    beginChange: function() {
126      if (!this.changeCount_) {
127        this.changeCount_ = 0;
128        this.selectedIndexBefore_ = this.selectedIndex_;
129      }
130      this.changeCount_++;
131    },
132
133    /**
134     * Call this after changes are done and it will dispatch a change event if
135     * any changes were actually done.
136     */
137    endChange: function() {
138      this.changeCount_--;
139      if (!this.changeCount_) {
140        if (this.selectedIndexBefore_ != this.selectedIndex_) {
141          var e = new Event('change');
142          var indexes = [this.selectedIndexBefore_, this.selectedIndex_];
143          e.changes = indexes.filter(function(index) {
144            return index != -1;
145          }).map(function(index) {
146            return {
147              index: index,
148              selected: index == this.selectedIndex_
149            };
150          }, this);
151          this.dispatchEvent(e);
152        }
153      }
154    },
155
156    leadIndex_: -1,
157
158    /**
159     * The leadIndex is used with multiple selection and it is the index that
160     * the user is moving using the arrow keys.
161     * @type {number}
162     */
163    get leadIndex() {
164      return this.leadIndex_;
165    },
166    set leadIndex(leadIndex) {
167      var li = Math.max(-1, Math.min(this.length_ - 1, leadIndex));
168      if (li != this.leadIndex_) {
169        var oldLeadIndex = this.leadIndex_;
170        this.leadIndex_ = li;
171        cr.dispatchPropertyChange(this, 'leadIndex', li, oldLeadIndex);
172        cr.dispatchPropertyChange(this, 'anchorIndex', li, oldLeadIndex);
173      }
174    },
175
176    /**
177     * The anchorIndex is used with multiple selection.
178     * @type {number}
179     */
180    get anchorIndex() {
181      return this.leadIndex;
182    },
183    set anchorIndex(anchorIndex) {
184      this.leadIndex = anchorIndex;
185    },
186
187    /**
188     * Whether the selection model supports multiple selected items.
189     * @type {boolean}
190     */
191    get multiple() {
192      return false;
193    },
194
195    /**
196     * Adjusts the selection after reordering of items in the table.
197     * @param {!Array.<number>} permutation The reordering permutation.
198     */
199    adjustToReordering: function(permutation) {
200    },
201
202    /**
203     * Adjust the selection by adding or removing a certain numbers of items.
204     * This should be called by the owner of the selection model as items are
205     * added and removed from the underlying data model.
206     * @param {number} index The index of the first change.
207     * @param {number} itemsRemoved Number of items removed.
208     * @param {number} itemsAdded Number of items added.
209     */
210    adjust: function(index, itemsRemoved, itemsAdded) {
211      function getNewAdjustedIndex(i) {
212        if (i >= index && i < index + itemsRemoved) {
213          return index;
214        } else if (i >= index) {
215          return i + itemsAdded - itemsRemoved;
216        }
217        return i;
218      }
219
220      this.length_ += itemsAdded - itemsRemoved;
221
222      var i = this.selectedIndex;
223      if (itemsRemoved > 0 && i >= index && i < index + itemsRemoved)
224        this.selectedIndex = -1;
225      else if (i >= index)
226        this.selectedIndex = i + itemsAdded - itemsRemoved;
227
228      this.leadIndex = getNewAdjustedIndex(this.leadIndex);
229    }
230  };
231
232  return {
233    ListSingleSelectionModel: ListSingleSelectionModel
234  };
235});
236