| /* |
| * 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)); |