<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <link rel="stylesheet" href="../vendors/jointjs/css/joint.min.css" /> <script src="../vendors/jointjs/dependencies/jquery.min.js"></script> <script src="../vendors/jointjs/dependencies//lodash.min.js"></script> <script src="../vendors/jointjs/dependencies/backbone-min.js"></script> <script src="../vendors/jointjs/js/joint.min.js"></script> <script src="../vendors/jointjs/lib/dagre.min.js"></script> <script src="../vendors/jointjs/lib/graphlib.min.js"></script> <script src="../vendors/jointjs/plugins/joint.layout.DirectedGraph.min.js"></script> <link rel="stylesheet" type="text/css" href="../css/graph.css" /> </head> <body> <div id="graphHolder"></div> <script type="text/javascript"> //adjacencyList, hard-encoded now but will be the result of a request to the server, format may change var vmList = { 'vms': [ 'VM 1', 'VM 2', 'VM 3', 'VM 4', 'VM 5', 'VM 6', 'VM 7', 'VM 8', 'VM 9', 'VM 10' ], 'links': [ ['VM 1', 'VM 2', 'I1', 'I2'], ['VM 2', 'VM 3', 'I3', 'I4'], ['VM 1', 'VM 3', 'I5', 'I6'], ['VM 2', 'VM 4', 'I5', 'I6'], ['VM 4', 'VM 5', 'I5', 'I6'], ['VM 4', 'VM 6', 'I5', 'I6'], ['VM 4', 'VM 1', 'I5', 'I6'], ['VM 7', 'VM 4', 'I5', 'I6'], ['VM 7', 'VM 3', 'I5', 'I6'], ['VM 6', 'VM 8', 'I5', 'I6'], ['VM 3', 'VM 9', 'I5', 'I6'], ['VM 3', 'VM 10', 'I5', 'I6'] ] }; //Custom element for inserting html joint.shapes.html = {}; joint.shapes.html.Element = joint.shapes.basic.Rect.extend({ defaults: joint.util.deepSupplement({ type: 'html.Element', attrs: { rect: { stroke: 'none', 'fill-opacity': 0 } } }, joint.shapes.basic.Rect.prototype.defaults) }); //Custom view for this element joint.shapes.html.ElementView = joint.dia.ElementView.extend({ initialize: function() { _.bindAll(this, 'updateBox'); joint.dia.ElementView.prototype.initialize.apply(this, arguments); this.$box = $(_.template(this.model.get('html'))()); // Prevent paper from handling pointerdown. this.$box.find('input,select').on('mousedown click', function(evt) { evt.stopPropagation(); }); // This is an example of reacting on the input change and storing the input data in the cell model. this.$box.find('input').on('change', _.bind(function(evt) { this.model.set('input', $(evt.target).val()); }, this)); this.$box.find('select').on('change', _.bind(function(evt) { this.model.set('select', $(evt.target).val()); }, this)); this.$box.find('select').val(this.model.get('select')); //this.$box.find('.config').on('click', CALL OVERLAY HERE); // Update the box position whenever the underlying model changes. this.model.on('change', this.updateBox, this); // Remove the box when the model gets removed from the graph. this.model.on('remove', this.removeBox, this); this.updateBox(); }, render: function() { joint.dia.ElementView.prototype.render.apply(this, arguments); this.paper.$el.prepend(this.$box); this.updateBox(); return this; }, updateBox: function() { // Set the position and dimension of the box so that it covers the JointJS element. var bbox = this.model.getBBox(); // Example of updating the HTML with a data stored in the cell model. this.$box.find('label').text(this.model.get('name')); this.$box.find('span').text(this.model.get('select')); this.$box.css({ width: bbox.width, height: bbox.height, left: bbox.x, top: bbox.y, transform: 'rotate(' + (this.model.get('angle') || 0) + 'deg)' }); }, removeBox: function(evt) { this.$box.remove(); } }); //Read the adjacencyList and build the elements and the links according to it function buildGraphFromAdjacencyList(adjacencyList) { var elements = []; var links = []; _.each(adjacencyList['vms'], function(vm) { elements.push(makeElement(vm)); }); _.each(adjacencyList['links'], function(link) { links.push(makeLink(link[0], link[1] , link[2], link[3])); }); // Links must be added after all the elements. This is because when the links // are added to the graph, link source/target // elements must be in the graph already. return elements.concat(links); } //Return a new link linking the parent and child elements with the interfaces names given in parameters function makeLink(parentElementLabel, childElementLabel, Iparent, Ichild) { return new joint.dia.Link({ source: { id: parentElementLabel }, target: { id: childElementLabel }, labels: [ { position: 20, attrs: { text: { text: Iparent } }}, { position: -20, attrs: { text: { text: Ichild } }} ] }); } //Return a new element function makeElement(label) { var maxLineLength = _.max(label.split('\n'), function(l) { return l.length; }).length; // Compute width/height of the rectangle based on the number // of lines in the label and the letter size. 0.6 * letterSize is // an approximation of the monospace font letter width. var width = 130; var height = 80; return new joint.shapes.html.Element({ id: label, name: label, size: { width: width, height: height }, html: [ '<div class="html-element">', '<img src="../images/ON.png">', '<label></label>', '<input type="image" src="../images/gear.png" class="config"></button>', '</div>' ].join(''), attrs: { rect: { fill: '#FE854F', width: width, height: height, rx: 5, ry: 5, stroke: 'none' } } }); } var graph = new joint.dia.Graph; var paper = new joint.dia.Paper({ el: $('#graphHolder'), width: 2000, height: 2000, model: graph, gridSize: 1 }); paper.$el.css('pointer-events', 'none'); var cells = buildGraphFromAdjacencyList(vmList); graph.resetCells(cells); joint.layout.DirectedGraph.layout(graph, { setLinkVertices: false, //Top to bottom generation rankDir: "TB", nodeSep: 150, edgeSep: 150, rankSep: 150 }); </script> </body> </html>