blob: dcc6fc5a091b5a350b725265bd5a29cb92a2cc0f [file] [log] [blame]
Akronc53cfc82020-10-19 11:00:58 +02001"use strict";
2
Akron9c4d1ae2016-05-25 21:43:22 +02003/**
4 * Create slider for menus.
5 * The slider will only be used by mouse - touch support
6 * shouldn't be necessary, as the menu can be scrolled using touch.
7 *
8 * @author Nils Diewald
9 */
Akron9905e2a2016-05-10 16:06:44 +020010define({
11
12 /**
Akron9c4d1ae2016-05-25 21:43:22 +020013 * Create new slider for Menu.
14 * @this {Slider}
15 * @constructor
16 * @param {Menu} menu object
Akron9905e2a2016-05-10 16:06:44 +020017 */
Akron47c086c2016-05-18 21:22:06 +020018 create : function (menu) {
19 return Object.create(this)._init(menu);
Akron9905e2a2016-05-10 16:06:44 +020020 },
21
Akron0b489ad2018-02-02 16:49:32 +010022
Akron9c4d1ae2016-05-25 21:43:22 +020023 /**
24 * Length attribute of the slider
25 * (as number of items).
26 *
27 * @param {number} Number of items (optional)
28 */
Akron6ed13992016-05-23 18:06:05 +020029 length : function (i) {
30 if (arguments.length === 0)
31 return this._length;
32 if (i == this._length)
Akron9c4d1ae2016-05-25 21:43:22 +020033 return this;
Akron6ed13992016-05-23 18:06:05 +020034 this._length = i;
Akron9c4d1ae2016-05-25 21:43:22 +020035 return this;
Akron6ed13992016-05-23 18:06:05 +020036 },
37
Akron0b489ad2018-02-02 16:49:32 +010038
Akron9c4d1ae2016-05-25 21:43:22 +020039 /**
40 * Limit of items per screen.
41 *
42 * @param {number} Number of items per screen (optional)
43 */
Akron6ed13992016-05-23 18:06:05 +020044 limit : function (i) {
45 if (arguments.length === 0)
46 return this._limit;
47 if (i == this._limit)
Akron9c4d1ae2016-05-25 21:43:22 +020048 return this;
Akron6ed13992016-05-23 18:06:05 +020049 this._limit = i;
Akron9c4d1ae2016-05-25 21:43:22 +020050 return this;
Akron6ed13992016-05-23 18:06:05 +020051 },
52
Akron0b489ad2018-02-02 16:49:32 +010053
Akron9c4d1ae2016-05-25 21:43:22 +020054 /**
55 * Is the slider active or not.
56 *
57 * @param {bool} true or false (optional)
58 */
Akrona92fd8d2016-05-24 21:13:41 +020059 active : function (bool) {
60 if (arguments.length === 1) {
61 if (bool) {
Akron0b489ad2018-02-02 16:49:32 +010062 if (!this._active) {
Akron24aa0052020-11-10 11:00:34 +010063 this._el.classList.add('active');
Akron0b489ad2018-02-02 16:49:32 +010064 this._active = true;
65 };
Akrona92fd8d2016-05-24 21:13:41 +020066 }
67 else if (this._active) {
Akron24aa0052020-11-10 11:00:34 +010068 this._el.classList.remove('active');
Akron0b489ad2018-02-02 16:49:32 +010069 this._active = false;
Akrona92fd8d2016-05-24 21:13:41 +020070 }
71 };
72 return this._active;
73 },
74
Akron0b489ad2018-02-02 16:49:32 +010075
Akron9c4d1ae2016-05-25 21:43:22 +020076 /**
77 * Move the slider to a relative position
78 *
79 * @param {number} relative position
80 */
Akron6ed13992016-05-23 18:06:05 +020081 movetoRel : function (relativePos) {
Akron71b91e42016-06-01 22:12:43 +020082
83 // This is important to find the correct percentage!
Akronc53cfc82020-10-19 11:00:58 +020084 const diffHeight = (this._rulerHeight - this._sliderHeight);
85 const relativeOffset = (relativePos / diffHeight);
Akron47c086c2016-05-18 21:22:06 +020086
Akron71b91e42016-06-01 22:12:43 +020087 // Offset is a value 0 to this._screens
Akronc53cfc82020-10-19 11:00:58 +020088 const off = this.offset(
Akron71b91e42016-06-01 22:12:43 +020089 parseInt(relativeOffset * this._screens) + this._event.initOffset
90 );
91
Akron47c086c2016-05-18 21:22:06 +020092 if (off !== undefined) {
93 this._menu.screen(off);
94 };
Akron6b24b202016-05-17 23:04:36 +020095 },
96
Akron0b489ad2018-02-02 16:49:32 +010097
Akron9c4d1ae2016-05-25 21:43:22 +020098 /**
99 * Move the slider to an absolute position
100 *
101 * @param {number} absolute position
102 */
Akron6ed13992016-05-23 18:06:05 +0200103 movetoAbs : function (absPos) {
Akronc53cfc82020-10-19 11:00:58 +0200104 const absOffset = (absPos / this._rulerHeight);
105 const off = this.offset(parseInt(absOffset * (this._screens + 1)));
Akron6ed13992016-05-23 18:06:05 +0200106
Akron6ed13992016-05-23 18:06:05 +0200107 if (off !== undefined) {
108 this._menu.screen(off);
109 };
Akron6b24b202016-05-17 23:04:36 +0200110 },
111
Akron0b489ad2018-02-02 16:49:32 +0100112
Akron9c4d1ae2016-05-25 21:43:22 +0200113 /**
114 * Screen offset of the slider
115 *
116 * @param {number} Offset position of the slider (optional)
117 */
Akron6ed13992016-05-23 18:06:05 +0200118 offset : function (off) {
119 if (arguments.length === 0)
120 return this._offset;
Akron6b24b202016-05-17 23:04:36 +0200121
Akron9c4d1ae2016-05-25 21:43:22 +0200122 // Normalize offset
123 if (off > this._screens)
Akron6ed13992016-05-23 18:06:05 +0200124 off = this._screens;
Akron9c4d1ae2016-05-25 21:43:22 +0200125 else if (off < 0)
Akron6ed13992016-05-23 18:06:05 +0200126 off = 0;
Akron24b1eaa2016-05-18 16:00:25 +0200127
Akron9c4d1ae2016-05-25 21:43:22 +0200128 // Identical with old value
Akron6ed13992016-05-23 18:06:05 +0200129 if (off === this._offset)
130 return undefined;
Akron6b24b202016-05-17 23:04:36 +0200131
Akron9c4d1ae2016-05-25 21:43:22 +0200132 // Set offset and move
Akron6ed13992016-05-23 18:06:05 +0200133 this._offset = off;
134 this._slider.style.top = (this._step * off) + '%';
135 return off;
136 },
Akron6b24b202016-05-17 23:04:36 +0200137
Akron0b489ad2018-02-02 16:49:32 +0100138
Akron9c4d1ae2016-05-25 21:43:22 +0200139 /**
140 * Get the associated dom element.
141 */
Akron6ed13992016-05-23 18:06:05 +0200142 element : function () {
Akron24aa0052020-11-10 11:00:34 +0100143 return this._el;
Akron6b24b202016-05-17 23:04:36 +0200144 },
145
Akron0b489ad2018-02-02 16:49:32 +0100146
Akron9c4d1ae2016-05-25 21:43:22 +0200147 /**
148 * Reinitialize the size of the slider.
149 * Necessary to call after each adjustment of the list.
150 */
151 reInit : function () {
152
Akronc53cfc82020-10-19 11:00:58 +0200153 const t = this;
Akron24aa0052020-11-10 11:00:34 +0100154 const s = t._el.style;
Akron9c4d1ae2016-05-25 21:43:22 +0200155
156 // Do not show the slider, in case there is nothing to scroll
Akronc53cfc82020-10-19 11:00:58 +0200157 if (t._length <= t._limit) {
Akron9c4d1ae2016-05-25 21:43:22 +0200158 s.display = 'none';
159 return;
160 }
161 else {
162 s.display = 'block';
163 };
164
Akronc53cfc82020-10-19 11:00:58 +0200165 t._height = ((t._limit / t._length) * 100);
166 t._screens = t._length - t._limit;
167 t._step = (100 - t._height) / t._screens;
168 t._slider.style.height = t._height + '%';
Akron9c4d1ae2016-05-25 21:43:22 +0200169 },
170
Akron0b489ad2018-02-02 16:49:32 +0100171
Akron9905e2a2016-05-10 16:06:44 +0200172 // Initialize prefix object
Akron47c086c2016-05-18 21:22:06 +0200173 _init : function (menu) {
Akronc53cfc82020-10-19 11:00:58 +0200174 const t = this;
Akron47c086c2016-05-18 21:22:06 +0200175
Akronc53cfc82020-10-19 11:00:58 +0200176 t._menu = menu;
Akron9905e2a2016-05-10 16:06:44 +0200177
Akronc53cfc82020-10-19 11:00:58 +0200178 t._offset = 0;
179 t._event = {};
180 t._active = false;
Akronf86eaea2016-05-13 18:02:27 +0200181
Akron24aa0052020-11-10 11:00:34 +0100182 const el = t._el = document.createElement('div');
Akron9c4d1ae2016-05-25 21:43:22 +0200183 el.setAttribute('class', 'ruler');
Akron9905e2a2016-05-10 16:06:44 +0200184
Akronc53cfc82020-10-19 11:00:58 +0200185 t._slider = el.appendChild(
Akron9905e2a2016-05-10 16:06:44 +0200186 document.createElement('span')
187 );
188
Akronc53cfc82020-10-19 11:00:58 +0200189 t._ruler = el.appendChild(document.createElement('div'));
Akron47c086c2016-05-18 21:22:06 +0200190
Akron6ed13992016-05-23 18:06:05 +0200191 // Do not mark the menu on mousedown
Akronc53cfc82020-10-19 11:00:58 +0200192 t._ruler.addEventListener('mousedown', function (e) {
Akron6ed13992016-05-23 18:06:05 +0200193 e.halt()
194 }, false);
195
196 // Move the slider to the click position
Akronc53cfc82020-10-19 11:00:58 +0200197 t._ruler.addEventListener('click', t._mouseclick.bind(t), false);
Akron6ed13992016-05-23 18:06:05 +0200198
Akronc53cfc82020-10-19 11:00:58 +0200199 t._slider.addEventListener('mousedown', t._mousedown.bind(t), false);
Akron6b24b202016-05-17 23:04:36 +0200200
Akronc53cfc82020-10-19 11:00:58 +0200201 return t;
Akron9905e2a2016-05-10 16:06:44 +0200202 },
203
Akron0b489ad2018-02-02 16:49:32 +0100204
Akron9c4d1ae2016-05-25 21:43:22 +0200205 // Reinit height based on dom position
Akron6ed13992016-05-23 18:06:05 +0200206 _initClientHeight : function () {
Akron24aa0052020-11-10 11:00:34 +0100207 this._rulerHeight = this._el.clientHeight;
Akron9c4d1ae2016-05-25 21:43:22 +0200208 this._sliderHeight = this._slider.clientHeight;
Akron9905e2a2016-05-10 16:06:44 +0200209 },
210
Akron0b489ad2018-02-02 16:49:32 +0100211
Akron9c4d1ae2016-05-25 21:43:22 +0200212 // Release mousemove event
Akron6ed13992016-05-23 18:06:05 +0200213 _mousemove : function (e) {
214 this.movetoRel(e.clientY - this._event.init);
215 e.halt();
Akronf86eaea2016-05-13 18:02:27 +0200216 },
217
Akron0b489ad2018-02-02 16:49:32 +0100218
Akron9c4d1ae2016-05-25 21:43:22 +0200219 // Release mouseup event
Akron6ed13992016-05-23 18:06:05 +0200220 _mouseup : function (e) {
Akrona92fd8d2016-05-24 21:13:41 +0200221 this.active(false);
Akron6ed13992016-05-23 18:06:05 +0200222 window.removeEventListener('mousemove', this._event.mov);
223 window.removeEventListener('mouseup', this._event.up);
224 this._menu.focus();
Akron9905e2a2016-05-10 16:06:44 +0200225 },
226
Akron0b489ad2018-02-02 16:49:32 +0100227
Akron9c4d1ae2016-05-25 21:43:22 +0200228 // Release mousedown event
Akron6ed13992016-05-23 18:06:05 +0200229 _mousedown : function (e) {
230 // Bind drag handler
Akronc53cfc82020-10-19 11:00:58 +0200231 const ev = this._event;
Akron71b91e42016-06-01 22:12:43 +0200232
233 // _step * _offset is the distance of the ruler to the top
234 ev.init = e.clientY;
235 ev.initOffset = this._offset;
236 // By substracting that, it is like initializing to the first point
237
Akron9c4d1ae2016-05-25 21:43:22 +0200238 ev.mov = this._mousemove.bind(this);
239 ev.up = this._mouseup.bind(this);
Akron6ed13992016-05-23 18:06:05 +0200240
241 // TODO: This may not be necessary all the time
242 this._initClientHeight();
243
Akrona92fd8d2016-05-24 21:13:41 +0200244 this.active(true);
Akron6ed13992016-05-23 18:06:05 +0200245
246 window.addEventListener('mousemove', ev.mov);
247 window.addEventListener('mouseup', ev.up);
248
249 e.halt();
250 },
251
Akron0b489ad2018-02-02 16:49:32 +0100252
Akron9c4d1ae2016-05-25 21:43:22 +0200253 // Release event to reposition slider on ruler
Akron6ed13992016-05-23 18:06:05 +0200254 _mouseclick : function (e) {
255 this._initClientHeight();
256
257 this.movetoAbs(
258 e.clientY - this._ruler.getClientRects()[0].top
259 );
260 e.halt();
Akron9905e2a2016-05-10 16:06:44 +0200261 }
262});