Fix slider behaviour in menus for full lists
diff --git a/dev/js/spec/menuSpec.js b/dev/js/spec/menuSpec.js
index 502549f..b8c7d44 100644
--- a/dev/js/spec/menuSpec.js
+++ b/dev/js/spec/menuSpec.js
@@ -395,7 +395,7 @@
it('should be visible', function () {
var menu = KorAP.HintMenu.create("cnx/", list);
- expect(menu.delete()).toBe(undefined);
+ expect(menu.unshow()).toBe(undefined);
menu.limit(3);
expect(menu.show()).toBe(true);
@@ -1071,12 +1071,14 @@
expect(menu.shownItem(1).active()).toBe(false);
expect(menu.shownItem(2).active()).toBe(false);
+ // Highlight the first entry
menu.next();
expect(menu.shownItem(0).active()).toBe(true);
expect(menu.shownItem(1).active()).toBe(false);
expect(menu.shownItem(2).active()).toBe(false);
+ // Highlight the second entry
menu.next();
expect(menu.shownItem(0).active()).toBe(false);
@@ -1087,29 +1089,36 @@
expect(menu.shownItem(0).lcField()).toEqual(' titel');
expect(menu.shownItem(1).lcField()).toEqual(' untertitel');
+ // Move to first screen
menu.screen(0);
+ expect(menu.shownItem(0).lcField()).toEqual(' titel');
+ expect(menu.shownItem(1).lcField()).toEqual(' untertitel');
expect(menu.shownItem(0).active()).toBe(false);
expect(menu.shownItem(1).active()).toBe(true);
expect(menu.shownItem(2).active()).toBe(false);
expect(menu.shownItem(3)).toBe(undefined);
-/*
-
-console.log('VVVVVVVVVVVVVVVVV');
+ // Move to second screen
menu.screen(1);
- expect(menu.shownItem(0)).not.toBeUndefined();
- expect(menu.shownItem(1)).not.toBeUndefined();
- expect(menu.shownItem(2)).not.toBeUndefined();
- expect(menu.shownItem(3)).toBe(undefined);
-
-
-console.log(menu.element().innerHTML);
-console.log(menu.shownItem(0)._element.outerHTML);
-
+ expect(menu.shownItem(0).lcField()).toEqual(' untertitel');
expect(menu.shownItem(0).active()).toBe(true);
expect(menu.shownItem(1).active()).toBe(false);
expect(menu.shownItem(2).active()).toBe(false);
-*/
+ expect(menu.shownItem(3)).toBe(undefined);
+
+ menu.screen(2);
+ expect(menu.shownItem(0).lcField()).toEqual(' veröffentlichungsdatum');
+ expect(menu.shownItem(0).active()).toBe(false);
+ expect(menu.shownItem(1).active()).toBe(false);
+ expect(menu.shownItem(2).active()).toBe(false);
+ expect(menu.shownItem(3)).toBe(undefined);
+
+ menu.screen(1);
+ expect(menu.shownItem(0).lcField()).toEqual(' untertitel');
+ expect(menu.shownItem(0).active()).toBe(true);
+ expect(menu.shownItem(1).active()).toBe(false);
+ expect(menu.shownItem(2).active()).toBe(false);
+ expect(menu.shownItem(3)).toBe(undefined);
});
diff --git a/dev/js/src/menu.js b/dev/js/src/menu.js
index cc75926..b9672c1 100644
--- a/dev/js/src/menu.js
+++ b/dev/js/src/menu.js
@@ -4,9 +4,9 @@
* @author Nils Diewald
*/
/*
- * TODO: First item shouldn't be automatically highlighted!
* TODO: space is not a valid prefix!
* TODO: Prefix should be case sensitive!
+ * TODO: What is _pos and what is position?
*/
define([
'menu/item',
@@ -48,23 +48,175 @@
return Object.create(this)._init(params);
},
+ // Initialize list
+ _init : function (itemClass, prefixClass, lengthFieldClass, params) {
+ var that = this;
+ this._itemClass = itemClass || defaultItemClass;
+
+ // Add prefix object
+ if (prefixClass !== undefined) {
+ this._prefix = prefixClass.create();
+ }
+ else {
+ this._prefix = defaultPrefixClass.create();
+ };
+ this._prefix._menu = this;
+
+ // Add lengthField object
+ if (lengthFieldClass !== undefined) {
+ this._lengthField = lengthFieldClass.create();
+ }
+ else {
+ this._lengthField = defaultLengthFieldClass.create();
+ };
+ this._lengthField._menu = this;
+
+ // Initialize slider
+ this._slider = sliderClass.create(this);
+
+ // Create the element
+ var e = document.createElement("ul");
+ e.style.opacity = 0;
+ e.style.outline = 0;
+ e.setAttribute('tabindex', 0);
+ e.classList.add('menu');
+ e.classList.add('roll');
+ e.appendChild(this._prefix.element());
+ e.appendChild(this._lengthField.element());
+ e.appendChild(this._slider.element());
+
+ // This has to be cleaned up later on
+ e["menu"] = this;
+
+ // Arrow keys
+ e.addEventListener(
+ 'keydown',
+ function (ev) {
+ that._keydown(ev)
+ },
+ false
+ );
+
+ // Strings
+ e.addEventListener(
+ 'keypress',
+ function (ev) {
+ that._keypress(ev)
+ },
+ false
+ );
+
+ // Mousewheel
+ e.addEventListener(
+ 'wheel',
+ function (ev) {
+ that._mousewheel(ev)
+ },
+ false
+ );
+
+ this._element = e;
+ this.active = false;
+ // this.selected = undefined;
+ this._items = new Array();
+ var i = 0;
+
+ // Initialize item list based on parameters
+ for (i in params) {
+ var obj = this._itemClass.create(params[i]);
+
+ // This may become circular
+ obj["_menu"] = this;
+ this._lengthField.add(params[i]);
+ this._items.push(obj);
+ };
+
+ this._limit = menuLimit;
+ this._slider.length(this.liveLength());
+ this._slider.limit(this._limit);
+
+ this.position = 0; // position in the active list
+ this._active = -1; // active item in the item list
+ this._firstActive = false; // Show the first item active always?
+ this._reset();
+ return this;
+ },
+
+ // Initialize the item list
+ _initList : function () {
+
+ // Create a new list
+ 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;
+
+ // There is no prefix set
+ if (this.prefix().length <= 0) {
+
+ // add all items to the list and lowlight
+ for (var i = 0; i < this._items.length; i++) {
+ this._list.push(i);
+ this._items[i].lowlight();
+ };
+
+ return true;
+ };
+
+ /*
+ * There is a prefix set, so filter the list!
+ */
+ var pos;
+ var paddedPrefix = " " + this.prefix();
+
+ // Iterate over all items and choose preferred matching items
+ // i.e. the matching happens at the word start
+ for (pos = 0; pos < this._items.length; pos++) {
+ if ((this.item(pos).lcField().indexOf(paddedPrefix)) >= 0)
+ this._list.push(pos);
+ };
+
+ // The list is empty - so lower your expectations
+ // Iterate over all items and choose matching items
+ // i.e. the matching happens anywhere in the word
+ 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 - yeah!
+ return this._list.length > 0 ? true : false;
+ },
+
+
/**
* Destroy this menu
* (in case you don't trust the
* mark and sweep GC)!
*/
destroy : function () {
- this._prefix._menu = undefined;
- this._lengthField._menu = undefined;
- this._slider._menu = undefined;
+ // Remove circular reference to "this" in menu
if (this._element != undefined)
delete this._element["menu"];
+ // Remove circular reference to "this" in items
for (var i = 0; i < this._items.length; i++) {
delete this._items[i]["_menu"];
};
+
+ // Remove circular reference to "this" in prefix
delete this._prefix['_menu'];
+ delete this._lengthField['_menu'];
+ delete this._slider['_menu'];
},
@@ -160,109 +312,17 @@
},
/**
- * Show screen X
+ * Show a screen with a given offset
+ * in the viewport.
*/
screen : function (nr) {
if (this._offset === nr)
return;
-
- this.delete();
+ this.unshow();
+ this._offset = nr;
this._showItems(nr);
},
- // Initialize list
- _init : function (itemClass, prefixClass, lengthFieldClass, params) {
- var that = this;
- this._itemClass = itemClass || defaultItemClass;
-
- // Add prefix object
- if (prefixClass !== undefined) {
- this._prefix = prefixClass.create();
- }
- else {
- this._prefix = defaultPrefixClass.create();
- };
- this._prefix._menu = this;
-
- // Add lengthField object
- if (lengthFieldClass !== undefined) {
- this._lengthField = lengthFieldClass.create();
- }
- else {
- this._lengthField = defaultLengthFieldClass.create();
- };
- this._lengthField._menu = this;
-
- // Initialize the slider
- this._slider = sliderClass.create(this);
-
- var e = document.createElement("ul");
- e.style.opacity = 0;
- e.style.outline = 0;
- e.setAttribute('tabindex', 0);
- e.classList.add('menu');
- e.classList.add('roll');
- e.appendChild(this._prefix.element());
- e.appendChild(this._lengthField.element());
- e.appendChild(this._slider.element());
-
- // This has to be cleaned up later on
- e["menu"] = this;
-
- // Arrow keys
- e.addEventListener(
- 'keydown',
- function (ev) {
- that._keydown(ev)
- },
- false
- );
-
- // Strings
- e.addEventListener(
- 'keypress',
- function (ev) {
- that._keypress(ev)
- },
- false
- );
-
- // Mousewheel
- e.addEventListener(
- 'wheel',
- function (ev) {
- that._mousewheel(ev)
- },
- false
- );
-
- this._element = e;
- this.active = false;
- // this.selected = undefined;
- this._items = new Array();
- var i = 0;
-
- // Initialize item list based on parameters
- for (i in params) {
- var obj = this._itemClass.create(params[i]);
-
- // This may become circular
- obj["_menu"] = this;
- this._lengthField.add(params[i]);
- this._items.push(obj);
- };
-
- this._limit = menuLimit;
- this._slider.length(this.liveLength());
- this._slider.limit(this._limit);
-
- this.position = 0; // position in the active list
- this._active = -1; // active item in the item list
- this._firstActive = false; // Show the first item active always?
- this._reset();
- return this;
- },
-
/**
* Get the associated dom element.
*/
@@ -270,10 +330,6 @@
return this._element;
},
- slider : function () {
- return this._slider;
- },
-
/**
* Get the creator class for items
*/
@@ -287,8 +343,10 @@
*/
limit : function (limit) {
if (arguments.length === 1) {
- this._limit = limit;
- this._slider.limit(limit);
+ if (this._limit !== limit) {
+ this._limit = limit;
+ this._slider.limit(limit);
+ };
return this;
};
return this._limit;
@@ -309,14 +367,6 @@
},
- // Reset chosen item and prefix
- _reset : function () {
- this._offset = 0;
- this._pos = 0;
- this._prefix.value('');
- },
-
-
/**
* Filter the list and make it visible
*
@@ -328,10 +378,10 @@
if (!this._initList())
return false;
- // show based on initial offset
- this.unmark();
- this.delete();
- this._showItems(0);
+ // show menu based on initial offset
+ this._unmark(); // Unmark everything that was marked before
+ this.unshow(); // Delete everything that is shown
+ this._showItems(0); // Show new item list
// Set the first element to active
// Todo: Or the last element chosen
@@ -345,15 +395,21 @@
this.position = -1;
}
+ // The prefix is not active
this._prefix.active(false);
+
+ // finally show the element
this._element.style.opacity = 1;
+ // Show the slider
this._slider.show();
// Iterate to the active item
if (this._active !== -1 && !this._prefix.isSet()) {
while (this._list[this.position] < this._active) {
+
+ // TODO. Improve this by moving using screen!
this.next();
};
};
@@ -369,8 +425,8 @@
*/
hide : function () {
this.active = false;
- this.unmark();
- this.delete();
+ this._unmark();
+ this.unshow();
this._element.style.opacity = 0;
this._prefix.clear();
this.onHide();
@@ -383,63 +439,6 @@
*/
onHide : function () {},
- // Initialize the list
- _initList : function () {
-
- // Create a new list
- 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;
-
- // There is no prefix set
- if (this.prefix().length <= 0) {
- var i = 0;
- for (; i < this._items.length; i++)
- this._list.push(i);
- while (this._items[++i] !== undefined) {
- this._items[i].lowlight();
- };
- return true;
- };
-
- // There is a prefix set, so filter the list
- var pos;
- var paddedPrefix = " " + this.prefix();
-
- // Iterate over all items and choose preferred matching items
- // i.e. the matching happens at the word start
- for (pos = 0; pos < this._items.length; pos++) {
- if ((this.item(pos).lcField().indexOf(paddedPrefix)) >= 0)
- this._list.push(pos);
- };
-
- // The list is empty - so lower your expectations
- // Iterate over all items and choose matching items
- // i.e. the matching happens anywhere in the word
- 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 - yeah!
- 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,
@@ -460,49 +459,20 @@
return this._lengthField;
},
- // Append Items that should be shown
- _showItems : function (off) {
-
- // Use list
- var shown = 0;
- var i;
- for (i in this._list) {
- // Don't show - it's before offset
- if (shown++ < off)
- continue;
-
- var itemNr = this._list[i];
- var item = this.item(itemNr);
- this._append(itemNr);
-
- if (i === this.position)
- item.active(true);
-
- // this._offset))
- if (shown >= (this.limit() + off))
- break;
- };
+ /**
+ * Get the associated slider object.
+ */
+ slider : function () {
+ return this._slider;
},
+
/**
* Delete all visible items from the menu element
*/
- delete : function () {
+ unshow : function () {
var child;
- /*
- // Iterate over all visible items
- for (var i = 0; i <= this.limit(); i++) {
-
- // there is a visible element
- // unhighlight!
- if (child = this.shownItem(i)) {
- child.lowlight();
- child.active(false);
- };
- };
- */
-
// Remove all children
var children = this._element.childNodes;
// Leave the prefix and lengthField
@@ -513,49 +483,6 @@
};
},
- // Unmark all items
- unmark : function () {
- for (var i in this._list) {
- var item = this._items[this._list[i]];
- item.lowlight();
- item.active(false);
- };
- },
-
- // 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());
-
- this._slider.offset(this._offset);
- },
-
-
- // 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());
-
- var e = this.element();
- // Append element after lengthField/prefix/slider
- e.insertBefore(
- item.element(),
- e.children[3]
- );
-
- this._slider.offset(this._offset);
- },
-
-
/**
* Get a specific item from the complete list
*
@@ -582,7 +509,7 @@
/**
- * Get a specific item from the visible list
+ * Get a specific item from the viewport list
*
* @param {number} index of the list item
* in the visible list
@@ -603,6 +530,16 @@
/**
+ * Length of the filtered item list.
+ */
+ liveLength : function () {
+ if (this._list === undefined)
+ this._initList();
+ return this._list.length;
+ },
+
+
+ /**
* Make the next item in the filtered menu active
*/
next : function () {
@@ -641,8 +578,8 @@
this.position = 0;
newItem = this.liveItem(0);
this._active = 0;
- this.unmark();
- this.delete();
+ this._unmark();
+ this.unshow();
this._showItems(0);
};
}
@@ -652,6 +589,7 @@
this._removeFirst();
this._offset++;
this._append(this._list[this.position]);
+ this._slider.offset(this._offset);
};
this._prefix.active(false);
@@ -659,43 +597,6 @@
},
/*
- * Page down to the first item on the next page
- */
- /*
- nextPage : function () {
-
- // Prefix is active
- if (this._prefix.active()) {
- this._prefix.active(false);
- }
-
- // Last item is chosen
- else if (this.position >= this.limit() + this._offset) {
-
- this.position = this.limit() + this._offset - 1;
- newItem = this.liveItem(this.position);
- var oldItem = this.liveItem(this.position--);
- oldItem.active(false);
- }
-
- // Last item of page is chosen
- else if (0) {
-
- // Jump to last item
- else {
- var oldItem = this.liveItem(this.position);
- oldItem.active(false);
-
- this.position = this.limit() + this._offset - 1;
- newItem = this.liveItem(this.position);
- };
-
- newItem.active(true);
- },
- */
-
-
- /*
* Make the previous item in the menu active
*/
prev : function () {
@@ -735,8 +636,8 @@
}
else {
newItem = this.liveItem(this.position);
- this.unmark();
- this.delete();
+ this._unmark();
+ this.unshow();
this._showItems(this._offset);
};
}
@@ -746,6 +647,8 @@
this._removeLast();
this._offset--;
this._prepend(this._list[this.position]);
+ // set the slider to the new offset
+ this._slider.offset(this._offset);
};
this._prefix.active(false);
@@ -753,13 +656,86 @@
},
- /**
- * Length of the filtered item list.
- */
- liveLength : function () {
- if (this._list === undefined)
- this._initList();
- return this._list.length;
+ // Unmark all items
+ _unmark : function () {
+ for (var i in this._list) {
+ var item = this._items[this._list[i]];
+ item.lowlight();
+ item.active(false);
+ };
+ },
+
+
+ // Reset chosen item and prefix
+ _reset : function () {
+ this._offset = 0;
+ this._pos = 0;
+ this._prefix.value('');
+ },
+
+
+ // Set boundary for viewport
+ _boundary : function (bool) {
+ this.item(this._list[0]).noMore(bool);
+ this.item(this._list[this._list.length - 1]).noMore(bool);
+ },
+
+
+ // Append Items that should be shown
+ _showItems : function (off) {
+
+ // Use list
+ var shown = 0;
+ var i;
+
+ for (i in this._list) {
+
+ // Don't show - it's before offset
+ shown++;
+ if (shown <= off)
+ continue;
+
+ var itemNr = this._list[i];
+ var item = this.item(itemNr);
+ this._append(itemNr);
+
+ // this._offset))
+ if (shown >= (this.limit() + off))
+ break;
+ };
+
+ // set the slider to the new offset
+ this._slider.offset(this._offset);
+ },
+
+
+ // 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());
+ },
+
+
+ // 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());
+
+ var e = this.element();
+ // Append element after lengthField/prefix/slider
+ e.insertBefore(
+ item.element(),
+ e.children[3]
+ );
},
@@ -778,3 +754,40 @@
}
};
});
+
+
+ /*
+ * Page down to the first item on the next page
+ */
+ /*
+ nextPage : function () {
+
+ // Prefix is active
+ if (this._prefix.active()) {
+ this._prefix.active(false);
+ }
+
+ // Last item is chosen
+ else if (this.position >= this.limit() + this._offset) {
+
+ this.position = this.limit() + this._offset - 1;
+ newItem = this.liveItem(this.position);
+ var oldItem = this.liveItem(this.position--);
+ oldItem.active(false);
+ }
+
+ // Last item of page is chosen
+ else if (0) {
+
+ // Jump to last item
+ else {
+ var oldItem = this.liveItem(this.position);
+ oldItem.active(false);
+
+ this.position = this.limit() + this._offset - 1;
+ newItem = this.liveItem(this.position);
+ };
+
+ newItem.active(true);
+ },
+ */