// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

/**
 * This view consists of two nested divs.  The outer one has a horizontal
 * scrollbar and the inner one has a height of 1 pixel and a width set to
 * allow an appropriate scroll range.  The view reports scroll events to
 * a callback specified on construction.
 *
 * All this funkiness is necessary because there is no HTML scroll control.
 * TODO(mmenke):  Consider implementing our own scrollbar directly.
 */
var HorizontalScrollbarView = (function() {
  'use strict';

  // We inherit from DivView.
  var superClass = DivView;

  /**
   * @constructor
   */
  function HorizontalScrollbarView(divId, innerDivId, callback) {
    superClass.call(this, divId);
    this.callback_ = callback;
    this.innerDiv_ = $(innerDivId);
    $(divId).onscroll = this.onScroll_.bind(this);

    // The current range and position of the scrollbar.  Because DOM updates
    // are asynchronous, the current state cannot be read directly from the DOM
    // after updating the range.
    this.range_ = 0;
    this.position_ = 0;

    // The DOM updates asynchronously, so sometimes we need a timer to update
    // the current scroll position after resizing the scrollbar.
    this.updatePositionTimerId_ = null;
  }

  HorizontalScrollbarView.prototype = {
    // Inherit the superclass's methods.
    __proto__: superClass.prototype,

    setGeometry: function(left, top, width, height) {
      superClass.prototype.setGeometry.call(this, left, top, width, height);
      this.setRange(this.range_);
    },

    show: function(isVisible) {
      superClass.prototype.show.call(this, isVisible);
    },

    /**
     * Sets the range of the scrollbar.  The scrollbar can have a value
     * anywhere from 0 to |range|, inclusive.  The width of the drag area
     * on the scrollbar will generally be based on the width of the scrollbar
     * relative to the size of |range|, so if the scrollbar is about the size
     * of the thing we're scrolling, we get fairly nice behavior.
     *
     * If |range| is less than the original position, |position_| is set to
     * |range|.  Otherwise, it is not modified.
     */
    setRange: function(range) {
      this.range_ = range;
      setNodeWidth(this.innerDiv_, this.getWidth() + range);
      if (range < this.position_)
        this.position_ = range;
      this.setPosition(this.position_);
    },

    /**
     * Sets the position of the scrollbar.  |position| must be between 0 and
     * |range_|, inclusive.
     */
    setPosition: function(position) {
      this.position_ = position;
      this.updatePosition_();
    },

    /**
     * Updates the visible position of the scrollbar to be |position_|.
     * On failure, calls itself again after a timeout.  This is needed because
     * setRange does not synchronously update the DOM.
     */
    updatePosition_: function() {
      // Clear the timer if we have one, so we don't have two timers running at
      // once.  This is safe even if we were just called from the timer, in
      // which case clearTimeout will silently fail.
      if (this.updatePositionTimerId_ !== null) {
        window.clearTimeout(this.updatePositionTimerId_);
        this.updatePositionTimerId_ = null;
      }

      this.getNode().scrollLeft = this.position_;
      if (this.getNode().scrollLeft != this.position_) {
        this.updatePositionTimerId_ =
            window.setTimeout(this.updatePosition_.bind(this));
      }
    },

    getRange: function() {
      return this.range_;
    },

    getPosition: function() {
      return this.position_;
    },

    onScroll_: function() {
      // If we're waiting to update the range, ignore messages from the
      // scrollbar.
      if (this.updatePositionTimerId_ !== null)
        return;
      var newPosition = this.getNode().scrollLeft;
      if (newPosition == this.position_)
        return;
      this.position_ = newPosition;
      this.callback_();
    }
  };

  return HorizontalScrollbarView;
})();