Introduce a general buttongroup menu

Change-Id: I2d84af1c9d43ce3e57b10321bd7d414a64cf68a8
diff --git a/dev/demo/buttongroup.html b/dev/demo/buttongroup.html
new file mode 100644
index 0000000..9a26ed1
--- /dev/null
+++ b/dev/demo/buttongroup.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <title>Hint demo</title>
+    <meta charset="utf-8" />
+    <script src="../js/lib/require.js" async="async"></script>
+    <link type="text/css" rel="stylesheet" href="../css/kalamar.css" />
+  </head>
+  <body>
+    <nav id="mainButton" style="position: absolute; top: 300px"></nav>
+  </body>
+  <script>
+requirejs.config({
+  baseUrl: '../js/src',
+  paths : {
+    'lib': '../lib'
+  }
+});
+
+
+require(['buttongroup'], function (btnClass) {
+  btns = btnClass.create();
+  btns.add('Meta',['meta'], function () {
+    console.log(this.button.classList.contains('meta'));
+  });
+  var list = btns.addList('More', ['list']);
+  list.readItems([
+    ['cool', 'cool', function (e, action) { console.log('really' + this.action()) }],
+    ['very cool', 'veryCool', function (e, action) { console.log('very cool') }]
+  ]);
+  document.getElementById('mainButton').appendChild(btns.element());
+});
+
+
+  </script>
+</html>
diff --git a/dev/js/runner/match.html b/dev/js/runner/match.html
index 564acab..cd4f93a 100644
--- a/dev/js/runner/match.html
+++ b/dev/js/runner/match.html
@@ -2,7 +2,7 @@
 <html>
 <head>
   <meta charset="utf-8">
-  <title>Spec Runner for Morph Table View</title>
+  <title>Spec Runner for Matches</title>
   <link rel="shortcut icon" type="image/png" href="../lib/jasmine-2.1.1/jasmine_favicon.png">
   <link rel="stylesheet" href="../lib/jasmine-2.1.1/jasmine.css">
   <script src="../lib/require.js"></script>
diff --git a/dev/js/spec/buttongroupSpec.js b/dev/js/spec/buttongroupSpec.js
index 1e27d96..67baaef 100644
--- a/dev/js/spec/buttongroupSpec.js
+++ b/dev/js/spec/buttongroupSpec.js
@@ -103,5 +103,9 @@
       group.element().firstChild.click();
       expect(fun.count).toEqual(1);
     });
+
+    it('should open lists', function () {
+      
+    });
   });
 });
diff --git a/dev/js/spec/matchSpec.js b/dev/js/spec/matchSpec.js
index b09a7cb..5463835 100644
--- a/dev/js/spec/matchSpec.js
+++ b/dev/js/spec/matchSpec.js
@@ -368,18 +368,18 @@
       m.open();
       var relation = e.querySelector("p.ref > div.action.bottom > span:nth-of-type(3)");
       expect(relation.getAttribute("class")).toEqual("tree");
-      expect(document.getElementById("treeMenu")).toBeNull();
+      expect(document.getElementsByClassName("button-group-list").length).toEqual(0);
 
       expect(document.activeElement.tagName).toEqual("BODY");
 
       // Show menu
       relation.click();
-      expect(document.getElementById("treeMenu")).toBeTruthy();
+      expect(document.getElementsByClassName("button-group-list").length).toEqual(1);
 
       expect(document.activeElement.tagName).toEqual("UL");
 
       // Choose first tree
-      document.getElementById("treeMenu").getElementsByTagName("li")[1].click();
+      document.getElementsByClassName("button-group-list")[0].getElementsByTagName("li")[1].click();
       expect(e.querySelector("div.matchinfo div.matchtree h6 span").innerText).toEqual("corenlp");
 
       // This should blur the focus
