WEB GUI: Live-reloading development environment

Change-Id: Ibe4a80905e7c140ff70f359d43a49e6430cc6838
diff --git a/.gitignore b/.gitignore
index 6df1d0e..5160dc9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -23,3 +23,5 @@
 /bin/
 
 web/gui/src/main/webapp/tests/node_modules
+web/gui/src/main/webapp/node_modules/
+npm-debug.log
diff --git a/web/gui/src/main/webapp/README.md b/web/gui/src/main/webapp/README.md
new file mode 100644
index 0000000..cbf3160
--- /dev/null
+++ b/web/gui/src/main/webapp/README.md
@@ -0,0 +1,29 @@
+# ONOS UI
+
+## Development environment
+
+To help with UI development we provide a dedicated environment that introduce an auto reload feature and allow you to change your javascript files without recompiling the application.
+
+To get started:
+- Be sure to have `Node Js` installed
+- Enter `web/gui/src/main/webapp/` folder
+- Run `npm install` to install required dependency
+- Run `npm start` to open start the development environment
+
+In the console you should see something like:
+
+```
+Dev server is up and listening on http://localhost: 8182
+[BS] Proxying: http://localhost:8181
+[BS] Access URLs:
+ ----------------------------------
+       Local: http://localhost:3000
+    External: http://10.1.8.46:3000
+ ----------------------------------
+          UI: http://localhost:3002
+ UI External: http://10.1.8.46:3002
+ ----------------------------------
+[BS] Watching files...
+```
+
+To open ONOS visit the local URL (eg: `http://localhost:3000`) plus `/onos/ui` (eg: `http://localhost:3000/onos/ui`)
\ No newline at end of file
diff --git a/web/gui/src/main/webapp/app/fw/util/prefs.js b/web/gui/src/main/webapp/app/fw/util/prefs.js
index d033db5..d85609b 100644
--- a/web/gui/src/main/webapp/app/fw/util/prefs.js
+++ b/web/gui/src/main/webapp/app/fw/util/prefs.js
@@ -85,7 +85,13 @@
             fs = _fs_;
             wss = _wss_;
 
