blob: 183e97a0005caa392f7fd6fea2a452e858718911 [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 () {
57 if (this._element != undefined)
58 delete this._element["menu"];
59
60 for (var i = 0; i < this._items.length; i++) {
61 delete this._items[i]["_menu"];
62 };
Nils Diewald5c5a7472015-04-02 22:13:38 +000063 delete this._prefix['_menu'];
Nils Diewald6e43ffd2015-03-25 18:55:39 +000064 },
65
Nils Diewald7148c6f2015-05-04 15:07:53 +000066
67 /**
68 * Focus on this menu.
69 */
Nils Diewald2fe12e12015-03-06 16:47:06 +000070 focus : function () {
71 this._element.focus();
72 },
73
Nils Diewald7148c6f2015-05-04 15:07:53 +000074
Nils Diewald59c02fc2015-03-07 01:29:09 +000075 // mouse wheel treatment
76 _mousewheel : function (e) {
77 var delta = 0;
Nils Diewald5975d702015-03-09 17:45:42 +000078
79 delta = e.deltaY / 120;
80 if (delta > 0)
Nils Diewald59c02fc2015-03-07 01:29:09 +000081 this.next();
Nils Diewald5975d702015-03-09 17:45:42 +000082 else if (delta < 0)
Nils Diewald59c02fc2015-03-07 01:29:09 +000083 this.prev();
Nils Diewald59c02fc2015-03-07 01:29:09 +000084 e.halt();
85 },
86
Nils Diewald7148c6f2015-05-04 15:07:53 +000087
Nils Diewald59c02fc2015-03-07 01:29:09 +000088 // Arrow key and prefix treatment
Nils Diewald47f366b2015-04-15 20:06:35 +000089 _keydown : function (e) {
Nils Diewald59c02fc2015-03-07 01:29:09 +000090 var code = _codeFromEvent(e);
91
Nils Diewald59c02fc2015-03-07 01:29:09 +000092 switch (code) {
93 case 27: // 'Esc'
94 e.halt();
95 this.hide();
96 break;
Nils Diewald5975d702015-03-09 17:45:42 +000097
Nils Diewald59c02fc2015-03-07 01:29:09 +000098 case 38: // 'Up'
99 e.halt();
100 this.prev();
101 break;
Nils Diewald5975d702015-03-09 17:45:42 +0000102 case 33: // 'Page up'
Nils Diewald59c02fc2015-03-07 01:29:09 +0000103 e.halt();
Nils Diewald5975d702015-03-09 17:45:42 +0000104 this.prev();
105 break;
106 case 40: // 'Down'
107 e.halt();
108 this.next();
109 break;
110 case 34: // 'Page down'
111 e.halt();
112 this.next();
113 break;
114 case 39: // 'Right'
Nils Diewalde8518f82015-03-18 22:41:49 +0000115 if (this._prefix.active())
116 break;
117
Nils Diewald5975d702015-03-09 17:45:42 +0000118 var item = this.liveItem(this._position);
Akron5ef4fa02015-06-02 16:25:14 +0200119
Nils Diewald5975d702015-03-09 17:45:42 +0000120 if (item["further"] !== undefined) {
121 item["further"].bind(item).apply();
Nils Diewald5975d702015-03-09 17:45:42 +0000122 };
Akron5ef4fa02015-06-02 16:25:14 +0200123
124 e.halt();
Nils Diewald5975d702015-03-09 17:45:42 +0000125 break;
126 case 13: // 'Enter'
127
128 // Click on prefix
129 if (this._prefix.active())
Nils Diewald6e43ffd2015-03-25 18:55:39 +0000130 this._prefix.onclick(e);
Nils Diewald5975d702015-03-09 17:45:42 +0000131
132 // Click on item
133 else
Nils Diewald6e43ffd2015-03-25 18:55:39 +0000134 this.liveItem(this._position).onclick(e);
Nils Diewald5975d702015-03-09 17:45:42 +0000135 e.halt();
Nils Diewald59c02fc2015-03-07 01:29:09 +0000136 break;
137 case 8: // 'Backspace'
Nils Diewald7148c6f2015-05-04 15:07:53 +0000138 this._prefix.chop();
Nils Diewald5975d702015-03-09 17:45:42 +0000139 this.show();
Nils Diewald59c02fc2015-03-07 01:29:09 +0000140 e.halt();
141 break;
Nils Diewald47f366b2015-04-15 20:06:35 +0000142 };
143 },
Nils Diewald59c02fc2015-03-07 01:29:09 +0000144
Nils Diewald47f366b2015-04-15 20:06:35 +0000145 // Add characters to prefix
146 _keypress : function (e) {
147 var c = String.fromCharCode(_codeFromEvent(e)).toLowerCase();
Nils Diewald5975d702015-03-09 17:45:42 +0000148
Nils Diewald47f366b2015-04-15 20:06:35 +0000149 // Add prefix
150 this._prefix.add(c);
151
152 if (!this.show()) {
153 this.prefix('').show();
154 e.halt();
Nils Diewald59c02fc2015-03-07 01:29:09 +0000155 };
156 },
157
Nils Diewald2fe12e12015-03-06 16:47:06 +0000158 // Initialize list
Akron1ff3ac22016-04-28 16:30:45 +0200159 _init : function (itemClass, prefixClass, lengthFieldClass, params) {
Nils Diewald59c02fc2015-03-07 01:29:09 +0000160 var that = this;
Nils Diewald0e6992a2015-04-14 20:13:52 +0000161 this._itemClass = itemClass || defaultItemClass;
Nils Diewald5975d702015-03-09 17:45:42 +0000162
Akronc7448732016-04-27 14:06:58 +0200163 // Add prefix object
164 if (prefixClass !== undefined) {
Nils Diewald5975d702015-03-09 17:45:42 +0000165 this._prefix = prefixClass.create();
Akronc7448732016-04-27 14:06:58 +0200166 }
167 else {
Nils Diewald0e6992a2015-04-14 20:13:52 +0000168 this._prefix = defaultPrefixClass.create();
Akronc7448732016-04-27 14:06:58 +0200169 };
Nils Diewald5c5a7472015-04-02 22:13:38 +0000170 this._prefix._menu = this;
171
Akronc7448732016-04-27 14:06:58 +0200172 // Add lengthField object
Akron1ff3ac22016-04-28 16:30:45 +0200173 if (lengthFieldClass !== undefined) {
174 this._lengthField = lengthFieldClass.create();
175 }
176 else {
177 this._lengthField = defaultLengthFieldClass.create();
178 };
Akronc7448732016-04-27 14:06:58 +0200179 this._lengthField._menu = this;
180
Akron9905e2a2016-05-10 16:06:44 +0200181 // Initialize the slider
182 this._slider = sliderClass.create();
Akronc7448732016-04-27 14:06:58 +0200183
Nils Diewald5975d702015-03-09 17:45:42 +0000184 var e = document.createElement("ul");
Nils Diewald59c02fc2015-03-07 01:29:09 +0000185 e.style.opacity = 0;
186 e.style.outline = 0;
187 e.setAttribute('tabindex', 0);
Nils Diewald5c5a7472015-04-02 22:13:38 +0000188 e.classList.add('menu');
Nils Diewald58141332015-04-07 16:18:45 +0000189 e.classList.add('roll');
Nils Diewald5975d702015-03-09 17:45:42 +0000190 e.appendChild(this._prefix.element());
Akronc7448732016-04-27 14:06:58 +0200191 e.appendChild(this._lengthField.element());
Akron9905e2a2016-05-10 16:06:44 +0200192 e.appendChild(this._slider.element());
Nils Diewald86dad5b2015-01-28 15:09:07 +0000193
Nils Diewald6e43ffd2015-03-25 18:55:39 +0000194 // This has to be cleaned up later on
195 e["menu"] = this;
196
Nils Diewald59c02fc2015-03-07 01:29:09 +0000197 // Arrow keys
198 e.addEventListener(
Nils Diewald47f366b2015-04-15 20:06:35 +0000199 'keydown',
200 function (ev) {
201 that._keydown(ev)
202 },
203 false
204 );
205
206 // Strings
207 e.addEventListener(
Nils Diewald5975d702015-03-09 17:45:42 +0000208 'keypress',
Nils Diewald59c02fc2015-03-07 01:29:09 +0000209 function (ev) {
Nils Diewald5975d702015-03-09 17:45:42 +0000210 that._keypress(ev)
Nils Diewald59c02fc2015-03-07 01:29:09 +0000211 },
212 false
213 );
214
215 // Mousewheel
216 e.addEventListener(
Nils Diewald5975d702015-03-09 17:45:42 +0000217 'wheel',
Nils Diewald59c02fc2015-03-07 01:29:09 +0000218 function (ev) {
219 that._mousewheel(ev)
Nils Diewald2fe12e12015-03-06 16:47:06 +0000220 },
221 false
222 );
223
Nils Diewald5975d702015-03-09 17:45:42 +0000224 this._element = e;
Nils Diewald86dad5b2015-01-28 15:09:07 +0000225 this.active = false;
Akron37513a62015-11-17 01:07:11 +0100226 // this.selected = undefined;
Nils Diewald86dad5b2015-01-28 15:09:07 +0000227 this._items = new Array();
Akron9905e2a2016-05-10 16:06:44 +0200228 var i = 0;
Nils Diewald2fe12e12015-03-06 16:47:06 +0000229
230 // Initialize item list based on parameters
Nils Diewald86dad5b2015-01-28 15:09:07 +0000231 for (i in params) {
Nils Diewald0e6992a2015-04-14 20:13:52 +0000232 var obj = this._itemClass.create(params[i]);
Nils Diewald6e43ffd2015-03-25 18:55:39 +0000233
234 // This may become circular
235 obj["_menu"] = this;
Akron1ff3ac22016-04-28 16:30:45 +0200236 this._lengthField.add(params[i]);
Nils Diewald2fe12e12015-03-06 16:47:06 +0000237 this._items.push(obj);
Nils Diewald86dad5b2015-01-28 15:09:07 +0000238 };
Akronc7448732016-04-27 14:06:58 +0200239
Nils Diewald0e6992a2015-04-14 20:13:52 +0000240 this._limit = menuLimit;
Akron9905e2a2016-05-10 16:06:44 +0200241 this._slider.length(i);
242 this._slider.limit(this._limit);
243
Nils Diewald86dad5b2015-01-28 15:09:07 +0000244 this._position = 0; // position in the active list
245 this._active = -1; // active item in the item list
Nils Diewald20f7ace2015-05-07 12:51:34 +0000246 this._firstActive = false; // Show the first item active always?
Nils Diewald86dad5b2015-01-28 15:09:07 +0000247 this._reset();
248 return this;
249 },
250
Nils Diewald2fe12e12015-03-06 16:47:06 +0000251 /**
Nils Diewald7148c6f2015-05-04 15:07:53 +0000252 * Get the associated dom element.
Nils Diewald2fe12e12015-03-06 16:47:06 +0000253 */
Nils Diewald86dad5b2015-01-28 15:09:07 +0000254 element : function () {
255 return this._element;
256 },
257
Nils Diewald7148c6f2015-05-04 15:07:53 +0000258
Nils Diewald2fe12e12015-03-06 16:47:06 +0000259 /**
Nils Diewald7148c6f2015-05-04 15:07:53 +0000260 * Get the creator class for items
Nils Diewald2fe12e12015-03-06 16:47:06 +0000261 */
Nils Diewald86dad5b2015-01-28 15:09:07 +0000262 itemClass : function () {
263 return this._itemClass;
264 },
265
266 /**
Nils Diewald7148c6f2015-05-04 15:07:53 +0000267 * Get and set the numerical value
268 * for the maximum number of items visible.
Nils Diewald86dad5b2015-01-28 15:09:07 +0000269 */
270 limit : function (limit) {
Nils Diewald5975d702015-03-09 17:45:42 +0000271 if (arguments.length === 1) {
Nils Diewald86dad5b2015-01-28 15:09:07 +0000272 this._limit = limit;
Akron9905e2a2016-05-10 16:06:44 +0200273 this._slider.limit(limit);
Nils Diewald5975d702015-03-09 17:45:42 +0000274 return this;
275 };
Nils Diewald86dad5b2015-01-28 15:09:07 +0000276 return this._limit;
277 },
278
Nils Diewald7148c6f2015-05-04 15:07:53 +0000279
Nils Diewald86dad5b2015-01-28 15:09:07 +0000280 /**
281 * Upgrade this object to another object,
282 * while private data stays intact.
283 *
Nils Diewald2fe12e12015-03-06 16:47:06 +0000284 * @param {Object} An object with properties.
Nils Diewald86dad5b2015-01-28 15:09:07 +0000285 */
286 upgradeTo : function (props) {
287 for (var prop in props) {
288 this[prop] = props[prop];
289 };
290 return this;
291 },
292
Nils Diewald7148c6f2015-05-04 15:07:53 +0000293
Nils Diewald2fe12e12015-03-06 16:47:06 +0000294 // Reset chosen item and prefix
Nils Diewald86dad5b2015-01-28 15:09:07 +0000295 _reset : function () {
296 this._offset = 0;
297 this._pos = 0;
Nils Diewald5975d702015-03-09 17:45:42 +0000298 this._prefix.value('');
Nils Diewald86dad5b2015-01-28 15:09:07 +0000299 },
300
Nils Diewald7148c6f2015-05-04 15:07:53 +0000301
Nils Diewald86dad5b2015-01-28 15:09:07 +0000302 /**
303 * Filter the list and make it visible
304 *
305 * @param {string} Prefix for filtering the list
306 */
Nils Diewald5975d702015-03-09 17:45:42 +0000307 show : function () {
Nils Diewalde8518f82015-03-18 22:41:49 +0000308
Nils Diewald86dad5b2015-01-28 15:09:07 +0000309 // Initialize the list
310 if (!this._initList())
Nils Diewald59c02fc2015-03-07 01:29:09 +0000311 return false;
Nils Diewald86dad5b2015-01-28 15:09:07 +0000312
Nils Diewald2fe12e12015-03-06 16:47:06 +0000313 // show based on initial offset
Nils Diewald86dad5b2015-01-28 15:09:07 +0000314 this._showItems(0);
315
316 // Set the first element to active
Nils Diewald2fe12e12015-03-06 16:47:06 +0000317 // Todo: Or the last element chosen
Nils Diewald20f7ace2015-05-07 12:51:34 +0000318 if (this._firstActive)
319 this.liveItem(0).active(true);
320
Akron37513a62015-11-17 01:07:11 +0100321 this._position = 0;
322
Nils Diewalde8518f82015-03-18 22:41:49 +0000323 this._prefix.active(false);
Nils Diewald86dad5b2015-01-28 15:09:07 +0000324
Nils Diewald2fe12e12015-03-06 16:47:06 +0000325 this._element.style.opacity = 1;
326
Akron9905e2a2016-05-10 16:06:44 +0200327 this._slider.show();
328
Akron37513a62015-11-17 01:07:11 +0100329 // Iterate to the active item
330 if (this._active !== -1 && !this._prefix.isSet()) {
331 while (this._list[this._position] < this._active) {
332 this.next();
333 };
334 };
335
Nils Diewald86dad5b2015-01-28 15:09:07 +0000336 // Add classes for rolling menus
337 this._boundary(true);
Nils Diewald59c02fc2015-03-07 01:29:09 +0000338 return true;
Nils Diewald86dad5b2015-01-28 15:09:07 +0000339 },
340
Nils Diewald7148c6f2015-05-04 15:07:53 +0000341
342 /**
343 * Hide the menu and call the onHide callback.
344 */
Nils Diewald2fe12e12015-03-06 16:47:06 +0000345 hide : function () {
Nils Diewald59c02fc2015-03-07 01:29:09 +0000346 this.active = false;
347 this.delete();
Nils Diewald2fe12e12015-03-06 16:47:06 +0000348 this._element.style.opacity = 0;
Nils Diewald7148c6f2015-05-04 15:07:53 +0000349 this._prefix.clear();
Nils Diewald5c5a7472015-04-02 22:13:38 +0000350 this.onHide();
Nils Diewald6e43ffd2015-03-25 18:55:39 +0000351 /* this._element.blur(); */
Nils Diewald86dad5b2015-01-28 15:09:07 +0000352 },
353
Nils Diewald7148c6f2015-05-04 15:07:53 +0000354 /**
355 * Function released when the menu hides.
356 * This method is expected to be overridden.
357 */
Nils Diewald5c5a7472015-04-02 22:13:38 +0000358 onHide : function () {},
359
Nils Diewald2fe12e12015-03-06 16:47:06 +0000360 // Initialize the list
Nils Diewald86dad5b2015-01-28 15:09:07 +0000361 _initList : function () {
362
Nils Diewald2fe12e12015-03-06 16:47:06 +0000363 // Create a new list
Nils Diewald86dad5b2015-01-28 15:09:07 +0000364 if (this._list === undefined) {
365 this._list = [];
366 }
367 else if (this._list.length != 0) {
368 this._boundary(false);
369 this._list.length = 0;
370 };
371
372 // Offset is initially zero
373 this._offset = 0;
374
Nils Diewald2fe12e12015-03-06 16:47:06 +0000375 // There is no prefix set
Nils Diewald86dad5b2015-01-28 15:09:07 +0000376 if (this.prefix().length <= 0) {
Nils Diewald5975d702015-03-09 17:45:42 +0000377 var i = 0;
378 for (; i < this._items.length; i++)
Nils Diewald86dad5b2015-01-28 15:09:07 +0000379 this._list.push(i);
Nils Diewald5975d702015-03-09 17:45:42 +0000380 while (this._items[++i] !== undefined) {
381 this._items[i].lowlight();
Nils Diewald5975d702015-03-09 17:45:42 +0000382 };
Nils Diewald86dad5b2015-01-28 15:09:07 +0000383 return true;
384 };
385
Nils Diewald2fe12e12015-03-06 16:47:06 +0000386 // There is a prefix set, so filter the list
Nils Diewald86dad5b2015-01-28 15:09:07 +0000387 var pos;
388 var paddedPrefix = " " + this.prefix();
389
Nils Diewald2fe12e12015-03-06 16:47:06 +0000390 // Iterate over all items and choose preferred matching items
391 // i.e. the matching happens at the word start
Nils Diewald86dad5b2015-01-28 15:09:07 +0000392 for (pos = 0; pos < this._items.length; pos++) {
393 if ((this.item(pos).lcField().indexOf(paddedPrefix)) >= 0)
394 this._list.push(pos);
395 };
396
Nils Diewald2fe12e12015-03-06 16:47:06 +0000397 // The list is empty - so lower your expectations
398 // Iterate over all items and choose matching items
399 // i.e. the matching happens anywhere in the word
Nils Diewald86dad5b2015-01-28 15:09:07 +0000400 if (this._list.length == 0) {
401 for (pos = 0; pos < this._items.length; pos++) {
402 if ((this.item(pos).lcField().indexOf(this.prefix())) >= 0)
403 this._list.push(pos);
404 };
405 };
406
Nils Diewald2fe12e12015-03-06 16:47:06 +0000407 // Filter was successful - yeah!
Nils Diewald86dad5b2015-01-28 15:09:07 +0000408 return this._list.length > 0 ? true : false;
409 },
410
411 // Set boundary for viewport
412 _boundary : function (bool) {
413 this.item(this._list[0]).noMore(bool);
414 this.item(this._list[this._list.length - 1]).noMore(bool);
415 },
416
Nils Diewald7148c6f2015-05-04 15:07:53 +0000417
Nils Diewald86dad5b2015-01-28 15:09:07 +0000418 /**
419 * Get the prefix for filtering,
Nils Diewald2fe12e12015-03-06 16:47:06 +0000420 * e.g. &quot;ve&quot; for &quot;verb&quot;
Nils Diewald86dad5b2015-01-28 15:09:07 +0000421 */
Nils Diewald5975d702015-03-09 17:45:42 +0000422 prefix : function (pref) {
423 if (arguments.length === 1) {
424 this._prefix.value(pref);
425 return this;
426 };
427 return this._prefix.value();
Nils Diewald86dad5b2015-01-28 15:09:07 +0000428 },
429
Akronc7448732016-04-27 14:06:58 +0200430 /**
431 * Get the lengthField object.
432 */
433 lengthField : function () {
434 return this._lengthField;
435 },
Nils Diewald7148c6f2015-05-04 15:07:53 +0000436
Nils Diewald2fe12e12015-03-06 16:47:06 +0000437 // Append Items that should be shown
Nils Diewald86dad5b2015-01-28 15:09:07 +0000438 _showItems : function (offset) {
439 this.delete();
440
441 // Use list
442 var shown = 0;
443 var i;
444 for (i in this._list) {
445
446 // Don't show - it's before offset
447 if (shown++ < offset)
448 continue;
449
450 this._append(this._list[i]);
451
452 if (shown >= (this.limit() + this._offset))
453 break;
454 };
455 },
456
457 /**
458 * Delete all visible items from the menu element
459 */
460 delete : function () {
461 var child;
Nils Diewald2fe12e12015-03-06 16:47:06 +0000462
Nils Diewald5975d702015-03-09 17:45:42 +0000463 /*
Nils Diewald2fe12e12015-03-06 16:47:06 +0000464 // Iterate over all visible items
Nils Diewald86dad5b2015-01-28 15:09:07 +0000465 for (var i = 0; i <= this.limit(); i++) {
466
Nils Diewald5975d702015-03-09 17:45:42 +0000467 // there is a visible element
468 // unhighlight!
Nils Diewald59c02fc2015-03-07 01:29:09 +0000469 if (child = this.shownItem(i)) {
Nils Diewald86dad5b2015-01-28 15:09:07 +0000470 child.lowlight();
Nils Diewald59c02fc2015-03-07 01:29:09 +0000471 child.active(false);
472 };
Nils Diewald86dad5b2015-01-28 15:09:07 +0000473 };
Nils Diewald5975d702015-03-09 17:45:42 +0000474 */
475
476 for (var i in this._list) {
477 var item = this._items[this._list[i]];
478 item.lowlight();
479 item.active(false);
480 };
Nils Diewald86dad5b2015-01-28 15:09:07 +0000481
Nils Diewald2fe12e12015-03-06 16:47:06 +0000482 // Remove all children
Nils Diewald5975d702015-03-09 17:45:42 +0000483 var children = this._element.childNodes;
Akronc7448732016-04-27 14:06:58 +0200484 // Leave the prefix and lengthField
Akron9905e2a2016-05-10 16:06:44 +0200485 for (var i = children.length - 1; i >= 3; i--) {
Nils Diewald5975d702015-03-09 17:45:42 +0000486 this._element.removeChild(
487 children[i]
488 );
489 };
Nils Diewald86dad5b2015-01-28 15:09:07 +0000490 },
491
492
493 // Append item to the shown list based on index
494 _append : function (i) {
495 var item = this.item(i);
496
497 // Highlight based on prefix
498 if (this.prefix().length > 0)
499 item.highlight(this.prefix());
500
501 // Append element
502 this.element().appendChild(item.element());
503 },
504
505
Nils Diewald2fe12e12015-03-06 16:47:06 +0000506 // Prepend item to the shown list based on index
507 _prepend : function (i) {
508 var item = this.item(i);
509
510 // Highlight based on prefix
511 if (this.prefix().length > 0)
512 item.highlight(this.prefix());
513
514 var e = this.element();
Akron9905e2a2016-05-10 16:06:44 +0200515 // Append element after lengthField/prefix/slider
Nils Diewald2fe12e12015-03-06 16:47:06 +0000516 e.insertBefore(
517 item.element(),
Akron9905e2a2016-05-10 16:06:44 +0200518 e.children[3]
Nils Diewald2fe12e12015-03-06 16:47:06 +0000519 );
520 },
521
522
523 /**
524 * Get a specific item from the complete list
525 *
526 * @param {number} index of the list item
527 */
528 item : function (index) {
529 return this._items[index]
530 },
531
532
Nils Diewald86dad5b2015-01-28 15:09:07 +0000533 /**
534 * Get a specific item from the filtered list
535 *
536 * @param {number} index of the list item
Nils Diewald2fe12e12015-03-06 16:47:06 +0000537 * in the filtered list
Nils Diewald86dad5b2015-01-28 15:09:07 +0000538 */
539 liveItem : function (index) {
540 if (this._list === undefined)
541 if (!this._initList())
542 return;
543
544 return this._items[this._list[index]];
545 },
546
Nils Diewald86dad5b2015-01-28 15:09:07 +0000547
548 /**
549 * Get a specific item from the visible list
550 *
551 * @param {number} index of the list item
Nils Diewald2fe12e12015-03-06 16:47:06 +0000552 * in the visible list
Nils Diewald86dad5b2015-01-28 15:09:07 +0000553 */
554 shownItem : function (index) {
555 if (index >= this.limit())
556 return;
557 return this.liveItem(this._offset + index);
558 },
559
560
Nils Diewald2fe12e12015-03-06 16:47:06 +0000561 /**
562 * Get the length of the full list
563 */
564 length : function () {
565 return this._items.length;
566 },
567
568
569 /**
570 * Make the next item in the filtered menu active
Nils Diewald86dad5b2015-01-28 15:09:07 +0000571 */
572 next : function () {
Nils Diewald5975d702015-03-09 17:45:42 +0000573
Nils Diewald86dad5b2015-01-28 15:09:07 +0000574 // No active element set
Nils Diewald5975d702015-03-09 17:45:42 +0000575 if (this._position === -1)
Nils Diewald86dad5b2015-01-28 15:09:07 +0000576 return;
577
Nils Diewald5975d702015-03-09 17:45:42 +0000578 var newItem;
579
Nils Diewald86dad5b2015-01-28 15:09:07 +0000580 // Set new live item
Nils Diewalde8518f82015-03-18 22:41:49 +0000581 if (!this._prefix.active()) {
582 var oldItem = this.liveItem(this._position);
583 oldItem.active(false);
584 };
585
586 this._position++;
587
Nils Diewald5975d702015-03-09 17:45:42 +0000588 newItem = this.liveItem(this._position);
Nils Diewald86dad5b2015-01-28 15:09:07 +0000589
Nils Diewald5975d702015-03-09 17:45:42 +0000590 // The next element is undefined - roll to top or to prefix
Nils Diewald86dad5b2015-01-28 15:09:07 +0000591 if (newItem === undefined) {
Nils Diewald5975d702015-03-09 17:45:42 +0000592
593 // Activate prefix
594 var prefix = this._prefix;
595
596 // Mark prefix
597 if (prefix.isSet() && !prefix.active()) {
598 this._position--;
599 prefix.active(true);
600 return;
601 }
602 else {
603 this._offset = 0;
604 this._position = 0;
605 newItem = this.liveItem(0);
606 this._showItems(0);
607 };
Nils Diewald86dad5b2015-01-28 15:09:07 +0000608 }
609
610 // The next element is outside the view - roll down
Nils Diewald2fe12e12015-03-06 16:47:06 +0000611 else if (this._position >= (this.limit() + this._offset)) {
Nils Diewald86dad5b2015-01-28 15:09:07 +0000612 this._removeFirst();
613 this._offset++;
614 this._append(this._list[this._position]);
615 };
Nils Diewald5975d702015-03-09 17:45:42 +0000616
617 this._prefix.active(false);
Nils Diewald86dad5b2015-01-28 15:09:07 +0000618 newItem.active(true);
619 },
620
Nils Diewalde8518f82015-03-18 22:41:49 +0000621 /*
622 * Page down to the first item on the next page
623 */
624 /*
625 nextPage : function () {
626
627 // Prefix is active
628 if (this._prefix.active()) {
629 this._prefix.active(false);
630 }
631
632 // Last item is chosen
633 else if (this._position >= this.limit() + this._offset) {
634
635 this._position = this.limit() + this._offset - 1;
636 newItem = this.liveItem(this._position);
637 var oldItem = this.liveItem(this._position--);
638 oldItem.active(false);
639 }
640
641 // Last item of page is chosen
642 else if (0) {
643
644 // Jump to last item
645 else {
646 var oldItem = this.liveItem(this._position);
647 oldItem.active(false);
648
649 this._position = this.limit() + this._offset - 1;
650 newItem = this.liveItem(this._position);
651 };
652
653 newItem.active(true);
654 },
655 */
656
Nils Diewald86dad5b2015-01-28 15:09:07 +0000657
658 /*
659 * Make the previous item in the menu active
660 */
Nils Diewald86dad5b2015-01-28 15:09:07 +0000661 prev : function () {
Nils Diewald2d210752015-03-09 19:01:15 +0000662
Nils Diewald2fe12e12015-03-06 16:47:06 +0000663 // No active element set
Nils Diewalde8518f82015-03-18 22:41:49 +0000664 if (this._position === -1) {
Nils Diewald86dad5b2015-01-28 15:09:07 +0000665 return;
Nils Diewalde8518f82015-03-18 22:41:49 +0000666 // TODO: Choose last item
667 };
Nils Diewald86dad5b2015-01-28 15:09:07 +0000668
Nils Diewald5975d702015-03-09 17:45:42 +0000669 var newItem;
670
Nils Diewald86dad5b2015-01-28 15:09:07 +0000671 // Set new live item
Nils Diewald2d210752015-03-09 19:01:15 +0000672 if (!this._prefix.active()) {
673 var oldItem = this.liveItem(this._position--);
674 oldItem.active(false);
675 };
676
Nils Diewald5975d702015-03-09 17:45:42 +0000677 newItem = this.liveItem(this._position);
Nils Diewald86dad5b2015-01-28 15:09:07 +0000678
679 // The previous element is undefined - roll to bottom
680 if (newItem === undefined) {
Nils Diewald5975d702015-03-09 17:45:42 +0000681
682 // Activate prefix
683 var prefix = this._prefix;
Nils Diewald2fe12e12015-03-06 16:47:06 +0000684 this._offset = this.liveLength() - this.limit();
Nils Diewald2d210752015-03-09 19:01:15 +0000685
686 // Normalize offset
687 this._offset = this._offset < 0 ? 0 : this._offset;
688
Nils Diewald2fe12e12015-03-06 16:47:06 +0000689 this._position = this.liveLength() - 1;
Nils Diewald5975d702015-03-09 17:45:42 +0000690
691 if (prefix.isSet() && !prefix.active()) {
692 this._position++;
693 prefix.active(true);
694 return;
695 }
696 else {
697 newItem = this.liveItem(this._position);
698 this._showItems(this._offset);
699 };
Nils Diewald86dad5b2015-01-28 15:09:07 +0000700 }
701
702 // The previous element is outside the view - roll up
703 else if (this._position < this._offset) {
704 this._removeLast();
705 this._offset--;
706 this._prepend(this._list[this._position]);
707 };
Nils Diewald2fe12e12015-03-06 16:47:06 +0000708
Nils Diewald5975d702015-03-09 17:45:42 +0000709 this._prefix.active(false);
Nils Diewald86dad5b2015-01-28 15:09:07 +0000710 newItem.active(true);
711 },
Nils Diewald86dad5b2015-01-28 15:09:07 +0000712
713
Nils Diewald7148c6f2015-05-04 15:07:53 +0000714 /**
715 * Length of the filtered item list.
716 */
Nils Diewald5975d702015-03-09 17:45:42 +0000717 liveLength : function () {
718 if (this._list === undefined)
719 this._initList();
720 return this._list.length;
721 },
722
723
Nils Diewald2fe12e12015-03-06 16:47:06 +0000724 // Remove the HTML node from the first item
Nils Diewald86dad5b2015-01-28 15:09:07 +0000725 _removeFirst : function () {
726 this.item(this._list[this._offset]).lowlight();
Akron9905e2a2016-05-10 16:06:44 +0200727 // leave lengthField/prefix/slider
728 this._element.removeChild(this._element.children[3]);
Nils Diewald86dad5b2015-01-28 15:09:07 +0000729 },
730
Nils Diewald2fe12e12015-03-06 16:47:06 +0000731
732 // Remove the HTML node from the last item
Nils Diewald86dad5b2015-01-28 15:09:07 +0000733 _removeLast : function () {
Nils Diewald2fe12e12015-03-06 16:47:06 +0000734 this.item(this._list[this._offset + this.limit() - 1]).lowlight();
Nils Diewald86dad5b2015-01-28 15:09:07 +0000735 this._element.removeChild(this._element.lastChild);
Nils Diewald2fe12e12015-03-06 16:47:06 +0000736 }
Nils Diewald86dad5b2015-01-28 15:09:07 +0000737 };
Nils Diewald0e6992a2015-04-14 20:13:52 +0000738});