list.js revision d0247b1b59f9c528cb6df88b4f2b9afaf80d181e
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.
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * @param {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.
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @type {{height: number, marginTop: number, marginBottom:number,
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     *     width: number, marginLeft: number, marginRight:number}}
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @private
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    measured_: undefined,
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.
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @type {function(): !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.
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @type {function(): !ListItem}
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
2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @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_);
3165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.addEventListener('mousedown', this.handlePointerDownUp_);
3175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.addEventListener('mouseup', this.handlePointerDownUp_);
3185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.addEventListener('keydown', this.handleKeyDown);
3195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.addEventListener('focus', this.handleElementFocus_, true);
3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.addEventListener('blur', this.handleElementBlur_, true);
3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.addEventListener('scroll', this.handleScroll.bind(this));
3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.setAttribute('role', 'list');
3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Make list focusable
3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (!this.hasAttribute('tabindex'))
3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.tabIndex = 0;
3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Try to get an unique id prefix from the id of this element or the
3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // nearest ancestor with an id.
3305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var element = this;
3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      while (element && !element.id)
3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        element = element.parentElement;
3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (element && element.id)
3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.uniqueIdPrefix_ = element.id;
3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      else
3365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.uniqueIdPrefix_ = 'list';
3375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // The next id suffix to use when giving each item an unique id.
3395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.nextUniqueIdSuffix_ = 0;
3405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
3415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
3435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {ListItem=} item The list item to measure.
3445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @return {number} The height of the given item. If the fixed height on CSS
3455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * is set by 'px', uses that value as height. Otherwise, measures the size.
3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @private
3475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
3485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    measureItemHeight_: function(item) {
3495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return this.measureItem(item).height;
3505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
3515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
3535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @return {number} The height of default item, measuring it if necessary.
3545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @private
3555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
3565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    getDefaultItemHeight_: function() {
3575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return this.getDefaultItemSize_().height;
3585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
3595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
3615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {number} index The index of the item.
3625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @return {number} The height of the item, measuring it if necessary.
3635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
3645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    getItemHeightByIndex_: function(index) {
3655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // If |this.fixedHeight_| is true, all the rows have same default height.
3665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (this.fixedHeight_)
3675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return this.getDefaultItemHeight_();
3685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (this.cachedItemHeights_[index])
3705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return this.cachedItemHeights_[index];
3715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var item = this.getListItemByIndex(index);
3732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (item) {
3742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        var h = this.measureItemHeight_(item);
3752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        this.cachedItemHeights_[index] = h;
3762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        return h;
3772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
3785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return this.getDefaultItemHeight_();
3795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
3805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
3825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @return {{height: number, width: number}} The height and width
3835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     *     of default item, measuring it if necessary.
3845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @private
3855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
3865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    getDefaultItemSize_: function() {
3875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (!this.measured_ || !this.measured_.height) {
3885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.measured_ = this.measureItem();
3895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
3905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return this.measured_;
3915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
3925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
3945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Creates an item (dataModel.item(0)) and measures its height. The item is
3955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * cached instead of creating a new one every time..
3965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {ListItem=} opt_item The list item to use to do the measuring. If
3975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     *     this is not provided an item will be created based on the first value
3985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     *     in the model.
3995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @return {{height: number, marginTop: number, marginBottom:number,
4005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     *     width: number, marginLeft: number, marginRight:number}}
4015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     *     The height and width of the item, taking
4025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     *     margins into account, and the top, bottom, left and right margins
4035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     *     themselves.
4045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
4055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    measureItem: function(opt_item) {
4065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var dataModel = this.dataModel;
4075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (!dataModel || !dataModel.length)
4085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return 0;
4095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var item = opt_item || this.cachedMeasuredItem_ ||
4105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          this.createItem(dataModel.item(0));
4115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (!opt_item) {
4125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.cachedMeasuredItem_ = item;
4135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.appendChild(item);
4145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
4155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var rect = item.getBoundingClientRect();
4175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var cs = getComputedStyle(item);
4185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var mt = parseFloat(cs.marginTop);
4195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var mb = parseFloat(cs.marginBottom);
4205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var ml = parseFloat(cs.marginLeft);
4215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var mr = parseFloat(cs.marginRight);
4225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var h = rect.height;
4235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var w = rect.width;
4245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var mh = 0;
4255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var mv = 0;
4265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Handle margin collapsing.
4285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (mt < 0 && mb < 0) {
4295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        mv = Math.min(mt, mb);
4305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } else if (mt >= 0 && mb >= 0) {
4315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        mv = Math.max(mt, mb);
4325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } else {
4335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        mv = mt + mb;
4345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
4355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      h += mv;
4365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (ml < 0 && mr < 0) {
4385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        mh = Math.min(ml, mr);
4395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } else if (ml >= 0 && mr >= 0) {
4405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        mh = Math.max(ml, mr);
4415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } else {
4425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        mh = ml + mr;
4435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
4445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      w += mh;
4455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (!opt_item)
4475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.removeChild(item);
4485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return {
4495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          height: Math.max(0, h),
4505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          marginTop: mt, marginBottom: mb,
4515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          width: Math.max(0, w),
4525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          marginLeft: ml, marginRight: mr};
4535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
4545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
4562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * Callback for the double click event.
4572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * @param {Event} e The mouse event object.
4585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @private
4595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
4602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    handleDoubleClick_: function(e) {
4615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (this.disabled)
4625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return;
4635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var target = e.target;
4655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      target = this.getListItemAncestor(target);
4675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (target)
4685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.activateItemAtIndex(this.getIndexOfListItem(target));
4695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
4705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
4722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * Callback for mousedown and mouseup events.
4732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * @param {Event} e The mouse event object.
4745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @private
4755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
4765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    handlePointerDownUp_: function(e) {
4775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (this.disabled)
4785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return;
4795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var target = e.target;
4815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // If the target was this element we need to make sure that the user did
4835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // not click on a border or a scrollbar.
4845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (target == this) {
4855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (inViewport(target, e))
4865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          this.selectionController_.handlePointerDownUp(e, -1);
4875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return;
4885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
4895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      target = this.getListItemAncestor(target);
4915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var index = this.getIndexOfListItem(target);
4935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.selectionController_.handlePointerDownUp(e, index);
4945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
4955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
4975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Called when an element in the list is focused. Marks the list as having
4985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * a focused element, and dispatches an event if it didn't have focus.
4995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {Event} e The focus event.
5005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @private
5015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
5025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    handleElementFocus_: function(e) {
5035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (!this.hasElementFocus)
5045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.hasElementFocus = true;
5055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
5065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
5085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Called when an element in the list is blurred. If focus moves outside
5095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * the list, marks the list as no longer having focus and dispatches an
5105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * event.
5115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {Event} e The blur event.
5125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @private
5135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
5145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    handleElementBlur_: function(e) {
5155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // When the blur event happens we do not know who is getting focus so we
5165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // delay this a bit until we know if the new focus node is outside the
5175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // list.
518ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch      // We need 51 msec delay because InlineEditableList sets focus after
519ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch      // 50 msec.
5205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var list = this;
5215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var doc = e.target.ownerDocument;
5225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      window.setTimeout(function() {
5235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var activeElement = doc.activeElement;
524ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch        if (!list.contains(activeElement))
5255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          list.hasElementFocus = false;
526ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch      }, 51);
5275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
5285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
5305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Returns the list item element containing the given element, or null if
5315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * it doesn't belong to any list item element.
5325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {HTMLElement} element The element.
5335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @return {ListItem} The list item containing |element|, or null.
5345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
5355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    getListItemAncestor: function(element) {
5365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var container = element;
5375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      while (container && container.parentNode != this) {
5385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        container = container.parentNode;
5395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
5405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return container;
5415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
5425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
5445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Handle a keydown event.
5455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {Event} e The keydown event.
5465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @return {boolean} Whether the key event was handled.
5475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
5485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    handleKeyDown: function(e) {
5495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (this.disabled)
5505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return;
5515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return this.selectionController_.handleKeyDown(e);
5535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
5545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
5565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Handle a scroll event.
5575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {Event} e The scroll event.
5585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
5595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    handleScroll: function(e) {
5607dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch      requestAnimationFrame(this.redraw.bind(this));
5615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
5625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
5645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Callback from the selection model. We dispatch {@code change} events
5655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * when the selection changes.
5665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {!cr.Event} e Event with change info.
5675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @private
5685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
5695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    handleOnChange_: function(ce) {
5705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ce.changes.forEach(function(change) {
5715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var listItem = this.getListItemByIndex(change.index);
5725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (listItem) {
5735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          listItem.selected = change.selected;
5745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          if (change.selected) {
5755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            listItem.setAttribute('aria-posinset', change.index + 1);
5765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            listItem.setAttribute('aria-setsize', this.dataModel.length);
5775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            this.setAttribute('aria-activedescendant', listItem.id);
5785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          } else {
5795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            listItem.removeAttribute('aria-posinset');
5805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            listItem.removeAttribute('aria-setsize');
5815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          }
5825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
5835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }, this);
5845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      cr.dispatchSimpleEvent(this, 'change');
5865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
5875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
5895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Handles a change of the lead item from the selection model.
5905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {Event} pe The property change event.
5915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @private
5925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
5935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    handleLeadChange_: function(pe) {
5945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var element;
5955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (pe.oldValue != -1) {
5965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if ((element = this.getListItemByIndex(pe.oldValue)))
5975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          element.lead = false;
5985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
5995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (pe.newValue != -1) {
6015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if ((element = this.getListItemByIndex(pe.newValue)))
6025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          element.lead = true;
6035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (pe.oldValue != pe.newValue) {
6045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          this.scrollIndexIntoView(pe.newValue);
6055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          // If the lead item has a different height than other items, then we
6065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          // may run into a problem that requires a second attempt to scroll
6075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          // it into view. The first scroll attempt will trigger a redraw,
6085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          // which will clear out the list and repopulate it with new items.
6095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          // During the redraw, the list may shrink temporarily, which if the
6105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          // lead item is the last item, will move the scrollTop up since it
6115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          // cannot extend beyond the end of the list. (Sadly, being scrolled to
6125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          // the bottom of the list is not "sticky.") So, we set a timeout to
6135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          // rescroll the list after this all gets sorted out. This is perhaps
6145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          // not the most elegant solution, but no others seem obvious.
6155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          var self = this;
6165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          window.setTimeout(function() {
6175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            self.scrollIndexIntoView(pe.newValue);
6185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          });
6195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
6205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
6215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
6225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
6245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * This handles data model 'permuted' event.
6255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * this event is dispatched as a part of sort or splice.
6265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * We need to
6275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     *  - adjust the cache.
6285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     *  - adjust selection.
6295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     *  - redraw. (called in this.endBatchUpdates())
6305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     *  It is important that the cache adjustment happens before selection model
6315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     *  adjustments.
6325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {Event} e The 'permuted' event.
6335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
6345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    handleDataModelPermuted_: function(e) {
6355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var newCachedItems = {};
6365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for (var index in this.cachedItems_) {
6375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (e.permutation[index] != -1) {
6385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          var newIndex = e.permutation[index];
6395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          newCachedItems[newIndex] = this.cachedItems_[index];
6405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          newCachedItems[newIndex].listIndex = newIndex;
6415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
6425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
6435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.cachedItems_ = newCachedItems;
6445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var newCachedItemHeights = {};
6465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for (var index in this.cachedItemHeights_) {
6475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (e.permutation[index] != -1) {
6485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          newCachedItemHeights[e.permutation[index]] =
6495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              this.cachedItemHeights_[index];
6505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
6515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
6525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.cachedItemHeights_ = newCachedItemHeights;
6535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.startBatchUpdates();
6555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var sm = this.selectionModel;
6575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      sm.adjustLength(e.newLength);
6585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      sm.adjustToReordering(e.permutation);
6595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.endBatchUpdates();
6615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
6625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    handleDataModelChange_: function(e) {
6645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      delete this.cachedItems_[e.index];
6655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      delete this.cachedItemHeights_[e.index];
6665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.cachedMeasuredItem_ = null;
6675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (e.index >= this.firstIndex_ &&
6695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          (e.index < this.lastIndex_ || this.remainingSpace_)) {
6705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.redraw();
6715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
6725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
6735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
6755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {number} index The index of the item.
6765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @return {number} The top position of the item inside the list.
6775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
6785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    getItemTop: function(index) {
6795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (this.fixedHeight_) {
6805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var itemHeight = this.getDefaultItemHeight_();
6815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return index * itemHeight;
6825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } else {
6832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        this.ensureAllItemSizesInCache();
6845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var top = 0;
6855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        for (var i = 0; i < index; i++) {
6865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          top += this.getItemHeightByIndex_(i);
6875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
6885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return top;
6895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
6905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
6915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
6935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {number} index The index of the item.
6945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @return {number} The row of the item. May vary in the case
6955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     *     of multiple columns.
6965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
6975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    getItemRow: function(index) {
6985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return index;
6995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
7005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
7025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {number} row The row.
7035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @return {number} The index of the first item in the row.
7045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
7055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    getFirstItemInRow: function(row) {
7065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return row;
7075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
7085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
7105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Ensures that a given index is inside the viewport.
7115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {number} index The index of the item to scroll into view.
7125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @return {boolean} Whether any scrolling was needed.
7135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
7145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    scrollIndexIntoView: function(index) {
7155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var dataModel = this.dataModel;
7165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (!dataModel || index < 0 || index >= dataModel.length)
7175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return false;
7185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var itemHeight = this.getItemHeightByIndex_(index);
7205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var scrollTop = this.scrollTop;
7215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var top = this.getItemTop(index);
7225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var clientHeight = this.clientHeight;
7235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var self = this;
7255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Function to adjust the tops of viewport and row.
7265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      function scrollToAdjustTop() {
7275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          self.scrollTop = top;
7285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          return true;
7295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      };
7305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Function to adjust the bottoms of viewport and row.
7315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      function scrollToAdjustBottom() {
7325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          var cs = getComputedStyle(self);
7335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          var paddingY = parseInt(cs.paddingTop, 10) +
7345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                         parseInt(cs.paddingBottom, 10);
7355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          if (top + itemHeight > scrollTop + clientHeight - paddingY) {
7375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            self.scrollTop = top + itemHeight - clientHeight + paddingY;
7385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            return true;
7395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          }
7405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          return false;
7415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      };
7425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Check if the entire of given indexed row can be shown in the viewport.
7445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (itemHeight <= clientHeight) {
7455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (top < scrollTop)
7465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          return scrollToAdjustTop();
7475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (scrollTop + clientHeight < top + itemHeight)
7485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          return scrollToAdjustBottom();
7495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } else {
7505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (scrollTop < top)
7515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          return scrollToAdjustTop();
7525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (top + itemHeight < scrollTop + clientHeight)
7535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          return scrollToAdjustBottom();
7545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
7555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return false;
7565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
7575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
7595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @return {!ClientRect} The rect to use for the context menu.
7605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
7615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    getRectForContextMenu: function() {
7625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // TODO(arv): Add trait support so we can share more code between trees
7635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // and lists.
7645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var index = this.selectionModel.selectedIndex;
7655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var el = this.getListItemByIndex(index);
7665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (el)
7675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return el.getBoundingClientRect();
7685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return this.getBoundingClientRect();
7695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
7705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
7725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Takes a value from the data model and finds the associated list item.
7735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {*} value The value in the data model that we want to get the list
7745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     *     item for.
7755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @return {ListItem} The first found list item or null if not found.
7765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
7775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    getListItem: function(value) {
7785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var dataModel = this.dataModel;
7795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (dataModel) {
7805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var index = dataModel.indexOf(value);
7815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return this.getListItemByIndex(index);
7825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
7835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return null;
7845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
7855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
7875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Find the list item element at the given index.
7885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {number} index The index of the list item to get.
7895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @return {ListItem} The found list item or null if not found.
7905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
7915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    getListItemByIndex: function(index) {
7925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return this.cachedItems_[index] || null;
7935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
7945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
7965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Find the index of the given list item element.
7975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {ListItem} item The list item to get the index of.
7985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @return {number} The index of the list item, or -1 if not found.
7995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
8005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    getIndexOfListItem: function(item) {
8015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var index = item.listIndex;
8025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (this.cachedItems_[index] == item) {
8035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return index;
8045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
8055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return -1;
8065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
8075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
8095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Creates a new list item.
8105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {*} value The value to use for the item.
8115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @return {!ListItem} The newly created list item.
8125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
8135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    createItem: function(value) {
8145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var item = new this.itemConstructor_(value);
8155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      item.label = value;
8165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      item.id = this.uniqueIdPrefix_ + '-' + this.nextUniqueIdSuffix_++;
8175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (typeof item.decorate == 'function')
8185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        item.decorate();
8195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return item;
8205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
8215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
8235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Creates the selection controller to use internally.
8245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {cr.ui.ListSelectionModel} sm The underlying selection model.
8255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @return {!cr.ui.ListSelectionController} The newly created selection
8265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     *     controller.
8275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
8285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    createSelectionController: function(sm) {
8295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return new ListSelectionController(sm);
8305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
8315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
8335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Return the heights (in pixels) of the top of the given item index within
8345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * the list, and the height of the given item itself, accounting for the
8355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * possibility that the lead item may be a different height.
8365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {number} index The index to find the top height of.
8375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @return {{top: number, height: number}} The heights for the given index.
8385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @private
8395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
8405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    getHeightsForIndex_: function(index) {
8415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var itemHeight = this.getItemHeightByIndex_(index);
8425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var top = this.getItemTop(index);
8435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return {top: top, height: itemHeight};
8445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
8455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
8475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Find the index of the list item containing the given y offset (measured
8485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * in pixels from the top) within the list. In the case of multiple columns,
8495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * returns the first index in the row.
8505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {number} offset The y offset in pixels to get the index of.
8515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @return {number} The index of the list item. Returns the list size if
8525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     *     given offset exceeds the height of list.
8535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @private
8545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
8555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    getIndexForListOffset_: function(offset) {
8565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var itemHeight = this.getDefaultItemHeight_();
8575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (!itemHeight)
8585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return this.dataModel.length;
8595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (this.fixedHeight_)
8615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return this.getFirstItemInRow(Math.floor(offset / itemHeight));
8625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // If offset exceeds the height of list.
8645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var lastHeight = 0;
8655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (this.dataModel.length) {
8665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var h = this.getHeightsForIndex_(this.dataModel.length - 1);
8675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        lastHeight = h.top + h.height;
8685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
8695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (lastHeight < offset)
8705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return this.dataModel.length;
8715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Estimates index.
8735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var estimatedIndex = Math.min(Math.floor(offset / itemHeight),
8745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                    this.dataModel.length - 1);
8755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var isIncrementing = this.getItemTop(estimatedIndex) < offset;
8765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Searchs the correct index.
8785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      do {
8795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var heights = this.getHeightsForIndex_(estimatedIndex);
8805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var top = heights.top;
8815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var height = heights.height;
8825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (top <= offset && offset <= (top + height))
8845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          break;
8855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        isIncrementing ? ++estimatedIndex : --estimatedIndex;
8875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } while (0 < estimatedIndex && estimatedIndex < this.dataModel.length);
8885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return estimatedIndex;
8905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
8915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
8935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Return the number of items that occupy the range of heights between the
8945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * top of the start item and the end offset.
8955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {number} startIndex The index of the first visible item.
8965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {number} endOffset The y offset in pixels of the end of the list.
8975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @return {number} The number of list items visible.
8985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @private
8995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
9005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    countItemsInRange_: function(startIndex, endOffset) {
9015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var endIndex = this.getIndexForListOffset_(endOffset);
9025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return endIndex - startIndex + 1;
9035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
9045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
9065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Calculates the number of items fitting in the given viewport.
9075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {number} scrollTop The scroll top position.
9085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {number} clientHeight The height of viewport.
9095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @return {{first: number, length: number, last: number}} The index of
9105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     *     first item in view port, The number of items, The item past the last.
9115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
9125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    getItemsInViewPort: function(scrollTop, clientHeight) {
9135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (this.autoExpands_) {
9145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return {
9155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          first: 0,
9165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          length: this.dataModel.length,
9175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          last: this.dataModel.length};
9185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } else {
9195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var firstIndex = this.getIndexForListOffset_(scrollTop);
9205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var lastIndex = this.getIndexForListOffset_(scrollTop + clientHeight);
9215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return {
9235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          first: firstIndex,
9245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          length: lastIndex - firstIndex + 1,
9255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          last: lastIndex + 1};
9265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
9275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
9285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
9305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Merges list items currently existing in the list with items in the range
9315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * [firstIndex, lastIndex). Removes or adds items if needed.
9325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Doesn't delete {@code this.pinnedItem_} if it is present (instead hides
9337dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch     * it if it is out of the range).
9345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {number} firstIndex The index of first item, inclusively.
9355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {number} lastIndex The index of last item, exclusively.
9365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
9377dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    mergeItems: function(firstIndex, lastIndex) {
9385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var self = this;
9395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var dataModel = this.dataModel;
9405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var currentIndex = firstIndex;
9415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      function insert() {
9435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var dataItem = dataModel.item(currentIndex);
9447dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch        var newItem = self.cachedItems_[currentIndex] ||
9457dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch            self.createItem(dataItem);
9465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        newItem.listIndex = currentIndex;
9477dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch        self.cachedItems_[currentIndex] = newItem;
9485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        self.insertBefore(newItem, item);
9495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        currentIndex++;
9505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
9515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      function remove() {
9535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var next = item.nextSibling;
9545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (item != self.pinnedItem_)
9555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          self.removeChild(item);
9565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        item = next;
9575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
9585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for (var item = this.beforeFiller_.nextSibling;
9605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)           item != this.afterFiller_ && currentIndex < lastIndex;) {
9615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (!this.isItem(item)) {
9625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          item = item.nextSibling;
9635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          continue;
9645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
9655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var index = item.listIndex;
9677dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch        if (this.cachedItems_[index] != item || index < currentIndex) {
9685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          remove();
9695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        } else if (index == currentIndex) {
9707dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch          this.cachedItems_[currentIndex] = item;
9715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          item = item.nextSibling;
9725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          currentIndex++;
9735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        } else {  // index > currentIndex
9745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          insert();
9755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
9765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
9775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      while (item != this.afterFiller_) {
9795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (this.isItem(item))
9805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          remove();
9815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        else
9825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          item = item.nextSibling;
9835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
9845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (this.pinnedItem_) {
9865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var index = this.pinnedItem_.listIndex;
9875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.pinnedItem_.hidden = index < firstIndex || index >= lastIndex;
9887dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch        this.cachedItems_[index] = this.pinnedItem_;
9895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (index >= lastIndex)
9905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          item = this.pinnedItem_;  // Insert new items before this one.
9915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
9925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      while (currentIndex < lastIndex)
9945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        insert();
9955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
9965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
9985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Ensures that all the item sizes in the list have been already cached.
9995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
10005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ensureAllItemSizesInCache: function() {
10015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var measuringIndexes = [];
10025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var isElementAppended = [];
10035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for (var y = 0; y < this.dataModel.length; y++) {
10045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (!this.cachedItemHeights_[y]) {
10055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          measuringIndexes.push(y);
10065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          isElementAppended.push(false);
10075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
10085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
10095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var measuringItems = [];
10115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Adds temporary elements.
10125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for (var y = 0; y < measuringIndexes.length; y++) {
10135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var index = measuringIndexes[y];
10145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var dataItem = this.dataModel.item(index);
10155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var listItem = this.cachedItems_[index] || this.createItem(dataItem);
10165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        listItem.listIndex = index;
10175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // If |listItems| is not on the list, apppends it to the list and sets
10195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // the flag.
10205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (!listItem.parentNode) {
10215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          this.appendChild(listItem);
10225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          isElementAppended[y] = true;
10235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
10245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.cachedItems_[index] = listItem;
10265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        measuringItems.push(listItem);
10275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
10285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // All mesurings must be placed after adding all the elements, to prevent
10305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // performance reducing.
10315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for (var y = 0; y < measuringIndexes.length; y++) {
10325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var index = measuringIndexes[y];
10335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.cachedItemHeights_[index] =
10345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            this.measureItemHeight_(measuringItems[y]);
10355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
10365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Removes all the temprary elements.
10385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for (var y = 0; y < measuringIndexes.length; y++) {
10395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // If the list item has been appended above, removes it.
10405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (isElementAppended[y])
10415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          this.removeChild(measuringItems[y]);
10425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
10435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
10445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
10465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Returns the height of after filler in the list.
10475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {number} lastIndex The index of item past the last in viewport.
10485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @return {number} The height of after filler.
10495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
10505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    getAfterFillerHeight: function(lastIndex) {
10515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (this.fixedHeight_) {
10525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var itemHeight = this.getDefaultItemHeight_();
10535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return (this.dataModel.length - lastIndex) * itemHeight;
10545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
10555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var height = 0;
10575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for (var i = lastIndex; i < this.dataModel.length; i++)
10585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        height += this.getItemHeightByIndex_(i);
10595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return height;
10605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
10615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
10635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Redraws the viewport.
10645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
10655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    redraw: function() {
10665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (this.batchCount_ != 0)
10675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return;
10685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var dataModel = this.dataModel;
10705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (!dataModel || !this.autoExpands_ && this.clientHeight == 0) {
10715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.cachedItems_ = {};
10725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.firstIndex_ = 0;
10735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.lastIndex_ = 0;
10742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        this.remainingSpace_ = this.clientHeight != 0;
10755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.mergeItems(0, 0, {}, {});
10765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return;
10775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
10785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Save the previous positions before any manipulation of elements.
10805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var scrollTop = this.scrollTop;
10815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var clientHeight = this.clientHeight;
10825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Store all the item sizes into the cache in advance, to prevent
10845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // interleave measuring with mutating dom.
10855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (!this.fixedHeight_)
10865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.ensureAllItemSizesInCache();
10875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var autoExpands = this.autoExpands_;
10895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var itemsInViewPort = this.getItemsInViewPort(scrollTop, clientHeight);
10915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Draws the hidden rows just above/below the viewport to prevent
10925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // flashing in scroll.
10937dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch      var firstIndex = Math.max(
10947dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch          0,
10957dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch          Math.min(dataModel.length - 1, itemsInViewPort.first - 1));
10965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var lastIndex = Math.min(itemsInViewPort.last + 1, dataModel.length);
10975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var beforeFillerHeight =
10995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          this.autoExpands ? 0 : this.getItemTop(firstIndex);
11005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var afterFillerHeight =
11015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          this.autoExpands ? 0 : this.getAfterFillerHeight(lastIndex);
11025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.beforeFiller_.style.height = beforeFillerHeight + 'px';
11045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var sm = this.selectionModel;
11065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var leadIndex = sm.leadIndex;
11075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1108d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      // If the pinned item is hidden and it is not the lead item, then remove
1109d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      // it from cache. Note, that we restore the hidden status to false, since
1110d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      // the item is still in cache, and may be reused.
11115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (this.pinnedItem_ &&
11127dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch          this.pinnedItem_ != this.cachedItems_[leadIndex]) {
1113d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)        if (this.pinnedItem_.hidden) {
11145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          this.removeChild(this.pinnedItem_);
1115d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)          this.pinnedItem_.hidden = false;
1116d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)        }
11175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.pinnedItem_ = undefined;
11185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
11195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11207dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch      this.mergeItems(firstIndex, lastIndex);
11215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11227dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch      if (!this.pinnedItem_ && this.cachedItems_[leadIndex] &&
11237dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch          this.cachedItems_[leadIndex].parentNode == this) {
11247dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch        this.pinnedItem_ = this.cachedItems_[leadIndex];
11255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
11265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.afterFiller_.style.height = afterFillerHeight + 'px';
11285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // We don't set the lead or selected properties until after adding all
11305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // items, in case they force relayout in response to these events.
11315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var listItem = null;
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++) {
11355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (sm.getIndexSelected(y))
11367dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch          this.cachedItems_[y].selected = true;
11375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        else if (y != leadIndex)
11387dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch          listItem = this.cachedItems_[y];
11395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
11405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.firstIndex_ = firstIndex;
11425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.lastIndex_ = lastIndex;
11435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.remainingSpace_ = itemsInViewPort.last > dataModel.length;
11455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Mesurings must be placed after adding all the elements, to prevent
11475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // performance reducing.
11485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (!this.fixedHeight_) {
11497dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch        for (var y = firstIndex; y < lastIndex; y++) {
11505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          this.cachedItemHeights_[y] =
11517dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch              this.measureItemHeight_(this.cachedItems_[y]);
11527dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch        }
11535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
11545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
11555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
11575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Restore the lead item that is present in the list but may be updated
11585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * in the data model (supposed to be used inside a batch update). Usually
11595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * such an item would be recreated in the redraw method. If reinsertion
11605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * is undesirable (for instance to prevent losing focus) the item may be
11615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * updated and restored. Assumed the listItem relates to the same data item
11625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * as the lead item in the begin of the batch update.
11635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     *
11645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {ListItem} leadItem Already existing lead item.
11655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
11665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    restoreLeadItem: function(leadItem) {
11675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      delete this.cachedItems_[leadItem.listIndex];
11685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      leadItem.listIndex = this.selectionModel.leadIndex;
11705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.pinnedItem_ = this.cachedItems_[leadItem.listIndex] = leadItem;
11715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
11725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
11745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Invalidates list by removing cached items.
11755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
11765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    invalidate: function() {
11775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.cachedItems_ = {};
11785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.cachedItemSized_ = {};
11795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
11805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
11825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Redraws a single item.
11835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {number} index The row index to redraw.
11845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
11855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    redrawItem: function(index) {
11865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (index >= this.firstIndex_ &&
11875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          (index < this.lastIndex_ || this.remainingSpace_)) {
11885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        delete this.cachedItems_[index];
11895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.redraw();
11905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
11915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
11925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
11945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Called when a list item is activated, currently only by a double click
11955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * event.
11965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {number} index The index of the activated item.
11975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
11985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    activateItemAtIndex: function(index) {
11995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
12005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
12025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Returns a ListItem for the leadIndex. If the item isn't present in the
12035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * list creates it and inserts to the list (may be invisible if it's out of
12045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * the visible range).
12055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     *
12065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Item returned from this method won't be removed until it remains a lead
12075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * item or til the data model changes (unlike other items that could be
12085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * removed when they go out of the visible range).
12095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     *
12105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @return {cr.ui.ListItem} The lead item for the list.
12115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
12125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ensureLeadItemExists: function() {
12135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var index = this.selectionModel.leadIndex;
12145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (index < 0)
12155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return null;
12165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var cachedItems = this.cachedItems_ || {};
12175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var item = cachedItems[index] ||
12195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 this.createItem(this.dataModel.item(index));
12205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (this.pinnedItem_ != item && this.pinnedItem_ &&
12215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          this.pinnedItem_.hidden) {
12225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.removeChild(this.pinnedItem_);
12235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
12245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.pinnedItem_ = item;
12255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      cachedItems[index] = item;
12265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      item.listIndex = index;
12275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (item.parentNode == this)
12285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return item;
12295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (this.batchCount_ != 0)
12315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        item.hidden = true;
12325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Item will get to the right place in redraw. Choose place to insert
12345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // reducing items reinsertion.
12355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (index <= this.firstIndex_)
12365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.insertBefore(item, this.beforeFiller_.nextSibling);
12375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      else
12385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.insertBefore(item, this.afterFiller_);
12395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.redraw();
12405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return item;
12415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
1242b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
1243b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    /**
1244b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)     * Starts drag selection by reacting 'dragstart' event.
1245b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)     * @param {Event} event Event of dragstart.
1246b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)     */
1247b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    startDragSelection: function(event) {
1248b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      event.preventDefault();
1249b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      var border = document.createElement('div');
1250b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      border.className = 'drag-selection-border';
1251b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      var rect = this.getBoundingClientRect();
1252b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      var startX = event.clientX - rect.left + this.scrollLeft;
1253b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      var startY = event.clientY - rect.top + this.scrollTop;
1254b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      border.style.left = startX + 'px';
1255b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      border.style.top = startY + 'px';
1256b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      var onMouseMove = function(event) {
1257b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)        var inRect = this.getBoundingClientRect();
1258b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)        var x = event.clientX - inRect.left + this.scrollLeft;
1259b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)        var y = event.clientY - inRect.top + this.scrollTop;
1260b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)        border.style.left = Math.min(startX, x) + 'px';
1261b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)        border.style.top = Math.min(startY, y) + 'px';
1262b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)        border.style.width = Math.abs(startX - x) + 'px';
1263b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)        border.style.height = Math.abs(startY - y) + 'px';
1264b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      }.bind(this);
1265b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      var onMouseUp = function() {
1266b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)        this.removeChild(border);
1267b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)        document.removeEventListener('mousemove', onMouseMove, true);
1268b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)        document.removeEventListener('mouseup', onMouseUp, true);
1269b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      }.bind(this);
1270b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      document.addEventListener('mousemove', onMouseMove, true);
1271b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      document.addEventListener('mouseup', onMouseUp, true);
1272b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      this.appendChild(border);
1273b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    },
12745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  };
12755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  cr.defineProperty(List, 'disabled', cr.PropertyKind.BOOL_ATTR);
12775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /**
12795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * Whether the list or one of its descendents has focus. This is necessary
12805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * because list items can contain controls that can be focused, and for some
12815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * purposes (e.g., styling), the list can still be conceptually focused at
12825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * that point even though it doesn't actually have the page focus.
12835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   */
12845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  cr.defineProperty(List, 'hasElementFocus', cr.PropertyKind.BOOL_ATTR);
12855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return {
12875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    List: List
12885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  };
12895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)});
1290