blob: 02d6fba891e72f26413034661d0353861c634177 [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([
10 'container/containeritem' //TODO why does this not work!!!
11], 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 };
34 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._prefix = undefined; //required for re-setting the menus pointer correctly
41 // after having upgraded a new item scss style to the prefix object.
42
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 Reppd162b2e2021-06-30 13:51:07 +020057
58
59 //t._el.classList.add('visible'); //Done by containermenu
60
Leo Reppc66268e2021-10-28 11:44:35 +020061 },
Leo Reppd162b2e2021-06-30 13:51:07 +020062
63 addItem : function (item) {
64 var cItem = this._containerItemClass.create().upgradeTo(item);
65 cItem._menu = this._menu; //if not set then undefined, but thats OK
66 this.items.push(cItem);
67 this._el.appendChild(cItem.element());
68 return cItem;
69 },
70
71 addMenu : function (menu) {
72 this._menu = menu;
73 if (this._prefix !== undefined) {
74 this._menu._prefix = this._prefix; // better than going via classList or something
75 };
76 for (let item of this.items) {
77 item._menu=menu;
78 }
79 },
80
81 addPrefix : function (prefix) {
82 prefix.isSelectable = function () {
83 return this.isSet(); //TODO check!
84 }
Leo Repp050a7342021-10-25 11:05:32 +020085 this._prefixPosition = this.items.length;
Leo Reppd162b2e2021-06-30 13:51:07 +020086 var prefItem = this.addItem(prefix);
87 this._prefix = prefItem;
88 if (this._menu !== undefined){
89 this._menu._prefix=prefItem;
90 }
91 },
Leo Reppc66268e2021-10-28 11:44:35 +020092
93 /**
94 * Remove a containeritem from the container by identity. Should not be used with prefix.
95 * If the active item is removed, this calls menu.next().
96 * @param {containerItemClass} item The item to be removed.
97 */
98 removeItem : function (item) {
99 if (this.items.indexOf(item) === -1){ // This is returned if indexOf cannot find the item.
100 KorAP.log(0,"Invalid item in containers removeItemByIndex: This containerItem is not contained", "container.js");
101 return;
102 };
103 if (item === this._prefix) {//CHANGE TO _cItemPrefix later!!!
104 KorAP.log(0,"Tried to remove the prefix item by calling removeItem. Please cut all connections from the menu to prefix and then\
105 the connection container._prefix before calling this function if you really want to remove the prefix.","container.js");
106 return;
107 };
108 if (item.active()) {
109 this._menu.next();
110 };
111 item._menu=undefined;
112 this._el.removeChild(item.element());
113 this.items.splice(this.items.indexOf(item) , 1);
114 },
Leo Reppd162b2e2021-06-30 13:51:07 +0200115
116 /**
Leo Reppc66268e2021-10-28 11:44:35 +0200117 * Remove a containeritem from the container by its index. Should not be used with prefix.
118 * CAUTION liveIndex, so what you see, is not the actual index within the containerItem list.
119 * This can be accessed with container.items . If the active item is removed, this calls menu.next().
120 * @param {Int} index The index of the item to be removed.
Leo Reppd162b2e2021-06-30 13:51:07 +0200121 */
Leo Reppc66268e2021-10-28 11:44:35 +0200122 removeItemByIndex : function (index) {
123 if (index < 0 || index >= this.length()){
124 KorAP.log(0,"Invalid index in containers removeItemByIndex: "+index, "container.js");
125 return;
126 };
127 this.removeItem(this.items[index]); //CAUTION liveIndex (what you see) != index within the list!
128 },
129
130 /**
131 * Exit the container unconditionally. Required so that active returns the
132 * correct result. Called when the prefix or similar resets the currently visual
133 * field.
134 */
Leo Reppd162b2e2021-06-30 13:51:07 +0200135 exit : function () {
136 if (this.position !== undefined) {
137 this.item().active(false);
138 this.position = undefined;
139 };
140 },
141
142 element : function () {
143 return this._el;
144 },
145
146 destroy : function () {
147 for (let item of this.items){
148 delete item['_menu'];
149 }
150 },
151
152 /**
Leo Reppc66268e2021-10-28 11:44:35 +0200153 * @returns whether an item within the container is active (by checking this.position)
154 */
Leo Reppd162b2e2021-06-30 13:51:07 +0200155 active : function () {
156 return this.position !== undefined;
157 },
158
159 /**
Leo Reppc66268e2021-10-28 11:44:35 +0200160 * Getter for items
161 * @param {Integer} index [optional] Index of to select item. If left blank this.position.
162 * @returns item at location index
163 */
Leo Reppd162b2e2021-06-30 13:51:07 +0200164 item : function (index) {
165 if (index === undefined) return this.items[this.position];
166 return this.items[index];
167 },
168
169 /**
Leo Repp050a7342021-10-25 11:05:32 +0200170 *
171 * Make the container active without having called prev or next.
172 * Occurs whenever the prefix makes the
173 * menus list empty while we had it selected.
174 * This potentially requires adjusting this.position.
175 */
176 makeActive : function () {
177 if (this.position === undefined) {
178 if (this._prefix.isSelectable()) {
179 this.position = this._prefixPosition; //make prefix active if it exists
180 this.item().active(true);
181 } else if (this.liveLength() > 0) {
182 this.position = 0;
183 this._prefix.active(false); // usually the menu makes the prefix active anyway.
184 this.item().active(true);
185 }
186 }
187 },
Leo Reppc66268e2021-10-28 11:44:35 +0200188
Leo Repp050a7342021-10-25 11:05:32 +0200189 /**
Leo Reppc66268e2021-10-28 11:44:35 +0200190 *
191 * Move on to the next item in container. Returns true if we then leave the container, false otherwise.
192 */
Leo Reppd162b2e2021-06-30 13:51:07 +0200193 next : function() {
194 if (this.position !== undefined){
195 this.item().active(false);
196 this.position++;
197 } else {
198 this.position = 0;
199 };
200 if (this.position >= this.length()) {
201 this.position=undefined;
202 return true;
203 };
204 while (!this.item().isSelectable()) {
205 this.position++;
206 if (this.position >= this.length()) {
207 this.position=undefined;
208 return true;
209 }
210 };
211 this.item().active(true);
212 return false;
213 },
214
215 /**
Leo Reppc66268e2021-10-28 11:44:35 +0200216 * Move on to the previous item in container. Returns true if we then leave the container, false otherwise.
217 */
Leo Reppd162b2e2021-06-30 13:51:07 +0200218 prev : function() {
219 if (this.position !== undefined){
220 this.item().active(false);
221 this.position = (this.position-1)
222 } else {
223 this.position = (this.items.length-1);
224 }
225 if (this.position<0) {
226 this.position=undefined;
227 return true;
228 }
229 while (!this.item().isSelectable()) {
230 this.position--;
231 if (this.position<0){
232 this.position=undefined;
233 return true;
234 };
235 };
236 this.item().active(true);
237 return false;
238 },
239
240 further : function () {
241 const item = this.item();
242 if (item["further"] !== undefined) {
243 item["further"].bind(item).apply();
244 };
245 },
246
247 enter : function (event) {
248 this.item().onclick(event);
249 },
250
251 chop : function () {
252 for (let item of this.items) {
253 item.chop();
254 }
255 },
256
257 add : function (letter) {
258 for (let item of this.items) {
259 item.add(letter);
260 }
261 },
262
263 length : function () {
264 return this.items.length;
265 },
266
267 /**
Leo Reppc66268e2021-10-28 11:44:35 +0200268 *
269 * @returns The number of items that are selectable. Is the actual length of the list.
270 */
Leo Reppd162b2e2021-06-30 13:51:07 +0200271 liveLength : function () {
272 var ll = 0;
273 for (let item of this.items){
274 if (item.isSelectable()){
275 ll++;
276 }
277 }
278 return ll;
279 }
280
281};
Akronb7a005a2021-09-21 17:43:02 +0200282});