blob: 0c84c4862b7a765a836a9bb234aab4567a41a688 [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) {
Akron7524be12016-06-01 17:31:33 +020058 if (params === undefined)
Akrone4961b12017-05-10 21:04:46 +020059 params = {};
Akron7524be12016-06-01 17:31:33 +020060
Akronc53cfc82020-10-19 11:00:58 +020061 const t = this;
Leo Repp56904d22021-04-26 15:53:22 +020062 t._notItemElements=3;
Akronc53cfc82020-10-19 11:00:58 +020063
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
Leo Repp56904d22021-04-26 15:53:22 +0200130 t._items = new Array(); //all childNodes, i.e. ItemClass, prefixClass
Akron5240b8c2016-05-20 09:17:41 +0200131
Akronc53cfc82020-10-19 11:00:58 +0200132 t.readItems(list);
Akroneaba63e2018-01-26 19:49:30 +0100133
Akronc53cfc82020-10-19 11:00:58 +0200134 t.dontHide = false;
hebastaf95226b2019-09-19 11:37:00 +0200135
Akronc53cfc82020-10-19 11:00:58 +0200136 return t;
Akroneaba63e2018-01-26 19:49:30 +0100137 },
138
139 // Read items to add to list
140 readItems : function (list) {
Akronc53cfc82020-10-19 11:00:58 +0200141 const t = this;
Akroneaba63e2018-01-26 19:49:30 +0100142
Leo Repp56904d22021-04-26 15:53:22 +0200143 t._list = undefined; //filtered List containing all itemClass items
Akroneaba63e2018-01-26 19:49:30 +0100144
145 // Remove circular reference to "this" in items
Akronc53cfc82020-10-19 11:00:58 +0200146 for (let i = 0; i < t._items.length; i++) {
147 delete t._items[i]["_menu"];
148 delete t._items[i];
Akroneaba63e2018-01-26 19:49:30 +0100149 };
150
Akronc53cfc82020-10-19 11:00:58 +0200151 t._items = new Array();
152 t.removeItems();
Akroneaba63e2018-01-26 19:49:30 +0100153
154
155 // Initialize items
Akronc53cfc82020-10-19 11:00:58 +0200156 t._lengthField.reset();
Akroneaba63e2018-01-26 19:49:30 +0100157
Akron5240b8c2016-05-20 09:17:41 +0200158 // Initialize item list based on parameters
Akron678c26f2020-10-09 08:52:50 +0200159 list.forEach(function(i){
Akronc53cfc82020-10-19 11:00:58 +0200160 const obj = this._itemClass.create(i);
Akron5240b8c2016-05-20 09:17:41 +0200161
Akrone4961b12017-05-10 21:04:46 +0200162 // This may become circular
163 obj["_menu"] = this;
Akron678c26f2020-10-09 08:52:50 +0200164 this._lengthField.add(i);
Akrone4961b12017-05-10 21:04:46 +0200165 this._items.push(obj);
Akronc53cfc82020-10-19 11:00:58 +0200166 }, t);
Akron5240b8c2016-05-20 09:17:41 +0200167
Akronc53cfc82020-10-19 11:00:58 +0200168 t._slider.length(t.liveLength())
169 .limit(t._limit)
Akrone4961b12017-05-10 21:04:46 +0200170 .reInit();
171
Akronc53cfc82020-10-19 11:00:58 +0200172 t._firstActive = false;
Akroneaba63e2018-01-26 19:49:30 +0100173 // Show the first item active always?
Akronc53cfc82020-10-19 11:00:58 +0200174 t.offset = 0;
175 t.position = 0;
Akron5240b8c2016-05-20 09:17:41 +0200176 },
Akron8aa1c522021-07-23 11:33:49 +0200177
178 // Append item to list
179 append : function (item) {
180 const t = this;
181 // This is cyclic!
182 item["_menu"] = t;
183 t._list = undefined;
184 t.removeItems();
185 t._items.push(item);
186 t._lengthField.add([item.content().data]);
187 t._slider.length(t.liveLength()).reInit();
188 t._firstActive = false;
189 t.offset = 0;
190 t.position = 0;
191 },
Akroneaba63e2018-01-26 19:49:30 +0100192
Akron5240b8c2016-05-20 09:17:41 +0200193 // Initialize the item list
194 _initList : function () {
Leo Repp56904d22021-04-26 15:53:22 +0200195 // Upon change also update alwaysmenu.js please
Akronc53cfc82020-10-19 11:00:58 +0200196 const t = this;
Akron5240b8c2016-05-20 09:17:41 +0200197
198 // Create a new list
Akronc53cfc82020-10-19 11:00:58 +0200199 if (t._list === undefined) {
200 t._list = [];
Akron5240b8c2016-05-20 09:17:41 +0200201 }
Akronc53cfc82020-10-19 11:00:58 +0200202 else if (t._list.length !== 0) {
203 t._boundary(false);
204 t._list.length = 0;
Akron5240b8c2016-05-20 09:17:41 +0200205 };
206
207 // Offset is initially zero
Akronc53cfc82020-10-19 11:00:58 +0200208 t.offset = 0;
Akron5240b8c2016-05-20 09:17:41 +0200209
210 // There is no prefix set
Akronc53cfc82020-10-19 11:00:58 +0200211 if (t.prefix().length <= 0) {
Akron5240b8c2016-05-20 09:17:41 +0200212
Akrone4961b12017-05-10 21:04:46 +0200213 // add all items to the list and lowlight
Akronb50964a2020-10-12 11:44:37 +0200214 let i = 0;
Akronc53cfc82020-10-19 11:00:58 +0200215 for (; i < t._items.length; i++) {
216 t._list.push(i);
217 t._items[i].lowlight();
Akrone4961b12017-05-10 21:04:46 +0200218 };
Akron5240b8c2016-05-20 09:17:41 +0200219
Akronc53cfc82020-10-19 11:00:58 +0200220 t._slider.length(i).reInit();
Akron97752a72016-05-25 14:43:07 +0200221
Akrone4961b12017-05-10 21:04:46 +0200222 return true;
Akron5240b8c2016-05-20 09:17:41 +0200223 };
224
225 /*
226 * There is a prefix set, so filter the list!
227 */
Akronc53cfc82020-10-19 11:00:58 +0200228 let pos;
229 const prefixList = t.prefix().toLowerCase().split(" ");
Akronacffc652017-12-18 21:21:25 +0100230
Akronc53cfc82020-10-19 11:00:58 +0200231 const items = [];
232 let maxPoints = 1; // minimum 1
Akron5240b8c2016-05-20 09:17:41 +0200233
234 // Iterate over all items and choose preferred matching items
235 // i.e. the matching happens at the word start
Akronc53cfc82020-10-19 11:00:58 +0200236 t._items.forEach(function(it, pos){
Akronacffc652017-12-18 21:21:25 +0100237
Akronb50964a2020-10-12 11:44:37 +0200238 let points = 0;
Akronacffc652017-12-18 21:21:25 +0100239
Akronb50964a2020-10-12 11:44:37 +0200240 prefixList.forEach(function(p) {
Akronacffc652017-12-18 21:21:25 +0100241
242 // Check if it matches at the beginning
Akronb50964a2020-10-12 11:44:37 +0200243 if ((it.lcField().includes(" " + p))) {
Akronacffc652017-12-18 21:21:25 +0100244 points += 5;
245 }
246
247 // Check if it matches anywhere
Akronb50964a2020-10-12 11:44:37 +0200248 else if (it.lcField().includes(p)) {
Akronacffc652017-12-18 21:21:25 +0100249 points += 1;
250 };
Akronb50964a2020-10-12 11:44:37 +0200251 });
Akronacffc652017-12-18 21:21:25 +0100252
253 if (points > maxPoints) {
254 this._list = [pos];
255 maxPoints = points;
256 }
257 else if (points == maxPoints) {
Akrone4961b12017-05-10 21:04:46 +0200258 this._list.push(pos);
Akronacffc652017-12-18 21:21:25 +0100259 }
Akronc53cfc82020-10-19 11:00:58 +0200260 }, t);
Akron5240b8c2016-05-20 09:17:41 +0200261
Akronc53cfc82020-10-19 11:00:58 +0200262 t._slider.length(t._list.length).reInit();
Akron6ed13992016-05-23 18:06:05 +0200263
Akron5240b8c2016-05-20 09:17:41 +0200264 // Filter was successful - yeah!
Akronc53cfc82020-10-19 11:00:58 +0200265 return t._list.length > 0 ? true : false;
Akron5240b8c2016-05-20 09:17:41 +0200266 },
267
268
Nils Diewald6e43ffd2015-03-25 18:55:39 +0000269 /**
270 * Destroy this menu
271 * (in case you don't trust the
272 * mark and sweep GC)!
273 */
274 destroy : function () {
Leo Repp56904d22021-04-26 15:53:22 +0200275 // Upon change also update alwaysmenu.js please
Akronc53cfc82020-10-19 11:00:58 +0200276 const t = this;
Akron47c086c2016-05-18 21:22:06 +0200277
Akron5240b8c2016-05-20 09:17:41 +0200278 // Remove circular reference to "this" in menu
Akron24aa0052020-11-10 11:00:34 +0100279 if (t._el != undefined)
280 delete t._el["menu"];
Nils Diewald6e43ffd2015-03-25 18:55:39 +0000281
Akron5240b8c2016-05-20 09:17:41 +0200282 // Remove circular reference to "this" in items
Akronc53cfc82020-10-19 11:00:58 +0200283 t._items.forEach(function(i) {
Akron678c26f2020-10-09 08:52:50 +0200284 delete i["_menu"];
285 });
Akron5240b8c2016-05-20 09:17:41 +0200286
287 // Remove circular reference to "this" in prefix
Akronc53cfc82020-10-19 11:00:58 +0200288 delete t._prefix['_menu'];
289 delete t._lengthField['_menu'];
290 delete t._slider['_menu'];
Nils Diewald6e43ffd2015-03-25 18:55:39 +0000291 },
292
Nils Diewald7148c6f2015-05-04 15:07:53 +0000293
294 /**
295 * Focus on this menu.
296 */
Nils Diewald2fe12e12015-03-06 16:47:06 +0000297 focus : function () {
Akron24aa0052020-11-10 11:00:34 +0100298 this._el.focus();
Nils Diewald2fe12e12015-03-06 16:47:06 +0000299 },
300
Nils Diewald7148c6f2015-05-04 15:07:53 +0000301
Nils Diewald59c02fc2015-03-07 01:29:09 +0000302 // mouse wheel treatment
303 _mousewheel : function (e) {
Akronc53cfc82020-10-19 11:00:58 +0200304 const delta = e.deltaY / 120;
Nils Diewald5975d702015-03-09 17:45:42 +0000305 if (delta > 0)
Akrone4961b12017-05-10 21:04:46 +0200306 this.next();
Nils Diewald5975d702015-03-09 17:45:42 +0000307 else if (delta < 0)
Akrone4961b12017-05-10 21:04:46 +0200308 this.prev();
Nils Diewald59c02fc2015-03-07 01:29:09 +0000309 e.halt();
310 },
311
Akronc53cfc82020-10-19 11:00:58 +0200312
Akrona1159ff2018-07-22 13:28:31 +0200313 // touchmove treatment
314 _touch : function (e) {
Akronc53cfc82020-10-19 11:00:58 +0200315 const s = this.slider();
316
Akrona1159ff2018-07-22 13:28:31 +0200317 if (e.type === "touchstart") {
Akronc53cfc82020-10-19 11:00:58 +0200318 this._lastTouch = e.touches[0].clientY;
Akrona1159ff2018-07-22 13:28:31 +0200319 }
320 else if (e.type === "touchend") {
Akrona1159ff2018-07-22 13:28:31 +0200321 this._lastTouch = undefined;
Akrona1159ff2018-07-22 13:28:31 +0200322 }
323 else if (e.type === "touchmove") {
Akronc53cfc82020-10-19 11:00:58 +0200324 const to = e.touches[0];
Akrone817b882018-08-31 14:09:17 +0200325
326 // TODO:
327 // Instead of using 26px, choose the item height
328 // or use the menu height // shownItems
329
Akrona1159ff2018-07-22 13:28:31 +0200330 // s.movetoRel(t.clientY - this._initTouch);
Akronc53cfc82020-10-19 11:00:58 +0200331 if ((this._lastTouch + 26) < to.clientY) {
Akrone817b882018-08-31 14:09:17 +0200332 this.viewDown();
Akronc53cfc82020-10-19 11:00:58 +0200333 this._lastTouch = to.clientY;
Akrona1159ff2018-07-22 13:28:31 +0200334 }
Akronc53cfc82020-10-19 11:00:58 +0200335 else if ((this._lastTouch - 26) > to.clientY) {
Akrone817b882018-08-31 14:09:17 +0200336 this.viewUp();
Akronc53cfc82020-10-19 11:00:58 +0200337 this._lastTouch = to.clientY;
Akrona1159ff2018-07-22 13:28:31 +0200338 }
339 e.halt();
340 };
341 },
Nils Diewald7148c6f2015-05-04 15:07:53 +0000342
Nils Diewald59c02fc2015-03-07 01:29:09 +0000343 // Arrow key and prefix treatment
Nils Diewald47f366b2015-04-15 20:06:35 +0000344 _keydown : function (e) {
Leo Repp56904d22021-04-26 15:53:22 +0200345 //Upon change also update alwaysmenu.js please
Akronc53cfc82020-10-19 11:00:58 +0200346 const t = this;
Nils Diewald59c02fc2015-03-07 01:29:09 +0000347
Akronc53cfc82020-10-19 11:00:58 +0200348 switch (_codeFromEvent(e)) {
349
Nils Diewald59c02fc2015-03-07 01:29:09 +0000350 case 27: // 'Esc'
Akrone4961b12017-05-10 21:04:46 +0200351 e.halt();
Akronc53cfc82020-10-19 11:00:58 +0200352 t.hide();
Akrone4961b12017-05-10 21:04:46 +0200353 break;
Nils Diewald5975d702015-03-09 17:45:42 +0000354
Nils Diewald59c02fc2015-03-07 01:29:09 +0000355 case 38: // 'Up'
Akrone4961b12017-05-10 21:04:46 +0200356 e.halt();
Akronc53cfc82020-10-19 11:00:58 +0200357 t.prev();
Akrone4961b12017-05-10 21:04:46 +0200358 break;
Akronc53cfc82020-10-19 11:00:58 +0200359
Nils Diewald5975d702015-03-09 17:45:42 +0000360 case 33: // 'Page up'
Akrone4961b12017-05-10 21:04:46 +0200361 e.halt();
Akronc53cfc82020-10-19 11:00:58 +0200362 t.pageUp();
Akrone4961b12017-05-10 21:04:46 +0200363 break;
Akronc53cfc82020-10-19 11:00:58 +0200364
Nils Diewald5975d702015-03-09 17:45:42 +0000365 case 40: // 'Down'
Akrone4961b12017-05-10 21:04:46 +0200366 e.halt();
Akronc53cfc82020-10-19 11:00:58 +0200367 t.next();
Akrone4961b12017-05-10 21:04:46 +0200368 break;
Akronc53cfc82020-10-19 11:00:58 +0200369
Nils Diewald5975d702015-03-09 17:45:42 +0000370 case 34: // 'Page down'
Akrone4961b12017-05-10 21:04:46 +0200371 e.halt();
Akronc53cfc82020-10-19 11:00:58 +0200372 t.pageDown();
Akrone4961b12017-05-10 21:04:46 +0200373 break;
Akronc53cfc82020-10-19 11:00:58 +0200374
Nils Diewald5975d702015-03-09 17:45:42 +0000375 case 39: // 'Right'
Akronc53cfc82020-10-19 11:00:58 +0200376 if (t._prefix.active())
Akrone4961b12017-05-10 21:04:46 +0200377 break;
Nils Diewalde8518f82015-03-18 22:41:49 +0000378
Akronc53cfc82020-10-19 11:00:58 +0200379 const item = t.liveItem(t.position);
Akrone4961b12017-05-10 21:04:46 +0200380
381 if (item["further"] !== undefined) {
382 item["further"].bind(item).apply();
383 };
384
385 e.halt();
386 break;
Nils Diewald5975d702015-03-09 17:45:42 +0000387
Akronc53cfc82020-10-19 11:00:58 +0200388 case 13: // 'Enter'
Akrone4961b12017-05-10 21:04:46 +0200389 // Click on prefix
Akronc53cfc82020-10-19 11:00:58 +0200390 if (t._prefix.active())
391 t._prefix.onclick(e);
Nils Diewald5975d702015-03-09 17:45:42 +0000392
Akrone4961b12017-05-10 21:04:46 +0200393 // Click on item
394 else
Akronc53cfc82020-10-19 11:00:58 +0200395 t.liveItem(t.position).onclick(e);
Akrone4961b12017-05-10 21:04:46 +0200396 e.halt();
397 break;
Akronc53cfc82020-10-19 11:00:58 +0200398
Nils Diewald59c02fc2015-03-07 01:29:09 +0000399 case 8: // 'Backspace'
Akronc53cfc82020-10-19 11:00:58 +0200400 t._prefix.chop();
401 t.show();
Akrone4961b12017-05-10 21:04:46 +0200402 e.halt();
403 break;
Nils Diewald47f366b2015-04-15 20:06:35 +0000404 };
405 },
Nils Diewald59c02fc2015-03-07 01:29:09 +0000406
Akronc53cfc82020-10-19 11:00:58 +0200407
Nils Diewald47f366b2015-04-15 20:06:35 +0000408 // Add characters to prefix
409 _keypress : function (e) {
Akron9c2f9382016-05-25 16:36:04 +0200410 if (e.charCode !== 0) {
Akrone4961b12017-05-10 21:04:46 +0200411 e.halt();
Akrone4961b12017-05-10 21:04:46 +0200412
413 // Add prefix
Akronc53cfc82020-10-19 11:00:58 +0200414 this._prefix.add(
415 String.fromCharCode(_codeFromEvent(e))
416 );
417
Akrone4961b12017-05-10 21:04:46 +0200418 this.show();
Akron9c2f9382016-05-25 16:36:04 +0200419 };
Nils Diewald59c02fc2015-03-07 01:29:09 +0000420 },
421
Akronc53cfc82020-10-19 11:00:58 +0200422
Akron47c086c2016-05-18 21:22:06 +0200423 /**
Akron5240b8c2016-05-20 09:17:41 +0200424 * Show a screen with a given offset
425 * in the viewport.
Akron47c086c2016-05-18 21:22:06 +0200426 */
427 screen : function (nr) {
Akronc53cfc82020-10-19 11:00:58 +0200428 const t = this;
Akrone817b882018-08-31 14:09:17 +0200429
430 // Normalize negative values
Akron3c2730f2016-05-24 15:08:29 +0200431 if (nr < 0) {
Akrone4961b12017-05-10 21:04:46 +0200432 nr = 0
Akron3c2730f2016-05-24 15:08:29 +0200433 }
Akrone817b882018-08-31 14:09:17 +0200434
435 // The shown list already shows everything
Akronc53cfc82020-10-19 11:00:58 +0200436 else if (t.liveLength() < t.limit()) {
Akrone817b882018-08-31 14:09:17 +0200437 return false;
438 }
439
440 // Move relatively to the next screen
Akronc53cfc82020-10-19 11:00:58 +0200441 else if (nr > (t.liveLength() - t.limit())) {
442 nr = (t.liveLength() - t.limit());
Akron3c2730f2016-05-24 15:08:29 +0200443 };
444
Akrone817b882018-08-31 14:09:17 +0200445 // no change
Akronc53cfc82020-10-19 11:00:58 +0200446 if (t.offset === nr)
Akrone817b882018-08-31 14:09:17 +0200447 return false;
Akron5a1f5bb2016-05-23 22:00:39 +0200448
Akronc53cfc82020-10-19 11:00:58 +0200449 t._showItems(nr);
Akrone817b882018-08-31 14:09:17 +0200450
451 return true;
Akron47c086c2016-05-18 21:22:06 +0200452 },
453
Akrone817b882018-08-31 14:09:17 +0200454
Nils Diewald2fe12e12015-03-06 16:47:06 +0000455 /**
Nils Diewald7148c6f2015-05-04 15:07:53 +0000456 * Get the associated dom element.
Nils Diewald2fe12e12015-03-06 16:47:06 +0000457 */
Nils Diewald86dad5b2015-01-28 15:09:07 +0000458 element : function () {
Akron24aa0052020-11-10 11:00:34 +0100459 return this._el;
Nils Diewald86dad5b2015-01-28 15:09:07 +0000460 },
461
Akronc53cfc82020-10-19 11:00:58 +0200462
Nils Diewald2fe12e12015-03-06 16:47:06 +0000463 /**
Nils Diewald7148c6f2015-05-04 15:07:53 +0000464 * Get the creator class for items
Nils Diewald2fe12e12015-03-06 16:47:06 +0000465 */
Nils Diewald86dad5b2015-01-28 15:09:07 +0000466 itemClass : function () {
467 return this._itemClass;
468 },
469
Akronc53cfc82020-10-19 11:00:58 +0200470
Nils Diewald86dad5b2015-01-28 15:09:07 +0000471 /**
Nils Diewald7148c6f2015-05-04 15:07:53 +0000472 * Get and set the numerical value
473 * for the maximum number of items visible.
Nils Diewald86dad5b2015-01-28 15:09:07 +0000474 */
475 limit : function (limit) {
Nils Diewald5975d702015-03-09 17:45:42 +0000476 if (arguments.length === 1) {
Akrone4961b12017-05-10 21:04:46 +0200477 if (this._limit !== limit) {
478 this._limit = limit;
479 this._slider.limit(limit).reInit();
480 };
481 return this;
Nils Diewald5975d702015-03-09 17:45:42 +0000482 };
Nils Diewald86dad5b2015-01-28 15:09:07 +0000483 return this._limit;
484 },
485
Nils Diewald7148c6f2015-05-04 15:07:53 +0000486
Nils Diewald86dad5b2015-01-28 15:09:07 +0000487 /**
Akron97752a72016-05-25 14:43:07 +0200488 * Filter the list and make it visible.
489 * This is always called once the prefix changes.
Nils Diewald86dad5b2015-01-28 15:09:07 +0000490 *
491 * @param {string} Prefix for filtering the list
492 */
Akron6ed13992016-05-23 18:06:05 +0200493 show : function (active) {
Leo Repp56904d22021-04-26 15:53:22 +0200494 //Upon change please also update alwaysmenu.js (only two lines new there)
Akronc53cfc82020-10-19 11:00:58 +0200495 const t = this;
Nils Diewald86dad5b2015-01-28 15:09:07 +0000496
Akron5240b8c2016-05-20 09:17:41 +0200497 // show menu based on initial offset
Akronc53cfc82020-10-19 11:00:58 +0200498 t._unmark(); // Unmark everything that was marked before
499 t.removeItems();
Akron6ed13992016-05-23 18:06:05 +0200500
501 // Initialize the list
Akronc53cfc82020-10-19 11:00:58 +0200502 if (!t._initList()) {
Akron6ac58442016-05-24 16:52:29 +0200503
Akrone4961b12017-05-10 21:04:46 +0200504 // The prefix is not active
Akronc53cfc82020-10-19 11:00:58 +0200505 t._prefix.active(true);
Akron6ed13992016-05-23 18:06:05 +0200506
Akrone4961b12017-05-10 21:04:46 +0200507 // finally show the element
Akron24aa0052020-11-10 11:00:34 +0100508 t._el.classList.add('visible');
Akrone4961b12017-05-10 21:04:46 +0200509
510 return true;
Akron6ed13992016-05-23 18:06:05 +0200511 };
512
Akronc53cfc82020-10-19 11:00:58 +0200513 let offset = 0;
Nils Diewald86dad5b2015-01-28 15:09:07 +0000514
Akron9c2f9382016-05-25 16:36:04 +0200515 // Set a chosen value to active and move the viewport
Akron6ed13992016-05-23 18:06:05 +0200516 if (arguments.length === 1) {
517
Akrone4961b12017-05-10 21:04:46 +0200518 // Normalize active value
519 if (active < 0) {
520 active = 0;
521 }
Akronc53cfc82020-10-19 11:00:58 +0200522 else if (active >= t.liveLength()) {
523 active = t.liveLength() - 1;
Akrone4961b12017-05-10 21:04:46 +0200524 };
Akron6ed13992016-05-23 18:06:05 +0200525
Akrone4961b12017-05-10 21:04:46 +0200526 // Item is outside the first viewport
Akronc53cfc82020-10-19 11:00:58 +0200527 if (active >= t._limit) {
Akrone4961b12017-05-10 21:04:46 +0200528 offset = active;
Akronc53cfc82020-10-19 11:00:58 +0200529 const newOffset = t.liveLength() - t._limit;
530 if (offset > newOffset) {
531 offset = newOffset;
Akrone4961b12017-05-10 21:04:46 +0200532 };
533 };
534
Akronc53cfc82020-10-19 11:00:58 +0200535 t.position = active;
Akron6ed13992016-05-23 18:06:05 +0200536 }
537
Akron9c2f9382016-05-25 16:36:04 +0200538 // Choose the first item
Akronc53cfc82020-10-19 11:00:58 +0200539 else if (t._firstActive) {
540 t.position = 0;
Akron47c086c2016-05-18 21:22:06 +0200541 }
Akroncb351d62016-05-19 23:10:33 +0200542
Akron9c2f9382016-05-25 16:36:04 +0200543 // Choose no item
Akron47c086c2016-05-18 21:22:06 +0200544 else {
Akronc53cfc82020-10-19 11:00:58 +0200545 t.position = -1;
Akron6ed13992016-05-23 18:06:05 +0200546 };
547
Akronc53cfc82020-10-19 11:00:58 +0200548 t.offset = offset;
549 t._showItems(offset); // Show new item list
Akron6ed13992016-05-23 18:06:05 +0200550
551 // Make chosen value active
Akronc53cfc82020-10-19 11:00:58 +0200552 if (t.position !== -1) {
553 t.liveItem(t.position).active(true);
Akron6ed13992016-05-23 18:06:05 +0200554 };
Akron37513a62015-11-17 01:07:11 +0100555
Akron5240b8c2016-05-20 09:17:41 +0200556 // The prefix is not active
Akronc53cfc82020-10-19 11:00:58 +0200557 t._prefix.active(false);
Nils Diewald86dad5b2015-01-28 15:09:07 +0000558
Akron5240b8c2016-05-20 09:17:41 +0200559 // finally show the element
Akron24aa0052020-11-10 11:00:34 +0100560 t._el.classList.add('visible');
Nils Diewald2fe12e12015-03-06 16:47:06 +0000561
Nils Diewald86dad5b2015-01-28 15:09:07 +0000562 // Add classes for rolling menus
Akronc53cfc82020-10-19 11:00:58 +0200563 t._boundary(true);
Akron6bb71582016-06-10 20:41:08 +0200564
Nils Diewald59c02fc2015-03-07 01:29:09 +0000565 return true;
Nils Diewald86dad5b2015-01-28 15:09:07 +0000566 },
567
Nils Diewald7148c6f2015-05-04 15:07:53 +0000568
569 /**
570 * Hide the menu and call the onHide callback.
571 */
Nils Diewald2fe12e12015-03-06 16:47:06 +0000572 hide : function () {
Akronc53cfc82020-10-19 11:00:58 +0200573 if (!this.dontHide) {
574 this.removeItems();
575 this._prefix.clear();
576 this.onHide();
Akron24aa0052020-11-10 11:00:34 +0100577 this._el.classList.remove('visible');
Akronc53cfc82020-10-19 11:00:58 +0200578 }
Akron24aa0052020-11-10 11:00:34 +0100579 // this._el.blur();
Nils Diewald86dad5b2015-01-28 15:09:07 +0000580 },
581
Akronc53cfc82020-10-19 11:00:58 +0200582
Nils Diewald7148c6f2015-05-04 15:07:53 +0000583 /**
584 * Function released when the menu hides.
585 * This method is expected to be overridden.
586 */
Nils Diewald5c5a7472015-04-02 22:13:38 +0000587 onHide : function () {},
588
Akronc53cfc82020-10-19 11:00:58 +0200589
Nils Diewald86dad5b2015-01-28 15:09:07 +0000590 /**
591 * Get the prefix for filtering,
Nils Diewald2fe12e12015-03-06 16:47:06 +0000592 * e.g. &quot;ve&quot; for &quot;verb&quot;
Nils Diewald86dad5b2015-01-28 15:09:07 +0000593 */
Nils Diewald5975d702015-03-09 17:45:42 +0000594 prefix : function (pref) {
595 if (arguments.length === 1) {
Akrone4961b12017-05-10 21:04:46 +0200596 this._prefix.value(pref);
597 return this;
Nils Diewald5975d702015-03-09 17:45:42 +0000598 };
599 return this._prefix.value();
Nils Diewald86dad5b2015-01-28 15:09:07 +0000600 },
601
Akronc53cfc82020-10-19 11:00:58 +0200602
Akronc7448732016-04-27 14:06:58 +0200603 /**
604 * Get the lengthField object.
605 */
606 lengthField : function () {
607 return this._lengthField;
608 },
Nils Diewald7148c6f2015-05-04 15:07:53 +0000609
Akronc53cfc82020-10-19 11:00:58 +0200610
Akron5240b8c2016-05-20 09:17:41 +0200611 /**
612 * Get the associated slider object.
613 */
614 slider : function () {
615 return this._slider;
Nils Diewald86dad5b2015-01-28 15:09:07 +0000616 },
617
Akron5240b8c2016-05-20 09:17:41 +0200618
Nils Diewald86dad5b2015-01-28 15:09:07 +0000619 /**
620 * Delete all visible items from the menu element
621 */
Leo Reppe5bd35a2021-05-05 15:28:26 +0200622
Leo Reppafcf9842021-06-30 13:01:09 +0200623 removeItems : function () {
Leo Reppe5bd35a2021-05-05 15:28:26 +0200624 const liElements=this._el.getElementsByTagName("LI");
Leo Reppd162b2e2021-06-30 13:51:07 +0200625 var ignoredCount = 0; //counts how many LI tag elements are not actually direct children
626 while (liElements.length>ignoredCount){
627 if (liElements[ignoredCount].parentNode === this._el){
628 this._el.removeChild(liElements[ignoredCount]);
629 } else {
630 ignoredCount++;
631 }
Nils Diewald5975d702015-03-09 17:45:42 +0000632 };
Leo Reppe5bd35a2021-05-05 15:28:26 +0200633 },
Leo Repp56904d22021-04-26 15:53:22 +0200634
Nils Diewald86dad5b2015-01-28 15:09:07 +0000635
Akronc53cfc82020-10-19 11:00:58 +0200636
Nils Diewald2fe12e12015-03-06 16:47:06 +0000637 /**
638 * Get a specific item from the complete list
639 *
640 * @param {number} index of the list item
641 */
642 item : function (index) {
643 return this._items[index]
644 },
645
646
Nils Diewald86dad5b2015-01-28 15:09:07 +0000647 /**
648 * Get a specific item from the filtered list
649 *
650 * @param {number} index of the list item
Nils Diewald2fe12e12015-03-06 16:47:06 +0000651 * in the filtered list
Nils Diewald86dad5b2015-01-28 15:09:07 +0000652 */
653 liveItem : function (index) {
654 if (this._list === undefined)
Akrone4961b12017-05-10 21:04:46 +0200655 if (!this._initList())
656 return;
Nils Diewald86dad5b2015-01-28 15:09:07 +0000657
658 return this._items[this._list[index]];
659 },
660
Nils Diewald86dad5b2015-01-28 15:09:07 +0000661
662 /**
Akron5240b8c2016-05-20 09:17:41 +0200663 * Get a specific item from the viewport list
Nils Diewald86dad5b2015-01-28 15:09:07 +0000664 *
665 * @param {number} index of the list item
Nils Diewald2fe12e12015-03-06 16:47:06 +0000666 * in the visible list
Nils Diewald86dad5b2015-01-28 15:09:07 +0000667 */
668 shownItem : function (index) {
669 if (index >= this.limit())
Akrone4961b12017-05-10 21:04:46 +0200670 return;
Akronc53cfc82020-10-19 11:00:58 +0200671
Akron9c4d1ae2016-05-25 21:43:22 +0200672 return this.liveItem(this.offset + index);
Nils Diewald86dad5b2015-01-28 15:09:07 +0000673 },
674
675
Nils Diewald2fe12e12015-03-06 16:47:06 +0000676 /**
Akron9c4d1ae2016-05-25 21:43:22 +0200677 * Get the length of the full item list
Nils Diewald2fe12e12015-03-06 16:47:06 +0000678 */
679 length : function () {
680 return this._items.length;
681 },
682
683
684 /**
Akron5240b8c2016-05-20 09:17:41 +0200685 * Length of the filtered item list.
686 */
687 liveLength : function () {
688 if (this._list === undefined)
Akrone4961b12017-05-10 21:04:46 +0200689 this._initList();
Akron5240b8c2016-05-20 09:17:41 +0200690 return this._list.length;
691 },
692
Akrone817b882018-08-31 14:09:17 +0200693
Akron5240b8c2016-05-20 09:17:41 +0200694 /**
Nils Diewald2fe12e12015-03-06 16:47:06 +0000695 * Make the next item in the filtered menu active
Nils Diewald86dad5b2015-01-28 15:09:07 +0000696 */
697 next : function () {
Leo Repp56904d22021-04-26 15:53:22 +0200698 //Upon change please update alwaysmenu.js next
Akronc53cfc82020-10-19 11:00:58 +0200699 const t = this;
Nils Diewald5975d702015-03-09 17:45:42 +0000700
Akronb38afb22016-05-25 19:30:01 +0200701 // No list
Akronc53cfc82020-10-19 11:00:58 +0200702 if (t.liveLength() === 0)
Akrone4961b12017-05-10 21:04:46 +0200703 return;
Akronb38afb22016-05-25 19:30:01 +0200704
Akron9c4d1ae2016-05-25 21:43:22 +0200705 // Deactivate old item
Akronc53cfc82020-10-19 11:00:58 +0200706 if (t.position !== -1 && !t._prefix.active()) {
707 t.liveItem(t.position).active(false);
Akron9c4d1ae2016-05-25 21:43:22 +0200708 };
Nils Diewalde8518f82015-03-18 22:41:49 +0000709
Akron9c4d1ae2016-05-25 21:43:22 +0200710 // Get new active item
Akronc53cfc82020-10-19 11:00:58 +0200711 t.position++;
712 let newItem = t.liveItem(t.position);
Nils Diewald86dad5b2015-01-28 15:09:07 +0000713
Nils Diewald5975d702015-03-09 17:45:42 +0000714 // The next element is undefined - roll to top or to prefix
Nils Diewald86dad5b2015-01-28 15:09:07 +0000715 if (newItem === undefined) {
Nils Diewald5975d702015-03-09 17:45:42 +0000716
Akrone4961b12017-05-10 21:04:46 +0200717 // Activate prefix
Akronc53cfc82020-10-19 11:00:58 +0200718 const prefix = this._prefix;
Nils Diewald5975d702015-03-09 17:45:42 +0000719
Akrone4961b12017-05-10 21:04:46 +0200720 // Prefix is set and not active - choose!
721 if (prefix.isSet() && !prefix.active()) {
Akronc53cfc82020-10-19 11:00:58 +0200722 t.position--;
Akrone4961b12017-05-10 21:04:46 +0200723 prefix.active(true);
724 return;
725 }
Akron9c4d1ae2016-05-25 21:43:22 +0200726
Akrone4961b12017-05-10 21:04:46 +0200727 // Choose first item
728 else {
Akronc53cfc82020-10-19 11:00:58 +0200729 newItem = t.liveItem(0);
Akrone4961b12017-05-10 21:04:46 +0200730 // choose first item
Akronc53cfc82020-10-19 11:00:58 +0200731 t.position = 0;
732 t._showItems(0);
Akrone4961b12017-05-10 21:04:46 +0200733 };
Nils Diewald86dad5b2015-01-28 15:09:07 +0000734 }
735
Akron5a1f5bb2016-05-23 22:00:39 +0200736 // The next element is after the viewport - roll down
Akronc53cfc82020-10-19 11:00:58 +0200737 else if (t.position >= (t.limit() + t.offset)) {
738 t.screen(t.position - t.limit() + 1);
Akron5a1f5bb2016-05-23 22:00:39 +0200739 }
740
741 // The next element is before the viewport - roll up
Akronc53cfc82020-10-19 11:00:58 +0200742 else if (t.position <= t.offset) {
743 t.screen(t.position);
Nils Diewald86dad5b2015-01-28 15:09:07 +0000744 };
Nils Diewald5975d702015-03-09 17:45:42 +0000745
Akronc53cfc82020-10-19 11:00:58 +0200746 t._prefix.active(false);
Nils Diewald86dad5b2015-01-28 15:09:07 +0000747 newItem.active(true);
748 },
749
Akronc53cfc82020-10-19 11:00:58 +0200750
Nils Diewalde8518f82015-03-18 22:41:49 +0000751 /*
Nils Diewald86dad5b2015-01-28 15:09:07 +0000752 * Make the previous item in the menu active
753 */
Nils Diewald86dad5b2015-01-28 15:09:07 +0000754 prev : function () {
Leo Repp56904d22021-04-26 15:53:22 +0200755 //Upon Change please update alwaysmenu.js prev
Akronc53cfc82020-10-19 11:00:58 +0200756 const t = this;
Nils Diewald2d210752015-03-09 19:01:15 +0000757
Akronb38afb22016-05-25 19:30:01 +0200758 // No list
Akronc53cfc82020-10-19 11:00:58 +0200759 if (t.liveLength() === 0)
Akrone4961b12017-05-10 21:04:46 +0200760 return;
Akronb38afb22016-05-25 19:30:01 +0200761
Akron9c4d1ae2016-05-25 21:43:22 +0200762 // Deactivate old item
Akronc53cfc82020-10-19 11:00:58 +0200763 if (!t._prefix.active()) {
Akron9c4d1ae2016-05-25 21:43:22 +0200764
Akrone4961b12017-05-10 21:04:46 +0200765 // No active element set
Akronc53cfc82020-10-19 11:00:58 +0200766 if (t.position === -1) {
767 t.position = t.liveLength();
Akrone4961b12017-05-10 21:04:46 +0200768 }
Akron9c4d1ae2016-05-25 21:43:22 +0200769
Akrone4961b12017-05-10 21:04:46 +0200770 // No active element set
771 else {
Akronc53cfc82020-10-19 11:00:58 +0200772 t.liveItem(t.position--).active(false);
Akrone4961b12017-05-10 21:04:46 +0200773 };
Nils Diewald2d210752015-03-09 19:01:15 +0000774 };
775
Akron9c4d1ae2016-05-25 21:43:22 +0200776 // Get new active item
Akronc53cfc82020-10-19 11:00:58 +0200777 let newItem = t.liveItem(t.position);
Nils Diewald86dad5b2015-01-28 15:09:07 +0000778
779 // The previous element is undefined - roll to bottom
780 if (newItem === undefined) {
Nils Diewald5975d702015-03-09 17:45:42 +0000781
Akrone4961b12017-05-10 21:04:46 +0200782 // Activate prefix
Akronc53cfc82020-10-19 11:00:58 +0200783 const prefix = t._prefix;
784 let offset = t.liveLength() - t.limit();
Akrone4961b12017-05-10 21:04:46 +0200785
786 // Normalize offset
787 offset = offset < 0 ? 0 : offset;
Nils Diewald2d210752015-03-09 19:01:15 +0000788
Akrone4961b12017-05-10 21:04:46 +0200789 // Choose the last item
Akronc53cfc82020-10-19 11:00:58 +0200790 t.position = t.liveLength() - 1;
Akrone4961b12017-05-10 21:04:46 +0200791
792 // Prefix is set and not active - choose!
793 if (prefix.isSet() && !prefix.active()) {
Akronc53cfc82020-10-19 11:00:58 +0200794 t.position++;
Akrone4961b12017-05-10 21:04:46 +0200795 prefix.active(true);
Akronc53cfc82020-10-19 11:00:58 +0200796 t.offset = offset;
Akrone4961b12017-05-10 21:04:46 +0200797 return;
798 }
Nils Diewald2d210752015-03-09 19:01:15 +0000799
Akrone4961b12017-05-10 21:04:46 +0200800 // Choose last item
801 else {
Akronc53cfc82020-10-19 11:00:58 +0200802 newItem = t.liveItem(t.position);
803 t._showItems(offset);
Akrone4961b12017-05-10 21:04:46 +0200804 };
Nils Diewald86dad5b2015-01-28 15:09:07 +0000805 }
806
Akron5a1f5bb2016-05-23 22:00:39 +0200807 // The previous element is before the view - roll up
Akronc53cfc82020-10-19 11:00:58 +0200808 else if (t.position < t.offset) {
809 t.screen(t.position);
Akron5a1f5bb2016-05-23 22:00:39 +0200810 }
811
812 // The previous element is after the view - roll down
Akronc53cfc82020-10-19 11:00:58 +0200813 else if (t.position >= (t.limit() + t.offset)) {
814 t.screen(t.position - t.limit() + 2);
Nils Diewald86dad5b2015-01-28 15:09:07 +0000815 };
Nils Diewald2fe12e12015-03-06 16:47:06 +0000816
Akronc53cfc82020-10-19 11:00:58 +0200817 t._prefix.active(false);
Nils Diewald86dad5b2015-01-28 15:09:07 +0000818 newItem.active(true);
819 },
Nils Diewald86dad5b2015-01-28 15:09:07 +0000820
Akronc53cfc82020-10-19 11:00:58 +0200821
Akron3c2730f2016-05-24 15:08:29 +0200822 /**
823 * Move the page up by limit!
824 */
825 pageUp : function () {
Akron9c4d1ae2016-05-25 21:43:22 +0200826 this.screen(this.offset - this.limit());
Akron3c2730f2016-05-24 15:08:29 +0200827 },
828
829
830 /**
831 * Move the page down by limit!
832 */
833 pageDown : function () {
Akron9c4d1ae2016-05-25 21:43:22 +0200834 this.screen(this.offset + this.limit());
Akron3c2730f2016-05-24 15:08:29 +0200835 },
836
Nils Diewald86dad5b2015-01-28 15:09:07 +0000837
Akrone817b882018-08-31 14:09:17 +0200838 /**
839 * Move the view one item up
840 */
841 viewUp : function () {
842 this.screen(this.offset - 1);
843 },
844
845
846 /**
847 * Move the view one item down
848 */
849 viewDown : function () {
850 this.screen(this.offset + 1);
851 },
852
Akronc53cfc82020-10-19 11:00:58 +0200853
Akron5240b8c2016-05-20 09:17:41 +0200854 // Unmark all items
855 _unmark : function () {
Akron678c26f2020-10-09 08:52:50 +0200856 this._list.forEach(function(it){
Akronc53cfc82020-10-19 11:00:58 +0200857 const item = this._items[it];
Akrone4961b12017-05-10 21:04:46 +0200858 item.lowlight();
Akron678c26f2020-10-09 08:52:50 +0200859 item.active(false);
860 }, this);
Akron5240b8c2016-05-20 09:17:41 +0200861 },
862
Akronc53cfc82020-10-19 11:00:58 +0200863
Akron5240b8c2016-05-20 09:17:41 +0200864 // Set boundary for viewport
865 _boundary : function (bool) {
Akron55a343b2018-04-06 19:57:36 +0200866 if (this._list.length === 0)
867 return;
Akronc53cfc82020-10-19 11:00:58 +0200868
Akron5240b8c2016-05-20 09:17:41 +0200869 this.item(this._list[0]).noMore(bool);
870 this.item(this._list[this._list.length - 1]).noMore(bool);
871 },
872
873
874 // Append Items that should be shown
875 _showItems : function (off) {
Akronc53cfc82020-10-19 11:00:58 +0200876 const t = this;
Akron5240b8c2016-05-20 09:17:41 +0200877
Akrona92fd8d2016-05-24 21:13:41 +0200878 // optimization: scroll down one step
Akronc53cfc82020-10-19 11:00:58 +0200879 if (t.offset === (off - 1)) {
880 t.offset = off;
Akron9c4d1ae2016-05-25 21:43:22 +0200881
Akrone4961b12017-05-10 21:04:46 +0200882 // Remove the HTML node from the first item
883 // leave lengthField/prefix/slider
Leo Reppd162b2e2021-06-30 13:51:07 +0200884 //console.log("_showItems, at _notItemElements is: ",t._el.children[this._notItemElements]);
Leo Repp56904d22021-04-26 15:53:22 +0200885 t._el.removeChild(t._el.children[this._notItemElements]);
Akronc53cfc82020-10-19 11:00:58 +0200886
887 t._append(
888 t._list[t.offset + t.limit() - 1]
889 );
Akrona92fd8d2016-05-24 21:13:41 +0200890 }
Akron5240b8c2016-05-20 09:17:41 +0200891
Akrona92fd8d2016-05-24 21:13:41 +0200892 // optimization: scroll up one step
Akronc53cfc82020-10-19 11:00:58 +0200893 else if (t.offset === (off + 1)) {
894 t.offset = off;
Akron9c4d1ae2016-05-25 21:43:22 +0200895
Akrone4961b12017-05-10 21:04:46 +0200896 // Remove the HTML node from the last item
Leo Reppd162b2e2021-06-30 13:51:07 +0200897 //console.log("_showItems, at lastChild is: ",t._el.lastChild);
Akron24aa0052020-11-10 11:00:34 +0100898 t._el.removeChild(t._el.lastChild);
Akron9c4d1ae2016-05-25 21:43:22 +0200899
Akronc53cfc82020-10-19 11:00:58 +0200900 t._prepend(t._list[t.offset]);
Akrona92fd8d2016-05-24 21:13:41 +0200901 }
Akronc53cfc82020-10-19 11:00:58 +0200902
Akrona92fd8d2016-05-24 21:13:41 +0200903 else {
Akronc53cfc82020-10-19 11:00:58 +0200904 t.offset = off;
Akron5240b8c2016-05-20 09:17:41 +0200905
Akrone4961b12017-05-10 21:04:46 +0200906 // Remove all items
Akronc53cfc82020-10-19 11:00:58 +0200907 t.removeItems();
Akron5240b8c2016-05-20 09:17:41 +0200908
Akrone4961b12017-05-10 21:04:46 +0200909 // Use list
Akronc53cfc82020-10-19 11:00:58 +0200910 let shown = 0;
Akron5240b8c2016-05-20 09:17:41 +0200911
Akronc53cfc82020-10-19 11:00:58 +0200912 for (let i = 0; i < t._list.length; i++) {
Akrona92fd8d2016-05-24 21:13:41 +0200913
Akrone4961b12017-05-10 21:04:46 +0200914 // Don't show - it's before offset
915 shown++;
916 if (shown <= off)
917 continue;
Akrona92fd8d2016-05-24 21:13:41 +0200918
Akronc53cfc82020-10-19 11:00:58 +0200919 t._append(t._list[i]);
Akrone4961b12017-05-10 21:04:46 +0200920
Akronc53cfc82020-10-19 11:00:58 +0200921 if (shown >= (t.limit() + off))
Akrone4961b12017-05-10 21:04:46 +0200922 break;
923 };
Akron5240b8c2016-05-20 09:17:41 +0200924 };
925
926 // set the slider to the new offset
Akronc53cfc82020-10-19 11:00:58 +0200927 t._slider.offset(t.offset);
Akron5240b8c2016-05-20 09:17:41 +0200928 },
929
930
931 // Append item to the shown list based on index
932 _append : function (i) {
Akronc53cfc82020-10-19 11:00:58 +0200933 const item = this.item(i);
Akron5240b8c2016-05-20 09:17:41 +0200934
935 // Highlight based on prefix
Akrona92fd8d2016-05-24 21:13:41 +0200936 if (this.prefix().length > 0) {
Akrone4961b12017-05-10 21:04:46 +0200937 item.highlight(this.prefix().toLowerCase());
Akrona92fd8d2016-05-24 21:13:41 +0200938 };
939
Akron5240b8c2016-05-20 09:17:41 +0200940 // Append element
941 this.element().appendChild(item.element());
942 },
943
944
945 // Prepend item to the shown list based on index
946 _prepend : function (i) {
Akronc53cfc82020-10-19 11:00:58 +0200947 const item = this.item(i);
Akron5240b8c2016-05-20 09:17:41 +0200948
949 // Highlight based on prefix
Akrona92fd8d2016-05-24 21:13:41 +0200950 if (this.prefix().length > 0) {
Akrone4961b12017-05-10 21:04:46 +0200951 item.highlight(this.prefix().toLowerCase());
Akrona92fd8d2016-05-24 21:13:41 +0200952 };
Akron5240b8c2016-05-20 09:17:41 +0200953
Akronc53cfc82020-10-19 11:00:58 +0200954 const e = this.element();
Akron9c4d1ae2016-05-25 21:43:22 +0200955
Akron5240b8c2016-05-20 09:17:41 +0200956 // Append element after lengthField/prefix/slider
957 e.insertBefore(
Akrone4961b12017-05-10 21:04:46 +0200958 item.element(),
Leo Repp56904d22021-04-26 15:53:22 +0200959 e.children[this._notItemElements]
Akron5240b8c2016-05-20 09:17:41 +0200960 );
Nils Diewald2fe12e12015-03-06 16:47:06 +0000961 }
Nils Diewald86dad5b2015-01-28 15:09:07 +0000962 };
Nils Diewald0e6992a2015-04-14 20:13:52 +0000963});