GUI -- Added KeyService and FnService; implemented 'T' key for toggle theme.

Change-Id: I6ae3cb76aaa5c72422eac180cb46d604ead21afc
diff --git a/web/gui/src/main/webapp/app/fw/lib/fn.js b/web/gui/src/main/webapp/app/fw/lib/fn.js
new file mode 100644
index 0000000..15a8843
--- /dev/null
+++ b/web/gui/src/main/webapp/app/fw/lib/fn.js
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ ONOS GUI -- General Purpose Functions
+
+ @author Simon Hunt
+ */
+(function (onos) {
+    'use strict';
+
+    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;
+            }
+        };
+    }]);
+
+}(ONOS));
diff --git a/web/gui/src/main/webapp/app/fw/lib/keys.js b/web/gui/src/main/webapp/app/fw/lib/keys.js
new file mode 100644
index 0000000..3c0a26a
--- /dev/null
+++ b/web/gui/src/main/webapp/app/fw/lib/keys.js
@@ -0,0 +1,158 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ ONOS GUI -- Key Handler Service
+
+ @author Simon Hunt
+ */
+(function (onos) {
+    'use strict';
+
+    // references to injected services
+    var f;
+
+    // internal state
+    var keyHandler = {
+            globalKeys: {},
+            maskedKeys: {},
+            viewKeys: {},
+            viewFn: null,
+            viewGestures: []
+        },
+        theme = 'light';
+
+    // TODO: we need to have the concept of view token here..
+    function getViewToken() {
+        return 'NotYetAViewToken';
+    }
+
+    function whatKey(code) {
+        switch (code) {
+            case 13: return 'enter';
+            case 16: return 'shift';
+            case 17: return 'ctrl';
+            case 18: return 'alt';
+            case 27: return 'esc';
+            case 32: return 'space';
+            case 37: return 'leftArrow';
+            case 38: return 'upArrow';
+            case 39: return 'rightArrow';
+            case 40: return 'downArrow';
+            case 91: return 'cmdLeft';
+            case 93: return 'cmdRight';
+            case 187: return 'equals';
+            case 189: return 'dash';
+            case 191: return 'slash';
+            case 192: return 'backQuote';
+            case 220: return 'backSlash';
+            default:
+                if ((code >= 48 && code <= 57) ||
+                    (code >= 65 && code <= 90)) {
+                    return String.fromCharCode(code);
+                } else if (code >= 112 && code <= 123) {
+                    return 'F' + (code - 111);
+                }
+                return '.';
+        }
+    }
+
+    function keyIn() {
+        var event = d3.event,
+            keyCode = event.keyCode,
+            key = whatKey(keyCode),
+            kh = keyHandler,
+            gk = kh.globalKeys[key],
+            gcb = f.isF(gk) || (f.isA(gk) && f.isF(gk[0])),
+            vk = kh.viewKeys[key],
+            vcb = f.isF(vk) || (f.isA(vk) && f.isF(vk[0])) || f.isF(kh.viewFn),
+            token = getViewToken();
+
+        // global callback?
+        if (gcb && gcb(token, key, keyCode, event)) {
+            // if the event was 'handled', we are done
+            return;
+        }
+        // otherwise, let the view callback have a shot
+        if (vcb) {
+            vcb(token, key, keyCode, event);
+        }
+    }
+
+    function setupGlobalKeys() {
+        $.extend(keyHandler, {
+            globalKeys: {
+                backSlash: [quickHelp, 'Show / hide Quick Help'],
+                slash: [quickHelp, 'Show / hide Quick Help'],
+                esc: [escapeKey, 'Dismiss dialog or cancel selections'],
+                T: [toggleTheme, "Toggle theme"]
+            },
+            globalFormat: ['backSlash', 'slash', 'esc', 'T'],
+
+            // Masked keys are global key handlers that always return true.
+            // That is, the view will never see the event for that key.
+            maskedKeys: {
+                slash: true,
+                backSlash: true,
+                T: true
+            }
+        });
+    }
+
+    function quickHelp(view, key, code, ev) {
+        // TODO: show quick help
+        //libApi.quickHelp.show(keyHandler);
+        console.log('QUICK-HELP');
+        return true;
+    }
+
+    function escapeKey(view, key, code, ev) {
+        // TODO: plumb in handling of alerts and quick help dismissal
+/*
+        if (alerts.open) {
+            closeAlerts();
+            return true;
+        }
+        if (libApi.quickHelp.hide()) {
+            return true;
+        }
+*/
+        console.log('ESCAPE');
+        return false;
+    }
+
+    function toggleTheme(view, key, code, ev) {
+        var body = d3.select('body');
+        theme = (theme === 'light') ? 'dark' : 'light';
+        body.classed('light dark', false);
+        body.classed(theme, true);
+        // TODO: emit theme-change event to current view...
+        //theme(view);
+        return true;
+    }
+
+    onos.factory('KeyService', ['FnService', function (fs) {
+        f = fs;
+        return {
+            init: function () {
+                console.log('initializing keydown handler....');
+                d3.select('body').on('keydown', keyIn);
+                setupGlobalKeys();
+            }
+        };
+    }]);
+
+}(ONOS));
diff --git a/web/gui/src/main/webapp/app/index.html b/web/gui/src/main/webapp/app/index.html
index d820b96..6084854 100644
--- a/web/gui/src/main/webapp/app/index.html
+++ b/web/gui/src/main/webapp/app/index.html
@@ -27,13 +27,13 @@
 
     <script src="../tp/d3.js"></script>
     <script src="../tp/topojson.v1.min.js"></script>
