Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / ui / file_manager / file_manager / foreground / js / scrollbar.js
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.
4
5 'use strict';
6
7 /**
8  * Creates a new scroll bar element.
9  * @extends {HTMLDivElement}
10  * @constructor
11  */
12 var ScrollBar = cr.ui.define('div');
13
14 /**
15  * Mode of the scrollbar. As for now, only vertical scrollbars are supported.
16  * @type {number}
17  */
18 ScrollBar.Mode = {
19   VERTICAL: 0,
20   HORIZONTAL: 1
21 };
22
23 ScrollBar.prototype = {
24   set mode(value) {
25     this.mode_ = value;
26     if (this.mode_ == ScrollBar.Mode.VERTICAL) {
27       this.classList.remove('scrollbar-horizontal');
28       this.classList.add('scrollbar-vertical');
29     } else {
30       this.classList.remove('scrollbar-vertical');
31       this.classList.add('scrollbar-horizontal');
32     }
33     this.redraw_();
34   },
35   get mode() {
36     return this.mode_;
37   }
38 };
39
40 /**
41  * Inherits after HTMLDivElement.
42  */
43 ScrollBar.prototype.__proto__ = HTMLDivElement.prototype;
44
45 /**
46  * Initializes the DOM structure of the scrollbar.
47  */
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;
52
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));
57 };
58
59 /**
60  * Initialize a scrollbar.
61  *
62  * @param {Element} parent Parent element, must have a relative or absolute
63  *     positioning.
64  * @param {Element=} opt_scrollableArea Element with scrollable contents.
65  *     If not passed, then call attachToView manually when the scrollable
66  *     element becomes available.
67  */
68 ScrollBar.prototype.initialize = function(parent, opt_scrollableArea) {
69   parent.appendChild(this);
70   if (opt_scrollableArea)
71     this.attachToView(opt_scrollableArea);
72 };
73
74 /**
75  * Attaches the scrollbar to a scrollable element and attaches handlers.
76  * @param {Element} view Scrollable element.
77  */
78 ScrollBar.prototype.attachToView = function(view) {
79   this.view_ = 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});
84   this.onRelayout_();
85 };
86
87 /**
88  * Scroll handler.
89  * @private
90  */
91 ScrollBar.prototype.onScroll_ = function() {
92   this.scrollTop_ = this.view_.scrollTop;
93   this.redraw_();
94 };
95
96 /**
97  * Relayout handler.
98  * @private
99  */
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;
105   this.redraw_();
106 };
107
108 /**
109  * Pressing on the scrollbar's button handler.
110  *
111  * @param {Event} event Pressing event.
112  * @private
113  */
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');
119
120   event.preventDefault();
121 };
122
123 /**
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_.
126  *
127  * @param {Event} event Mouse event.
128  * @private
129  */
130 ScrollBar.prototype.onMouseUp_ = function(event) {
131   this.buttonPressed_ = false;
132   this.button_.classList.remove('pressed');
133 };
134
135 /**
136  * Mouse move handler. Updates the scroll position.
137  *
138  * @param {Event} event Mouse event.
139  * @private
140  */
141 ScrollBar.prototype.onMouseMove_ = function(event) {
142   if (!this.buttonPressed_)
143     return;
144   if (!event.which) {
145     this.onMouseUp_(event);
146     return;
147   }
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.
155   buttonPosition =
156       Math.max(0, Math.min(buttonPosition, clientSize - buttonSize));
157   var scrollPosition;
158   if (clientSize > buttonSize) {
159     scrollPosition = Math.max(totalSize - clientSize, 0) *
160         buttonPosition / (clientSize - buttonSize);
161   } else {
162     scrollPosition = 0;
163   }
164
165   this.scrollTop_ = scrollPosition;
166   this.view_.scrollTop = scrollPosition;
167   this.redraw_();
168 };
169
170 /**
171  * Handles changed in Dom by redrawing the scrollbar. Ignores consecutive calls.
172  * @private
173  */
174 ScrollBar.prototype.onDomChanged_ = function() {
175   if (this.domChangedTimer_) {
176     clearTimeout(this.domChangedTimer_);
177     this.domChangedTimer_ = null;
178   }
179   this.domChangedTimer_ = setTimeout(function() {
180     this.onRelayout_();
181     this.domChangedTimer_ = null;
182   }.bind(this), 50);
183 };
184
185 /**
186  * Redraws the scrollbar.
187  * @private
188  */
189 ScrollBar.prototype.redraw_ = function() {
190   if (!this.view_)
191     return;
192
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;
198
199   var buttonSize = Math.max(50, clientSize / totalSize * clientSize);
200   var buttonPosition;
201   if (clientSize - buttonSize > 0) {
202     buttonPosition = scrollPosition / (totalSize - clientSize) *
203         (clientSize - buttonSize);
204   } else {
205     buttonPosition = 0;
206   }
207   var buttonTop = buttonPosition + clientTop;
208
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';
217     }.bind(this));
218   }
219
220   this.lastButtonTop_ = buttonTop;
221   this.lastButtonSize_ = buttonSize;
222 };
223
224 /**
225  * Returns the viewport height of the view.
226  * @return {number} The viewport height of the view in px.
227  * @protected
228  */
229 ScrollBar.prototype.getClientHeight = function() {
230   return this.clientHeight_;
231 };
232
233 /**
234  * Returns the total height of the view.
235  * @return {number} The total height of the view in px.
236  * @protected
237  */
238 ScrollBar.prototype.getTotalHeight = function() {
239   return this.scrollHeight_;
240 };
241
242 /**
243  * Creates a new scroll bar for elements in the main panel.
244  * @extends {ScrollBar}
245  * @constructor
246  */
247 var MainPanelScrollBar = cr.ui.define('div');
248
249 /**
250  * Inherits after ScrollBar.
251  */
252 MainPanelScrollBar.prototype.__proto__ = ScrollBar.prototype;
253
254 /** @override */
255 MainPanelScrollBar.prototype.decorate = function() {
256   ScrollBar.prototype.decorate.call(this);
257
258   /**
259    * Margin for the transparent preview panel at the bottom.
260    * @type {number}
261    * @private
262    */
263   this.bottomMarginForPanel_ = 0;
264 };
265
266 /**
267  * GReturns the viewport height of the view, considering the preview panel.
268  *
269  * @return {number} The viewport height of the view in px.
270  * @override
271  * @protected
272  */
273 MainPanelScrollBar.prototype.getClientHeight = function() {
274   return this.clientHeight_ - this.bottomMarginForPanel_;
275 };
276
277 /**
278  * Returns the total height of the view, considering the preview panel.
279  *
280  * @return {number} The total height of the view in px.
281  * @override
282  * @protected
283  */
284 MainPanelScrollBar.prototype.getTotalHeight = function() {
285   return this.scrollHeight_ - this.bottomMarginForPanel_;
286 };
287
288 /**
289  * Sets the bottom margin height of the view for the transparent preview panel.
290  * @param {number} margin Margin to be set in px.
291  */
292 MainPanelScrollBar.prototype.setBottomMarginForPanel = function(margin) {
293   this.bottomMarginForPanel_ = margin;
294 };