From 7d627229840e98c43410dc31392df13c5cd3870e Mon Sep 17 00:00:00 2001 From: Ronaldo Pereira Date: Thu, 12 May 2016 19:26:22 -0300 Subject: [PATCH] WIP: Introduces a new design for web-inspector This is an UI implementation to visualyze flow data that is being generated by Soletta. This patch adds the basic features/images of the tool. Signed-off-by: Ronaldo Pereira --- src/bin/sol-fbp-runner/Modal.js | 50 ++ src/bin/sol-fbp-runner/Port.js | 122 +++++ src/bin/sol-fbp-runner/Widget.js | 222 +++++++++ src/bin/sol-fbp-runner/css/style.css | 192 ++++++++ src/bin/sol-fbp-runner/images/ico_close.png | Bin 0 -> 23693 bytes src/bin/sol-fbp-runner/images/ico_info.png | Bin 0 -> 7806 bytes src/bin/sol-fbp-runner/web-inspector.css | 204 ++------ src/bin/sol-fbp-runner/web-inspector.html | 72 +-- src/bin/sol-fbp-runner/web-inspector.js | 487 +++++--------------- 9 files changed, 745 insertions(+), 604 deletions(-) create mode 100644 src/bin/sol-fbp-runner/Modal.js create mode 100644 src/bin/sol-fbp-runner/Port.js create mode 100644 src/bin/sol-fbp-runner/Widget.js create mode 100644 src/bin/sol-fbp-runner/css/style.css create mode 100644 src/bin/sol-fbp-runner/images/ico_close.png create mode 100644 src/bin/sol-fbp-runner/images/ico_info.png diff --git a/src/bin/sol-fbp-runner/Modal.js b/src/bin/sol-fbp-runner/Modal.js new file mode 100644 index 000000000..ac1a09801 --- /dev/null +++ b/src/bin/sol-fbp-runner/Modal.js @@ -0,0 +1,50 @@ +(function(window) { + + 'use strict' + + function Modal(){ + + var instance = this; + + this.el = $("
").addClass("modal").appendTo("#modal-container"); + this.header = $("
").addClass("modal-header").appendTo(this.el); + this.title = $("").appendTo(this.header).addClass("modal-title"); + this.content = $("
").appendTo(this.el);
+
+		this.el.hide();
+		this.shield = $("
").attr("id","shield"); + + this.closeButton = $("").attr("src","images/ico_close.png").addClass("close").appendTo(this.header) + .click(function(){ + instance.hide(); + }); + + + } + + Modal.prototype.show = function(data, title) { + + var instance = this; + + this.title.empty().append(title); + this.content.empty().append(data); + + //adding click to the shield div + this.shield.insertBefore(this.el) + .click(function() { + instance.hide(); + }); + + this.el.show(400); + }; + + Modal.prototype.hide = function() { + this.title.empty(); + this.content.empty(); + this.el.hide(); + this.shield.remove(); + }; + + window.Modal = Modal; + +})(window) diff --git a/src/bin/sol-fbp-runner/Port.js b/src/bin/sol-fbp-runner/Port.js new file mode 100644 index 000000000..1c744611e --- /dev/null +++ b/src/bin/sol-fbp-runner/Port.js @@ -0,0 +1,122 @@ +(function(window) { + + 'use strict' + + function Port (params) { + + //data properties + var instance = this; //returns the __proto__ of the object + + this.name = params["name"]; + this.value = params["value"]; + this.type = params["type"] || "in"; + this.data_type = params["data_type"]; + this.widget = params["widget"]; + this.port_index = params["port_index"]; + this.required = params["required"] || true; + this.key = "port_" + this.type + "_" + params["key"]; + this.connections = []; //the pair port which this port is connected + this.emitt = false; + + //DOM properties + this.el = $("
") + .attr("id",this.key) + .addClass("port "+this.getPortType()) + .append(this.name); + + //adding the port to the correct container + if(this.type == "in") this.widget.getLeft().append(this.el); + if(this.type == "out") this.widget.getRight().append(this.el); + + //MOUSE AND CUSTOM EVENTS + this.el.click(function (event) { + $(event.target).trigger({ + type:"port-select", + port:instance, + widget:instance.widget, + value:instance.value + }); + + }); + + this.el.mouseover(function (event) { + $(event.target).trigger({ + type:"port-over", + port:instance, + widget:instance.widget, + connections:instance.connections + }); + }); + + this.el.mouseout(function (event) { + $(event.target).trigger({ + type:"port-out", + port:instance, + widget:instance.widget, + connections:instance.connections + }); + }); + } + + Port.prototype.update = function (value) { + + this.value = value; //retrieves only the valu + + var widget = this.widget; + var instance = this.instance; + + if(this.emitt) this.el.trigger( + { + type:"port-update", + widget:widget, + port:instance, + value:value, + }); + } + + Port.prototype.getElement = function() { + return this.el; + } + + Port.prototype.getParentWidget = function() { + return this.widget; + } + + Port.prototype.getPortType = function() { + return (this.type === "in") ? "in" : "out"; + } + + Port.prototype.select = function() { + this.emitt = true; + this.el.addClass("selected"); + } + + Port.prototype.unselect = function() { + this.emitt = false; + this.el.removeClass("selected"); + } + + Port.prototype.connect = function (port) { + + //ERROR TRYING TO CONNECT PORTs! + if(this.connections.indexOf(port.key) == -1) + { + this.connections.push(port.key); + this.widget.autoSelectPort(this); + } + + } + + Port.prototype.hidePort = function() { + this.el.addClass("hidden"); + this.el.addClass("non-selectable"); + this.el.unbind(); + } + + Port.prototype.showPort = function() { + //TODO + } + + window.Port = Port; + +})(window) diff --git a/src/bin/sol-fbp-runner/Widget.js b/src/bin/sol-fbp-runner/Widget.js new file mode 100644 index 000000000..56e553aa0 --- /dev/null +++ b/src/bin/sol-fbp-runner/Widget.js @@ -0,0 +1,222 @@ +(function(window) { + + 'use strict' + + function Widget(payload, key) { + + //caching a secure reference to the instance + var instance = this; + //var selectedPort; + + Widget.prototype.portsCount = 0; + + //unique id for the widget and other unique properties + this.raw = JSON.stringify(payload, null, 4); + this.key = key; + this.widgetName = payload.id; //change to 'name ?' + this.uid = payload.path[payload.path.length-1]; + this.type = payload.type; + this.category = payload.category; + this.description = payload.description; + this.url = payload.url; + this.portsIn = []; + this.portsOut = []; + this.autoSelected = false; + this.hidden = true; + this.selectedPort = null; + this.totalPorts = 0; + + //TODO: we should change the id key to name + this.name = "widget" + payload.id; + + //title and subtitle of the widget + this.title = $("").addClass("widget-title").append(this.widgetName); + this.subtitle = $("").addClass("widget-subtitle").append(this.type); + + //info button + var info = $("").attr("src","images/ico_info.png").addClass("info"); + info.click(function(event) { + window.modal.show(instance.raw, instance.type); + }); + + //creating DOM elements + this.displayValue = $("").addClass("display-value"); + this.el = $("
").addClass("widget").attr("id",this.uid).appendTo("#container"); + $("
").addClass("widget-header").appendTo(this.el).append(this.title).append(this.subtitle).append(info); + $("
").addClass("widget-display").appendTo(this.el).addClass("widget-data").append(this.displayValue); + this.widgetPorts = $("
").addClass("widget-ports").appendTo(this.el); + + //putting the ports in the correct container + this.lports = $("
").addClass("port-container").appendTo("#"+this.uid + " .widget-ports"); + this.rports = $("
").addClass("port-container").appendTo("#"+this.uid + " .widget-ports"); + + //reading all in ports + var port; + payload.ports_in.forEach(function(element, index) { + port = new Port( + { + name:element["name"], + value:undefined, + type:"in", + port_index:element["base_port_idx"], + data_type:element["data_type"], + required:element["required"], + widget:instance, + key:instance.key + "_" + Widget.prototype.portsCount + }); + + instance.portsIn.push(port); + instance.totalPorts++; + port.el.on("port-select", instance.onPortSelect); + Widget.prototype.portsCount ++; + }); + + //reading all out ports + payload.ports_out.forEach(function(element, index) { + port = new Port( + { + name:element["name"], + value:undefined, + type:"out", + port_index:element["base_port_idx"], + data_type:element["data_type"], + required:element["required"], + widget:instance, + key:instance.key + "_" + Widget.prototype.portsCount + }); + instance.portsOut.push(port); + instance.totalPorts++; + port.el.on("port-select", instance.onPortSelect); + Widget.prototype.portsCount ++; + }); + + //Auto Adjusting the size of the Widget Container + var h = Math.max(this.portsIn.length, this.portsOut.length) * 26; + this.widgetPorts.css("height",h); + + } + + Widget.prototype.onPortSelect = function(event) { + event.widget.selectPort(event.port, event.value); + }; + + Widget.prototype.getUID = function() { + return this.uid; + }; + + //UPDATES THE VALUES OF ALL PORTS + Widget.prototype.update = function(type, data) { + + //getting the child ports of the widget + var port = (type === "deliver") ? this.portsIn[data.port_idx] : this.portsOut[data.port_idx]; + + //if the ports have no pair, skip + if(port.connections.length < 0) return; + + //found paired ports, update its value + var packet = data.packet; + var packetType = packet.packet_type; + + if(packetType === "empty"){ + port.update("empty"); + }else if(packetType === "int" || packetType === "float"){ + port.update(packet.payload.value) + }else if(packetType === "byte"){ + port.update(packet.payload); + }else if(packetType === "boolean"){ + port.update(packet.payload.toString()); + } + }; + + Widget.prototype.fadeIn = function(delay) { + var instance = this; + setTimeout(function(){ + instance.el.fadeIn(300); + },delay); + }; + + Widget.prototype.clearPorts = function() { + + this.portsIn.forEach(function(p, i){ + if(p.connections.length === 0){ + p.hidePort(); + } + }) + + this.portsOut.forEach(function(p, i){ + if(p.connections === 0){ + p.hidePort(); + } + }) + }; + + Widget.prototype.autoSelectPort = function(port) { + if(this.selectedPort == null){ + this.selectPort(port,port.value); + } + }; + + Widget.prototype.selectPort = function(port, value) { + + //dealing with repeated port click + if(port === this.selectedPort) { + port.unselect(); + this.unsubscribe(port); + this.selectedPort = null; + return; + } + + //clearing last selected port + if(this.selectedPort != null) { + this.selectedPort.unselect(); + this.unsubscribe(port); + } + + //selecting new port + this.clearDisplay().append(value); + this.selectedPort = port; + this.selectedPort.select(); + this.subscribe(port); + }; + + Widget.prototype.onPortValueChange = function(event) { + event.widget.clearDisplay().append(event.value); + }; + + Widget.prototype.subscribe = function(port) { + port.el.on('port-update', this.onPortValueChange); + }; + + Widget.prototype.unsubscribe = function(port) { + port.el.off('port-update', this.onPortValueChange); + this.clearDisplay(port.widget); + }; + + Widget.prototype.clearDisplay = function() { + var display = $("#" + this.uid + " .display-value").empty(); + return display; + }; + + Widget.prototype.getElement = function() { + return this.el; + }; + + Widget.prototype.getLeft = function() { + return this.lports; + }; + + Widget.prototype.getRight = function() { + return this.rports; + }; + + Widget.prototype.getInByIndex = function(index) { + return this.portsIn[index]; + }; + + Widget.prototype.getOutByIndex = function(index) { + return this.portsOut[index]; + }; + + window.Widget = Widget; + +})(window) diff --git a/src/bin/sol-fbp-runner/css/style.css b/src/bin/sol-fbp-runner/css/style.css new file mode 100644 index 000000000..3d5bce215 --- /dev/null +++ b/src/bin/sol-fbp-runner/css/style.css @@ -0,0 +1,192 @@ +/* + * This file is part of the Soletta Project + * + * Copyright (C) 2015 Intel Corporation. All rights reserved. + * + * 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. + */ +* { + font-family: Arial, Helvetica, sans-serif; +} +body { + background-color: #4C5155; +} +h1 { + background-color: blue; +} +body { + margin: 0px; + border: 0px; +} +.widget { + width: 180px; + margin: 10px; + overflow: hidden; + float: left; +} +.widget-header { + background-color: #2D3237; + color: white; + width: 100%; + height: 46px; + padding-left: 10px; + padding-top: 10px; +} +.widget-title { + width: 140px; + overflow: hidden; + color: white; + font-size: 1.3em !important; + display: block; +} +.widget-subitle { + width: 140px; + height: 15px; + overflow: hidden; + font-size: 8px !important; + display: block; + background-color: red; +} +.widget-display { + background-Color: white; + width: 100%; + height: 120px; +} +.display-value { + display: block; + word-wrap: break-word; + margin-left: 30px; + margin-right: 30px; + padding-top: 20px; + color: #2D3237; +} +.widget-data { + font-size: 2em; +} +.widget-ports { + width: 100%; + background-color: #777E83; +} +.port { + width: 85px; + /*border-bottom: 1px solid @gray;*/ + color: white; + font-size: .6em; + padding: 8px 0; + cursor: pointer; + overflow: hidden; +} +.port.in { + background-color: #777E83; + float: left; + text-align: left; + border-right: 1px solid white; + padding-left: 8px; +} +.port.out { + background-color: #777E83; + float: right; + text-align: right; + border-left: 1px solid white; + margin-left: -4px; + padding-right: 8px; +} +.port.selected { + background-color: #2D3237; +} +.port.paired-slave { + background-color: #E71C4C; +} +.port.paired-master { + background-color: #39CD89; +} +.port.hidden { + opacity: 0.2; + cursor: default; +} +.port-container { + width: 50%; + min-height: 50px; + float: left; + overflow: hidden; +} +.info { + float: right; + border: 0; + cursor: pointer; + width: 16px; + height: 16px; + margin-right: 18px; + margin-top: -18px; +} +.close { + float: right; + border: 0; + cursor: pointer; + width: 24px; + height: 24px; + -webkit-filter: invert(100%); + filter: invert(100%); +} +.modal { + width: 750px; + height: 500px; + overflow: hidden; + position: fixed; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + background-color: white; +} +.modal-header { + background-color: #7595C3; + height: 30px; + padding: .5em; + overflow: hidden; +} +.modal-title { + font-size: 2em; + color: white; + margin: 15px; +} +#shield { + position: fixed; + width: 100%; + height: 100%; + background-color: rgba(0, 0, 0, 0.7); +} +pre { + position: absolute; + height: 455px; + width: 735px; + left: 15px; + background-color: white; + color: #2D3237; + overflow-y: scroll; + top: 30px; +} +.non-selectable { + -webkit-touch-callout: none; + /* iOS Safari */ + -webkit-user-select: none; + /* Chrome/Safari/Opera */ + -khtml-user-select: none; + /* Konqueror */ + -moz-user-select: none; + /* Firefox */ + -ms-user-select: none; + /* Internet Explorer/Edge */ + user-select: none; + /* Non-prefixed version, currently + not supported by any browser */ +} diff --git a/src/bin/sol-fbp-runner/images/ico_close.png b/src/bin/sol-fbp-runner/images/ico_close.png new file mode 100644 index 0000000000000000000000000000000000000000..b8cfbb37ee22938184918fe6bf4ca836ed00ffbb GIT binary patch literal 23693 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 literal 0 HcmV?d00001 diff --git a/src/bin/sol-fbp-runner/web-inspector.css b/src/bin/sol-fbp-runner/web-inspector.css index 9154bd652..18430e8b6 100644 --- a/src/bin/sol-fbp-runner/web-inspector.css +++ b/src/bin/sol-fbp-runner/web-inspector.css @@ -16,173 +16,37 @@ * limitations under the License. */ -td.fbp-node-ports-in, td.fbp-node-ports-out { - min-width: 300pt; -} - -div.fbp-node-port-names-in, div.fbp-node-port-names-out { - font-family: monospace; - color: #333; -} - -div.fbp-node-port-description-in, div.fbp-node-port-description-out { - font-family: sans-serif; - font-size: smaller; - color: #999; -} - -div.fbp-node-port-data_type-in, div.fbp-node-port-data_type-in, span.fbp-packet { - font-family: monospace; -} - -span.fbp-packet-type-rgb-preview { - margin: 2pt 2pt 2pt 2pt; - padding: 2pt 2pt 2pt 2pt; - display: inline-block; - width: 10pt; - height: 10pt; - border: 1px solid black; -} - -div.fbp-node-port-required-true { - font-weight: bold; -} - -span.fbp-packet-type-int { - color: #0000aa; -} - -span.fbp-packet-type-float { - color: #aa00aa; -} - -span.fbp-packet-type-string { - color: #aa7700; -} - -span.fbp-packet-type-error { - color: #ff0000; -} - -span.fbp-packet-type-boolean { - color: #0077aa; -} - -table#fbp-container-inspector { - width: 100%; -} - -td.fbp-node-ports-in, td.fbp-node-ports-out { - padding-top: 30pt; - vertical-align: top; -} - -td.fbp-node-info h3 { - height: 30pt; - font-size: 1em; -} - -table.fbp-log { - width: 100%; -} - -table.fbp-log thead, table.fbp-log tbody { - display: block; -} - -table.fbp-log tbody { - height: 100pt; - overflow-y: auto; - overflow-x: auto; -} - -table.fbp-log tr td { - font-family: monospace; - font-size: smaller; -} - -td.fbp-log-timestamp { - text-align: right; -} - -td.fbp-node-info { - vertical-align: top; - min-width: 300pt; - border: 1px solid #ccc; - background-color: #d9d9d9; -} - -td.fbp-node-info dl { - font-size: x-small; -} - -td.fbp-node-info dl dt { - color: #666; - float: left; - clear: left; -} - -td.fbp-node-info dl dt:after { - content: ": "; -} - -td.fbp-node-info dl dd { - color: #333; - padding: 0 0 0.5em 0; - -webkit-margin-start: 10pt; -} - -span.fbp-node-option-type-rgb-preview { - margin: 1pt 1pt 1pt 1pt; - padding: 1pt 1pt 1pt 1pt; - display: inline-block; - width: 10pt; - height: 8pt; - border: 1px solid black; -} - -div.fbp-node-port-info-in, div.fbp-node-port-info-out, div.fbp-node-options-info { -} - -div.fbp-node-port-container-in, div.fbp-node-port-container-out { - margin: 1pt 1pt 1pt; - padding: 2pt 2pt 2pt; -} - -div.fbp-node-port-name-in { - margin: 1pt 1pt 1pt; - padding: 2pt 2pt 2pt; - float: right; - clear: right; - background-color: #d9d9d9; - border: 1px solid #ccc; -} - -div.fbp-node-port-value-in { - float: right; -} - -div.fbp-node-port-name-out { - margin: 1pt 1pt 1pt; - padding: 2pt 2pt 2pt; - float: left; - clear: left; - background-color: #d9d9d9; - border: 1px solid #ccc; -} - -div.fbp-node-port-value-out { - float: left; -} - -div.fbp-node-port-data_type-in-float div div div.fbp-node-port-name-in, div.fbp-node-port-data_type-out-float div div div.fbp-node-port-name-out { - background-color: #ff66ff; -} - -div.fbp-node-port-data_type-in-int div div div.fbp-node-port-name-in, div.fbp-node-port-data_type-out-int div div div.fbp-node-port-name-out { - background-color: #6666ff; -} - -div.fbp-node-port-data_type-in-boolean div div div.fbp-node-port-name-in, div.fbp-node-port-data_type-out-boolean div div div.fbp-node-port-name-out { - background-color: #66aaff; -} +.widget{ + background-color: #C0C0C0; + width:350px; + height:360px; + float:left; + margin:10px; + overflow: hidden;; + border-radius: 10px; +} +.widgetHeader{ + background-Color:#A0A0A0; + width: 350px; + height:50px; + padding-left: 10px; + padding-top: 10px +} +.widgetData{ + font-size: 3em; +} + +.port-in{ + width:100px; + height:30px; + background-color: #00FF00; + margin: 1px; + padding: 4px; +} +.port-out{ + width:100px; + height:30px; + background-color: #FF0000; + margin: 1px; + padding: 4px; +} \ No newline at end of file diff --git a/src/bin/sol-fbp-runner/web-inspector.html b/src/bin/sol-fbp-runner/web-inspector.html index a0f83ef8e..af80e96fd 100644 --- a/src/bin/sol-fbp-runner/web-inspector.html +++ b/src/bin/sol-fbp-runner/web-inspector.html @@ -1,57 +1,21 @@ - - - - - - -
- - -
-
- - - - - - - - -
Input PortsNodeOutput Ports
-
-
-
- -
-

Flow Packets Sent

- - - - - - - - - -
TimestampNodePortPacket
-
-
-
- -
-

Flow Packets Delivered

- - - - - - - - - -
TimestampNodePortPacket
-
- + + + + + + + + + + +
+ +
+
+ + + diff --git a/src/bin/sol-fbp-runner/web-inspector.js b/src/bin/sol-fbp-runner/web-inspector.js index e80f2630b..98f8dba5d 100644 --- a/src/bin/sol-fbp-runner/web-inspector.js +++ b/src/bin/sol-fbp-runner/web-inspector.js @@ -16,451 +16,178 @@ * limitations under the License. */ +var widgets = []; +var nodeId = -1; +var isCleared = false; +var max = 2; +var count = 0; + + +String.prototype.replaceAll = function(search, replacement) { + var target = this; + return target.replace(new RegExp(search, 'g'), replacement); +}; + + var FBPInspector = function () { - this.html = { - nodes: this.el("fbp-inspector-nodes"), - logSent: this.el("fbp-log-sent"), - logDelivered: this.el("fbp-log-delivered"), - start: this.el("fbp-start"), - finish: this.el("fbp-finish"), - }; if (!window.EventSource) { alert("Your browser does not support server-side-events (SSE): no EventSource()."); - this.html.start.disabled = true; - this.html.finish.disabled = true; return; } - - this.clearTableRows(this.html.nodes); - this.html.start.disabled = false; - this.html.finish.disabled = true; }; FBPInspector.prototype.el = function (id) { return document.getElementById(id); }; -FBPInspector.prototype.clearTableRows = function (domElem) { - while (domElem.rows.length) - domElem.deleteRow(0); -}; - -FBPInspector.prototype.addTableRow = function (domElem, idx, rowSpec) { - var r = domElem.insertRow(idx); - for (var i = 0; i < rowSpec.length; i++) { - var c = r.insertCell(i); - var rs = rowSpec[i]; - if (rs.content) - c.textContent = rs.content; - if (rs.html) - c.innerHTML = rs.html; - c.className = rs.className; - }; -}; FBPInspector.prototype.start = function () { - this.nodes = {}; - this.events = []; this.source = new EventSource(window.location.origin + '/events'); this.source.fbpInspector = this; this.source.onopen = function (e) { - this.fbpInspector.clearTableRows(this.fbpInspector.html.nodes); - this.fbpInspector.logDeliveredClear(); - this.fbpInspector.logSentClear(); - this.fbpInspector.html.start.disabled = true; - this.fbpInspector.html.finish.disabled = false; + //TODO }; this.source.onerror = function (e) { this.fbpInspector.finish(); }; + var ev; this.source.onmessage = function (e) { - var ev = JSON.parse(e.data); - this.fbpInspector.feedEvent(ev); - }; -}; - -FBPInspector.prototype.logSentClear = function () { - this.clearTableRows(this.html.logSent); -}; - -FBPInspector.prototype.logDeliveredClear = function () { - this.clearTableRows(this.html.logDelivered); -}; - -FBPInspector.prototype.getNodeName = function (node) { - if (this.nodes[node]) - return this.nodes[node].creation.payload.id || node; - return node; -}; - -FBPInspector.prototype.renderPacket = function (packet) { - var packet_type = packet.packet_type; - var payload = packet.payload; - var extraClass = ""; - var body = ""; - - if (packet_type == "empty" || - packet_type == "any") { - } else if (packet_type == "boolean" || - packet_type == "byte" || - packet_type == "timestamp") { - body = payload; - } else if (packet_type == "string") { - body = payload; // TODO: escape - } else if (payload instanceof Array) { - extraClass += " fbp-packet-composed"; - for (var i = 0; i < payload.length; i++) { - if (i > 0) - body += ', '; - body += this.renderPacket(payload[i]); - } - } else { - var keys = []; - for (var key in payload) { - if (!payload.hasOwnProperty(key)) - continue; - keys.push(key); + try { + feed(e.data); + } catch (err){ + var fixed = e.data.toString().replaceAll(":inf,",':"inf",'); + feed(fixed); } - - keys.sort(); - - for (var i = 0; i < keys.length; i++) { - var key = keys[i]; - var value = payload[key]; - if (i > 0) - body += ', '; - body += '' + - '' + key + ': ' + // TODO: escape - '' + value + '' + // TODO: escape - ''; - } - - if (packet_type == "rgb" && payload) { - var r = Math.round(255 * (payload.red / payload.red_max)); - var g = Math.round(255 * (payload.green / payload.green_max)); - var b = Math.round(255 * (payload.blue / payload.blue_max)); - - body += ''; + function feed(data){ + ev = JSON.parse(data); + this.fbpInspector.feedEvent(ev); } - } - - return '' + body + ''; + }; }; -FBPInspector.prototype.renderOptionValue = function (data_type, value) { - var extraClass = ""; - var body = ""; - - if (data_type == "boolean" || - data_type == "byte" || - data_type == "timestamp") { - body = value; - } else if (data_type == "string") { - body = value; // TODO: escape - } else { - var keys = []; - for (var key in value) { - if (!value.hasOwnProperty(key)) - continue; - keys.push(key); - } - - keys.sort(); - - for (var i = 0; i < keys.length; i++) { - var key = keys[i]; - var val = value[key]; - if (i > 0) - body += ', '; - body += '' + - '' + key + ': ' + // TODO: escape - '' + val + '' + // TODO: escape - ''; - } - - if (data_type == "rgb" && value) { - var r = Math.round(255 * (value.red / value.red_max)); - var g = Math.round(255 * (value.green / value.green_max)); - var b = Math.round(255 * (value.blue / value.blue_max)); - - body += ''; - } +FBPInspector.prototype.finish = function () { + if (this.source) { + this.source.close(); + this.source = null; } +}; - return '' + body + ''; -} - -FBPInspector.prototype.renderNodeInfo = function (node) { - var n = this.nodes[node]; - var payload = n.creation.payload; - var creationTimestamp = n.creation.timestamp; - var destructionTimestamp = n.destruction; +FBPInspector.prototype.logSentClear = function () {}; - // TODO: hadle escaping of html +FBPInspector.prototype.logDeliveredClear = function () {}; - var html = '

' + this.getNodeName(node) + '

' + - '
'; +FBPInspector.prototype.feedEvent = function (data) { - if (payload.type) { - html += '
type
' + - '
' + payload.type + '
'; - } + if (data.event == "open") { - if (payload.category) { - html += '
category
' + - '
' + payload.category + '
'; - } + if(data.payload.id.indexOf(".fbp") > -1) return; - if (payload.description) { - html += '
description
' + - '
' + payload.description + '
'; - } + var widget = new Widget(data.payload, widgets.length); + widgets.push(widget); - if (payload.author) { - html += '
author
' + - '
' + payload.author + '
'; - } + } else if (data.event == "close") { + //TODO - if (payload.url) { - html += '
url
' + - '
' + payload.url + '
'; - } + } else if (data.event == "connect") { + connectPorts(data.payload); - if (payload.license) { - html += '
license
' + - '
' + payload.license + '
'; - } + } else if (data.event == "disconnect") { + //TODO - if (payload.version) { - html += '
version
' + - '
' + payload.version + '
'; - } + } else if (data.event == "send") { - if (payload.options) { - html += '
options
' + - '
' + - '' + - '' + - '' + - '' + - ''; - - for (var i = 0; i < payload.options.length; i++) { - var o = payload.options[i]; - // TODO: hide fbp-node-options-info by default, dettach it and make a floating window on mouse-over row - html += '' + - '' + - '' + - '' + - ''; + if (!isCleared) { + widgets.forEach(function(item, index) { + item.clearPorts(); + }) + isCleared = true; } - html += '
NameValueInfo
' + o.name + '' + this.renderOptionValue(o.data_type, o.value) + '' + - '
' + - '
name
'+ o.name + '
' + - '
required
'+ o.required + '
' + - '
type
' + o.data_type + '
' + - '
default value
' + this.renderOptionValue(o.data_type, o.defvalue) + '
' + - '
description
' + o.description + '
' + - '
'; - } - - html += '
creation
' + - '
' + creationTimestamp + '
'; - - if (destructionTimestamp) { - html += '
destruction
' + - '
' + destructionTimestamp + '
'; - } - - return html; -}; - -FBPInspector.prototype.renderNodePorts = function (node, direction, descriptions) { - var n = this.nodes[node]; - var html = ""; + nodeId = data.payload.node; + widgets.forEach(function(widget, index) { + if (widget.getUID() === nodeId) { - // TODO: hadle escaping of html - - for (var i = 0; i < descriptions.length; i++) { - var pdesc = descriptions[i]; - var uid_prefix = direction + '-' + node + '-'; - var uid = uid_prefix + pdesc.base_port_idx; - - html += '
'; - - html += '
'; - - if (pdesc.array_size == 0) { - var port_uid = uid; - html += '
' + pdesc.name + '
' + - '
'; - } else { - for (var idx = 0; idx < pdesc.array_size; idx++) { - var port_uid = uid_prefix + pdesc.base_port_idx + idx; - html += '
' + pdesc.name + '[' + idx + ']
' + - '
'; + widget.update("send",data.payload); } - } + }); - // TODO: hide fbp-node-port-info by default, dettach it and make a floating window on mouse-over row - html += '
'; + } else if (data.event == "deliver") { //RECEIVED - var infoName = pdesc.name; - if (pdesc.array_size) { - infoName += '[' + pdesc.array_size + ']'; - } - - html += '
name
' + infoName + '
'; - html += '
required
' + pdesc.required + '
'; - html += '
type
' + pdesc.data_type + '
'; - html += '
description
' + pdesc.description + '
'; - - html += '
'; + nodeId = data.payload.node; + widgets.forEach(function(widget, index){ + if (widget.getUID() === nodeId) { + widget.update("deliver",data.payload); + } + }); } - return html; -}; - -FBPInspector.prototype.feedEvent = function (ev) { - this.events.push(ev); - - var payload = ev.payload; - - if (ev.event == "open") { - var path = payload.path; - var node = path[path.length - 1]; - this.nodes[node] = { - creation: ev, - destruction: null, - inputConnections: [], - outputConnections: [], - }; - - var row = this.html.nodes.insertRow(this.html.nodes.rows.length - 1); - row.id = "fbp-node-" + node; - row.className = "fbp-node"; - - var cell; - - cell = row.insertCell(0); - cell.id = "fbp-node-ports-in-" + node; - cell.className = "fbp-node-ports-in"; - cell.innerHTML = this.renderNodePorts(node, 'in', ev.payload.ports_in); - - cell = row.insertCell(1); - cell.id = "fbp-node-info-" + node; - cell.className = "fbp-node-info"; - cell.innerHTML = this.renderNodeInfo(node); - cell = row.insertCell(2); - cell.id = "fbp-node-ports-out-" + node; - cell.className = "fbp-node-ports-out"; - cell.innerHTML = this.renderNodePorts(node, 'out', ev.payload.ports_out); - - } else if (ev.event == "close") { - var node = payload; - - this.nodes[node].destruction = ev.timestamp; - - this.el("fbp-node-info-" + node).innerHTML += '
destruction
' + - '
' + ev.timestamp + '
'; - this.el("fbp-node-" + node).className += "fbp-node-closed"; - - } else if (ev.event == "connect") { - var srcConn = payload.src; - var dstConn = payload.dst; - - var srcNode = this.nodes[srcConn.node]; - var dstNode = this.nodes[dstConn.node]; - - srcNode.outputConnections.push(ev); - dstNode.inputConnections.push(ev); - - // TODO: show - - } else if (ev.event == "disconnect") { - var srcConn = payload.src; - var dstConn = payload.dst; - - var srcNode = this.nodes[srcConn.node]; - var dstNode = this.nodes[dstConn.node]; + function compareNames(a,b) { + return a === b; + } - var i; + var dest; + var src; + function connectPorts(data) { - for (i = 0; i < srcNode.outputConnections.length; i++) { - var c = srcNode.outputConnections[i]; - if (c.payload.src.conn_id == srcConn.conn_id) - srcNode.outputConnections.splice(i, 1); + dest = { + node: data.dst.node, + idx: data.dst.port_idx, + port: null } - for (i = 0; i < dstNode.inputConnections.length; i++) { - var c = dstNode.inputConnections[i]; - if (c.payload.dst.conn_id == dstConn.conn_id) - dstNode.inputConnections.splice(i, 1); + src = { + node: data.src.node, + idx: data.src.port_idx, + port: null } + //getting the source port + widgets.forEach(function(widget, index) { - // TODO: show - - } else if (ev.event == "send") { - var packetHtml = this.renderPacket(payload.packet); + if (widget.getUID() === src.node) { + src.port = widget.getOutByIndex(src.idx); + } else if (widget.getUID() === dest.node) { + dest.port = widget.getInByIndex(dest.idx) + }; - this.addTableRow(this.html.logSent, -1, - [{content: ev.timestamp, className: "fbp-log-timestamp"}, - {content: this.getNodeName(payload.node), className: "fbp-log-node"}, - {content: payload.port_name || payload.port_idx, className: "fbp-log-port"}, - {html: packetHtml, className: "fbp-log-packet"}]); + }); - var valEl = this.el("fbp-node-port-value-out-" + payload.node + "-" + payload.port_idx); - if (valEl) { - valEl.innerHTML = packetHtml; - // TODO: plot graph over timestamp + if (dest.port !== null) { + dest.port.connect(src.port); + dest.port.getElement().on("port-over", onPortMouseEvent); + dest.port.getElement().on("port-out", onPortMouseEvent); } - } else if (ev.event == "deliver") { - var packetHtml = this.renderPacket(payload.packet); - - this.addTableRow(this.html.logDelivered, -1, - [{content: ev.timestamp, className: "fbp-log-timestamp"}, - {content: this.getNodeName(payload.node), className: "fbp-log-node"}, - {content: payload.port_name || payload.port_idx, className: "fbp-log-port"}, - {html: packetHtml, className: "fbp-log-packet"}]); - var valEl = this.el("fbp-node-port-value-in-" + payload.node + "-" + payload.port_idx); - if (valEl) { - valEl.innerHTML = packetHtml; - // TODO: plot graph over timestamp + if (src.port !== null) { + src.port.connect(dest.port); + src.port.getElement().on("port-over", onPortMouseEvent); + src.port.getElement().on("port-out", onPortMouseEvent); } } -}; - -FBPInspector.prototype.finish = function () { - if (this.source) { - this.source.close(); - this.source = null; + function onPortMouseEvent(event){ + if (event.type == "port-over") { + event["connections"].forEach(function(item, index) { + $("#"+item).addClass("paired-slave"); + }); + $("#"+event["port"].key).addClass("paired-master"); + } else { + event["connections"].forEach(function(item, index) { + $("#"+item).removeClass("paired-slave"); + }); + $("#"+event["port"].key).removeClass("paired-master"); + } } - - this.html.start.disabled = false; - this.html.finish.disabled = true; }; window.onload = function () { + + window.modal = new Modal(); + window.fbpInspector = new FBPInspector(); + fbpInspector.start(); + };