blob: fe290c3a6a67bff399fc65250c93916b6e734062 [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 Hunt9f5e97d2015-12-11 10:32:09 -080024 var $log, $timeout, fs, ts, ns, qhs;
25
26 // constants
27 var eeggMin = 'shiftO',
28 eeggMax = 'shiftONOS';
Simon Hunt1eecfa22014-12-16 14:46:29 -080029
30 // internal state
Simon Hunt36fc15c2015-02-12 17:02:58 -080031 var enabled = true,
Simon Huntd5579252015-10-06 15:09:14 -070032 globalEnabled = true,
Simon Hunt36fc15c2015-02-12 17:02:58 -080033 keyHandler = {
Simon Hunt1eecfa22014-12-16 14:46:29 -080034 globalKeys: {},
35 maskedKeys: {},
36 viewKeys: {},
37 viewFn: null,
38 viewGestures: []
Simon Hunt9f5e97d2015-12-11 10:32:09 -080039 },
40 eegg = '';
Simon Hunt1eecfa22014-12-16 14:46:29 -080041
Simon Hunt9f5e97d2015-12-11 10:32:09 -080042 function layEgg(key) {
43 eegg += key;
44 if (eeggMax.indexOf(eegg) === 0) {
45 if (eegg === eeggMax) {
46 d3.select('body').append('div').attr('id', 'eegg')
47 .append('img').attr('src', 'data/img/eegg.png');
48 $timeout(function () { d3.select('#eegg').remove(); }, 3000);
Thomas Vachuskafe1f01a2015-12-10 23:56:02 -080049 eegg = '';
50 }
51 return true;
Simon Hunt9f5e97d2015-12-11 10:32:09 -080052 }
53 if (eegg !== eeggMin) {
Thomas Vachuskafe1f01a2015-12-10 23:56:02 -080054 eegg = '';
55 }
56 return false;
57 }
58
Simon Hunt1eecfa22014-12-16 14:46:29 -080059 function whatKey(code) {
60 switch (code) {
Simon Hunt18d4bc82016-01-08 14:09:35 -080061 case 8: return 'delete';
62 case 9: return 'tab';
Simon Hunt1eecfa22014-12-16 14:46:29 -080063 case 13: return 'enter';
64 case 16: return 'shift';
65 case 17: return 'ctrl';
66 case 18: return 'alt';
67 case 27: return 'esc';
68 case 32: return 'space';
69 case 37: return 'leftArrow';
70 case 38: return 'upArrow';
71 case 39: return 'rightArrow';
72 case 40: return 'downArrow';
73 case 91: return 'cmdLeft';
74 case 93: return 'cmdRight';
Simon Hunt18d4bc82016-01-08 14:09:35 -080075 case 186: return 'semicolon';
Simon Hunt1eecfa22014-12-16 14:46:29 -080076 case 187: return 'equals';
Simon Hunt90dcc3e2015-03-25 15:01:27 -070077 case 188: return 'comma';
Simon Hunt1eecfa22014-12-16 14:46:29 -080078 case 189: return 'dash';
Simon Hunt90dcc3e2015-03-25 15:01:27 -070079 case 190: return 'dot';
Simon Hunt1eecfa22014-12-16 14:46:29 -080080 case 191: return 'slash';
81 case 192: return 'backQuote';
Simon Hunt18d4bc82016-01-08 14:09:35 -080082 case 219: return 'openBracket';
Simon Hunt1eecfa22014-12-16 14:46:29 -080083 case 220: return 'backSlash';
Simon Hunt18d4bc82016-01-08 14:09:35 -080084 case 221: return 'closeBracket';
85 case 222: return 'quote';
Simon Hunt1eecfa22014-12-16 14:46:29 -080086 default:
87 if ((code >= 48 && code <= 57) ||
88 (code >= 65 && code <= 90)) {
89 return String.fromCharCode(code);
90 } else if (code >= 112 && code <= 123) {
91 return 'F' + (code - 111);
92 }
93 return '.';
94 }
95 }
96
97 function keyIn() {
98 var event = d3.event,
99 keyCode = event.keyCode,
100 key = whatKey(keyCode),
101 kh = keyHandler,
102 gk = kh.globalKeys[key],
Yuta HIGUCHI4f39bcd2014-12-18 20:46:14 -0800103 gcb = fs.isF(gk) || (fs.isA(gk) && fs.isF(gk[0])),
Simon Hunt1eecfa22014-12-16 14:46:29 -0800104 vk = kh.viewKeys[key],
Simon Hunt09060142015-03-18 20:23:32 -0700105 kl = fs.isF(kh.viewKeys._keyListener),
Yuta HIGUCHI4f39bcd2014-12-18 20:46:14 -0800106 vcb = fs.isF(vk) || (fs.isA(vk) && fs.isF(vk[0])) || fs.isF(kh.viewFn),
Simon Hunt1bf1e8c2015-04-08 10:12:43 -0700107 token = 'keyev'; // indicate this was a key-pressed event
Simon Hunt1eecfa22014-12-16 14:46:29 -0800108
Simon Huntdc6362a2014-12-18 19:55:23 -0800109 d3.event.stopPropagation();
110
Simon Hunt36fc15c2015-02-12 17:02:58 -0800111 if (enabled) {
Thomas Vachuskafe1f01a2015-12-10 23:56:02 -0800112 if (layEgg(key)) return;
113
Simon Hunt36fc15c2015-02-12 17:02:58 -0800114 // global callback?
115 if (gcb && gcb(token, key, keyCode, event)) {
116 // if the event was 'handled', we are done
117 return;
118 }
119 // otherwise, let the view callback have a shot
120 if (vcb) {
121 vcb(token, key, keyCode, event);
122 }
Simon Hunt09060142015-03-18 20:23:32 -0700123 if (kl) {
124 kl(key);
125 }
Simon Hunt1eecfa22014-12-16 14:46:29 -0800126 }
127 }
128
129 function setupGlobalKeys() {
Simon Hunt404f6b22015-01-21 14:00:56 -0800130 angular.extend(keyHandler, {
Simon Hunt1eecfa22014-12-16 14:46:29 -0800131 globalKeys: {
132 backSlash: [quickHelp, 'Show / hide Quick Help'],
133 slash: [quickHelp, 'Show / hide Quick Help'],
134 esc: [escapeKey, 'Dismiss dialog or cancel selections'],
135 T: [toggleTheme, "Toggle theme"]
136 },
137 globalFormat: ['backSlash', 'slash', 'esc', 'T'],
138
139 // Masked keys are global key handlers that always return true.
140 // That is, the view will never see the event for that key.
141 maskedKeys: {
142 slash: true,
143 backSlash: true,
144 T: true
145 }
146 });
147 }
148
149 function quickHelp(view, key, code, ev) {
Simon Huntd5579252015-10-06 15:09:14 -0700150 if (!globalEnabled) {
151 return false;
152 }
Simon Hunt639dc662015-02-18 14:19:20 -0800153 qhs.showQuickHelp(keyHandler);
Simon Hunt1eecfa22014-12-16 14:46:29 -0800154 return true;
155 }
156
Simon Hunta0eb0a82015-02-11 12:30:06 -0800157 // returns true if we 'consumed' the ESC keypress, false otherwise
Simon Hunt1eecfa22014-12-16 14:46:29 -0800158 function escapeKey(view, key, code, ev) {
Simon Hunt9d286562015-03-09 13:53:50 -0700159 return ns.hideIfShown() || qhs.hideQuickHelp();
Simon Hunt1eecfa22014-12-16 14:46:29 -0800160 }
161
162 function toggleTheme(view, key, code, ev) {
Simon Huntd5579252015-10-06 15:09:14 -0700163 if (!globalEnabled) {
164 return false;
165 }
Yuta HIGUCHI4f39bcd2014-12-18 20:46:14 -0800166 ts.toggleTheme();
Simon Hunt1eecfa22014-12-16 14:46:29 -0800167 return true;
168 }
169
Simon Hunt59df0b22014-12-17 10:32:25 -0800170 function setKeyBindings(keyArg) {
171 var viewKeys,
172 masked = [];
173
Yuta HIGUCHI4f39bcd2014-12-18 20:46:14 -0800174 if (fs.isF(keyArg)) {
Simon Hunt59df0b22014-12-17 10:32:25 -0800175 // set general key handler callback
176 keyHandler.viewFn = keyArg;
177 } else {
178 // set specific key filter map
179 viewKeys = d3.map(keyArg).keys();
180 viewKeys.forEach(function (key) {
181 if (keyHandler.maskedKeys[key]) {
Simon Huntaf322072014-12-18 13:23:40 -0800182 masked.push('setKeyBindings(): Key "' + key + '" is reserved');
Simon Hunt59df0b22014-12-17 10:32:25 -0800183 }
184 });
185
186 if (masked.length) {
Simon Huntaf322072014-12-18 13:23:40 -0800187 $log.warn(masked.join('\n'));
Simon Hunt59df0b22014-12-17 10:32:25 -0800188 }
189 keyHandler.viewKeys = keyArg;
190 }
191 }
192
193 function getKeyBindings() {
194 var gkeys = d3.map(keyHandler.globalKeys).keys(),
195 masked = d3.map(keyHandler.maskedKeys).keys(),
196 vkeys = d3.map(keyHandler.viewKeys).keys(),
Yuta HIGUCHI4f39bcd2014-12-18 20:46:14 -0800197 vfn = !!fs.isF(keyHandler.viewFn);
Simon Hunt59df0b22014-12-17 10:32:25 -0800198
199 return {
200 globalKeys: gkeys,
201 maskedKeys: masked,
202 viewKeys: vkeys,
203 viewFunction: vfn
204 };
205 }
206
Bri Prebilic Cole9dcaea52015-07-21 14:39:48 -0700207 function unbindKeys() {
208 keyHandler.viewKeys = {};
209 keyHandler.viewFn = null;
210 keyHandler.viewGestures = [];
211 }
212
Simon Hunt71892222015-09-29 13:39:40 -0700213 function checkNotGlobal(o) {
214 var oops = [];
215 if (fs.isO(o)) {
216 angular.forEach(o, function (val, key) {
217 if (keyHandler.globalKeys[key]) {
218 oops.push(key);
219 }
220 });
221 if (oops.length) {
222 $log.warn('Ignoring reserved global key(s):', oops.join(','));
223 oops.forEach(function (key) {
224 delete o[key];
225 });
226 }
227 }
228 }
229
Simon Huntdc6362a2014-12-18 19:55:23 -0800230 angular.module('onosUtil')
Simon Hunt639dc662015-02-18 14:19:20 -0800231 .factory('KeyService',
Simon Hunt9f5e97d2015-12-11 10:32:09 -0800232 ['$log', '$timeout', 'FnService', 'ThemeService', 'NavService',
Simon Hunt639dc662015-02-18 14:19:20 -0800233
Simon Hunt9f5e97d2015-12-11 10:32:09 -0800234 function (_$log_, _$timeout_, _fs_, _ts_, _ns_) {
Yuta HIGUCHI4f39bcd2014-12-18 20:46:14 -0800235 $log = _$log_;
Simon Hunt9f5e97d2015-12-11 10:32:09 -0800236 $timeout = _$timeout_;
Yuta HIGUCHI4f39bcd2014-12-18 20:46:14 -0800237 fs = _fs_;
238 ts = _ts_;
Simon Hunt9d286562015-03-09 13:53:50 -0700239 ns = _ns_;
Yuta HIGUCHI4f39bcd2014-12-18 20:46:14 -0800240
Simon Huntdc6362a2014-12-18 19:55:23 -0800241 return {
Simon Hunt639dc662015-02-18 14:19:20 -0800242 bindQhs: function (_qhs_) {
243 qhs = _qhs_;
244 },
Simon Huntdc6362a2014-12-18 19:55:23 -0800245 installOn: function (elem) {
246 elem.on('keydown', keyIn);
247 setupGlobalKeys();
248 },
Simon Huntdc6362a2014-12-18 19:55:23 -0800249 keyBindings: function (x) {
250 if (x === undefined) {
251 return getKeyBindings();
252 } else {
253 setKeyBindings(x);
254 }
255 },
Bri Prebilic Cole9dcaea52015-07-21 14:39:48 -0700256 unbindKeys: unbindKeys,
Simon Huntdc6362a2014-12-18 19:55:23 -0800257 gestureNotes: function (g) {
258 if (g === undefined) {
259 return keyHandler.viewGestures;
260 } else {
Yuta HIGUCHI4f39bcd2014-12-18 20:46:14 -0800261 keyHandler.viewGestures = fs.isA(g) || [];
Simon Huntdc6362a2014-12-18 19:55:23 -0800262 }
Simon Hunt36fc15c2015-02-12 17:02:58 -0800263 },
264 enableKeys: function (b) {
265 enabled = b;
Simon Hunt71892222015-09-29 13:39:40 -0700266 },
Simon Huntd5579252015-10-06 15:09:14 -0700267 enableGlobalKeys: function (b) {
268 globalEnabled = b;
269 },
Simon Hunt71892222015-09-29 13:39:40 -0700270 checkNotGlobal: checkNotGlobal
Simon Huntdc6362a2014-12-18 19:55:23 -0800271 };
Simon Hunt1eecfa22014-12-16 14:46:29 -0800272 }]);
273
Simon Huntdc6362a2014-12-18 19:55:23 -0800274}());