blob: adaf81a7d51e2132a9ae9a29afdb7436b33caea9 [file] [log] [blame]
Akron9c4d1ae2016-05-25 21:43:22 +02001/**
2 * Create slider for menus.
3 * The slider will only be used by mouse - touch support
4 * shouldn't be necessary, as the menu can be scrolled using touch.
5 *
6 * @author Nils Diewald
7 */
Akron9905e2a2016-05-10 16:06:44 +02008define({
9
10 /**
Akron9c4d1ae2016-05-25 21:43:22 +020011 * Create new slider for Menu.
12 * @this {Slider}
13 * @constructor
14 * @param {Menu} menu object
Akron9905e2a2016-05-10 16:06:44 +020015 */
Akron47c086c2016-05-18 21:22:06 +020016 create : function (menu) {
17 return Object.create(this)._init(menu);
Akron9905e2a2016-05-10 16:06:44 +020018 },
19
Akron0b489ad2018-02-02 16:49:32 +010020
Akron9c4d1ae2016-05-25 21:43:22 +020021 /**
22 * Length attribute of the slider
23 * (as number of items).
24 *
25 * @param {number} Number of items (optional)
26 */
Akron6ed13992016-05-23 18:06:05 +020027 length : function (i) {
28 if (arguments.length === 0)
29 return this._length;
30 if (i == this._length)
Akron9c4d1ae2016-05-25 21:43:22 +020031 return this;
Akron6ed13992016-05-23 18:06:05 +020032 this._length = i;
Akron9c4d1ae2016-05-25 21:43:22 +020033 return this;
Akron6ed13992016-05-23 18:06:05 +020034 },
35
Akron0b489ad2018-02-02 16:49:32 +010036
Akron9c4d1ae2016-05-25 21:43:22 +020037 /**
38 * Limit of items per screen.
39 *
40 * @param {number} Number of items per screen (optional)
41 */
Akron6ed13992016-05-23 18:06:05 +020042 limit : function (i) {
43 if (arguments.length === 0)
44 return this._limit;
45 if (i == this._limit)
Akron9c4d1ae2016-05-25 21:43:22 +020046 return this;
Akron6ed13992016-05-23 18:06:05 +020047 this._limit = i;
Akron9c4d1ae2016-05-25 21:43:22 +020048 return this;
Akron6ed13992016-05-23 18:06:05 +020049 },
50
Akron0b489ad2018-02-02 16:49:32 +010051
Akron9c4d1ae2016-05-25 21:43:22 +020052 /**
53 * Is the slider active or not.
54 *
55 * @param {bool} true or false (optional)
56 */
Akrona92fd8d2016-05-24 21:13:41 +020057 active : function (bool) {
58 if (arguments.length === 1) {
59 if (bool) {
Akron0b489ad2018-02-02 16:49:32 +010060 if (!this._active) {
61 this._element.classList.add('active');
62 this._active = true;
63 };
Akrona92fd8d2016-05-24 21:13:41 +020064 }
65 else if (this._active) {
Akron0b489ad2018-02-02 16:49:32 +010066 this._element.classList.remove('active');
67 this._active = false;
Akrona92fd8d2016-05-24 21:13:41 +020068 }
69 };
70 return this._active;
71 },
72
Akron0b489ad2018-02-02 16:49:32 +010073
Akron9c4d1ae2016-05-25 21:43:22 +020074 /**
75 * Move the slider to a relative position
76 *
77 * @param {number} relative position
78 */
Akron6ed13992016-05-23 18:06:05 +020079 movetoRel : function (relativePos) {
Akron71b91e42016-06-01 22:12:43 +020080
81 // This is important to find the correct percentage!
Akron24b1eaa2016-05-18 16:00:25 +020082 var diffHeight = (this._rulerHeight - this._sliderHeight);
Akron47c086c2016-05-18 21:22:06 +020083 var relativeOffset = (relativePos / diffHeight);
84
Akron71b91e42016-06-01 22:12:43 +020085 // Offset is a value 0 to this._screens
86 var off = this.offset(
87 parseInt(relativeOffset * this._screens) + this._event.initOffset
88 );
89
Akron47c086c2016-05-18 21:22:06 +020090 if (off !== undefined) {
91 this._menu.screen(off);
92 };
Akron6b24b202016-05-17 23:04:36 +020093 },
94
Akron0b489ad2018-02-02 16:49:32 +010095
Akron9c4d1ae2016-05-25 21:43:22 +020096 /**
97 * Move the slider to an absolute position
98 *
99 * @param {number} absolute position
100 */
Akron6ed13992016-05-23 18:06:05 +0200101 movetoAbs : function (absPos) {
102 var absOffset = (absPos / this._rulerHeight);
103
104 var off = this.offset(parseInt(absOffset * (this._screens + 1)));
105 if (off !== undefined) {
106 this._menu.screen(off);
107 };
Akron6b24b202016-05-17 23:04:36 +0200108 },
109
Akron0b489ad2018-02-02 16:49:32 +0100110
Akron9c4d1ae2016-05-25 21:43:22 +0200111 /**
112 * Screen offset of the slider
113 *
114 * @param {number} Offset position of the slider (optional)
115 */
Akron6ed13992016-05-23 18:06:05 +0200116 offset : function (off) {
117 if (arguments.length === 0)
118 return this._offset;
Akron6b24b202016-05-17 23:04:36 +0200119
Akron9c4d1ae2016-05-25 21:43:22 +0200120 // Normalize offset
121 if (off > this._screens)
Akron6ed13992016-05-23 18:06:05 +0200122 off = this._screens;
Akron9c4d1ae2016-05-25 21:43:22 +0200123 else if (off < 0)
Akron6ed13992016-05-23 18:06:05 +0200124 off = 0;
Akron24b1eaa2016-05-18 16:00:25 +0200125
Akron9c4d1ae2016-05-25 21:43:22 +0200126 // Identical with old value
Akron6ed13992016-05-23 18:06:05 +0200127 if (off === this._offset)
128 return undefined;
Akron6b24b202016-05-17 23:04:36 +0200129
Akron9c4d1ae2016-05-25 21:43:22 +0200130 // Set offset and move
Akron6ed13992016-05-23 18:06:05 +0200131 this._offset = off;
132 this._slider.style.top = (this._step * off) + '%';
133 return off;
134 },
Akron6b24b202016-05-17 23:04:36 +0200135
Akron0b489ad2018-02-02 16:49:32 +0100136
Akron9c4d1ae2016-05-25 21:43:22 +0200137 /**
138 * Get the associated dom element.
139 */
Akron6ed13992016-05-23 18:06:05 +0200140 element : function () {
141 return this._element;
Akron6b24b202016-05-17 23:04:36 +0200142 },
143
Akron0b489ad2018-02-02 16:49:32 +0100144
Akron9c4d1ae2016-05-25 21:43:22 +0200145 /**
146 * Reinitialize the size of the slider.
147 * Necessary to call after each adjustment of the list.
148 */
149 reInit : function () {
150
151 var s = this._element.style;
152
153 // Do not show the slider, in case there is nothing to scroll
154 if (this._length <= this._limit) {
155 s.display = 'none';
156 return;
157 }
158 else {
159 s.display = 'block';
160 };
161
162 this._height = ((this._limit / this._length) * 100);
163 this._screens = this._length - this._limit;
164 this._step = (100 - this._height) / this._screens;
165 this._slider.style.height = this._height + '%';
166 },
167
Akron0b489ad2018-02-02 16:49:32 +0100168
Akron9905e2a2016-05-10 16:06:44 +0200169 // Initialize prefix object
Akron47c086c2016-05-18 21:22:06 +0200170 _init : function (menu) {
171
172 this._menu = menu;
Akron9905e2a2016-05-10 16:06:44 +0200173
Akronf86eaea2016-05-13 18:02:27 +0200174 this._offset = 0;
Akron6b24b202016-05-17 23:04:36 +0200175 this._event = {};
Akrona92fd8d2016-05-24 21:13:41 +0200176 this._active = false;
Akronf86eaea2016-05-13 18:02:27 +0200177
Akron9c4d1ae2016-05-25 21:43:22 +0200178 var el = this._element = document.createElement('div');
179 el.setAttribute('class', 'ruler');
Akron9905e2a2016-05-10 16:06:44 +0200180
Akron9c4d1ae2016-05-25 21:43:22 +0200181 this._slider = el.appendChild(
Akron9905e2a2016-05-10 16:06:44 +0200182 document.createElement('span')
183 );
184
Akron9c4d1ae2016-05-25 21:43:22 +0200185 this._ruler = el.appendChild(document.createElement('div'));
Akron47c086c2016-05-18 21:22:06 +0200186
Akron6ed13992016-05-23 18:06:05 +0200187 // Do not mark the menu on mousedown
188 this._ruler.addEventListener('mousedown', function (e) {
189 e.halt()
190 }, false);
191
192 // Move the slider to the click position
193 this._ruler.addEventListener('click', this._mouseclick.bind(this), false);
194
Akron6b24b202016-05-17 23:04:36 +0200195 this._slider.addEventListener('mousedown', this._mousedown.bind(this), false);
196
Akron9905e2a2016-05-10 16:06:44 +0200197 return this;
198 },
199
Akron0b489ad2018-02-02 16:49:32 +0100200
Akron9c4d1ae2016-05-25 21:43:22 +0200201 // Reinit height based on dom position
Akron6ed13992016-05-23 18:06:05 +0200202 _initClientHeight : function () {
Akron9c4d1ae2016-05-25 21:43:22 +0200203 this._rulerHeight = this._element.clientHeight;
204 this._sliderHeight = this._slider.clientHeight;
Akron9905e2a2016-05-10 16:06:44 +0200205 },
206
Akron0b489ad2018-02-02 16:49:32 +0100207
Akron9c4d1ae2016-05-25 21:43:22 +0200208 // Release mousemove event
Akron6ed13992016-05-23 18:06:05 +0200209 _mousemove : function (e) {
210 this.movetoRel(e.clientY - this._event.init);
211 e.halt();
Akronf86eaea2016-05-13 18:02:27 +0200212 },
213
Akron0b489ad2018-02-02 16:49:32 +0100214
Akron9c4d1ae2016-05-25 21:43:22 +0200215 // Release mouseup event
Akron6ed13992016-05-23 18:06:05 +0200216 _mouseup : function (e) {
Akrona92fd8d2016-05-24 21:13:41 +0200217 this.active(false);
Akron6ed13992016-05-23 18:06:05 +0200218 window.removeEventListener('mousemove', this._event.mov);
219 window.removeEventListener('mouseup', this._event.up);
220 this._menu.focus();
Akron9905e2a2016-05-10 16:06:44 +0200221 },
222
Akron0b489ad2018-02-02 16:49:32 +0100223
Akron9c4d1ae2016-05-25 21:43:22 +0200224 // Release mousedown event
Akron6ed13992016-05-23 18:06:05 +0200225 _mousedown : function (e) {
226 // Bind drag handler
227 var ev = this._event;
Akron71b91e42016-06-01 22:12:43 +0200228
229 // _step * _offset is the distance of the ruler to the top
230 ev.init = e.clientY;
231 ev.initOffset = this._offset;
232 // By substracting that, it is like initializing to the first point
233
Akron9c4d1ae2016-05-25 21:43:22 +0200234 ev.mov = this._mousemove.bind(this);
235 ev.up = this._mouseup.bind(this);
Akron6ed13992016-05-23 18:06:05 +0200236
237 // TODO: This may not be necessary all the time
238 this._initClientHeight();
239
Akrona92fd8d2016-05-24 21:13:41 +0200240 this.active(true);
Akron6ed13992016-05-23 18:06:05 +0200241
242 window.addEventListener('mousemove', ev.mov);
243 window.addEventListener('mouseup', ev.up);
244
245 e.halt();
246 },
247
Akron0b489ad2018-02-02 16:49:32 +0100248
Akron9c4d1ae2016-05-25 21:43:22 +0200249 // Release event to reposition slider on ruler
Akron6ed13992016-05-23 18:06:05 +0200250 _mouseclick : function (e) {
251 this._initClientHeight();
252
253 this.movetoAbs(
254 e.clientY - this._ruler.getClientRects()[0].top
255 );
256 e.halt();
Akron9905e2a2016-05-10 16:06:44 +0200257 }
258});