1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
8 * Creates a new scroll bar element.
9 * @extends {HTMLDivElement}
12 var ScrollBar = cr.ui.define('div');
15 * Mode of the scrollbar. As for now, only vertical scrollbars are supported.
23 ScrollBar.prototype = {
26 if (this.mode_ == ScrollBar.Mode.VERTICAL) {
27 this.classList.remove('scrollbar-horizontal');
28 this.classList.add('scrollbar-vertical');
30 this.classList.remove('scrollbar-vertical');
31 this.classList.add('scrollbar-horizontal');
41 * Inherits after HTMLDivElement.
43 ScrollBar.prototype.__proto__ = HTMLDivElement.prototype;
46 * Initializes the DOM structure of the scrollbar.
48 ScrollBar.prototype.decorate = function() {
49 this.classList.add('scrollbar');
50 this.button_ = util.createChild(this, 'scrollbar-button', 'div');
51 this.mode = ScrollBar.Mode.VERTICAL;
53 this.button_.addEventListener('mousedown',
54 this.onButtonPressed_.bind(this));
55 window.addEventListener('mouseup', this.onMouseUp_.bind(this));
56 window.addEventListener('mousemove', this.onMouseMove_.bind(this));
60 * Initialize a scrollbar.
62 * @param {Element} parent Parent element, must have a relative or absolute
64 * @param {Element=} opt_scrollableArea Element with scrollable contents.
65 * If not passed, then call attachToView manually when the scrollable
66 * element becomes available.
68 ScrollBar.prototype.initialize = function(parent, opt_scrollableArea) {
69 parent.appendChild(this);
70 if (opt_scrollableArea)
71 this.attachToView(opt_scrollableArea);
75 * Attaches the scrollbar to a scrollable element and attaches handlers.
76 * @param {Element} view Scrollable element.
78 ScrollBar.prototype.attachToView = function(view) {
80 this.view_.addEventListener('scroll', this.onScroll_.bind(this));
81 this.view_.addEventListener('relayout', this.onRelayout_.bind(this));
82 this.domObserver_ = new MutationObserver(this.onDomChanged_.bind(this));
83 this.domObserver_.observe(this.view_, {subtree: true, attributes: true});
91 ScrollBar.prototype.onScroll_ = function() {
92 this.scrollTop_ = this.view_.scrollTop;
100 ScrollBar.prototype.onRelayout_ = function() {
101 this.scrollHeight_ = this.view_.scrollHeight;
102 this.clientHeight_ = this.view_.clientHeight;
103 this.offsetTop_ = this.view_.offsetTop;
104 this.scrollTop_ = this.view_.scrollTop;
109 * Pressing on the scrollbar's button handler.
111 * @param {Event} event Pressing event.
114 ScrollBar.prototype.onButtonPressed_ = function(event) {
115 this.buttonPressed_ = true;
116 this.buttonPressedEvent_ = event;
117 this.buttonPressedPosition_ = this.button_.offsetTop - this.view_.offsetTop;
118 this.button_.classList.add('pressed');
120 event.preventDefault();
124 * Releasing the button handler. Note, that it may not be called when releasing
125 * outside of the window. Therefore this is also called from onMouseMove_.
127 * @param {Event} event Mouse event.
130 ScrollBar.prototype.onMouseUp_ = function(event) {
131 this.buttonPressed_ = false;
132 this.button_.classList.remove('pressed');
136 * Mouse move handler. Updates the scroll position.
138 * @param {Event} event Mouse event.
141 ScrollBar.prototype.onMouseMove_ = function(event) {
142 if (!this.buttonPressed_)
145 this.onMouseUp_(event);
148 var clientSize = this.getClientHeight();
149 var totalSize = this.getTotalHeight();
150 // TODO(hirono): Fix the geometric calculation. crbug.com/253779
151 var buttonSize = Math.max(50, clientSize / totalSize * clientSize);
152 var buttonPosition = this.buttonPressedPosition_ +
153 (event.screenY - this.buttonPressedEvent_.screenY);
154 // Ensures the scrollbar is in the view.
156 Math.max(0, Math.min(buttonPosition, clientSize - buttonSize));
158 if (clientSize > buttonSize) {
159 scrollPosition = Math.max(totalSize - clientSize, 0) *
160 buttonPosition / (clientSize - buttonSize);
165 this.scrollTop_ = scrollPosition;
166 this.view_.scrollTop = scrollPosition;
171 * Handles changed in Dom by redrawing the scrollbar. Ignores consecutive calls.
174 ScrollBar.prototype.onDomChanged_ = function() {
175 if (this.domChangedTimer_) {
176 clearTimeout(this.domChangedTimer_);
177 this.domChangedTimer_ = null;
179 this.domChangedTimer_ = setTimeout(function() {
181 this.domChangedTimer_ = null;
186 * Redraws the scrollbar.
189 ScrollBar.prototype.redraw_ = function() {
193 var clientSize = this.getClientHeight();
194 var clientTop = this.offsetTop_;
195 var scrollPosition = this.scrollTop_;
196 var totalSize = this.getTotalHeight();
197 var hidden = totalSize <= clientSize;
199 var buttonSize = Math.max(50, clientSize / totalSize * clientSize);
201 if (clientSize - buttonSize > 0) {
202 buttonPosition = scrollPosition / (totalSize - clientSize) *
203 (clientSize - buttonSize);
207 var buttonTop = buttonPosition + clientTop;
209 var time = Date.now();
210 if (this.hidden != hidden ||
211 this.lastButtonTop_ != buttonTop ||
212 this.lastButtonSize_ != buttonSize) {
213 requestAnimationFrame(function() {
214 this.hidden = hidden;
215 this.button_.style.top = buttonTop + 'px';
216 this.button_.style.height = buttonSize + 'px';
220 this.lastButtonTop_ = buttonTop;
221 this.lastButtonSize_ = buttonSize;
225 * Returns the viewport height of the view.
226 * @return {number} The viewport height of the view in px.
229 ScrollBar.prototype.getClientHeight = function() {
230 return this.clientHeight_;
234 * Returns the total height of the view.
235 * @return {number} The total height of the view in px.
238 ScrollBar.prototype.getTotalHeight = function() {
239 return this.scrollHeight_;
243 * Creates a new scroll bar for elements in the main panel.
244 * @extends {ScrollBar}
247 var MainPanelScrollBar = cr.ui.define('div');
250 * Inherits after ScrollBar.
252 MainPanelScrollBar.prototype.__proto__ = ScrollBar.prototype;
255 MainPanelScrollBar.prototype.decorate = function() {
256 ScrollBar.prototype.decorate.call(this);
259 * Margin for the transparent preview panel at the bottom.
263 this.bottomMarginForPanel_ = 0;
267 * GReturns the viewport height of the view, considering the preview panel.
269 * @return {number} The viewport height of the view in px.
273 MainPanelScrollBar.prototype.getClientHeight = function() {
274 return this.clientHeight_ - this.bottomMarginForPanel_;
278 * Returns the total height of the view, considering the preview panel.
280 * @return {number} The total height of the view in px.
284 MainPanelScrollBar.prototype.getTotalHeight = function() {
285 return this.scrollHeight_ - this.bottomMarginForPanel_;
289 * Sets the bottom margin height of the view for the transparent preview panel.
290 * @param {number} margin Margin to be set in px.
292 MainPanelScrollBar.prototype.setBottomMarginForPanel = function(margin) {
293 this.bottomMarginForPanel_ = margin;