blob: 0d5e6acf43d826cbae1b7a1eb30b8ed8e81768d0 [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 Diewald5975d702015-03-09 17:45:42 +000078 _keypress : 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;
129 default:
Nils Diewald5975d702015-03-09 17:45:42 +0000130 if (e.key !== undefined &&
131 e.key.length != 1)
Nils Diewald59c02fc2015-03-07 01:29:09 +0000132 return;
133
134 // Add prefix
Nils Diewald5975d702015-03-09 17:45:42 +0000135 this._prefix.add(e.key.toLowerCase());
136
137 if (!this.show()) {
138 this.prefix('').show();
139 };
Nils Diewald59c02fc2015-03-07 01:29:09 +0000140 };
141 },
142
Nils Diewald2fe12e12015-03-06 16:47:06 +0000143 // Initialize list
Nils Diewald5975d702015-03-09 17:45:42 +0000144 _init : function (itemClass, prefixClass, params) {
Nils Diewald59c02fc2015-03-07 01:29:09 +0000145 var that = this;
Nils Diewald0e6992a2015-04-14 20:13:52 +0000146 this._itemClass = itemClass || defaultItemClass;
Nils Diewald5975d702015-03-09 17:45:42 +0000147
148 if (prefixClass !== undefined)
149 this._prefix = prefixClass.create();
150 else
Nils Diewald0e6992a2015-04-14 20:13:52 +0000151 this._prefix = defaultPrefixClass.create();
Nils Diewald5975d702015-03-09 17:45:42 +0000152
Nils Diewald5c5a7472015-04-02 22:13:38 +0000153 this._prefix._menu = this;
154
Nils Diewald5975d702015-03-09 17:45:42 +0000155 var e = document.createElement("ul");
Nils Diewald59c02fc2015-03-07 01:29:09 +0000156 e.style.opacity = 0;
157 e.style.outline = 0;
158 e.setAttribute('tabindex', 0);
Nils Diewald5c5a7472015-04-02 22:13:38 +0000159 e.classList.add('menu');
Nils Diewald58141332015-04-07 16:18:45 +0000160 e.classList.add('roll');
Nils Diewald5975d702015-03-09 17:45:42 +0000161 e.appendChild(this._prefix.element());
Nils Diewald86dad5b2015-01-28 15:09:07 +0000162
Nils Diewald6e43ffd2015-03-25 18:55:39 +0000163 // This has to be cleaned up later on
164 e["menu"] = this;
165
Nils Diewald59c02fc2015-03-07 01:29:09 +0000166 // Arrow keys
167 e.addEventListener(
Nils Diewald5975d702015-03-09 17:45:42 +0000168 'keypress',
Nils Diewald59c02fc2015-03-07 01:29:09 +0000169 function (ev) {
Nils Diewald5975d702015-03-09 17:45:42 +0000170 that._keypress(ev)
Nils Diewald59c02fc2015-03-07 01:29:09 +0000171 },
172 false
173 );
174
175 // Mousewheel
176 e.addEventListener(
Nils Diewald5975d702015-03-09 17:45:42 +0000177 'wheel',
Nils Diewald59c02fc2015-03-07 01:29:09 +0000178 function (ev) {
179 that._mousewheel(ev)
Nils Diewald2fe12e12015-03-06 16:47:06 +0000180 },
181 false
182 );
183
Nils Diewald5975d702015-03-09 17:45:42 +0000184 this._element = e;
Nils Diewald86dad5b2015-01-28 15:09:07 +0000185 this.active = false;
186 this._items = new Array();
187 var i;
Nils Diewald2fe12e12015-03-06 16:47:06 +0000188
189 // Initialize item list based on parameters
Nils Diewald86dad5b2015-01-28 15:09:07 +0000190 for (i in params) {
Nils Diewald0e6992a2015-04-14 20:13:52 +0000191 var obj = this._itemClass.create(params[i]);
Nils Diewald6e43ffd2015-03-25 18:55:39 +0000192
193 // This may become circular
194 obj["_menu"] = this;
195
Nils Diewald2fe12e12015-03-06 16:47:06 +0000196 this._items.push(obj);
Nils Diewald86dad5b2015-01-28 15:09:07 +0000197 };
Nils Diewald0e6992a2015-04-14 20:13:52 +0000198 this._limit = menuLimit;
Nils Diewald86dad5b2015-01-28 15:09:07 +0000199 this._position = 0; // position in the active list
200 this._active = -1; // active item in the item list
Nils Diewald86dad5b2015-01-28 15:09:07 +0000201 this._reset();
202 return this;
203 },
204
Nils Diewald2fe12e12015-03-06 16:47:06 +0000205 /**
206 * Get the instantiated HTML element
207 */
Nils Diewald86dad5b2015-01-28 15:09:07 +0000208 element : function () {
209 return this._element;
210 },
211
Nils Diewald2fe12e12015-03-06 16:47:06 +0000212 /**
213 * Get the creator object for items
214 */
Nils Diewald86dad5b2015-01-28 15:09:07 +0000215 itemClass : function () {
216 return this._itemClass;
217 },
218
219 /**
Nils Diewald2fe12e12015-03-06 16:47:06 +0000220 * Get and set numerical value for limit,
221 * i.e. the number of items visible.
Nils Diewald86dad5b2015-01-28 15:09:07 +0000222 */
223 limit : function (limit) {
Nils Diewald5975d702015-03-09 17:45:42 +0000224 if (arguments.length === 1) {
Nils Diewald86dad5b2015-01-28 15:09:07 +0000225 this._limit = limit;
Nils Diewald5975d702015-03-09 17:45:42 +0000226 return this;
227 };
Nils Diewald86dad5b2015-01-28 15:09:07 +0000228 return this._limit;
229 },
230
231 /**
232 * Upgrade this object to another object,
233 * while private data stays intact.
234 *
Nils Diewald2fe12e12015-03-06 16:47:06 +0000235 * @param {Object} An object with properties.
Nils Diewald86dad5b2015-01-28 15:09:07 +0000236 */
237 upgradeTo : function (props) {
238 for (var prop in props) {
239 this[prop] = props[prop];
240 };
241 return this;
242 },
243
Nils Diewald2fe12e12015-03-06 16:47:06 +0000244 // Reset chosen item and prefix
Nils Diewald86dad5b2015-01-28 15:09:07 +0000245 _reset : function () {
246 this._offset = 0;
247 this._pos = 0;
Nils Diewald5975d702015-03-09 17:45:42 +0000248 this._prefix.value('');
Nils Diewald86dad5b2015-01-28 15:09:07 +0000249 },
250
251 /**
252 * Filter the list and make it visible
253 *
254 * @param {string} Prefix for filtering the list
255 */
Nils Diewald5975d702015-03-09 17:45:42 +0000256 show : function () {
Nils Diewalde8518f82015-03-18 22:41:49 +0000257
Nils Diewald86dad5b2015-01-28 15:09:07 +0000258 // Initialize the list
259 if (!this._initList())
Nils Diewald59c02fc2015-03-07 01:29:09 +0000260 return false;
Nils Diewald86dad5b2015-01-28 15:09:07 +0000261
Nils Diewald2fe12e12015-03-06 16:47:06 +0000262 // show based on initial offset
Nils Diewald86dad5b2015-01-28 15:09:07 +0000263 this._showItems(0);
264
265 // Set the first element to active
Nils Diewald2fe12e12015-03-06 16:47:06 +0000266 // Todo: Or the last element chosen
Nils Diewald86dad5b2015-01-28 15:09:07 +0000267 this.liveItem(0).active(true);
Nils Diewalde8518f82015-03-18 22:41:49 +0000268 this._prefix.active(false);
Nils Diewald86dad5b2015-01-28 15:09:07 +0000269
Nils Diewald86dad5b2015-01-28 15:09:07 +0000270 this._active = this._list[0];
Nils Diewald5975d702015-03-09 17:45:42 +0000271 this._position = 0;
Nils Diewald2fe12e12015-03-06 16:47:06 +0000272 this._element.style.opacity = 1;
273
Nils Diewald86dad5b2015-01-28 15:09:07 +0000274 // Add classes for rolling menus
275 this._boundary(true);
Nils Diewald59c02fc2015-03-07 01:29:09 +0000276 return true;
Nils Diewald86dad5b2015-01-28 15:09:07 +0000277 },
278
Nils Diewald2fe12e12015-03-06 16:47:06 +0000279 hide : function () {
Nils Diewald59c02fc2015-03-07 01:29:09 +0000280 this.active = false;
281 this.delete();
Nils Diewald2fe12e12015-03-06 16:47:06 +0000282 this._element.style.opacity = 0;
Nils Diewald5c5a7472015-04-02 22:13:38 +0000283 this.onHide();
Nils Diewald6e43ffd2015-03-25 18:55:39 +0000284 /* this._element.blur(); */
Nils Diewald86dad5b2015-01-28 15:09:07 +0000285 },
286
Nils Diewald5c5a7472015-04-02 22:13:38 +0000287 // To be override
288 onHide : function () {},
289
Nils Diewald2fe12e12015-03-06 16:47:06 +0000290 // Initialize the list
Nils Diewald86dad5b2015-01-28 15:09:07 +0000291 _initList : function () {
292
Nils Diewald2fe12e12015-03-06 16:47:06 +0000293 // Create a new list
Nils Diewald86dad5b2015-01-28 15:09:07 +0000294 if (this._list === undefined) {
295 this._list = [];
296 }
297 else if (this._list.length != 0) {
298 this._boundary(false);
299 this._list.length = 0;
300 };
301
302 // Offset is initially zero
303 this._offset = 0;
304
Nils Diewald2fe12e12015-03-06 16:47:06 +0000305 // There is no prefix set
Nils Diewald86dad5b2015-01-28 15:09:07 +0000306 if (this.prefix().length <= 0) {
Nils Diewald5975d702015-03-09 17:45:42 +0000307 var i = 0;
308 for (; i < this._items.length; i++)
Nils Diewald86dad5b2015-01-28 15:09:07 +0000309 this._list.push(i);
Nils Diewald5975d702015-03-09 17:45:42 +0000310 while (this._items[++i] !== undefined) {
311 this._items[i].lowlight();
Nils Diewald2488d052015-04-09 21:46:02 +0000312 // console.log(this._item);
Nils Diewald5975d702015-03-09 17:45:42 +0000313 };
Nils Diewald86dad5b2015-01-28 15:09:07 +0000314 return true;
315 };
316
Nils Diewald2fe12e12015-03-06 16:47:06 +0000317 // There is a prefix set, so filter the list
Nils Diewald86dad5b2015-01-28 15:09:07 +0000318 var pos;
319 var paddedPrefix = " " + this.prefix();
320
Nils Diewald2fe12e12015-03-06 16:47:06 +0000321 // Iterate over all items and choose preferred matching items
322 // i.e. the matching happens at the word start
Nils Diewald86dad5b2015-01-28 15:09:07 +0000323 for (pos = 0; pos < this._items.length; pos++) {
324 if ((this.item(pos).lcField().indexOf(paddedPrefix)) >= 0)
325 this._list.push(pos);
326 };
327
Nils Diewald2fe12e12015-03-06 16:47:06 +0000328 // The list is empty - so lower your expectations
329 // Iterate over all items and choose matching items
330 // i.e. the matching happens anywhere in the word
Nils Diewald86dad5b2015-01-28 15:09:07 +0000331 if (this._list.length == 0) {
332 for (pos = 0; pos < this._items.length; pos++) {
333 if ((this.item(pos).lcField().indexOf(this.prefix())) >= 0)
334 this._list.push(pos);
335 };
336 };
337
Nils Diewald2fe12e12015-03-06 16:47:06 +0000338 // Filter was successful - yeah!
Nils Diewald86dad5b2015-01-28 15:09:07 +0000339 return this._list.length > 0 ? true : false;
340 },
341
342 // Set boundary for viewport
343 _boundary : function (bool) {
344 this.item(this._list[0]).noMore(bool);
345 this.item(this._list[this._list.length - 1]).noMore(bool);
346 },
347
348 /**
349 * Get the prefix for filtering,
Nils Diewald2fe12e12015-03-06 16:47:06 +0000350 * e.g. &quot;ve&quot; for &quot;verb&quot;
Nils Diewald86dad5b2015-01-28 15:09:07 +0000351 */
Nils Diewald5975d702015-03-09 17:45:42 +0000352 prefix : function (pref) {
353 if (arguments.length === 1) {
354 this._prefix.value(pref);
355 return this;
356 };
357 return this._prefix.value();
Nils Diewald86dad5b2015-01-28 15:09:07 +0000358 },
359
Nils Diewald2fe12e12015-03-06 16:47:06 +0000360 // Append Items that should be shown
Nils Diewald86dad5b2015-01-28 15:09:07 +0000361 _showItems : function (offset) {
362 this.delete();
363
364 // Use list
365 var shown = 0;
366 var i;
367 for (i in this._list) {
368
369 // Don't show - it's before offset
370 if (shown++ < offset)
371 continue;
372
373 this._append(this._list[i]);
374
375 if (shown >= (this.limit() + this._offset))
376 break;
377 };
378 },
379
380 /**
381 * Delete all visible items from the menu element
382 */
383 delete : function () {
384 var child;
Nils Diewald2fe12e12015-03-06 16:47:06 +0000385
Nils Diewald5975d702015-03-09 17:45:42 +0000386 /*
Nils Diewald2fe12e12015-03-06 16:47:06 +0000387 // Iterate over all visible items
Nils Diewald86dad5b2015-01-28 15:09:07 +0000388 for (var i = 0; i <= this.limit(); i++) {
389
Nils Diewald5975d702015-03-09 17:45:42 +0000390 // there is a visible element
391 // unhighlight!
Nils Diewald59c02fc2015-03-07 01:29:09 +0000392 if (child = this.shownItem(i)) {
Nils Diewald86dad5b2015-01-28 15:09:07 +0000393 child.lowlight();
Nils Diewald59c02fc2015-03-07 01:29:09 +0000394 child.active(false);
395 };
Nils Diewald86dad5b2015-01-28 15:09:07 +0000396 };
Nils Diewald5975d702015-03-09 17:45:42 +0000397 */
398
399 for (var i in this._list) {
400 var item = this._items[this._list[i]];
401 item.lowlight();
402 item.active(false);
403 };
Nils Diewald86dad5b2015-01-28 15:09:07 +0000404
Nils Diewald2fe12e12015-03-06 16:47:06 +0000405 // Remove all children
Nils Diewald5975d702015-03-09 17:45:42 +0000406 var children = this._element.childNodes;
407 for (var i = children.length - 1; i >= 1; i--) {
408 this._element.removeChild(
409 children[i]
410 );
411 };
Nils Diewald86dad5b2015-01-28 15:09:07 +0000412 },
413
414
415 // Append item to the shown list based on index
416 _append : function (i) {
417 var item = this.item(i);
418
419 // Highlight based on prefix
420 if (this.prefix().length > 0)
421 item.highlight(this.prefix());
422
423 // Append element
424 this.element().appendChild(item.element());
425 },
426
427
Nils Diewald2fe12e12015-03-06 16:47:06 +0000428 // Prepend item to the shown list based on index
429 _prepend : function (i) {
430 var item = this.item(i);
431
432 // Highlight based on prefix
433 if (this.prefix().length > 0)
434 item.highlight(this.prefix());
435
436 var e = this.element();
437 // Append element
438 e.insertBefore(
439 item.element(),
Nils Diewald5975d702015-03-09 17:45:42 +0000440 e.children[1]
Nils Diewald2fe12e12015-03-06 16:47:06 +0000441 );
442 },
443
444
445 /**
446 * Get a specific item from the complete list
447 *
448 * @param {number} index of the list item
449 */
450 item : function (index) {
451 return this._items[index]
452 },
453
454
Nils Diewald86dad5b2015-01-28 15:09:07 +0000455 /**
456 * Get a specific item from the filtered list
457 *
458 * @param {number} index of the list item
Nils Diewald2fe12e12015-03-06 16:47:06 +0000459 * in the filtered list
Nils Diewald86dad5b2015-01-28 15:09:07 +0000460 */
461 liveItem : function (index) {
462 if (this._list === undefined)
463 if (!this._initList())
464 return;
465
466 return this._items[this._list[index]];
467 },
468
Nils Diewald86dad5b2015-01-28 15:09:07 +0000469
470 /**
471 * Get a specific item from the visible list
472 *
473 * @param {number} index of the list item
Nils Diewald2fe12e12015-03-06 16:47:06 +0000474 * in the visible list
Nils Diewald86dad5b2015-01-28 15:09:07 +0000475 */
476 shownItem : function (index) {
477 if (index >= this.limit())
478 return;
479 return this.liveItem(this._offset + index);
480 },
481
482
Nils Diewald2fe12e12015-03-06 16:47:06 +0000483 /**
484 * Get the length of the full list
485 */
486 length : function () {
487 return this._items.length;
488 },
489
490
491 /**
492 * Make the next item in the filtered menu active
Nils Diewald86dad5b2015-01-28 15:09:07 +0000493 */
494 next : function () {
Nils Diewald5975d702015-03-09 17:45:42 +0000495
Nils Diewald86dad5b2015-01-28 15:09:07 +0000496 // No active element set
Nils Diewald5975d702015-03-09 17:45:42 +0000497 if (this._position === -1)
Nils Diewald86dad5b2015-01-28 15:09:07 +0000498 return;
499
Nils Diewald5975d702015-03-09 17:45:42 +0000500 var newItem;
501
Nils Diewald86dad5b2015-01-28 15:09:07 +0000502 // Set new live item
Nils Diewalde8518f82015-03-18 22:41:49 +0000503 if (!this._prefix.active()) {
504 var oldItem = this.liveItem(this._position);
505 oldItem.active(false);
506 };
507
508 this._position++;
509
Nils Diewald5975d702015-03-09 17:45:42 +0000510 newItem = this.liveItem(this._position);
Nils Diewald86dad5b2015-01-28 15:09:07 +0000511
Nils Diewald5975d702015-03-09 17:45:42 +0000512 // The next element is undefined - roll to top or to prefix
Nils Diewald86dad5b2015-01-28 15:09:07 +0000513 if (newItem === undefined) {
Nils Diewald5975d702015-03-09 17:45:42 +0000514
515 // Activate prefix
516 var prefix = this._prefix;
517
518 // Mark prefix
519 if (prefix.isSet() && !prefix.active()) {
520 this._position--;
521 prefix.active(true);
522 return;
523 }
524 else {
525 this._offset = 0;
526 this._position = 0;
527 newItem = this.liveItem(0);
528 this._showItems(0);
529 };
Nils Diewald86dad5b2015-01-28 15:09:07 +0000530 }
531
532 // The next element is outside the view - roll down
Nils Diewald2fe12e12015-03-06 16:47:06 +0000533 else if (this._position >= (this.limit() + this._offset)) {
Nils Diewald86dad5b2015-01-28 15:09:07 +0000534 this._removeFirst();
535 this._offset++;
536 this._append(this._list[this._position]);
537 };
Nils Diewald5975d702015-03-09 17:45:42 +0000538
539 this._prefix.active(false);
Nils Diewald86dad5b2015-01-28 15:09:07 +0000540 newItem.active(true);
541 },
542
Nils Diewalde8518f82015-03-18 22:41:49 +0000543 /*
544 * Page down to the first item on the next page
545 */
546 /*
547 nextPage : function () {
548
549 // Prefix is active
550 if (this._prefix.active()) {
551 this._prefix.active(false);
552 }
553
554 // Last item is chosen
555 else if (this._position >= this.limit() + this._offset) {
556
557 this._position = this.limit() + this._offset - 1;
558 newItem = this.liveItem(this._position);
559 var oldItem = this.liveItem(this._position--);
560 oldItem.active(false);
561 }
562
563 // Last item of page is chosen
564 else if (0) {
565
566 // Jump to last item
567 else {
568 var oldItem = this.liveItem(this._position);
569 oldItem.active(false);
570
571 this._position = this.limit() + this._offset - 1;
572 newItem = this.liveItem(this._position);
573 };
574
575 newItem.active(true);
576 },
577 */
578
Nils Diewald86dad5b2015-01-28 15:09:07 +0000579
580 /*
581 * Make the previous item in the menu active
582 */
Nils Diewald86dad5b2015-01-28 15:09:07 +0000583 prev : function () {
Nils Diewald2d210752015-03-09 19:01:15 +0000584
Nils Diewald2fe12e12015-03-06 16:47:06 +0000585 // No active element set
Nils Diewalde8518f82015-03-18 22:41:49 +0000586 if (this._position === -1) {
Nils Diewald86dad5b2015-01-28 15:09:07 +0000587 return;
Nils Diewalde8518f82015-03-18 22:41:49 +0000588 // TODO: Choose last item
589 };
Nils Diewald86dad5b2015-01-28 15:09:07 +0000590
Nils Diewald5975d702015-03-09 17:45:42 +0000591 var newItem;
592
Nils Diewald86dad5b2015-01-28 15:09:07 +0000593 // Set new live item
Nils Diewald2d210752015-03-09 19:01:15 +0000594 if (!this._prefix.active()) {
595 var oldItem = this.liveItem(this._position--);
596 oldItem.active(false);
597 };
598
Nils Diewald5975d702015-03-09 17:45:42 +0000599 newItem = this.liveItem(this._position);
Nils Diewald86dad5b2015-01-28 15:09:07 +0000600
601 // The previous element is undefined - roll to bottom
602 if (newItem === undefined) {
Nils Diewald5975d702015-03-09 17:45:42 +0000603
604 // Activate prefix
605 var prefix = this._prefix;
Nils Diewald2fe12e12015-03-06 16:47:06 +0000606 this._offset = this.liveLength() - this.limit();
Nils Diewald2d210752015-03-09 19:01:15 +0000607
608 // Normalize offset
609 this._offset = this._offset < 0 ? 0 : this._offset;
610
Nils Diewald2fe12e12015-03-06 16:47:06 +0000611 this._position = this.liveLength() - 1;
Nils Diewald5975d702015-03-09 17:45:42 +0000612
613 if (prefix.isSet() && !prefix.active()) {
614 this._position++;
615 prefix.active(true);
616 return;
617 }
618 else {
619 newItem = this.liveItem(this._position);
620 this._showItems(this._offset);
621 };
Nils Diewald86dad5b2015-01-28 15:09:07 +0000622 }
623
624 // The previous element is outside the view - roll up
625 else if (this._position < this._offset) {
626 this._removeLast();
627 this._offset--;
628 this._prepend(this._list[this._position]);
629 };
Nils Diewald2fe12e12015-03-06 16:47:06 +0000630
Nils Diewald5975d702015-03-09 17:45:42 +0000631 this._prefix.active(false);
Nils Diewald86dad5b2015-01-28 15:09:07 +0000632 newItem.active(true);
633 },
Nils Diewald86dad5b2015-01-28 15:09:07 +0000634
635
Nils Diewald5975d702015-03-09 17:45:42 +0000636 // Length of the filtered list
637 liveLength : function () {
638 if (this._list === undefined)
639 this._initList();
640 return this._list.length;
641 },
642
643
Nils Diewald2fe12e12015-03-06 16:47:06 +0000644 // Remove the HTML node from the first item
Nils Diewald86dad5b2015-01-28 15:09:07 +0000645 _removeFirst : function () {
646 this.item(this._list[this._offset]).lowlight();
Nils Diewald5975d702015-03-09 17:45:42 +0000647 this._element.removeChild(this._element.children[1]);
Nils Diewald86dad5b2015-01-28 15:09:07 +0000648 },
649
Nils Diewald2fe12e12015-03-06 16:47:06 +0000650
651 // Remove the HTML node from the last item
Nils Diewald86dad5b2015-01-28 15:09:07 +0000652 _removeLast : function () {
Nils Diewald2fe12e12015-03-06 16:47:06 +0000653 this.item(this._list[this._offset + this.limit() - 1]).lowlight();
Nils Diewald86dad5b2015-01-28 15:09:07 +0000654 this._element.removeChild(this._element.lastChild);
Nils Diewald2fe12e12015-03-06 16:47:06 +0000655 }
Nils Diewald86dad5b2015-01-28 15:09:07 +0000656 };
Nils Diewald0e6992a2015-04-14 20:13:52 +0000657});