Restructuring GUI code - first pass - WIP.
diff --git a/web/gui/src/main/webapp/onos2.js b/web/gui/src/main/webapp/onos2.js
new file mode 100644
index 0000000..bd001a5
--- /dev/null
+++ b/web/gui/src/main/webapp/onos2.js
@@ -0,0 +1,303 @@
+/*
+ * 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 -- Base Framework
+
+ @author Simon Hunt
+ */
+
+(function ($) {
+ 'use strict';
+ var tsI = new Date().getTime(), // initialize time stamp
+ tsB, // build time stamp
+ defaultHash = 'temp1';
+
+
+ // attach our main function to the jQuery object
+ $.onos = function (options) {
+ var publicApi; // public api
+
+ // internal state
+ var views = {},
+ current = {
+ view: null,
+ ctx: ''
+ },
+ built = false,
+ errorCount = 0;
+
+ // DOM elements etc.
+ var $view;
+
+
+ // ..........................................................
+ // Internal functions
+
+ // throw an error
+ function throwError(msg) {
+ // separate function, as we might add tracing here too, later
+ throw new Error(msg);
+ }
+
+ function doError(msg) {
+ errorCount++;
+ console.warn(msg);
+ }
+
+ // hash navigation
+ function hash() {
+ var hash = window.location.hash,
+ redo = false,
+ view,
+ t;
+
+ if (!hash) {
+ hash = defaultHash;
+ redo = true;
+ }
+
+ t = parseHash(hash);
+ if (!t || !t.vid) {
+ doError('Unable to parse target hash: ' + hash);
+ }
+
+ view = views[t.vid];
+ if (!view) {
+ doError('No view defined with id: ' + t.vid);
+ }
+
+ if (redo) {
+ window.location.hash = makeHash(t);
+ // the above will result in a hashchange event, invoking
+ // this function again
+ } else {
+ // hash was not modified... navigate to where we need to be
+ navigate(hash, view, t);
+ }
+
+ }
+
+ function parseHash(s) {
+ // extract navigation coordinates from the supplied string
+ // "vid,ctx" --> { vid:vid, ctx:ctx }
+
+ var m = /^[#]{0,1}(\S+),(\S*)$/.exec(s);
+ if (m) {
+ return { vid: m[1], ctx: m[2] };
+ }
+
+ m = /^[#]{0,1}(\S+)$/.exec(s);
+ return m ? { vid: m[1] } : null;
+ }
+
+ function makeHash(t, ctx) {
+ // make a hash string from the given navigation coordinates.
+ // if t is not an object, then it is a vid
+ var h = t,
+ c = ctx || '';
+
+ if ($.isPlainObject(t)) {
+ h = t.vid;
+ c = t.ctx || '';
+ }
+
+ if (c) {
+ h += ',' + c;
+ }
+ return h;
+ }
+
+ function navigate(hash, view, t) {
+ // closePanes() // flyouts etc.
+ // updateNav() // accordion / selected nav item
+ createView(view);
+ setView(view, hash, t);
+ }
+
+ function reportBuildErrors() {
+ // TODO: validate registered views / nav-item linkage etc.
+ console.log('(no build errors)');
+ }
+
+ // ..........................................................
+ // View life-cycle functions
+
+ function createView(view) {
+ var $d;
+ // lazy initialization of the view
+ if (view && !view.$div) {
+ $d = $view.append('div')
+ .attr({
+ id: view.vid
+ });
+ view.$div = $d; // cache a reference to the selected div
+ }
+ }
+
+ function setView(view, hash, t) {
+ // set the specified view as current, while invoking the
+ // appropriate life-cycle callbacks
+
+ // if there is a current view, and it is not the same as
+ // the incoming view, then unload it...
+ if (current.view && !(current.view.vid !== view.vid)) {
+ current.view.unload();
+ }
+
+ // cache new view and context
+ current.view = view;
+ current.ctx = t.ctx || '';
+
+ // TODO: clear radio button set (store on view?)
+
+ // preload is called only once, after the view is in the DOM
+ if (!view.preloaded) {
+ view.preload(t.ctx);
+ }
+
+ // clear the view of stale data
+ view.reset();
+
+ // load the view
+ view.load(t.ctx);
+ }
+
+ function resizeView() {
+ if (current.view) {
+ current.view.resize();
+ }
+ }
+
+ // ..........................................................
+ // View class
+ // Captures state information about a view.
+
+ // Constructor
+ // vid : view id
+ // nid : id of associated nav-item (optional)
+ // cb : callbacks (preload, reset, load, resize, unload, error)
+ // data: custom data object (optional)
+ function View(vid) {
+ var av = 'addView(): ',
+ args = Array.prototype.slice.call(arguments),
+ nid,
+ cb,
+ data;
+
+ args.shift(); // first arg is always vid
+ if (typeof args[0] === 'string') { // nid specified
+ nid = args.shift();
+ }
+ cb = args.shift();
+ data = args.shift();
+
+ this.vid = vid;
+
+ if (validateViewArgs(vid)) {
+ this.nid = nid; // explicit navitem id (can be null)
+ this.cb = $.isPlainObject(cb) ? cb : {}; // callbacks
+ this.data = data; // custom data (can be null)
+ this.$div = null; // view not yet added to DOM
+ this.ok = true; // valid view
+ }
+
+ }
+
+ function validateViewArgs(vid) {
+ var ok = false;
+ if (typeof vid !== 'string' || !vid) {
+ doError(av + 'vid required');
+ } else if (views[vid]) {
+ doError(av + 'View ID "' + vid + '" already exists');
+ } else {
+ ok = true;
+ }
+ return ok;
+ }
+
+ var viewInstanceMethods = {
+ toString: function () {
+ return '[View: id="' + this.vid + '"]';
+ },
+
+ token: function() {
+ return {
+ vid: this.vid,
+ nid: this.nid,
+ data: this.data
+ }
+ }
+ // TODO: create, preload, reset, load, error, resize, unload
+ };
+
+ // attach instance methods to the view prototype
+ $.extend(View.prototype, viewInstanceMethods);
+
+ // ..........................................................
+ // Exported API
+
+ publicApi = {
+ printTime: function () {
+ console.log("the time is " + new Date());
+ },
+
+ addView: function (vid, nid, cb, data) {
+ var view = new View(vid, nid, cb, data),
+ token;
+ if (view.ok) {
+ views[vid] = view;
+ token = view.token();
+ } else {
+ token = { vid: view.vid, bad: true };
+ }
+ return token;
+ }
+ };
+
+ // function to be called from index.html to build the ONOS UI
+ function buildOnosUi() {
+ tsB = new Date().getTime();
+ tsI = tsB - tsI; // initialization duration
+
+ console.log('ONOS UI initialized in ' + tsI + 'ms');
+
+ if (built) {
+ throwError("ONOS UI already built!");
+ }
+ built = true;
+
+ $view = d3.select('#view');
+
+ $(window).on('hashchange', hash);
+
+ // Invoke hashchange callback to navigate to content
+ // indicated by the window location hash.
+ hash();
+
+ // If there were any build errors, report them
+ reportBuildErrors();
+ }
+
+
+ // export the api and build-UI function
+ return {
+ api: publicApi,
+ buildUi: buildOnosUi
+ };
+ };
+
+}(jQuery));
\ No newline at end of file