blob: 3f6a54d5cc46017cfd17feb1c4e93571897d3be0 [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 Repp57997402021-08-18 16:37:52 +020034 this._el = document.createElement("ul");
35 this._el.style.outline = 0;
36 this._el.setAttribute('tabindex', 0);
37 this._el.classList.add('menu', 'container'); //container class allows for more stylesheet changes
Leo Reppd162b2e2021-06-30 13:51:07 +020038
39 this.items = new Array();
Leo Repp050a7342021-10-25 11:05:32 +020040 //items are stored in the order they are added in. This includes the prefix.
Leo Reppd162b2e2021-06-30 13:51:07 +020041 if (listOfContainerItems !== undefined) {
42 for (let item of listOfContainerItems) {
43 this.addItem(item);
44 }
45 }
46
Leo Repp050a7342021-10-25 11:05:32 +020047 this.position = undefined; //undefined = not in container,
48 // 0 to length-1 = in container
49
50 this._prefixPosition = undefined; //Required so that switching
51 // to prefix by default is supported
52
Leo Repp57997402021-08-18 16:37:52 +020053 this._menu = undefined // add later
54
Leo Reppd162b2e2021-06-30 13:51:07 +020055
56
57 //t._el.classList.add('visible'); //Done by containermenu
Leo Repp57997402021-08-18 16:37:52 +020058 },
Leo Reppd162b2e2021-06-30 13:51:07 +020059
Leo Repp57997402021-08-18 16:37:52 +020060 /**
61 * Adds a static item to this container by creating a standard containerItem as specified when this container was created,
62 * then upgrading it to the item passed to this function, and calling element() and content(). For a full list of supported functions see
63 * containeritem.js .
64 * Example:
65 *
66 * menu.container().addItem(
67 * {defaultTextValue : "dynamic", onClick : function (e) { ... }
68 * )
69 *
70 * For a full demo see containermenudemo.js.
71 *
72 * @param {Object} item An object with any number of functions like in containeritem.js or an attribute defaultTextValue,
73 * as well as any number of own properties.
74 * @returns the new use-ready containerItem
75 */
Leo Reppd162b2e2021-06-30 13:51:07 +020076 addItem : function (item) {
Leo Repp57997402021-08-18 16:37:52 +020077 //Call Order: First _containerItemClass is created and then upgraded To whatever object is passed to this function
78 //Then container calls first element() and then container()
Leo Reppd162b2e2021-06-30 13:51:07 +020079 var cItem = this._containerItemClass.create().upgradeTo(item);
80 cItem._menu = this._menu; //if not set then undefined, but thats OK
81 this.items.push(cItem);
Leo Repp57997402021-08-18 16:37:52 +020082 if (this._cItemPrefix !== undefined){ //this must be dynamic adding of CIs, move prefix to the back
83 this.items.splice(this.items.indexOf(this._cItemPrefix) , 1); //remove cItemPrefix
84 this.items.push(this._cItemPrefix); //and move it to the end;
85 };
Leo Reppd162b2e2021-06-30 13:51:07 +020086 this._el.appendChild(cItem.element());
Leo Repp57997402021-08-18 16:37:52 +020087 cItem.content(); // create its textNode
Leo Reppd162b2e2021-06-30 13:51:07 +020088 return cItem;
89 },
90
91 addMenu : function (menu) {
92 this._menu = menu;
Leo Repp57997402021-08-18 16:37:52 +020093 if (this._cItemPrefix !== undefined) {
94 this._menu._prefix = this._cItemPrefix; // better than going via classList or something
Leo Reppd162b2e2021-06-30 13:51:07 +020095 };
96 for (let item of this.items) {
97 item._menu=menu;
98 }
99 },
100
101 addPrefix : function (prefix) {
102 prefix.isSelectable = function () {
103 return this.isSet(); //TODO check!
104 }
Leo Repp050a7342021-10-25 11:05:32 +0200105 this._prefixPosition = this.items.length;
Leo Repp57997402021-08-18 16:37:52 +0200106 prefix.content = function () {}; //Does not need a textNode Child!
Leo Reppd162b2e2021-06-30 13:51:07 +0200107 var prefItem = this.addItem(prefix);
Leo Repp57997402021-08-18 16:37:52 +0200108 this._cItemPrefix = prefItem;
109 prefItem._el["onclick"] = prefItem.onclick.bind(prefItem);
Leo Reppd162b2e2021-06-30 13:51:07 +0200110 if (this._menu !== undefined){
111 this._menu._prefix=prefItem;
112 }
113 },
Leo Reppc66268e2021-10-28 11:44:35 +0200114
Leo Repp57997402021-08-18 16:37:52 +0200115 //Taken from Branch 5133
Leo Reppc66268e2021-10-28 11:44:35 +0200116 /**
117 * Remove a containeritem from the container by identity. Should not be used with prefix.
118 * If the active item is removed, this calls menu.next().
119 * @param {containerItemClass} item The item to be removed.
120 */
121 removeItem : function (item) {
122 if (this.items.indexOf(item) === -1){ // This is returned if indexOf cannot find the item.
123 KorAP.log(0,"Invalid item in containers removeItemByIndex: This containerItem is not contained", "container.js");
124 return;
125 };
Leo Repp57997402021-08-18 16:37:52 +0200126 if (item === this._cItemPrefix) {
127 KorAP.log(0,"Tried to remove the prefix item. Illegal.");
128 console.log("Tried to remove the prefix item by calling removeItem. Please cut all connections from the menu to prefix and then\
129 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 +0200130 return;
131 };
132 if (item.active()) {
133 this._menu.next();
134 };
135 item._menu=undefined;
136 this._el.removeChild(item.element());
137 this.items.splice(this.items.indexOf(item) , 1);
138 },
Leo Reppd162b2e2021-06-30 13:51:07 +0200139
140 /**
Leo Reppc66268e2021-10-28 11:44:35 +0200141 * Remove a containeritem from the container by its index. Should not be used with prefix.
142 * CAUTION liveIndex, so what you see, is not the actual index within the containerItem list.
143 * This can be accessed with container.items . If the active item is removed, this calls menu.next().
144 * @param {Int} index The index of the item to be removed.
Leo Reppd162b2e2021-06-30 13:51:07 +0200145 */
Leo Reppc66268e2021-10-28 11:44:35 +0200146 removeItemByIndex : function (index) {
147 if (index < 0 || index >= this.length()){
148 KorAP.log(0,"Invalid index in containers removeItemByIndex: "+index, "container.js");
149 return;
150 };
151 this.removeItem(this.items[index]); //CAUTION liveIndex (what you see) != index within the list!
152 },
153
154 /**
155 * Exit the container unconditionally. Required so that active returns the
156 * correct result. Called when the prefix or similar resets the currently visual
157 * field.
158 */
Leo Reppd162b2e2021-06-30 13:51:07 +0200159 exit : function () {
160 if (this.position !== undefined) {
161 this.item().active(false);
162 this.position = undefined;
163 };
164 },
165
166 element : function () {
167 return this._el;
168 },
169
170 destroy : function () {
171 for (let item of this.items){
172 delete item['_menu'];
173 }
174 },
175
176 /**
Leo Reppc66268e2021-10-28 11:44:35 +0200177 * @returns whether an item within the container is active (by checking this.position)
178 */
Leo Reppd162b2e2021-06-30 13:51:07 +0200179 active : function () {
180 return this.position !== undefined;
181 },
182
183 /**
Leo Reppc66268e2021-10-28 11:44:35 +0200184 * Getter for items
185 * @param {Integer} index [optional] Index of to select item. If left blank this.position.
186 * @returns item at location index
187 */
Leo Reppd162b2e2021-06-30 13:51:07 +0200188 item : function (index) {
189 if (index === undefined) return this.items[this.position];
190 return this.items[index];
191 },
192
193 /**
Leo Repp050a7342021-10-25 11:05:32 +0200194 *
195 * Make the container active without having called prev or next.
196 * Occurs whenever the prefix makes the
197 * menus list empty while we had it selected.
198 * This potentially requires adjusting this.position.
199 */
Leo Repp57997402021-08-18 16:37:52 +0200200 makeActive : function () {
Leo Repp050a7342021-10-25 11:05:32 +0200201 if (this.position === undefined) {
Leo Repp57997402021-08-18 16:37:52 +0200202 if (this._cItemPrefix.isSelectable()) {
Leo Repp050a7342021-10-25 11:05:32 +0200203 this.position = this._prefixPosition; //make prefix active if it exists
204 this.item().active(true);
205 } else if (this.liveLength() > 0) {
206 this.position = 0;
Leo Repp57997402021-08-18 16:37:52 +0200207 this._cItemPrefix.active(false); // usually the menu makes the prefix active anyway.
Leo Repp050a7342021-10-25 11:05:32 +0200208 this.item().active(true);
209 }
210 }
211 },
Leo Reppc66268e2021-10-28 11:44:35 +0200212
Leo Repp050a7342021-10-25 11:05:32 +0200213 /**
Leo Reppc66268e2021-10-28 11:44:35 +0200214 *
215 * Move on to the next item in container. Returns true if we then leave the container, false otherwise.
216 */
Leo Reppd162b2e2021-06-30 13:51:07 +0200217 next : function() {
218 if (this.position !== undefined){
219 this.item().active(false);
220 this.position++;
221 } else {
222 this.position = 0;
223 };
224 if (this.position >= this.length()) {
225 this.position=undefined;
226 return true;
227 };
228 while (!this.item().isSelectable()) {
229 this.position++;
230 if (this.position >= this.length()) {
231 this.position=undefined;
232 return true;
233 }
234 };
235 this.item().active(true);
236 return false;
237 },
238
239 /**
Leo Reppc66268e2021-10-28 11:44:35 +0200240 * Move on to the previous item in container. Returns true if we then leave the container, false otherwise.
241 */
Leo Reppd162b2e2021-06-30 13:51:07 +0200242 prev : function() {
243 if (this.position !== undefined){
244 this.item().active(false);
245 this.position = (this.position-1)
246 } else {
247 this.position = (this.items.length-1);
248 }
249 if (this.position<0) {
250 this.position=undefined;
251 return true;
252 }
253 while (!this.item().isSelectable()) {
254 this.position--;
255 if (this.position<0){
256 this.position=undefined;
257 return true;
258 };
259 };
260 this.item().active(true);
261 return false;
262 },
263
264 further : function () {
265 const item = this.item();
266 if (item["further"] !== undefined) {
267 item["further"].bind(item).apply();
268 };
269 },
270
271 enter : function (event) {
272 this.item().onclick(event);
273 },
274
275 chop : function () {
276 for (let item of this.items) {
277 item.chop();
278 }
279 },
280
281 add : function (letter) {
282 for (let item of this.items) {
283 item.add(letter);
284 }
285 },
286
287 length : function () {
288 return this.items.length;
289 },
290
291 /**
Leo Reppc66268e2021-10-28 11:44:35 +0200292 *
293 * @returns The number of items that are selectable. Is the actual length of the list.
294 */
Leo Reppd162b2e2021-06-30 13:51:07 +0200295 liveLength : function () {
296 var ll = 0;
297 for (let item of this.items){
298 if (item.isSelectable()){
299 ll++;
300 }
301 }
302 return ll;
303 }
304
305};
Akronb7a005a2021-09-21 17:43:02 +0200306});