blob: 6aff7facc2038207de2203af8fab60a40f2146fd [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;
Leo Repp56904d22021-04-26 15:53:22 +020063 t._notItemElements=3;
Akronc53cfc82020-10-19 11:00:58 +020064
65 t._itemClass = params["itemClass"] || defaultItemClass;
Akron5240b8c2016-05-20 09:17:41 +020066
67 // Add prefix object
Akron7524be12016-06-01 17:31:33 +020068 if (params["prefixClass"] !== undefined) {
Akronc53cfc82020-10-19 11:00:58 +020069 t._prefix = params["prefixClass"].create();
Akron5240b8c2016-05-20 09:17:41 +020070 }
71 else {
Akronc53cfc82020-10-19 11:00:58 +020072 t._prefix = defaultPrefixClass.create();
Akron5240b8c2016-05-20 09:17:41 +020073 };
Akronc53cfc82020-10-19 11:00:58 +020074 t._prefix._menu = t;
Akron5240b8c2016-05-20 09:17:41 +020075
76 // Add lengthField object
Akron7524be12016-06-01 17:31:33 +020077 if (params["lengthFieldClass"] !== undefined) {
Akronc53cfc82020-10-19 11:00:58 +020078 t._lengthField = params["lengthFieldClass"].create();
Akron5240b8c2016-05-20 09:17:41 +020079 }
80 else {
Akronc53cfc82020-10-19 11:00:58 +020081 t._lengthField = defaultLengthFieldClass.create();
Akron5240b8c2016-05-20 09:17:41 +020082 };
Akronc53cfc82020-10-19 11:00:58 +020083 t._lengthField._menu = t;
Akron5240b8c2016-05-20 09:17:41 +020084
85 // Initialize slider
Akronc53cfc82020-10-19 11:00:58 +020086 t._slider = sliderClass.create(t);
Akron5240b8c2016-05-20 09:17:41 +020087
88 // Create the element
Akron9c4d1ae2016-05-25 21:43:22 +020089 var el = document.createElement("ul");
Akronc53cfc82020-10-19 11:00:58 +020090 el.style.outline = 0;
91 el.setAttribute('tabindex', 0);
92 el.classList.add('menu', 'roll');
93 el.appendChild(t._prefix.element());
94 el.appendChild(t._lengthField.element());
95 el.appendChild(t._slider.element());
Akron5240b8c2016-05-20 09:17:41 +020096
97 // This has to be cleaned up later on
Akronc53cfc82020-10-19 11:00:58 +020098 el["menu"] = t;
Akron5240b8c2016-05-20 09:17:41 +020099
100 // Arrow keys
Akron9c4d1ae2016-05-25 21:43:22 +0200101 el.addEventListener(
Akrone4961b12017-05-10 21:04:46 +0200102 'keydown',
Akronc53cfc82020-10-19 11:00:58 +0200103 t._keydown.bind(t),
Akrone4961b12017-05-10 21:04:46 +0200104 false
Akron5240b8c2016-05-20 09:17:41 +0200105 );
106
107 // Strings
Akron9c4d1ae2016-05-25 21:43:22 +0200108 el.addEventListener(
Akrone4961b12017-05-10 21:04:46 +0200109 'keypress',
Akronc53cfc82020-10-19 11:00:58 +0200110 t._keypress.bind(t),
Akrone4961b12017-05-10 21:04:46 +0200111 false
Akron5240b8c2016-05-20 09:17:41 +0200112 );
113
114 // Mousewheel
Akron9c4d1ae2016-05-25 21:43:22 +0200115 el.addEventListener(
Akrone4961b12017-05-10 21:04:46 +0200116 'wheel',
Akronc53cfc82020-10-19 11:00:58 +0200117 t._mousewheel.bind(t),
Akrone4961b12017-05-10 21:04:46 +0200118 false
Akron5240b8c2016-05-20 09:17:41 +0200119 );
Akrona1159ff2018-07-22 13:28:31 +0200120
Akronc53cfc82020-10-19 11:00:58 +0200121 // Touch events
122 ['touchstart', 'touchend', 'touchmove'].forEach(
123 e => el.addEventListener(e, t._touch.bind(t), false)
Akrona1159ff2018-07-22 13:28:31 +0200124 );
125
126
Akron24aa0052020-11-10 11:00:34 +0100127 t._el = el;
Akroneaba63e2018-01-26 19:49:30 +0100128
Akronc53cfc82020-10-19 11:00:58 +0200129 t._limit = menuLimit;
Akrone4961b12017-05-10 21:04:46 +0200130
Leo Repp56904d22021-04-26 15:53:22 +0200131 t._items = new Array(); //all childNodes, i.e. ItemClass, prefixClass
Akron5240b8c2016-05-20 09:17:41 +0200132
Akroneaba63e2018-01-26 19:49:30 +0100133 // TODO:
134 // Make this separate from _init
Akronc53cfc82020-10-19 11:00:58 +0200135 t.readItems(list);
Akroneaba63e2018-01-26 19:49:30 +0100136
Akronc53cfc82020-10-19 11:00:58 +0200137 t.dontHide = false;
hebastaf95226b2019-09-19 11:37:00 +0200138
Akronc53cfc82020-10-19 11:00:58 +0200139 return t;
Akroneaba63e2018-01-26 19:49:30 +0100140 },
141
142 // Read items to add to list
143 readItems : function (list) {
Akronc53cfc82020-10-19 11:00:58 +0200144 const t = this;
Akroneaba63e2018-01-26 19:49:30 +0100145
Leo Repp56904d22021-04-26 15:53:22 +0200146 t._list = undefined; //filtered List containing all itemClass items
Akroneaba63e2018-01-26 19:49:30 +0100147
148 // Remove circular reference to "this" in items
Akronc53cfc82020-10-19 11:00:58 +0200149 for (let i = 0; i < t._items.length; i++) {
150 delete t._items[i]["_menu"];
151 delete t._items[i];
Akroneaba63e2018-01-26 19:49:30 +0100152 };
153
Akronc53cfc82020-10-19 11:00:58 +0200154 t._items = new Array();
155 t.removeItems();
Akroneaba63e2018-01-26 19:49:30 +0100156
157
158 // Initialize items
Akronc53cfc82020-10-19 11:00:58 +0200159 t._lengthField.reset();
Akroneaba63e2018-01-26 19:49:30 +0100160
Akron5240b8c2016-05-20 09:17:41 +0200161 // Initialize item list based on parameters
Akron678c26f2020-10-09 08:52:50 +0200162 list.forEach(function(i){
Akronc53cfc82020-10-19 11:00:58 +0200163 const obj = this._itemClass.create(i);
Akron5240b8c2016-05-20 09:17:41 +0200164
Akrone4961b12017-05-10 21:04:46 +0200165 // This may become circular
166 obj["_menu"] = this;
Akron678c26f2020-10-09 08:52:50 +0200167 this._lengthField.add(i);
Akrone4961b12017-05-10 21:04:46 +0200168 this._items.push(obj);
Akronc53cfc82020-10-19 11:00:58 +0200169 }, t);
Akron5240b8c2016-05-20 09:17:41 +0200170
Akronc53cfc82020-10-19 11:00:58 +0200171 t._slider.length(t.liveLength())
172 .limit(t._limit)
Akrone4961b12017-05-10 21:04:46 +0200173 .reInit();
174
Akronc53cfc82020-10-19 11:00:58 +0200175 t._firstActive = false;
Akroneaba63e2018-01-26 19:49:30 +0100176 // Show the first item active always?
Akronc53cfc82020-10-19 11:00:58 +0200177 t.offset = 0;
178 t.position = 0;
Akron5240b8c2016-05-20 09:17:41 +0200179 },
Akron8aa1c522021-07-23 11:33:49 +0200180
181 // Append item to list
182 append : function (item) {
183 const t = this;
184 // This is cyclic!
185 item["_menu"] = t;
186 t._list = undefined;
187 t.removeItems();
188 t._items.push(item);
189 t._lengthField.add([item.content().data]);
190 t._slider.length(t.liveLength()).reInit();
191 t._firstActive = false;
192 t.offset = 0;
193 t.position = 0;
194 },
Akroneaba63e2018-01-26 19:49:30 +0100195
Akron5240b8c2016-05-20 09:17:41 +0200196 // Initialize the item list
197 _initList : function () {
Leo Repp56904d22021-04-26 15:53:22 +0200198 // Upon change also update alwaysmenu.js please
Akronc53cfc82020-10-19 11:00:58 +0200199 const t = this;
Akron5240b8c2016-05-20 09:17:41 +0200200
201 // Create a new list
Akronc53cfc82020-10-19 11:00:58 +0200202 if (t._list === undefined) {
203 t._list = [];
Akron5240b8c2016-05-20 09:17:41 +0200204 }
Akronc53cfc82020-10-19 11:00:58 +0200205 else if (t._list.length !== 0) {
206 t._boundary(false);
207 t._list.length = 0;
Akron5240b8c2016-05-20 09:17:41 +0200208 };
209
210 // Offset is initially zero
Akronc53cfc82020-10-19 11:00:58 +0200211 t.offset = 0;
Akron5240b8c2016-05-20 09:17:41 +0200212
213 // There is no prefix set
Akronc53cfc82020-10-19 11:00:58 +0200214 if (t.prefix().length <= 0) {
Akron5240b8c2016-05-20 09:17:41 +0200215
Akrone4961b12017-05-10 21:04:46 +0200216 // add all items to the list and lowlight
Akronb50964a2020-10-12 11:44:37 +0200217 let i = 0;
Akronc53cfc82020-10-19 11:00:58 +0200218 for (; i < t._items.length; i++) {
219 t._list.push(i);
220 t._items[i].lowlight();
Akrone4961b12017-05-10 21:04:46 +0200221 };
Akron5240b8c2016-05-20 09:17:41 +0200222
Akronc53cfc82020-10-19 11:00:58 +0200223 t._slider.length(i).reInit();
Akron97752a72016-05-25 14:43:07 +0200224
Akrone4961b12017-05-10 21:04:46 +0200225 return true;
Akron5240b8c2016-05-20 09:17:41 +0200226 };
227
228 /*
229 * There is a prefix set, so filter the list!
230 */
Akronc53cfc82020-10-19 11:00:58 +0200231 let pos;
232 const prefixList = t.prefix().toLowerCase().split(" ");
Akronacffc652017-12-18 21:21:25 +0100233
Akronc53cfc82020-10-19 11:00:58 +0200234 const items = [];
235 let maxPoints = 1; // minimum 1
Akron5240b8c2016-05-20 09:17:41 +0200236
237 // Iterate over all items and choose preferred matching items
238 // i.e. the matching happens at the word start
Akronc53cfc82020-10-19 11:00:58 +0200239 t._items.forEach(function(it, pos){
Akronacffc652017-12-18 21:21:25 +0100240
Akronb50964a2020-10-12 11:44:37 +0200241 let points = 0;
Akronacffc652017-12-18 21:21:25 +0100242
Akronb50964a2020-10-12 11:44:37 +0200243 prefixList.forEach(function(p) {
Akronacffc652017-12-18 21:21:25 +0100244
245 // Check if it matches at the beginning
Akronb50964a2020-10-12 11:44:37 +0200246 if ((it.lcField().includes(" " + p))) {
Akronacffc652017-12-18 21:21:25 +0100247 points += 5;
248 }
249
250 // Check if it matches anywhere
Akronb50964a2020-10-12 11:44:37 +0200251 else if (it.lcField().includes(p)) {
Akronacffc652017-12-18 21:21:25 +0100252 points += 1;
253 };
Akronb50964a2020-10-12 11:44:37 +0200254 });
Akronacffc652017-12-18 21:21:25 +0100255
256 if (points > maxPoints) {
257 this._list = [pos];
258 maxPoints = points;
259 }
260 else if (points == maxPoints) {
Akrone4961b12017-05-10 21:04:46 +0200261 this._list.push(pos);
Akronacffc652017-12-18 21:21:25 +0100262 }
Akronc53cfc82020-10-19 11:00:58 +0200263 }, t);
Akron5240b8c2016-05-20 09:17:41 +0200264
Akronc53cfc82020-10-19 11:00:58 +0200265 t._slider.length(t._list.length).reInit();
Akron6ed13992016-05-23 18:06:05 +0200266
Akron5240b8c2016-05-20 09:17:41 +0200267 // Filter was successful - yeah!
Akronc53cfc82020-10-19 11:00:58 +0200268 return t._list.length > 0 ? true : false;
Akron5240b8c2016-05-20 09:17:41 +0200269 },
270
271
Nils Diewald6e43ffd2015-03-25 18:55:39 +0000272 /**
273 * Destroy this menu
274 * (in case you don't trust the
275 * mark and sweep GC)!
276 */
277 destroy : function () {
Leo Repp56904d22021-04-26 15:53:22 +0200278 // Upon change also update alwaysmenu.js please
Akronc53cfc82020-10-19 11:00:58 +0200279 const t = this;
Akron47c086c2016-05-18 21:22:06 +0200280
Akron5240b8c2016-05-20 09:17:41 +0200281 // Remove circular reference to "this" in menu
Akron24aa0052020-11-10 11:00:34 +0100282 if (t._el != undefined)
283 delete t._el["menu"];
Nils Diewald6e43ffd2015-03-25 18:55:39 +0000284
Akron5240b8c2016-05-20 09:17:41 +0200285 // Remove circular reference to "this" in items
Akronc53cfc82020-10-19 11:00:58 +0200286 t._items.forEach(function(i) {
Akron678c26f2020-10-09 08:52:50 +0200287 delete i["_menu"];
288 });
Akron5240b8c2016-05-20 09:17:41 +0200289
290 // Remove circular reference to "this" in prefix
Akronc53cfc82020-10-19 11:00:58 +0200291 delete t._prefix['_menu'];
292 delete t._lengthField['_menu'];
293 delete t._slider['_menu'];
Nils Diewald6e43ffd2015-03-25 18:55:39 +0000294 },
295
Nils Diewald7148c6f2015-05-04 15:07:53 +0000296
297 /**
298 * Focus on this menu.
299 */
Nils Diewald2fe12e12015-03-06 16:47:06 +0000300 focus : function () {
Akron24aa0052020-11-10 11:00:34 +0100301 this._el.focus();
Nils Diewald2fe12e12015-03-06 16:47:06 +0000302 },
303
Nils Diewald7148c6f2015-05-04 15:07:53 +0000304
Nils Diewald59c02fc2015-03-07 01:29:09 +0000305 // mouse wheel treatment
306 _mousewheel : function (e) {
Akronc53cfc82020-10-19 11:00:58 +0200307 const delta = e.deltaY / 120;
Nils Diewald5975d702015-03-09 17:45:42 +0000308 if (delta > 0)
Akrone4961b12017-05-10 21:04:46 +0200309 this.next();
Nils Diewald5975d702015-03-09 17:45:42 +0000310 else if (delta < 0)
Akrone4961b12017-05-10 21:04:46 +0200311 this.prev();
Nils Diewald59c02fc2015-03-07 01:29:09 +0000312 e.halt();
313 },
314
Akronc53cfc82020-10-19 11:00:58 +0200315
Akrona1159ff2018-07-22 13:28:31 +0200316 // touchmove treatment
317 _touch : function (e) {
Akronc53cfc82020-10-19 11:00:58 +0200318 const s = this.slider();
319
Akrona1159ff2018-07-22 13:28:31 +0200320 if (e.type === "touchstart") {
Akronc53cfc82020-10-19 11:00:58 +0200321 this._lastTouch = e.touches[0].clientY;
Akrona1159ff2018-07-22 13:28:31 +0200322 }
323 else if (e.type === "touchend") {
Akrona1159ff2018-07-22 13:28:31 +0200324 this._lastTouch = undefined;
Akrona1159ff2018-07-22 13:28:31 +0200325 }
326 else if (e.type === "touchmove") {
Akronc53cfc82020-10-19 11:00:58 +0200327 const to = e.touches[0];
Akrone817b882018-08-31 14:09:17 +0200328
329 // TODO:
330 // Instead of using 26px, choose the item height
331 // or use the menu height // shownItems
332
Akrona1159ff2018-07-22 13:28:31 +0200333 // s.movetoRel(t.clientY - this._initTouch);
Akronc53cfc82020-10-19 11:00:58 +0200334 if ((this._lastTouch + 26) < to.clientY) {
Akrone817b882018-08-31 14:09:17 +0200335 this.viewDown();
Akronc53cfc82020-10-19 11:00:58 +0200336 this._lastTouch = to.clientY;
Akrona1159ff2018-07-22 13:28:31 +0200337 }
Akronc53cfc82020-10-19 11:00:58 +0200338 else if ((this._lastTouch - 26) > to.clientY) {
Akrone817b882018-08-31 14:09:17 +0200339 this.viewUp();
Akronc53cfc82020-10-19 11:00:58 +0200340 this._lastTouch = to.clientY;
Akrona1159ff2018-07-22 13:28:31 +0200341 }
342 e.halt();
343 };
344 },
Nils Diewald7148c6f2015-05-04 15:07:53 +0000345
Nils Diewald59c02fc2015-03-07 01:29:09 +0000346 // Arrow key and prefix treatment
Nils Diewald47f366b2015-04-15 20:06:35 +0000347 _keydown : function (e) {
Leo Repp56904d22021-04-26 15:53:22 +0200348 //Upon change also update alwaysmenu.js please
Akronc53cfc82020-10-19 11:00:58 +0200349 const t = this;
Nils Diewald59c02fc2015-03-07 01:29:09 +0000350
Akronc53cfc82020-10-19 11:00:58 +0200351 switch (_codeFromEvent(e)) {
352
Nils Diewald59c02fc2015-03-07 01:29:09 +0000353 case 27: // 'Esc'
Akrone4961b12017-05-10 21:04:46 +0200354 e.halt();
Akronc53cfc82020-10-19 11:00:58 +0200355 t.hide();
Akrone4961b12017-05-10 21:04:46 +0200356 break;
Nils Diewald5975d702015-03-09 17:45:42 +0000357
Nils Diewald59c02fc2015-03-07 01:29:09 +0000358 case 38: // 'Up'
Akrone4961b12017-05-10 21:04:46 +0200359 e.halt();
Akronc53cfc82020-10-19 11:00:58 +0200360 t.prev();
Akrone4961b12017-05-10 21:04:46 +0200361 break;
Akronc53cfc82020-10-19 11:00:58 +0200362
Nils Diewald5975d702015-03-09 17:45:42 +0000363 case 33: // 'Page up'
Akrone4961b12017-05-10 21:04:46 +0200364 e.halt();
Akronc53cfc82020-10-19 11:00:58 +0200365 t.pageUp();
Akrone4961b12017-05-10 21:04:46 +0200366 break;
Akronc53cfc82020-10-19 11:00:58 +0200367
Nils Diewald5975d702015-03-09 17:45:42 +0000368 case 40: // 'Down'
Akrone4961b12017-05-10 21:04:46 +0200369 e.halt();
Akronc53cfc82020-10-19 11:00:58 +0200370 t.next();
Akrone4961b12017-05-10 21:04:46 +0200371 break;
Akronc53cfc82020-10-19 11:00:58 +0200372
Nils Diewald5975d702015-03-09 17:45:42 +0000373 case 34: // 'Page down'
Akrone4961b12017-05-10 21:04:46 +0200374 e.halt();
Akronc53cfc82020-10-19 11:00:58 +0200375 t.pageDown();
Akrone4961b12017-05-10 21:04:46 +0200376 break;
Akronc53cfc82020-10-19 11:00:58 +0200377
Nils Diewald5975d702015-03-09 17:45:42 +0000378 case 39: // 'Right'
Akronc53cfc82020-10-19 11:00:58 +0200379 if (t._prefix.active())
Akrone4961b12017-05-10 21:04:46 +0200380 break;
Nils Diewalde8518f82015-03-18 22:41:49 +0000381
Akronc53cfc82020-10-19 11:00:58 +0200382 const item = t.liveItem(t.position);
Akrone4961b12017-05-10 21:04:46 +0200383
384 if (item["further"] !== undefined) {
385 item["further"].bind(item).apply();
386 };
387
388 e.halt();
389 break;
Nils Diewald5975d702015-03-09 17:45:42 +0000390
Akronc53cfc82020-10-19 11:00:58 +0200391 case 13: // 'Enter'
Akrone4961b12017-05-10 21:04:46 +0200392 // Click on prefix
Akronc53cfc82020-10-19 11:00:58 +0200393 if (t._prefix.active())
394 t._prefix.onclick(e);
Nils Diewald5975d702015-03-09 17:45:42 +0000395
Akrone4961b12017-05-10 21:04:46 +0200396 // Click on item
397 else
Akronc53cfc82020-10-19 11:00:58 +0200398 t.liveItem(t.position).onclick(e);
Akrone4961b12017-05-10 21:04:46 +0200399 e.halt();
400 break;
Akronc53cfc82020-10-19 11:00:58 +0200401
Nils Diewald59c02fc2015-03-07 01:29:09 +0000402 case 8: // 'Backspace'
Akronc53cfc82020-10-19 11:00:58 +0200403 t._prefix.chop();
404 t.show();
Akrone4961b12017-05-10 21:04:46 +0200405 e.halt();
406 break;
Nils Diewald47f366b2015-04-15 20:06:35 +0000407 };
408 },
Nils Diewald59c02fc2015-03-07 01:29:09 +0000409
Akronc53cfc82020-10-19 11:00:58 +0200410
Nils Diewald47f366b2015-04-15 20:06:35 +0000411 // Add characters to prefix
412 _keypress : function (e) {
Akron9c2f9382016-05-25 16:36:04 +0200413 if (e.charCode !== 0) {
Akrone4961b12017-05-10 21:04:46 +0200414 e.halt();
Akrone4961b12017-05-10 21:04:46 +0200415
416 // Add prefix
Akronc53cfc82020-10-19 11:00:58 +0200417 this._prefix.add(
418 String.fromCharCode(_codeFromEvent(e))
419 );
420
Akrone4961b12017-05-10 21:04:46 +0200421 this.show();
Akron9c2f9382016-05-25 16:36:04 +0200422 };
Nils Diewald59c02fc2015-03-07 01:29:09 +0000423 },
424
Akronc53cfc82020-10-19 11:00:58 +0200425
Akron47c086c2016-05-18 21:22:06 +0200426 /**
Akron5240b8c2016-05-20 09:17:41 +0200427 * Show a screen with a given offset
428 * in the viewport.
Akron47c086c2016-05-18 21:22:06 +0200429 */
430 screen : function (nr) {
Akronc53cfc82020-10-19 11:00:58 +0200431 const t = this;
Akrone817b882018-08-31 14:09:17 +0200432
433 // Normalize negative values
Akron3c2730f2016-05-24 15:08:29 +0200434 if (nr < 0) {
Akrone4961b12017-05-10 21:04:46 +0200435 nr = 0
Akron3c2730f2016-05-24 15:08:29 +0200436 }
Akrone817b882018-08-31 14:09:17 +0200437
438 // The shown list already shows everything
Akronc53cfc82020-10-19 11:00:58 +0200439 else if (t.liveLength() < t.limit()) {
Akrone817b882018-08-31 14:09:17 +0200440 return false;
441 }
442
443 // Move relatively to the next screen
Akronc53cfc82020-10-19 11:00:58 +0200444 else if (nr > (t.liveLength() - t.limit())) {
445 nr = (t.liveLength() - t.limit());
Akron3c2730f2016-05-24 15:08:29 +0200446 };
447
Akrone817b882018-08-31 14:09:17 +0200448 // no change
Akronc53cfc82020-10-19 11:00:58 +0200449 if (t.offset === nr)
Akrone817b882018-08-31 14:09:17 +0200450 return false;
Akron5a1f5bb2016-05-23 22:00:39 +0200451
Akronc53cfc82020-10-19 11:00:58 +0200452 t._showItems(nr);
Akrone817b882018-08-31 14:09:17 +0200453
454 return true;
Akron47c086c2016-05-18 21:22:06 +0200455 },
456
Akrone817b882018-08-31 14:09:17 +0200457
Nils Diewald2fe12e12015-03-06 16:47:06 +0000458 /**
Nils Diewald7148c6f2015-05-04 15:07:53 +0000459 * Get the associated dom element.
Nils Diewald2fe12e12015-03-06 16:47:06 +0000460 */
Nils Diewald86dad5b2015-01-28 15:09:07 +0000461 element : function () {
Akron24aa0052020-11-10 11:00:34 +0100462 return this._el;
Nils Diewald86dad5b2015-01-28 15:09:07 +0000463 },
464
Akronc53cfc82020-10-19 11:00:58 +0200465
Nils Diewald2fe12e12015-03-06 16:47:06 +0000466 /**
Nils Diewald7148c6f2015-05-04 15:07:53 +0000467 * Get the creator class for items
Nils Diewald2fe12e12015-03-06 16:47:06 +0000468 */
Nils Diewald86dad5b2015-01-28 15:09:07 +0000469 itemClass : function () {
470 return this._itemClass;
471 },
472
Akronc53cfc82020-10-19 11:00:58 +0200473
Nils Diewald86dad5b2015-01-28 15:09:07 +0000474 /**
Nils Diewald7148c6f2015-05-04 15:07:53 +0000475 * Get and set the numerical value
476 * for the maximum number of items visible.
Nils Diewald86dad5b2015-01-28 15:09:07 +0000477 */
478 limit : function (limit) {
Nils Diewald5975d702015-03-09 17:45:42 +0000479 if (arguments.length === 1) {
Akrone4961b12017-05-10 21:04:46 +0200480 if (this._limit !== limit) {
481 this._limit = limit;
482 this._slider.limit(limit).reInit();
483 };
484 return this;
Nils Diewald5975d702015-03-09 17:45:42 +0000485 };
Nils Diewald86dad5b2015-01-28 15:09:07 +0000486 return this._limit;
487 },
488
Nils Diewald7148c6f2015-05-04 15:07:53 +0000489
Nils Diewald86dad5b2015-01-28 15:09:07 +0000490 /**
491 * Upgrade this object to another object,
492 * while private data stays intact.
493 *
Nils Diewald2fe12e12015-03-06 16:47:06 +0000494 * @param {Object} An object with properties.
Nils Diewald86dad5b2015-01-28 15:09:07 +0000495 */
496 upgradeTo : function (props) {
497 for (var prop in props) {
Akrone4961b12017-05-10 21:04:46 +0200498 this[prop] = props[prop];
Nils Diewald86dad5b2015-01-28 15:09:07 +0000499 };
500 return this;
501 },
502
Nils Diewald7148c6f2015-05-04 15:07:53 +0000503
Nils Diewald86dad5b2015-01-28 15:09:07 +0000504 /**
Akron97752a72016-05-25 14:43:07 +0200505 * Filter the list and make it visible.
506 * This is always called once the prefix changes.
Nils Diewald86dad5b2015-01-28 15:09:07 +0000507 *
508 * @param {string} Prefix for filtering the list
509 */
Akron6ed13992016-05-23 18:06:05 +0200510 show : function (active) {
Leo Repp56904d22021-04-26 15:53:22 +0200511 //Upon change please also update alwaysmenu.js (only two lines new there)
Akronc53cfc82020-10-19 11:00:58 +0200512 const t = this;
Nils Diewald86dad5b2015-01-28 15:09:07 +0000513
Akron5240b8c2016-05-20 09:17:41 +0200514 // show menu based on initial offset
Akronc53cfc82020-10-19 11:00:58 +0200515 t._unmark(); // Unmark everything that was marked before
516 t.removeItems();
Akron6ed13992016-05-23 18:06:05 +0200517
518 // Initialize the list
Akronc53cfc82020-10-19 11:00:58 +0200519 if (!t._initList()) {
Akron6ac58442016-05-24 16:52:29 +0200520
Akrone4961b12017-05-10 21:04:46 +0200521 // The prefix is not active
Akronc53cfc82020-10-19 11:00:58 +0200522 t._prefix.active(true);
Akron6ed13992016-05-23 18:06:05 +0200523
Akrone4961b12017-05-10 21:04:46 +0200524 // finally show the element
Akron24aa0052020-11-10 11:00:34 +0100525 t._el.classList.add('visible');
Akrone4961b12017-05-10 21:04:46 +0200526
527 return true;
Akron6ed13992016-05-23 18:06:05 +0200528 };
529
Akronc53cfc82020-10-19 11:00:58 +0200530 let offset = 0;
Nils Diewald86dad5b2015-01-28 15:09:07 +0000531
Akron9c2f9382016-05-25 16:36:04 +0200532 // Set a chosen value to active and move the viewport
Akron6ed13992016-05-23 18:06:05 +0200533 if (arguments.length === 1) {
534
Akrone4961b12017-05-10 21:04:46 +0200535 // Normalize active value
536 if (active < 0) {
537 active = 0;
538 }
Akronc53cfc82020-10-19 11:00:58 +0200539 else if (active >= t.liveLength()) {
540 active = t.liveLength() - 1;
Akrone4961b12017-05-10 21:04:46 +0200541 };
Akron6ed13992016-05-23 18:06:05 +0200542
Akrone4961b12017-05-10 21:04:46 +0200543 // Item is outside the first viewport
Akronc53cfc82020-10-19 11:00:58 +0200544 if (active >= t._limit) {
Akrone4961b12017-05-10 21:04:46 +0200545 offset = active;
Akronc53cfc82020-10-19 11:00:58 +0200546 const newOffset = t.liveLength() - t._limit;
547 if (offset > newOffset) {
548 offset = newOffset;
Akrone4961b12017-05-10 21:04:46 +0200549 };
550 };
551
Akronc53cfc82020-10-19 11:00:58 +0200552 t.position = active;
Akron6ed13992016-05-23 18:06:05 +0200553 }
554
Akron9c2f9382016-05-25 16:36:04 +0200555 // Choose the first item
Akronc53cfc82020-10-19 11:00:58 +0200556 else if (t._firstActive) {
557 t.position = 0;
Akron47c086c2016-05-18 21:22:06 +0200558 }
Akroncb351d62016-05-19 23:10:33 +0200559
Akron9c2f9382016-05-25 16:36:04 +0200560 // Choose no item
Akron47c086c2016-05-18 21:22:06 +0200561 else {
Akronc53cfc82020-10-19 11:00:58 +0200562 t.position = -1;
Akron6ed13992016-05-23 18:06:05 +0200563 };
564
Akronc53cfc82020-10-19 11:00:58 +0200565 t.offset = offset;
566 t._showItems(offset); // Show new item list
Akron6ed13992016-05-23 18:06:05 +0200567
568 // Make chosen value active
Akronc53cfc82020-10-19 11:00:58 +0200569 if (t.position !== -1) {
570 t.liveItem(t.position).active(true);
Akron6ed13992016-05-23 18:06:05 +0200571 };
Akron37513a62015-11-17 01:07:11 +0100572
Akron5240b8c2016-05-20 09:17:41 +0200573 // The prefix is not active
Akronc53cfc82020-10-19 11:00:58 +0200574 t._prefix.active(false);
Nils Diewald86dad5b2015-01-28 15:09:07 +0000575
Akron5240b8c2016-05-20 09:17:41 +0200576 // finally show the element
Akron24aa0052020-11-10 11:00:34 +0100577 t._el.classList.add('visible');
Nils Diewald2fe12e12015-03-06 16:47:06 +0000578
Nils Diewald86dad5b2015-01-28 15:09:07 +0000579 // Add classes for rolling menus
Akronc53cfc82020-10-19 11:00:58 +0200580 t._boundary(true);
Akron6bb71582016-06-10 20:41:08 +0200581
Nils Diewald59c02fc2015-03-07 01:29:09 +0000582 return true;
Nils Diewald86dad5b2015-01-28 15:09:07 +0000583 },
584
Nils Diewald7148c6f2015-05-04 15:07:53 +0000585
586 /**
587 * Hide the menu and call the onHide callback.
588 */
Nils Diewald2fe12e12015-03-06 16:47:06 +0000589 hide : function () {
Akronc53cfc82020-10-19 11:00:58 +0200590 if (!this.dontHide) {
591 this.removeItems();
592 this._prefix.clear();
593 this.onHide();
Akron24aa0052020-11-10 11:00:34 +0100594 this._el.classList.remove('visible');
Akronc53cfc82020-10-19 11:00:58 +0200595 }
Akron24aa0052020-11-10 11:00:34 +0100596 // this._el.blur();
Nils Diewald86dad5b2015-01-28 15:09:07 +0000597 },
598
Akronc53cfc82020-10-19 11:00:58 +0200599
Nils Diewald7148c6f2015-05-04 15:07:53 +0000600 /**
601 * Function released when the menu hides.
602 * This method is expected to be overridden.
603 */
Nils Diewald5c5a7472015-04-02 22:13:38 +0000604 onHide : function () {},
605
Akronc53cfc82020-10-19 11:00:58 +0200606
Nils Diewald86dad5b2015-01-28 15:09:07 +0000607 /**
608 * Get the prefix for filtering,
Nils Diewald2fe12e12015-03-06 16:47:06 +0000609 * e.g. &quot;ve&quot; for &quot;verb&quot;
Nils Diewald86dad5b2015-01-28 15:09:07 +0000610 */
Nils Diewald5975d702015-03-09 17:45:42 +0000611 prefix : function (pref) {
612 if (arguments.length === 1) {
Akrone4961b12017-05-10 21:04:46 +0200613 this._prefix.value(pref);
614 return this;
Nils Diewald5975d702015-03-09 17:45:42 +0000615 };
616 return this._prefix.value();
Nils Diewald86dad5b2015-01-28 15:09:07 +0000617 },
618
Akronc53cfc82020-10-19 11:00:58 +0200619
Akronc7448732016-04-27 14:06:58 +0200620 /**
621 * Get the lengthField object.
622 */
623 lengthField : function () {
624 return this._lengthField;
625 },
Nils Diewald7148c6f2015-05-04 15:07:53 +0000626
Akronc53cfc82020-10-19 11:00:58 +0200627
Akron5240b8c2016-05-20 09:17:41 +0200628 /**
629 * Get the associated slider object.
630 */
631 slider : function () {
632 return this._slider;
Nils Diewald86dad5b2015-01-28 15:09:07 +0000633 },
634
Akron5240b8c2016-05-20 09:17:41 +0200635
Nils Diewald86dad5b2015-01-28 15:09:07 +0000636 /**
637 * Delete all visible items from the menu element
638 */
Leo Reppe5bd35a2021-05-05 15:28:26 +0200639
640 removeItems : function () {
641 const liElements=this._el.getElementsByTagName("LI");
642 while (liElements.length>0){
643 this._el.removeChild(liElements[0]);
Nils Diewald5975d702015-03-09 17:45:42 +0000644 };
Leo Reppe5bd35a2021-05-05 15:28:26 +0200645 },
Leo Repp56904d22021-04-26 15:53:22 +0200646
Nils Diewald86dad5b2015-01-28 15:09:07 +0000647
Akronc53cfc82020-10-19 11:00:58 +0200648
Nils Diewald2fe12e12015-03-06 16:47:06 +0000649 /**
650 * Get a specific item from the complete list
651 *
652 * @param {number} index of the list item
653 */
654 item : function (index) {
655 return this._items[index]
656 },
657
658
Nils Diewald86dad5b2015-01-28 15:09:07 +0000659 /**
660 * Get a specific item from the filtered list
661 *
662 * @param {number} index of the list item
Nils Diewald2fe12e12015-03-06 16:47:06 +0000663 * in the filtered list
Nils Diewald86dad5b2015-01-28 15:09:07 +0000664 */
665 liveItem : function (index) {
666 if (this._list === undefined)
Akrone4961b12017-05-10 21:04:46 +0200667 if (!this._initList())
668 return;
Nils Diewald86dad5b2015-01-28 15:09:07 +0000669
670 return this._items[this._list[index]];
671 },
672
Nils Diewald86dad5b2015-01-28 15:09:07 +0000673
674 /**
Akron5240b8c2016-05-20 09:17:41 +0200675 * Get a specific item from the viewport list
Nils Diewald86dad5b2015-01-28 15:09:07 +0000676 *
677 * @param {number} index of the list item
Nils Diewald2fe12e12015-03-06 16:47:06 +0000678 * in the visible list
Nils Diewald86dad5b2015-01-28 15:09:07 +0000679 */
680 shownItem : function (index) {
681 if (index >= this.limit())
Akrone4961b12017-05-10 21:04:46 +0200682 return;
Akronc53cfc82020-10-19 11:00:58 +0200683
Akron9c4d1ae2016-05-25 21:43:22 +0200684 return this.liveItem(this.offset + index);
Nils Diewald86dad5b2015-01-28 15:09:07 +0000685 },
686
687
Nils Diewald2fe12e12015-03-06 16:47:06 +0000688 /**
Akron9c4d1ae2016-05-25 21:43:22 +0200689 * Get the length of the full item list
Nils Diewald2fe12e12015-03-06 16:47:06 +0000690 */
691 length : function () {
692 return this._items.length;
693 },
694
695
696 /**
Akron5240b8c2016-05-20 09:17:41 +0200697 * Length of the filtered item list.
698 */
699 liveLength : function () {
700 if (this._list === undefined)
Akrone4961b12017-05-10 21:04:46 +0200701 this._initList();
Akron5240b8c2016-05-20 09:17:41 +0200702 return this._list.length;
703 },
704
Akrone817b882018-08-31 14:09:17 +0200705
Akron5240b8c2016-05-20 09:17:41 +0200706 /**
Nils Diewald2fe12e12015-03-06 16:47:06 +0000707 * Make the next item in the filtered menu active
Nils Diewald86dad5b2015-01-28 15:09:07 +0000708 */
709 next : function () {
Leo Repp56904d22021-04-26 15:53:22 +0200710 //Upon change please update alwaysmenu.js next
Akronc53cfc82020-10-19 11:00:58 +0200711 const t = this;
Nils Diewald5975d702015-03-09 17:45:42 +0000712
Akronb38afb22016-05-25 19:30:01 +0200713 // No list
Akronc53cfc82020-10-19 11:00:58 +0200714 if (t.liveLength() === 0)
Akrone4961b12017-05-10 21:04:46 +0200715 return;
Akronb38afb22016-05-25 19:30:01 +0200716
Akron9c4d1ae2016-05-25 21:43:22 +0200717 // Deactivate old item
Akronc53cfc82020-10-19 11:00:58 +0200718 if (t.position !== -1 && !t._prefix.active()) {
719 t.liveItem(t.position).active(false);
Akron9c4d1ae2016-05-25 21:43:22 +0200720 };
Nils Diewalde8518f82015-03-18 22:41:49 +0000721
Akron9c4d1ae2016-05-25 21:43:22 +0200722 // Get new active item
Akronc53cfc82020-10-19 11:00:58 +0200723 t.position++;
724 let newItem = t.liveItem(t.position);
Nils Diewald86dad5b2015-01-28 15:09:07 +0000725
Nils Diewald5975d702015-03-09 17:45:42 +0000726 // The next element is undefined - roll to top or to prefix
Nils Diewald86dad5b2015-01-28 15:09:07 +0000727 if (newItem === undefined) {
Nils Diewald5975d702015-03-09 17:45:42 +0000728
Akrone4961b12017-05-10 21:04:46 +0200729 // Activate prefix
Akronc53cfc82020-10-19 11:00:58 +0200730 const prefix = this._prefix;
Nils Diewald5975d702015-03-09 17:45:42 +0000731
Akrone4961b12017-05-10 21:04:46 +0200732 // Prefix is set and not active - choose!
733 if (prefix.isSet() && !prefix.active()) {
Akronc53cfc82020-10-19 11:00:58 +0200734 t.position--;
Akrone4961b12017-05-10 21:04:46 +0200735 prefix.active(true);
736 return;
737 }
Akron9c4d1ae2016-05-25 21:43:22 +0200738
Akrone4961b12017-05-10 21:04:46 +0200739 // Choose first item
740 else {
Akronc53cfc82020-10-19 11:00:58 +0200741 newItem = t.liveItem(0);
Akrone4961b12017-05-10 21:04:46 +0200742 // choose first item
Akronc53cfc82020-10-19 11:00:58 +0200743 t.position = 0;
744 t._showItems(0);
Akrone4961b12017-05-10 21:04:46 +0200745 };
Nils Diewald86dad5b2015-01-28 15:09:07 +0000746 }
747
Akron5a1f5bb2016-05-23 22:00:39 +0200748 // The next element is after the viewport - roll down
Akronc53cfc82020-10-19 11:00:58 +0200749 else if (t.position >= (t.limit() + t.offset)) {
750 t.screen(t.position - t.limit() + 1);
Akron5a1f5bb2016-05-23 22:00:39 +0200751 }
752
753 // The next element is before the viewport - roll up
Akronc53cfc82020-10-19 11:00:58 +0200754 else if (t.position <= t.offset) {
755 t.screen(t.position);
Nils Diewald86dad5b2015-01-28 15:09:07 +0000756 };
Nils Diewald5975d702015-03-09 17:45:42 +0000757
Akronc53cfc82020-10-19 11:00:58 +0200758 t._prefix.active(false);
Nils Diewald86dad5b2015-01-28 15:09:07 +0000759 newItem.active(true);
760 },
761
Akronc53cfc82020-10-19 11:00:58 +0200762
Nils Diewalde8518f82015-03-18 22:41:49 +0000763 /*
Nils Diewald86dad5b2015-01-28 15:09:07 +0000764 * Make the previous item in the menu active
765 */
Nils Diewald86dad5b2015-01-28 15:09:07 +0000766 prev : function () {
Leo Repp56904d22021-04-26 15:53:22 +0200767 //Upon Change please update alwaysmenu.js prev
Akronc53cfc82020-10-19 11:00:58 +0200768 const t = this;
Nils Diewald2d210752015-03-09 19:01:15 +0000769
Akronb38afb22016-05-25 19:30:01 +0200770 // No list
Akronc53cfc82020-10-19 11:00:58 +0200771 if (t.liveLength() === 0)
Akrone4961b12017-05-10 21:04:46 +0200772 return;
Akronb38afb22016-05-25 19:30:01 +0200773
Akron9c4d1ae2016-05-25 21:43:22 +0200774 // Deactivate old item
Akronc53cfc82020-10-19 11:00:58 +0200775 if (!t._prefix.active()) {
Akron9c4d1ae2016-05-25 21:43:22 +0200776
Akrone4961b12017-05-10 21:04:46 +0200777 // No active element set
Akronc53cfc82020-10-19 11:00:58 +0200778 if (t.position === -1) {
779 t.position = t.liveLength();
Akrone4961b12017-05-10 21:04:46 +0200780 }
Akron9c4d1ae2016-05-25 21:43:22 +0200781
Akrone4961b12017-05-10 21:04:46 +0200782 // No active element set
783 else {
Akronc53cfc82020-10-19 11:00:58 +0200784 t.liveItem(t.position--).active(false);
Akrone4961b12017-05-10 21:04:46 +0200785 };
Nils Diewald2d210752015-03-09 19:01:15 +0000786 };
787
Akron9c4d1ae2016-05-25 21:43:22 +0200788 // Get new active item
Akronc53cfc82020-10-19 11:00:58 +0200789 let newItem = t.liveItem(t.position);
Nils Diewald86dad5b2015-01-28 15:09:07 +0000790
791 // The previous element is undefined - roll to bottom
792 if (newItem === undefined) {
Nils Diewald5975d702015-03-09 17:45:42 +0000793
Akrone4961b12017-05-10 21:04:46 +0200794 // Activate prefix
Akronc53cfc82020-10-19 11:00:58 +0200795 const prefix = t._prefix;
796 let offset = t.liveLength() - t.limit();
Akrone4961b12017-05-10 21:04:46 +0200797
798 // Normalize offset
799 offset = offset < 0 ? 0 : offset;
Nils Diewald2d210752015-03-09 19:01:15 +0000800
Akrone4961b12017-05-10 21:04:46 +0200801 // Choose the last item
Akronc53cfc82020-10-19 11:00:58 +0200802 t.position = t.liveLength() - 1;
Akrone4961b12017-05-10 21:04:46 +0200803
804 // Prefix is set and not active - choose!
805 if (prefix.isSet() && !prefix.active()) {
Akronc53cfc82020-10-19 11:00:58 +0200806 t.position++;
Akrone4961b12017-05-10 21:04:46 +0200807 prefix.active(true);
Akronc53cfc82020-10-19 11:00:58 +0200808 t.offset = offset;
Akrone4961b12017-05-10 21:04:46 +0200809 return;
810 }
Nils Diewald2d210752015-03-09 19:01:15 +0000811
Akrone4961b12017-05-10 21:04:46 +0200812 // Choose last item
813 else {
Akronc53cfc82020-10-19 11:00:58 +0200814 newItem = t.liveItem(t.position);
815 t._showItems(offset);
Akrone4961b12017-05-10 21:04:46 +0200816 };
Nils Diewald86dad5b2015-01-28 15:09:07 +0000817 }
818
Akron5a1f5bb2016-05-23 22:00:39 +0200819 // The previous element is before the view - roll up
Akronc53cfc82020-10-19 11:00:58 +0200820 else if (t.position < t.offset) {
821 t.screen(t.position);
Akron5a1f5bb2016-05-23 22:00:39 +0200822 }
823
824 // The previous element is after the view - roll down
Akronc53cfc82020-10-19 11:00:58 +0200825 else if (t.position >= (t.limit() + t.offset)) {
826 t.screen(t.position - t.limit() + 2);
Nils Diewald86dad5b2015-01-28 15:09:07 +0000827 };
Nils Diewald2fe12e12015-03-06 16:47:06 +0000828
Akronc53cfc82020-10-19 11:00:58 +0200829 t._prefix.active(false);
Nils Diewald86dad5b2015-01-28 15:09:07 +0000830 newItem.active(true);
831 },
Nils Diewald86dad5b2015-01-28 15:09:07 +0000832
Akronc53cfc82020-10-19 11:00:58 +0200833
Akron3c2730f2016-05-24 15:08:29 +0200834 /**
835 * Move the page up by limit!
836 */
837 pageUp : function () {
Akron9c4d1ae2016-05-25 21:43:22 +0200838 this.screen(this.offset - this.limit());
Akron3c2730f2016-05-24 15:08:29 +0200839 },
840
841
842 /**
843 * Move the page down by limit!
844 */
845 pageDown : function () {
Akron9c4d1ae2016-05-25 21:43:22 +0200846 this.screen(this.offset + this.limit());
Akron3c2730f2016-05-24 15:08:29 +0200847 },
848
Nils Diewald86dad5b2015-01-28 15:09:07 +0000849
Akrone817b882018-08-31 14:09:17 +0200850 /**
851 * Move the view one item up
852 */
853 viewUp : function () {
854 this.screen(this.offset - 1);
855 },
856
857
858 /**
859 * Move the view one item down
860 */
861 viewDown : function () {
862 this.screen(this.offset + 1);
863 },
864
Akronc53cfc82020-10-19 11:00:58 +0200865
Akron5240b8c2016-05-20 09:17:41 +0200866 // Unmark all items
867 _unmark : function () {
Akron678c26f2020-10-09 08:52:50 +0200868 this._list.forEach(function(it){
Akronc53cfc82020-10-19 11:00:58 +0200869 const item = this._items[it];
Akrone4961b12017-05-10 21:04:46 +0200870 item.lowlight();
Akron678c26f2020-10-09 08:52:50 +0200871 item.active(false);
872 }, this);
Akron5240b8c2016-05-20 09:17:41 +0200873 },
874
Akronc53cfc82020-10-19 11:00:58 +0200875
Akron5240b8c2016-05-20 09:17:41 +0200876 // Set boundary for viewport
877 _boundary : function (bool) {
Akron55a343b2018-04-06 19:57:36 +0200878 if (this._list.length === 0)
879 return;
Akronc53cfc82020-10-19 11:00:58 +0200880
Akron5240b8c2016-05-20 09:17:41 +0200881 this.item(this._list[0]).noMore(bool);
882 this.item(this._list[this._list.length - 1]).noMore(bool);
883 },
884
885
886 // Append Items that should be shown
887 _showItems : function (off) {
Akronc53cfc82020-10-19 11:00:58 +0200888 const t = this;
Akron5240b8c2016-05-20 09:17:41 +0200889
Akrona92fd8d2016-05-24 21:13:41 +0200890 // optimization: scroll down one step
Akronc53cfc82020-10-19 11:00:58 +0200891 if (t.offset === (off - 1)) {
892 t.offset = off;
Akron9c4d1ae2016-05-25 21:43:22 +0200893
Akrone4961b12017-05-10 21:04:46 +0200894 // Remove the HTML node from the first item
895 // leave lengthField/prefix/slider
Leo Repp56904d22021-04-26 15:53:22 +0200896 t._el.removeChild(t._el.children[this._notItemElements]);
Akronc53cfc82020-10-19 11:00:58 +0200897
898 t._append(
899 t._list[t.offset + t.limit() - 1]
900 );
Akrona92fd8d2016-05-24 21:13:41 +0200901 }
Akron5240b8c2016-05-20 09:17:41 +0200902
Akrona92fd8d2016-05-24 21:13:41 +0200903 // optimization: scroll up one step
Akronc53cfc82020-10-19 11:00:58 +0200904 else if (t.offset === (off + 1)) {
905 t.offset = off;
Akron9c4d1ae2016-05-25 21:43:22 +0200906
Akrone4961b12017-05-10 21:04:46 +0200907 // Remove the HTML node from the last item
Akron24aa0052020-11-10 11:00:34 +0100908 t._el.removeChild(t._el.lastChild);
Akron9c4d1ae2016-05-25 21:43:22 +0200909
Akronc53cfc82020-10-19 11:00:58 +0200910 t._prepend(t._list[t.offset]);
Akrona92fd8d2016-05-24 21:13:41 +0200911 }
Akronc53cfc82020-10-19 11:00:58 +0200912
Akrona92fd8d2016-05-24 21:13:41 +0200913 else {
Akronc53cfc82020-10-19 11:00:58 +0200914 t.offset = off;
Akron5240b8c2016-05-20 09:17:41 +0200915
Akrone4961b12017-05-10 21:04:46 +0200916 // Remove all items
Akronc53cfc82020-10-19 11:00:58 +0200917 t.removeItems();
Akron5240b8c2016-05-20 09:17:41 +0200918
Akrone4961b12017-05-10 21:04:46 +0200919 // Use list
Akronc53cfc82020-10-19 11:00:58 +0200920 let shown = 0;
Akron5240b8c2016-05-20 09:17:41 +0200921
Akronc53cfc82020-10-19 11:00:58 +0200922 for (let i = 0; i < t._list.length; i++) {
Akrona92fd8d2016-05-24 21:13:41 +0200923
Akrone4961b12017-05-10 21:04:46 +0200924 // Don't show - it's before offset
925 shown++;
926 if (shown <= off)
927 continue;
Akrona92fd8d2016-05-24 21:13:41 +0200928
Akronc53cfc82020-10-19 11:00:58 +0200929 t._append(t._list[i]);
Akrone4961b12017-05-10 21:04:46 +0200930
Akronc53cfc82020-10-19 11:00:58 +0200931 if (shown >= (t.limit() + off))
Akrone4961b12017-05-10 21:04:46 +0200932 break;
933 };
Akron5240b8c2016-05-20 09:17:41 +0200934 };
935
936 // set the slider to the new offset
Akronc53cfc82020-10-19 11:00:58 +0200937 t._slider.offset(t.offset);
Akron5240b8c2016-05-20 09:17:41 +0200938 },
939
940
941 // Append item to the shown list based on index
942 _append : function (i) {
Akronc53cfc82020-10-19 11:00:58 +0200943 const item = this.item(i);
Akron5240b8c2016-05-20 09:17:41 +0200944
945 // Highlight based on prefix
Akrona92fd8d2016-05-24 21:13:41 +0200946 if (this.prefix().length > 0) {
Akrone4961b12017-05-10 21:04:46 +0200947 item.highlight(this.prefix().toLowerCase());
Akrona92fd8d2016-05-24 21:13:41 +0200948 };
949
Akron5240b8c2016-05-20 09:17:41 +0200950 // Append element
951 this.element().appendChild(item.element());
952 },
953
954
955 // Prepend item to the shown list based on index
956 _prepend : function (i) {
Akronc53cfc82020-10-19 11:00:58 +0200957 const item = this.item(i);
Akron5240b8c2016-05-20 09:17:41 +0200958
959 // Highlight based on prefix
Akrona92fd8d2016-05-24 21:13:41 +0200960 if (this.prefix().length > 0) {
Akrone4961b12017-05-10 21:04:46 +0200961 item.highlight(this.prefix().toLowerCase());
Akrona92fd8d2016-05-24 21:13:41 +0200962 };
Akron5240b8c2016-05-20 09:17:41 +0200963
Akronc53cfc82020-10-19 11:00:58 +0200964 const e = this.element();
Akron9c4d1ae2016-05-25 21:43:22 +0200965
Akron5240b8c2016-05-20 09:17:41 +0200966 // Append element after lengthField/prefix/slider
967 e.insertBefore(
Akrone4961b12017-05-10 21:04:46 +0200968 item.element(),
Leo Repp56904d22021-04-26 15:53:22 +0200969 e.children[this._notItemElements]
Akron5240b8c2016-05-20 09:17:41 +0200970 );
Leo Repp7b0fbf92021-07-14 11:25:59 +0200971 },
972
973 /**
974 * A Method for generating an array of nodes, that are direct descendants of the menus
975 * element node, using a tag tagName as a parameter. Supposed to be used by the specification only.
976 * @param {String} tagName The tag the children are looked for by
977 * @returns An array of children nodes with tag tagName
978 */
979 directElementChildrenByTagName : function (tagName) {
980 const tagElementsCollection=this._el.getElementsByTagName(tagName);
981 //var tagElements = Array.from(tagElementsCollection);
982 //var tagElements = [...tagElementsCollection];
983 //This one has the best compatability:
984 var tagElements = Array.prototype.slice.call(tagElementsCollection);
985 const t = this;
986 //filter by actually being direct child node
987 tagElements = tagElements.filter(element => element.parentNode === t._el);
988 return tagElements;
Nils Diewald2fe12e12015-03-06 16:47:06 +0000989 }
Nils Diewald86dad5b2015-01-28 15:09:07 +0000990 };
Nils Diewald0e6992a2015-04-14 20:13:52 +0000991});