New menu with a container for mutliple always available non scrollable items

Change-Id: I5c8379cc82038da4a6c0923bbf59ec8faaa1eb9f
diff --git a/dev/js/src/container/container.js b/dev/js/src/container/container.js
new file mode 100644
index 0000000..9b436ca
--- /dev/null
+++ b/dev/js/src/container/container.js
@@ -0,0 +1,230 @@
+/**
+ * Container for several containerItem style items. Functions like a mini menu with next, prev, add etc propagation,
+ * but no event handling or slider or lengthField. Supposed to be subelement to a (container)menu class item.
+ * 
+ * @author Leo Repp
+ */
+
+"use strict";
+define([
+  'container/containeritem'   //TODO why does this not work!!!
+], function (
+  defaultContainerItemClass
+) {
+
+  return {
+    /**
+     * 
+     * @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
+     * @param {object} params May contain attribute containerItemClass for a base class all containerItems build upon
+     * @returns The container object
+     */
+    create : function (listOfContainerItems, params) {
+      var obj = Object.create(this);
+      obj._init(listOfContainerItems, params);
+      return obj;
+    },
+
+    _init : function (listOfContainerItems, params){
+      if (params !== undefined && params["containerItemClass"] !== undefined){
+        this._containerItemClass = params["containerItemClass"];
+      } else {
+        this._containerItemClass = defaultContainerItemClass;
+      };
+      var el = document.createElement("ul");
+      el.style.outline = 0;
+      el.setAttribute('tabindex', 0);
+      el.classList.add('menu', 'container'); //container class allows for more stylesheet changes
+
+      this._el = el;
+      this._prefix = undefined; //required for re-setting the menus pointer correctly
+      // after having upgraded a new item scss style to the prefix object.
+
+      this.items = new Array();
+      if (listOfContainerItems !== undefined) {
+        for (let item of listOfContainerItems) {
+          this.addItem(item);
+        }
+      }
+
+      this.position = undefined; //undefined = not in container, 0 to length-1 = in container
+
+      
+      //t._el.classList.add('visible'); //Done by containermenu
+
+
+    },
+    /**
+     * Upgrade this object to another object,
+     * while private data stays intact.
+     *
+    * @param {Object} An object with properties.
+    */
+    upgradeTo : function (props) {
+      for (let prop in props) {
+        this[prop] = props[prop];
+      };
+      return this;
+    },
+
+    addItem : function (item) {
+      var cItem = this._containerItemClass.create().upgradeTo(item);
+      cItem._menu = this._menu; //if not set then undefined, but thats OK
+      this.items.push(cItem);
+      this._el.appendChild(cItem.element());
+      return cItem;
+    },
+
+    addMenu : function (menu) {
+      this._menu = menu;
+      if (this._prefix !== undefined) {
+        this._menu._prefix = this._prefix; // better than going via classList or something
+      };
+      for (let item of this.items) {
+        item._menu=menu;
+      }
+    },
+
+    addPrefix : function (prefix) {
+      prefix.isSelectable =  function () {
+        return this.isSet(); //TODO check!
+      }
+      var prefItem = this.addItem(prefix);
+      this._prefix = prefItem;
+      if (this._menu !== undefined){
+        this._menu._prefix=prefItem;
+      }
+    },
+
+    /**
+     * Exit the container unconditionally. Required so that active returns the
+     * correct result. Called when the prefix or similar resets the currently visual
+     * field.
+     */
+    exit : function () {
+      if (this.position !== undefined) {
+        this.item().active(false);
+        this.position = undefined;
+      };
+    },
+
+    element : function () {
+      return this._el;
+    },
+
+    destroy : function () {
+      for (let item of this.items){
+        delete item['_menu'];
+      }
+    },
+
+    /**
+     * @returns whether an item within the container is active (by checking this.position)
+     */
+    active : function () {
+      return this.position !== undefined;
+    },
+
+    /**
+     * Getter for items
+     * @param {Integer} index [optional] Index of to select item. If left blank this.position.
+     * @returns item at location index
+     */
+    item : function (index) {
+      if (index === undefined) return this.items[this.position];
+      return this.items[index];
+    },
+
+    /**
+     * Move on to the next item in container. Returns true if we then leave the container, false otherwise.
+     */
+    next : function() {
+      if (this.position !== undefined){
+        this.item().active(false);
+        this.position++;
+      } else {
+        this.position = 0;
+      };
+      if (this.position >= this.length()) {
+        this.position=undefined;
+        return true;
+      };
+      while (!this.item().isSelectable()) {
+        this.position++;
+        if (this.position >= this.length()) {
+          this.position=undefined;
+          return true;
+        }
+      };
+      this.item().active(true);
+      return false;
+    },
+
+    /**
+     * Move on to the previous item in container. Returns true if we then leave the container, false otherwise.
+     */
+    prev : function() {
+      if (this.position !== undefined){
+        this.item().active(false);
+        this.position = (this.position-1)
+      } else {
+        this.position = (this.items.length-1);
+      }
+      if (this.position<0) {
+        this.position=undefined;
+        return true;
+      }
+      while (!this.item().isSelectable()) {
+        this.position--;
+        if (this.position<0){
+          this.position=undefined;
+          return true;
+        };
+      };
+      this.item().active(true);
+      return false;
+    },
+
+    further : function () {
+      const item = this.item();
+      if (item["further"] !== undefined) {
+        item["further"].bind(item).apply();
+      };
+    },
+
+    enter : function (event) {
+      this.item().onclick(event);
+    },
+
+    chop : function () {
+      for (let item of this.items) {
+        item.chop();
+      }
+    },
+
+    add : function (letter) {
+      for (let item of this.items) {
+        item.add(letter);
+      }
+    },
+
+    length : function () {
+      return this.items.length;
+    },
+
+    /**
+     * 
+     * @returns The number of items that are selectable. Is the actual length of the list.
+     */
+    liveLength : function () {
+      var ll = 0;
+      for (let item of this.items){
+        if (item.isSelectable()){
+          ll++;
+        }
+      }
+      return ll;
+    }
+
+};
+});
\ No newline at end of file
diff --git a/dev/js/src/container/containeritem.js b/dev/js/src/container/containeritem.js
new file mode 100644
index 0000000..10709b8
--- /dev/null
+++ b/dev/js/src/container/containeritem.js
@@ -0,0 +1,108 @@
+/**
+ * API/ skeleton/ base class for an item contained within a container.
+ * Here we see which functions container supports for containerItems.
+ * 
+ * @author Leo Repp
+ */
+
+
+//"use strict";
+
+define({
+
+  /**
+   * API for an item contained within a container
+   */
+  create : function () {
+    return Object.create(this); //._init();
+  },
+
+  /**
+   * Upgrade this object to another object,
+   * while private data stays intact.
+   *
+   * @param {Object} An object with properties.
+   */
+  upgradeTo : function (props) {
+    for (let prop in props) {
+      this[prop] = props[prop];
+    };
+    return this;
+  },
+
+  /**
+   * Check or set if the item is active
+   *
+   * @param {boolean|null} State of activity
+   */
+  active : function (bool) {
+    const cl = this.element().classList;
+    if (bool === undefined) return cl.contains("active");
+    else if (bool) cl.add("active");
+    else cl.remove("active"); //allows for setting it to inactive if not (equal to undefined or truthy)
+  },
+
+  /**
+   * Get/create the document element of the container item. Can be overwritten. Standard class: li
+   */
+  element : function () {
+    // already defined
+    if (this._el !== undefined) return this._el;
+    
+    // Create list item
+    const li = document.createElement("li");
+
+    // Connect action
+    if (this["onclick"] !== undefined) {
+      li["onclick"] = this.onclick.bind(this);
+    };    
+    return this._el = li;
+  },
+
+  /**
+   * Expected to be overwritten
+   * @returns whether the item is currently an option to be selected, or if it should just be skipped
+   */
+  isSelectable : function () {
+    return true;
+  },
+
+  /**
+   * API skeleton for reading letters. Expected to be overwritten.
+   * @param {String} letter The letter to be read
+   */
+  add : function (letter) {},
+    
+  
+  /**
+   * API skeleton for clearing whole contents. Expected to be overwritten.
+   */
+  clear : function () {},
+  
+  
+  /**
+   * API skeleton method for execution. Expected to be overwritten.
+   * @param {Event} event Event passed down by menu.
+   */
+  onclick : function (e) {},
+  
+    
+  /**
+   * API skeleton method for when backspace is pressed. Expected to be overwritten.
+   */
+  chop : function () {},
+
+  /**
+   * API skeleton method for pressing "right". Expected to be overwritten.
+   * @param {Event} event Event passed down by menu.
+   */
+  further : function (e) {},
+
+  /**
+   * Return menu list. This._menu gets written by the container class
+   */
+  menu : function () {
+    return this._menu;
+  }
+
+});
\ No newline at end of file