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) || [];
+ }
+ }
};
}]);
diff --git a/web/gui/src/main/webapp/tests/fw/lib/fn-spec.js b/web/gui/src/main/webapp/tests/fw/lib/fn-spec.js
index 4bd1b4a..44c12b5 100644
--- a/web/gui/src/main/webapp/tests/fw/lib/fn-spec.js
+++ b/web/gui/src/main/webapp/tests/fw/lib/fn-spec.js
@@ -26,7 +26,8 @@
someObject = { foo: 'bar'},
someNumber = 42,
someString = 'xyyzy',
- someDate = new Date();
+ someDate = new Date(),
+ stringArray = ['foo', 'bar'];
beforeEach(module('onosApp'));
@@ -149,4 +150,20 @@
it('isO(): the reference for object', function () {
expect(fs.isO(someObject)).toBe(someObject);
});
+
+ // === Tests for contains()
+ it('contains(): false for improper args', function () {
+ expect(fs.contains()).toBeFalsy();
+ });
+ it('contains(): false for non-array', function () {
+ expect(fs.contains(null, 1)).toBeFalsy();
+ });
+ it ('contains(): true for contained item', function () {
+ expect(fs.contains(someArray, 1)).toBeTruthy();
+ expect(fs.contains(stringArray, 'bar')).toBeTruthy();
+ });
+ it ('contains(): false for non-contained item', function () {
+ expect(fs.contains(someArray, 109)).toBeFalsy();
+ expect(fs.contains(stringArray, 'zonko')).toBeFalsy();
+ });
});
diff --git a/web/gui/src/main/webapp/tests/fw/lib/keys-spec.js b/web/gui/src/main/webapp/tests/fw/lib/keys-spec.js
index 7d53c23..d8fa818 100644
--- a/web/gui/src/main/webapp/tests/fw/lib/keys-spec.js
+++ b/web/gui/src/main/webapp/tests/fw/lib/keys-spec.js
@@ -20,9 +20,7 @@
@author Simon Hunt
*/
describe('factory: fw/lib/keys.js', function() {
- var ks,
- fs,
- d3Elem;
+ var ks, fs, d3Elem, elem, last;
beforeEach(module('onosApp'));
@@ -30,7 +28,14 @@
ks = KeyService;
fs = FnService;
d3Elem = d3.select('body').append('p').attr('id', 'ptest');
+ elem = d3Elem.node();
ks.installOn(d3Elem);
+ last = {
+ view: null,
+ key: null,
+ code: null,
+ ev: null
+ };
}));
afterEach(function () {
@@ -74,45 +79,120 @@
element.dispatchEvent(ev);
}
+ // === Theme related tests
it('should start in light theme', function () {
expect(ks.theme()).toEqual('light');
});
it('should toggle to dark theme', function () {
- jsKeyDown(d3Elem.node(), 84); // 'T'
+ jsKeyDown(elem, 84); // 'T'
expect(ks.theme()).toEqual('dark');
});
- // key code lookups
- // NOTE: should be injecting keydown events, rather than exposing whatKey()
- it('whatKey: 13', function () {
- expect(ks.whatKey(13)).toEqual('enter');
+ // === Key binding related tests
+ it('should start with default key bindings', function () {
+ var state = ks.keyBindings(),
+ gk = state.globalKeys,
+ mk = state.maskedKeys,
+ vk = state.viewKeys,
+ vf = state.viewFunction;
+
+ expect(gk.length).toEqual(4);
+ ['backSlash', 'slash', 'esc', 'T'].forEach(function (k) {
+ expect(fs.contains(gk, k)).toBeTruthy();
+ });
+
+ expect(mk.length).toEqual(3);
+ ['backSlash', 'slash', 'T'].forEach(function (k) {
+ expect(fs.contains(mk, k)).toBeTruthy();
+ });
+
+ expect(vk.length).toEqual(0);
+ expect(vf).toBeFalsy();
});
- it('whatKey: 16', function () {
- expect(ks.whatKey(16)).toEqual('shift');
+
+ function bindTestKeys(withDescs) {
+ var keys = ['A', '1', 'F5', 'equals'],
+ kb = {};
+
+ function cb(view, key, code, ev) {
+ last.view = view;
+ last.key = key;
+ last.code = code;
+ last.ev = ev;
+ }
+
+ function bind(k) {
+ return withDescs ? [cb, 'desc for key ' + k] : cb;
+ }
+
+ keys.forEach(function (k) {
+ kb[k] = bind(k);
+ });
+
+ ks.keyBindings(kb);
+ }
+
+ function verifyCall(key, code) {
+ // TODO: update expectation, when view tokens are implemented
+ expect(last.view).toEqual('NotYetAViewToken');
+ last.view = null;
+
+ expect(last.key).toEqual(key);
+ last.key = null;
+
+ expect(last.code).toEqual(code);
+ last.code = null;
+
+ expect(last.ev).toBeTruthy();
+ last.ev = null;
+ }
+
+ function verifyNoCall() {
+ expect(last.view).toBeNull();
+ expect(last.key).toBeNull();
+ expect(last.code).toBeNull();
+ expect(last.ev).toBeNull();
+ }
+
+ function verifyTestKeys() {
+ jsKeyDown(elem, 65); // 'A'
+ verifyCall('A', 65);
+ jsKeyDown(elem, 66); // 'B'
+ verifyNoCall();
+
+ jsKeyDown(elem, 49); // '1'
+ verifyCall('1', 49);
+ jsKeyDown(elem, 50); // '2'
+ verifyNoCall();
+
+ jsKeyDown(elem, 116); // 'F5'
+ verifyCall('F5', 116);
+ jsKeyDown(elem, 117); // 'F6'
+ verifyNoCall();
+
+ jsKeyDown(elem, 187); // 'equals'
+ verifyCall('equals', 187);
+ jsKeyDown(elem, 189); // 'dash'
+ verifyNoCall();
+
+ var vk = ks.keyBindings().viewKeys;
+
+ expect(vk.length).toEqual(4);
+ ['A', '1', 'F5', 'equals'].forEach(function (k) {
+ expect(fs.contains(vk, k)).toBeTruthy();
+ });
+
+ expect(ks.keyBindings().viewFunction).toBeFalsy();
+ }
+
+ it('should allow specific key bindings', function () {
+ bindTestKeys();
+ verifyTestKeys();
});
- it('whatKey: 40', function () {
- expect(ks.whatKey(40)).toEqual('downArrow');
- });
- it('whatKey: 65', function () {
- expect(ks.whatKey(65)).toEqual('A');
- });
- it('whatKey: 84', function () {
- expect(ks.whatKey(84)).toEqual('T');
- });
- it('whatKey: 49', function () {
- expect(ks.whatKey(49)).toEqual('1');
- });
- it('whatKey: 55', function () {
- expect(ks.whatKey(55)).toEqual('7');
- });
- it('whatKey: 112', function () {
- expect(ks.whatKey(112)).toEqual('F1');
- });
- it('whatKey: 123', function () {
- expect(ks.whatKey(123)).toEqual('F12');
- });
- it('whatKey: 1', function () {
- expect(ks.whatKey(1)).toEqual('.');
+
+ it('should allow specific key bindings with descriptions', function () {
+ bindTestKeys(true);
+ verifyTestKeys();
});
});