GUI -- Added keyBindings() and gestureNotes() to Key Service.
- Cleaned up fn.js and added contains().
- Unit tests added too.

Change-Id: Id310675836e592af7a4a763f6624c0ee31adfbf5
diff --git a/web/gui/src/main/webapp/app/fw/lib/fn.js b/web/gui/src/main/webapp/app/fw/lib/fn.js
index 15a8843..5269d4d 100644
--- a/web/gui/src/main/webapp/app/fw/lib/fn.js
+++ b/web/gui/src/main/webapp/app/fw/lib/fn.js
@@ -22,20 +22,33 @@
 (function (onos) {
     'use strict';
 
+    function isF(f) {
+        return $.isFunction(f) ? f : null;
+    }
+
+    function isA(a) {
+        return $.isArray(a) ? a : null;
+    }
+
+    function isS(s) {
+        return typeof s === 'string' ? s : null;
+    }
+
+    function isO(o) {
+        return $.isPlainObject(o) ? o : null;
+    }
+
+    function contains(a, x) {
+        return isA(a) && a.indexOf(x) > -1;
+    }
+
     onos.factory('FnService', [function () {
         return {
-            isF: function (f) {
-                return $.isFunction(f) ? f : null;
-            },
-            isA: function (a) {
-                return $.isArray(a) ? a : null;
-            },
-            isS: function (s) {
-                return typeof s === 'string' ? s : null;
-            },
-            isO: function (o) {
-                return $.isPlainObject(o) ? o : null;
-            }
+            isF: isF,
+            isA: isA,
+            isS: isS,
+            isO: isO,
+            contains: contains
         };
     }]);
 
diff --git a/web/gui/src/main/webapp/app/fw/lib/keys.js b/web/gui/src/main/webapp/app/fw/lib/keys.js
index c5f0a19..f4318df 100644
--- a/web/gui/src/main/webapp/app/fw/lib/keys.js
+++ b/web/gui/src/main/webapp/app/fw/lib/keys.js
@@ -144,6 +144,45 @@
         return true;
     }
 
+    function setKeyBindings(keyArg) {
+        var viewKeys,
+            masked = [];
+
+        if (f.isF(keyArg)) {
+            // set general key handler callback
+            keyHandler.viewFn = keyArg;
+        } else {
+            // set specific key filter map
+            viewKeys = d3.map(keyArg).keys();
+            viewKeys.forEach(function (key) {
+                if (keyHandler.maskedKeys[key]) {
+                    masked.push('  Key "' + key + '" is reserved');
+                }
+            });
+
+            if (masked.length) {
+                // TODO: use alert service
+                window.alert('WARNING...\n\nsetKeys():\n' + masked.join('\n'));
+            }
+            keyHandler.viewKeys = keyArg;
+        }
+    }
+
+    function getKeyBindings() {
+        var gkeys = d3.map(keyHandler.globalKeys).keys(),
+            masked = d3.map(keyHandler.maskedKeys).keys(),
+            vkeys = d3.map(keyHandler.viewKeys).keys(),
+            vfn = !!f.isF(keyHandler.viewFn);
+
+        return {
+            globalKeys: gkeys,
+            maskedKeys: masked,
+            viewKeys: vkeys,
+            viewFunction: vfn
+        };
+    }
+
+    // TODO: inject alert service
     onos.factory('KeyService', ['FnService', function (fs) {
         f = fs;
         return {
@@ -154,7 +193,20 @@
             theme: function () {
                 return theme;
             },
-            whatKey: whatKey
+            keyBindings: function (x) {
+                if (x === undefined) {
+                    return getKeyBindings();
+                } else {
+                    setKeyBindings(x);
+                }
+            },
+            gestureNotes: function (g) {
+                if (g === undefined) {
+                    return keyHandler.viewGestures;
+                } else {
+                    keyHandler.viewGestures = f.isA(g) || [];
+                }
+            }
         };
     }]);