blob: d345fda99c53693d0446f365dccdf2b834bb7237 [file] [log] [blame]
Simon Hunt195cb382014-11-03 17:50:51 -08001/*
2 * Copyright 2014 Open Networking Laboratory
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17/*
Simon Hunt142d0032014-11-04 20:13:09 -080018 ONOS network topology viewer - version 1.1
Simon Hunt195cb382014-11-03 17:50:51 -080019
20 @author Simon Hunt
21 */
22
23(function (onos) {
24 'use strict';
25
Simon Hunt1a9eff92014-11-07 11:06:34 -080026 // shorter names for library APIs
27 var d3u = onos.lib.d3util;
28
Simon Hunt195cb382014-11-03 17:50:51 -080029 // configuration data
30 var config = {
Simon Hunt99c13842014-11-06 18:23:12 -080031 useLiveData: false,
Simon Hunt195cb382014-11-03 17:50:51 -080032 debugOn: false,
33 debug: {
Simon Hunt99c13842014-11-06 18:23:12 -080034 showNodeXY: true,
35 showKeyHandler: false
Simon Hunt195cb382014-11-03 17:50:51 -080036 },
37 options: {
38 layering: true,
39 collisionPrevention: true,
Simon Hunt142d0032014-11-04 20:13:09 -080040 showBackground: true
Simon Hunt195cb382014-11-03 17:50:51 -080041 },
42 backgroundUrl: 'img/us-map.png',
43 data: {
44 live: {
45 jsonUrl: 'rs/topology/graph',
46 detailPrefix: 'rs/topology/graph/',
47 detailSuffix: ''
48 },
49 fake: {
50 jsonUrl: 'json/network2.json',
51 detailPrefix: 'json/',
52 detailSuffix: '.json'
53 }
54 },
Simon Hunt99c13842014-11-06 18:23:12 -080055 labels: {
56 imgPad: 16,
57 padLR: 4,
58 padTB: 3,
59 marginLR: 3,
60 marginTB: 2,
61 port: {
62 gap: 3,
63 width: 18,
64 height: 14
65 }
66 },
Simon Hunt1a9eff92014-11-07 11:06:34 -080067 topo: {
68 linkInColor: '#66f',
69 linkInWidth: 14
70 },
Simon Hunt99c13842014-11-06 18:23:12 -080071 icons: {
72 w: 28,
73 h: 28,
74 xoff: -12,
75 yoff: -8
76 },
Simon Hunt195cb382014-11-03 17:50:51 -080077 iconUrl: {
78 device: 'img/device.png',
79 host: 'img/host.png',
80 pkt: 'img/pkt.png',
81 opt: 'img/opt.png'
82 },
Simon Hunt195cb382014-11-03 17:50:51 -080083 force: {
Simon Huntc7ee0662014-11-05 16:44:37 -080084 note: 'node.class or link.class is used to differentiate',
85 linkDistance: {
86 infra: 200,
87 host: 40
88 },
89 linkStrength: {
90 infra: 1.0,
91 host: 1.0
92 },
93 charge: {
94 device: -400,
95 host: -100
96 },
97 pad: 20,
Simon Hunt195cb382014-11-03 17:50:51 -080098 translate: function() {
99 return 'translate(' +
Simon Huntc7ee0662014-11-05 16:44:37 -0800100 config.force.pad + ',' +
101 config.force.pad + ')';
Simon Hunt195cb382014-11-03 17:50:51 -0800102 }
Simon Hunt142d0032014-11-04 20:13:09 -0800103 }
Simon Hunt195cb382014-11-03 17:50:51 -0800104 };
105
Simon Hunt142d0032014-11-04 20:13:09 -0800106 // radio buttons
107 var btnSet = [
Simon Hunt934c3ce2014-11-05 11:45:07 -0800108 { text: 'All Layers', cb: showAllLayers },
109 { text: 'Packet Only', cb: showPacketLayer },
110 { text: 'Optical Only', cb: showOpticalLayer }
111 ];
112
113 // key bindings
114 var keyDispatch = {
Simon Hunt99c13842014-11-06 18:23:12 -0800115 space: injectTestEvent, // TODO: remove (testing only)
Simon Hunt1a9eff92014-11-07 11:06:34 -0800116 S: injectStartupEvents, // TODO: remove (testing only)
117 A: testAlert, // TODO: remove (testing only)
118 M: testMe, // TODO: remove (testing only)
Simon Hunt99c13842014-11-06 18:23:12 -0800119
Simon Hunt934c3ce2014-11-05 11:45:07 -0800120 B: toggleBg,
121 G: toggleLayout,
122 L: cycleLabels,
123 P: togglePorts,
124 U: unpin
125 };
Simon Hunt142d0032014-11-04 20:13:09 -0800126
Simon Hunt195cb382014-11-03 17:50:51 -0800127 // state variables
Simon Hunt99c13842014-11-06 18:23:12 -0800128 var network = {
129 nodes: [],
130 links: [],
131 lookup: {}
132 },
133 labelIdx = 0,
Simon Hunt195cb382014-11-03 17:50:51 -0800134 selected = {},
135 highlighted = null,
136 hovered = null,
137 viewMode = 'showAll',
138 portLabelsOn = false;
139
Simon Hunt934c3ce2014-11-05 11:45:07 -0800140 // D3 selections
141 var svg,
142 bgImg,
Simon Huntc7ee0662014-11-05 16:44:37 -0800143 topoG,
144 nodeG,
145 linkG,
146 node,
147 link;
Simon Hunt195cb382014-11-03 17:50:51 -0800148
Simon Hunt142d0032014-11-04 20:13:09 -0800149 // ==============================
Simon Hunt934c3ce2014-11-05 11:45:07 -0800150 // For Debugging / Development
Simon Hunt195cb382014-11-03 17:50:51 -0800151
Simon Hunt99c13842014-11-06 18:23:12 -0800152 var eventPrefix = 'json/eventTest_',
Simon Hunt1a9eff92014-11-07 11:06:34 -0800153 eventNumber = 0,
154 alertNumber = 0;
Simon Hunt195cb382014-11-03 17:50:51 -0800155
Simon Hunt99c13842014-11-06 18:23:12 -0800156 function note(label, msg) {
157 console.log('NOTE: ' + label + ': ' + msg);
Simon Hunt195cb382014-11-03 17:50:51 -0800158 }
159
Simon Hunt99c13842014-11-06 18:23:12 -0800160 function debug(what) {
161 return config.debugOn && config.debug[what];
Simon Hunt934c3ce2014-11-05 11:45:07 -0800162 }
163
Simon Hunt99c13842014-11-06 18:23:12 -0800164
Simon Hunt934c3ce2014-11-05 11:45:07 -0800165 // ==============================
166 // Key Callbacks
167
Simon Hunt1a9eff92014-11-07 11:06:34 -0800168 function testAlert(view) {
169 alertNumber++;
170 view.alert("Test me! -- " + alertNumber);
171 }
172
Simon Hunt99c13842014-11-06 18:23:12 -0800173 function testMe(view) {
Simon Hunt99c13842014-11-06 18:23:12 -0800174 }
175
176 function injectTestEvent(view) {
177 eventNumber++;
178 var eventUrl = eventPrefix + eventNumber + '.json';
179
180 console.log('Fetching JSON: ' + eventUrl);
181 d3.json(eventUrl, function(err, data) {
182 if (err) {
183 view.dataLoadError(err, eventUrl);
184 } else {
185 handleServerEvent(data);
186 }
187 });
Simon Hunt934c3ce2014-11-05 11:45:07 -0800188 }
189
Simon Hunt1a9eff92014-11-07 11:06:34 -0800190 function injectStartupEvents(view) {
191 var lastStartupEvent = 32;
192 while (eventNumber < lastStartupEvent) {
193 injectTestEvent(view);
194 }
195 }
196
Simon Hunt934c3ce2014-11-05 11:45:07 -0800197 function toggleBg() {
198 var vis = bgImg.style('visibility');
199 bgImg.style('visibility', (vis === 'hidden') ? 'visible' : 'hidden');
200 }
201
202 function toggleLayout(view) {
203
204 }
205
Simon Hunt99c13842014-11-06 18:23:12 -0800206 function cycleLabels() {
207 labelIdx = (labelIdx === network.deviceLabelCount - 1) ? 0 : labelIdx + 1;
208 network.nodes.forEach(function (d) {
209 var idx = (labelIdx < d.labels.length) ? labelIdx : 0,
210 node = d3.select('#' + safeId(d.id)),
211 box;
Simon Hunt934c3ce2014-11-05 11:45:07 -0800212
Simon Hunt99c13842014-11-06 18:23:12 -0800213 node.select('text')
214 .text(d.labels[idx])
215 .style('opacity', 0)
216 .transition()
217 .style('opacity', 1);
218
219 box = adjustRectToFitText(node);
220
221 node.select('rect')
222 .transition()
223 .attr(box);
224
225 node.select('image')
226 .transition()
227 .attr('x', box.x + config.icons.xoff)
228 .attr('y', box.y + config.icons.yoff);
229 });
Simon Hunt934c3ce2014-11-05 11:45:07 -0800230 }
231
232 function togglePorts(view) {
233
234 }
235
236 function unpin(view) {
237
238 }
239
240 // ==============================
241 // Radio Button Callbacks
242
Simon Hunt195cb382014-11-03 17:50:51 -0800243 function showAllLayers() {
Simon Hunt142d0032014-11-04 20:13:09 -0800244// network.node.classed('inactive', false);
245// network.link.classed('inactive', false);
246// d3.selectAll('svg .port').classed('inactive', false);
247// d3.selectAll('svg .portText').classed('inactive', false);
Simon Hunt934c3ce2014-11-05 11:45:07 -0800248 // TODO ...
249 console.log('showAllLayers()');
Simon Hunt195cb382014-11-03 17:50:51 -0800250 }
251
252 function showPacketLayer() {
Simon Hunt934c3ce2014-11-05 11:45:07 -0800253 showAllLayers();
254 // TODO ...
255 console.log('showPacketLayer()');
Simon Hunt195cb382014-11-03 17:50:51 -0800256 }
257
258 function showOpticalLayer() {
Simon Hunt934c3ce2014-11-05 11:45:07 -0800259 showAllLayers();
260 // TODO ...
261 console.log('showOpticalLayer()');
Simon Hunt195cb382014-11-03 17:50:51 -0800262 }
263
Simon Hunt142d0032014-11-04 20:13:09 -0800264 // ==============================
Simon Hunt934c3ce2014-11-05 11:45:07 -0800265 // Private functions
266
Simon Hunt99c13842014-11-06 18:23:12 -0800267 function safeId(s) {
268 return s.replace(/[^a-z0-9]/gi, '-');
269 }
270
Simon Huntc7ee0662014-11-05 16:44:37 -0800271 // set the size of the given element to that of the view (reduced if padded)
272 function setSize(el, view, pad) {
273 var padding = pad ? pad * 2 : 0;
Simon Hunt934c3ce2014-11-05 11:45:07 -0800274 el.attr({
Simon Huntc7ee0662014-11-05 16:44:37 -0800275 width: view.width() - padding,
276 height: view.height() - padding
Simon Hunt934c3ce2014-11-05 11:45:07 -0800277 });
278 }
279
Simon Hunt99c13842014-11-06 18:23:12 -0800280 function establishWebSocket() {
281 // TODO: establish a real web-socket
282 // NOTE, for now, we are using the 'Q' key to artificially inject
283 // "events" from the server.
284 }
Simon Hunt934c3ce2014-11-05 11:45:07 -0800285
Simon Hunt99c13842014-11-06 18:23:12 -0800286 // ==============================
287 // Event handlers for server-pushed events
288
289 var eventDispatch = {
290 addDevice: addDevice,
291 updateDevice: updateDevice,
292 removeDevice: removeDevice,
293 addLink: addLink
294 };
295
296 function addDevice(data) {
297 var device = data.payload,
298 node = createDeviceNode(device);
299 note('addDevice', device.id);
300
301 network.nodes.push(node);
302 network.lookup[node.id] = node;
303 updateNodes();
304 network.force.start();
305 }
306
307 function updateDevice(data) {
308 var device = data.payload;
309 note('updateDevice', device.id);
310
311 }
312
313 function removeDevice(data) {
314 var device = data.payload;
315 note('removeDevice', device.id);
316
317 }
318
319 function addLink(data) {
320 var link = data.payload,
321 lnk = createLink(link);
322
323 if (lnk) {
324 note('addLink', lnk.id);
325
326 network.links.push(lnk);
327 updateLinks();
328 network.force.start();
329 }
330 }
331
332 // ....
333
334 function unknownEvent(data) {
335 // TODO: use dialog, not alert
336 alert('Unknown event type: "' + data.event + '"');
337 }
338
339 function handleServerEvent(data) {
340 var fn = eventDispatch[data.event] || unknownEvent;
341 fn(data);
342 }
343
344 // ==============================
345 // force layout modification functions
346
347 function translate(x, y) {
348 return 'translate(' + x + ',' + y + ')';
349 }
350
351 function createLink(link) {
352 var type = link.type,
353 src = link.src,
354 dst = link.dst,
355 w = link.linkWidth,
356 srcNode = network.lookup[src],
357 dstNode = network.lookup[dst],
358 lnk;
359
360 if (!(srcNode && dstNode)) {
361 alert('nodes not on map');
362 return null;
363 }
364
365 lnk = {
366 id: safeId(src) + '~' + safeId(dst),
367 source: srcNode,
368 target: dstNode,
369 class: 'link',
370 svgClass: type ? 'link ' + type : 'link',
371 x1: srcNode.x,
372 y1: srcNode.y,
373 x2: dstNode.x,
374 y2: dstNode.y,
375 width: w
376 };
377 return lnk;
378 }
379
Simon Hunt1a9eff92014-11-07 11:06:34 -0800380 function linkWidth(w) {
381 // w is number of links between nodes. Scale appropriately.
382 return w * 1.2;
383 }
384
Simon Hunt99c13842014-11-06 18:23:12 -0800385 function updateLinks() {
386 link = linkG.selectAll('.link')
387 .data(network.links, function (d) { return d.id; });
388
389 // operate on existing links, if necessary
390 // link .foo() .bar() ...
391
392 // operate on entering links:
393 var entering = link.enter()
394 .append('line')
395 .attr({
396 id: function (d) { return d.id; },
397 class: function (d) { return d.svgClass; },
398 x1: function (d) { return d.x1; },
399 y1: function (d) { return d.y1; },
400 x2: function (d) { return d.x2; },
401 y2: function (d) { return d.y2; },
Simon Hunt1a9eff92014-11-07 11:06:34 -0800402 stroke: config.topo.linkInColor,
403 'stroke-width': config.topo.linkInWidth
Simon Hunt99c13842014-11-06 18:23:12 -0800404 })
405 .transition().duration(1000)
406 .attr({
Simon Hunt1a9eff92014-11-07 11:06:34 -0800407 'stroke-width': function (d) { return linkWidth(d.width); },
Simon Hunt99c13842014-11-06 18:23:12 -0800408 stroke: '#666' // TODO: remove explicit stroke, rather...
409 });
410
411 // augment links
412 // TODO: add src/dst port labels etc.
413
414 }
415
416 function createDeviceNode(device) {
417 // start with the object as is
418 var node = device,
419 type = device.type;
420
421 // Augment as needed...
422 node.class = 'device';
423 node.svgClass = type ? 'node device ' + type : 'node device';
424 positionNode(node);
425
426 // cache label array length
427 network.deviceLabelCount = device.labels.length;
428
429 return node;
430 }
431
432 function positionNode(node) {
433 var meta = node.metaUi,
434 x = 0,
435 y = 0;
436
437 if (meta) {
438 x = meta.x;
439 y = meta.y;
440 }
441 if (x && y) {
442 node.fixed = true;
443 }
444 node.x = x || network.view.width() / 2;
445 node.y = y || network.view.height() / 2;
446 }
447
448
449 function iconUrl(d) {
450 return 'img/' + d.type + '.png';
451 }
452
453 // returns the newly computed bounding box of the rectangle
454 function adjustRectToFitText(n) {
455 var text = n.select('text'),
456 box = text.node().getBBox(),
457 lab = config.labels;
458
459 text.attr('text-anchor', 'middle')
460 .attr('y', '-0.8em')
461 .attr('x', lab.imgPad/2);
462
463 // translate the bbox so that it is centered on [x,y]
464 box.x = -box.width / 2;
465 box.y = -box.height / 2;
466
467 // add padding
468 box.x -= (lab.padLR + lab.imgPad/2);
469 box.width += lab.padLR * 2 + lab.imgPad;
470 box.y -= lab.padTB;
471 box.height += lab.padTB * 2;
472
473 return box;
474 }
475
Simon Hunt1a9eff92014-11-07 11:06:34 -0800476 function mkSvgClass(d) {
477 return d.fixed ? d.svgClass + ' fixed' : d.svgClass;
478 }
479
Simon Hunt99c13842014-11-06 18:23:12 -0800480 function updateNodes() {
481 node = nodeG.selectAll('.node')
482 .data(network.nodes, function (d) { return d.id; });
483
484 // operate on existing nodes, if necessary
485 //node .foo() .bar() ...
486
487 // operate on entering nodes:
488 var entering = node.enter()
489 .append('g')
490 .attr({
491 id: function (d) { return safeId(d.id); },
Simon Hunt1a9eff92014-11-07 11:06:34 -0800492 class: mkSvgClass,
Simon Hunt99c13842014-11-06 18:23:12 -0800493 transform: function (d) { return translate(d.x, d.y); },
494 opacity: 0
495 })
Simon Hunt1a9eff92014-11-07 11:06:34 -0800496 .call(network.drag)
Simon Hunt99c13842014-11-06 18:23:12 -0800497 //.on('mouseover', function (d) {})
498 //.on('mouseover', function (d) {})
499 .transition()
500 .attr('opacity', 1);
501
502 // augment device nodes...
503 entering.filter('.device').each(function (d) {
504 var node = d3.select(this),
505 icon = iconUrl(d),
506 idx = (labelIdx < d.labels.length) ? labelIdx : 0,
507 box;
508
509 node.append('rect')
510 .attr({
511 'rx': 5,
512 'ry': 5
513 });
514
515 node.append('text')
516 .text(d.labels[idx])
517 .attr('dy', '1.1em');
518
519 box = adjustRectToFitText(node);
520
521 node.select('rect')
522 .attr(box);
523
524 if (icon) {
525 var cfg = config.icons;
526 node.append('svg:image')
527 .attr({
528 x: box.x + config.icons.xoff,
529 y: box.y + config.icons.yoff,
530 width: cfg.w,
531 height: cfg.h,
532 'xlink:href': icon
533 });
534 }
535
536 // debug function to show the modelled x,y coordinates of nodes...
537 if (debug('showNodeXY')) {
538 node.select('rect').attr('fill-opacity', 0.5);
539 node.append('circle')
540 .attr({
541 class: 'debug',
542 cx: 0,
543 cy: 0,
544 r: '3px'
545 });
Simon Huntc7ee0662014-11-05 16:44:37 -0800546 }
547 });
Simon Hunt934c3ce2014-11-05 11:45:07 -0800548
Simon Huntc7ee0662014-11-05 16:44:37 -0800549
Simon Hunt99c13842014-11-06 18:23:12 -0800550 // operate on both existing and new nodes, if necessary
551 //node .foo() .bar() ...
Simon Huntc7ee0662014-11-05 16:44:37 -0800552
Simon Hunt99c13842014-11-06 18:23:12 -0800553 // operate on exiting nodes:
554 // TODO: figure out how to remove the node 'g' AND its children
555 node.exit()
556 .transition()
557 .duration(750)
558 .attr({
559 opacity: 0,
560 cx: 0,
561 cy: 0,
562 r: 0
563 })
564 .remove();
Simon Huntc7ee0662014-11-05 16:44:37 -0800565 }
566
567
568 function tick() {
569 node.attr({
Simon Hunt99c13842014-11-06 18:23:12 -0800570 transform: function (d) { return translate(d.x, d.y); }
Simon Huntc7ee0662014-11-05 16:44:37 -0800571 });
572
573 link.attr({
574 x1: function (d) { return d.source.x; },
575 y1: function (d) { return d.source.y; },
576 x2: function (d) { return d.target.x; },
577 y2: function (d) { return d.target.y; }
578 });
579 }
Simon Hunt934c3ce2014-11-05 11:45:07 -0800580
581 // ==============================
Simon Hunt142d0032014-11-04 20:13:09 -0800582 // View life-cycle callbacks
Simon Hunt195cb382014-11-03 17:50:51 -0800583
Simon Hunt142d0032014-11-04 20:13:09 -0800584 function preload(view, ctx) {
585 var w = view.width(),
586 h = view.height(),
587 idBg = view.uid('bg'),
Simon Huntc7ee0662014-11-05 16:44:37 -0800588 showBg = config.options.showBackground ? 'visible' : 'hidden',
589 fcfg = config.force,
590 fpad = fcfg.pad,
591 forceDim = [w - 2*fpad, h - 2*fpad];
Simon Hunt195cb382014-11-03 17:50:51 -0800592
Simon Hunt142d0032014-11-04 20:13:09 -0800593 // NOTE: view.$div is a D3 selection of the view's div
594 svg = view.$div.append('svg');
Simon Hunt934c3ce2014-11-05 11:45:07 -0800595 setSize(svg, view);
596
Simon Hunt1a9eff92014-11-07 11:06:34 -0800597 // add blue glow filter to svg layer
598 d3u.appendGlow(svg);
599
Simon Hunt142d0032014-11-04 20:13:09 -0800600 // load the background image
601 bgImg = svg.append('svg:image')
Simon Hunt195cb382014-11-03 17:50:51 -0800602 .attr({
Simon Hunt142d0032014-11-04 20:13:09 -0800603 id: idBg,
604 width: w,
605 height: h,
Simon Hunt195cb382014-11-03 17:50:51 -0800606 'xlink:href': config.backgroundUrl
607 })
Simon Hunt142d0032014-11-04 20:13:09 -0800608 .style({
609 visibility: showBg
Simon Hunt195cb382014-11-03 17:50:51 -0800610 });
Simon Huntc7ee0662014-11-05 16:44:37 -0800611
612 // group for the topology
613 topoG = svg.append('g')
614 .attr('transform', fcfg.translate());
615
616 // subgroups for links and nodes
617 linkG = topoG.append('g').attr('id', 'links');
618 nodeG = topoG.append('g').attr('id', 'nodes');
619
620 // selection of nodes and links
621 link = linkG.selectAll('.link');
622 node = nodeG.selectAll('.node');
623
Simon Hunt99c13842014-11-06 18:23:12 -0800624 function ldist(d) {
625 return fcfg.linkDistance[d.class] || 150;
626 }
627 function lstrg(d) {
628 return fcfg.linkStrength[d.class] || 1;
629 }
630 function lchrg(d) {
631 return fcfg.charge[d.class] || -200;
632 }
633
Simon Hunt1a9eff92014-11-07 11:06:34 -0800634 function selectCb(d, self) {
635 // TODO: selectObject(d, self);
636 }
637
638 function atDragEnd(d, self) {
639 // once we've finished moving, pin the node in position,
640 // if it is a device (not a host)
641 if (d.class === 'device') {
642 d.fixed = true;
643 d3.select(self).classed('fixed', true)
644 // TODO: send new [x,y] back to server, via websocket.
645 }
646 }
647
Simon Huntc7ee0662014-11-05 16:44:37 -0800648 // set up the force layout
649 network.force = d3.layout.force()
650 .size(forceDim)
651 .nodes(network.nodes)
652 .links(network.links)
Simon Hunt99c13842014-11-06 18:23:12 -0800653 .charge(lchrg)
654 .linkDistance(ldist)
655 .linkStrength(lstrg)
Simon Huntc7ee0662014-11-05 16:44:37 -0800656 .on('tick', tick);
Simon Hunt195cb382014-11-03 17:50:51 -0800657
Simon Hunt1a9eff92014-11-07 11:06:34 -0800658 network.drag = d3u.createDragBehavior(network.force, selectCb, atDragEnd);
659 }
Simon Hunt195cb382014-11-03 17:50:51 -0800660
Simon Hunt142d0032014-11-04 20:13:09 -0800661 function load(view, ctx) {
Simon Hunt99c13842014-11-06 18:23:12 -0800662 // cache the view token, so network topo functions can access it
663 network.view = view;
664
665 // set our radio buttons and key bindings
Simon Hunt934c3ce2014-11-05 11:45:07 -0800666 view.setRadio(btnSet);
667 view.setKeys(keyDispatch);
Simon Hunt195cb382014-11-03 17:50:51 -0800668
Simon Hunt99c13842014-11-06 18:23:12 -0800669 establishWebSocket();
Simon Hunt195cb382014-11-03 17:50:51 -0800670 }
671
Simon Hunt142d0032014-11-04 20:13:09 -0800672 function resize(view, ctx) {
Simon Hunt934c3ce2014-11-05 11:45:07 -0800673 setSize(svg, view);
674 setSize(bgImg, view);
Simon Hunt99c13842014-11-06 18:23:12 -0800675
676 // TODO: hook to recompute layout, perhaps? work with zoom/pan code
677 // adjust force layout size
Simon Hunt142d0032014-11-04 20:13:09 -0800678 }
679
680
681 // ==============================
682 // View registration
Simon Hunt195cb382014-11-03 17:50:51 -0800683
Simon Hunt25248912014-11-04 11:25:48 -0800684 onos.ui.addView('topo', {
Simon Hunt142d0032014-11-04 20:13:09 -0800685 preload: preload,
686 load: load,
687 resize: resize
Simon Hunt195cb382014-11-03 17:50:51 -0800688 });
689
Simon Hunt195cb382014-11-03 17:50:51 -0800690}(ONOS));