blob: ce59f3cb1c0f4727973700680374bb61a5ba7c0d [file] [log] [blame]
Nils Diewald2fe12e12015-03-06 16:47:06 +00001/**
Nils Diewald7148c6f2015-05-04 15:07:53 +00002 * Scrollable drop-down menus with view filter.
Nils Diewald2fe12e12015-03-06 16:47:06 +00003 *
4 * @author Nils Diewald
5 */
Nils Diewald2488d052015-04-09 21:46:02 +00006/*
Nils Diewald20f7ace2015-05-07 12:51:34 +00007 * TODO: First item shouldn't be automatically highlighted!
Nils Diewald0e6992a2015-04-14 20:13:52 +00008 * TODO: space is not a valid prefix!
Nils Diewald7148c6f2015-05-04 15:07:53 +00009 * TODO: Prefix should be case sensitive!
Nils Diewald2488d052015-04-09 21:46:02 +000010 */
Nils Diewald0e6992a2015-04-14 20:13:52 +000011define([
12 'menu/item',
13 'menu/prefix',
Akronc7448732016-04-27 14:06:58 +020014 'menu/lengthField',
Akron9905e2a2016-05-10 16:06:44 +020015 'menu/slider',
Nils Diewald0e6992a2015-04-14 20:13:52 +000016 'util'
17], function (defaultItemClass,
Akronc7448732016-04-27 14:06:58 +020018 defaultPrefixClass,
Akron9905e2a2016-05-10 16:06:44 +020019 defaultLengthFieldClass,
20 sliderClass) {
Nils Diewaldfda29d92015-01-22 17:28:01 +000021
Nils Diewald0e6992a2015-04-14 20:13:52 +000022 // Default maximum number of menu items
23 var menuLimit = 8;
24
25 function _codeFromEvent (e) {
26 if (e.charCode && (e.keyCode == 0))
27 return e.charCode
28 return e.keyCode;
Nils Diewald59c02fc2015-03-07 01:29:09 +000029 };
30
Nils Diewald86dad5b2015-01-28 15:09:07 +000031
32 /**
33 * List of items for drop down menu (complete).
34 * Only a sublist of the menu is filtered (live).
35 * Only a sublist of the filtered menu is visible (shown).
36 */
Nils Diewald0e6992a2015-04-14 20:13:52 +000037 return {
Nils Diewald86dad5b2015-01-28 15:09:07 +000038 /**
39 * Create new Menu based on the action prefix
40 * and a list of menu items.
41 *
42 * @this {Menu}
43 * @constructor
44 * @param {string} Context prefix
45 * @param {Array.<Array.<string>>} List of menu items
46 */
47 create : function (params) {
Nils Diewald0e6992a2015-04-14 20:13:52 +000048 return Object.create(this)._init(params);
Nils Diewald86dad5b2015-01-28 15:09:07 +000049 },
50
Nils Diewald6e43ffd2015-03-25 18:55:39 +000051 /**
52 * Destroy this menu
53 * (in case you don't trust the
54 * mark and sweep GC)!
55 */
56 destroy : function () {
Akron47c086c2016-05-18 21:22:06 +020057 this._prefix._menu = undefined;
58 this._lengthField._menu = undefined;
59 this._slider._menu = undefined;
60
Nils Diewald6e43ffd2015-03-25 18:55:39 +000061 if (this._element != undefined)
62 delete this._element["menu"];
63
64 for (var i = 0; i < this._items.length; i++) {
65 delete this._items[i]["_menu"];
66 };
Nils Diewald5c5a7472015-04-02 22:13:38 +000067 delete this._prefix['_menu'];
Nils Diewald6e43ffd2015-03-25 18:55:39 +000068 },
69
Nils Diewald7148c6f2015-05-04 15:07:53 +000070
71 /**
72 * Focus on this menu.
73 */
Nils Diewald2fe12e12015-03-06 16:47:06 +000074 focus : function () {
75 this._element.focus();
76 },
77
Nils Diewald7148c6f2015-05-04 15:07:53 +000078
Nils Diewald59c02fc2015-03-07 01:29:09 +000079 // mouse wheel treatment
80 _mousewheel : function (e) {
81 var delta = 0;
Nils Diewald5975d702015-03-09 17:45:42 +000082
83 delta = e.deltaY / 120;
84 if (delta > 0)
Nils Diewald59c02fc2015-03-07 01:29:09 +000085 this.next();
Nils Diewald5975d702015-03-09 17:45:42 +000086 else if (delta < 0)
Nils Diewald59c02fc2015-03-07 01:29:09 +000087 this.prev();
Nils Diewald59c02fc2015-03-07 01:29:09 +000088 e.halt();
89 },
90
Nils Diewald7148c6f2015-05-04 15:07:53 +000091
Nils Diewald59c02fc2015-03-07 01:29:09 +000092 // Arrow key and prefix treatment
Nils Diewald47f366b2015-04-15 20:06:35 +000093 _keydown : function (e) {
Nils Diewald59c02fc2015-03-07 01:29:09 +000094 var code = _codeFromEvent(e);
95
Nils Diewald59c02fc2015-03-07 01:29:09 +000096 switch (code) {
97 case 27: // 'Esc'
98 e.halt();
99 this.hide();
100 break;
Nils Diewald5975d702015-03-09 17:45:42 +0000101
Nils Diewald59c02fc2015-03-07 01:29:09 +0000102 case 38: // 'Up'
103 e.halt();
104 this.prev();
105 break;
Nils Diewald5975d702015-03-09 17:45:42 +0000106 case 33: // 'Page up'
Nils Diewald59c02fc2015-03-07 01:29:09 +0000107 e.halt();
Nils Diewald5975d702015-03-09 17:45:42 +0000108 this.prev();
109 break;
110 case 40: // 'Down'
111 e.halt();
112 this.next();
113 break;
114 case 34: // 'Page down'
115 e.halt();
116 this.next();
117 break;
118 case 39: // 'Right'
Nils Diewalde8518f82015-03-18 22:41:49 +0000119 if (this._prefix.active())
120 break;
121
Akronf86eaea2016-05-13 18:02:27 +0200122 var item = this.liveItem(this.position);
Akron5ef4fa02015-06-02 16:25:14 +0200123
Nils Diewald5975d702015-03-09 17:45:42 +0000124 if (item["further"] !== undefined) {
125 item["further"].bind(item).apply();
Nils Diewald5975d702015-03-09 17:45:42 +0000126 };
Akron5ef4fa02015-06-02 16:25:14 +0200127
128 e.halt();
Nils Diewald5975d702015-03-09 17:45:42 +0000129 break;
130 case 13: // 'Enter'
131
132 // Click on prefix
133 if (this._prefix.active())
Nils Diewald6e43ffd2015-03-25 18:55:39 +0000134 this._prefix.onclick(e);
Nils Diewald5975d702015-03-09 17:45:42 +0000135
136 // Click on item
137 else
Akronf86eaea2016-05-13 18:02:27 +0200138 this.liveItem(this.position).onclick(e);
Nils Diewald5975d702015-03-09 17:45:42 +0000139 e.halt();
Nils Diewald59c02fc2015-03-07 01:29:09 +0000140 break;
141 case 8: // 'Backspace'
Nils Diewald7148c6f2015-05-04 15:07:53 +0000142 this._prefix.chop();
Nils Diewald5975d702015-03-09 17:45:42 +0000143 this.show();
Nils Diewald59c02fc2015-03-07 01:29:09 +0000144 e.halt();
145 break;
Nils Diewald47f366b2015-04-15 20:06:35 +0000146 };
147 },
Nils Diewald59c02fc2015-03-07 01:29:09 +0000148
Nils Diewald47f366b2015-04-15 20:06:35 +0000149 // Add characters to prefix
150 _keypress : function (e) {
151 var c = String.fromCharCode(_codeFromEvent(e)).toLowerCase();
Nils Diewald5975d702015-03-09 17:45:42 +0000152
Nils Diewald47f366b2015-04-15 20:06:35 +0000153 // Add prefix
154 this._prefix.add(c);
155
156 if (!this.show()) {
157 this.prefix('').show();
158 e.halt();
Nils Diewald59c02fc2015-03-07 01:29:09 +0000159 };
160 },
161
Akron47c086c2016-05-18 21:22:06 +0200162 /**
163 * Show screen X
164 */
165 screen : function (nr) {
166 if (this._offset === nr)
167 return;
168
169 this._showItems(nr);
170 },
171
Nils Diewald2fe12e12015-03-06 16:47:06 +0000172 // Initialize list
Akron1ff3ac22016-04-28 16:30:45 +0200173 _init : function (itemClass, prefixClass, lengthFieldClass, params) {
Nils Diewald59c02fc2015-03-07 01:29:09 +0000174 var that = this;
Nils Diewald0e6992a2015-04-14 20:13:52 +0000175 this._itemClass = itemClass || defaultItemClass;
Nils Diewald5975d702015-03-09 17:45:42 +0000176
Akronc7448732016-04-27 14:06:58 +0200177 // Add prefix object
178 if (prefixClass !== undefined) {
Nils Diewald5975d702015-03-09 17:45:42 +0000179 this._prefix = prefixClass.create();
Akronc7448732016-04-27 14:06:58 +0200180 }
181 else {
Nils Diewald0e6992a2015-04-14 20:13:52 +0000182 this._prefix = defaultPrefixClass.create();
Akronc7448732016-04-27 14:06:58 +0200183 };
Nils Diewald5c5a7472015-04-02 22:13:38 +0000184 this._prefix._menu = this;
185
Akronc7448732016-04-27 14:06:58 +0200186 // Add lengthField object
Akron1ff3ac22016-04-28 16:30:45 +0200187 if (lengthFieldClass !== undefined) {
188 this._lengthField = lengthFieldClass.create();
189 }
190 else {
191 this._lengthField = defaultLengthFieldClass.create();
192 };
Akronc7448732016-04-27 14:06:58 +0200193 this._lengthField._menu = this;
194
Akron9905e2a2016-05-10 16:06:44 +0200195 // Initialize the slider
Akron47c086c2016-05-18 21:22:06 +0200196 this._slider = sliderClass.create(this);
Akronc7448732016-04-27 14:06:58 +0200197
Nils Diewald5975d702015-03-09 17:45:42 +0000198 var e = document.createElement("ul");
Nils Diewald59c02fc2015-03-07 01:29:09 +0000199 e.style.opacity = 0;
200 e.style.outline = 0;
201 e.setAttribute('tabindex', 0);
Nils Diewald5c5a7472015-04-02 22:13:38 +0000202 e.classList.add('menu');
Nils Diewald58141332015-04-07 16:18:45 +0000203 e.classList.add('roll');
Nils Diewald5975d702015-03-09 17:45:42 +0000204 e.appendChild(this._prefix.element());
Akronc7448732016-04-27 14:06:58 +0200205 e.appendChild(this._lengthField.element());
Akron9905e2a2016-05-10 16:06:44 +0200206 e.appendChild(this._slider.element());
Nils Diewald86dad5b2015-01-28 15:09:07 +0000207
Nils Diewald6e43ffd2015-03-25 18:55:39 +0000208 // This has to be cleaned up later on
209 e["menu"] = this;
210
Nils Diewald59c02fc2015-03-07 01:29:09 +0000211 // Arrow keys
212 e.addEventListener(
Nils Diewald47f366b2015-04-15 20:06:35 +0000213 'keydown',
214 function (ev) {
215 that._keydown(ev)
216 },
217 false
218 );
219
220 // Strings
221 e.addEventListener(
Nils Diewald5975d702015-03-09 17:45:42 +0000222 'keypress',
Nils Diewald59c02fc2015-03-07 01:29:09 +0000223 function (ev) {
Nils Diewald5975d702015-03-09 17:45:42 +0000224 that._keypress(ev)
Nils Diewald59c02fc2015-03-07 01:29:09 +0000225 },
226 false
227 );
228
229 // Mousewheel
230 e.addEventListener(
Nils Diewald5975d702015-03-09 17:45:42 +0000231 'wheel',
Nils Diewald59c02fc2015-03-07 01:29:09 +0000232 function (ev) {
233 that._mousewheel(ev)
Nils Diewald2fe12e12015-03-06 16:47:06 +0000234 },
235 false
236 );
237
Nils Diewald5975d702015-03-09 17:45:42 +0000238 this._element = e;
Nils Diewald86dad5b2015-01-28 15:09:07 +0000239 this.active = false;
Akron37513a62015-11-17 01:07:11 +0100240 // this.selected = undefined;
Nils Diewald86dad5b2015-01-28 15:09:07 +0000241 this._items = new Array();
Akron9905e2a2016-05-10 16:06:44 +0200242 var i = 0;
Nils Diewald2fe12e12015-03-06 16:47:06 +0000243
244 // Initialize item list based on parameters
Nils Diewald86dad5b2015-01-28 15:09:07 +0000245 for (i in params) {
Nils Diewald0e6992a2015-04-14 20:13:52 +0000246 var obj = this._itemClass.create(params[i]);
Nils Diewald6e43ffd2015-03-25 18:55:39 +0000247
248 // This may become circular
249 obj["_menu"] = this;
Akron1ff3ac22016-04-28 16:30:45 +0200250 this._lengthField.add(params[i]);
Nils Diewald2fe12e12015-03-06 16:47:06 +0000251 this._items.push(obj);
Nils Diewald86dad5b2015-01-28 15:09:07 +0000252 };
Akronc7448732016-04-27 14:06:58 +0200253
Nils Diewald0e6992a2015-04-14 20:13:52 +0000254 this._limit = menuLimit;
Akronf86eaea2016-05-13 18:02:27 +0200255 this._slider.length(this.liveLength());
Akron9905e2a2016-05-10 16:06:44 +0200256 this._slider.limit(this._limit);
257
Akronf86eaea2016-05-13 18:02:27 +0200258 this.position = 0; // position in the active list
259 this._active = -1; // active item in the item list
Nils Diewald20f7ace2015-05-07 12:51:34 +0000260 this._firstActive = false; // Show the first item active always?
Nils Diewald86dad5b2015-01-28 15:09:07 +0000261 this._reset();
262 return this;
263 },
264
Nils Diewald2fe12e12015-03-06 16:47:06 +0000265 /**
Nils Diewald7148c6f2015-05-04 15:07:53 +0000266 * Get the associated dom element.
Nils Diewald2fe12e12015-03-06 16:47:06 +0000267 */
Nils Diewald86dad5b2015-01-28 15:09:07 +0000268 element : function () {
269 return this._element;
270 },
271
Akronf86eaea2016-05-13 18:02:27 +0200272 slider : function () {
273 return this._slider;
274 },
Nils Diewald7148c6f2015-05-04 15:07:53 +0000275
Nils Diewald2fe12e12015-03-06 16:47:06 +0000276 /**
Nils Diewald7148c6f2015-05-04 15:07:53 +0000277 * Get the creator class for items
Nils Diewald2fe12e12015-03-06 16:47:06 +0000278 */
Nils Diewald86dad5b2015-01-28 15:09:07 +0000279 itemClass : function () {
280 return this._itemClass;
281 },
282
283 /**
Nils Diewald7148c6f2015-05-04 15:07:53 +0000284 * Get and set the numerical value
285 * for the maximum number of items visible.
Nils Diewald86dad5b2015-01-28 15:09:07 +0000286 */
287 limit : function (limit) {
Nils Diewald5975d702015-03-09 17:45:42 +0000288 if (arguments.length === 1) {
Nils Diewald86dad5b2015-01-28 15:09:07 +0000289 this._limit = limit;
Akron9905e2a2016-05-10 16:06:44 +0200290 this._slider.limit(limit);
Nils Diewald5975d702015-03-09 17:45:42 +0000291 return this;
292 };
Nils Diewald86dad5b2015-01-28 15:09:07 +0000293 return this._limit;
294 },
295
Nils Diewald7148c6f2015-05-04 15:07:53 +0000296
Nils Diewald86dad5b2015-01-28 15:09:07 +0000297 /**
298 * Upgrade this object to another object,
299 * while private data stays intact.
300 *
Nils Diewald2fe12e12015-03-06 16:47:06 +0000301 * @param {Object} An object with properties.
Nils Diewald86dad5b2015-01-28 15:09:07 +0000302 */
303 upgradeTo : function (props) {
304 for (var prop in props) {
305 this[prop] = props[prop];
306 };
307 return this;
308 },
309
Nils Diewald7148c6f2015-05-04 15:07:53 +0000310
Nils Diewald2fe12e12015-03-06 16:47:06 +0000311 // Reset chosen item and prefix
Nils Diewald86dad5b2015-01-28 15:09:07 +0000312 _reset : function () {
313 this._offset = 0;
314 this._pos = 0;
Nils Diewald5975d702015-03-09 17:45:42 +0000315 this._prefix.value('');
Nils Diewald86dad5b2015-01-28 15:09:07 +0000316 },
317
Nils Diewald7148c6f2015-05-04 15:07:53 +0000318
Nils Diewald86dad5b2015-01-28 15:09:07 +0000319 /**
320 * Filter the list and make it visible
321 *
322 * @param {string} Prefix for filtering the list
323 */
Nils Diewald5975d702015-03-09 17:45:42 +0000324 show : function () {
Nils Diewalde8518f82015-03-18 22:41:49 +0000325
Nils Diewald86dad5b2015-01-28 15:09:07 +0000326 // Initialize the list
327 if (!this._initList())
Nils Diewald59c02fc2015-03-07 01:29:09 +0000328 return false;
Nils Diewald86dad5b2015-01-28 15:09:07 +0000329
Nils Diewald2fe12e12015-03-06 16:47:06 +0000330 // show based on initial offset
Nils Diewald86dad5b2015-01-28 15:09:07 +0000331 this._showItems(0);
332
333 // Set the first element to active
Nils Diewald2fe12e12015-03-06 16:47:06 +0000334 // Todo: Or the last element chosen
Akron47c086c2016-05-18 21:22:06 +0200335 if (this._firstActive) {
Nils Diewald20f7ace2015-05-07 12:51:34 +0000336 this.liveItem(0).active(true);
Akron47c086c2016-05-18 21:22:06 +0200337 this.position = 0;
338 this._active = 0;
339 }
340 else {
341 this.position = -1;
342 }
Akron37513a62015-11-17 01:07:11 +0100343
Nils Diewalde8518f82015-03-18 22:41:49 +0000344 this._prefix.active(false);
Nils Diewald86dad5b2015-01-28 15:09:07 +0000345
Nils Diewald2fe12e12015-03-06 16:47:06 +0000346 this._element.style.opacity = 1;
347
Akron9905e2a2016-05-10 16:06:44 +0200348 this._slider.show();
349
Akron37513a62015-11-17 01:07:11 +0100350 // Iterate to the active item
351 if (this._active !== -1 && !this._prefix.isSet()) {
Akronf86eaea2016-05-13 18:02:27 +0200352 while (this._list[this.position] < this._active) {
Akron37513a62015-11-17 01:07:11 +0100353 this.next();
354 };
355 };
356
Nils Diewald86dad5b2015-01-28 15:09:07 +0000357 // Add classes for rolling menus
358 this._boundary(true);
Nils Diewald59c02fc2015-03-07 01:29:09 +0000359 return true;
Nils Diewald86dad5b2015-01-28 15:09:07 +0000360 },
361
Nils Diewald7148c6f2015-05-04 15:07:53 +0000362
363 /**
364 * Hide the menu and call the onHide callback.
365 */
Nils Diewald2fe12e12015-03-06 16:47:06 +0000366 hide : function () {
Nils Diewald59c02fc2015-03-07 01:29:09 +0000367 this.active = false;
368 this.delete();
Nils Diewald2fe12e12015-03-06 16:47:06 +0000369 this._element.style.opacity = 0;
Nils Diewald7148c6f2015-05-04 15:07:53 +0000370 this._prefix.clear();
Nils Diewald5c5a7472015-04-02 22:13:38 +0000371 this.onHide();
Nils Diewald6e43ffd2015-03-25 18:55:39 +0000372 /* this._element.blur(); */
Nils Diewald86dad5b2015-01-28 15:09:07 +0000373 },
374
Nils Diewald7148c6f2015-05-04 15:07:53 +0000375 /**
376 * Function released when the menu hides.
377 * This method is expected to be overridden.
378 */
Nils Diewald5c5a7472015-04-02 22:13:38 +0000379 onHide : function () {},
380
Nils Diewald2fe12e12015-03-06 16:47:06 +0000381 // Initialize the list
Nils Diewald86dad5b2015-01-28 15:09:07 +0000382 _initList : function () {
383
Nils Diewald2fe12e12015-03-06 16:47:06 +0000384 // Create a new list
Nils Diewald86dad5b2015-01-28 15:09:07 +0000385 if (this._list === undefined) {
386 this._list = [];
387 }
388 else if (this._list.length != 0) {
389 this._boundary(false);
390 this._list.length = 0;
391 };
392
393 // Offset is initially zero
394 this._offset = 0;
395
Nils Diewald2fe12e12015-03-06 16:47:06 +0000396 // There is no prefix set
Nils Diewald86dad5b2015-01-28 15:09:07 +0000397 if (this.prefix().length <= 0) {
Nils Diewald5975d702015-03-09 17:45:42 +0000398 var i = 0;
399 for (; i < this._items.length; i++)
Nils Diewald86dad5b2015-01-28 15:09:07 +0000400 this._list.push(i);
Nils Diewald5975d702015-03-09 17:45:42 +0000401 while (this._items[++i] !== undefined) {
402 this._items[i].lowlight();
Nils Diewald5975d702015-03-09 17:45:42 +0000403 };
Nils Diewald86dad5b2015-01-28 15:09:07 +0000404 return true;
405 };
406
Nils Diewald2fe12e12015-03-06 16:47:06 +0000407 // There is a prefix set, so filter the list
Nils Diewald86dad5b2015-01-28 15:09:07 +0000408 var pos;
409 var paddedPrefix = " " + this.prefix();
410
Nils Diewald2fe12e12015-03-06 16:47:06 +0000411 // Iterate over all items and choose preferred matching items
412 // i.e. the matching happens at the word start
Nils Diewald86dad5b2015-01-28 15:09:07 +0000413 for (pos = 0; pos < this._items.length; pos++) {
414 if ((this.item(pos).lcField().indexOf(paddedPrefix)) >= 0)
415 this._list.push(pos);
416 };
417
Nils Diewald2fe12e12015-03-06 16:47:06 +0000418 // The list is empty - so lower your expectations
419 // Iterate over all items and choose matching items
420 // i.e. the matching happens anywhere in the word
Nils Diewald86dad5b2015-01-28 15:09:07 +0000421 if (this._list.length == 0) {
422 for (pos = 0; pos < this._items.length; pos++) {
423 if ((this.item(pos).lcField().indexOf(this.prefix())) >= 0)
424 this._list.push(pos);
425 };
426 };
427
Nils Diewald2fe12e12015-03-06 16:47:06 +0000428 // Filter was successful - yeah!
Nils Diewald86dad5b2015-01-28 15:09:07 +0000429 return this._list.length > 0 ? true : false;
430 },
431
432 // Set boundary for viewport
433 _boundary : function (bool) {
434 this.item(this._list[0]).noMore(bool);
435 this.item(this._list[this._list.length - 1]).noMore(bool);
436 },
437
Nils Diewald7148c6f2015-05-04 15:07:53 +0000438
Nils Diewald86dad5b2015-01-28 15:09:07 +0000439 /**
440 * Get the prefix for filtering,
Nils Diewald2fe12e12015-03-06 16:47:06 +0000441 * e.g. &quot;ve&quot; for &quot;verb&quot;
Nils Diewald86dad5b2015-01-28 15:09:07 +0000442 */
Nils Diewald5975d702015-03-09 17:45:42 +0000443 prefix : function (pref) {
444 if (arguments.length === 1) {
445 this._prefix.value(pref);
446 return this;
447 };
448 return this._prefix.value();
Nils Diewald86dad5b2015-01-28 15:09:07 +0000449 },
450
Akronc7448732016-04-27 14:06:58 +0200451 /**
452 * Get the lengthField object.
453 */
454 lengthField : function () {
455 return this._lengthField;
456 },
Nils Diewald7148c6f2015-05-04 15:07:53 +0000457
Nils Diewald2fe12e12015-03-06 16:47:06 +0000458 // Append Items that should be shown
Akron47c086c2016-05-18 21:22:06 +0200459 _showItems : function (off) {
460
Nils Diewald86dad5b2015-01-28 15:09:07 +0000461 this.delete();
462
463 // Use list
464 var shown = 0;
465 var i;
466 for (i in this._list) {
Nils Diewald86dad5b2015-01-28 15:09:07 +0000467 // Don't show - it's before offset
Akron47c086c2016-05-18 21:22:06 +0200468 if (shown++ < off)
Nils Diewald86dad5b2015-01-28 15:09:07 +0000469 continue;
470
Akron47c086c2016-05-18 21:22:06 +0200471 var itemNr = this._list[i];
472 var item = this.item(itemNr);
473 this._append(itemNr);
Nils Diewald86dad5b2015-01-28 15:09:07 +0000474
Akron47c086c2016-05-18 21:22:06 +0200475 /*
476 this._items[this._list[i]].active();
477 console.dir([i, this._active]);
478 if (this._active === i) {
479console.log('True!')
480 this._items[this._list[i]].active(true);
481 };
482 */
483
484 // this._offset))
485 if (shown >= (this.limit() + off))
Nils Diewald86dad5b2015-01-28 15:09:07 +0000486 break;
487 };
488 },
489
490 /**
491 * Delete all visible items from the menu element
492 */
493 delete : function () {
494 var child;
Nils Diewald2fe12e12015-03-06 16:47:06 +0000495
Nils Diewald5975d702015-03-09 17:45:42 +0000496 /*
Nils Diewald2fe12e12015-03-06 16:47:06 +0000497 // Iterate over all visible items
Nils Diewald86dad5b2015-01-28 15:09:07 +0000498 for (var i = 0; i <= this.limit(); i++) {
499
Nils Diewald5975d702015-03-09 17:45:42 +0000500 // there is a visible element
501 // unhighlight!
Nils Diewald59c02fc2015-03-07 01:29:09 +0000502 if (child = this.shownItem(i)) {
Nils Diewald86dad5b2015-01-28 15:09:07 +0000503 child.lowlight();
Nils Diewald59c02fc2015-03-07 01:29:09 +0000504 child.active(false);
505 };
Nils Diewald86dad5b2015-01-28 15:09:07 +0000506 };
Nils Diewald5975d702015-03-09 17:45:42 +0000507 */
508
509 for (var i in this._list) {
510 var item = this._items[this._list[i]];
511 item.lowlight();
512 item.active(false);
513 };
Nils Diewald86dad5b2015-01-28 15:09:07 +0000514
Nils Diewald2fe12e12015-03-06 16:47:06 +0000515 // Remove all children
Nils Diewald5975d702015-03-09 17:45:42 +0000516 var children = this._element.childNodes;
Akronc7448732016-04-27 14:06:58 +0200517 // Leave the prefix and lengthField
Akron9905e2a2016-05-10 16:06:44 +0200518 for (var i = children.length - 1; i >= 3; i--) {
Nils Diewald5975d702015-03-09 17:45:42 +0000519 this._element.removeChild(
520 children[i]
521 );
522 };
Nils Diewald86dad5b2015-01-28 15:09:07 +0000523 },
524
525
526 // Append item to the shown list based on index
527 _append : function (i) {
528 var item = this.item(i);
529
530 // Highlight based on prefix
531 if (this.prefix().length > 0)
532 item.highlight(this.prefix());
533
534 // Append element
535 this.element().appendChild(item.element());
Akronf86eaea2016-05-13 18:02:27 +0200536
537 this._slider.offset(this._offset);
Nils Diewald86dad5b2015-01-28 15:09:07 +0000538 },
539
540
Nils Diewald2fe12e12015-03-06 16:47:06 +0000541 // Prepend item to the shown list based on index
542 _prepend : function (i) {
543 var item = this.item(i);
544
545 // Highlight based on prefix
546 if (this.prefix().length > 0)
547 item.highlight(this.prefix());
548
549 var e = this.element();
Akron9905e2a2016-05-10 16:06:44 +0200550 // Append element after lengthField/prefix/slider
Nils Diewald2fe12e12015-03-06 16:47:06 +0000551 e.insertBefore(
552 item.element(),
Akron9905e2a2016-05-10 16:06:44 +0200553 e.children[3]
Nils Diewald2fe12e12015-03-06 16:47:06 +0000554 );
Akronf86eaea2016-05-13 18:02:27 +0200555
556 this._slider.offset(this._offset);
Nils Diewald2fe12e12015-03-06 16:47:06 +0000557 },
558
559
560 /**
561 * Get a specific item from the complete list
562 *
563 * @param {number} index of the list item
564 */
565 item : function (index) {
566 return this._items[index]
567 },
568
569
Nils Diewald86dad5b2015-01-28 15:09:07 +0000570 /**
571 * Get a specific item from the filtered list
572 *
573 * @param {number} index of the list item
Nils Diewald2fe12e12015-03-06 16:47:06 +0000574 * in the filtered list
Nils Diewald86dad5b2015-01-28 15:09:07 +0000575 */
576 liveItem : function (index) {
577 if (this._list === undefined)
578 if (!this._initList())
579 return;
580
581 return this._items[this._list[index]];
582 },
583
Nils Diewald86dad5b2015-01-28 15:09:07 +0000584
585 /**
586 * Get a specific item from the visible list
587 *
588 * @param {number} index of the list item
Nils Diewald2fe12e12015-03-06 16:47:06 +0000589 * in the visible list
Nils Diewald86dad5b2015-01-28 15:09:07 +0000590 */
591 shownItem : function (index) {
592 if (index >= this.limit())
593 return;
594 return this.liveItem(this._offset + index);
595 },
596
597
Nils Diewald2fe12e12015-03-06 16:47:06 +0000598 /**
599 * Get the length of the full list
600 */
601 length : function () {
602 return this._items.length;
603 },
604
605
606 /**
607 * Make the next item in the filtered menu active
Nils Diewald86dad5b2015-01-28 15:09:07 +0000608 */
609 next : function () {
Nils Diewald5975d702015-03-09 17:45:42 +0000610
Nils Diewald86dad5b2015-01-28 15:09:07 +0000611 // No active element set
Nils Diewald5975d702015-03-09 17:45:42 +0000612 var newItem;
613
Akron47c086c2016-05-18 21:22:06 +0200614 if (this.position !== -1) {
615 // Set new live item
616 if (!this._prefix.active()) {
617 var oldItem = this.liveItem(this.position);
618 oldItem.active(false);
619 };
Nils Diewalde8518f82015-03-18 22:41:49 +0000620 };
621
Akronf86eaea2016-05-13 18:02:27 +0200622 this.position++;
Akron47c086c2016-05-18 21:22:06 +0200623 this._active = this.position;
Nils Diewalde8518f82015-03-18 22:41:49 +0000624
Akronf86eaea2016-05-13 18:02:27 +0200625 newItem = this.liveItem(this.position);
Nils Diewald86dad5b2015-01-28 15:09:07 +0000626
Nils Diewald5975d702015-03-09 17:45:42 +0000627 // The next element is undefined - roll to top or to prefix
Nils Diewald86dad5b2015-01-28 15:09:07 +0000628 if (newItem === undefined) {
Nils Diewald5975d702015-03-09 17:45:42 +0000629
630 // Activate prefix
631 var prefix = this._prefix;
632
633 // Mark prefix
634 if (prefix.isSet() && !prefix.active()) {
Akronf86eaea2016-05-13 18:02:27 +0200635 this.position--;
Nils Diewald5975d702015-03-09 17:45:42 +0000636 prefix.active(true);
Akron47c086c2016-05-18 21:22:06 +0200637 this._active = -1;
Nils Diewald5975d702015-03-09 17:45:42 +0000638 return;
639 }
640 else {
641 this._offset = 0;
Akronf86eaea2016-05-13 18:02:27 +0200642 this.position = 0;
Nils Diewald5975d702015-03-09 17:45:42 +0000643 newItem = this.liveItem(0);
Akron47c086c2016-05-18 21:22:06 +0200644 this._active = 0;
Nils Diewald5975d702015-03-09 17:45:42 +0000645 this._showItems(0);
646 };
Nils Diewald86dad5b2015-01-28 15:09:07 +0000647 }
648
649 // The next element is outside the view - roll down
Akronf86eaea2016-05-13 18:02:27 +0200650 else if (this.position >= (this.limit() + this._offset)) {
Nils Diewald86dad5b2015-01-28 15:09:07 +0000651 this._removeFirst();
652 this._offset++;
Akronf86eaea2016-05-13 18:02:27 +0200653 this._append(this._list[this.position]);
Nils Diewald86dad5b2015-01-28 15:09:07 +0000654 };
Nils Diewald5975d702015-03-09 17:45:42 +0000655
656 this._prefix.active(false);
Nils Diewald86dad5b2015-01-28 15:09:07 +0000657 newItem.active(true);
658 },
659
Nils Diewalde8518f82015-03-18 22:41:49 +0000660 /*
661 * Page down to the first item on the next page
662 */
663 /*
664 nextPage : function () {
665
666 // Prefix is active
667 if (this._prefix.active()) {
668 this._prefix.active(false);
669 }
670
671 // Last item is chosen
Akronf86eaea2016-05-13 18:02:27 +0200672 else if (this.position >= this.limit() + this._offset) {
Nils Diewalde8518f82015-03-18 22:41:49 +0000673
Akronf86eaea2016-05-13 18:02:27 +0200674 this.position = this.limit() + this._offset - 1;
675 newItem = this.liveItem(this.position);
676 var oldItem = this.liveItem(this.position--);
Nils Diewalde8518f82015-03-18 22:41:49 +0000677 oldItem.active(false);
678 }
679
680 // Last item of page is chosen
681 else if (0) {
682
683 // Jump to last item
684 else {
Akronf86eaea2016-05-13 18:02:27 +0200685 var oldItem = this.liveItem(this.position);
Nils Diewalde8518f82015-03-18 22:41:49 +0000686 oldItem.active(false);
687
Akronf86eaea2016-05-13 18:02:27 +0200688 this.position = this.limit() + this._offset - 1;
689 newItem = this.liveItem(this.position);
Nils Diewalde8518f82015-03-18 22:41:49 +0000690 };
691
692 newItem.active(true);
693 },
694 */
695
Nils Diewald86dad5b2015-01-28 15:09:07 +0000696
697 /*
698 * Make the previous item in the menu active
699 */
Nils Diewald86dad5b2015-01-28 15:09:07 +0000700 prev : function () {
Nils Diewald2d210752015-03-09 19:01:15 +0000701
Nils Diewald2fe12e12015-03-06 16:47:06 +0000702 // No active element set
Akronf86eaea2016-05-13 18:02:27 +0200703 if (this.position === -1) {
Nils Diewald86dad5b2015-01-28 15:09:07 +0000704 return;
Nils Diewalde8518f82015-03-18 22:41:49 +0000705 // TODO: Choose last item
706 };
Nils Diewald86dad5b2015-01-28 15:09:07 +0000707
Nils Diewald5975d702015-03-09 17:45:42 +0000708 var newItem;
709
Nils Diewald86dad5b2015-01-28 15:09:07 +0000710 // Set new live item
Nils Diewald2d210752015-03-09 19:01:15 +0000711 if (!this._prefix.active()) {
Akronf86eaea2016-05-13 18:02:27 +0200712 var oldItem = this.liveItem(this.position--);
Nils Diewald2d210752015-03-09 19:01:15 +0000713 oldItem.active(false);
714 };
715
Akronf86eaea2016-05-13 18:02:27 +0200716 newItem = this.liveItem(this.position);
Nils Diewald86dad5b2015-01-28 15:09:07 +0000717
718 // The previous element is undefined - roll to bottom
719 if (newItem === undefined) {
Nils Diewald5975d702015-03-09 17:45:42 +0000720
721 // Activate prefix
722 var prefix = this._prefix;
Nils Diewald2fe12e12015-03-06 16:47:06 +0000723 this._offset = this.liveLength() - this.limit();
Nils Diewald2d210752015-03-09 19:01:15 +0000724
725 // Normalize offset
726 this._offset = this._offset < 0 ? 0 : this._offset;
727
Akronf86eaea2016-05-13 18:02:27 +0200728 this.position = this.liveLength() - 1;
Nils Diewald5975d702015-03-09 17:45:42 +0000729
730 if (prefix.isSet() && !prefix.active()) {
Akronf86eaea2016-05-13 18:02:27 +0200731 this.position++;
Nils Diewald5975d702015-03-09 17:45:42 +0000732 prefix.active(true);
733 return;
734 }
735 else {
Akronf86eaea2016-05-13 18:02:27 +0200736 newItem = this.liveItem(this.position);
Nils Diewald5975d702015-03-09 17:45:42 +0000737 this._showItems(this._offset);
738 };
Nils Diewald86dad5b2015-01-28 15:09:07 +0000739 }
740
741 // The previous element is outside the view - roll up
Akronf86eaea2016-05-13 18:02:27 +0200742 else if (this.position < this._offset) {
Nils Diewald86dad5b2015-01-28 15:09:07 +0000743 this._removeLast();
744 this._offset--;
Akronf86eaea2016-05-13 18:02:27 +0200745 this._prepend(this._list[this.position]);
Nils Diewald86dad5b2015-01-28 15:09:07 +0000746 };
Nils Diewald2fe12e12015-03-06 16:47:06 +0000747
Nils Diewald5975d702015-03-09 17:45:42 +0000748 this._prefix.active(false);
Nils Diewald86dad5b2015-01-28 15:09:07 +0000749 newItem.active(true);
750 },
Nils Diewald86dad5b2015-01-28 15:09:07 +0000751
752
Nils Diewald7148c6f2015-05-04 15:07:53 +0000753 /**
754 * Length of the filtered item list.
755 */
Nils Diewald5975d702015-03-09 17:45:42 +0000756 liveLength : function () {
757 if (this._list === undefined)
758 this._initList();
759 return this._list.length;
760 },
761
762
Nils Diewald2fe12e12015-03-06 16:47:06 +0000763 // Remove the HTML node from the first item
Nils Diewald86dad5b2015-01-28 15:09:07 +0000764 _removeFirst : function () {
765 this.item(this._list[this._offset]).lowlight();
Akron9905e2a2016-05-10 16:06:44 +0200766 // leave lengthField/prefix/slider
767 this._element.removeChild(this._element.children[3]);
Nils Diewald86dad5b2015-01-28 15:09:07 +0000768 },
769
Nils Diewald2fe12e12015-03-06 16:47:06 +0000770
771 // Remove the HTML node from the last item
Nils Diewald86dad5b2015-01-28 15:09:07 +0000772 _removeLast : function () {
Nils Diewald2fe12e12015-03-06 16:47:06 +0000773 this.item(this._list[this._offset + this.limit() - 1]).lowlight();
Nils Diewald86dad5b2015-01-28 15:09:07 +0000774 this._element.removeChild(this._element.lastChild);
Nils Diewald2fe12e12015-03-06 16:47:06 +0000775 }
Nils Diewald86dad5b2015-01-28 15:09:07 +0000776 };
Nils Diewald0e6992a2015-04-14 20:13:52 +0000777});