Use requirejs for clientside scripting
diff --git a/dev/js/src/menu/item.js b/dev/js/src/menu/item.js
new file mode 100644
index 0000000..b80aa2a
--- /dev/null
+++ b/dev/js/src/menu/item.js
@@ -0,0 +1,213 @@
+/*
+ * MenuItems may define:
+ *
+ * onclick: action happen on click and enter.
+ * further: action happen on right arrow
+ */
+
+/**
+ * Item in the Dropdown menu
+ */
+define({
+  /**
+   * Create a new MenuItem object.
+   *
+   * @constructor
+   * @this {MenuItem}
+   * @param {Array.<string>} An array object of name, action and
+   *   optionally a description
+   */
+  create : function (params) {
+    return Object.create(this)._init(params);
+  },
+
+  /**
+   * 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;
+  },
+
+  content : function (content) {
+    if (arguments.length === 1)
+      this._content = document.createTextNode(content);
+    return this._content;
+  },
+
+  lcField : function () {
+    return this._lcField;
+  },
+
+  action : function (action) {
+    if (arguments.length === 1)
+      this._action = action;
+    return this._action;
+  },
+
+  /**
+   * Check or set if the item is active
+   *
+   * @param {boolean|null} State of activity
+   */
+  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");
+  },
+
+  /**
+   * Check or set if the item is
+   * at the boundary of the menu
+   * list
+   *
+   * @param {boolean|null} State of activity
+   */
+  noMore : function (bool) {
+    var cl = this.element().classList;
+    if (bool === undefined)
+      return cl.contains("no-more");
+    else if (bool)
+      cl.add("no-more");
+    else
+      cl.remove("no-more");
+  },
+  
+  /**
+   * Get the document element of the menu item
+   */
+  element : function () {
+    // already defined
+    if (this._element !== undefined)
+      return this._element;
+    
+    // Create list item
+    var li = document.createElement("li");
+
+    // Connect action
+    if (this["onclick"] !== undefined) {
+      li["onclick"] = this.onclick.bind(this);
+    };
+
+    // Append template
+    li.appendChild(this.content());
+    
+    return this._element = li;
+  },
+
+  /**
+   * Highlight parts of the item
+   *
+   * @param {string} Prefix string for highlights
+   */
+  highlight : function (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
+  _highlight : function (elem, prefix) {
+    
+    if (elem.nodeType === 3) {
+      
+      var text   = elem.nodeValue;
+      var textlc = text.toLowerCase();
+      var pos    = textlc.indexOf(prefix);
+      if (pos >= 0) {
+	
+	// First element
+	if (pos > 0) {
+	  elem.parentNode.insertBefore(
+	    document.createTextNode(text.substr(0, pos)),
+	    elem
+	  );
+	};
+	
+	// Second element
+	var hl = document.createElement("mark");
+	hl.appendChild(
+	  document.createTextNode(text.substr(pos, prefix.length))
+	);
+	elem.parentNode.insertBefore(hl, elem);
+	
+	// Third element
+	var third = text.substr(pos + prefix.length);
+	if (third.length > 0) {
+	  var thirdE = document.createTextNode(third);
+	  elem.parentNode.insertBefore(
+	    thirdE,
+	    elem
+	  );
+	  this._highlight(thirdE, prefix);
+	};
+	
+	var p = elem.parentNode;
+	p.removeChild(elem);
+      };
+    }
+    else {
+      var children = elem.childNodes;
+      for (var i = children.length -1; i >= 0; i--) {
+	this._highlight(children[i], prefix);
+      };
+    };
+  },
+
+  /**
+   * Remove highlight of the menu item
+   */
+  lowlight : function () {
+    var e = this.element();
+    
+    var marks = e.getElementsByTagName("mark");
+    for (var i = marks.length - 1; i >= 0; i--) {
+      // Create text node clone
+      var x = document.createTextNode(
+	marks[i].firstChild.nodeValue
+      );
+      
+      // Replace with content
+      marks[i].parentNode.replaceChild(
+	x,
+	marks[i]
+      );
+    };
+
+    // Remove consecutive textnodes
+    e.normalize();
+  },
+
+  // Initialize menu item
+  _init : function (params) {
+    
+    if (params[0] === undefined)
+      throw new Error("Missing parameters");
+
+    this.content(params[0]);
+    
+    if (params.length === 2)
+      this._action = params[1];
+
+    this._lcField = ' ' + this.content().textContent.toLowerCase();
+
+    return this;
+  },
+
+  /**
+   * Return menu list.
+   */
+  menu : function () {
+    return this._menu;
+  }
+});