blob: 6c4b7594c72416621942d2c6bdf6247fbe9a0a00 [file] [log] [blame]
Leo Reppd162b2e2021-06-30 13:51:07 +02001/**
2 * Container for several containerItem style items. Functions like a mini menu with next, prev, add etc propagation,
3 * but no event handling or slider or lengthField. Supposed to be subelement to a (container)menu class item.
4 *
5 * @author Leo Repp
6 */
7
8"use strict";
9define([
Leo Repp57997402021-08-18 16:37:52 +020010 'container/containeritem'
Leo Reppd162b2e2021-06-30 13:51:07 +020011], function (
12 defaultContainerItemClass
13) {
14
15 return {
16 /**
Leo Reppc66268e2021-10-28 11:44:35 +020017 *
18 * @param {Array<object>} listOfContainerItems List of items that will be placed within the container and that realise some of the functions supplied in containeritem.js
19 * @param {object} params May contain attribute containerItemClass for a base class all containerItems build upon
20 * @returns The container object
21 */
Leo Reppd162b2e2021-06-30 13:51:07 +020022 create : function (listOfContainerItems, params) {
23 var obj = Object.create(this);
24 obj._init(listOfContainerItems, params);
25 return obj;
26 },
27
28 _init : function (listOfContainerItems, params){
29 if (params !== undefined && params["containerItemClass"] !== undefined){
30 this._containerItemClass = params["containerItemClass"];
31 } else {
32 this._containerItemClass = defaultContainerItemClass;
33 };
Leo Repp84539162021-10-25 12:06:07 +020034 var el = document.createElement("ul");
35 el.style.outline = 0;
36 el.setAttribute('tabindex', 0);
37 el.classList.add('menu', 'container'); //container class allows for more stylesheet changes
38
39 this._el = el;
40 this._cItemPrefix = undefined; //required for re-setting the menus pointer correctly
41 // after having upgraded a new item scss style to the prefix object.
Leo Reppd162b2e2021-06-30 13:51:07 +020042
43 this.items = new Array();
Leo Repp050a7342021-10-25 11:05:32 +020044 //items are stored in the order they are added in. This includes the prefix.
Leo Reppd162b2e2021-06-30 13:51:07 +020045 if (listOfContainerItems !== undefined) {
46 for (let item of listOfContainerItems) {
47 this.addItem(item);
48 }
49 }
50
Leo Repp050a7342021-10-25 11:05:32 +020051 this.position = undefined; //undefined = not in container,
52 // 0 to length-1 = in container
53
54 this._prefixPosition = undefined; //Required so that switching
55 // to prefix by default is supported
56
Leo Repp57997402021-08-18 16:37:52 +020057 this._menu = undefined // add later
58
Leo Reppd162b2e2021-06-30 13:51:07 +020059
60
61 //t._el.classList.add('visible'); //Done by containermenu
Leo Repp57997402021-08-18 16:37:52 +020062 },
Leo Reppd162b2e2021-06-30 13:51:07 +020063
Leo Repp84539162021-10-25 12:06:07 +020064
Leo Repp57997402021-08-18 16:37:52 +020065 /**
Leo Repp84539162021-10-25 12:06:07 +020066 * Adds a static item to this container by creating a standard containerItem as specified when this container was created,
67 * then upgrading it to the item passed to this function, and calling element() and content(). For a full list of supported functions see
68 * containeritem.js .
69 * Example:
70 *
71 * menu.container().addItem(
72 * {defaultTextValue : "dynamic", onClick : function (e) { ... }
73 * )
74 *
75 * For a full demo see containermenudemo.js.
76 *
77 * @param {Object} item An object with any number of functions like in containeritem.js or an attribute defaultTextValue,
78 * as well as any number of own properties.
79 * @returns the new use-ready containerItem
80 */
Leo Reppd162b2e2021-06-30 13:51:07 +020081 addItem : function (item) {
Leo Repp57997402021-08-18 16:37:52 +020082 //Call Order: First _containerItemClass is created and then upgraded To whatever object is passed to this function
83 //Then container calls first element() and then container()
Leo Reppd162b2e2021-06-30 13:51:07 +020084 var cItem = this._containerItemClass.create().upgradeTo(item);
85 cItem._menu = this._menu; //if not set then undefined, but thats OK
86 this.items.push(cItem);
Leo Repp84539162021-10-25 12:06:07 +020087 this.element().appendChild(cItem.element());
88 cItem.initContent(); // create its textNode
Leo Repp57997402021-08-18 16:37:52 +020089 if (this._cItemPrefix !== undefined){ //this must be dynamic adding of CIs, move prefix to the back
Leo Repp84539162021-10-25 12:06:07 +020090 //adjust the prefix' position within .items to be in the back
Leo Repp57997402021-08-18 16:37:52 +020091 this.items.splice(this.items.indexOf(this._cItemPrefix) , 1); //remove cItemPrefix
92 this.items.push(this._cItemPrefix); //and move it to the end;
Leo Repp84539162021-10-25 12:06:07 +020093 //adjust the prefix' HTML elements position
94 this.element().removeChild(this._cItemPrefix.element());
95 this.element().appendChild(this._cItemPrefix.element());
96 //adjust the prefix' stored position
97 this._prefixPosition = this.items.length;
Leo Repp57997402021-08-18 16:37:52 +020098 };
Leo Reppd162b2e2021-06-30 13:51:07 +020099 return cItem;
100 },
101
102 addMenu : function (menu) {
103 this._menu = menu;
Leo Repp57997402021-08-18 16:37:52 +0200104 if (this._cItemPrefix !== undefined) {
105 this._menu._prefix = this._cItemPrefix; // better than going via classList or something
Leo Reppd162b2e2021-06-30 13:51:07 +0200106 };
107 for (let item of this.items) {
108 item._menu=menu;
Leo Repp84539162021-10-25 12:06:07 +0200109 };
Leo Reppd162b2e2021-06-30 13:51:07 +0200110 },
111
112 addPrefix : function (prefix) {
113 prefix.isSelectable = function () {
114 return this.isSet(); //TODO check!
Leo Repp84539162021-10-25 12:06:07 +0200115 };
Leo Repp050a7342021-10-25 11:05:32 +0200116 this._prefixPosition = this.items.length;
Leo Repp84539162021-10-25 12:06:07 +0200117 prefix.initContent = function () {}; //Does not need a textNode Child!
Leo Reppd162b2e2021-06-30 13:51:07 +0200118 var prefItem = this.addItem(prefix);
Leo Repp57997402021-08-18 16:37:52 +0200119 this._cItemPrefix = prefItem;
120 prefItem._el["onclick"] = prefItem.onclick.bind(prefItem);
Leo Reppd162b2e2021-06-30 13:51:07 +0200121 if (this._menu !== undefined){
122 this._menu._prefix=prefItem;
Leo Repp84539162021-10-25 12:06:07 +0200123 };
Leo Reppd162b2e2021-06-30 13:51:07 +0200124 },
Leo Reppc66268e2021-10-28 11:44:35 +0200125
Leo Repp57997402021-08-18 16:37:52 +0200126 //Taken from Branch 5133
Leo Reppc66268e2021-10-28 11:44:35 +0200127 /**
128 * Remove a containeritem from the container by identity. Should not be used with prefix.
129 * If the active item is removed, this calls menu.next().
130 * @param {containerItemClass} item The item to be removed.
131 */
132 removeItem : function (item) {
133 if (this.items.indexOf(item) === -1){ // This is returned if indexOf cannot find the item.
134 KorAP.log(0,"Invalid item in containers removeItemByIndex: This containerItem is not contained", "container.js");
135 return;
136 };
Leo Repp57997402021-08-18 16:37:52 +0200137 if (item === this._cItemPrefix) {
138 KorAP.log(0,"Tried to remove the prefix item. Illegal.");
139 console.log("Tried to remove the prefix item by calling removeItem. Please cut all connections from the menu to prefix and then\
140 the connection container._cItemPrefix before calling this function if you really want to remove the prefix.","container.js");
Leo Reppc66268e2021-10-28 11:44:35 +0200141 return;
142 };
143 if (item.active()) {
144 this._menu.next();
145 };
146 item._menu=undefined;
147 this._el.removeChild(item.element());
148 this.items.splice(this.items.indexOf(item) , 1);
149 },
Leo Reppd162b2e2021-06-30 13:51:07 +0200150
Leo Repp84539162021-10-25 12:06:07 +0200151
Leo Reppd162b2e2021-06-30 13:51:07 +0200152 /**
Leo Reppc66268e2021-10-28 11:44:35 +0200153 * Remove a containeritem from the container by its index. Should not be used with prefix.
154 * CAUTION liveIndex, so what you see, is not the actual index within the containerItem list.
155 * This can be accessed with container.items . If the active item is removed, this calls menu.next().
156 * @param {Int} index The index of the item to be removed.
Leo Reppd162b2e2021-06-30 13:51:07 +0200157 */
Leo Reppc66268e2021-10-28 11:44:35 +0200158 removeItemByIndex : function (index) {
159 if (index < 0 || index >= this.length()){
160 KorAP.log(0,"Invalid index in containers removeItemByIndex: "+index, "container.js");
161 return;
162 };
163 this.removeItem(this.items[index]); //CAUTION liveIndex (what you see) != index within the list!
164 },
165
166 /**
167 * Exit the container unconditionally. Required so that active returns the
168 * correct result. Called when the prefix or similar resets the currently visual
169 * field.
170 */
Leo Reppd162b2e2021-06-30 13:51:07 +0200171 exit : function () {
172 if (this.position !== undefined) {
173 this.item().active(false);
174 this.position = undefined;
175 };
176 },
177
178 element : function () {
179 return this._el;
180 },
181
182 destroy : function () {
183 for (let item of this.items){
184 delete item['_menu'];
185 }
186 },
187
188 /**
Leo Reppc66268e2021-10-28 11:44:35 +0200189 * @returns whether an item within the container is active (by checking this.position)
190 */
Leo Reppd162b2e2021-06-30 13:51:07 +0200191 active : function () {
192 return this.position !== undefined;
193 },
194
195 /**
Leo Reppc66268e2021-10-28 11:44:35 +0200196 * Getter for items
197 * @param {Integer} index [optional] Index of to select item. If left blank this.position.
198 * @returns item at location index
199 */
Leo Reppd162b2e2021-06-30 13:51:07 +0200200 item : function (index) {
201 if (index === undefined) return this.items[this.position];
202 return this.items[index];
203 },
204
205 /**
Leo Repp050a7342021-10-25 11:05:32 +0200206 *
207 * Make the container active without having called prev or next.
208 * Occurs whenever the prefix makes the
209 * menus list empty while we had it selected.
210 * This potentially requires adjusting this.position.
211 */
Leo Repp57997402021-08-18 16:37:52 +0200212 makeActive : function () {
Leo Repp050a7342021-10-25 11:05:32 +0200213 if (this.position === undefined) {
Leo Repp57997402021-08-18 16:37:52 +0200214 if (this._cItemPrefix.isSelectable()) {
Leo Repp050a7342021-10-25 11:05:32 +0200215 this.position = this._prefixPosition; //make prefix active if it exists
216 this.item().active(true);
217 } else if (this.liveLength() > 0) {
218 this.position = 0;
Leo Repp57997402021-08-18 16:37:52 +0200219 this._cItemPrefix.active(false); // usually the menu makes the prefix active anyway.
Leo Repp050a7342021-10-25 11:05:32 +0200220 this.item().active(true);
Leo Repp84539162021-10-25 12:06:07 +0200221 } else {
222 console.log("It appears that both containermenu and its container contain no selectable items.\
223 Let us hope there is no problem. - container.js");
224 };
225 };
Leo Repp050a7342021-10-25 11:05:32 +0200226 },
Leo Reppc66268e2021-10-28 11:44:35 +0200227
Leo Repp050a7342021-10-25 11:05:32 +0200228 /**
Leo Reppc66268e2021-10-28 11:44:35 +0200229 *
230 * Move on to the next item in container. Returns true if we then leave the container, false otherwise.
231 */
Leo Reppd162b2e2021-06-30 13:51:07 +0200232 next : function() {
233 if (this.position !== undefined){
234 this.item().active(false);
235 this.position++;
236 } else {
237 this.position = 0;
238 };
239 if (this.position >= this.length()) {
240 this.position=undefined;
241 return true;
242 };
243 while (!this.item().isSelectable()) {
244 this.position++;
245 if (this.position >= this.length()) {
246 this.position=undefined;
247 return true;
248 }
249 };
250 this.item().active(true);
251 return false;
252 },
253
254 /**
Leo Reppc66268e2021-10-28 11:44:35 +0200255 * Move on to the previous item in container. Returns true if we then leave the container, false otherwise.
256 */
Leo Reppd162b2e2021-06-30 13:51:07 +0200257 prev : function() {
258 if (this.position !== undefined){
259 this.item().active(false);
260 this.position = (this.position-1)
261 } else {
262 this.position = (this.items.length-1);
263 }
264 if (this.position<0) {
265 this.position=undefined;
266 return true;
267 }
268 while (!this.item().isSelectable()) {
269 this.position--;
270 if (this.position<0){
271 this.position=undefined;
272 return true;
273 };
274 };
275 this.item().active(true);
276 return false;
277 },
278
279 further : function () {
280 const item = this.item();
281 if (item["further"] !== undefined) {
282 item["further"].bind(item).apply();
283 };
284 },
285
286 enter : function (event) {
287 this.item().onclick(event);
288 },
289
290 chop : function () {
291 for (let item of this.items) {
292 item.chop();
293 }
294 },
295
296 add : function (letter) {
297 for (let item of this.items) {
298 item.add(letter);
299 }
300 },
301
302 length : function () {
303 return this.items.length;
304 },
305
306 /**
Leo Reppc66268e2021-10-28 11:44:35 +0200307 *
308 * @returns The number of items that are selectable. Is the actual length of the list.
309 */
Leo Reppd162b2e2021-06-30 13:51:07 +0200310 liveLength : function () {
311 var ll = 0;
312 for (let item of this.items){
313 if (item.isSelectable()){
314 ll++;
315 }
316 }
317 return ll;
318 }
319
320};
Akronb7a005a2021-09-21 17:43:02 +0200321});