-            cache = userPrefs;
+            try {
+                cache = angular.isDefined(userPrefs) ? userPrefs : {};
+            }
+            catch(e){
+                // browser throws error for non-existing globals
+                cache = {}
+            }
 
             wss.bindHandlers({
                 updatePrefs: updatePrefs
diff --git a/web/gui/src/main/webapp/app/view/topo/topoToolbar.js b/web/gui/src/main/webapp/app/view/topo/topoToolbar.js
index 7bc944f..85062ef 100644
--- a/web/gui/src/main/webapp/app/view/topo/topoToolbar.js
+++ b/web/gui/src/main/webapp/app/view/topo/topoToolbar.js
@@ -96,7 +96,6 @@
 
     function init(_api_) {
         api = _api_;
-
         // retrieve initial toggle button settings from user prefs
         setInitToggleState();
     }
diff --git a/web/gui/src/main/webapp/bs-config.js b/web/gui/src/main/webapp/bs-config.js
new file mode 100644
index 0000000..00aaa18
--- /dev/null
+++ b/web/gui/src/main/webapp/bs-config.js
@@ -0,0 +1,77 @@
+'use strict';
+/*
+ |--------------------------------------------------------------------------
+ | Browser-sync config file
+ |--------------------------------------------------------------------------
+ |
+ | For up-to-date information about the options:
+ |   http://www.browsersync.io/docs/options/
+ |
+ | There are more options than you see here, these are just the ones that are
+ | set internally. See the website for more info.
+ |
+ |
+ */
+var fs = require('fs');
+var httpProxy = require('http-proxy');
+
+var proxy = httpProxy.createProxyServer({
+  target: 'http://localhost:8182',
+});
+
+var defaultViews = fs.readdirSync('./app/view/');
+var viewNameMatcher = new RegExp(/\/onos\/ui\/app\/view\/(.+)\/.+\.js/);
+
+proxy.on('upgrade', function (req, socket, head) {
+  console.log('[WS]: ', head);
+  proxy.ws(req, socket, head);
+});
+
+proxy.on('error', function(error, req, res) {
+  res.writeHead(500, {
+    'Content-Type': 'text/plain'
+  });
+  console.error('[Proxy]', error);
+});
+
+module.exports = {
+  'files': [
+    './app/**/*.css',
+    './app/**/*.js',
+    './app/**/*.json',
+    './app/**/*.html',
+    './app/**/*.jpg',
+    './app/**/*.png',
+    './app/**/*.gif',
+    '../../../../../apps/**/*.js',
+    '../../../../../apps/**/*.html',
+    '../../../../../apps/**/*.css'
+  ],
+  proxy: {
+    target: "http://localhost:8181",
+    ws: true,
+    middleware: function(req, res, next){
+
+
+      var viewName = viewNameMatcher.exec(req.url);
+
+      if(!!viewName && defaultViews.indexOf(viewName[1]) === -1){
+        // in this case it is an external application that extend the view
+        // so we redirect the request to the app folder
+        req.url = req.url.replace('/onos/ui/', '/apps/' + viewName[1] + '/app/src/main/resources/');
+        proxy.web(req, res);
+      }
+      // NOTE onos.js should not be proxied (require server side injection)
+      else if(req.url.match(/.js$/) && req.url !== '/onos/ui/onos.js'){
+        // redirect onos base js files to the source folder
+        req.url = req.url.replace('/onos/ui/', '/web/gui/src/main/webapp/');
+        proxy.web(req, res); 
+      }
+      else{
+        return next();
+      }
+    }
+  },
+  'port': 3000,
+  'open': false
+};
\ No newline at end of file
diff --git a/web/gui/src/main/webapp/dev_server.js b/web/gui/src/main/webapp/dev_server.js
new file mode 100644
index 0000000..513d37a
--- /dev/null
+++ b/web/gui/src/main/webapp/dev_server.js
@@ -0,0 +1,29 @@
+'use strict';
+
+var http = require('http');
+// var httpProxy = require('http-proxy');
+var connect = require('connect');
+var serveStatic = require('serve-static');
+var path = require('path');
+
+var conf = {
+  paths: {
+    root: '../../../../../'
+  },
+  port: '8182'
+}
+
+var httpProxyInit = function (baseDir) {
+
+  var app = connect();
+
+  app.use(serveStatic(path.join(__dirname, baseDir)));
+
+  var server = http.createServer(app);
+
+  server.listen(conf.port, function(){
+    console.log('Dev server is up and listening on http://localhost:', conf.port);
+  });
+};
+
+httpProxyInit(conf.paths.root);
\ No newline at end of file
diff --git a/web/gui/src/main/webapp/onos.js b/web/gui/src/main/webapp/onos.js
index 263c67d..042a9ac 100644
--- a/web/gui/src/main/webapp/onos.js
+++ b/web/gui/src/main/webapp/onos.js
@@ -17,7 +17,6 @@
 /*
  ONOS GUI -- Main Application Module
  */
-
 (function () {
     'use strict';
 
diff --git a/web/gui/src/main/webapp/package.json b/web/gui/src/main/webapp/package.json
new file mode 100644
index 0000000..c4f2cb7
--- /dev/null
+++ b/web/gui/src/main/webapp/package.json
@@ -0,0 +1,23 @@
+{
+  "name": "onos-gui",
+  "version": "1.0.0",
+  "description": "The GUI interface for ONOS",
+  "main": "index.html",
+  "directories": {
+    "test": "tests"
+  },
+  "scripts": {
+    "bs": "browser-sync start --config bs-config.js",
+    "dev-server": "node dev_server.js",
+    "start": "parallelshell \"npm run dev-server\" \"npm run bs\""
+  },
+  "author": "ON.Lab",
+  "license": "Apache 2.0",
+  "devDependencies": {
+    "browser-sync": "^2.12.8",
+    "connect": "^3.4.1",
+    "http-proxy": "^1.13.2",
+    "parallelshell": "^2.0.0",
+    "serve-static": "^1.10.2"
+  }
+}