Added prefix view to menus
diff --git a/public/js/src/menu.js b/public/js/src/menu.js
index 175360a..c5bd2f8 100644
--- a/public/js/src/menu.js
+++ b/public/js/src/menu.js
@@ -44,102 +44,114 @@
// mouse wheel treatment
_mousewheel : function (e) {
var delta = 0;
- if (e.wheelDelta) {
- delta = event.wheelDelta / 120;
- }
- else if (e.detail) {
- delta = - e.detail / 3;
- };
- if (delta < 0) {
+
+ delta = e.deltaY / 120;
+ if (delta > 0)
this.next();
- }
- else {
+ else if (delta < 0)
this.prev();
- };
e.halt();
},
// Arrow key and prefix treatment
- _keydown : function (e) {
+ _keypress : function (e) {
var code = _codeFromEvent(e);
- /*
- * keyCodes:
- * - Down = 40
- * - Esc = 27
- * - Up = 38
- * - Enter = 13
- * - shift = 16
- * for characters use e.key
- */
-
switch (code) {
case 27: // 'Esc'
e.halt();
this.hide();
break;
- case 40: // 'Down'
- e.halt();
- this.next();
- break;
+
case 38: // 'Up'
e.halt();
this.prev();
break;
- case 13: // 'Enter'
- console.log('hide');
+ case 33: // 'Page up'
e.halt();
- this.hide();
+ this.prev();
+ break;
+ case 40: // 'Down'
+ e.halt();
+ this.next();
+ break;
+ case 34: // 'Page down'
+ e.halt();
+ this.next();
+ break;
+ case 39: // 'Right'
+ var item = this.liveItem(this._position);
+ if (item["further"] !== undefined) {
+ item["further"].bind(item).apply();
+ e.halt();
+ };
+ break;
+ case 13: // 'Enter'
+
+ // Click on prefix
+ if (this._prefix.active())
+ this._prefix.onclick();
+
+ // Click on item
+ else
+ this.liveItem(this._position).onclick();
+ e.halt();
break;
case 8: // 'Backspace'
- var p = this.prefix();
- if (p.length > 1) {
- p = p.substring(0, p.length - 1)
- this.show(p);
- }
- else {
- this.show();
- };
+ this._prefix.backspace();
+ this.show();
e.halt();
break;
default:
- if (e.key !== undefined && e.key.length != 1)
+ if (e.key !== undefined &&
+ e.key.length != 1)
return;
// Add prefix
- if (!this.show(this.prefix() + e.key))
- this.hide();
+ this._prefix.add(e.key.toLowerCase());
+
+ if (!this.show()) {
+ this.prefix('').show();
+ };
};
},
// Initialize list
- _init : function (itemClass, params) {
- // this._element.addEventListener("click", chooseHint, false);
+ _init : function (itemClass, prefixClass, params) {
var that = this;
this._itemClass = itemClass;
- var e =this._element = document.createElement("ul");
+
+ if (prefixClass !== undefined)
+ this._prefix = prefixClass.create();
+ else
+ this._prefix = KorAP.MenuPrefix.create();
+
+ var e = document.createElement("ul");
e.style.opacity = 0;
e.style.outline = 0;
e.setAttribute('tabindex', 0);
+ e.setAttribute('class', 'menu');
+ e.appendChild(this._prefix.element());
// Arrow keys
e.addEventListener(
- "keydown",
+ 'keypress',
function (ev) {
- that._keydown(ev)
+ that._keypress(ev)
},
false
);
// Mousewheel
e.addEventListener(
- 'DOMMouseScroll',
+ 'wheel',
function (ev) {
that._mousewheel(ev)
},
false
);
+ this._element = e;
this.active = false;
this._items = new Array();
var i;
@@ -175,8 +187,10 @@
* i.e. the number of items visible.
*/
limit : function (limit) {
- if (arguments.length === 1)
+ if (arguments.length === 1) {
this._limit = limit;
+ return this;
+ };
return this._limit;
},
@@ -197,7 +211,7 @@
_reset : function () {
this._offset = 0;
this._pos = 0;
- this._prefix = undefined;
+ this._prefix.value('');
},
/**
@@ -205,9 +219,7 @@
*
* @param {string} Prefix for filtering the list
*/
- show : function (prefix) {
- this._prefix = prefix;
-
+ show : function () {
// Initialize the list
if (!this._initList())
return false;
@@ -219,9 +231,8 @@
// Todo: Or the last element chosen
this.liveItem(0).active(true);
- this._position = 0;
this._active = this._list[0];
-
+ this._position = 0;
this._element.style.opacity = 1;
// Add classes for rolling menus
@@ -256,8 +267,13 @@
// There is no prefix set
if (this.prefix().length <= 0) {
- for (var i = 0; i < this._items.length; i++)
+ var i = 0;
+ for (; i < this._items.length; i++)
this._list.push(i);
+ while (this._items[++i] !== undefined) {
+ this._items[i].lowlight();
+ console.log(this._item);
+ };
return true;
};
@@ -296,8 +312,12 @@
* Get the prefix for filtering,
* e.g. "ve" for "verb"
*/
- prefix : function () {
- return this._prefix || '';
+ prefix : function (pref) {
+ if (arguments.length === 1) {
+ this._prefix.value(pref);
+ return this;
+ };
+ return this._prefix.value();
},
// Append Items that should be shown
@@ -326,19 +346,32 @@
delete : function () {
var child;
+ /*
// Iterate over all visible items
for (var i = 0; i <= this.limit(); i++) {
- // there is a visible element - unhighlight!
+ // there is a visible element
+ // unhighlight!
if (child = this.shownItem(i)) {
child.lowlight();
child.active(false);
};
};
+ */
+
+ for (var i in this._list) {
+ var item = this._items[this._list[i]];
+ item.lowlight();
+ item.active(false);
+ };
// Remove all children
- while (child = this._element.firstChild)
- this._element.removeChild(child);
+ var children = this._element.childNodes;
+ for (var i = children.length - 1; i >= 1; i--) {
+ this._element.removeChild(
+ children[i]
+ );
+ };
},
@@ -367,7 +400,7 @@
// Append element
e.insertBefore(
item.element(),
- e.firstChild
+ e.children[1]
);
},
@@ -422,21 +455,36 @@
* Make the next item in the filtered menu active
*/
next : function () {
+
// No active element set
- if (this._position == -1)
+ if (this._position === -1)
return;
+ var newItem;
+
// Set new live item
var oldItem = this.liveItem(this._position++);
oldItem.active(false);
- var newItem = this.liveItem(this._position);
+ newItem = this.liveItem(this._position);
- // The next element is undefined - roll to top
+ // The next element is undefined - roll to top or to prefix
if (newItem === undefined) {
- this._offset = 0;
- this._position = 0;
- newItem = this.liveItem(0);
- this._showItems(0);
+
+ // Activate prefix
+ var prefix = this._prefix;
+
+ // Mark prefix
+ if (prefix.isSet() && !prefix.active()) {
+ this._position--;
+ prefix.active(true);
+ return;
+ }
+ else {
+ this._offset = 0;
+ this._position = 0;
+ newItem = this.liveItem(0);
+ this._showItems(0);
+ };
}
// The next element is outside the view - roll down
@@ -445,6 +493,8 @@
this._offset++;
this._append(this._list[this._position]);
};
+
+ this._prefix.active(false);
newItem.active(true);
},
@@ -457,17 +507,30 @@
if (this._position == -1)
return;
+ var newItem;
+
// Set new live item
var oldItem = this.liveItem(this._position--);
oldItem.active(false);
- var newItem = this.liveItem(this._position);
+ newItem = this.liveItem(this._position);
// The previous element is undefined - roll to bottom
if (newItem === undefined) {
+
+ // Activate prefix
+ var prefix = this._prefix;
this._offset = this.liveLength() - this.limit();
this._position = this.liveLength() - 1;
- newItem = this.liveItem(this._position);
- this._showItems(this._offset);
+
+ if (prefix.isSet() && !prefix.active()) {
+ this._position++;
+ prefix.active(true);
+ return;
+ }
+ else {
+ newItem = this.liveItem(this._position);
+ this._showItems(this._offset);
+ };
}
// The previous element is outside the view - roll up
@@ -477,14 +540,23 @@
this._prepend(this._list[this._position]);
};
+ this._prefix.active(false);
newItem.active(true);
},
+ // Length of the filtered list
+ liveLength : function () {
+ if (this._list === undefined)
+ this._initList();
+ return this._list.length;
+ },
+
+
// Remove the HTML node from the first item
_removeFirst : function () {
this.item(this._list[this._offset]).lowlight();
- this._element.removeChild(this._element.firstChild);
+ this._element.removeChild(this._element.children[1]);
},
@@ -492,13 +564,6 @@
_removeLast : function () {
this.item(this._list[this._offset + this.limit() - 1]).lowlight();
this._element.removeChild(this._element.lastChild);
- },
-
- // Length of the filtered list
- liveLength : function () {
- if (this._list === undefined)
- this._initList();
- return this._list.length;
}
};
@@ -533,6 +598,7 @@
return this;
},
+
content : function (content) {
if (arguments.length === 1)
this._content = document.createTextNode(content);
@@ -593,7 +659,8 @@
var li = document.createElement("li");
// Connect action
- li["action"] = this._action;
+ if (this.onclick !== undefined)
+ li["onclick"] = this.onclick.bind(this);
// Append template
li.appendChild(this.content());
@@ -703,10 +770,89 @@
},
};
+ KorAP.MenuPrefix = {
+ create : function (params) {
+ return Object.create(KorAP.MenuPrefix)._init();
+ },
+ _init : function () {
+ this._string = '';
+
+ // Add prefix span
+ this._element = document.createElement('span');
+ this._element.classList.add('pref');
+ return this;
+ },
+ _update : function () {
+ this._element.innerHTML
+ = this._string;
+ },
+
+ /**
+ * 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;
+ },
+
+ active : function (bool) {
+ var cl = this.element().classList;
+ if (bool === undefined)
+ return cl.contains("active");
+ else if (bool)
+ cl.add("active");
+ else
+ cl.remove("active");
+ },
+ element : function () {
+ return this._element;
+ },
+ isSet : function () {
+ return this._string.length > 0 ?
+ true : false;
+ },
+ value : function (string) {
+ if (arguments.length === 1) {
+ this._string = string;
+ this._update();
+ };
+ return this._string;
+ },
+ add : function (string) {
+ this._string += string;
+ this._update();
+ },
+ onclick : function () {},
+ backspace : function () {
+ if (this._string.length > 1) {
+ this._string = this._string.substring(
+ 0, this._string.length - 1
+ );
+ }
+ else {
+ this._string = '';
+ };
+
+ this._update();
+ }
+ };
+
function _codeFromEvent (e) {
- if ((e.charCode) && (e.keyCode==0))
+ if (e.charCode && (e.keyCode == 0))
return e.charCode
return e.keyCode;
};
}(this.KorAP));
+
+/**
+ * MenuItems may define:
+ *
+ * onclick: action happen on click and enter.
+ * further: action happen on right arrow
+ */