blob: 6c831f4791826f65e493f5b8ff4dfbdfc473fbc7 [file] [log] [blame]
Simon Hunt1eecfa22014-12-16 14:46:29 -08001/*
Simon Hunt8ead3a22015-01-06 11:00:15 -08002 * Copyright 2014,2015 Open Networking Laboratory
Simon Hunt1eecfa22014-12-16 14:46:29 -08003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17/*
Simon Huntdc6362a2014-12-18 19:55:23 -080018 ONOS GUI -- Util -- Key Handler Service
Simon Hunt1eecfa22014-12-16 14:46:29 -080019 */
Simon Huntdc6362a2014-12-18 19:55:23 -080020(function () {
Simon Hunt1eecfa22014-12-16 14:46:29 -080021 'use strict';
22
23 // references to injected services
Simon Hunt9d286562015-03-09 13:53:50 -070024 var $log, fs, ts, ns, qhs;
Simon Hunt1eecfa22014-12-16 14:46:29 -080025
26 // internal state
Simon Hunt36fc15c2015-02-12 17:02:58 -080027 var enabled = true,
28 keyHandler = {
Simon Hunt1eecfa22014-12-16 14:46:29 -080029 globalKeys: {},
30 maskedKeys: {},
31 viewKeys: {},
32 viewFn: null,
33 viewGestures: []
Yuta HIGUCHI4f39bcd2014-12-18 20:46:14 -080034 };
Simon Hunt1eecfa22014-12-16 14:46:29 -080035
Simon Hunt9d286562015-03-09 13:53:50 -070036 // TODO: do we still need to have the concept of view token here..?
Simon Hunt1eecfa22014-12-16 14:46:29 -080037 function getViewToken() {
38 return 'NotYetAViewToken';
39 }
40
41 function whatKey(code) {
42 switch (code) {
43 case 13: return 'enter';
44 case 16: return 'shift';
45 case 17: return 'ctrl';
46 case 18: return 'alt';
47 case 27: return 'esc';
48 case 32: return 'space';
49 case 37: return 'leftArrow';
50 case 38: return 'upArrow';
51 case 39: return 'rightArrow';
52 case 40: return 'downArrow';
53 case 91: return 'cmdLeft';
54 case 93: return 'cmdRight';
55 case 187: return 'equals';
Simon Hunt90dcc3e2015-03-25 15:01:27 -070056 case 188: return 'comma';
Simon Hunt1eecfa22014-12-16 14:46:29 -080057 case 189: return 'dash';
Simon Hunt90dcc3e2015-03-25 15:01:27 -070058 case 190: return 'dot';
Simon Hunt1eecfa22014-12-16 14:46:29 -080059 case 191: return 'slash';
60 case 192: return 'backQuote';
61 case 220: return 'backSlash';
62 default:
63 if ((code >= 48 && code <= 57) ||
64 (code >= 65 && code <= 90)) {
65 return String.fromCharCode(code);
66 } else if (code >= 112 && code <= 123) {
67 return 'F' + (code - 111);
68 }
69 return '.';
70 }
71 }
72
73 function keyIn() {
74 var event = d3.event,
75 keyCode = event.keyCode,
76 key = whatKey(keyCode),
77 kh = keyHandler,
78 gk = kh.globalKeys[key],
Yuta HIGUCHI4f39bcd2014-12-18 20:46:14 -080079 gcb = fs.isF(gk) || (fs.isA(gk) && fs.isF(gk[0])),
Simon Hunt1eecfa22014-12-16 14:46:29 -080080 vk = kh.viewKeys[key],
Simon Hunt09060142015-03-18 20:23:32 -070081 kl = fs.isF(kh.viewKeys._keyListener),
Yuta HIGUCHI4f39bcd2014-12-18 20:46:14 -080082 vcb = fs.isF(vk) || (fs.isA(vk) && fs.isF(vk[0])) || fs.isF(kh.viewFn),
Simon Hunt1eecfa22014-12-16 14:46:29 -080083 token = getViewToken();
84
Simon Huntdc6362a2014-12-18 19:55:23 -080085 d3.event.stopPropagation();
86
Simon Hunt36fc15c2015-02-12 17:02:58 -080087 if (enabled) {
88 // global callback?
89 if (gcb && gcb(token, key, keyCode, event)) {
90 // if the event was 'handled', we are done
91 return;
92 }
93 // otherwise, let the view callback have a shot
94 if (vcb) {
95 vcb(token, key, keyCode, event);
96 }
Simon Hunt09060142015-03-18 20:23:32 -070097 if (kl) {
98 kl(key);
99 }
Simon Hunt1eecfa22014-12-16 14:46:29 -0800100 }
101 }
102
103 function setupGlobalKeys() {
Simon Hunt404f6b22015-01-21 14:00:56 -0800104 angular.extend(keyHandler, {
Simon Hunt1eecfa22014-12-16 14:46:29 -0800105 globalKeys: {
106 backSlash: [quickHelp, 'Show / hide Quick Help'],
107 slash: [quickHelp, 'Show / hide Quick Help'],
108 esc: [escapeKey, 'Dismiss dialog or cancel selections'],
109 T: [toggleTheme, "Toggle theme"]
110 },
111 globalFormat: ['backSlash', 'slash', 'esc', 'T'],
112
113 // Masked keys are global key handlers that always return true.
114 // That is, the view will never see the event for that key.
115 maskedKeys: {
116 slash: true,
117 backSlash: true,
118 T: true
119 }
120 });
121 }
122
123 function quickHelp(view, key, code, ev) {
Simon Hunt639dc662015-02-18 14:19:20 -0800124 qhs.showQuickHelp(keyHandler);
Simon Hunt1eecfa22014-12-16 14:46:29 -0800125 return true;
126 }
127
Simon Hunta0eb0a82015-02-11 12:30:06 -0800128 // returns true if we 'consumed' the ESC keypress, false otherwise
Simon Hunt1eecfa22014-12-16 14:46:29 -0800129 function escapeKey(view, key, code, ev) {
Simon Hunt9d286562015-03-09 13:53:50 -0700130 return ns.hideIfShown() || qhs.hideQuickHelp();
Simon Hunt1eecfa22014-12-16 14:46:29 -0800131 }
132
133 function toggleTheme(view, key, code, ev) {
Yuta HIGUCHI4f39bcd2014-12-18 20:46:14 -0800134 ts.toggleTheme();
Simon Hunt1eecfa22014-12-16 14:46:29 -0800135 return true;
136 }
137
Simon Hunt59df0b22014-12-17 10:32:25 -0800138 function setKeyBindings(keyArg) {
139 var viewKeys,
140 masked = [];
141
Yuta HIGUCHI4f39bcd2014-12-18 20:46:14 -0800142 if (fs.isF(keyArg)) {
Simon Hunt59df0b22014-12-17 10:32:25 -0800143 // set general key handler callback
144 keyHandler.viewFn = keyArg;
145 } else {
146 // set specific key filter map
147 viewKeys = d3.map(keyArg).keys();
148 viewKeys.forEach(function (key) {
149 if (keyHandler.maskedKeys[key]) {
Simon Huntaf322072014-12-18 13:23:40 -0800150 masked.push('setKeyBindings(): Key "' + key + '" is reserved');
Simon Hunt59df0b22014-12-17 10:32:25 -0800151 }
152 });
153
154 if (masked.length) {
Simon Huntaf322072014-12-18 13:23:40 -0800155 $log.warn(masked.join('\n'));
Simon Hunt59df0b22014-12-17 10:32:25 -0800156 }
157 keyHandler.viewKeys = keyArg;
158 }
159 }
160
161 function getKeyBindings() {
162 var gkeys = d3.map(keyHandler.globalKeys).keys(),
163 masked = d3.map(keyHandler.maskedKeys).keys(),
164 vkeys = d3.map(keyHandler.viewKeys).keys(),
Yuta HIGUCHI4f39bcd2014-12-18 20:46:14 -0800165 vfn = !!fs.isF(keyHandler.viewFn);
Simon Hunt59df0b22014-12-17 10:32:25 -0800166
167 return {
168 globalKeys: gkeys,
169 maskedKeys: masked,
170 viewKeys: vkeys,
171 viewFunction: vfn
172 };
173 }
174
Simon Huntdc6362a2014-12-18 19:55:23 -0800175 angular.module('onosUtil')
Simon Hunt639dc662015-02-18 14:19:20 -0800176 .factory('KeyService',
Simon Hunt9d286562015-03-09 13:53:50 -0700177 ['$log', 'FnService', 'ThemeService', 'NavService',
Simon Hunt639dc662015-02-18 14:19:20 -0800178
Simon Hunt9d286562015-03-09 13:53:50 -0700179 function (_$log_, _fs_, _ts_, _ns_) {
Yuta HIGUCHI4f39bcd2014-12-18 20:46:14 -0800180 $log = _$log_;
181 fs = _fs_;
182 ts = _ts_;
Simon Hunt9d286562015-03-09 13:53:50 -0700183 ns = _ns_;
Yuta HIGUCHI4f39bcd2014-12-18 20:46:14 -0800184
Simon Huntdc6362a2014-12-18 19:55:23 -0800185 return {
Simon Hunt639dc662015-02-18 14:19:20 -0800186 bindQhs: function (_qhs_) {
187 qhs = _qhs_;
188 },
Simon Huntdc6362a2014-12-18 19:55:23 -0800189 installOn: function (elem) {
190 elem.on('keydown', keyIn);
191 setupGlobalKeys();
192 },
Simon Huntdc6362a2014-12-18 19:55:23 -0800193 keyBindings: function (x) {
194 if (x === undefined) {
195 return getKeyBindings();
196 } else {
197 setKeyBindings(x);
198 }
199 },
200 gestureNotes: function (g) {
201 if (g === undefined) {
202 return keyHandler.viewGestures;
203 } else {
Yuta HIGUCHI4f39bcd2014-12-18 20:46:14 -0800204 keyHandler.viewGestures = fs.isA(g) || [];
Simon Huntdc6362a2014-12-18 19:55:23 -0800205 }
Simon Hunt36fc15c2015-02-12 17:02:58 -0800206 },
207 enableKeys: function (b) {
208 enabled = b;
Simon Hunt59df0b22014-12-17 10:32:25 -0800209 }
Simon Huntdc6362a2014-12-18 19:55:23 -0800210 };
Simon Hunt1eecfa22014-12-16 14:46:29 -0800211 }]);
212
Simon Huntdc6362a2014-12-18 19:55:23 -0800213}());