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)/**
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @fileoverview Card slider implementation. Allows you to create interactions
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * that have items that can slide left to right to reveal additional items.
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Works by adding the necessary event handlers to a specific DOM structure
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * including a frame, container and cards.
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * - The frame defines the boundary of one item. Each card will be expanded to
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *   fill the width of the frame. This element is also overflow hidden so that
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *   the additional items left / right do not trigger horizontal scrolling.
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * - The container is what all the touch events are attached to. This element
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *   will be expanded to be the width of all cards.
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * - The cards are the individual viewable items. There should be one card for
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *   each item in the list. Only one card will be visible at a time. Two cards
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *   will be visible while you are transitioning between cards.
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * This class is designed to work well on any hardware-accelerated touch device.
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * It should still work on pre-hardware accelerated devices it just won't feel
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * very good. It should also work well with a mouse.
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use an anonymous function to enable strict mode just for this file (which
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// will be concatenated with other files when embedded in Chrome
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)cr.define('cr.ui', function() {
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  'use strict';
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /**
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * @constructor
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * @param {!Element} frame The bounding rectangle that cards are visible in.
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * @param {!Element} container The surrounding element that will have event
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   *     listeners attached to it.
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * @param {number} cardWidth The width of each card should have.
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   */
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  function CardSlider(frame, container, cardWidth) {
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @type {!Element}
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @private
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.frame_ = frame;
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @type {!Element}
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @private
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.container_ = container;
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Array of card elements.
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @type {!Array.<!Element>}
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @private
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.cards_ = [];
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Index of currently shown card.
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @type {number}
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @private
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.currentCard_ = -1;
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @type {number}
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @private
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.cardWidth_ = cardWidth;
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @type {!cr.ui.TouchHandler}
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @private
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.touchHandler_ = new cr.ui.TouchHandler(this.container_);
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /**
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * The time to transition between cards when animating. Measured in ms.
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * @type {number}
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * @private
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * @const
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   */
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CardSlider.TRANSITION_TIME_ = 200;
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /**
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * The minimum velocity required to transition cards if they did not drag past
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * the halfway point between cards. Measured in pixels / ms.
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * @type {number}
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * @private
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * @const
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   */
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CardSlider.TRANSITION_VELOCITY_THRESHOLD_ = 0.2;
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CardSlider.prototype = {
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * The current left offset of the container relative to the frame. This
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * position does not include deltas from active drag operations, and
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * always aligns with a frame boundary.
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @type {number}
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @private
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    currentLeft_: 0,
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Current offset relative to |currentLeft_| due to an active drag
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * operation.
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @type {number}
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @private
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    deltaX_: 0,
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Initialize all elements and event handlers. Must call after construction
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * and before usage.
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {boolean} ignoreMouseWheelEvents If true, horizontal mouse wheel
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     *     events will be ignored, rather than flipping between pages.
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    initialize: function(ignoreMouseWheelEvents) {
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var view = this.container_.ownerDocument.defaultView;
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      assert(view.getComputedStyle(this.container_).display == '-webkit-box',
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          'Container should be display -webkit-box.');
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      assert(view.getComputedStyle(this.frame_).overflow == 'hidden',
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          'Frame should be overflow hidden.');
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      assert(view.getComputedStyle(this.container_).position == 'static',
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          'Container should be position static.');
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.updateCardWidths_();
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.mouseWheelScrollAmount_ = 0;
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.mouseWheelCardSelected_ = false;
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.mouseWheelIsContinuous_ = false;
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.scrollClearTimeout_ = null;
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (!ignoreMouseWheelEvents) {
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.frame_.addEventListener('mousewheel',
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                     this.onMouseWheel_.bind(this));
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.container_.addEventListener(
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          'webkitTransitionEnd', this.onWebkitTransitionEnd_.bind(this));
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Also support touch events in case a touch screen happens to be
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // available.  Note that this has minimal impact in the common case of
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // no touch events (eg. we're mainly just adding listeners for events that
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // will never trigger).
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var TouchHandler = cr.ui.TouchHandler;
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.container_.addEventListener(TouchHandler.EventType.TOUCH_START,
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                       this.onTouchStart_.bind(this));
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.container_.addEventListener(TouchHandler.EventType.DRAG_START,
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                       this.onDragStart_.bind(this));
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.container_.addEventListener(TouchHandler.EventType.DRAG_MOVE,
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                       this.onDragMove_.bind(this));
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.container_.addEventListener(TouchHandler.EventType.DRAG_END,
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                       this.onDragEnd_.bind(this));
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.touchHandler_.enable(/* opt_capture */ false);
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Use in cases where the width of the frame has changed in order to update
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * the width of cards. For example should be used when orientation changes
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * in full width sliders.
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {number} newCardWidth Width all cards should have, in pixels.
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    resize: function(newCardWidth) {
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (newCardWidth != this.cardWidth_) {
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.cardWidth_ = newCardWidth;
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.updateCardWidths_();
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // Must upate the transform on the container to show the correct card.
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.transformToCurrentCard_();
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Sets the cards used. Can be called more than once to switch card sets.
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {!Array.<!Element>} cards The individual viewable cards.
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {number} index Index of the card to in the new set of cards to
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     *     navigate to.
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    setCards: function(cards, index) {
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      assert(index >= 0 && index < cards.length,
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          'Invalid index in CardSlider#setCards');
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.cards_ = cards;
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.updateCardWidths_();
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.updateSelectedCardAttributes_();
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Jump to the given card index.
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.selectCard(index, false, false, true);
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Ensures that for all cards:
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * - if the card is the current card, then it has 'selected-card' in its
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     *   classList, and is visible for accessibility
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * - if the card is not the selected card, then it does not have
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     *   'selected-card' in its classList, and is invisible for accessibility.
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @private
2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    updateSelectedCardAttributes_: function() {
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for (var i = 0; i < this.cards_.length; i++) {
2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (i == this.currentCard_) {
2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          this.cards_[i].classList.add('selected-card');
2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          this.cards_[i].removeAttribute('aria-hidden');
2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        } else {
2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          this.cards_[i].classList.remove('selected-card');
2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          this.cards_[i].setAttribute('aria-hidden', true);
2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Updates the width of each card.
2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @private
2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    updateCardWidths_: function() {
2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for (var i = 0, card; card = this.cards_[i]; i++)
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        card.style.width = this.cardWidth_ + 'px';
2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Returns the index of the current card.
2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @return {number} index of the current card.
2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    get currentCard() {
2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return this.currentCard_;
2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Allows setting the current card index.
2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {number} index A new index to set the current index to.
2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @return {number} The new index after having been set.
2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    set currentCard(index) {
2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return (this.currentCard_ = index);
2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Returns the number of cards.
2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @return {number} number of cards.
2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    get cardCount() {
2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return this.cards_.length;
2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Returns the current card itself.
2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @return {!Element} the currently shown card.
2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    get currentCardValue() {
2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return this.cards_[this.currentCard_];
2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Returns the frame holding the cards.
2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @return {Element} The frame used to position the cards.
2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    get frame() {
2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return this.frame_;
2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Handle horizontal scrolls to flip between pages.
2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @private
2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    onMouseWheel_: function(e) {
2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (e.wheelDeltaX == 0)
2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return;
2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Continuous devices such as an Apple Touchpad or Apple MagicMouse will
2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // send arbitrary delta values. Conversly, standard mousewheels will
2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // send delta values in increments of 120.  (There is of course a small
2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // chance we mistake a continuous device for a non-continuous device.
2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Unfortunately there isn't a better way to do this until real touch
2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // events are available to desktop clients.)
2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var DISCRETE_DELTA = 120;
2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (e.wheelDeltaX % DISCRETE_DELTA)
2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.mouseWheelIsContinuous_ = true;
2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (this.mouseWheelIsContinuous_) {
2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // For continuous devices, detect a page swipe when the accumulated
2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // delta matches a pre-defined threshhold.  After changing the page,
2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // ignore wheel events for a short time before repeating this process.
2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (this.mouseWheelCardSelected_) return;
2875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.mouseWheelScrollAmount_ += e.wheelDeltaX;
2885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (Math.abs(this.mouseWheelScrollAmount_) >= 600) {
2895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          var pagesToScroll = this.mouseWheelScrollAmount_ > 0 ? 1 : -1;
2905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          if (!isRTL())
2915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            pagesToScroll *= -1;
2925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          var newCardIndex = this.currentCard + pagesToScroll;
2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          newCardIndex = Math.min(this.cards_.length - 1,
2945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  Math.max(0, newCardIndex));
2955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          this.selectCard(newCardIndex, true);
2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          this.mouseWheelCardSelected_ = true;
2975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
2985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } else {
2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // For discrete devices, consider each wheel tick a page change.
3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var pagesToScroll = e.wheelDeltaX / DISCRETE_DELTA;
3015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (!isRTL())
3025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          pagesToScroll *= -1;
3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var newCardIndex = this.currentCard + pagesToScroll;
3045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        newCardIndex = Math.min(this.cards_.length - 1,
3055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                Math.max(0, newCardIndex));
3065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.selectCard(newCardIndex, true);
3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // We got a mouse wheel event, so cancel any pending scroll wheel timeout.
3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (this.scrollClearTimeout_ != null)
3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        clearTimeout(this.scrollClearTimeout_);
3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // If we didn't use up all the scroll, hold onto it for a little bit, but
3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // drop it after a delay.
3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (this.mouseWheelScrollAmount_ != 0) {
3155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.scrollClearTimeout_ =
3165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            setTimeout(this.clearMouseWheelScroll_.bind(this), 500);
3175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
3185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
3195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Resets the amount of horizontal scroll we've seen to 0. See
3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * onMouseWheel_.
3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @private
3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    clearMouseWheelScroll_: function() {
3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.mouseWheelScrollAmount_ = 0;
3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.mouseWheelCardSelected_ = false;
3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Handles the ends of -webkit-transitions on -webkit-transform (animated
3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * card switches).
3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {Event} e The webkitTransitionEnd event.
3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @private
3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
3365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    onWebkitTransitionEnd_: function(e) {
3375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Ignore irrelevant transitions that might bubble up.
3385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (e.target !== this.container_ ||
3395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          e.propertyName != '-webkit-transform') {
3405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return;
3415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
3425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.fireChangeEndedEvent_(true);
3435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
3445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Dispatches a simple event to tell subscribers we're done moving to the
3475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * newly selected card.
3485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {boolean} wasAnimated whether or not the change was animated.
3495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @private
3505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
3515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    fireChangeEndedEvent_: function(wasAnimated) {
3525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var e = document.createEvent('Event');
3535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      e.initEvent('cardSlider:card_change_ended', true, true);
3545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      e.cardSlider = this;
3555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      e.changedTo = this.currentCard_;
3565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      e.wasAnimated = wasAnimated;
3575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.container_.dispatchEvent(e);
3585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
3595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
3615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Add a card to the card slider at a particular index. If the card being
3625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * added is inserted in front of the current card, cardSlider.currentCard
3635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * will be adjusted accordingly (to current card + 1).
3645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {!Node} card A card that will be added to the card slider.
3655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {number} index An index at which the given |card| should be
3665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     *     inserted. Must be positive and less than the number of cards.
3675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
3685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    addCardAtIndex: function(card, index) {
3695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      assert(card instanceof Node, '|card| isn\'t a Node');
3705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.assertValidIndex_(index);
3715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.cards_ = Array.prototype.concat.call(
3725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          this.cards_.slice(0, index), card, this.cards_.slice(index));
3735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.updateSelectedCardAttributes_();
3755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (this.currentCard_ == -1)
3775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.currentCard_ = 0;
3785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      else if (index <= this.currentCard_)
3795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.selectCard(this.currentCard_ + 1, false, true, true);
3805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.fireAddedEvent_(card, index);
3825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
3835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
3855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Append a card to the end of the list.
3865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {!Node} card A card to add at the end of the card slider.
3875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
3885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    appendCard: function(card) {
3895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      assert(card instanceof Node, '|card| isn\'t a Node');
3905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.cards_.push(card);
3915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.fireAddedEvent_(card, this.cards_.length - 1);
3925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
3935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
3955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Dispatches a simple event to tell interested subscribers that a card was
3965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * added to this card slider.
3975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {Node} card The recently added card.
3985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {number} index The position of the newly added card.
3995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @private
4005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
4015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    fireAddedEvent_: function(card, index) {
4025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.assertValidIndex_(index);
4035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var e = document.createEvent('Event');
4045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      e.initEvent('cardSlider:card_added', true, true);
4055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      e.addedIndex = index;
4065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      e.addedCard = card;
4075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.container_.dispatchEvent(e);
4085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
4095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
4112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * Returns the card at a particular index.
4122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * @param {number} index The index of the card to return.
4132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * @return {!Element} The card at the given index.
4142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     */
4152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    getCardAtIndex: function(index) {
4162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      this.assertValidIndex_(index);
4172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return this.cards_[index];
4182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    },
4192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    /**
4215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Removes a card by index from the card slider. If the card to be removed
4225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * is the current card or in front of the current card, the current card
4235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * will be updated (to current card - 1).
4245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {!Node} card A card to be removed.
4255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
4265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    removeCard: function(card) {
4275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      assert(card instanceof Node, '|card| isn\'t a Node');
4285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.removeCardAtIndex(this.cards_.indexOf(card));
4295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
4305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
4325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Removes a card by index from the card slider. If the card to be removed
4335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * is the current card or in front of the current card, the current card
4345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * will be updated (to current card - 1).
4355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {number} index The index of the tile that should be removed.
4365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
4375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    removeCardAtIndex: function(index) {
4385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.assertValidIndex_(index);
4395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var removed = this.cards_.splice(index, 1).pop();
4405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (this.cards_.length == 0)
4425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.currentCard_ = -1;
4435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      else if (index < this.currentCard_)
4445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.selectCard(this.currentCard_ - 1, false, true);
4455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.fireRemovedEvent_(removed, index);
4475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
4485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
4505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Dispatches a cardSlider:card_removed event so interested subscribers know
4515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * when a card was removed from this card slider.
4525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {Node} card The recently removed card.
4535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {number} index The index of the card before it was removed.
4545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @private
4555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
4565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    fireRemovedEvent_: function(card, index) {
4575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var e = document.createEvent('Event');
4585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      e.initEvent('cardSlider:card_removed', true, true);
4595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      e.removedCard = card;
4605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      e.removedIndex = index;
4615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.container_.dispatchEvent(e);
4625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
4635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
4655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * This re-syncs the -webkit-transform that's used to position the frame in
4665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * the likely event it needs to be updated by a card being inserted or
4675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * removed in the flow.
4685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
4695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    repositionFrame: function() {
4705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.transformToCurrentCard_();
4715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
4725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
4745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Checks the the given |index| exists in this.cards_.
4755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {number} index An index to check.
4765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @private
4775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
4785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    assertValidIndex_: function(index) {
4795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      assert(index >= 0 && index < this.cards_.length);
4805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
4815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
4835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Selects a new card, ensuring that it is a valid index, transforming the
4845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * view and possibly calling the change card callback.
4855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {number} newCardIndex Index of card to show.
4865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {boolean=} opt_animate If true will animate transition from
4875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     *     current position to new position.
4885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {boolean=} opt_dontNotify If true, don't tell subscribers that
4895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     *     we've changed cards.
4905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {boolean=} opt_forceChange If true, ignore if the card already
4915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     *     selected.
4925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
4935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    selectCard: function(newCardIndex,
4945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                         opt_animate,
4955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                         opt_dontNotify,
4965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                         opt_forceChange) {
4975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.assertValidIndex_(newCardIndex);
4985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var previousCard = this.currentCardValue;
5005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var isChangingCard =
5015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          !this.cards_[newCardIndex].classList.contains('selected-card');
5025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (typeof opt_forceChange != 'undefined' && opt_forceChange)
5045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        isChangingCard = true;
5055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (isChangingCard) {
5075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.currentCard_ = newCardIndex;
5085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.updateSelectedCardAttributes_();
5095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
5105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var willTransitionHappen = this.transformToCurrentCard_(opt_animate);
5125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (isChangingCard && !opt_dontNotify) {
5145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var event = document.createEvent('Event');
5155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        event.initEvent('cardSlider:card_changed', true, true);
5165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        event.cardSlider = this;
5175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        event.wasAnimated = !!opt_animate;
5185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.container_.dispatchEvent(event);
5195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // We also dispatch an event on the cards themselves.
5215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (previousCard) {
5225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          cr.dispatchSimpleEvent(previousCard, 'carddeselected',
5235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                 true, true);
5245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
5255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        cr.dispatchSimpleEvent(this.currentCardValue, 'cardselected',
5265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                               true, true);
5275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
5285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // If we're not changing, animated, or transitioning, fire a
5305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // cardSlider:card_change_ended event right away.
5315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if ((!isChangingCard || !opt_animate || !willTransitionHappen) &&
5325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          !opt_dontNotify) {
5335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.fireChangeEndedEvent_(false);
5345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
5355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
5365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
5385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Selects a card from the stack. Passes through to selectCard.
5395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {Node} newCard The card that should be selected.
5405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {boolean=} opt_animate Whether to animate.
5415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
5425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    selectCardByValue: function(newCard, opt_animate) {
5435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var i = this.cards_.indexOf(newCard);
5445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      assert(i != -1);
5455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.selectCard(i, opt_animate);
5465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
5475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
5495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Centers the view on the card denoted by this.currentCard. Can either
5505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * animate to that card or snap to it.
5515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {boolean=} opt_animate If true will animate transition from
5525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     *     current position to new position.
5535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @return {boolean} Whether or not a transformation was necessary.
5545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @private
5555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
5565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    transformToCurrentCard_: function(opt_animate) {
5575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var prevLeft = this.currentLeft_;
5585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.currentLeft_ = -this.cardWidth_ *
5595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          (isRTL() ? this.cards_.length - this.currentCard - 1 :
5605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     this.currentCard);
5615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // If there's no change, return something to let the caller know there
5635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // won't be a transition occuring.
5645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (prevLeft == this.currentLeft_ && this.deltaX_ == 0)
5655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return false;
5665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Animate to the current card, which will either transition if the
5685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // current card is new, or reset the existing card if we didn't drag
5695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // enough to change cards.
5705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var transition = '';
5715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (opt_animate) {
5725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        transition = '-webkit-transform ' + CardSlider.TRANSITION_TIME_ +
5735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     'ms ease-in-out';
5745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
5755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.container_.style.WebkitTransition = transition;
5765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.translateTo_(this.currentLeft_);
5775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return true;
5795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
5805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
5825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Moves the view to the specified position.
5835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {number} x Horizontal position to move to.
5845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @private
5855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
5865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    translateTo_: function(x) {
5875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // We use a webkitTransform to slide because this is GPU accelerated on
5885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Chrome and iOS.  Once Chrome does GPU acceleration on the position
5895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // fixed-layout elements we could simply set the element's position to
5905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // fixed and modify 'left' instead.
5915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.deltaX_ = x - this.currentLeft_;
5925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.container_.style.WebkitTransform = 'translate3d(' + x + 'px, 0, 0)';
5935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
5945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /* Touch ******************************************************************/
5965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
5985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Clear any transition that is in progress and enable dragging for the
5995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * touch.
6005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {!cr.ui.TouchHandler.Event} e The TouchHandler event.
6015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @private
6025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
6035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    onTouchStart_: function(e) {
6045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.container_.style.WebkitTransition = '';
6055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      e.enableDrag = true;
6065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
6075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
6095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Tell the TouchHandler that dragging is acceptable when the user begins by
6105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * scrolling horizontally and there is more than one card to slide.
6115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {!cr.ui.TouchHandler.Event} e The TouchHandler event.
6125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @private
6135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
6145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    onDragStart_: function(e) {
6155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      e.enableDrag = this.cardCount > 1 && Math.abs(e.dragDeltaX) >
6165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          Math.abs(e.dragDeltaY);
6175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
6185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
6205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * On each drag move event reposition the container appropriately so the
6215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * cards look like they are sliding.
6225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {!cr.ui.TouchHandler.Event} e The TouchHandler event.
6235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @private
6245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
6255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    onDragMove_: function(e) {
6265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var deltaX = e.dragDeltaX;
6275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // If dragging beyond the first or last card then apply a backoff so the
6285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // dragging feels stickier than usual.
6295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (!this.currentCard && deltaX > 0 ||
6305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          this.currentCard == (this.cards_.length - 1) && deltaX < 0) {
6315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        deltaX /= 2;
6325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
6335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.translateTo_(this.currentLeft_ + deltaX);
6345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
6355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
6375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * On drag end events we may want to transition to another card, depending
6385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * on the ending position of the drag and the velocity of the drag.
6395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @param {!cr.ui.TouchHandler.Event} e The TouchHandler event.
6405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * @private
6415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
6425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    onDragEnd_: function(e) {
6435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var deltaX = e.dragDeltaX;
6445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var velocity = this.touchHandler_.getEndVelocity().x;
6455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var newX = this.currentLeft_ + deltaX;
6465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var newCardIndex = Math.round(-newX / this.cardWidth_);
6475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (newCardIndex == this.currentCard && Math.abs(velocity) >
6495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          CardSlider.TRANSITION_VELOCITY_THRESHOLD_) {
6505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // The drag wasn't far enough to change cards but the velocity was
6515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // high enough to transition anyways. If the velocity is to the left
6525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // (negative) then the user wishes to go right (card + 1).
6535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        newCardIndex += velocity > 0 ? -1 : 1;
6545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
6555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Ensure that the new card index is valid.  The new card index could be
6565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // invalid if a swipe suggests scrolling off the end of the list of
6575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // cards.
6585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (newCardIndex < 0)
6595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        newCardIndex = 0;
6605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      else if (newCardIndex >= this.cardCount)
6615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        newCardIndex = this.cardCount - 1;
6625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.selectCard(newCardIndex, /* animate */ true);
6635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
6645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
6665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Cancel any current touch/slide as if we saw a touch end
6675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
6685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    cancelTouch: function() {
6695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Stop listening to any current touch
6705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.touchHandler_.cancelTouch();
6715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Ensure we're at a card bounary
6735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.transformToCurrentCard_(true);
6745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
6755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  };
6765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return {
6785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    CardSlider: CardSlider
6795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  };
6805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)});
681