Added rewrite capabilities and separated menu
diff --git a/public/js/src/menu.js b/public/js/src/menu.js
index 3b79bdd..b251000 100644
--- a/public/js/src/menu.js
+++ b/public/js/src/menu.js
@@ -3,6 +3,388 @@
(function (KorAP) {
"use strict";
+ // Default maximum number of menu items
+ KorAP.menuLimit = 8;
+
+ /**
+ * List of items for drop down menu (complete).
+ * Only a sublist of the menu is filtered (live).
+ * Only a sublist of the filtered menu is visible (shown).
+ */
+ KorAP.Menu = {
+ /**
+ * Create new Menu based on the action prefix
+ * and a list of menu items.
+ *
+ * @this {Menu}
+ * @constructor
+ * @param {string} Context prefix
+ * @param {Array.<Array.<string>>} List of menu items
+ */
+ create : function (params) {
+ return Object.create(KorAP.Menu)._init(params);
+ },
+
+ _init : function (itemClass, params) {
+ // this._element.addEventListener("click", chooseHint, false);
+ this._itemClass = itemClass;
+ this._element = document.createElement("ul");
+ this._element.style.opacity = 0;
+
+ this.active = false;
+ this._items = new Array();
+ var i;
+ for (i in params) {
+ var obj = itemClass.create(params[i]);
+ this._items.push(
+ obj
+ );
+ };
+ this._limit = KorAP.menuLimit;
+ this._position = 0; // position in the active list
+ this._active = -1; // active item in the item list
+
+ this._reset();
+ return this;
+ },
+
+ element : function () {
+ return this._element;
+ },
+
+ itemClass : function () {
+ return this._itemClass;
+ },
+
+ /**
+ * Get and set numerical value for limit
+ */
+ limit : function (limit) {
+ if (arguments.length === 1)
+ this._limit = limit;
+ return this._limit;
+ },
+
+ /**
+ * Upgrade this object to another object,
+ * while private data stays intact.
+ *
+ * @param {Object] An object with properties.
+ */
+ upgradeTo : function (props) {
+ for (var prop in props) {
+ this[prop] = props[prop];
+ };
+ return this;
+ },
+
+ _reset : function () {
+ this._offset = 0;
+ this._pos = 0;
+ this._prefix = undefined;
+ },
+
+ /**
+ * Filter the list and make it visible
+ *
+ * @param {string} Prefix for filtering the list
+ */
+ show : function (prefix) {
+ this._prefix = prefix;
+
+ // Initialize the list
+ if (!this._initList())
+ return;
+
+ // show based on offset
+ this._showItems(0);
+
+ // Set the first element to active
+ this.liveItem(0).active(true);
+
+ this._position = 0;
+ this._active = this._list[0];
+
+ // Add classes for rolling menus
+ this._boundary(true);
+ },
+
+ /**
+ * Get a specific item from the complete list
+ *
+ * @param {number} index of the list item
+ */
+ item : function (index) {
+ return this._items[index]
+ },
+
+ _initList : function () {
+
+ if (this._list === undefined) {
+ this._list = [];
+ }
+ else if (this._list.length != 0) {
+ this._boundary(false);
+ this._list.length = 0;
+ };
+
+ // Offset is initially zero
+ this._offset = 0;
+
+ if (this.prefix().length <= 0) {
+ for (var i = 0; i < this._items.length; i++)
+ this._list.push(i);
+ return true;
+ };
+
+ var pos;
+ var paddedPrefix = " " + this.prefix();
+
+ for (pos = 0; pos < this._items.length; pos++) {
+ if ((this.item(pos).lcField().indexOf(paddedPrefix)) >= 0)
+ this._list.push(pos);
+ };
+
+ if (this._list.length == 0) {
+ for (pos = 0; pos < this._items.length; pos++) {
+ if ((this.item(pos).lcField().indexOf(this.prefix())) >= 0)
+ this._list.push(pos);
+ };
+ };
+
+ // Filter was successful
+ return this._list.length > 0 ? true : false;
+ },
+
+ // Set boundary for viewport
+ _boundary : function (bool) {
+ this.item(this._list[0]).noMore(bool);
+ this.item(this._list[this._list.length - 1]).noMore(bool);
+ },
+
+ /**
+ * Get the prefix for filtering,
+ * e.g. "ve"" for "verb"
+ */
+ prefix : function () {
+ return this._prefix || '';
+ },
+
+ _showItems : function (offset) {
+ this.delete();
+
+ // Use list
+ var shown = 0;
+ var i;
+ for (i in this._list) {
+
+ // Don't show - it's before offset
+ if (shown++ < offset)
+ continue;
+
+ this._append(this._list[i]);
+
+ if (shown >= (this.limit() + this._offset))
+ break;
+ };
+ },
+
+ /**
+ * Delete all visible items from the menu element
+ */
+ delete : function () {
+ var child;
+ for (var i = 0; i <= this.limit(); i++) {
+
+ if (child = this.shownItem(i))
+ child.lowlight();
+ };
+
+ while (child = this._element.firstChild)
+ this._element.removeChild(child);
+ },
+
+
+ // Append item to the shown list based on index
+ _append : function (i) {
+ var item = this.item(i);
+
+ // Highlight based on prefix
+ if (this.prefix().length > 0)
+ item.highlight(this.prefix());
+
+ // Append element
+ this.element().appendChild(item.element());
+ },
+
+
+ /**
+ * Get a specific item from the filtered list
+ *
+ * @param {number} index of the list item
+ */
+ liveItem : function (index) {
+ if (this._list === undefined)
+ if (!this._initList())
+ return;
+
+ return this._items[this._list[index]];
+ },
+
+ length : function () {
+ return this._items.length;
+ },
+
+
+ /**
+ * Get a specific item from the visible list
+ *
+ * @param {number} index of the list item
+ */
+ shownItem : function (index) {
+ if (index >= this.limit())
+ return;
+ return this.liveItem(this._offset + index);
+ },
+
+
+ /*
+ * Make the next item in the menu active
+ */
+ next : function () {
+ // No active element set
+ if (this._position == -1)
+ return;
+
+ // Set new live item
+ var oldItem = this.liveItem(this._position++);
+ oldItem.active(false);
+ var newItem = this.liveItem(this._position);
+
+ // The next element is undefined - roll to top
+ if (newItem === undefined) {
+ this._offset = 0;
+ this._position = 0;
+ newItem = this.liveItem(0);
+ this._showItems(0);
+ }
+
+ // The next element is outside the view - roll down
+ else if (this._position >= (this.limit + this._offset)) {
+ this._removeFirst();
+ this._offset++;
+ this._append(this._list[this._position]);
+ };
+ newItem.active(true);
+ },
+
+
+ /*
+ * Make the previous item in the menu active
+ */
+/*
+ prev : function () {
+ if (this._position == -1)
+ return;
+
+ // Set new live item
+ var oldItem = this.liveItem(this._position--);
+ oldItem.active(false);
+ var newItem = this.liveItem(this._position);
+
+ // The previous element is undefined - roll to bottom
+ if (newItem === undefined) {
+ this._position = this.liveLength - 1;
+ newItem = this.liveItem(this._position);
+ this._offset = this.liveLength - this.limit;
+ this._showItems(this._offset);
+ }
+
+ // The previous element is outside the view - roll up
+ else if (this._position < this._offset) {
+ this._removeLast();
+ this._offset--;
+ this._prepend(this._list[this._position]);
+ };
+ newItem.active(true);
+ },
+*/
+
+
+ /**
+ * Get the context of the menue,
+ * e.g. "tt/" for the tree tagger menu
+ */
+/*
+ get context () {
+ return this._context;
+ },
+*/
+/*
+ get liveLength () {
+ if (this._list === undefined)
+ this._initList();
+ return this._list.length;
+ },
+*/
+/*
+ chooseHint : function (e) {
+ var element = e.target;
+ while (element.nodeName == "STRONG" || element.nodeName == "SPAN")
+ element = element.parentNode;
+
+ if (element === undefined || element.nodeName != "LI")
+ return;
+
+ var action = element.getAttribute('data-action');
+ hint.insertText(action);
+ var menu = hint.menu();
+ menu.hide();
+
+ // Fill this with the correct value
+ var show;
+ if ((show = hint.analyzeContext()) != "-") {
+ menu.show(show);
+ menu.update(
+ hint._search.getBoundingClientRect().right
+ );
+ };
+
+ hint._search.focus();
+ },
+
+ _removeFirst : function () {
+ this.item(this._list[this._offset]).lowlight();
+ this._element.removeChild(this._element.firstChild);
+ },
+
+ _removeLast : function () {
+ this.item(this._list[this._offset + this.limit - 1]).lowlight();
+ this._element.removeChild(this._element.lastChild);
+ },
+
+
+ // Prepend item to the shown list based on index
+ _prepend : function (i) {
+ var item = this.item(i);
+
+ // Highlight based on prefix
+ if (this.prefix.length > 0)
+ item.highlight(this.prefix);
+
+ // Append element
+ this.element.insertBefore(
+ item.element,
+ this.element.firstChild
+ );
+ },
+*/
+
+ };
+
+
+
+
/**
* Item in the Dropdown menu
*/
@@ -107,7 +489,10 @@
* @param {string} Prefix string for highlights
*/
highlight : function (prefix) {
- this._highlight(this.element().firstChild, prefix);
+ var children = this.element().childNodes;
+ for (var i = children.length -1; i >= 0; i--) {
+ this._highlight(children[i], prefix);
+ };
},
// Highlight a certain substring of the menu item
@@ -185,6 +570,7 @@
// Initialize menu item
_init : function (params) {
+
if (params[0] === undefined)
throw new Error("Missing parameters");