@@ -701,14 +701,14 @@
 
   
   describe('KorAP.MatchTreeMenu', function () {
-    var matchTreeMenu = require('match/treemenu');
+    var matchTreeMenu = require('buttongroup/menu');
     var matchTreeItem = require('match/treeitem');
 
     it('should be initializable', function () {
       var menu = matchTreeMenu.create([
         ['cnx/c', 'cnx', 'c'],
         ['xip/c', 'xip', 'c']
-      ]);
+      ], matchTreeItem);
 
       expect(menu.itemClass()).toEqual(matchTreeItem);
       expect(menu.element().nodeName).toEqual('UL');
diff --git a/dev/js/src/buttongroup.js b/dev/js/src/buttongroup.js
index 12870b5..443d650 100644
--- a/dev/js/src/buttongroup.js
+++ b/dev/js/src/buttongroup.js
@@ -1,4 +1,6 @@
-define(['util'], function () {
+define(['buttongroup/menu','menu/item','util'], function (treeMenuClass, defaultItemClass) {
+  "use strict";
+  
   return {
     /**
      * Create button group
@@ -44,6 +46,8 @@
     
     /**
      * Add button in order
+     *
+     * Returns the button element
      */
     add : function (title, classes, cb) {
       var b = this._element.addE('span');
@@ -60,13 +64,36 @@
         e.halt();
         
         // Call callback
-        cb.apply(that._bind || this, e)
+        var obj = that._bind || this;
+        obj.button = b;
+        cb.apply(obj, e)
       });
+
+      return b;
     },
 
     
     /**
-     * Bind an object to all callbacks of the button group
+     * Add button that spawns a list in order.
+     *
+     * Returns the list object.
+     */
+    addList : function (title, classes, itemClass) {
+      var list = treeMenuClass.create([], itemClass || defaultItemClass);
+      this.add(title, classes, function (e) {
+        list.show();
+        list.button(this.button);
+        list.focus();
+      });
+
+      return list;
+    },
+    
+
+    /**
+     * Bind an object to all callbacks of the button group.
+     * To get the button element inside the callback,
+     * use this.button
      */
     bind : function (obj) {
       if (obj !== undefined) {
diff --git a/dev/js/src/match/treemenu.js b/dev/js/src/buttongroup/menu.js
similarity index 86%
rename from dev/js/src/match/treemenu.js
rename to dev/js/src/buttongroup/menu.js
index 0010271..7ab149d 100644
--- a/dev/js/src/match/treemenu.js
+++ b/dev/js/src/buttongroup/menu.js
@@ -1,7 +1,7 @@
   /**
-   * Menu to choose from for tree views.
+   * Menu to choose from in a button group.
    */
-define(['menu', 'match/treeitem'], function (menuClass, itemClass) {
+define(['menu'], function (menuClass) {
   "use strict";
 
   return {
@@ -15,7 +15,7 @@
      * @param params The match menu items
      *   as an array of arrays.
      */
-    create : function (list) {
+    create : function (list, itemClass) {
       var obj = Object.create(menuClass)
 	        .upgradeTo(this)
 	        ._init(list, {itemClass : itemClass});
@@ -28,7 +28,7 @@
 	      this.menu.hide();
       });
 
-      e.setAttribute('id', 'treeMenu');
+      e.classList.add('button-group-list');
 
       // Add menu to body
       document.getElementsByTagName('body')[0].appendChild(e);
@@ -38,6 +38,8 @@
 
     /**
      * The match information object of the menu.
+     * TODO:
+     *   Rename to 'Panel'
      */
     info :function (infoVar) {
       if (infoVar !== undefined)
@@ -47,10 +49,10 @@
     },
 
     // Attach menu to button
-    attachTo : function (e) {
+    button : function (btn) {
 
       // this._attached = e;
-      this._repos(e);
+      this._repos(btn);
       this.slider().reInit();
 
       /*
@@ -62,7 +64,7 @@
       };
 
       this._onscroll = function () {
-        this._repos(e);
+        this._repos(btn);
       }.bind(this);
       
       window.addEventListener('scroll', this._onscroll);
diff --git a/dev/js/src/hint/item.js b/dev/js/src/hint/item.js
index 47d332b..80ac78b 100644
--- a/dev/js/src/hint/item.js
+++ b/dev/js/src/hint/item.js
@@ -40,19 +40,20 @@
       };
       return this._content;
     },
-
+  
     /**
      * Override the click action
      * of the menu item.
      */
     onclick : function (e) {
       var m = this.menu();
-      var h = m.hint();
       // m.hide();
 
       // Reset prefix
       m.prefix("");
 
+      var h = m.hint();
+
       // Update input field
       var input = h.inputField();
       input.insert(this._action).update();
diff --git a/dev/js/src/match.js b/dev/js/src/match.js
index 4e7bd1a..59d2e83 100644
--- a/dev/js/src/match.js
+++ b/dev/js/src/match.js
@@ -11,10 +11,11 @@
  */
 define([
   'match/info',      // rename to anno
-  'match/treemenu',
+  'match/treeitem',
   'buttongroup',
+  'buttongroup/menu',
 	'util'
-], function (infoClass,matchTreeMenuClass,buttonGroupClass) { //, refClass) {
+], function (infoClass,treeItemClass,buttonGroupClass,buttonGroupMenuClass) { //, refClass) {
 
   // Localization values
   const loc   = KorAP.Locale;
@@ -210,7 +211,7 @@
       btns.add(
         loc.ADDTREE, ['tree'], function (e) {
           if (KorAP.TreeMenu === undefined) {
-            KorAP.TreeMenu = matchTreeMenuClass.create([]);
+            KorAP.TreeMenu = buttonGroupMenuClass.create([], treeItemClass);
           };
 
           var tm = KorAP.TreeMenu;
@@ -221,7 +222,7 @@
 
           // Reposition and show menu
           tm.show();
-          tm.attachTo(this);
+          tm.button(this);
           tm.focus();
         }
       );
diff --git a/dev/js/src/menu/item.js b/dev/js/src/menu/item.js
index 812a398..e06de1e 100644
--- a/dev/js/src/menu/item.js
+++ b/dev/js/src/menu/item.js
@@ -47,15 +47,6 @@
   },
 
   /**
-   * Get the lower cased field of the item
-   * (used for analyses).
-   */
-  lcField : function () {
-    return this._lcField;
-  },
-
-
-  /**
    * Get or set the information for action of this item. 
    */
   action : function (action) {
@@ -63,6 +54,16 @@
       this._action = action;
     return this._action;
   },
+  
+
+  /**
+   * Get the lower cased field of the item
+   * (used for analyses).
+   */
+  lcField : function () {
+    return this._lcField;
+  },
+
 
   /**
    * Check or set if the item is active
@@ -170,6 +171,7 @@
     this._prefix = null;
   },
 
+
   // Highlight a certain substring of the menu item
   _highlight : function (elem, prefixString) {    
     if (elem.nodeType === 3) {
@@ -260,15 +262,36 @@
 
     this.content(params[0]);
     
-    if (params.length === 2)
+    if (params.length > 1) {
       this._action = params[1];
 
+      if (params.length > 2)
+        this._onclick = params[2];
+    };
+    
     this._lcField = ' ' + this.content().textContent.toLowerCase();
     this._prefix = null;
 
     return this;
   },
 
+
+  /**
+   * The click action of the menu item.
+   */
+  onclick : function (e) {
+    var m = this.menu();
+
+    // Reset prefix
+    m.prefix("");
+
+    if (this._onclick)
+      this._onclick.apply(this, e);
+
+    m.hide();
+  },
+
+  
   /**
    * Return menu list.
    */
diff --git a/dev/scss/main/buttongroup.scss b/dev/scss/main/buttongroup.scss
index 607f9e3..dea337d 100644
--- a/dev/scss/main/buttongroup.scss
+++ b/dev/scss/main/buttongroup.scss
@@ -40,3 +40,17 @@
     }
   }
 }
+
+ul.button-group-list {
+  border-top-right-radius: 8px;
+  z-index: 150;
+  font-size: 10pt;
+  position: fixed;
+  left: 0;
+  text-align: left;
+  margin: -1 * $border-size;
+  margin-top: 0;
+  > li:first-of-type {
+	  border-top-right-radius: 5px;
+  }
+}
diff --git a/dev/scss/main/matchinfo.scss b/dev/scss/main/matchinfo.scss
index bf25ff1..f2ed006 100644
--- a/dev/scss/main/matchinfo.scss
+++ b/dev/scss/main/matchinfo.scss
@@ -79,23 +79,6 @@
     }
   }
 }
-
-ul#treeMenu {
-  border-top-right-radius: 8px;
-  z-index: 150;
-  font-size: 10pt;
-  position: fixed;
-  // width: $left-width;
-  left: 0;
-  // bottom: 0;
-  text-align: left;
-  margin: -1* $border-size;
-  margin-top: 0;
-  > li:first-of-type {
-	  border-top-right-radius: 5px;
-  }
-}
-
  
 div.matchtable > div {
   z-index: 20;