Index: netlog_viewer/horizontal_scrollbar_view.js |
diff --git a/netlog_viewer/horizontal_scrollbar_view.js b/netlog_viewer/horizontal_scrollbar_view.js |
new file mode 100644 |
index 0000000000000000000000000000000000000000..d443c89717468ee7f7f5ebb2ae2fca91fecad244 |
--- /dev/null |
+++ b/netlog_viewer/horizontal_scrollbar_view.js |
@@ -0,0 +1,124 @@ |
+// 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; |
+})(); |
+ |