OLD | NEW |
| (Empty) |
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 }; | |
OLD | NEW |