blob: baef16aaa7a8b6ef745e7b9a8ef6ed4246728e3f [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
Akron9c4d1ae2016-05-25 21:43:22 +020020 /**
21 * Length attribute of the slider
22 * (as number of items).
23 *
24 * @param {number} Number of items (optional)
25 */
Akron6ed13992016-05-23 18:06:05 +020026 length : function (i) {
27 if (arguments.length === 0)
28 return this._length;
29 if (i == this._length)
Akron9c4d1ae2016-05-25 21:43:22 +020030 return this;
Akron6ed13992016-05-23 18:06:05 +020031 this._length = i;
Akron9c4d1ae2016-05-25 21:43:22 +020032 return this;
Akron6ed13992016-05-23 18:06:05 +020033 },
34
Akron9c4d1ae2016-05-25 21:43:22 +020035 /**
36 * Limit of items per screen.
37 *
38 * @param {number} Number of items per screen (optional)
39 */
Akron6ed13992016-05-23 18:06:05 +020040 limit : function (i) {
41 if (arguments.length === 0)
42 return this._limit;
43 if (i == this._limit)
Akron9c4d1ae2016-05-25 21:43:22 +020044 return this;
Akron6ed13992016-05-23 18:06:05 +020045 this._limit = i;
Akron9c4d1ae2016-05-25 21:43:22 +020046 return this;
Akron6ed13992016-05-23 18:06:05 +020047 },
48
Akron9c4d1ae2016-05-25 21:43:22 +020049 /**
50 * Is the slider active or not.
51 *
52 * @param {bool} true or false (optional)
53 */
Akrona92fd8d2016-05-24 21:13:41 +020054 active : function (bool) {
55 if (arguments.length === 1) {
56 if (bool) {
57 if (!this._active) {
58 this._element.classList.add('active');
59 this._active = true;
60 };
61 }
62 else if (this._active) {
63 this._element.classList.remove('active');
64 this._active = false;
65 }
66 };
67 return this._active;
68 },
69
Akron9c4d1ae2016-05-25 21:43:22 +020070 /**
71 * Move the slider to a relative position
72 *
73 * @param {number} relative position
74 */
Akron6ed13992016-05-23 18:06:05 +020075 movetoRel : function (relativePos) {
Akron24b1eaa2016-05-18 16:00:25 +020076 var diffHeight = (this._rulerHeight - this._sliderHeight);
Akron47c086c2016-05-18 21:22:06 +020077 var relativeOffset = (relativePos / diffHeight);
78
79 var off = this.offset(parseInt(relativeOffset * this._screens));
80 if (off !== undefined) {
81 this._menu.screen(off);
82 };
Akron6b24b202016-05-17 23:04:36 +020083 },
84
Akron9c4d1ae2016-05-25 21:43:22 +020085 /**
86 * Move the slider to an absolute position
87 *
88 * @param {number} absolute position
89 */
Akron6ed13992016-05-23 18:06:05 +020090 movetoAbs : function (absPos) {
91 var absOffset = (absPos / this._rulerHeight);
92
93 var off = this.offset(parseInt(absOffset * (this._screens + 1)));
94 if (off !== undefined) {
95 this._menu.screen(off);
96 };
Akron6b24b202016-05-17 23:04:36 +020097 },
98
Akron9c4d1ae2016-05-25 21:43:22 +020099 /**
100 * Screen offset of the slider
101 *
102 * @param {number} Offset position of the slider (optional)
103 */
Akron6ed13992016-05-23 18:06:05 +0200104 offset : function (off) {
105 if (arguments.length === 0)
106 return this._offset;
Akron6b24b202016-05-17 23:04:36 +0200107
Akron9c4d1ae2016-05-25 21:43:22 +0200108 // Normalize offset
109 if (off > this._screens)
Akron6ed13992016-05-23 18:06:05 +0200110 off = this._screens;
Akron9c4d1ae2016-05-25 21:43:22 +0200111 else if (off < 0)
Akron6ed13992016-05-23 18:06:05 +0200112 off = 0;
Akron24b1eaa2016-05-18 16:00:25 +0200113
Akron9c4d1ae2016-05-25 21:43:22 +0200114 // Identical with old value
Akron6ed13992016-05-23 18:06:05 +0200115 if (off === this._offset)
116 return undefined;
Akron6b24b202016-05-17 23:04:36 +0200117
Akron9c4d1ae2016-05-25 21:43:22 +0200118 // Set offset and move
Akron6ed13992016-05-23 18:06:05 +0200119 this._offset = off;
120 this._slider.style.top = (this._step * off) + '%';
121 return off;
122 },
Akron6b24b202016-05-17 23:04:36 +0200123
Akron9c4d1ae2016-05-25 21:43:22 +0200124 /**
125 * Get the associated dom element.
126 */
Akron6ed13992016-05-23 18:06:05 +0200127 element : function () {
128 return this._element;
Akron6b24b202016-05-17 23:04:36 +0200129 },
130
Akron9c4d1ae2016-05-25 21:43:22 +0200131 /**
132 * Reinitialize the size of the slider.
133 * Necessary to call after each adjustment of the list.
134 */
135 reInit : function () {
136
137 var s = this._element.style;
138
139 // Do not show the slider, in case there is nothing to scroll
140 if (this._length <= this._limit) {
141 s.display = 'none';
142 return;
143 }
144 else {
145 s.display = 'block';
146 };
147
148 this._height = ((this._limit / this._length) * 100);
149 this._screens = this._length - this._limit;
150 this._step = (100 - this._height) / this._screens;
151 this._slider.style.height = this._height + '%';
152 },
153
Akron9905e2a2016-05-10 16:06:44 +0200154 // Initialize prefix object
Akron47c086c2016-05-18 21:22:06 +0200155 _init : function (menu) {
156
157 this._menu = menu;
Akron9905e2a2016-05-10 16:06:44 +0200158
Akronf86eaea2016-05-13 18:02:27 +0200159 this._offset = 0;
Akron6b24b202016-05-17 23:04:36 +0200160 this._event = {};
Akrona92fd8d2016-05-24 21:13:41 +0200161 this._active = false;
Akronf86eaea2016-05-13 18:02:27 +0200162
Akron9c4d1ae2016-05-25 21:43:22 +0200163 var el = this._element = document.createElement('div');
164 el.setAttribute('class', 'ruler');
Akron9905e2a2016-05-10 16:06:44 +0200165
Akron9c4d1ae2016-05-25 21:43:22 +0200166 this._slider = el.appendChild(
Akron9905e2a2016-05-10 16:06:44 +0200167 document.createElement('span')
168 );
169
Akron9c4d1ae2016-05-25 21:43:22 +0200170 this._ruler = el.appendChild(document.createElement('div'));
Akron47c086c2016-05-18 21:22:06 +0200171
Akron6ed13992016-05-23 18:06:05 +0200172 // Do not mark the menu on mousedown
173 this._ruler.addEventListener('mousedown', function (e) {
174 e.halt()
175 }, false);
176
177 // Move the slider to the click position
178 this._ruler.addEventListener('click', this._mouseclick.bind(this), false);
179
Akron6b24b202016-05-17 23:04:36 +0200180 this._slider.addEventListener('mousedown', this._mousedown.bind(this), false);
181
Akron9905e2a2016-05-10 16:06:44 +0200182 return this;
183 },
184
Akron9c4d1ae2016-05-25 21:43:22 +0200185 // Reinit height based on dom position
Akron6ed13992016-05-23 18:06:05 +0200186 _initClientHeight : function () {
Akron9c4d1ae2016-05-25 21:43:22 +0200187 this._rulerHeight = this._element.clientHeight;
188 this._sliderHeight = this._slider.clientHeight;
Akron9905e2a2016-05-10 16:06:44 +0200189 },
190
Akron9c4d1ae2016-05-25 21:43:22 +0200191 // Release mousemove event
Akron6ed13992016-05-23 18:06:05 +0200192 _mousemove : function (e) {
193 this.movetoRel(e.clientY - this._event.init);
194 e.halt();
Akronf86eaea2016-05-13 18:02:27 +0200195 },
196
Akron9c4d1ae2016-05-25 21:43:22 +0200197 // Release mouseup event
Akron6ed13992016-05-23 18:06:05 +0200198 _mouseup : function (e) {
Akrona92fd8d2016-05-24 21:13:41 +0200199 this.active(false);
Akron6ed13992016-05-23 18:06:05 +0200200 window.removeEventListener('mousemove', this._event.mov);
201 window.removeEventListener('mouseup', this._event.up);
202 this._menu.focus();
Akron9905e2a2016-05-10 16:06:44 +0200203 },
204
Akron9c4d1ae2016-05-25 21:43:22 +0200205 // Release mousedown event
Akron6ed13992016-05-23 18:06:05 +0200206 _mousedown : function (e) {
207 // Bind drag handler
208 var ev = this._event;
209 ev.init = e.clientY - (this._step * this._offset);
Akron9c4d1ae2016-05-25 21:43:22 +0200210 ev.mov = this._mousemove.bind(this);
211 ev.up = this._mouseup.bind(this);
Akron6ed13992016-05-23 18:06:05 +0200212
213 // TODO: This may not be necessary all the time
214 this._initClientHeight();
215
Akrona92fd8d2016-05-24 21:13:41 +0200216 this.active(true);
Akron6ed13992016-05-23 18:06:05 +0200217
218 window.addEventListener('mousemove', ev.mov);
219 window.addEventListener('mouseup', ev.up);
220
221 e.halt();
222 },
223
Akron9c4d1ae2016-05-25 21:43:22 +0200224 // Release event to reposition slider on ruler
Akron6ed13992016-05-23 18:06:05 +0200225 _mouseclick : function (e) {
226 this._initClientHeight();
227
228 this.movetoAbs(
229 e.clientY - this._ruler.getClientRects()[0].top
230 );
231 e.halt();
Akron9905e2a2016-05-10 16:06:44 +0200232 }
233});