list.js revision 4e180b6a0b4720a9b8e9e959a882386f690f08ff
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_);
3164e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      this.addEventListener('mousedown', handleMouseDown);
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) {
5154e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      if (!this.contains(e.relatedTarget))
5164e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        this.hasElementFocus = false;
5175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
5185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
5205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Returns the list item element containing the given element, or null if
5215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * it doesn't belong to any list item element.
5225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {HTMLElement} element The element.
5235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @return {ListItem} The list item containing |element|, or null.
5245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
5255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    getListItemAncestor: function(element) {
5265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var container = element;
5275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      while (container && container.parentNode != this) {
5285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        container = container.parentNode;
5295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
5305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return container;
5315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
5325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
5345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Handle a keydown event.
5355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {Event} e The keydown event.
5365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @return {boolean} Whether the key event was handled.
5375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
5385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    handleKeyDown: function(e) {
5395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (this.disabled)
5405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return;
5415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return this.selectionController_.handleKeyDown(e);
5435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
5445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
5465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Handle a scroll event.
5475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {Event} e The scroll event.
5485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
5495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    handleScroll: function(e) {
5507dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch      requestAnimationFrame(this.redraw.bind(this));
5515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
5525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
5545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Callback from the selection model. We dispatch {@code change} events
5555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * when the selection changes.
5564e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)     * @param {!Event} e Event with change info.
5575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @private
5585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
5595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    handleOnChange_: function(ce) {
5605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ce.changes.forEach(function(change) {
5615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var listItem = this.getListItemByIndex(change.index);
5625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (listItem) {
5635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          listItem.selected = change.selected;
5645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          if (change.selected) {
5655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            listItem.setAttribute('aria-posinset', change.index + 1);
5665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            listItem.setAttribute('aria-setsize', this.dataModel.length);
5675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            this.setAttribute('aria-activedescendant', listItem.id);
5685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          } else {
5695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            listItem.removeAttribute('aria-posinset');
5705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            listItem.removeAttribute('aria-setsize');
5715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          }
5725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
5735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }, this);
5745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      cr.dispatchSimpleEvent(this, 'change');
5765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
5775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
5795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Handles a change of the lead item from the selection model.
5805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {Event} pe The property change event.
5815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @private
5825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
5835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    handleLeadChange_: function(pe) {
5845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var element;
5855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (pe.oldValue != -1) {
5865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if ((element = this.getListItemByIndex(pe.oldValue)))
5875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          element.lead = false;
5885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
5895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (pe.newValue != -1) {
5915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if ((element = this.getListItemByIndex(pe.newValue)))
5925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          element.lead = true;
5935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (pe.oldValue != pe.newValue) {
5945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          this.scrollIndexIntoView(pe.newValue);
5955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          // If the lead item has a different height than other items, then we
5965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          // may run into a problem that requires a second attempt to scroll
5975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          // it into view. The first scroll attempt will trigger a redraw,
5985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          // which will clear out the list and repopulate it with new items.
5995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          // During the redraw, the list may shrink temporarily, which if the
6005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          // lead item is the last item, will move the scrollTop up since it
6015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          // cannot extend beyond the end of the list. (Sadly, being scrolled to
6025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          // the bottom of the list is not "sticky.") So, we set a timeout to
6035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          // rescroll the list after this all gets sorted out. This is perhaps
6045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          // not the most elegant solution, but no others seem obvious.
6055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          var self = this;
6065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          window.setTimeout(function() {
6075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            self.scrollIndexIntoView(pe.newValue);
6085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          });
6095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
6105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
6115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
6125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
6145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * This handles data model 'permuted' event.
6155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * this event is dispatched as a part of sort or splice.
6165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * We need to
6175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     *  - adjust the cache.
6185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     *  - adjust selection.
6195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     *  - redraw. (called in this.endBatchUpdates())
6205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     *  It is important that the cache adjustment happens before selection model
6215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     *  adjustments.
6225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {Event} e The 'permuted' event.
6235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
6245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    handleDataModelPermuted_: function(e) {
6255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var newCachedItems = {};
6265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for (var index in this.cachedItems_) {
6275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (e.permutation[index] != -1) {
6285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          var newIndex = e.permutation[index];
6295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          newCachedItems[newIndex] = this.cachedItems_[index];
6305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          newCachedItems[newIndex].listIndex = newIndex;
6315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
6325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
6335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.cachedItems_ = newCachedItems;
6345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var newCachedItemHeights = {};
6365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for (var index in this.cachedItemHeights_) {
6375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (e.permutation[index] != -1) {
6385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          newCachedItemHeights[e.permutation[index]] =
6395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              this.cachedItemHeights_[index];
6405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
6415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
6425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.cachedItemHeights_ = newCachedItemHeights;
6435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.startBatchUpdates();
6455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var sm = this.selectionModel;
6475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      sm.adjustLength(e.newLength);
6485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      sm.adjustToReordering(e.permutation);
6495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.endBatchUpdates();
6515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
6525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    handleDataModelChange_: function(e) {
6545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      delete this.cachedItems_[e.index];
6555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      delete this.cachedItemHeights_[e.index];
6565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.cachedMeasuredItem_ = null;
6575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (e.index >= this.firstIndex_ &&
6595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          (e.index < this.lastIndex_ || this.remainingSpace_)) {
6605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.redraw();
6615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
6625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
6635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
6655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {number} index The index of the item.
6665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @return {number} The top position of the item inside the list.
6675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
6685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    getItemTop: function(index) {
6695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (this.fixedHeight_) {
6705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var itemHeight = this.getDefaultItemHeight_();
6715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return index * itemHeight;
6725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } else {
6732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        this.ensureAllItemSizesInCache();
6745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var top = 0;
6755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        for (var i = 0; i < index; i++) {
6765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          top += this.getItemHeightByIndex_(i);
6775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
6785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return top;
6795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
6805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
6815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
6835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {number} index The index of the item.
6845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @return {number} The row of the item. May vary in the case
6855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     *     of multiple columns.
6865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
6875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    getItemRow: function(index) {
6885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return index;
6895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
6905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
6925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {number} row The row.
6935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @return {number} The index of the first item in the row.
6945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
6955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    getFirstItemInRow: function(row) {
6965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return row;
6975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
6985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
7005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Ensures that a given index is inside the viewport.
7015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {number} index The index of the item to scroll into view.
7025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @return {boolean} Whether any scrolling was needed.
7035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
7045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    scrollIndexIntoView: function(index) {
7055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var dataModel = this.dataModel;
7065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (!dataModel || index < 0 || index >= dataModel.length)
7075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return false;
7085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var itemHeight = this.getItemHeightByIndex_(index);
7105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var scrollTop = this.scrollTop;
7115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var top = this.getItemTop(index);
7125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var clientHeight = this.clientHeight;
7135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var self = this;
7155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Function to adjust the tops of viewport and row.
7165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      function scrollToAdjustTop() {
7175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          self.scrollTop = top;
7185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          return true;
7195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      };
7205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Function to adjust the bottoms of viewport and row.
7215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      function scrollToAdjustBottom() {
7225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          var cs = getComputedStyle(self);
7235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          var paddingY = parseInt(cs.paddingTop, 10) +
7245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                         parseInt(cs.paddingBottom, 10);
7255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          if (top + itemHeight > scrollTop + clientHeight - paddingY) {
7275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            self.scrollTop = top + itemHeight - clientHeight + paddingY;
7285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            return true;
7295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          }
7305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          return false;
7315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      };
7325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Check if the entire of given indexed row can be shown in the viewport.
7345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (itemHeight <= clientHeight) {
7355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (top < scrollTop)
7365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          return scrollToAdjustTop();
7375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (scrollTop + clientHeight < top + itemHeight)
7385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          return scrollToAdjustBottom();
7395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } else {
7405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (scrollTop < top)
7415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          return scrollToAdjustTop();
7425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (top + itemHeight < scrollTop + clientHeight)
7435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          return scrollToAdjustBottom();
7445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
7455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return false;
7465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
7475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
7495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @return {!ClientRect} The rect to use for the context menu.
7505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
7515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    getRectForContextMenu: function() {
7525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // TODO(arv): Add trait support so we can share more code between trees
7535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // and lists.
7545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var index = this.selectionModel.selectedIndex;
7555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var el = this.getListItemByIndex(index);
7565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (el)
7575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return el.getBoundingClientRect();
7585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return this.getBoundingClientRect();
7595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
7605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
7625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Takes a value from the data model and finds the associated list item.
7635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {*} value The value in the data model that we want to get the list
7645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     *     item for.
7655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @return {ListItem} The first found list item or null if not found.
7665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
7675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    getListItem: function(value) {
7685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var dataModel = this.dataModel;
7695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (dataModel) {
7705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var index = dataModel.indexOf(value);
7715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return this.getListItemByIndex(index);
7725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
7735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return null;
7745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
7755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
7775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Find the list item element at the given index.
7785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {number} index The index of the list item to get.
7795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @return {ListItem} The found list item or null if not found.
7805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
7815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    getListItemByIndex: function(index) {
7825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return this.cachedItems_[index] || null;
7835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
7845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
7865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Find the index of the given list item element.
7875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {ListItem} item The list item to get the index of.
7885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @return {number} The index of the list item, or -1 if not found.
7895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
7905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    getIndexOfListItem: function(item) {
7915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var index = item.listIndex;
7925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (this.cachedItems_[index] == item) {
7935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return index;
7945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
7955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return -1;
7965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
7975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
7995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Creates a new list item.
8005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {*} value The value to use for the item.
8015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @return {!ListItem} The newly created list item.
8025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
8035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    createItem: function(value) {
8045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var item = new this.itemConstructor_(value);
8055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      item.label = value;
8065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      item.id = this.uniqueIdPrefix_ + '-' + this.nextUniqueIdSuffix_++;
8075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (typeof item.decorate == 'function')
8085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        item.decorate();
8095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return item;
8105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
8115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
8135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Creates the selection controller to use internally.
8145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {cr.ui.ListSelectionModel} sm The underlying selection model.
8155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @return {!cr.ui.ListSelectionController} The newly created selection
8165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     *     controller.
8175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
8185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    createSelectionController: function(sm) {
8195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return new ListSelectionController(sm);
8205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
8215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
8235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Return the heights (in pixels) of the top of the given item index within
8245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * the list, and the height of the given item itself, accounting for the
8255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * possibility that the lead item may be a different height.
8265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {number} index The index to find the top height of.
8275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @return {{top: number, height: number}} The heights for the given index.
8285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @private
8295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
8305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    getHeightsForIndex_: function(index) {
8315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var itemHeight = this.getItemHeightByIndex_(index);
8325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var top = this.getItemTop(index);
8335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return {top: top, height: itemHeight};
8345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
8355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
8375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Find the index of the list item containing the given y offset (measured
8385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * in pixels from the top) within the list. In the case of multiple columns,
8395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * returns the first index in the row.
8405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {number} offset The y offset in pixels to get the index of.
8415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @return {number} The index of the list item. Returns the list size if
8425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     *     given offset exceeds the height of list.
8435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @private
8445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
8455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    getIndexForListOffset_: function(offset) {
8465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var itemHeight = this.getDefaultItemHeight_();
8475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (!itemHeight)
8485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return this.dataModel.length;
8495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (this.fixedHeight_)
8515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return this.getFirstItemInRow(Math.floor(offset / itemHeight));
8525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // If offset exceeds the height of list.
8545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var lastHeight = 0;
8555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (this.dataModel.length) {
8565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var h = this.getHeightsForIndex_(this.dataModel.length - 1);
8575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        lastHeight = h.top + h.height;
8585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
8595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (lastHeight < offset)
8605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return this.dataModel.length;
8615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Estimates index.
8635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var estimatedIndex = Math.min(Math.floor(offset / itemHeight),
8645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                    this.dataModel.length - 1);
8655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var isIncrementing = this.getItemTop(estimatedIndex) < offset;
8665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Searchs the correct index.
8685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      do {
8695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var heights = this.getHeightsForIndex_(estimatedIndex);
8705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var top = heights.top;
8715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var height = heights.height;
8725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (top <= offset && offset <= (top + height))
8745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          break;
8755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        isIncrementing ? ++estimatedIndex : --estimatedIndex;
8775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } while (0 < estimatedIndex && estimatedIndex < this.dataModel.length);
8785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return estimatedIndex;
8805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
8815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
8835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Return the number of items that occupy the range of heights between the
8845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * top of the start item and the end offset.
8855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {number} startIndex The index of the first visible item.
8865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {number} endOffset The y offset in pixels of the end of the list.
8875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @return {number} The number of list items visible.
8885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @private
8895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
8905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    countItemsInRange_: function(startIndex, endOffset) {
8915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var endIndex = this.getIndexForListOffset_(endOffset);
8925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return endIndex - startIndex + 1;
8935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
8945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
8965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Calculates the number of items fitting in the given viewport.
8975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {number} scrollTop The scroll top position.
8985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {number} clientHeight The height of viewport.
8995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @return {{first: number, length: number, last: number}} The index of
9005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     *     first item in view port, The number of items, The item past the last.
9015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
9025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    getItemsInViewPort: function(scrollTop, clientHeight) {
9035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (this.autoExpands_) {
9045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return {
9055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          first: 0,
9065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          length: this.dataModel.length,
9075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          last: this.dataModel.length};
9085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } else {
9095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var firstIndex = this.getIndexForListOffset_(scrollTop);
9105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var lastIndex = this.getIndexForListOffset_(scrollTop + clientHeight);
9115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return {
9135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          first: firstIndex,
9145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          length: lastIndex - firstIndex + 1,
9155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          last: lastIndex + 1};
9165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
9175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
9185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
9205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Merges list items currently existing in the list with items in the range
9215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * [firstIndex, lastIndex). Removes or adds items if needed.
9225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Doesn't delete {@code this.pinnedItem_} if it is present (instead hides
9237dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch     * it if it is out of the range).
9245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {number} firstIndex The index of first item, inclusively.
9255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {number} lastIndex The index of last item, exclusively.
9265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
9277dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    mergeItems: function(firstIndex, lastIndex) {
9285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var self = this;
9295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var dataModel = this.dataModel;
9305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var currentIndex = firstIndex;
9315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      function insert() {
9335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var dataItem = dataModel.item(currentIndex);
9347dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch        var newItem = self.cachedItems_[currentIndex] ||
9357dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch            self.createItem(dataItem);
9365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        newItem.listIndex = currentIndex;
9377dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch        self.cachedItems_[currentIndex] = newItem;
9385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        self.insertBefore(newItem, item);
9395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        currentIndex++;
9405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
9415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      function remove() {
9435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var next = item.nextSibling;
9445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (item != self.pinnedItem_)
9455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          self.removeChild(item);
9465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        item = next;
9475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
9485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for (var item = this.beforeFiller_.nextSibling;
9505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)           item != this.afterFiller_ && currentIndex < lastIndex;) {
9515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (!this.isItem(item)) {
9525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          item = item.nextSibling;
9535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          continue;
9545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
9555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var index = item.listIndex;
9577dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch        if (this.cachedItems_[index] != item || index < currentIndex) {
9585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          remove();
9595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        } else if (index == currentIndex) {
9607dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch          this.cachedItems_[currentIndex] = item;
9615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          item = item.nextSibling;
9625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          currentIndex++;
9635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        } else {  // index > currentIndex
9645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          insert();
9655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
9665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
9675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      while (item != this.afterFiller_) {
9695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (this.isItem(item))
9705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          remove();
9715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        else
9725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          item = item.nextSibling;
9735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
9745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (this.pinnedItem_) {
9765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var index = this.pinnedItem_.listIndex;
9775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.pinnedItem_.hidden = index < firstIndex || index >= lastIndex;
9787dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch        this.cachedItems_[index] = this.pinnedItem_;
9795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (index >= lastIndex)
9805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          item = this.pinnedItem_;  // Insert new items before this one.
9815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
9825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      while (currentIndex < lastIndex)
9845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        insert();
9855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
9865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
9885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Ensures that all the item sizes in the list have been already cached.
9895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
9905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ensureAllItemSizesInCache: function() {
9915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var measuringIndexes = [];
9925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var isElementAppended = [];
9935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for (var y = 0; y < this.dataModel.length; y++) {
9945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (!this.cachedItemHeights_[y]) {
9955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          measuringIndexes.push(y);
9965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          isElementAppended.push(false);
9975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
9985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
9995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var measuringItems = [];
10015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Adds temporary elements.
10025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for (var y = 0; y < measuringIndexes.length; y++) {
10035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var index = measuringIndexes[y];
10045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var dataItem = this.dataModel.item(index);
10055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var listItem = this.cachedItems_[index] || this.createItem(dataItem);
10065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        listItem.listIndex = index;
10075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // If |listItems| is not on the list, apppends it to the list and sets
10095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // the flag.
10105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (!listItem.parentNode) {
10115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          this.appendChild(listItem);
10125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          isElementAppended[y] = true;
10135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
10145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.cachedItems_[index] = listItem;
10165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        measuringItems.push(listItem);
10175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
10185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // All mesurings must be placed after adding all the elements, to prevent
10205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // performance reducing.
10215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for (var y = 0; y < measuringIndexes.length; y++) {
10225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var index = measuringIndexes[y];
10235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.cachedItemHeights_[index] =
10245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            this.measureItemHeight_(measuringItems[y]);
10255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
10265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Removes all the temprary elements.
10285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for (var y = 0; y < measuringIndexes.length; y++) {
10295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // If the list item has been appended above, removes it.
10305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (isElementAppended[y])
10315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          this.removeChild(measuringItems[y]);
10325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
10335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
10345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
10365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Returns the height of after filler in the list.
10375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {number} lastIndex The index of item past the last in viewport.
10385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @return {number} The height of after filler.
10395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
10405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    getAfterFillerHeight: function(lastIndex) {
10415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (this.fixedHeight_) {
10425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var itemHeight = this.getDefaultItemHeight_();
10435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return (this.dataModel.length - lastIndex) * itemHeight;
10445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
10455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var height = 0;
10475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for (var i = lastIndex; i < this.dataModel.length; i++)
10485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        height += this.getItemHeightByIndex_(i);
10495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return height;
10505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
10515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
10535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Redraws the viewport.
10545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
10555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    redraw: function() {
10565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (this.batchCount_ != 0)
10575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return;
10585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var dataModel = this.dataModel;
10605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (!dataModel || !this.autoExpands_ && this.clientHeight == 0) {
10615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.cachedItems_ = {};
10625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.firstIndex_ = 0;
10635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.lastIndex_ = 0;
10642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        this.remainingSpace_ = this.clientHeight != 0;
10655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.mergeItems(0, 0, {}, {});
10665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return;
10675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
10685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Save the previous positions before any manipulation of elements.
10705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var scrollTop = this.scrollTop;
10715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var clientHeight = this.clientHeight;
10725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Store all the item sizes into the cache in advance, to prevent
10745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // interleave measuring with mutating dom.
10755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (!this.fixedHeight_)
10765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.ensureAllItemSizesInCache();
10775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var autoExpands = this.autoExpands_;
10795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var itemsInViewPort = this.getItemsInViewPort(scrollTop, clientHeight);
10815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Draws the hidden rows just above/below the viewport to prevent
10825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // flashing in scroll.
10837dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch      var firstIndex = Math.max(
10847dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch          0,
10857dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch          Math.min(dataModel.length - 1, itemsInViewPort.first - 1));
10865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var lastIndex = Math.min(itemsInViewPort.last + 1, dataModel.length);
10875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var beforeFillerHeight =
10895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          this.autoExpands ? 0 : this.getItemTop(firstIndex);
10905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var afterFillerHeight =
10915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          this.autoExpands ? 0 : this.getAfterFillerHeight(lastIndex);
10925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.beforeFiller_.style.height = beforeFillerHeight + 'px';
10945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var sm = this.selectionModel;
10965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var leadIndex = sm.leadIndex;
10975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1098d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      // If the pinned item is hidden and it is not the lead item, then remove
1099d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      // it from cache. Note, that we restore the hidden status to false, since
1100d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      // the item is still in cache, and may be reused.
11015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (this.pinnedItem_ &&
11027dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch          this.pinnedItem_ != this.cachedItems_[leadIndex]) {
1103d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)        if (this.pinnedItem_.hidden) {
11045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          this.removeChild(this.pinnedItem_);
1105d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)          this.pinnedItem_.hidden = false;
1106d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)        }
11075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.pinnedItem_ = undefined;
11085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
11095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11107dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch      this.mergeItems(firstIndex, lastIndex);
11115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11127dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch      if (!this.pinnedItem_ && this.cachedItems_[leadIndex] &&
11137dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch          this.cachedItems_[leadIndex].parentNode == this) {
11147dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch        this.pinnedItem_ = this.cachedItems_[leadIndex];
11155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
11165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.afterFiller_.style.height = afterFillerHeight + 'px';
11185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11194e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      // Restores the number of pixels scrolled, since it might be changed while
11204e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      // DOM operations.
11214e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      this.scrollTop = scrollTop;
11224e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
11235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // We don't set the lead or selected properties until after adding all
11245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // items, in case they force relayout in response to these events.
11255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var listItem = null;
11267dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch      if (leadIndex != -1 && this.cachedItems_[leadIndex])
11277dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch        this.cachedItems_[leadIndex].lead = true;
11285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for (var y = firstIndex; y < lastIndex; y++) {
11295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (sm.getIndexSelected(y))
11307dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch          this.cachedItems_[y].selected = true;
11315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        else if (y != leadIndex)
11327dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch          listItem = this.cachedItems_[y];
11335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
11345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.firstIndex_ = firstIndex;
11365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.lastIndex_ = lastIndex;
11375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.remainingSpace_ = itemsInViewPort.last > dataModel.length;
11395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Mesurings must be placed after adding all the elements, to prevent
11415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // performance reducing.
11425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (!this.fixedHeight_) {
11437dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch        for (var y = firstIndex; y < lastIndex; y++) {
11445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          this.cachedItemHeights_[y] =
11457dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch              this.measureItemHeight_(this.cachedItems_[y]);
11467dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch        }
11475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
11485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
11495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
11515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Restore the lead item that is present in the list but may be updated
11525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * in the data model (supposed to be used inside a batch update). Usually
11535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * such an item would be recreated in the redraw method. If reinsertion
11545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * is undesirable (for instance to prevent losing focus) the item may be
11555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * updated and restored. Assumed the listItem relates to the same data item
11565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * as the lead item in the begin of the batch update.
11575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     *
11585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {ListItem} leadItem Already existing lead item.
11595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
11605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    restoreLeadItem: function(leadItem) {
11615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      delete this.cachedItems_[leadItem.listIndex];
11625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      leadItem.listIndex = this.selectionModel.leadIndex;
11645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.pinnedItem_ = this.cachedItems_[leadItem.listIndex] = leadItem;
11655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
11665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
11685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Invalidates list by removing cached items.
11695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
11705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    invalidate: function() {
11715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.cachedItems_ = {};
11725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.cachedItemSized_ = {};
11735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
11745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
11765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Redraws a single item.
11775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {number} index The row index to redraw.
11785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
11795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    redrawItem: function(index) {
11805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (index >= this.firstIndex_ &&
11815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          (index < this.lastIndex_ || this.remainingSpace_)) {
11825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        delete this.cachedItems_[index];
11835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.redraw();
11845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
11855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
11865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
11885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Called when a list item is activated, currently only by a double click
11895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * event.
11905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {number} index The index of the activated item.
11915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
11925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    activateItemAtIndex: function(index) {
11935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
11945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
11965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Returns a ListItem for the leadIndex. If the item isn't present in the
11975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * list creates it and inserts to the list (may be invisible if it's out of
11985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * the visible range).
11995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     *
12005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Item returned from this method won't be removed until it remains a lead
12015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * item or til the data model changes (unlike other items that could be
12025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * removed when they go out of the visible range).
12035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     *
12045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @return {cr.ui.ListItem} The lead item for the list.
12055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
12065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ensureLeadItemExists: function() {
12075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var index = this.selectionModel.leadIndex;
12085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (index < 0)
12095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return null;
12105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var cachedItems = this.cachedItems_ || {};
12115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var item = cachedItems[index] ||
12135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 this.createItem(this.dataModel.item(index));
12145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (this.pinnedItem_ != item && this.pinnedItem_ &&
12155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          this.pinnedItem_.hidden) {
12165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.removeChild(this.pinnedItem_);
12175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
12185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.pinnedItem_ = item;
12195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      cachedItems[index] = item;
12205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      item.listIndex = index;
12215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (item.parentNode == this)
12225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return item;
12235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (this.batchCount_ != 0)
12255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        item.hidden = true;
12265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Item will get to the right place in redraw. Choose place to insert
12285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // reducing items reinsertion.
12295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (index <= this.firstIndex_)
12305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.insertBefore(item, this.beforeFiller_.nextSibling);
12315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      else
12325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.insertBefore(item, this.afterFiller_);
12335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.redraw();
12345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return item;
12355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
1236b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
1237b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    /**
1238b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)     * Starts drag selection by reacting 'dragstart' event.
1239b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)     * @param {Event} event Event of dragstart.
1240b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)     */
1241b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    startDragSelection: function(event) {
1242b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      event.preventDefault();
1243b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      var border = document.createElement('div');
1244b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      border.className = 'drag-selection-border';
1245b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      var rect = this.getBoundingClientRect();
1246b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      var startX = event.clientX - rect.left + this.scrollLeft;
1247b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      var startY = event.clientY - rect.top + this.scrollTop;
1248b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      border.style.left = startX + 'px';
1249b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      border.style.top = startY + 'px';
1250b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      var onMouseMove = function(event) {
1251b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)        var inRect = this.getBoundingClientRect();
1252b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)        var x = event.clientX - inRect.left + this.scrollLeft;
1253b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)        var y = event.clientY - inRect.top + this.scrollTop;
1254b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)        border.style.left = Math.min(startX, x) + 'px';
1255b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)        border.style.top = Math.min(startY, y) + 'px';
1256b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)        border.style.width = Math.abs(startX - x) + 'px';
1257b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)        border.style.height = Math.abs(startY - y) + 'px';
1258b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      }.bind(this);
1259b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      var onMouseUp = function() {
1260b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)        this.removeChild(border);
1261b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)        document.removeEventListener('mousemove', onMouseMove, true);
1262b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)        document.removeEventListener('mouseup', onMouseUp, true);
1263b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      }.bind(this);
1264b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      document.addEventListener('mousemove', onMouseMove, true);
1265b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      document.addEventListener('mouseup', onMouseUp, true);
1266b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      this.appendChild(border);
1267b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    },
12685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  };
12695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  cr.defineProperty(List, 'disabled', cr.PropertyKind.BOOL_ATTR);
12715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /**
12735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * Whether the list or one of its descendents has focus. This is necessary
12745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * because list items can contain controls that can be focused, and for some
12755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * purposes (e.g., styling), the list can still be conceptually focused at
12765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * that point even though it doesn't actually have the page focus.
12775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   */
12785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  cr.defineProperty(List, 'hasElementFocus', cr.PropertyKind.BOOL_ATTR);
12795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12804e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  /**
12814e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)   * Mousedown event handler.
12824e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)   * @this {List}
12834e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)   * @param {MouseEvent} e The mouse event object.
12844e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)   */
12854e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  function handleMouseDown(e) {
12864e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    this.handlePointerDownUp_(e);
12874e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
12884e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    if (e.defaultPrevented)
12894e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      return;
12904e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
12914e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    // If non-focusable area in a list item is clicked and the item still
12924e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    // contains the focused element, the item did a special focus handling
12934e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    // [1] and we should not focus on the list.
12944e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    //
12954e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    // [1] For example, clicking non-focusable area gives focus on the first
12964e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    // form control in the item.
12974e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    var listItem = this.getListItemAncestor(e.target);
12984e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    if (listItem && !tryFocusOnAncestor(e.target, listItem) &&
12994e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        listItem.contains(listItem.ownerDocument.activeElement)) {
13004e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      e.preventDefault();
13014e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    }
13024e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  }
13034e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
13044e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  /**
13054e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)   * Try focusing on |eventTarget| or its ancestor under |root|.
13064e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)   * This is a helper for handleMouseDown.
13074e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)   * @param {!Element} start An element which we start to try.
13084e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)   * @param {!Element} root An element which we finish to try.
13094e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)   * @return {boolean} True if we focused on an element successfully.
13104e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)   */
13114e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  function tryFocusOnAncestor(start, root) {
13124e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    for (var element = start; element && element != root;
13134e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        element = element.parentElement) {
13144e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      element.focus();
13154e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      if (root.ownerDocument.activeElement == element)
13164e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        return true;
13174e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    }
13184e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    return false;
13194e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  }
13204e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
13215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return {
13225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    List: List
13235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  };
13245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)});
1325