-
-    <!-- NOTE: We are going to see if we can dispense with jQuery... -->
-    <!--<script src="../tp/jquery-2.1.1.min.js"></script>-->
+    <script src="../tp/jquery-2.1.1.min.js"></script>
 
     <!-- ONOS UI Framework included here -->
     <!-- TODO: use a single catenated-minified file here -->
     <script src="onos.js"></script>
+    <script src="fw/lib/fn.js"></script>
+    <script src="fw/lib/keys.js"></script>
     <script src="fw/mast/mast.js"></script>
 
     <!-- Framework and library stylesheets included here -->
@@ -50,16 +50,18 @@
     <!-- TODO: inject style-sheet refs server-side -->
 </head>
 <body class="light" ng-app="onosApp">
-    <div id="mast"
-         ng-controller="MastCtrl as mastCtrl"
-         ng-include="'fw/mast/mast.html'"></div>
+    <div id="frame" ng-controller="OnosCtrl as onosCtrl">
+        <div id="mast"
+             ng-controller="MastCtrl as mastCtrl"
+             ng-include="'fw/mast/mast.html'"></div>
 
-    <div id="view" ng-view></div>
+        <div id="view" ng-view></div>
 
-    <div id="floatpanels"></div>
-    <div id="alerts"></div>
-    <div id="flash"></div>
-    <div id="quickhelp"></div>
-    <div id="deathmask"></div>
+        <div id="floatpanels"></div>
+        <div id="alerts"></div>
+        <div id="flash"></div>
+        <div id="quickhelp"></div>
+        <div id="deathmask"></div>
+    </div>
 </body>
 </html>
diff --git a/web/gui/src/main/webapp/app/onos.js b/web/gui/src/main/webapp/app/onos.js
index ea29256..631464b 100644
--- a/web/gui/src/main/webapp/app/onos.js
+++ b/web/gui/src/main/webapp/app/onos.js
@@ -19,13 +19,17 @@
 
  @author Simon Hunt
  */
+
+// our one global variable
+var ONOS;
+
 (function () {
     'use strict';
 
-    angular.module('onosApp', ['onosMast'])
-        .controller('OnosCtrl', [function () {
-            // controller logic here
+    ONOS = angular.module('onosApp', ['onosMast'])
+        .controller('OnosCtrl', ['KeyService', function (ks) {
             console.log('OnosCtrl has been created');
+            ks.init();
         }]);
 
 }());