Simon Hunt

GUI -- augmented hash parsing to include flags (after '?'), which are passed int…

…o view callbacks as a boolean map.
 - moved event test files into sub directories
 - prepared topo2.js for scenario choice via hash context and 'local' (and 'debug') flag.
 - added 'simple' scenario: 2 switches, 1 link, and 2 hosts.
 - augmented topo event dispatch for yet-to-be-implemented event handlers.
 - implemented addHost() event handler.

Change-Id: I06b032684fd4d5f85262d13d58ad10edae23b3ed
Showing 56 changed files with 280 additions and 35 deletions
......@@ -90,6 +90,7 @@
<script src="sampleAlt2.js"></script>
<script src="sampleRadio.js"></script>
<script src="sampleKeys.js"></script>
<script src="sampleHash.js"></script>
<!-- Contributed (application) views injected here -->
<!-- TODO: replace with template marker and inject refs server-side -->
......
{
"comments": [
"This scenario steps through adding a host intent."
],
"title": "Host Intent Scenario",
"params": {
"lastAuto": 0
}
}
\ No newline at end of file
{
"event": "addDevice",
"payload": {
"id": "of:0000ffffffff0008",
"type": "switch",
"online": false,
"labels": [
"0000ffffffff0008",
"FF:FF:FF:FF:00:08",
"sw-8"
],
"metaUi": {
"x": 400,
"y": 280
}
}
}
{
"event": "addDevice",
"payload": {
"id": "of:0000ffffffff0003",
"type": "switch",
"online": false,
"labels": [
"0000ffffffff0003",
"FF:FF:FF:FF:00:03",
"sw-3"
],
"metaUi": {
"x": 800,
"y": 280
}
}
}
{
"event": "addLink",
"payload": {
"src": "of:0000ffffffff0003",
"srcPort": "21",
"dst": "of:0000ffffffff0008",
"dstPort": "20",
"type": "infra",
"linkWidth": 2,
"props" : {
"BW": "70 G"
}
}
}
{
"event": "addHost",
"payload": {
"id": "00:00:00:00:00:03/-1",
"cp": {
"device": "of:0000ffffffff0003",
"port": 1
},
"labels": [
"10.0.0.3",
"00:00:00:00:00:03"
],
"metaUi": {
}
}
}
{
"event": "addHost",
"payload": {
"id": "00:00:00:00:00:08/-1",
"cp": {
"device": "of:0000ffffffff0008",
"port": 1
},
"labels": [
"10.0.0.8",
"00:00:00:00:00:08"
],
"metaUi": {
}
}
}
{
"comments": [
"Add two devices and one link (auto), and two hosts."
],
"title": "Simple Startup Scenario",
"params": {
"lastAuto": 0
}
}
\ No newline at end of file
{
"comments": [
"This scenario steps through adding devices and links.",
"(Typical 'start-ip' of the view.)"
],
"title": "Startup Scenario",
"params": {
"lastAuto": 32
}
}
\ No newline at end of file
......@@ -52,6 +52,7 @@
current = {
view: null,
ctx: '',
flags: {},
theme: settings.theme
},
built = false,
......@@ -110,6 +111,7 @@
function doError(msg) {
errorCount++;
console.error(msg);
doAlert(msg);
}
function trace(msg) {
......@@ -140,7 +142,7 @@
t = parseHash(hash);
if (!t || !t.vid) {
doError('Unable to parse target hash: ' + hash);
doError('Unable to parse target hash: "' + hash + '"');
}
view = views[t.vid];
......@@ -160,33 +162,72 @@
function parseHash(s) {
// extract navigation coordinates from the supplied string
// "vid,ctx" --> { vid:vid, ctx:ctx }
// "vid,ctx?flag1,flag2" --> { vid:vid, ctx:ctx, flags:{...} }
traceFn('parseHash', s);
var m = /^[#]{0,1}(\S+),(\S*)$/.exec(s);
// look for use of flags, first
var vidctx,
vid,
ctx,
flags,
flagMap,
m;
// RE that includes flags ('?flag1,flag2')
m = /^[#]{0,1}(.+)\?(.+)$/.exec(s);
if (m) {
return { vid: m[1], ctx: m[2] };
vidctx = m[1];
flags = m[2];
flagMap = {};
} else {
// no flags
m = /^[#]{0,1}((.+)(,.+)*)$/.exec(s);
if (m) {
vidctx = m[1];
} else {
// bad hash
return null;
}
}
vidctx = vidctx.split(',');
vid = vidctx[0];
ctx = vidctx[1];
if (flags) {
flags.split(',').forEach(function (f) {
flagMap[f.trim()] = true;
});
}
m = /^[#]{0,1}(\S+)$/.exec(s);
return m ? { vid: m[1] } : null;
return {
vid: vid.trim(),
ctx: ctx ? ctx.trim() : '',
flags: flagMap
};
}
function makeHash(t, ctx) {
function makeHash(t, ctx, flags) {
traceFn('makeHash');
// make a hash string from the given navigation coordinates.
// make a hash string from the given navigation coordinates,
// and optional flags map.
// if t is not an object, then it is a vid
var h = t,
c = ctx || '';
c = ctx || '',
f = $.isPlainObject(flags) ? flags : null;
if ($.isPlainObject(t)) {
h = t.vid;
c = t.ctx || '';
f = t.flags || null;
}
if (c) {
h += ',' + c;
}
if (f) {
h += '?' + d3.map(f).keys().join(',');
}
trace('hash = "' + h + '"');
return h;
}
......@@ -244,6 +285,9 @@
// set the specified view as current, while invoking the
// appropriate life-cycle callbacks
// first, we'll start by closing the alerts pane, if open
closeAlerts();
// 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)) {
......@@ -258,10 +302,11 @@
// cache new view and context
current.view = view;
current.ctx = t.ctx || '';
current.flags = t.flags || {};
// preload is called only once, after the view is in the DOM
if (!view.preloaded) {
view.preload(current.ctx);
view.preload(current.ctx, current.flags);
view.preloaded = true;
}
......@@ -269,7 +314,7 @@
view.reset();
// load the view
view.load(current.ctx);
view.load(current.ctx, current.flags);
}
// generate 'unique' id by prefixing view id
......@@ -454,7 +499,7 @@
d3.selectAll('.onosView').call(setViewDimensions);
// allow current view to react to resize event...
if (current.view) {
current.view.resize(current.ctx);
current.view.resize(current.ctx, current.flags);
}
}
......@@ -521,13 +566,13 @@
}
},
preload: function (ctx) {
preload: function (ctx, flags) {
var c = ctx || '',
fn = isF(this.cb.preload);
traceFn('View.preload', this.vid + ', ' + c);
if (fn) {
trace('PRELOAD cb for ' + this.vid);
fn(this.token(), c);
fn(this.token(), c, flags);
}
},
......@@ -544,15 +589,14 @@
}
},
load: function (ctx) {
load: function (ctx, flags) {
var c = ctx || '',
fn = isF(this.cb.load);
traceFn('View.load', this.vid + ', ' + c);
this.$div.classed('currentView', true);
// TODO: add radio button set, if needed
if (fn) {
trace('LOAD cb for ' + this.vid);
fn(this.token(), c);
fn(this.token(), c, flags);
}
},
......@@ -560,14 +604,13 @@
var fn = isF(this.cb.unload);
traceFn('View.unload', this.vid);
this.$div.classed('currentView', false);
// TODO: remove radio button set, if needed
if (fn) {
trace('UNLOAD cb for ' + this.vid);
fn(this.token());
}
},
resize: function (ctx) {
resize: function (ctx, flags) {
var c = ctx || '',
fn = isF(this.cb.resize),
w = this.width(),
......@@ -576,7 +619,7 @@
' [' + w + 'x' + h + ']');
if (fn) {
trace('RESIZE cb for ' + this.vid);
fn(this.token(), c);
fn(this.token(), c, flags);
}
},
......
......@@ -21,12 +21,17 @@
*/
(function () {
// NOTE: DON'T Want to do this.. we want to be able to
// use the parameter section, for example:
// #viewId,context?flag1,flag2,flag3
// Check if the URL in the address bar contains a parameter section
// (delineated by '?'). If this is the case, rewrite using '#' instead.
var m = /([^?]*)\?(.*)/.exec(window.location.href);
if (m) {
window.location.href = m[1] + '#' + m[2];
}
//var m = /([^?]*)\?(.*)/.exec(window.location.href);
//if (m) {
// window.location.href = m[1] + '#' + m[2];
//}
}());
......
/*
* 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.
*/
/*
Sample view to illustrate hash formats.
@author Simon Hunt
*/
(function (onos) {
'use strict';
var intro = "Try using the following hashes in the address bar:",
hashPrefix = '#sampleHash',
suffixes = [
'',
',one',
',two',
',context,ignored',
',context,ignored?a,b,c',
',two?foo',
',three?foo,bar'
],
$d;
function note(txt) {
$d.append('p')
.text(txt)
.style({
'font-size': '10pt',
color: 'darkorange',
padding: '0 20px',
margin: 0
});
}
function para(txt, color) {
var c = color || 'black';
$d.append('p')
.text(txt)
.style({
padding: '2px 8px',
color: c
});
}
function load(view, ctx, flags) {
var c = ctx || '(undefined)',
f = flags ? d3.map(flags).keys() : [];
$d = view.$div;
para(intro);
suffixes.forEach(function (s) {
note(hashPrefix + s);
});
para('View ID: ' + view.vid, 'blue');
para('Context: ' + c, 'blue');
para('Flags: { ' + f.join(', ') + ' }', 'magenta');
}
// == register the view here, with links to lifecycle callbacks
onos.ui.addView('sampleHash', {
reset: true, // empty the div on reset
load: load
});
}(ONOS));
......@@ -20,55 +20,59 @@
@author Simon Hunt
*/
svg #topo-bg {
#topo svg #topo-bg {
opacity: 0.5;
}
/* NODES */
svg .node.device {
#topo svg .node.device {
stroke: none;
stroke-width: 1.5px;
cursor: pointer;
}
svg .node.device rect {
#topo svg .node.device rect {
stroke-width: 1.5px;
}
svg .node.device.fixed rect {
#topo svg .node.device.fixed rect {
stroke-width: 1.5;
stroke: #ccc;
}
svg .node.device.switch {
#topo svg .node.device.switch {
fill: #17f;
}
svg .node.device.roadm {
#topo svg .node.device.roadm {
fill: #03c;
}
svg .node text {
#topo svg .node.host {
fill: #846;
}
#topo svg .node text {
stroke: none;
fill: white;
font: 10pt sans-serif;
pointer-events: none;
}
svg .node.selected rect,
svg .node.selected circle {
#topo svg .node.selected rect,
#topo svg .node.selected circle {
filter: url(#blue-glow);
}
/* LINKS */
svg .link {
#topo svg .link {
opacity: .7;
}
/* for debugging */
svg .node circle.debug {
#topo svg .node circle.debug {
fill: white;
stroke: red;
}
......
This diff is collapsed. Click to expand it.