blob: 01bf7d8bb3691d229a251db0424e690d44d00667 [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/*
Akron3c2730f2016-05-24 15:08:29 +02007 * TODO: Show the slider briefly on move (whenever screen is called).
Akron9c4d1ae2016-05-25 21:43:22 +02008 * TODO: Ignore alt+ and strg+ key strokes.
Akron0b92f692016-05-25 22:37:13 +02009 * TODO: Should scroll to a chosen value after prefixing, if the chosen value is live
Akron201b72a2016-06-03 01:46:19 +020010 * TODO: Add a "title" to a menu that is not scrollable.
11 * TODO: Make the menu responsive by showing less items on smaller screens
12 * or anytime items would be outside the screen.
Akron02360e42016-06-07 13:41:12 +020013 * TODO: Add a .match() method to items for scrolling and probably for prefixing.
14 * TODO: Add static header (for title, sortation fields, but also for menu points like "fragments" and "history".
Akrone91da782017-12-15 17:17:50 +010015 * TODO: Support space separated list of prefixes so "co no" will highlight "common noun"
Nils Diewald2488d052015-04-09 21:46:02 +000016 */
Akrone51eaa32020-11-10 09:35:53 +010017
18"use strict";
Nils Diewald0e6992a2015-04-14 20:13:52 +000019define([
20 'menu/item',
21 'menu/prefix',
Akronc7448732016-04-27 14:06:58 +020022 'menu/lengthField',
Akron9905e2a2016-05-10 16:06:44 +020023 'menu/slider',
Nils Diewald0e6992a2015-04-14 20:13:52 +000024 'util'
25], function (defaultItemClass,
Akrone4961b12017-05-10 21:04:46 +020026 defaultPrefixClass,
27 defaultLengthFieldClass,
28 sliderClass) {
Nils Diewaldfda29d92015-01-22 17:28:01 +000029
Nils Diewald0e6992a2015-04-14 20:13:52 +000030 // Default maximum number of menu items
Akronc53cfc82020-10-19 11:00:58 +020031 const menuLimit = 8;
Nils Diewald0e6992a2015-04-14 20:13:52 +000032
Nils Diewald86dad5b2015-01-28 15:09:07 +000033 /**
34 * List of items for drop down menu (complete).
35 * Only a sublist of the menu is filtered (live).
36 * Only a sublist of the filtered menu is visible (shown).
37 */
Nils Diewald0e6992a2015-04-14 20:13:52 +000038 return {
Nils Diewald86dad5b2015-01-28 15:09:07 +000039 /**
40 * Create new Menu based on the action prefix
41 * and a list of menu items.
42 *
Akron7524be12016-06-01 17:31:33 +020043 *
44 * Accepts an associative array containg the elements
45 * itemClass, prefixClass, lengthFieldClass
46 *
Nils Diewald86dad5b2015-01-28 15:09:07 +000047 * @this {Menu}
48 * @constructor
49 * @param {string} Context prefix
50 * @param {Array.<Array.<string>>} List of menu items
51 */
Akron7524be12016-06-01 17:31:33 +020052 create : function (list, params) {
53 return Object.create(this)._init(list, params);
Nils Diewald86dad5b2015-01-28 15:09:07 +000054 },
55
Akron5240b8c2016-05-20 09:17:41 +020056 // Initialize list
Akron7524be12016-06-01 17:31:33 +020057 _init : function (list, params) {
Akronc53cfc82020-10-19 11:00:58 +020058
Akron7524be12016-06-01 17:31:33 +020059 if (params === undefined)
Akrone4961b12017-05-10 21:04:46 +020060 params = {};
Akron7524be12016-06-01 17:31:33 +020061
Akronc53cfc82020-10-19 11:00:58 +020062 const t = this;
63
64 t._itemClass = params["itemClass"] || defaultItemClass;
Akron5240b8c2016-05-20 09:17:41 +020065
66 // Add prefix object
Akron7524be12016-06-01 17:31:33 +020067 if (params["prefixClass"] !== undefined) {
Akronc53cfc82020-10-19 11:00:58 +020068 t._prefix = params["prefixClass"].create();
Akron5240b8c2016-05-20 09:17:41 +020069 }
70 else {
Akronc53cfc82020-10-19 11:00:58 +020071 t._prefix = defaultPrefixClass.create();
Akron5240b8c2016-05-20 09:17:41 +020072 };
Akronc53cfc82020-10-19 11:00:58 +020073 t._prefix._menu = t;
Akron5240b8c2016-05-20 09:17:41 +020074
75 // Add lengthField object
Akron7524be12016-06-01 17:31:33 +020076 if (params["lengthFieldClass"] !== undefined) {
Akronc53cfc82020-10-19 11:00:58 +020077 t._lengthField = params["lengthFieldClass"].create();
Akron5240b8c2016-05-20 09:17:41 +020078 }
79 else {
Akronc53cfc82020-10-19 11:00:58 +020080 t._lengthField = defaultLengthFieldClass.create();
Akron5240b8c2016-05-20 09:17:41 +020081 };
Akronc53cfc82020-10-19 11:00:58 +020082 t._lengthField._menu = t;
Akron5240b8c2016-05-20 09:17:41 +020083
84 // Initialize slider
Akronc53cfc82020-10-19 11:00:58 +020085 t._slider = sliderClass.create(t);
Akron5240b8c2016-05-20 09:17:41 +020086
87 // Create the element
Akron9c4d1ae2016-05-25 21:43:22 +020088 var el = document.createElement("ul");
Akronc53cfc82020-10-19 11:00:58 +020089 el.style.outline = 0;
90 el.setAttribute('tabindex', 0);
91 el.classList.add('menu', 'roll');
92 el.appendChild(t._prefix.element());
93 el.appendChild(t._lengthField.element());
94 el.appendChild(t._slider.element());
Akron5240b8c2016-05-20 09:17:41 +020095
96 // This has to be cleaned up later on
Akronc53cfc82020-10-19 11:00:58 +020097 el["menu"] = t;
Akron5240b8c2016-05-20 09:17:41 +020098
99 // Arrow keys
Akron9c4d1ae2016-05-25 21:43:22 +0200100 el.addEventListener(
Akrone4961b12017-05-10 21:04:46 +0200101 'keydown',
Akronc53cfc82020-10-19 11:00:58 +0200102 t._keydown.bind(t),
Akrone4961b12017-05-10 21:04:46 +0200103 false
Akron5240b8c2016-05-20 09:17:41 +0200104 );
105
106 // Strings
Akron9c4d1ae2016-05-25 21:43:22 +0200107 el.addEventListener(
Akrone4961b12017-05-10 21:04:46 +0200108 'keypress',
Akronc53cfc82020-10-19 11:00:58 +0200109 t._keypress.bind(t),
Akrone4961b12017-05-10 21:04:46 +0200110 false
Akron5240b8c2016-05-20 09:17:41 +0200111 );
112
113 // Mousewheel
Akron9c4d1ae2016-05-25 21:43:22 +0200114 el.addEventListener(
Akrone4961b12017-05-10 21:04:46 +0200115 'wheel',
Akronc53cfc82020-10-19 11:00:58 +0200116 t._mousewheel.bind(t),
Akrone4961b12017-05-10 21:04:46 +0200117 false
Akron5240b8c2016-05-20 09:17:41 +0200118 );
Akrona1159ff2018-07-22 13:28:31 +0200119
Akronc53cfc82020-10-19 11:00:58 +0200120 // Touch events
121 ['touchstart', 'touchend', 'touchmove'].forEach(
122 e => el.addEventListener(e, t._touch.bind(t), false)
Akrona1159ff2018-07-22 13:28:31 +0200123 );
124
125
Akron24aa0052020-11-10 11:00:34 +0100126 t._el = el;
Akroneaba63e2018-01-26 19:49:30 +0100127
Akronc53cfc82020-10-19 11:00:58 +0200128 t._limit = menuLimit;
Akrone4961b12017-05-10 21:04:46 +0200129
Akronc53cfc82020-10-19 11:00:58 +0200130 t._items = new Array();
Akron5240b8c2016-05-20 09:17:41 +0200131
Akroneaba63e2018-01-26 19:49:30 +0100132 // TODO:
133 // Make this separate from _init
Akronc53cfc82020-10-19 11:00:58 +0200134 t.readItems(list);
Akroneaba63e2018-01-26 19:49:30 +0100135
Akronc53cfc82020-10-19 11:00:58 +0200136 t.dontHide = false;
hebastaf95226b2019-09-19 11:37:00 +0200137
Akronc53cfc82020-10-19 11:00:58 +0200138 return t;
Akroneaba63e2018-01-26 19:49:30 +0100139 },
140
141 // Read items to add to list
142 readItems : function (list) {
Akronc53cfc82020-10-19 11:00:58 +0200143 const t = this;
Akroneaba63e2018-01-26 19:49:30 +0100144
Akronc53cfc82020-10-19 11:00:58 +0200145 t._list = undefined;
Akroneaba63e2018-01-26 19:49:30 +0100146
147 // Remove circular reference to "this" in items
Akronc53cfc82020-10-19 11:00:58 +0200148 for (let i = 0; i < t._items.length; i++) {
149 delete t._items[i]["_menu"];
150 delete t._items[i];
Akroneaba63e2018-01-26 19:49:30 +0100151 };
152
Akronc53cfc82020-10-19 11:00:58 +0200153 t._items = new Array();
154 t.removeItems();
Akroneaba63e2018-01-26 19:49:30 +0100155
156
157 // Initialize items
Akronc53cfc82020-10-19 11:00:58 +0200158 t._lengthField.reset();
Akroneaba63e2018-01-26 19:49:30 +0100159
Akron5240b8c2016-05-20 09:17:41 +0200160 // Initialize item list based on parameters
Akron678c26f2020-10-09 08:52:50 +0200161 list.forEach(function(i){
Akronc53cfc82020-10-19 11:00:58 +0200162 const obj = this._itemClass.create(i);
Akron5240b8c2016-05-20 09:17:41 +0200163
Akrone4961b12017-05-10 21:04:46 +0200164 // This may become circular
165 obj["_menu"] = this;
Akron678c26f2020-10-09 08:52:50 +0200166 this._lengthField.add(i);
Akrone4961b12017-05-10 21:04:46 +0200167 this._items.push(obj);
Akronc53cfc82020-10-19 11:00:58 +0200168 }, t);
Akron5240b8c2016-05-20 09:17:41 +0200169
Akronc53cfc82020-10-19 11:00:58 +0200170 t._slider.length(t.liveLength())
171 .limit(t._limit)
Akrone4961b12017-05-10 21:04:46 +0200172 .reInit();
173
Akronc53cfc82020-10-19 11:00:58 +0200174 t._firstActive = false;
Akroneaba63e2018-01-26 19:49:30 +0100175 // Show the first item active always?
Akronc53cfc82020-10-19 11:00:58 +0200176 t.offset = 0;
177 t.position = 0;
Akron5240b8c2016-05-20 09:17:41 +0200178 },
Akroneaba63e2018-01-26 19:49:30 +0100179
Akron5240b8c2016-05-20 09:17:41 +0200180 // Initialize the item list
181 _initList : function () {
Akronc53cfc82020-10-19 11:00:58 +0200182 const t = this;
Akron5240b8c2016-05-20 09:17:41 +0200183
184 // Create a new list
Akronc53cfc82020-10-19 11:00:58 +0200185 if (t._list === undefined) {
186 t._list = [];
Akron5240b8c2016-05-20 09:17:41 +0200187 }
Akronc53cfc82020-10-19 11:00:58 +0200188 else if (t._list.length !== 0) {
189 t._boundary(false);
190 t._list.length = 0;
Akron5240b8c2016-05-20 09:17:41 +0200191 };
192
193 // Offset is initially zero
Akronc53cfc82020-10-19 11:00:58 +0200194 t.offset = 0;
Akron5240b8c2016-05-20 09:17:41 +0200195
196 // There is no prefix set
Akronc53cfc82020-10-19 11:00:58 +0200197 if (t.prefix().length <= 0) {
Akron5240b8c2016-05-20 09:17:41 +0200198
Akrone4961b12017-05-10 21:04:46 +0200199 // add all items to the list and lowlight
Akronb50964a2020-10-12 11:44:37 +0200200 let i = 0;
Akronc53cfc82020-10-19 11:00:58 +0200201 for (; i < t._items.length; i++) {
202 t._list.push(i);
203 t._items[i].lowlight();
Akrone4961b12017-05-10 21:04:46 +0200204 };
Akron5240b8c2016-05-20 09:17:41 +0200205
Akronc53cfc82020-10-19 11:00:58 +0200206 t._slider.length(i).reInit();
Akron97752a72016-05-25 14:43:07 +0200207
Akrone4961b12017-05-10 21:04:46 +0200208 return true;
Akron5240b8c2016-05-20 09:17:41 +0200209 };
210
211 /*
212 * There is a prefix set, so filter the list!
213 */
Akronc53cfc82020-10-19 11:00:58 +0200214 let pos;
215 const prefixList = t.prefix().toLowerCase().split(" ");
Akronacffc652017-12-18 21:21:25 +0100216
Akronc53cfc82020-10-19 11:00:58 +0200217 const items = [];
218 let maxPoints = 1; // minimum 1
Akron5240b8c2016-05-20 09:17:41 +0200219
220 // Iterate over all items and choose preferred matching items
221 // i.e. the matching happens at the word start
Akronc53cfc82020-10-19 11:00:58 +0200222 t._items.forEach(function(it, pos){
Akronacffc652017-12-18 21:21:25 +0100223
Akronb50964a2020-10-12 11:44:37 +0200224 let points = 0;
Akronacffc652017-12-18 21:21:25 +0100225
Akronb50964a2020-10-12 11:44:37 +0200226 prefixList.forEach(function(p) {
Akronacffc652017-12-18 21:21:25 +0100227
228 // Check if it matches at the beginning
Akronb50964a2020-10-12 11:44:37 +0200229 if ((it.lcField().includes(" " + p))) {
Akronacffc652017-12-18 21:21:25 +0100230 points += 5;
231 }
232
233 // Check if it matches anywhere
Akronb50964a2020-10-12 11:44:37 +0200234 else if (it.lcField().includes(p)) {
Akronacffc652017-12-18 21:21:25 +0100235 points += 1;
236 };
Akronb50964a2020-10-12 11:44:37 +0200237 });
Akronacffc652017-12-18 21:21:25 +0100238
239 if (points > maxPoints) {
240 this._list = [pos];
241 maxPoints = points;
242 }
243 else if (points == maxPoints) {
Akrone4961b12017-05-10 21:04:46 +0200244 this._list.push(pos);
Akronacffc652017-12-18 21:21:25 +0100245 }
Akronc53cfc82020-10-19 11:00:58 +0200246 }, t);
Akron5240b8c2016-05-20 09:17:41 +0200247
Akronc53cfc82020-10-19 11:00:58 +0200248 t._slider.length(t._list.length).reInit();
Akron6ed13992016-05-23 18:06:05 +0200249
Akron5240b8c2016-05-20 09:17:41 +0200250 // Filter was successful - yeah!
Akronc53cfc82020-10-19 11:00:58 +0200251 return t._list.length > 0 ? true : false;
Akron5240b8c2016-05-20 09:17:41 +0200252 },
253
254
Nils Diewald6e43ffd2015-03-25 18:55:39 +0000255 /**
256 * Destroy this menu
257 * (in case you don't trust the
258 * mark and sweep GC)!
259 */
260 destroy : function () {
Akronc53cfc82020-10-19 11:00:58 +0200261 const t = this;
Akron47c086c2016-05-18 21:22:06 +0200262
Akron5240b8c2016-05-20 09:17:41 +0200263 // Remove circular reference to "this" in menu
Akron24aa0052020-11-10 11:00:34 +0100264 if (t._el != undefined)
265 delete t._el["menu"];
Nils Diewald6e43ffd2015-03-25 18:55:39 +0000266
Akron5240b8c2016-05-20 09:17:41 +0200267 // Remove circular reference to "this" in items
Akronc53cfc82020-10-19 11:00:58 +0200268 t._items.forEach(function(i) {
Akron678c26f2020-10-09 08:52:50 +0200269 delete i["_menu"];
270 });
Akron5240b8c2016-05-20 09:17:41 +0200271
272 // Remove circular reference to "this" in prefix
Akronc53cfc82020-10-19 11:00:58 +0200273 delete t._prefix['_menu'];
274 delete t._lengthField['_menu'];
275 delete t._slider['_menu'];
Nils Diewald6e43ffd2015-03-25 18:55:39 +0000276 },
277
Nils Diewald7148c6f2015-05-04 15:07:53 +0000278
279 /**
280 * Focus on this menu.
281 */
Nils Diewald2fe12e12015-03-06 16:47:06 +0000282 focus : function () {
Akron24aa0052020-11-10 11:00:34 +0100283 this._el.focus();
Nils Diewald2fe12e12015-03-06 16:47:06 +0000284 },
285
Nils Diewald7148c6f2015-05-04 15:07:53 +0000286
Nils Diewald59c02fc2015-03-07 01:29:09 +0000287 // mouse wheel treatment
288 _mousewheel : function (e) {
Akronc53cfc82020-10-19 11:00:58 +0200289 const delta = e.deltaY / 120;
Nils Diewald5975d702015-03-09 17:45:42 +0000290 if (delta > 0)
Akrone4961b12017-05-10 21:04:46 +0200291 this.next();
Nils Diewald5975d702015-03-09 17:45:42 +0000292 else if (delta < 0)
Akrone4961b12017-05-10 21:04:46 +0200293 this.prev();
Nils Diewald59c02fc2015-03-07 01:29:09 +0000294 e.halt();
295 },
296
Akronc53cfc82020-10-19 11:00:58 +0200297
Akrona1159ff2018-07-22 13:28:31 +0200298 // touchmove treatment
299 _touch : function (e) {
Akronc53cfc82020-10-19 11:00:58 +0200300 const s = this.slider();
301
Akrona1159ff2018-07-22 13:28:31 +0200302 if (e.type === "touchstart") {
Akronc53cfc82020-10-19 11:00:58 +0200303 this._lastTouch = e.touches[0].clientY;
Akrona1159ff2018-07-22 13:28:31 +0200304 }
305 else if (e.type === "touchend") {
Akrona1159ff2018-07-22 13:28:31 +0200306 this._lastTouch = undefined;
Akrona1159ff2018-07-22 13:28:31 +0200307 }
308 else if (e.type === "touchmove") {
Akronc53cfc82020-10-19 11:00:58 +0200309 const to = e.touches[0];
Akrone817b882018-08-31 14:09:17 +0200310
311 // TODO:
312 // Instead of using 26px, choose the item height
313 // or use the menu height // shownItems
314
Akrona1159ff2018-07-22 13:28:31 +0200315 // s.movetoRel(t.clientY - this._initTouch);
Akronc53cfc82020-10-19 11:00:58 +0200316 if ((this._lastTouch + 26) < to.clientY) {
Akrone817b882018-08-31 14:09:17 +0200317 this.viewDown();
Akronc53cfc82020-10-19 11:00:58 +0200318 this._lastTouch = to.clientY;
Akrona1159ff2018-07-22 13:28:31 +0200319 }
Akronc53cfc82020-10-19 11:00:58 +0200320 else if ((this._lastTouch - 26) > to.clientY) {
Akrone817b882018-08-31 14:09:17 +0200321 this.viewUp();
Akronc53cfc82020-10-19 11:00:58 +0200322 this._lastTouch = to.clientY;
Akrona1159ff2018-07-22 13:28:31 +0200323 }
324 e.halt();
325 };
326 },
Nils Diewald7148c6f2015-05-04 15:07:53 +0000327
Nils Diewald59c02fc2015-03-07 01:29:09 +0000328 // Arrow key and prefix treatment
Nils Diewald47f366b2015-04-15 20:06:35 +0000329 _keydown : function (e) {
Akronc53cfc82020-10-19 11:00:58 +0200330 const t = this;
Nils Diewald59c02fc2015-03-07 01:29:09 +0000331
Akronc53cfc82020-10-19 11:00:58 +0200332 switch (_codeFromEvent(e)) {
333
Nils Diewald59c02fc2015-03-07 01:29:09 +0000334 case 27: // 'Esc'
Akrone4961b12017-05-10 21:04:46 +0200335 e.halt();
Akronc53cfc82020-10-19 11:00:58 +0200336 t.hide();
Akrone4961b12017-05-10 21:04:46 +0200337 break;
Nils Diewald5975d702015-03-09 17:45:42 +0000338
Nils Diewald59c02fc2015-03-07 01:29:09 +0000339 case 38: // 'Up'
Akrone4961b12017-05-10 21:04:46 +0200340 e.halt();
Akronc53cfc82020-10-19 11:00:58 +0200341 t.prev();
Akrone4961b12017-05-10 21:04:46 +0200342 break;
Akronc53cfc82020-10-19 11:00:58 +0200343
Nils Diewald5975d702015-03-09 17:45:42 +0000344 case 33: // 'Page up'
Akrone4961b12017-05-10 21:04:46 +0200345 e.halt();
Akronc53cfc82020-10-19 11:00:58 +0200346 t.pageUp();
Akrone4961b12017-05-10 21:04:46 +0200347 break;
Akronc53cfc82020-10-19 11:00:58 +0200348
Nils Diewald5975d702015-03-09 17:45:42 +0000349 case 40: // 'Down'
Akrone4961b12017-05-10 21:04:46 +0200350 e.halt();
Akronc53cfc82020-10-19 11:00:58 +0200351 t.next();
Akrone4961b12017-05-10 21:04:46 +0200352 break;
Akronc53cfc82020-10-19 11:00:58 +0200353
Nils Diewald5975d702015-03-09 17:45:42 +0000354 case 34: // 'Page down'
Akrone4961b12017-05-10 21:04:46 +0200355 e.halt();
Akronc53cfc82020-10-19 11:00:58 +0200356 t.pageDown();
Akrone4961b12017-05-10 21:04:46 +0200357 break;
Akronc53cfc82020-10-19 11:00:58 +0200358
Nils Diewald5975d702015-03-09 17:45:42 +0000359 case 39: // 'Right'
Akronc53cfc82020-10-19 11:00:58 +0200360 if (t._prefix.active())
Akrone4961b12017-05-10 21:04:46 +0200361 break;
Nils Diewalde8518f82015-03-18 22:41:49 +0000362
Akronc53cfc82020-10-19 11:00:58 +0200363 const item = t.liveItem(t.position);
Akrone4961b12017-05-10 21:04:46 +0200364
365 if (item["further"] !== undefined) {
366 item["further"].bind(item).apply();
367 };
368
369 e.halt();
370 break;
Nils Diewald5975d702015-03-09 17:45:42 +0000371
Akronc53cfc82020-10-19 11:00:58 +0200372 case 13: // 'Enter'
Akrone4961b12017-05-10 21:04:46 +0200373 // Click on prefix
Akronc53cfc82020-10-19 11:00:58 +0200374 if (t._prefix.active())
375 t._prefix.onclick(e);
Nils Diewald5975d702015-03-09 17:45:42 +0000376
Akrone4961b12017-05-10 21:04:46 +0200377 // Click on item
378 else
Akronc53cfc82020-10-19 11:00:58 +0200379 t.liveItem(t.position).onclick(e);
Akrone4961b12017-05-10 21:04:46 +0200380 e.halt();
381 break;
Akronc53cfc82020-10-19 11:00:58 +0200382
Nils Diewald59c02fc2015-03-07 01:29:09 +0000383 case 8: // 'Backspace'
Akronc53cfc82020-10-19 11:00:58 +0200384 t._prefix.chop();
385 t.show();
Akrone4961b12017-05-10 21:04:46 +0200386 e.halt();
387 break;
Nils Diewald47f366b2015-04-15 20:06:35 +0000388 };
389 },
Nils Diewald59c02fc2015-03-07 01:29:09 +0000390
Akronc53cfc82020-10-19 11:00:58 +0200391
Nils Diewald47f366b2015-04-15 20:06:35 +0000392 // Add characters to prefix
393 _keypress : function (e) {
Akron9c2f9382016-05-25 16:36:04 +0200394 if (e.charCode !== 0) {
Akrone4961b12017-05-10 21:04:46 +0200395 e.halt();
Akrone4961b12017-05-10 21:04:46 +0200396
397 // Add prefix
Akronc53cfc82020-10-19 11:00:58 +0200398 this._prefix.add(
399 String.fromCharCode(_codeFromEvent(e))
400 );
401
Akrone4961b12017-05-10 21:04:46 +0200402 this.show();
Akron9c2f9382016-05-25 16:36:04 +0200403 };
Nils Diewald59c02fc2015-03-07 01:29:09 +0000404 },
405
Akronc53cfc82020-10-19 11:00:58 +0200406
Akron47c086c2016-05-18 21:22:06 +0200407 /**
Akron5240b8c2016-05-20 09:17:41 +0200408 * Show a screen with a given offset
409 * in the viewport.
Akron47c086c2016-05-18 21:22:06 +0200410 */
411 screen : function (nr) {
Akronc53cfc82020-10-19 11:00:58 +0200412 const t = this;
Akrone817b882018-08-31 14:09:17 +0200413
414 // Normalize negative values
Akron3c2730f2016-05-24 15:08:29 +0200415 if (nr < 0) {
Akrone4961b12017-05-10 21:04:46 +0200416 nr = 0
Akron3c2730f2016-05-24 15:08:29 +0200417 }
Akrone817b882018-08-31 14:09:17 +0200418
419 // The shown list already shows everything
Akronc53cfc82020-10-19 11:00:58 +0200420 else if (t.liveLength() < t.limit()) {
Akrone817b882018-08-31 14:09:17 +0200421 return false;
422 }
423
424 // Move relatively to the next screen
Akronc53cfc82020-10-19 11:00:58 +0200425 else if (nr > (t.liveLength() - t.limit())) {
426 nr = (t.liveLength() - t.limit());
Akron3c2730f2016-05-24 15:08:29 +0200427 };
428
Akrone817b882018-08-31 14:09:17 +0200429 // no change
Akronc53cfc82020-10-19 11:00:58 +0200430 if (t.offset === nr)
Akrone817b882018-08-31 14:09:17 +0200431 return false;
Akron5a1f5bb2016-05-23 22:00:39 +0200432
Akronc53cfc82020-10-19 11:00:58 +0200433 t._showItems(nr);
Akrone817b882018-08-31 14:09:17 +0200434
435 return true;
Akron47c086c2016-05-18 21:22:06 +0200436 },
437
Akrone817b882018-08-31 14:09:17 +0200438
Nils Diewald2fe12e12015-03-06 16:47:06 +0000439 /**
Nils Diewald7148c6f2015-05-04 15:07:53 +0000440 * Get the associated dom element.
Nils Diewald2fe12e12015-03-06 16:47:06 +0000441 */
Nils Diewald86dad5b2015-01-28 15:09:07 +0000442 element : function () {
Akron24aa0052020-11-10 11:00:34 +0100443 return this._el;
Nils Diewald86dad5b2015-01-28 15:09:07 +0000444 },
445
Akronc53cfc82020-10-19 11:00:58 +0200446
Nils Diewald2fe12e12015-03-06 16:47:06 +0000447 /**
Nils Diewald7148c6f2015-05-04 15:07:53 +0000448 * Get the creator class for items
Nils Diewald2fe12e12015-03-06 16:47:06 +0000449 */
Nils Diewald86dad5b2015-01-28 15:09:07 +0000450 itemClass : function () {
451 return this._itemClass;
452 },
453
Akronc53cfc82020-10-19 11:00:58 +0200454
Nils Diewald86dad5b2015-01-28 15:09:07 +0000455 /**
Nils Diewald7148c6f2015-05-04 15:07:53 +0000456 * Get and set the numerical value
457 * for the maximum number of items visible.
Nils Diewald86dad5b2015-01-28 15:09:07 +0000458 */
459 limit : function (limit) {
Nils Diewald5975d702015-03-09 17:45:42 +0000460 if (arguments.length === 1) {
Akrone4961b12017-05-10 21:04:46 +0200461 if (this._limit !== limit) {
462 this._limit = limit;
463 this._slider.limit(limit).reInit();
464 };
465 return this;
Nils Diewald5975d702015-03-09 17:45:42 +0000466 };
Nils Diewald86dad5b2015-01-28 15:09:07 +0000467 return this._limit;
468 },
469
Nils Diewald7148c6f2015-05-04 15:07:53 +0000470
Nils Diewald86dad5b2015-01-28 15:09:07 +0000471 /**
472 * Upgrade this object to another object,
473 * while private data stays intact.
474 *
Nils Diewald2fe12e12015-03-06 16:47:06 +0000475 * @param {Object} An object with properties.
Nils Diewald86dad5b2015-01-28 15:09:07 +0000476 */
477 upgradeTo : function (props) {
478 for (var prop in props) {
Akrone4961b12017-05-10 21:04:46 +0200479 this[prop] = props[prop];
Nils Diewald86dad5b2015-01-28 15:09:07 +0000480 };
481 return this;
482 },
483
Nils Diewald7148c6f2015-05-04 15:07:53 +0000484
Nils Diewald86dad5b2015-01-28 15:09:07 +0000485 /**
Akron97752a72016-05-25 14:43:07 +0200486 * Filter the list and make it visible.
487 * This is always called once the prefix changes.
Nils Diewald86dad5b2015-01-28 15:09:07 +0000488 *
489 * @param {string} Prefix for filtering the list
490 */
Akron6ed13992016-05-23 18:06:05 +0200491 show : function (active) {
Akronc53cfc82020-10-19 11:00:58 +0200492 const t = this;
Nils Diewald86dad5b2015-01-28 15:09:07 +0000493
Akron5240b8c2016-05-20 09:17:41 +0200494 // show menu based on initial offset
Akronc53cfc82020-10-19 11:00:58 +0200495 t._unmark(); // Unmark everything that was marked before
496 t.removeItems();
Akron6ed13992016-05-23 18:06:05 +0200497
498 // Initialize the list
Akronc53cfc82020-10-19 11:00:58 +0200499 if (!t._initList()) {
Akron6ac58442016-05-24 16:52:29 +0200500
Akrone4961b12017-05-10 21:04:46 +0200501 // The prefix is not active
Akronc53cfc82020-10-19 11:00:58 +0200502 t._prefix.active(true);
Akron6ed13992016-05-23 18:06:05 +0200503
Akrone4961b12017-05-10 21:04:46 +0200504 // finally show the element
Akron24aa0052020-11-10 11:00:34 +0100505 t._el.classList.add('visible');
Akrone4961b12017-05-10 21:04:46 +0200506
507 return true;
Akron6ed13992016-05-23 18:06:05 +0200508 };
509
Akronc53cfc82020-10-19 11:00:58 +0200510 let offset = 0;
Nils Diewald86dad5b2015-01-28 15:09:07 +0000511
Akron9c2f9382016-05-25 16:36:04 +0200512 // Set a chosen value to active and move the viewport
Akron6ed13992016-05-23 18:06:05 +0200513 if (arguments.length === 1) {
514
Akrone4961b12017-05-10 21:04:46 +0200515 // Normalize active value
516 if (active < 0) {
517 active = 0;
518 }
Akronc53cfc82020-10-19 11:00:58 +0200519 else if (active >= t.liveLength()) {
520 active = t.liveLength() - 1;
Akrone4961b12017-05-10 21:04:46 +0200521 };
Akron6ed13992016-05-23 18:06:05 +0200522
Akrone4961b12017-05-10 21:04:46 +0200523 // Item is outside the first viewport
Akronc53cfc82020-10-19 11:00:58 +0200524 if (active >= t._limit) {
Akrone4961b12017-05-10 21:04:46 +0200525 offset = active;
Akronc53cfc82020-10-19 11:00:58 +0200526 const newOffset = t.liveLength() - t._limit;
527 if (offset > newOffset) {
528 offset = newOffset;
Akrone4961b12017-05-10 21:04:46 +0200529 };
530 };
531
Akronc53cfc82020-10-19 11:00:58 +0200532 t.position = active;
Akron6ed13992016-05-23 18:06:05 +0200533 }
534
Akron9c2f9382016-05-25 16:36:04 +0200535 // Choose the first item
Akronc53cfc82020-10-19 11:00:58 +0200536 else if (t._firstActive) {
537 t.position = 0;
Akron47c086c2016-05-18 21:22:06 +0200538 }
Akroncb351d62016-05-19 23:10:33 +0200539
Akron9c2f9382016-05-25 16:36:04 +0200540 // Choose no item
Akron47c086c2016-05-18 21:22:06 +0200541 else {
Akronc53cfc82020-10-19 11:00:58 +0200542 t.position = -1;
Akron6ed13992016-05-23 18:06:05 +0200543 };
544
Akronc53cfc82020-10-19 11:00:58 +0200545 t.offset = offset;
546 t._showItems(offset); // Show new item list
Akron6ed13992016-05-23 18:06:05 +0200547
548 // Make chosen value active
Akronc53cfc82020-10-19 11:00:58 +0200549 if (t.position !== -1) {
550 t.liveItem(t.position).active(true);
Akron6ed13992016-05-23 18:06:05 +0200551 };
Akron37513a62015-11-17 01:07:11 +0100552
Akron5240b8c2016-05-20 09:17:41 +0200553 // The prefix is not active
Akronc53cfc82020-10-19 11:00:58 +0200554 t._prefix.active(false);
Nils Diewald86dad5b2015-01-28 15:09:07 +0000555
Akron5240b8c2016-05-20 09:17:41 +0200556 // finally show the element
Akron24aa0052020-11-10 11:00:34 +0100557 t._el.classList.add('visible');
Nils Diewald2fe12e12015-03-06 16:47:06 +0000558
Nils Diewald86dad5b2015-01-28 15:09:07 +0000559 // Add classes for rolling menus
Akronc53cfc82020-10-19 11:00:58 +0200560 t._boundary(true);
Akron6bb71582016-06-10 20:41:08 +0200561
Nils Diewald59c02fc2015-03-07 01:29:09 +0000562 return true;
Nils Diewald86dad5b2015-01-28 15:09:07 +0000563 },
564
Nils Diewald7148c6f2015-05-04 15:07:53 +0000565
566 /**
567 * Hide the menu and call the onHide callback.
568 */
Nils Diewald2fe12e12015-03-06 16:47:06 +0000569 hide : function () {
Akronc53cfc82020-10-19 11:00:58 +0200570 if (!this.dontHide) {
571 this.removeItems();
572 this._prefix.clear();
573 this.onHide();
Akron24aa0052020-11-10 11:00:34 +0100574 this._el.classList.remove('visible');
Akronc53cfc82020-10-19 11:00:58 +0200575 }
Akron24aa0052020-11-10 11:00:34 +0100576 // this._el.blur();
Nils Diewald86dad5b2015-01-28 15:09:07 +0000577 },
578
Akronc53cfc82020-10-19 11:00:58 +0200579
Nils Diewald7148c6f2015-05-04 15:07:53 +0000580 /**
581 * Function released when the menu hides.
582 * This method is expected to be overridden.
583 */
Nils Diewald5c5a7472015-04-02 22:13:38 +0000584 onHide : function () {},
585
Akronc53cfc82020-10-19 11:00:58 +0200586
Nils Diewald86dad5b2015-01-28 15:09:07 +0000587 /**
588 * Get the prefix for filtering,
Nils Diewald2fe12e12015-03-06 16:47:06 +0000589 * e.g. &quot;ve&quot; for &quot;verb&quot;
Nils Diewald86dad5b2015-01-28 15:09:07 +0000590 */
Nils Diewald5975d702015-03-09 17:45:42 +0000591 prefix : function (pref) {
592 if (arguments.length === 1) {
Akrone4961b12017-05-10 21:04:46 +0200593 this._prefix.value(pref);
594 return this;
Nils Diewald5975d702015-03-09 17:45:42 +0000595 };
596 return this._prefix.value();
Nils Diewald86dad5b2015-01-28 15:09:07 +0000597 },
598
Akronc53cfc82020-10-19 11:00:58 +0200599
Akronc7448732016-04-27 14:06:58 +0200600 /**
601 * Get the lengthField object.
602 */
603 lengthField : function () {
604 return this._lengthField;
605 },
Nils Diewald7148c6f2015-05-04 15:07:53 +0000606
Akronc53cfc82020-10-19 11:00:58 +0200607
Akron5240b8c2016-05-20 09:17:41 +0200608 /**
609 * Get the associated slider object.
610 */
611 slider : function () {
612 return this._slider;
Nils Diewald86dad5b2015-01-28 15:09:07 +0000613 },
614
Akron5240b8c2016-05-20 09:17:41 +0200615
Nils Diewald86dad5b2015-01-28 15:09:07 +0000616 /**
617 * Delete all visible items from the menu element
618 */
Leo Reppe5bd35a2021-05-05 15:28:26 +0200619
620 removeItems : function () {
621 const liElements=this._el.getElementsByTagName("LI");
622 while (liElements.length>0){
623 this._el.removeChild(liElements[0]);
Nils Diewald5975d702015-03-09 17:45:42 +0000624 };
Leo Reppe5bd35a2021-05-05 15:28:26 +0200625 },
Nils Diewald86dad5b2015-01-28 15:09:07 +0000626
Akronc53cfc82020-10-19 11:00:58 +0200627
Nils Diewald2fe12e12015-03-06 16:47:06 +0000628 /**
629 * Get a specific item from the complete list
630 *
631 * @param {number} index of the list item
632 */
633 item : function (index) {
634 return this._items[index]
635 },
636
637
Nils Diewald86dad5b2015-01-28 15:09:07 +0000638 /**
639 * Get a specific item from the filtered list
640 *
641 * @param {number} index of the list item
Nils Diewald2fe12e12015-03-06 16:47:06 +0000642 * in the filtered list
Nils Diewald86dad5b2015-01-28 15:09:07 +0000643 */
644 liveItem : function (index) {
645 if (this._list === undefined)
Akrone4961b12017-05-10 21:04:46 +0200646 if (!this._initList())
647 return;
Nils Diewald86dad5b2015-01-28 15:09:07 +0000648
649 return this._items[this._list[index]];
650 },
651
Nils Diewald86dad5b2015-01-28 15:09:07 +0000652
653 /**
Akron5240b8c2016-05-20 09:17:41 +0200654 * Get a specific item from the viewport list
Nils Diewald86dad5b2015-01-28 15:09:07 +0000655 *
656 * @param {number} index of the list item
Nils Diewald2fe12e12015-03-06 16:47:06 +0000657 * in the visible list
Nils Diewald86dad5b2015-01-28 15:09:07 +0000658 */
659 shownItem : function (index) {
660 if (index >= this.limit())
Akrone4961b12017-05-10 21:04:46 +0200661 return;
Akronc53cfc82020-10-19 11:00:58 +0200662
Akron9c4d1ae2016-05-25 21:43:22 +0200663 return this.liveItem(this.offset + index);
Nils Diewald86dad5b2015-01-28 15:09:07 +0000664 },
665
666
Nils Diewald2fe12e12015-03-06 16:47:06 +0000667 /**
Akron9c4d1ae2016-05-25 21:43:22 +0200668 * Get the length of the full item list
Nils Diewald2fe12e12015-03-06 16:47:06 +0000669 */
670 length : function () {
671 return this._items.length;
672 },
673
674
675 /**
Akron5240b8c2016-05-20 09:17:41 +0200676 * Length of the filtered item list.
677 */
678 liveLength : function () {
679 if (this._list === undefined)
Akrone4961b12017-05-10 21:04:46 +0200680 this._initList();
Akron5240b8c2016-05-20 09:17:41 +0200681 return this._list.length;
682 },
683
Akrone817b882018-08-31 14:09:17 +0200684
Akron5240b8c2016-05-20 09:17:41 +0200685 /**
Nils Diewald2fe12e12015-03-06 16:47:06 +0000686 * Make the next item in the filtered menu active
Nils Diewald86dad5b2015-01-28 15:09:07 +0000687 */
688 next : function () {
Akronc53cfc82020-10-19 11:00:58 +0200689 const t = this;
Nils Diewald5975d702015-03-09 17:45:42 +0000690
Akronb38afb22016-05-25 19:30:01 +0200691 // No list
Akronc53cfc82020-10-19 11:00:58 +0200692 if (t.liveLength() === 0)
Akrone4961b12017-05-10 21:04:46 +0200693 return;
Akronb38afb22016-05-25 19:30:01 +0200694
Akron9c4d1ae2016-05-25 21:43:22 +0200695 // Deactivate old item
Akronc53cfc82020-10-19 11:00:58 +0200696 if (t.position !== -1 && !t._prefix.active()) {
697 t.liveItem(t.position).active(false);
Akron9c4d1ae2016-05-25 21:43:22 +0200698 };
Nils Diewalde8518f82015-03-18 22:41:49 +0000699
Akron9c4d1ae2016-05-25 21:43:22 +0200700 // Get new active item
Akronc53cfc82020-10-19 11:00:58 +0200701 t.position++;
702 let newItem = t.liveItem(t.position);
Nils Diewald86dad5b2015-01-28 15:09:07 +0000703
Nils Diewald5975d702015-03-09 17:45:42 +0000704 // The next element is undefined - roll to top or to prefix
Nils Diewald86dad5b2015-01-28 15:09:07 +0000705 if (newItem === undefined) {
Nils Diewald5975d702015-03-09 17:45:42 +0000706
Akrone4961b12017-05-10 21:04:46 +0200707 // Activate prefix
Akronc53cfc82020-10-19 11:00:58 +0200708 const prefix = this._prefix;
Nils Diewald5975d702015-03-09 17:45:42 +0000709
Akrone4961b12017-05-10 21:04:46 +0200710 // Prefix is set and not active - choose!
711 if (prefix.isSet() && !prefix.active()) {
Akronc53cfc82020-10-19 11:00:58 +0200712 t.position--;
Akrone4961b12017-05-10 21:04:46 +0200713 prefix.active(true);
714 return;
715 }
Akron9c4d1ae2016-05-25 21:43:22 +0200716
Akrone4961b12017-05-10 21:04:46 +0200717 // Choose first item
718 else {
Akronc53cfc82020-10-19 11:00:58 +0200719 newItem = t.liveItem(0);
Akrone4961b12017-05-10 21:04:46 +0200720 // choose first item
Akronc53cfc82020-10-19 11:00:58 +0200721 t.position = 0;
722 t._showItems(0);
Akrone4961b12017-05-10 21:04:46 +0200723 };
Nils Diewald86dad5b2015-01-28 15:09:07 +0000724 }
725
Akron5a1f5bb2016-05-23 22:00:39 +0200726 // The next element is after the viewport - roll down
Akronc53cfc82020-10-19 11:00:58 +0200727 else if (t.position >= (t.limit() + t.offset)) {
728 t.screen(t.position - t.limit() + 1);
Akron5a1f5bb2016-05-23 22:00:39 +0200729 }
730
731 // The next element is before the viewport - roll up
Akronc53cfc82020-10-19 11:00:58 +0200732 else if (t.position <= t.offset) {
733 t.screen(t.position);
Nils Diewald86dad5b2015-01-28 15:09:07 +0000734 };
Nils Diewald5975d702015-03-09 17:45:42 +0000735
Akronc53cfc82020-10-19 11:00:58 +0200736 t._prefix.active(false);
Nils Diewald86dad5b2015-01-28 15:09:07 +0000737 newItem.active(true);
738 },
739
Akronc53cfc82020-10-19 11:00:58 +0200740
Nils Diewalde8518f82015-03-18 22:41:49 +0000741 /*
Nils Diewald86dad5b2015-01-28 15:09:07 +0000742 * Make the previous item in the menu active
743 */
Nils Diewald86dad5b2015-01-28 15:09:07 +0000744 prev : function () {
Akronc53cfc82020-10-19 11:00:58 +0200745 const t = this;
Nils Diewald2d210752015-03-09 19:01:15 +0000746
Akronb38afb22016-05-25 19:30:01 +0200747 // No list
Akronc53cfc82020-10-19 11:00:58 +0200748 if (t.liveLength() === 0)
Akrone4961b12017-05-10 21:04:46 +0200749 return;
Akronb38afb22016-05-25 19:30:01 +0200750
Akron9c4d1ae2016-05-25 21:43:22 +0200751 // Deactivate old item
Akronc53cfc82020-10-19 11:00:58 +0200752 if (!t._prefix.active()) {
Akron9c4d1ae2016-05-25 21:43:22 +0200753
Akrone4961b12017-05-10 21:04:46 +0200754 // No active element set
Akronc53cfc82020-10-19 11:00:58 +0200755 if (t.position === -1) {
756 t.position = t.liveLength();
Akrone4961b12017-05-10 21:04:46 +0200757 }
Akron9c4d1ae2016-05-25 21:43:22 +0200758
Akrone4961b12017-05-10 21:04:46 +0200759 // No active element set
760 else {
Akronc53cfc82020-10-19 11:00:58 +0200761 t.liveItem(t.position--).active(false);
Akrone4961b12017-05-10 21:04:46 +0200762 };
Nils Diewald2d210752015-03-09 19:01:15 +0000763 };
764
Akron9c4d1ae2016-05-25 21:43:22 +0200765 // Get new active item
Akronc53cfc82020-10-19 11:00:58 +0200766 let newItem = t.liveItem(t.position);
Nils Diewald86dad5b2015-01-28 15:09:07 +0000767
768 // The previous element is undefined - roll to bottom
769 if (newItem === undefined) {
Nils Diewald5975d702015-03-09 17:45:42 +0000770
Akrone4961b12017-05-10 21:04:46 +0200771 // Activate prefix
Akronc53cfc82020-10-19 11:00:58 +0200772 const prefix = t._prefix;
773 let offset = t.liveLength() - t.limit();
Akrone4961b12017-05-10 21:04:46 +0200774
775 // Normalize offset
776 offset = offset < 0 ? 0 : offset;
Nils Diewald2d210752015-03-09 19:01:15 +0000777
Akrone4961b12017-05-10 21:04:46 +0200778 // Choose the last item
Akronc53cfc82020-10-19 11:00:58 +0200779 t.position = t.liveLength() - 1;
Akrone4961b12017-05-10 21:04:46 +0200780
781 // Prefix is set and not active - choose!
782 if (prefix.isSet() && !prefix.active()) {
Akronc53cfc82020-10-19 11:00:58 +0200783 t.position++;
Akrone4961b12017-05-10 21:04:46 +0200784 prefix.active(true);
Akronc53cfc82020-10-19 11:00:58 +0200785 t.offset = offset;
Akrone4961b12017-05-10 21:04:46 +0200786 return;
787 }
Nils Diewald2d210752015-03-09 19:01:15 +0000788
Akrone4961b12017-05-10 21:04:46 +0200789 // Choose last item
790 else {
Akronc53cfc82020-10-19 11:00:58 +0200791 newItem = t.liveItem(t.position);
792 t._showItems(offset);
Akrone4961b12017-05-10 21:04:46 +0200793 };
Nils Diewald86dad5b2015-01-28 15:09:07 +0000794 }
795
Akron5a1f5bb2016-05-23 22:00:39 +0200796 // The previous element is before the view - roll up
Akronc53cfc82020-10-19 11:00:58 +0200797 else if (t.position < t.offset) {
798 t.screen(t.position);
Akron5a1f5bb2016-05-23 22:00:39 +0200799 }
800
801 // The previous element is after the view - roll down
Akronc53cfc82020-10-19 11:00:58 +0200802 else if (t.position >= (t.limit() + t.offset)) {
803 t.screen(t.position - t.limit() + 2);
Nils Diewald86dad5b2015-01-28 15:09:07 +0000804 };
Nils Diewald2fe12e12015-03-06 16:47:06 +0000805
Akronc53cfc82020-10-19 11:00:58 +0200806 t._prefix.active(false);
Nils Diewald86dad5b2015-01-28 15:09:07 +0000807 newItem.active(true);
808 },
Nils Diewald86dad5b2015-01-28 15:09:07 +0000809
Akronc53cfc82020-10-19 11:00:58 +0200810
Akron3c2730f2016-05-24 15:08:29 +0200811 /**
812 * Move the page up by limit!
813 */
814 pageUp : function () {
Akron9c4d1ae2016-05-25 21:43:22 +0200815 this.screen(this.offset - this.limit());
Akron3c2730f2016-05-24 15:08:29 +0200816 },
817
818
819 /**
820 * Move the page down by limit!
821 */
822 pageDown : function () {
Akron9c4d1ae2016-05-25 21:43:22 +0200823 this.screen(this.offset + this.limit());
Akron3c2730f2016-05-24 15:08:29 +0200824 },
825
Nils Diewald86dad5b2015-01-28 15:09:07 +0000826
Akrone817b882018-08-31 14:09:17 +0200827 /**
828 * Move the view one item up
829 */
830 viewUp : function () {
831 this.screen(this.offset - 1);
832 },
833
834
835 /**
836 * Move the view one item down
837 */
838 viewDown : function () {
839 this.screen(this.offset + 1);
840 },
841
Akronc53cfc82020-10-19 11:00:58 +0200842
Akron5240b8c2016-05-20 09:17:41 +0200843 // Unmark all items
844 _unmark : function () {
Akron678c26f2020-10-09 08:52:50 +0200845 this._list.forEach(function(it){
Akronc53cfc82020-10-19 11:00:58 +0200846 const item = this._items[it];
Akrone4961b12017-05-10 21:04:46 +0200847 item.lowlight();
Akron678c26f2020-10-09 08:52:50 +0200848 item.active(false);
849 }, this);
Akron5240b8c2016-05-20 09:17:41 +0200850 },
851
Akronc53cfc82020-10-19 11:00:58 +0200852
Akron5240b8c2016-05-20 09:17:41 +0200853 // Set boundary for viewport
854 _boundary : function (bool) {
Akron55a343b2018-04-06 19:57:36 +0200855 if (this._list.length === 0)
856 return;
Akronc53cfc82020-10-19 11:00:58 +0200857
Akron5240b8c2016-05-20 09:17:41 +0200858 this.item(this._list[0]).noMore(bool);
859 this.item(this._list[this._list.length - 1]).noMore(bool);
860 },
861
862
863 // Append Items that should be shown
864 _showItems : function (off) {
Akronc53cfc82020-10-19 11:00:58 +0200865 const t = this;
Akron5240b8c2016-05-20 09:17:41 +0200866
Akrona92fd8d2016-05-24 21:13:41 +0200867 // optimization: scroll down one step
Akronc53cfc82020-10-19 11:00:58 +0200868 if (t.offset === (off - 1)) {
869 t.offset = off;
Akron9c4d1ae2016-05-25 21:43:22 +0200870
Akrone4961b12017-05-10 21:04:46 +0200871 // Remove the HTML node from the first item
872 // leave lengthField/prefix/slider
Akron24aa0052020-11-10 11:00:34 +0100873 t._el.removeChild(t._el.children[3]);
Akronc53cfc82020-10-19 11:00:58 +0200874
875 t._append(
876 t._list[t.offset + t.limit() - 1]
877 );
Akrona92fd8d2016-05-24 21:13:41 +0200878 }
Akron5240b8c2016-05-20 09:17:41 +0200879
Akrona92fd8d2016-05-24 21:13:41 +0200880 // optimization: scroll up one step
Akronc53cfc82020-10-19 11:00:58 +0200881 else if (t.offset === (off + 1)) {
882 t.offset = off;
Akron9c4d1ae2016-05-25 21:43:22 +0200883
Akrone4961b12017-05-10 21:04:46 +0200884 // Remove the HTML node from the last item
Akron24aa0052020-11-10 11:00:34 +0100885 t._el.removeChild(t._el.lastChild);
Akron9c4d1ae2016-05-25 21:43:22 +0200886
Akronc53cfc82020-10-19 11:00:58 +0200887 t._prepend(t._list[t.offset]);
Akrona92fd8d2016-05-24 21:13:41 +0200888 }
Akronc53cfc82020-10-19 11:00:58 +0200889
Akrona92fd8d2016-05-24 21:13:41 +0200890 else {
Akronc53cfc82020-10-19 11:00:58 +0200891 t.offset = off;
Akron5240b8c2016-05-20 09:17:41 +0200892
Akrone4961b12017-05-10 21:04:46 +0200893 // Remove all items
Akronc53cfc82020-10-19 11:00:58 +0200894 t.removeItems();
Akron5240b8c2016-05-20 09:17:41 +0200895
Akrone4961b12017-05-10 21:04:46 +0200896 // Use list
Akronc53cfc82020-10-19 11:00:58 +0200897 let shown = 0;
Akron5240b8c2016-05-20 09:17:41 +0200898
Akronc53cfc82020-10-19 11:00:58 +0200899 for (let i = 0; i < t._list.length; i++) {
Akrona92fd8d2016-05-24 21:13:41 +0200900
Akrone4961b12017-05-10 21:04:46 +0200901 // Don't show - it's before offset
902 shown++;
903 if (shown <= off)
904 continue;
Akrona92fd8d2016-05-24 21:13:41 +0200905
Akronc53cfc82020-10-19 11:00:58 +0200906 t._append(t._list[i]);
Akrone4961b12017-05-10 21:04:46 +0200907
Akronc53cfc82020-10-19 11:00:58 +0200908 if (shown >= (t.limit() + off))
Akrone4961b12017-05-10 21:04:46 +0200909 break;
910 };
Akron5240b8c2016-05-20 09:17:41 +0200911 };
912
913 // set the slider to the new offset
Akronc53cfc82020-10-19 11:00:58 +0200914 t._slider.offset(t.offset);
Akron5240b8c2016-05-20 09:17:41 +0200915 },
916
917
918 // Append item to the shown list based on index
919 _append : function (i) {
Akronc53cfc82020-10-19 11:00:58 +0200920 const item = this.item(i);
Akron5240b8c2016-05-20 09:17:41 +0200921
922 // Highlight based on prefix
Akrona92fd8d2016-05-24 21:13:41 +0200923 if (this.prefix().length > 0) {
Akrone4961b12017-05-10 21:04:46 +0200924 item.highlight(this.prefix().toLowerCase());
Akrona92fd8d2016-05-24 21:13:41 +0200925 };
926
Akron5240b8c2016-05-20 09:17:41 +0200927 // Append element
928 this.element().appendChild(item.element());
929 },
930
931
932 // Prepend item to the shown list based on index
933 _prepend : function (i) {
Akronc53cfc82020-10-19 11:00:58 +0200934 const item = this.item(i);
Akron5240b8c2016-05-20 09:17:41 +0200935
936 // Highlight based on prefix
Akrona92fd8d2016-05-24 21:13:41 +0200937 if (this.prefix().length > 0) {
Akrone4961b12017-05-10 21:04:46 +0200938 item.highlight(this.prefix().toLowerCase());
Akrona92fd8d2016-05-24 21:13:41 +0200939 };
Akron5240b8c2016-05-20 09:17:41 +0200940
Akronc53cfc82020-10-19 11:00:58 +0200941 const e = this.element();
Akron9c4d1ae2016-05-25 21:43:22 +0200942
Akron5240b8c2016-05-20 09:17:41 +0200943 // Append element after lengthField/prefix/slider
944 e.insertBefore(
Akrone4961b12017-05-10 21:04:46 +0200945 item.element(),
946 e.children[3]
Akron5240b8c2016-05-20 09:17:41 +0200947 );
Nils Diewald2fe12e12015-03-06 16:47:06 +0000948 }
Nils Diewald86dad5b2015-01-28 15:09:07 +0000949 };
Nils Diewald0e6992a2015-04-14 20:13:52 +0000950});