blob: 2bc356b4264e5cc4a62dbda1eb6ea0bef9c706e9 [file] [log] [blame]
Nils Diewald2fe12e12015-03-06 16:47:06 +00001/**
2 * Create scrollable drop-down menus.
3 *
4 * @author Nils Diewald
5 */
Nils Diewald2488d052015-04-09 21:46:02 +00006/*
Nils Diewald0e6992a2015-04-14 20:13:52 +00007 * TODO: space is not a valid prefix!
Nils Diewald2488d052015-04-09 21:46:02 +00008 */
Nils Diewald0e6992a2015-04-14 20:13:52 +00009define([
10 'menu/item',
11 'menu/prefix',
12 'util'
13], function (defaultItemClass,
14 defaultPrefixClass) {
Nils Diewaldfda29d92015-01-22 17:28:01 +000015
Nils Diewald0e6992a2015-04-14 20:13:52 +000016 // Todo: This may not be necessary
17 // Default maximum number of menu items
18 var menuLimit = 8;
19
20 function _codeFromEvent (e) {
21 if (e.charCode && (e.keyCode == 0))
22 return e.charCode
23 return e.keyCode;
Nils Diewald59c02fc2015-03-07 01:29:09 +000024 };
25
Nils Diewald86dad5b2015-01-28 15:09:07 +000026
27 /**
28 * List of items for drop down menu (complete).
29 * Only a sublist of the menu is filtered (live).
30 * Only a sublist of the filtered menu is visible (shown).
31 */
Nils Diewald0e6992a2015-04-14 20:13:52 +000032 return {
Nils Diewald86dad5b2015-01-28 15:09:07 +000033 /**
34 * Create new Menu based on the action prefix
35 * and a list of menu items.
36 *
37 * @this {Menu}
38 * @constructor
39 * @param {string} Context prefix
40 * @param {Array.<Array.<string>>} List of menu items
41 */
42 create : function (params) {
Nils Diewald0e6992a2015-04-14 20:13:52 +000043 return Object.create(this)._init(params);
Nils Diewald86dad5b2015-01-28 15:09:07 +000044 },
45
Nils Diewald6e43ffd2015-03-25 18:55:39 +000046 /**
47 * Destroy this menu
48 * (in case you don't trust the
49 * mark and sweep GC)!
50 */
51 destroy : function () {
52 if (this._element != undefined)
53 delete this._element["menu"];
54
55 for (var i = 0; i < this._items.length; i++) {
56 delete this._items[i]["_menu"];
57 };
Nils Diewald5c5a7472015-04-02 22:13:38 +000058 delete this._prefix['_menu'];
Nils Diewald6e43ffd2015-03-25 18:55:39 +000059 },
60
Nils Diewald2fe12e12015-03-06 16:47:06 +000061 focus : function () {
62 this._element.focus();
63 },
64
Nils Diewald59c02fc2015-03-07 01:29:09 +000065 // mouse wheel treatment
66 _mousewheel : function (e) {
67 var delta = 0;
Nils Diewald5975d702015-03-09 17:45:42 +000068
69 delta = e.deltaY / 120;
70 if (delta > 0)
Nils Diewald59c02fc2015-03-07 01:29:09 +000071 this.next();
Nils Diewald5975d702015-03-09 17:45:42 +000072 else if (delta < 0)
Nils Diewald59c02fc2015-03-07 01:29:09 +000073 this.prev();
Nils Diewald59c02fc2015-03-07 01:29:09 +000074 e.halt();
75 },
76
77 // Arrow key and prefix treatment
Nils Diewald47f366b2015-04-15 20:06:35 +000078 _keydown : function (e) {
Nils Diewald59c02fc2015-03-07 01:29:09 +000079 var code = _codeFromEvent(e);
80
Nils Diewald59c02fc2015-03-07 01:29:09 +000081 switch (code) {
82 case 27: // 'Esc'
83 e.halt();
84 this.hide();
85 break;
Nils Diewald5975d702015-03-09 17:45:42 +000086
Nils Diewald59c02fc2015-03-07 01:29:09 +000087 case 38: // 'Up'
88 e.halt();
89 this.prev();
90 break;
Nils Diewald5975d702015-03-09 17:45:42 +000091 case 33: // 'Page up'
Nils Diewald59c02fc2015-03-07 01:29:09 +000092 e.halt();
Nils Diewald5975d702015-03-09 17:45:42 +000093 this.prev();
94 break;
95 case 40: // 'Down'
96 e.halt();
97 this.next();
98 break;
99 case 34: // 'Page down'
100 e.halt();
101 this.next();
102 break;
103 case 39: // 'Right'
Nils Diewalde8518f82015-03-18 22:41:49 +0000104 if (this._prefix.active())
105 break;
106
Nils Diewald5975d702015-03-09 17:45:42 +0000107 var item = this.liveItem(this._position);
108 if (item["further"] !== undefined) {
109 item["further"].bind(item).apply();
110 e.halt();
111 };
112 break;
113 case 13: // 'Enter'
114
115 // Click on prefix
116 if (this._prefix.active())
Nils Diewald6e43ffd2015-03-25 18:55:39 +0000117 this._prefix.onclick(e);
Nils Diewald5975d702015-03-09 17:45:42 +0000118
119 // Click on item
120 else
Nils Diewald6e43ffd2015-03-25 18:55:39 +0000121 this.liveItem(this._position).onclick(e);
Nils Diewald5975d702015-03-09 17:45:42 +0000122 e.halt();
Nils Diewald59c02fc2015-03-07 01:29:09 +0000123 break;
124 case 8: // 'Backspace'
Nils Diewald5975d702015-03-09 17:45:42 +0000125 this._prefix.backspace();
126 this.show();
Nils Diewald59c02fc2015-03-07 01:29:09 +0000127 e.halt();
128 break;
Nils Diewald47f366b2015-04-15 20:06:35 +0000129 };
130 },
Nils Diewald59c02fc2015-03-07 01:29:09 +0000131
Nils Diewald47f366b2015-04-15 20:06:35 +0000132 // Add characters to prefix
133 _keypress : function (e) {
134 var c = String.fromCharCode(_codeFromEvent(e)).toLowerCase();
Nils Diewald5975d702015-03-09 17:45:42 +0000135
Nils Diewald47f366b2015-04-15 20:06:35 +0000136 // Add prefix
137 this._prefix.add(c);
138
139 if (!this.show()) {
140 this.prefix('').show();
141 e.halt();
Nils Diewald59c02fc2015-03-07 01:29:09 +0000142 };
143 },
144
Nils Diewald2fe12e12015-03-06 16:47:06 +0000145 // Initialize list
Nils Diewald5975d702015-03-09 17:45:42 +0000146 _init : function (itemClass, prefixClass, params) {
Nils Diewald59c02fc2015-03-07 01:29:09 +0000147 var that = this;
Nils Diewald0e6992a2015-04-14 20:13:52 +0000148 this._itemClass = itemClass || defaultItemClass;
Nils Diewald5975d702015-03-09 17:45:42 +0000149
150 if (prefixClass !== undefined)
151 this._prefix = prefixClass.create();
152 else
Nils Diewald0e6992a2015-04-14 20:13:52 +0000153 this._prefix = defaultPrefixClass.create();
Nils Diewald5975d702015-03-09 17:45:42 +0000154
Nils Diewald5c5a7472015-04-02 22:13:38 +0000155 this._prefix._menu = this;
156
Nils Diewald5975d702015-03-09 17:45:42 +0000157 var e = document.createElement("ul");
Nils Diewald59c02fc2015-03-07 01:29:09 +0000158 e.style.opacity = 0;
159 e.style.outline = 0;
160 e.setAttribute('tabindex', 0);
Nils Diewald5c5a7472015-04-02 22:13:38 +0000161 e.classList.add('menu');
Nils Diewald58141332015-04-07 16:18:45 +0000162 e.classList.add('roll');
Nils Diewald5975d702015-03-09 17:45:42 +0000163 e.appendChild(this._prefix.element());
Nils Diewald86dad5b2015-01-28 15:09:07 +0000164
Nils Diewald6e43ffd2015-03-25 18:55:39 +0000165 // This has to be cleaned up later on
166 e["menu"] = this;
167
Nils Diewald59c02fc2015-03-07 01:29:09 +0000168 // Arrow keys
169 e.addEventListener(
Nils Diewald47f366b2015-04-15 20:06:35 +0000170 'keydown',
171 function (ev) {
172 that._keydown(ev)
173 },
174 false
175 );
176
177 // Strings
178 e.addEventListener(
Nils Diewald5975d702015-03-09 17:45:42 +0000179 'keypress',
Nils Diewald59c02fc2015-03-07 01:29:09 +0000180 function (ev) {
Nils Diewald5975d702015-03-09 17:45:42 +0000181 that._keypress(ev)
Nils Diewald59c02fc2015-03-07 01:29:09 +0000182 },
183 false
184 );
185
186 // Mousewheel
187 e.addEventListener(
Nils Diewald5975d702015-03-09 17:45:42 +0000188 'wheel',
Nils Diewald59c02fc2015-03-07 01:29:09 +0000189 function (ev) {
190 that._mousewheel(ev)
Nils Diewald2fe12e12015-03-06 16:47:06 +0000191 },
192 false
193 );
194
Nils Diewald5975d702015-03-09 17:45:42 +0000195 this._element = e;
Nils Diewald86dad5b2015-01-28 15:09:07 +0000196 this.active = false;
197 this._items = new Array();
198 var i;
Nils Diewald2fe12e12015-03-06 16:47:06 +0000199
200 // Initialize item list based on parameters
Nils Diewald86dad5b2015-01-28 15:09:07 +0000201 for (i in params) {
Nils Diewald0e6992a2015-04-14 20:13:52 +0000202 var obj = this._itemClass.create(params[i]);
Nils Diewald6e43ffd2015-03-25 18:55:39 +0000203
204 // This may become circular
205 obj["_menu"] = this;
206
Nils Diewald2fe12e12015-03-06 16:47:06 +0000207 this._items.push(obj);
Nils Diewald86dad5b2015-01-28 15:09:07 +0000208 };
Nils Diewald0e6992a2015-04-14 20:13:52 +0000209 this._limit = menuLimit;
Nils Diewald86dad5b2015-01-28 15:09:07 +0000210 this._position = 0; // position in the active list
211 this._active = -1; // active item in the item list
Nils Diewald86dad5b2015-01-28 15:09:07 +0000212 this._reset();
213 return this;
214 },
215
Nils Diewald2fe12e12015-03-06 16:47:06 +0000216 /**
217 * Get the instantiated HTML element
218 */
Nils Diewald86dad5b2015-01-28 15:09:07 +0000219 element : function () {
220 return this._element;
221 },
222
Nils Diewald2fe12e12015-03-06 16:47:06 +0000223 /**
224 * Get the creator object for items
225 */
Nils Diewald86dad5b2015-01-28 15:09:07 +0000226 itemClass : function () {
227 return this._itemClass;
228 },
229
230 /**
Nils Diewald2fe12e12015-03-06 16:47:06 +0000231 * Get and set numerical value for limit,
232 * i.e. the number of items visible.
Nils Diewald86dad5b2015-01-28 15:09:07 +0000233 */
234 limit : function (limit) {
Nils Diewald5975d702015-03-09 17:45:42 +0000235 if (arguments.length === 1) {
Nils Diewald86dad5b2015-01-28 15:09:07 +0000236 this._limit = limit;
Nils Diewald5975d702015-03-09 17:45:42 +0000237 return this;
238 };
Nils Diewald86dad5b2015-01-28 15:09:07 +0000239 return this._limit;
240 },
241
242 /**
243 * Upgrade this object to another object,
244 * while private data stays intact.
245 *
Nils Diewald2fe12e12015-03-06 16:47:06 +0000246 * @param {Object} An object with properties.
Nils Diewald86dad5b2015-01-28 15:09:07 +0000247 */
248 upgradeTo : function (props) {
249 for (var prop in props) {
250 this[prop] = props[prop];
251 };
252 return this;
253 },
254
Nils Diewald2fe12e12015-03-06 16:47:06 +0000255 // Reset chosen item and prefix
Nils Diewald86dad5b2015-01-28 15:09:07 +0000256 _reset : function () {
257 this._offset = 0;
258 this._pos = 0;
Nils Diewald5975d702015-03-09 17:45:42 +0000259 this._prefix.value('');
Nils Diewald86dad5b2015-01-28 15:09:07 +0000260 },
261
262 /**
263 * Filter the list and make it visible
264 *
265 * @param {string} Prefix for filtering the list
266 */
Nils Diewald5975d702015-03-09 17:45:42 +0000267 show : function () {
Nils Diewalde8518f82015-03-18 22:41:49 +0000268
Nils Diewald86dad5b2015-01-28 15:09:07 +0000269 // Initialize the list
270 if (!this._initList())
Nils Diewald59c02fc2015-03-07 01:29:09 +0000271 return false;
Nils Diewald86dad5b2015-01-28 15:09:07 +0000272
Nils Diewald2fe12e12015-03-06 16:47:06 +0000273 // show based on initial offset
Nils Diewald86dad5b2015-01-28 15:09:07 +0000274 this._showItems(0);
275
276 // Set the first element to active
Nils Diewald2fe12e12015-03-06 16:47:06 +0000277 // Todo: Or the last element chosen
Nils Diewald86dad5b2015-01-28 15:09:07 +0000278 this.liveItem(0).active(true);
Nils Diewalde8518f82015-03-18 22:41:49 +0000279 this._prefix.active(false);
Nils Diewald86dad5b2015-01-28 15:09:07 +0000280
Nils Diewald86dad5b2015-01-28 15:09:07 +0000281 this._active = this._list[0];
Nils Diewald5975d702015-03-09 17:45:42 +0000282 this._position = 0;
Nils Diewald2fe12e12015-03-06 16:47:06 +0000283 this._element.style.opacity = 1;
284
Nils Diewald86dad5b2015-01-28 15:09:07 +0000285 // Add classes for rolling menus
286 this._boundary(true);
Nils Diewald59c02fc2015-03-07 01:29:09 +0000287 return true;
Nils Diewald86dad5b2015-01-28 15:09:07 +0000288 },
289
Nils Diewald2fe12e12015-03-06 16:47:06 +0000290 hide : function () {
Nils Diewald59c02fc2015-03-07 01:29:09 +0000291 this.active = false;
292 this.delete();
Nils Diewald2fe12e12015-03-06 16:47:06 +0000293 this._element.style.opacity = 0;
Nils Diewald5c5a7472015-04-02 22:13:38 +0000294 this.onHide();
Nils Diewald6e43ffd2015-03-25 18:55:39 +0000295 /* this._element.blur(); */
Nils Diewald86dad5b2015-01-28 15:09:07 +0000296 },
297
Nils Diewald5c5a7472015-04-02 22:13:38 +0000298 // To be override
299 onHide : function () {},
300
Nils Diewald2fe12e12015-03-06 16:47:06 +0000301 // Initialize the list
Nils Diewald86dad5b2015-01-28 15:09:07 +0000302 _initList : function () {
303
Nils Diewald2fe12e12015-03-06 16:47:06 +0000304 // Create a new list
Nils Diewald86dad5b2015-01-28 15:09:07 +0000305 if (this._list === undefined) {
306 this._list = [];
307 }
308 else if (this._list.length != 0) {
309 this._boundary(false);
310 this._list.length = 0;
311 };
312
313 // Offset is initially zero
314 this._offset = 0;
315
Nils Diewald2fe12e12015-03-06 16:47:06 +0000316 // There is no prefix set
Nils Diewald86dad5b2015-01-28 15:09:07 +0000317 if (this.prefix().length <= 0) {
Nils Diewald5975d702015-03-09 17:45:42 +0000318 var i = 0;
319 for (; i < this._items.length; i++)
Nils Diewald86dad5b2015-01-28 15:09:07 +0000320 this._list.push(i);
Nils Diewald5975d702015-03-09 17:45:42 +0000321 while (this._items[++i] !== undefined) {
322 this._items[i].lowlight();
Nils Diewald2488d052015-04-09 21:46:02 +0000323 // console.log(this._item);
Nils Diewald5975d702015-03-09 17:45:42 +0000324 };
Nils Diewald86dad5b2015-01-28 15:09:07 +0000325 return true;
326 };
327
Nils Diewald2fe12e12015-03-06 16:47:06 +0000328 // There is a prefix set, so filter the list
Nils Diewald86dad5b2015-01-28 15:09:07 +0000329 var pos;
330 var paddedPrefix = " " + this.prefix();
331
Nils Diewald2fe12e12015-03-06 16:47:06 +0000332 // Iterate over all items and choose preferred matching items
333 // i.e. the matching happens at the word start
Nils Diewald86dad5b2015-01-28 15:09:07 +0000334 for (pos = 0; pos < this._items.length; pos++) {
335 if ((this.item(pos).lcField().indexOf(paddedPrefix)) >= 0)
336 this._list.push(pos);
337 };
338
Nils Diewald2fe12e12015-03-06 16:47:06 +0000339 // The list is empty - so lower your expectations
340 // Iterate over all items and choose matching items
341 // i.e. the matching happens anywhere in the word
Nils Diewald86dad5b2015-01-28 15:09:07 +0000342 if (this._list.length == 0) {
343 for (pos = 0; pos < this._items.length; pos++) {
344 if ((this.item(pos).lcField().indexOf(this.prefix())) >= 0)
345 this._list.push(pos);
346 };
347 };
348
Nils Diewald2fe12e12015-03-06 16:47:06 +0000349 // Filter was successful - yeah!
Nils Diewald86dad5b2015-01-28 15:09:07 +0000350 return this._list.length > 0 ? true : false;
351 },
352
353 // Set boundary for viewport
354 _boundary : function (bool) {
355 this.item(this._list[0]).noMore(bool);
356 this.item(this._list[this._list.length - 1]).noMore(bool);
357 },
358
359 /**
360 * Get the prefix for filtering,
Nils Diewald2fe12e12015-03-06 16:47:06 +0000361 * e.g. &quot;ve&quot; for &quot;verb&quot;
Nils Diewald86dad5b2015-01-28 15:09:07 +0000362 */
Nils Diewald5975d702015-03-09 17:45:42 +0000363 prefix : function (pref) {
364 if (arguments.length === 1) {
365 this._prefix.value(pref);
366 return this;
367 };
368 return this._prefix.value();
Nils Diewald86dad5b2015-01-28 15:09:07 +0000369 },
370
Nils Diewald2fe12e12015-03-06 16:47:06 +0000371 // Append Items that should be shown
Nils Diewald86dad5b2015-01-28 15:09:07 +0000372 _showItems : function (offset) {
373 this.delete();
374
375 // Use list
376 var shown = 0;
377 var i;
378 for (i in this._list) {
379
380 // Don't show - it's before offset
381 if (shown++ < offset)
382 continue;
383
384 this._append(this._list[i]);
385
386 if (shown >= (this.limit() + this._offset))
387 break;
388 };
389 },
390
391 /**
392 * Delete all visible items from the menu element
393 */
394 delete : function () {
395 var child;
Nils Diewald2fe12e12015-03-06 16:47:06 +0000396
Nils Diewald5975d702015-03-09 17:45:42 +0000397 /*
Nils Diewald2fe12e12015-03-06 16:47:06 +0000398 // Iterate over all visible items
Nils Diewald86dad5b2015-01-28 15:09:07 +0000399 for (var i = 0; i <= this.limit(); i++) {
400
Nils Diewald5975d702015-03-09 17:45:42 +0000401 // there is a visible element
402 // unhighlight!
Nils Diewald59c02fc2015-03-07 01:29:09 +0000403 if (child = this.shownItem(i)) {
Nils Diewald86dad5b2015-01-28 15:09:07 +0000404 child.lowlight();
Nils Diewald59c02fc2015-03-07 01:29:09 +0000405 child.active(false);
406 };
Nils Diewald86dad5b2015-01-28 15:09:07 +0000407 };
Nils Diewald5975d702015-03-09 17:45:42 +0000408 */
409
410 for (var i in this._list) {
411 var item = this._items[this._list[i]];
412 item.lowlight();
413 item.active(false);
414 };
Nils Diewald86dad5b2015-01-28 15:09:07 +0000415
Nils Diewald2fe12e12015-03-06 16:47:06 +0000416 // Remove all children
Nils Diewald5975d702015-03-09 17:45:42 +0000417 var children = this._element.childNodes;
418 for (var i = children.length - 1; i >= 1; i--) {
419 this._element.removeChild(
420 children[i]
421 );
422 };
Nils Diewald86dad5b2015-01-28 15:09:07 +0000423 },
424
425
426 // Append item to the shown list based on index
427 _append : function (i) {
428 var item = this.item(i);
429
430 // Highlight based on prefix
431 if (this.prefix().length > 0)
432 item.highlight(this.prefix());
433
434 // Append element
435 this.element().appendChild(item.element());
436 },
437
438
Nils Diewald2fe12e12015-03-06 16:47:06 +0000439 // Prepend item to the shown list based on index
440 _prepend : function (i) {
441 var item = this.item(i);
442
443 // Highlight based on prefix
444 if (this.prefix().length > 0)
445 item.highlight(this.prefix());
446
447 var e = this.element();
448 // Append element
449 e.insertBefore(
450 item.element(),
Nils Diewald5975d702015-03-09 17:45:42 +0000451 e.children[1]
Nils Diewald2fe12e12015-03-06 16:47:06 +0000452 );
453 },
454
455
456 /**
457 * Get a specific item from the complete list
458 *
459 * @param {number} index of the list item
460 */
461 item : function (index) {
462 return this._items[index]
463 },
464
465
Nils Diewald86dad5b2015-01-28 15:09:07 +0000466 /**
467 * Get a specific item from the filtered list
468 *
469 * @param {number} index of the list item
Nils Diewald2fe12e12015-03-06 16:47:06 +0000470 * in the filtered list
Nils Diewald86dad5b2015-01-28 15:09:07 +0000471 */
472 liveItem : function (index) {
473 if (this._list === undefined)
474 if (!this._initList())
475 return;
476
477 return this._items[this._list[index]];
478 },
479
Nils Diewald86dad5b2015-01-28 15:09:07 +0000480
481 /**
482 * Get a specific item from the visible list
483 *
484 * @param {number} index of the list item
Nils Diewald2fe12e12015-03-06 16:47:06 +0000485 * in the visible list
Nils Diewald86dad5b2015-01-28 15:09:07 +0000486 */
487 shownItem : function (index) {
488 if (index >= this.limit())
489 return;
490 return this.liveItem(this._offset + index);
491 },
492
493
Nils Diewald2fe12e12015-03-06 16:47:06 +0000494 /**
495 * Get the length of the full list
496 */
497 length : function () {
498 return this._items.length;
499 },
500
501
502 /**
503 * Make the next item in the filtered menu active
Nils Diewald86dad5b2015-01-28 15:09:07 +0000504 */
505 next : function () {
Nils Diewald5975d702015-03-09 17:45:42 +0000506
Nils Diewald86dad5b2015-01-28 15:09:07 +0000507 // No active element set
Nils Diewald5975d702015-03-09 17:45:42 +0000508 if (this._position === -1)
Nils Diewald86dad5b2015-01-28 15:09:07 +0000509 return;
510
Nils Diewald5975d702015-03-09 17:45:42 +0000511 var newItem;
512
Nils Diewald86dad5b2015-01-28 15:09:07 +0000513 // Set new live item
Nils Diewalde8518f82015-03-18 22:41:49 +0000514 if (!this._prefix.active()) {
515 var oldItem = this.liveItem(this._position);
516 oldItem.active(false);
517 };
518
519 this._position++;
520
Nils Diewald5975d702015-03-09 17:45:42 +0000521 newItem = this.liveItem(this._position);
Nils Diewald86dad5b2015-01-28 15:09:07 +0000522
Nils Diewald5975d702015-03-09 17:45:42 +0000523 // The next element is undefined - roll to top or to prefix
Nils Diewald86dad5b2015-01-28 15:09:07 +0000524 if (newItem === undefined) {
Nils Diewald5975d702015-03-09 17:45:42 +0000525
526 // Activate prefix
527 var prefix = this._prefix;
528
529 // Mark prefix
530 if (prefix.isSet() && !prefix.active()) {
531 this._position--;
532 prefix.active(true);
533 return;
534 }
535 else {
536 this._offset = 0;
537 this._position = 0;
538 newItem = this.liveItem(0);
539 this._showItems(0);
540 };
Nils Diewald86dad5b2015-01-28 15:09:07 +0000541 }
542
543 // The next element is outside the view - roll down
Nils Diewald2fe12e12015-03-06 16:47:06 +0000544 else if (this._position >= (this.limit() + this._offset)) {
Nils Diewald86dad5b2015-01-28 15:09:07 +0000545 this._removeFirst();
546 this._offset++;
547 this._append(this._list[this._position]);
548 };
Nils Diewald5975d702015-03-09 17:45:42 +0000549
550 this._prefix.active(false);
Nils Diewald86dad5b2015-01-28 15:09:07 +0000551 newItem.active(true);
552 },
553
Nils Diewalde8518f82015-03-18 22:41:49 +0000554 /*
555 * Page down to the first item on the next page
556 */
557 /*
558 nextPage : function () {
559
560 // Prefix is active
561 if (this._prefix.active()) {
562 this._prefix.active(false);
563 }
564
565 // Last item is chosen
566 else if (this._position >= this.limit() + this._offset) {
567
568 this._position = this.limit() + this._offset - 1;
569 newItem = this.liveItem(this._position);
570 var oldItem = this.liveItem(this._position--);
571 oldItem.active(false);
572 }
573
574 // Last item of page is chosen
575 else if (0) {
576
577 // Jump to last item
578 else {
579 var oldItem = this.liveItem(this._position);
580 oldItem.active(false);
581
582 this._position = this.limit() + this._offset - 1;
583 newItem = this.liveItem(this._position);
584 };
585
586 newItem.active(true);
587 },
588 */
589
Nils Diewald86dad5b2015-01-28 15:09:07 +0000590
591 /*
592 * Make the previous item in the menu active
593 */
Nils Diewald86dad5b2015-01-28 15:09:07 +0000594 prev : function () {
Nils Diewald2d210752015-03-09 19:01:15 +0000595
Nils Diewald2fe12e12015-03-06 16:47:06 +0000596 // No active element set
Nils Diewalde8518f82015-03-18 22:41:49 +0000597 if (this._position === -1) {
Nils Diewald86dad5b2015-01-28 15:09:07 +0000598 return;
Nils Diewalde8518f82015-03-18 22:41:49 +0000599 // TODO: Choose last item
600 };
Nils Diewald86dad5b2015-01-28 15:09:07 +0000601
Nils Diewald5975d702015-03-09 17:45:42 +0000602 var newItem;
603
Nils Diewald86dad5b2015-01-28 15:09:07 +0000604 // Set new live item
Nils Diewald2d210752015-03-09 19:01:15 +0000605 if (!this._prefix.active()) {
606 var oldItem = this.liveItem(this._position--);
607 oldItem.active(false);
608 };
609
Nils Diewald5975d702015-03-09 17:45:42 +0000610 newItem = this.liveItem(this._position);
Nils Diewald86dad5b2015-01-28 15:09:07 +0000611
612 // The previous element is undefined - roll to bottom
613 if (newItem === undefined) {
Nils Diewald5975d702015-03-09 17:45:42 +0000614
615 // Activate prefix
616 var prefix = this._prefix;
Nils Diewald2fe12e12015-03-06 16:47:06 +0000617 this._offset = this.liveLength() - this.limit();
Nils Diewald2d210752015-03-09 19:01:15 +0000618
619 // Normalize offset
620 this._offset = this._offset < 0 ? 0 : this._offset;
621
Nils Diewald2fe12e12015-03-06 16:47:06 +0000622 this._position = this.liveLength() - 1;
Nils Diewald5975d702015-03-09 17:45:42 +0000623
624 if (prefix.isSet() && !prefix.active()) {
625 this._position++;
626 prefix.active(true);
627 return;
628 }
629 else {
630 newItem = this.liveItem(this._position);
631 this._showItems(this._offset);
632 };
Nils Diewald86dad5b2015-01-28 15:09:07 +0000633 }
634
635 // The previous element is outside the view - roll up
636 else if (this._position < this._offset) {
637 this._removeLast();
638 this._offset--;
639 this._prepend(this._list[this._position]);
640 };
Nils Diewald2fe12e12015-03-06 16:47:06 +0000641
Nils Diewald5975d702015-03-09 17:45:42 +0000642 this._prefix.active(false);
Nils Diewald86dad5b2015-01-28 15:09:07 +0000643 newItem.active(true);
644 },
Nils Diewald86dad5b2015-01-28 15:09:07 +0000645
646
Nils Diewald5975d702015-03-09 17:45:42 +0000647 // Length of the filtered list
648 liveLength : function () {
649 if (this._list === undefined)
650 this._initList();
651 return this._list.length;
652 },
653
654
Nils Diewald2fe12e12015-03-06 16:47:06 +0000655 // Remove the HTML node from the first item
Nils Diewald86dad5b2015-01-28 15:09:07 +0000656 _removeFirst : function () {
657 this.item(this._list[this._offset]).lowlight();
Nils Diewald5975d702015-03-09 17:45:42 +0000658 this._element.removeChild(this._element.children[1]);
Nils Diewald86dad5b2015-01-28 15:09:07 +0000659 },
660
Nils Diewald2fe12e12015-03-06 16:47:06 +0000661
662 // Remove the HTML node from the last item
Nils Diewald86dad5b2015-01-28 15:09:07 +0000663 _removeLast : function () {
Nils Diewald2fe12e12015-03-06 16:47:06 +0000664 this.item(this._list[this._offset + this.limit() - 1]).lowlight();
Nils Diewald86dad5b2015-01-28 15:09:07 +0000665 this._element.removeChild(this._element.lastChild);
Nils Diewald2fe12e12015-03-06 16:47:06 +0000666 }
Nils Diewald86dad5b2015-01-28 15:09:07 +0000667 };
Nils Diewald0e6992a2015-04-14 20:13:52 +0000668});