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)// require: array_data_model.js
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// require: list_selection_model.js
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// require: list_selection_controller.js
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// require: list_item.js
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @fileoverview This implements a list control.
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)cr.define('cr.ui', function() {
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /** @const */ var ListSelectionModel = cr.ui.ListSelectionModel;
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /** @const */ var ListSelectionController = cr.ui.ListSelectionController;
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /** @const */ var ArrayDataModel = cr.ui.ArrayDataModel;
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /**
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * Whether a mouse event is inside the element viewport. This will return
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * false if the mouseevent was generated over a border or a scrollbar.
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * @param {!HTMLElement} el The element to test the event with.
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * @param {!Event} e The mouse event.
241320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci   * @return {boolean} Whether the mouse event was inside the viewport.
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   */
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  function inViewport(el, e) {
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var rect = el.getBoundingClientRect();
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var x = e.clientX;
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var y = e.clientY;
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return x >= rect.left + el.clientLeft &&
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)           x < rect.left + el.clientLeft + el.clientWidth &&
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)           y >= rect.top + el.clientTop &&
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)           y < rect.top + el.clientTop + el.clientHeight;
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  function getComputedStyle(el) {
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return el.ownerDocument.defaultView.getComputedStyle(el);
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /**
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * Creates a new list element.
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * @param {Object=} opt_propertyBag Optional properties.
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * @constructor
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * @extends {HTMLUListElement}
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   */
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var List = cr.ui.define('list');
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  List.prototype = {
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    __proto__: HTMLUListElement.prototype,
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Measured size of list items. This is lazily calculated the first time it
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * is needed. Note that lead item is allowed to have a different height, to
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * accommodate lists where a single item at a time can be expanded to show
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * more detail.
561320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci     * @type {?{height: number, marginTop: number, marginBottom: number,
571320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci     *     width: number, marginLeft: number, marginRight: number}}
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @private
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
601320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    measured_: null,
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Whether or not the list is autoexpanding. If true, the list resizes
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * its height to accomadate all children.
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @type {boolean}
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @private
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    autoExpands_: false,
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Whether or not the rows on list have various heights. If true, all the
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * rows have the same fixed height. Otherwise, each row resizes its height
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * to accommodate all contents.
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @type {boolean}
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @private
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    fixedHeight_: true,
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Whether or not the list view has a blank space below the last row.
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @type {boolean}
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @private
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    remainingSpace_: true,
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Function used to create grid items.
881320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci     * @type {function(new:cr.ui.ListItem, *)}
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @private
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    itemConstructor_: cr.ui.ListItem,
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Function used to create grid items.
951320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci     * @return {function(new:cr.ui.ListItem, Object)}
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    get itemConstructor() {
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return this.itemConstructor_;
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    set itemConstructor(func) {
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (func != this.itemConstructor_) {
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.itemConstructor_ = func;
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.cachedItems_ = {};
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.redraw();
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    dataModel_: null,
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * The data model driving the list.
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @type {ArrayDataModel}
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    set dataModel(dataModel) {
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (this.dataModel_ != dataModel) {
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (!this.boundHandleDataModelPermuted_) {
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          this.boundHandleDataModelPermuted_ =
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              this.handleDataModelPermuted_.bind(this);
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          this.boundHandleDataModelChange_ =
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              this.handleDataModelChange_.bind(this);
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (this.dataModel_) {
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          this.dataModel_.removeEventListener(
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              'permuted',
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              this.boundHandleDataModelPermuted_);
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          this.dataModel_.removeEventListener('change',
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                              this.boundHandleDataModelChange_);
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.dataModel_ = dataModel;
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.cachedItems_ = {};
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.cachedItemHeights_ = {};
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.selectionModel.clear();
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (dataModel)
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          this.selectionModel.adjustLength(dataModel.length);
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (this.dataModel_) {
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          this.dataModel_.addEventListener(
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              'permuted',
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              this.boundHandleDataModelPermuted_);
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          this.dataModel_.addEventListener('change',
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                           this.boundHandleDataModelChange_);
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.redraw();
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    get dataModel() {
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return this.dataModel_;
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Cached item for measuring the default item size by measureItem().
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @type {ListItem}
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    cachedMeasuredItem_: null,
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * The selection model to use.
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @type {cr.ui.ListSelectionModel}
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    get selectionModel() {
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return this.selectionModel_;
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    set selectionModel(sm) {
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var oldSm = this.selectionModel_;
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (oldSm == sm)
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return;
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (!this.boundHandleOnChange_) {
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.boundHandleOnChange_ = this.handleOnChange_.bind(this);
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.boundHandleLeadChange_ = this.handleLeadChange_.bind(this);
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (oldSm) {
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        oldSm.removeEventListener('change', this.boundHandleOnChange_);
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        oldSm.removeEventListener('leadIndexChange',
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  this.boundHandleLeadChange_);
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.selectionModel_ = sm;
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.selectionController_ = this.createSelectionController(sm);
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (sm) {
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        sm.addEventListener('change', this.boundHandleOnChange_);
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        sm.addEventListener('leadIndexChange', this.boundHandleLeadChange_);
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Whether or not the list auto-expands.
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @type {boolean}
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    get autoExpands() {
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return this.autoExpands_;
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    set autoExpands(autoExpands) {
2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (this.autoExpands_ == autoExpands)
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return;
2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.autoExpands_ = autoExpands;
2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.redraw();
2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Whether or not the rows on list have various heights.
2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @type {boolean}
2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    get fixedHeight() {
2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return this.fixedHeight_;
2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    set fixedHeight(fixedHeight) {
2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (this.fixedHeight_ == fixedHeight)
2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return;
2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.fixedHeight_ = fixedHeight;
2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.redraw();
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Convenience alias for selectionModel.selectedItem
2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @type {*}
2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    get selectedItem() {
2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var dataModel = this.dataModel;
2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (dataModel) {
2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var index = this.selectionModel.selectedIndex;
2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (index != -1)
2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          return dataModel.item(index);
2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return null;
2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    set selectedItem(selectedItem) {
2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var dataModel = this.dataModel;
2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (dataModel) {
2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var index = this.dataModel.indexOf(selectedItem);
2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.selectionModel.selectedIndex = index;
2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Convenience alias for selectionModel.selectedItems
2451320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci     * @type {!Array.<*>}
2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    get selectedItems() {
2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var indexes = this.selectionModel.selectedIndexes;
2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var dataModel = this.dataModel;
2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (dataModel) {
2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return indexes.map(function(i) {
2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          return dataModel.item(i);
2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        });
2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return [];
2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * The HTML elements representing the items.
2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @type {HTMLCollection}
2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    get items() {
2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return Array.prototype.filter.call(this.children,
2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                         this.isItem, this);
2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Returns true if the child is a list item. Subclasses may override this
2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * to filter out certain elements.
2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {Node} child Child of the list.
2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @return {boolean} True if a list item.
2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    isItem: function(child) {
2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return child.nodeType == Node.ELEMENT_NODE &&
2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             child != this.beforeFiller_ && child != this.afterFiller_;
2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    batchCount_: 0,
2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * When making a lot of updates to the list, the code could be wrapped in
2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * the startBatchUpdates and finishBatchUpdates to increase performance. Be
2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * sure that the code will not return without calling endBatchUpdates or the
2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * list will not be correctly updated.
2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    startBatchUpdates: function() {
2875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.batchCount_++;
2885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
2895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
2915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * See startBatchUpdates.
2925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    endBatchUpdates: function() {
2945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.batchCount_--;
2955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (this.batchCount_ == 0)
2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.redraw();
2975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
2985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Initializes the element.
3015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
3025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    decorate: function() {
3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Add fillers.
3045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.beforeFiller_ = this.ownerDocument.createElement('div');
3055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.afterFiller_ = this.ownerDocument.createElement('div');
3065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.beforeFiller_.className = 'spacer';
3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.afterFiller_.className = 'spacer';
3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.textContent = '';
3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.appendChild(this.beforeFiller_);
3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.appendChild(this.afterFiller_);
3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var length = this.dataModel ? this.dataModel.length : 0;
3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.selectionModel = new ListSelectionModel(length);
3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      this.addEventListener('dblclick', this.handleDoubleClick_);
3164e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      this.addEventListener('mousedown', handleMouseDown);
317cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      this.addEventListener('dragstart', handleDragStart, true);
3185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.addEventListener('mouseup', this.handlePointerDownUp_);
3195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.addEventListener('keydown', this.handleKeyDown);
3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.addEventListener('focus', this.handleElementFocus_, true);
3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.addEventListener('blur', this.handleElementBlur_, true);
3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.addEventListener('scroll', this.handleScroll.bind(this));
3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.setAttribute('role', 'list');
3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Make list focusable
3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (!this.hasAttribute('tabindex'))
3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.tabIndex = 0;
3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Try to get an unique id prefix from the id of this element or the
3305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // nearest ancestor with an id.
3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var element = this;
3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      while (element && !element.id)
3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        element = element.parentElement;
3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (element && element.id)
3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.uniqueIdPrefix_ = element.id;
3365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      else
3375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.uniqueIdPrefix_ = 'list';
3385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // The next id suffix to use when giving each item an unique id.
3405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.nextUniqueIdSuffix_ = 0;
3415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
3425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
3445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {ListItem=} item The list item to measure.
3455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @return {number} The height of the given item. If the fixed height on CSS
3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * is set by 'px', uses that value as height. Otherwise, measures the size.
3475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @private
3485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
3495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    measureItemHeight_: function(item) {
3505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return this.measureItem(item).height;
3515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
3525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
3545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @return {number} The height of default item, measuring it if necessary.
3555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @private
3565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
3575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    getDefaultItemHeight_: function() {
3585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return this.getDefaultItemSize_().height;
3595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
3605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
3625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {number} index The index of the item.
3635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @return {number} The height of the item, measuring it if necessary.
3645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
3655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    getItemHeightByIndex_: function(index) {
3665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // If |this.fixedHeight_| is true, all the rows have same default height.
3675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (this.fixedHeight_)
3685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return this.getDefaultItemHeight_();
3695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (this.cachedItemHeights_[index])
3715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return this.cachedItemHeights_[index];
3725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var item = this.getListItemByIndex(index);
3742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (item) {
3752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        var h = this.measureItemHeight_(item);
3762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        this.cachedItemHeights_[index] = h;
3772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        return h;
3782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
3795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return this.getDefaultItemHeight_();
3805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
3815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
3835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @return {{height: number, width: number}} The height and width
3845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     *     of default item, measuring it if necessary.
3855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @private
3865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
3875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    getDefaultItemSize_: function() {
3885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (!this.measured_ || !this.measured_.height) {
3895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.measured_ = this.measureItem();
3905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
3915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return this.measured_;
3925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
3935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
3955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Creates an item (dataModel.item(0)) and measures its height. The item is
3965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * cached instead of creating a new one every time..
3975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {ListItem=} opt_item The list item to use to do the measuring. If
3985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     *     this is not provided an item will be created based on the first value
3995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     *     in the model.
4001320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci     * @return {{height: number, marginTop: number, marginBottom: number,
4011320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci     *     width: number, marginLeft: number, marginRight: number}}
4025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     *     The height and width of the item, taking
4035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     *     margins into account, and the top, bottom, left and right margins
4045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     *     themselves.
4055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
4065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    measureItem: function(opt_item) {
4075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var dataModel = this.dataModel;
4081320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      if (!dataModel || !dataModel.length) {
4091320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        return {height: 0, marginTop: 0, marginBottom: 0,
4101320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci                width: 0, marginLeft: 0, marginRight: 0};
4111320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      }
4125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var item = opt_item || this.cachedMeasuredItem_ ||
4135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          this.createItem(dataModel.item(0));
4145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (!opt_item) {
4155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.cachedMeasuredItem_ = item;
4165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.appendChild(item);
4175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
4185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var rect = item.getBoundingClientRect();
4205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var cs = getComputedStyle(item);
4215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var mt = parseFloat(cs.marginTop);
4225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var mb = parseFloat(cs.marginBottom);
4235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var ml = parseFloat(cs.marginLeft);
4245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var mr = parseFloat(cs.marginRight);
4255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var h = rect.height;
4265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var w = rect.width;
4275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var mh = 0;
4285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var mv = 0;
4295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Handle margin collapsing.
4315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (mt < 0 && mb < 0) {
4325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        mv = Math.min(mt, mb);
4335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } else if (mt >= 0 && mb >= 0) {
4345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        mv = Math.max(mt, mb);
4355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } else {
4365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        mv = mt + mb;
4375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
4385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      h += mv;
4395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (ml < 0 && mr < 0) {
4415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        mh = Math.min(ml, mr);
4425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } else if (ml >= 0 && mr >= 0) {
4435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        mh = Math.max(ml, mr);
4445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } else {
4455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        mh = ml + mr;
4465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
4475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      w += mh;
4485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (!opt_item)
4505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.removeChild(item);
4515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return {
4525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          height: Math.max(0, h),
4535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          marginTop: mt, marginBottom: mb,
4545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          width: Math.max(0, w),
4555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          marginLeft: ml, marginRight: mr};
4565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
4575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
4592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * Callback for the double click event.
4602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * @param {Event} e The mouse event object.
4615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @private
4625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
4632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    handleDoubleClick_: function(e) {
4645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (this.disabled)
4655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return;
4665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4671320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      var target = /** @type {HTMLElement} */(e.target);
4685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4691320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      var ancestor = this.getListItemAncestor(target);
4701320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      var index = -1;
4711320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      if (ancestor) {
4721320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        index = this.getIndexOfListItem(ancestor);
4731320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        this.activateItemAtIndex(index);
4741320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      }
4751320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
4761320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      var sm = this.selectionModel;
4771320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      var indexSelected = sm.getIndexSelected(index);
4781320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      if (!indexSelected)
4791320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        this.handlePointerDownUp_(e);
4805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
4815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
4832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * Callback for mousedown and mouseup events.
4842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * @param {Event} e The mouse event object.
4855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @private
4865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
4875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    handlePointerDownUp_: function(e) {
4885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (this.disabled)
4895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return;
4905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4911320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      var target = /** @type {HTMLElement} */(e.target);
4925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // If the target was this element we need to make sure that the user did
4945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // not click on a border or a scrollbar.
4955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (target == this) {
4965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (inViewport(target, e))
4975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          this.selectionController_.handlePointerDownUp(e, -1);
4985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return;
4995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
5005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      target = this.getListItemAncestor(target);
5025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var index = this.getIndexOfListItem(target);
5045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.selectionController_.handlePointerDownUp(e, index);
5055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
5065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
5085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Called when an element in the list is focused. Marks the list as having
5095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * a focused element, and dispatches an event if it didn't have focus.
5105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {Event} e The focus event.
5115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @private
5125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
5135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    handleElementFocus_: function(e) {
5145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (!this.hasElementFocus)
5155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.hasElementFocus = true;
5165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
5175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
5195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Called when an element in the list is blurred. If focus moves outside
5205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * the list, marks the list as no longer having focus and dispatches an
5215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * event.
5225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {Event} e The blur event.
5235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @private
5245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
5255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    handleElementBlur_: function(e) {
5264e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      if (!this.contains(e.relatedTarget))
5274e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        this.hasElementFocus = false;
5285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
5295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
5315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Returns the list item element containing the given element, or null if
5325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * it doesn't belong to any list item element.
5335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {HTMLElement} element The element.
5341320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci     * @return {HTMLLIElement} The list item containing |element|, or null.
5355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
5365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    getListItemAncestor: function(element) {
5375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var container = element;
5385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      while (container && container.parentNode != this) {
5395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        container = container.parentNode;
5405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
5411320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      return container && assertInstanceof(container, HTMLLIElement);
5425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
5435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
5455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Handle a keydown event.
5465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {Event} e The keydown event.
5475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
5485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    handleKeyDown: function(e) {
5491320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      if (!this.disabled)
5501320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        this.selectionController_.handleKeyDown(e);
5515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
5525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
5545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Handle a scroll event.
5555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {Event} e The scroll event.
5565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
5575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    handleScroll: function(e) {
5587dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch      requestAnimationFrame(this.redraw.bind(this));
5595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
5605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
5625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Callback from the selection model. We dispatch {@code change} events
5635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * when the selection changes.
5641320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci     * @param {!Event} ce Event with change info.
5655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @private
5665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
5675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    handleOnChange_: function(ce) {
5685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ce.changes.forEach(function(change) {
5695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var listItem = this.getListItemByIndex(change.index);
5705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (listItem) {
5715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          listItem.selected = change.selected;
5725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          if (change.selected) {
5735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            listItem.setAttribute('aria-posinset', change.index + 1);
5745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            listItem.setAttribute('aria-setsize', this.dataModel.length);
5755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            this.setAttribute('aria-activedescendant', listItem.id);
5765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          } else {
5775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            listItem.removeAttribute('aria-posinset');
5785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            listItem.removeAttribute('aria-setsize');
5795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          }
5805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
5815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }, this);
5825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      cr.dispatchSimpleEvent(this, 'change');
5845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
5855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
5875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Handles a change of the lead item from the selection model.
5885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {Event} pe The property change event.
5895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @private
5905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
5915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    handleLeadChange_: function(pe) {
5925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var element;
5935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (pe.oldValue != -1) {
5945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if ((element = this.getListItemByIndex(pe.oldValue)))
5955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          element.lead = false;
5965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
5975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (pe.newValue != -1) {
5995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if ((element = this.getListItemByIndex(pe.newValue)))
6005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          element.lead = true;
6015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (pe.oldValue != pe.newValue) {
6025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          this.scrollIndexIntoView(pe.newValue);
6035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          // If the lead item has a different height than other items, then we
6045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          // may run into a problem that requires a second attempt to scroll
6055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          // it into view. The first scroll attempt will trigger a redraw,
6065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          // which will clear out the list and repopulate it with new items.
6075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          // During the redraw, the list may shrink temporarily, which if the
6085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          // lead item is the last item, will move the scrollTop up since it
6095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          // cannot extend beyond the end of the list. (Sadly, being scrolled to
6105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          // the bottom of the list is not "sticky.") So, we set a timeout to
6115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          // rescroll the list after this all gets sorted out. This is perhaps
6125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          // not the most elegant solution, but no others seem obvious.
6135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          var self = this;
6145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          window.setTimeout(function() {
6155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            self.scrollIndexIntoView(pe.newValue);
6161320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci          }, 0);
6175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
6185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
6195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
6205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
6225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * This handles data model 'permuted' event.
6235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * this event is dispatched as a part of sort or splice.
6245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * We need to
6255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     *  - adjust the cache.
6265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     *  - adjust selection.
6275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     *  - redraw. (called in this.endBatchUpdates())
6285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     *  It is important that the cache adjustment happens before selection model
6295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     *  adjustments.
6305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {Event} e The 'permuted' event.
6315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
6325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    handleDataModelPermuted_: function(e) {
6335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var newCachedItems = {};
6345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for (var index in this.cachedItems_) {
6355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (e.permutation[index] != -1) {
6365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          var newIndex = e.permutation[index];
6375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          newCachedItems[newIndex] = this.cachedItems_[index];
6385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          newCachedItems[newIndex].listIndex = newIndex;
6395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
6405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
6415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.cachedItems_ = newCachedItems;
6420529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch      this.pinnedItem_ = null;
6435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var newCachedItemHeights = {};
6455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for (var index in this.cachedItemHeights_) {
6465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (e.permutation[index] != -1) {
6475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          newCachedItemHeights[e.permutation[index]] =
6485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              this.cachedItemHeights_[index];
6495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
6505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
6515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.cachedItemHeights_ = newCachedItemHeights;
6525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.startBatchUpdates();
6545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var sm = this.selectionModel;
6565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      sm.adjustLength(e.newLength);
6575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      sm.adjustToReordering(e.permutation);
6585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.endBatchUpdates();
6605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
6615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    handleDataModelChange_: function(e) {
6635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      delete this.cachedItems_[e.index];
6645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      delete this.cachedItemHeights_[e.index];
6655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.cachedMeasuredItem_ = null;
6665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (e.index >= this.firstIndex_ &&
6685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          (e.index < this.lastIndex_ || this.remainingSpace_)) {
6695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.redraw();
6705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
6715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
6725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
6745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {number} index The index of the item.
6755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @return {number} The top position of the item inside the list.
6765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
6775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    getItemTop: function(index) {
6785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (this.fixedHeight_) {
6795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var itemHeight = this.getDefaultItemHeight_();
6805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return index * itemHeight;
6815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } else {
6822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        this.ensureAllItemSizesInCache();
6835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var top = 0;
6845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        for (var i = 0; i < index; i++) {
6855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          top += this.getItemHeightByIndex_(i);
6865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
6875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return top;
6885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
6895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
6905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
6925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {number} index The index of the item.
6935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @return {number} The row of the item. May vary in the case
6945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     *     of multiple columns.
6955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
6965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    getItemRow: function(index) {
6975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return index;
6985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
6995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
7015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {number} row The row.
7025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @return {number} The index of the first item in the row.
7035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
7045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    getFirstItemInRow: function(row) {
7055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return row;
7065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
7075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
7095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Ensures that a given index is inside the viewport.
7105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {number} index The index of the item to scroll into view.
7115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @return {boolean} Whether any scrolling was needed.
7125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
7135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    scrollIndexIntoView: function(index) {
7145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var dataModel = this.dataModel;
7155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (!dataModel || index < 0 || index >= dataModel.length)
7165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return false;
7175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var itemHeight = this.getItemHeightByIndex_(index);
7195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var scrollTop = this.scrollTop;
7205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var top = this.getItemTop(index);
7215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var clientHeight = this.clientHeight;
7225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
723a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      var cs = getComputedStyle(this);
724a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      var paddingY = parseInt(cs.paddingTop, 10) +
725a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                     parseInt(cs.paddingBottom, 10);
726a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      var availableHeight = clientHeight - paddingY;
727a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
7285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var self = this;
7295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Function to adjust the tops of viewport and row.
7305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      function scrollToAdjustTop() {
7315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          self.scrollTop = top;
7325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          return true;
7335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      };
7345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Function to adjust the bottoms of viewport and row.
7355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      function scrollToAdjustBottom() {
736a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)          self.scrollTop = top + itemHeight - availableHeight;
737a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)          return true;
7385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      };
7395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Check if the entire of given indexed row can be shown in the viewport.
741a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      if (itemHeight <= availableHeight) {
7425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (top < scrollTop)
7435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          return scrollToAdjustTop();
744a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        if (scrollTop + availableHeight < top + itemHeight)
7455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          return scrollToAdjustBottom();
7465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } else {
7475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (scrollTop < top)
7485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          return scrollToAdjustTop();
749a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        if (top + itemHeight < scrollTop + availableHeight)
7505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          return scrollToAdjustBottom();
7515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
7525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return false;
7535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
7545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
7565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @return {!ClientRect} The rect to use for the context menu.
7575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
7585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    getRectForContextMenu: function() {
7595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // TODO(arv): Add trait support so we can share more code between trees
7605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // and lists.
7615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var index = this.selectionModel.selectedIndex;
7625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var el = this.getListItemByIndex(index);
7635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (el)
7645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return el.getBoundingClientRect();
7655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return this.getBoundingClientRect();
7665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
7675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
7695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Takes a value from the data model and finds the associated list item.
7705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {*} value The value in the data model that we want to get the list
7715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     *     item for.
7725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @return {ListItem} The first found list item or null if not found.
7735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
7745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    getListItem: function(value) {
7755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var dataModel = this.dataModel;
7765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (dataModel) {
7775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var index = dataModel.indexOf(value);
7785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return this.getListItemByIndex(index);
7795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
7805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return null;
7815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
7825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
7845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Find the list item element at the given index.
7855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {number} index The index of the list item to get.
7865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @return {ListItem} The found list item or null if not found.
7875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
7885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    getListItemByIndex: function(index) {
7895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return this.cachedItems_[index] || null;
7905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
7915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
7935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Find the index of the given list item element.
7945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {ListItem} item The list item to get the index of.
7955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @return {number} The index of the list item, or -1 if not found.
7965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
7975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    getIndexOfListItem: function(item) {
7985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var index = item.listIndex;
7995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (this.cachedItems_[index] == item) {
8005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return index;
8015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
8025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return -1;
8035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
8045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
8065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Creates a new list item.
8075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {*} value The value to use for the item.
8085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @return {!ListItem} The newly created list item.
8095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
8105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    createItem: function(value) {
8115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var item = new this.itemConstructor_(value);
8125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      item.label = value;
8135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      item.id = this.uniqueIdPrefix_ + '-' + this.nextUniqueIdSuffix_++;
8145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (typeof item.decorate == 'function')
8155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        item.decorate();
8165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return item;
8175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
8185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
8205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Creates the selection controller to use internally.
8215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {cr.ui.ListSelectionModel} sm The underlying selection model.
8225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @return {!cr.ui.ListSelectionController} The newly created selection
8235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     *     controller.
8245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
8255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    createSelectionController: function(sm) {
8265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return new ListSelectionController(sm);
8275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
8285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
8305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Return the heights (in pixels) of the top of the given item index within
8315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * the list, and the height of the given item itself, accounting for the
8325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * possibility that the lead item may be a different height.
8335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {number} index The index to find the top height of.
8345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @return {{top: number, height: number}} The heights for the given index.
8355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @private
8365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
8375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    getHeightsForIndex_: function(index) {
8385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var itemHeight = this.getItemHeightByIndex_(index);
8395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var top = this.getItemTop(index);
8405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return {top: top, height: itemHeight};
8415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
8425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
8445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Find the index of the list item containing the given y offset (measured
8455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * in pixels from the top) within the list. In the case of multiple columns,
8465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * returns the first index in the row.
8475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {number} offset The y offset in pixels to get the index of.
8485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @return {number} The index of the list item. Returns the list size if
8495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     *     given offset exceeds the height of list.
8505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @private
8515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
8525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    getIndexForListOffset_: function(offset) {
8535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var itemHeight = this.getDefaultItemHeight_();
8545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (!itemHeight)
8555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return this.dataModel.length;
8565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (this.fixedHeight_)
8585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return this.getFirstItemInRow(Math.floor(offset / itemHeight));
8595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // If offset exceeds the height of list.
8615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var lastHeight = 0;
8625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (this.dataModel.length) {
8635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var h = this.getHeightsForIndex_(this.dataModel.length - 1);
8645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        lastHeight = h.top + h.height;
8655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
8665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (lastHeight < offset)
8675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return this.dataModel.length;
8685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Estimates index.
8705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var estimatedIndex = Math.min(Math.floor(offset / itemHeight),
8715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                    this.dataModel.length - 1);
8725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var isIncrementing = this.getItemTop(estimatedIndex) < offset;
8735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Searchs the correct index.
8755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      do {
8765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var heights = this.getHeightsForIndex_(estimatedIndex);
8775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var top = heights.top;
8785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var height = heights.height;
8795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (top <= offset && offset <= (top + height))
8815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          break;
8825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        isIncrementing ? ++estimatedIndex : --estimatedIndex;
8845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } while (0 < estimatedIndex && estimatedIndex < this.dataModel.length);
8855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return estimatedIndex;
8875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
8885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
8905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Return the number of items that occupy the range of heights between the
8915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * top of the start item and the end offset.
8925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {number} startIndex The index of the first visible item.
8935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {number} endOffset The y offset in pixels of the end of the list.
8945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @return {number} The number of list items visible.
8955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @private
8965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
8975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    countItemsInRange_: function(startIndex, endOffset) {
8985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var endIndex = this.getIndexForListOffset_(endOffset);
8995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return endIndex - startIndex + 1;
9005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
9015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
9035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Calculates the number of items fitting in the given viewport.
9045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {number} scrollTop The scroll top position.
9055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {number} clientHeight The height of viewport.
9065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @return {{first: number, length: number, last: number}} The index of
9075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     *     first item in view port, The number of items, The item past the last.
9085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
9095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    getItemsInViewPort: function(scrollTop, clientHeight) {
9105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (this.autoExpands_) {
9115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return {
9125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          first: 0,
9135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          length: this.dataModel.length,
9145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          last: this.dataModel.length};
9155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } else {
9165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var firstIndex = this.getIndexForListOffset_(scrollTop);
9175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var lastIndex = this.getIndexForListOffset_(scrollTop + clientHeight);
9185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return {
9205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          first: firstIndex,
9215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          length: lastIndex - firstIndex + 1,
9225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          last: lastIndex + 1};
9235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
9245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
9255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
9275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Merges list items currently existing in the list with items in the range
9285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * [firstIndex, lastIndex). Removes or adds items if needed.
9295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Doesn't delete {@code this.pinnedItem_} if it is present (instead hides
9307dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch     * it if it is out of the range).
9315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {number} firstIndex The index of first item, inclusively.
9325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {number} lastIndex The index of last item, exclusively.
9335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
9347dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    mergeItems: function(firstIndex, lastIndex) {
9355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var self = this;
9365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var dataModel = this.dataModel;
9375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var currentIndex = firstIndex;
9385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      function insert() {
9405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var dataItem = dataModel.item(currentIndex);
9417dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch        var newItem = self.cachedItems_[currentIndex] ||
9427dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch            self.createItem(dataItem);
9435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        newItem.listIndex = currentIndex;
9447dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch        self.cachedItems_[currentIndex] = newItem;
9455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        self.insertBefore(newItem, item);
9465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        currentIndex++;
9475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
9485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      function remove() {
9505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var next = item.nextSibling;
9515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (item != self.pinnedItem_)
9525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          self.removeChild(item);
9535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        item = next;
9545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
9555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for (var item = this.beforeFiller_.nextSibling;
9575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)           item != this.afterFiller_ && currentIndex < lastIndex;) {
9585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (!this.isItem(item)) {
9595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          item = item.nextSibling;
9605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          continue;
9615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
9625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var index = item.listIndex;
9647dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch        if (this.cachedItems_[index] != item || index < currentIndex) {
9655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          remove();
9665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        } else if (index == currentIndex) {
9677dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch          this.cachedItems_[currentIndex] = item;
9685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          item = item.nextSibling;
9695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          currentIndex++;
9705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        } else {  // index > currentIndex
9715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          insert();
9725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
9735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
9745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      while (item != this.afterFiller_) {
9765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (this.isItem(item))
9775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          remove();
9785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        else
9795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          item = item.nextSibling;
9805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
9815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (this.pinnedItem_) {
9835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var index = this.pinnedItem_.listIndex;
9845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.pinnedItem_.hidden = index < firstIndex || index >= lastIndex;
9857dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch        this.cachedItems_[index] = this.pinnedItem_;
9865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (index >= lastIndex)
9875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          item = this.pinnedItem_;  // Insert new items before this one.
9885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
9895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      while (currentIndex < lastIndex)
9915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        insert();
9925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
9935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
9955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Ensures that all the item sizes in the list have been already cached.
9965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
9975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ensureAllItemSizesInCache: function() {
9985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var measuringIndexes = [];
9995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var isElementAppended = [];
10005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for (var y = 0; y < this.dataModel.length; y++) {
10015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (!this.cachedItemHeights_[y]) {
10025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          measuringIndexes.push(y);
10035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          isElementAppended.push(false);
10045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
10055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
10065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var measuringItems = [];
10085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Adds temporary elements.
10095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for (var y = 0; y < measuringIndexes.length; y++) {
10105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var index = measuringIndexes[y];
10115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var dataItem = this.dataModel.item(index);
10125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var listItem = this.cachedItems_[index] || this.createItem(dataItem);
10135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        listItem.listIndex = index;
10145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // If |listItems| is not on the list, apppends it to the list and sets
10165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // the flag.
10175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (!listItem.parentNode) {
10185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          this.appendChild(listItem);
10195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          isElementAppended[y] = true;
10205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
10215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.cachedItems_[index] = listItem;
10235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        measuringItems.push(listItem);
10245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
10255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // All mesurings must be placed after adding all the elements, to prevent
10275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // performance reducing.
10285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for (var y = 0; y < measuringIndexes.length; y++) {
10295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var index = measuringIndexes[y];
10305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.cachedItemHeights_[index] =
10315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            this.measureItemHeight_(measuringItems[y]);
10325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
10335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Removes all the temprary elements.
10355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for (var y = 0; y < measuringIndexes.length; y++) {
10365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // If the list item has been appended above, removes it.
10375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (isElementAppended[y])
10385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          this.removeChild(measuringItems[y]);
10395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
10405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
10415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
10435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Returns the height of after filler in the list.
10445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {number} lastIndex The index of item past the last in viewport.
10455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @return {number} The height of after filler.
10465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
10475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    getAfterFillerHeight: function(lastIndex) {
10485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (this.fixedHeight_) {
10495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var itemHeight = this.getDefaultItemHeight_();
10505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return (this.dataModel.length - lastIndex) * itemHeight;
10515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
10525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var height = 0;
10545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for (var i = lastIndex; i < this.dataModel.length; i++)
10555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        height += this.getItemHeightByIndex_(i);
10565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return height;
10575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
10585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
10605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Redraws the viewport.
10615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
10625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    redraw: function() {
10635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (this.batchCount_ != 0)
10645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return;
10655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var dataModel = this.dataModel;
10675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (!dataModel || !this.autoExpands_ && this.clientHeight == 0) {
10685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.cachedItems_ = {};
10695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.firstIndex_ = 0;
10705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.lastIndex_ = 0;
10712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        this.remainingSpace_ = this.clientHeight != 0;
10721320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        this.mergeItems(0, 0);
10735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return;
10745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
10755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Save the previous positions before any manipulation of elements.
10775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var scrollTop = this.scrollTop;
10785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var clientHeight = this.clientHeight;
10795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Store all the item sizes into the cache in advance, to prevent
10815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // interleave measuring with mutating dom.
10825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (!this.fixedHeight_)
10835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.ensureAllItemSizesInCache();
10845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var autoExpands = this.autoExpands_;
10865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var itemsInViewPort = this.getItemsInViewPort(scrollTop, clientHeight);
10885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Draws the hidden rows just above/below the viewport to prevent
10895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // flashing in scroll.
10907dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch      var firstIndex = Math.max(
10917dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch          0,
10927dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch          Math.min(dataModel.length - 1, itemsInViewPort.first - 1));
10935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var lastIndex = Math.min(itemsInViewPort.last + 1, dataModel.length);
10945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var beforeFillerHeight =
10965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          this.autoExpands ? 0 : this.getItemTop(firstIndex);
10975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var afterFillerHeight =
10985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          this.autoExpands ? 0 : this.getAfterFillerHeight(lastIndex);
10995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.beforeFiller_.style.height = beforeFillerHeight + 'px';
11015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var sm = this.selectionModel;
11035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var leadIndex = sm.leadIndex;
11045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1105d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      // If the pinned item is hidden and it is not the lead item, then remove
1106d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      // it from cache. Note, that we restore the hidden status to false, since
1107d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      // the item is still in cache, and may be reused.
11085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (this.pinnedItem_ &&
11097dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch          this.pinnedItem_ != this.cachedItems_[leadIndex]) {
1110d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)        if (this.pinnedItem_.hidden) {
11115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          this.removeChild(this.pinnedItem_);
1112d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)          this.pinnedItem_.hidden = false;
1113d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)        }
11145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.pinnedItem_ = undefined;
11155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
11165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11177dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch      this.mergeItems(firstIndex, lastIndex);
11185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11197dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch      if (!this.pinnedItem_ && this.cachedItems_[leadIndex] &&
11207dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch          this.cachedItems_[leadIndex].parentNode == this) {
11217dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch        this.pinnedItem_ = this.cachedItems_[leadIndex];
11225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
11235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.afterFiller_.style.height = afterFillerHeight + 'px';
11255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11264e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      // Restores the number of pixels scrolled, since it might be changed while
11274e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      // DOM operations.
11284e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      this.scrollTop = scrollTop;
11294e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
11305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // We don't set the lead or selected properties until after adding all
11315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // items, in case they force relayout in response to these events.
11327dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch      if (leadIndex != -1 && this.cachedItems_[leadIndex])
11337dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch        this.cachedItems_[leadIndex].lead = true;
11345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for (var y = firstIndex; y < lastIndex; y++) {
1135cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        if (sm.getIndexSelected(y) != this.cachedItems_[y].selected)
1136cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)          this.cachedItems_[y].selected = !this.cachedItems_[y].selected;
11375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
11385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.firstIndex_ = firstIndex;
11405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.lastIndex_ = lastIndex;
11415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.remainingSpace_ = itemsInViewPort.last > dataModel.length;
11435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Mesurings must be placed after adding all the elements, to prevent
11455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // performance reducing.
11465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (!this.fixedHeight_) {
11477dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch        for (var y = firstIndex; y < lastIndex; y++) {
11485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          this.cachedItemHeights_[y] =
11497dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch              this.measureItemHeight_(this.cachedItems_[y]);
11507dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch        }
11515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
11525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
11535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
11555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Restore the lead item that is present in the list but may be updated
11565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * in the data model (supposed to be used inside a batch update). Usually
11575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * such an item would be recreated in the redraw method. If reinsertion
11585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * is undesirable (for instance to prevent losing focus) the item may be
11595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * updated and restored. Assumed the listItem relates to the same data item
11605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * as the lead item in the begin of the batch update.
11615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     *
11625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {ListItem} leadItem Already existing lead item.
11635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
11645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    restoreLeadItem: function(leadItem) {
11655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      delete this.cachedItems_[leadItem.listIndex];
11665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      leadItem.listIndex = this.selectionModel.leadIndex;
11685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.pinnedItem_ = this.cachedItems_[leadItem.listIndex] = leadItem;
11695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
11705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
11725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Invalidates list by removing cached items.
11735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
11745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    invalidate: function() {
11755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.cachedItems_ = {};
11765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.cachedItemSized_ = {};
11775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
11785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
11805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Redraws a single item.
11815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {number} index The row index to redraw.
11825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
11835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    redrawItem: function(index) {
11845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (index >= this.firstIndex_ &&
11855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          (index < this.lastIndex_ || this.remainingSpace_)) {
11865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        delete this.cachedItems_[index];
11875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.redraw();
11885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
11895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
11905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
11925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Called when a list item is activated, currently only by a double click
11935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * event.
11945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {number} index The index of the activated item.
11955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
11965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    activateItemAtIndex: function(index) {
11975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
11985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
12005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Returns a ListItem for the leadIndex. If the item isn't present in the
12015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * list creates it and inserts to the list (may be invisible if it's out of
12025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * the visible range).
12035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     *
12045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Item returned from this method won't be removed until it remains a lead
12055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * item or til the data model changes (unlike other items that could be
12065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * removed when they go out of the visible range).
12075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     *
12085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @return {cr.ui.ListItem} The lead item for the list.
12095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
12105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ensureLeadItemExists: function() {
12115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var index = this.selectionModel.leadIndex;
12125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (index < 0)
12135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return null;
12145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var cachedItems = this.cachedItems_ || {};
12155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var item = cachedItems[index] ||
12175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 this.createItem(this.dataModel.item(index));
12185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (this.pinnedItem_ != item && this.pinnedItem_ &&
12195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          this.pinnedItem_.hidden) {
12205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.removeChild(this.pinnedItem_);
12215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
12225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.pinnedItem_ = item;
12235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      cachedItems[index] = item;
12245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      item.listIndex = index;
12255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (item.parentNode == this)
12265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return item;
12275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (this.batchCount_ != 0)
12295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        item.hidden = true;
12305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Item will get to the right place in redraw. Choose place to insert
12325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // reducing items reinsertion.
12335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (index <= this.firstIndex_)
12345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.insertBefore(item, this.beforeFiller_.nextSibling);
12355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      else
12365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.insertBefore(item, this.afterFiller_);
12375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.redraw();
12385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return item;
12395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
1240b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
1241b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    /**
1242b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)     * Starts drag selection by reacting 'dragstart' event.
1243b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)     * @param {Event} event Event of dragstart.
1244b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)     */
1245b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    startDragSelection: function(event) {
1246b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      event.preventDefault();
1247b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      var border = document.createElement('div');
1248b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      border.className = 'drag-selection-border';
1249b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      var rect = this.getBoundingClientRect();
1250b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      var startX = event.clientX - rect.left + this.scrollLeft;
1251b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      var startY = event.clientY - rect.top + this.scrollTop;
1252b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      border.style.left = startX + 'px';
1253b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      border.style.top = startY + 'px';
1254b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      var onMouseMove = function(event) {
1255b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)        var inRect = this.getBoundingClientRect();
1256b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)        var x = event.clientX - inRect.left + this.scrollLeft;
1257b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)        var y = event.clientY - inRect.top + this.scrollTop;
1258b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)        border.style.left = Math.min(startX, x) + 'px';
1259b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)        border.style.top = Math.min(startY, y) + 'px';
1260b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)        border.style.width = Math.abs(startX - x) + 'px';
1261b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)        border.style.height = Math.abs(startY - y) + 'px';
1262b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      }.bind(this);
1263b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      var onMouseUp = function() {
1264b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)        this.removeChild(border);
1265b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)        document.removeEventListener('mousemove', onMouseMove, true);
1266b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)        document.removeEventListener('mouseup', onMouseUp, true);
1267b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      }.bind(this);
1268b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      document.addEventListener('mousemove', onMouseMove, true);
1269b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      document.addEventListener('mouseup', onMouseUp, true);
1270b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      this.appendChild(border);
1271b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    },
12725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  };
12735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  cr.defineProperty(List, 'disabled', cr.PropertyKind.BOOL_ATTR);
12755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /**
12775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * Whether the list or one of its descendents has focus. This is necessary
12785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * because list items can contain controls that can be focused, and for some
12795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * purposes (e.g., styling), the list can still be conceptually focused at
12805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * that point even though it doesn't actually have the page focus.
12815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   */
12825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  cr.defineProperty(List, 'hasElementFocus', cr.PropertyKind.BOOL_ATTR);
12835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12844e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  /**
12854e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)   * Mousedown event handler.
12861320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci   * @this {cr.ui.List}
12871320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci   * @param {Event} e The mouse event object.
12884e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)   */
12894e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  function handleMouseDown(e) {
12901320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    e.target = /** @type {!HTMLElement} */(e.target);
12910f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)    var listItem = this.getListItemAncestor(e.target);
12920f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)    var wasSelected = listItem && listItem.selected;
12934e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    this.handlePointerDownUp_(e);
12944e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
12950f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)    if (e.defaultPrevented || e.button != 0)
12960f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)      return;
12970f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)
12980f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)    // The following hack is required only if the listItem gets selected.
12990f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)    if (!listItem || wasSelected || !listItem.selected)
13004e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      return;
13014e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
13024e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    // If non-focusable area in a list item is clicked and the item still
13034e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    // contains the focused element, the item did a special focus handling
13044e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    // [1] and we should not focus on the list.
13054e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    //
13064e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    // [1] For example, clicking non-focusable area gives focus on the first
13074e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    // form control in the item.
1308a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    if (!containsFocusableElement(e.target, listItem) &&
13094e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        listItem.contains(listItem.ownerDocument.activeElement)) {
13104e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      e.preventDefault();
13114e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    }
13124e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  }
13134e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
13144e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  /**
1315cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)   * Dragstart event handler.
1316cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)   * If there is an item at starting position of drag operation and the item
1317cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)   * is not selected, select it.
13181320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci   * @this {cr.ui.List}
13191320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci   * @param {Event} e The event object for 'dragstart'.
1320cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)   */
1321cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  function handleDragStart(e) {
13221320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    e = /** @type {MouseEvent} */(e);
1323cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    var element = e.target.ownerDocument.elementFromPoint(e.clientX, e.clientY);
1324cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    var listItem = this.getListItemAncestor(element);
1325cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    if (!listItem)
1326cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      return;
1327cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
1328cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    var index = this.getIndexOfListItem(listItem);
1329cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    if (index == -1)
1330cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      return;
1331cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
1332cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    var isAlreadySelected = this.selectionModel_.getIndexSelected(index);
1333cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    if (!isAlreadySelected)
1334cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      this.selectionModel_.selectedIndex = index;
1335cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  }
1336cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
1337cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  /**
1338a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)   * Check if |start| or its ancestor under |root| is focusable.
13394e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)   * This is a helper for handleMouseDown.
1340a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)   * @param {!Element} start An element which we start to check.
1341a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)   * @param {!Element} root An element which we finish to check.
1342a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)   * @return {boolean} True if we found a focusable element.
13434e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)   */
1344a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  function containsFocusableElement(start, root) {
13454e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    for (var element = start; element && element != root;
13464e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        element = element.parentElement) {
1347a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      if (element.tabIndex >= 0 && !element.disabled)
13484e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        return true;
13494e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    }
13504e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    return false;
13514e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  }
13524e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
13535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return {
13545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    List: List
13555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  };
13565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)});
1357