blob: d56402a080af5117433cf965840322265090cb13 [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,
Simon Huntd5579252015-10-06 15:09:14 -070028 globalEnabled = true,
Simon Hunt36fc15c2015-02-12 17:02:58 -080029 keyHandler = {
Simon Hunt1eecfa22014-12-16 14:46:29 -080030 globalKeys: {},
31 maskedKeys: {},
32 viewKeys: {},
33 viewFn: null,
34 viewGestures: []
Yuta HIGUCHI4f39bcd2014-12-18 20:46:14 -080035 };
Simon Hunt1eecfa22014-12-16 14:46:29 -080036
Thomas Vachuskafe1f01a2015-12-10 23:56:02 -080037 var eegg = '', eeggMax = 'shiftONOS', eeggMin = 'shiftO';
38
39 function layEgg(lastKey) {
40 eegg += lastKey;
41 if (eeggMax.indexOf(eegg) == 0) {
42 if (eeggMax == eegg) {
43 d3.select('body').append('div').attr('id', 'eegg').append('img').attr('src', 'data/img/eegg.png');
44 setTimeout(function () { d3.select('#eegg').remove(); }, 3000);
45 eegg = '';
46 }
47 return true;
48 } else if (eeggMin != eegg) {
49 eegg = '';
50 }
51 return false;
52 }
53
Simon Hunt1eecfa22014-12-16 14:46:29 -080054 function whatKey(code) {
55 switch (code) {
56 case 13: return 'enter';
57 case 16: return 'shift';
58 case 17: return 'ctrl';
59 case 18: return 'alt';
60 case 27: return 'esc';
61 case 32: return 'space';
62 case 37: return 'leftArrow';
63 case 38: return 'upArrow';
64 case 39: return 'rightArrow';
65 case 40: return 'downArrow';
66 case 91: return 'cmdLeft';
67 case 93: return 'cmdRight';
68 case 187: return 'equals';
Simon Hunt90dcc3e2015-03-25 15:01:27 -070069 case 188: return 'comma';
Simon Hunt1eecfa22014-12-16 14:46:29 -080070 case 189: return 'dash';
Simon Hunt90dcc3e2015-03-25 15:01:27 -070071 case 190: return 'dot';
Simon Hunt1eecfa22014-12-16 14:46:29 -080072 case 191: return 'slash';
73 case 192: return 'backQuote';
74 case 220: return 'backSlash';
75 default:
76 if ((code >= 48 && code <= 57) ||
77 (code >= 65 && code <= 90)) {
78 return String.fromCharCode(code);
79 } else if (code >= 112 && code <= 123) {
80 return 'F' + (code - 111);
81 }
82 return '.';
83 }
84 }
85
86 function keyIn() {
87 var event = d3.event,
88 keyCode = event.keyCode,
89 key = whatKey(keyCode),
90 kh = keyHandler,
91 gk = kh.globalKeys[key],
Yuta HIGUCHI4f39bcd2014-12-18 20:46:14 -080092 gcb = fs.isF(gk) || (fs.isA(gk) && fs.isF(gk[0])),
Simon Hunt1eecfa22014-12-16 14:46:29 -080093 vk = kh.viewKeys[key],
Simon Hunt09060142015-03-18 20:23:32 -070094 kl = fs.isF(kh.viewKeys._keyListener),
Yuta HIGUCHI4f39bcd2014-12-18 20:46:14 -080095 vcb = fs.isF(vk) || (fs.isA(vk) && fs.isF(vk[0])) || fs.isF(kh.viewFn),
Simon Hunt1bf1e8c2015-04-08 10:12:43 -070096 token = 'keyev'; // indicate this was a key-pressed event
Simon Hunt1eecfa22014-12-16 14:46:29 -080097
Simon Huntdc6362a2014-12-18 19:55:23 -080098 d3.event.stopPropagation();
99
Simon Hunt36fc15c2015-02-12 17:02:58 -0800100 if (enabled) {
Thomas Vachuskafe1f01a2015-12-10 23:56:02 -0800101 if (layEgg(key)) return;
102
Simon Hunt36fc15c2015-02-12 17:02:58 -0800103 // global callback?
104 if (gcb && gcb(token, key, keyCode, event)) {
105 // if the event was 'handled', we are done
106 return;
107 }
108 // otherwise, let the view callback have a shot
109 if (vcb) {
110 vcb(token, key, keyCode, event);
111 }
Simon Hunt09060142015-03-18 20:23:32 -0700112 if (kl) {
113 kl(key);
114 }
Simon Hunt1eecfa22014-12-16 14:46:29 -0800115 }
116 }
117
118 function setupGlobalKeys() {
Simon Hunt404f6b22015-01-21 14:00:56 -0800119 angular.extend(keyHandler, {
Simon Hunt1eecfa22014-12-16 14:46:29 -0800120 globalKeys: {
121 backSlash: [quickHelp, 'Show / hide Quick Help'],
122 slash: [quickHelp, 'Show / hide Quick Help'],
123 esc: [escapeKey, 'Dismiss dialog or cancel selections'],
124 T: [toggleTheme, "Toggle theme"]
125 },
126 globalFormat: ['backSlash', 'slash', 'esc', 'T'],
127
128 // Masked keys are global key handlers that always return true.
129 // That is, the view will never see the event for that key.
130 maskedKeys: {
131 slash: true,
132 backSlash: true,
133 T: true
134 }
135 });
136 }
137
138 function quickHelp(view, key, code, ev) {
Simon Huntd5579252015-10-06 15:09:14 -0700139 if (!globalEnabled) {
140 return false;
141 }
Simon Hunt639dc662015-02-18 14:19:20 -0800142 qhs.showQuickHelp(keyHandler);
Simon Hunt1eecfa22014-12-16 14:46:29 -0800143 return true;
144 }
145
Simon Hunta0eb0a82015-02-11 12:30:06 -0800146 // returns true if we 'consumed' the ESC keypress, false otherwise
Simon Hunt1eecfa22014-12-16 14:46:29 -0800147 function escapeKey(view, key, code, ev) {
Simon Hunt9d286562015-03-09 13:53:50 -0700148 return ns.hideIfShown() || qhs.hideQuickHelp();
Simon Hunt1eecfa22014-12-16 14:46:29 -0800149 }
150
151 function toggleTheme(view, key, code, ev) {
Simon Huntd5579252015-10-06 15:09:14 -0700152 if (!globalEnabled) {
153 return false;
154 }
Yuta HIGUCHI4f39bcd2014-12-18 20:46:14 -0800155 ts.toggleTheme();
Simon Hunt1eecfa22014-12-16 14:46:29 -0800156 return true;
157 }
158
Simon Hunt59df0b22014-12-17 10:32:25 -0800159 function setKeyBindings(keyArg) {
160 var viewKeys,
161 masked = [];
162
Yuta HIGUCHI4f39bcd2014-12-18 20:46:14 -0800163 if (fs.isF(keyArg)) {
Simon Hunt59df0b22014-12-17 10:32:25 -0800164 // set general key handler callback
165 keyHandler.viewFn = keyArg;
166 } else {
167 // set specific key filter map
168 viewKeys = d3.map(keyArg).keys();
169 viewKeys.forEach(function (key) {
170 if (keyHandler.maskedKeys[key]) {
Simon Huntaf322072014-12-18 13:23:40 -0800171 masked.push('setKeyBindings(): Key "' + key + '" is reserved');
Simon Hunt59df0b22014-12-17 10:32:25 -0800172 }
173 });
174
175 if (masked.length) {
Simon Huntaf322072014-12-18 13:23:40 -0800176 $log.warn(masked.join('\n'));
Simon Hunt59df0b22014-12-17 10:32:25 -0800177 }
178 keyHandler.viewKeys = keyArg;
179 }
180 }
181
182 function getKeyBindings() {
183 var gkeys = d3.map(keyHandler.globalKeys).keys(),
184 masked = d3.map(keyHandler.maskedKeys).keys(),
185 vkeys = d3.map(keyHandler.viewKeys).keys(),
Yuta HIGUCHI4f39bcd2014-12-18 20:46:14 -0800186 vfn = !!fs.isF(keyHandler.viewFn);
Simon Hunt59df0b22014-12-17 10:32:25 -0800187
188 return {
189 globalKeys: gkeys,
190 maskedKeys: masked,
191 viewKeys: vkeys,
192 viewFunction: vfn
193 };
194 }
195
Bri Prebilic Cole9dcaea52015-07-21 14:39:48 -0700196 function unbindKeys() {
197 keyHandler.viewKeys = {};
198 keyHandler.viewFn = null;
199 keyHandler.viewGestures = [];
200 }
201
Simon Hunt71892222015-09-29 13:39:40 -0700202 function checkNotGlobal(o) {
203 var oops = [];
204 if (fs.isO(o)) {
205 angular.forEach(o, function (val, key) {
206 if (keyHandler.globalKeys[key]) {
207 oops.push(key);
208 }
209 });
210 if (oops.length) {
211 $log.warn('Ignoring reserved global key(s):', oops.join(','));
212 oops.forEach(function (key) {
213 delete o[key];
214 });
215 }
216 }
217 }
218
Simon Huntdc6362a2014-12-18 19:55:23 -0800219 angular.module('onosUtil')
Simon Hunt639dc662015-02-18 14:19:20 -0800220 .factory('KeyService',
Simon Hunt9d286562015-03-09 13:53:50 -0700221 ['$log', 'FnService', 'ThemeService', 'NavService',
Simon Hunt639dc662015-02-18 14:19:20 -0800222
Simon Hunt9d286562015-03-09 13:53:50 -0700223 function (_$log_, _fs_, _ts_, _ns_) {
Yuta HIGUCHI4f39bcd2014-12-18 20:46:14 -0800224 $log = _$log_;
225 fs = _fs_;
226 ts = _ts_;
Simon Hunt9d286562015-03-09 13:53:50 -0700227 ns = _ns_;
Yuta HIGUCHI4f39bcd2014-12-18 20:46:14 -0800228
Simon Huntdc6362a2014-12-18 19:55:23 -0800229 return {
Simon Hunt639dc662015-02-18 14:19:20 -0800230 bindQhs: function (_qhs_) {
231 qhs = _qhs_;
232 },
Simon Huntdc6362a2014-12-18 19:55:23 -0800233 installOn: function (elem) {
234 elem.on('keydown', keyIn);
235 setupGlobalKeys();
236 },
Simon Huntdc6362a2014-12-18 19:55:23 -0800237 keyBindings: function (x) {
238 if (x === undefined) {
239 return getKeyBindings();
240 } else {
241 setKeyBindings(x);
242 }
243 },
Bri Prebilic Cole9dcaea52015-07-21 14:39:48 -0700244 unbindKeys: unbindKeys,
Simon Huntdc6362a2014-12-18 19:55:23 -0800245 gestureNotes: function (g) {
246 if (g === undefined) {
247 return keyHandler.viewGestures;
248 } else {
Yuta HIGUCHI4f39bcd2014-12-18 20:46:14 -0800249 keyHandler.viewGestures = fs.isA(g) || [];
Simon Huntdc6362a2014-12-18 19:55:23 -0800250 }
Simon Hunt36fc15c2015-02-12 17:02:58 -0800251 },
252 enableKeys: function (b) {
253 enabled = b;
Simon Hunt71892222015-09-29 13:39:40 -0700254 },
Simon Huntd5579252015-10-06 15:09:14 -0700255 enableGlobalKeys: function (b) {
256 globalEnabled = b;
257 },
Simon Hunt71892222015-09-29 13:39:40 -0700258 checkNotGlobal: checkNotGlobal
Simon Huntdc6362a2014-12-18 19:55:23 -0800259 };
Simon Hunt1eecfa22014-12-16 14:46:29 -0800260 }]);
261
Simon Huntdc6362a2014-12-18 19:55:23 -0800262}());