15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)cr.define('cr.ui', function() {
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /** @const */ var Event = cr.Event;
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /** @const */ var EventTarget = cr.EventTarget;
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /**
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * Creates a new selection model that is to be used with lists.
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   *
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * @param {number=} opt_length The number items in the selection.
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   *
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * @constructor
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * @extends {!cr.EventTarget}
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   */
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  function ListSelectionModel(opt_length) {
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.length_ = opt_length || 0;
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Even though selectedIndexes_ is really a map we use an array here to get
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // iteration in the order of the indexes.
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.selectedIndexes_ = [];
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // True if any item could be lead or anchor. False if only selected ones.
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.independentLeadItem_ = !cr.isMac && !cr.isChromeOS;
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ListSelectionModel.prototype = {
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    __proto__: EventTarget.prototype,
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * The number of items in the model.
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @type {number}
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    get length() {
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return this.length_;
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * The selected indexes.
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Setter also changes lead and anchor indexes if value list is nonempty.
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @type {!Array}
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    get selectedIndexes() {
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return Object.keys(this.selectedIndexes_).map(Number);
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    set selectedIndexes(selectedIndexes) {
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.beginChange();
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var unselected = {};
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for (var index in this.selectedIndexes_) {
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        unselected[index] = true;
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for (var i = 0; i < selectedIndexes.length; i++) {
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var index = selectedIndexes[i];
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (index in this.selectedIndexes_) {
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          delete unselected[index];
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        } else {
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          this.selectedIndexes_[index] = true;
593240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch          // Mark the index as changed. If previously marked, then unmark,
603240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch          // since it just got reverted to the original state.
613240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch          if (index in this.changedIndexes_)
623240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch            delete this.changedIndexes_[index];
633240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch          else
643240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch            this.changedIndexes_[index] = true;
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for (var index in unselected) {
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        delete this.selectedIndexes_[index];
703240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch        // Mark the index as changed. If previously marked, then unmark,
713240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch        // since it just got reverted to the original state.
723240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch        if (index in this.changedIndexes_)
733240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch          delete this.changedIndexes_[index];
743240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch        else
753240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch          this.changedIndexes_[index] = false;
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (selectedIndexes.length) {
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.leadIndex = this.anchorIndex = selectedIndexes[0];
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } else {
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.leadIndex = this.anchorIndex = -1;
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.endChange();
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Convenience getter which returns the first selected index.
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Setter also changes lead and anchor indexes if value is nonnegative.
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @type {number}
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    get selectedIndex() {
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for (var i in this.selectedIndexes_) {
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return Number(i);
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return -1;
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    set selectedIndex(selectedIndex) {
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.selectedIndexes = selectedIndex != -1 ? [selectedIndex] : [];
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Returns the last selected index or -1 if no item selected.
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @type {number}
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    get lastSelectedIndex() {
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var result = -1;
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for (var i in this.selectedIndexes_) {
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        result = Math.max(result, Number(i));
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return result;
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Selects a range of indexes, starting with {@code start} and ends with
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * {@code end}.
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {number} start The first index to select.
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {number} end The last index to select.
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    selectRange: function(start, end) {
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Swap if starts comes after end.
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (start > end) {
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var tmp = start;
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        start = end;
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        end = tmp;
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.beginChange();
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for (var index = start; index != end; index++) {
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.setIndexSelected(index, true);
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.setIndexSelected(end, true);
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.endChange();
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Selects all indexes.
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    selectAll: function() {
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.selectRange(0, this.length - 1);
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Clears the selection
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    clear: function() {
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.beginChange();
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.length_ = 0;
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.anchorIndex = this.leadIndex = -1;
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.unselectAll();
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.endChange();
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Unselects all selected items.
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    unselectAll: function() {
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.beginChange();
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for (var i in this.selectedIndexes_) {
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.setIndexSelected(i, false);
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.endChange();
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Sets the selected state for an index.
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {number} index The index to set the selected state for.
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {boolean} b Whether to select the index or not.
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    setIndexSelected: function(index, b) {
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var oldSelected = index in this.selectedIndexes_;
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (oldSelected == b)
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return;
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (b)
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.selectedIndexes_[index] = true;
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      else
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        delete this.selectedIndexes_[index];
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.beginChange();
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.changedIndexes_[index] = b;
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // End change dispatches an event which in turn may update the view.
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.endChange();
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Whether a given index is selected or not.
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {number} index The index to check.
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @return {boolean} Whether an index is selected.
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    getIndexSelected: function(index) {
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return index in this.selectedIndexes_;
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * This is used to begin batching changes. Call {@code endChange} when you
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * are done making changes.
2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    beginChange: function() {
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (!this.changeCount_) {
2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.changeCount_ = 0;
2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.changedIndexes_ = {};
2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.oldLeadIndex_ = this.leadIndex_;
2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.oldAnchorIndex_ = this.anchorIndex_;
2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.changeCount_++;
2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Call this after changes are done and it will dispatch a change event if
2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * any changes were actually done.
2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    endChange: function() {
2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.changeCount_--;
2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (!this.changeCount_) {
2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // Calls delayed |dispatchPropertyChange|s, only when |leadIndex| or
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // |anchorIndex| has been actually changed in the batch.
2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.leadIndex_ = this.adjustIndex_(this.leadIndex_);
2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (this.leadIndex_ != this.oldLeadIndex_) {
2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          cr.dispatchPropertyChange(this, 'leadIndex',
2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                    this.leadIndex_, this.oldLeadIndex_);
2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.oldLeadIndex_ = null;
2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.anchorIndex_ = this.adjustIndex_(this.anchorIndex_);
2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (this.anchorIndex_ != this.oldAnchorIndex_) {
2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          cr.dispatchPropertyChange(this, 'anchorIndex',
2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                    this.anchorIndex_, this.oldAnchorIndex_);
2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.oldAnchorIndex_ = null;
2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var indexes = Object.keys(this.changedIndexes_);
2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (indexes.length) {
2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          var e = new Event('change');
2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          e.changes = indexes.map(function(index) {
2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            return {
2402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              index: Number(index),
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              selected: this.changedIndexes_[index]
2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            };
2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          }, this);
2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          this.dispatchEvent(e);
2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.changedIndexes_ = {};
2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    leadIndex_: -1,
2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    oldLeadIndex_: null,
2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * The leadIndex is used with multiple selection and it is the index that
2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * the user is moving using the arrow keys.
2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @type {number}
2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    get leadIndex() {
2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return this.leadIndex_;
2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    set leadIndex(leadIndex) {
2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var oldValue = this.leadIndex_;
2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var newValue = this.adjustIndex_(leadIndex);
2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.leadIndex_ = newValue;
2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Delays the call of dispatchPropertyChange if batch is running.
2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (!this.changeCount_ && newValue != oldValue)
2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        cr.dispatchPropertyChange(this, 'leadIndex', newValue, oldValue);
2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    anchorIndex_: -1,
2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    oldAnchorIndex_: null,
2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * The anchorIndex is used with multiple selection.
2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @type {number}
2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    get anchorIndex() {
2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return this.anchorIndex_;
2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    set anchorIndex(anchorIndex) {
2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var oldValue = this.anchorIndex_;
2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var newValue = this.adjustIndex_(anchorIndex);
2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.anchorIndex_ = newValue;
2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Delays the call of dispatchPropertyChange if batch is running.
2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (!this.changeCount_ && newValue != oldValue)
2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        cr.dispatchPropertyChange(this, 'anchorIndex', newValue, oldValue);
2875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
2885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
2905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Helper method that adjustes a value before assiging it to leadIndex or
2915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * anchorIndex.
2925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {number} index New value for leadIndex or anchorIndex.
2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @return {number} Corrected value.
2945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
2955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    adjustIndex_: function(index) {
2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      index = Math.max(-1, Math.min(this.length_ - 1, index));
2975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // On Mac and ChromeOS lead and anchor items are forced to be among
2985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // selected items. This rule is not enforces until end of batch update.
2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (!this.changeCount_ && !this.independentLeadItem_ &&
3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          !this.getIndexSelected(index)) {
3015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        index = this.lastSelectedIndex;
3025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return index;
3045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
3055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Whether the selection model supports multiple selected items.
3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @type {boolean}
3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    get multiple() {
3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return true;
3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
3155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Adjusts the selection after reordering of items in the table.
3165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {!Array.<number>} permutation The reordering permutation.
3175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
3185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    adjustToReordering: function(permutation) {
3195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.beginChange();
3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var oldLeadIndex = this.leadIndex;
3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var oldAnchorIndex = this.anchorIndex;
3222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      var oldSelectedItemsCount = this.selectedIndexes.length;
3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.selectedIndexes = this.selectedIndexes.map(function(oldIndex) {
3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return permutation[oldIndex];
3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }).filter(function(index) {
3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return index != -1;
3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      });
3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Will be adjusted in endChange.
3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (oldLeadIndex != -1)
3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.leadIndex = permutation[oldLeadIndex];
3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (oldAnchorIndex != -1)
3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.anchorIndex = permutation[oldAnchorIndex];
3352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (oldSelectedItemsCount && !this.selectedIndexes.length &&
3372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          this.length_) {
3382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        // All selected items are deleted. We move selection to next item of
3392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        // last selected item.
3402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        this.selectedIndexes = [Math.min(oldLeadIndex, this.length_ - 1)];
3412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
3422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.endChange();
3445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
3455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
3475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Adjusts selection model length.
3485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {number} length New selection model length.
3495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
3505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    adjustLength: function(length) {
3515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.length_ = length;
3525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
3535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  };
3545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return {
3565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ListSelectionModel: ListSelectionModel
3575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  };
3585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)});
359