/** * CertainTrust SDK * * Implements the computational trust model "CertainTrust" * in JavaScript. * See for further details. * * * Telecooperation Department, Technische Universität Darmstadt * * * Prof. Dr. Max Mühlhäuser * Florian Volk * * * @author Daniel Dieth * @author David Kalnischkies * @author Maria Pelevina * @version 1.0 */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ //########################################################################################## // CertainTrustTViz constructor //########################################################################################## var CertainTrustTViz = function(data, config) { this.NR = CertainTrustTVizElement.length(); CertainTrustTVizElement.push(this); this.certainTrusts = []; this.add(data); // set sane defaults for config if nothing is set if (config === undefined) config = {}; if (config.id === undefined) config.id = 'certaintrust-tviz-' + this.NR; // design your widget if (config.canvas === undefined) config.canvas = {}; if (config.canvas.height === undefined) config.canvas.height = 440; if (config.canvas.width === undefined) config.canvas.width = 440; if (config.canvas.inner === undefined) config.canvas.inner = 30; if (config.middle === undefined) config.middle = 'AVERAGE'; if (config.onClick === undefined) config.onClick = undefined; this.ID = config.id; this.config = config; var element = document.createElement('div'); element.setAttribute('id', this.ID); var dom = this.certainTrusts[0]._insertElement(config, element); this.update(); if (dom !== undefined) return dom; }; CertainTrustTViz.prototype.update = function() { var xmlns = 'http://www.w3.org/2000/svg'; var dotx = this.config.canvas.height / 2; var doty = this.config.canvas.width / 2; var rotate = 360 - 45; var circle_y = this.config.canvas.inner; var circle_height = dotx - (2 * circle_y); var rotationstep = 360 / this.certainTrusts.length; var element = document.getElementById(this.ID); // see if we have to redraw thanks to an added/removed CertainTrust element if (element.hasChildNodes() === true) { var old_arcs = document.getElementById(this.ID + '-arcs'); if (old_arcs.childNodes.length !== this.certainTrusts.length) element.removeChild(element.firstChild); } var arcs = document.createElementNS(xmlns, 'g'); arcs.setAttribute('stroke', 'none'); arcs.setAttribute('class', 'certaintrust-tviz-arcs'); arcs.setAttribute('id', this.ID + '-arcs'); // first time run or removed by the if above: full build if (element.hasChildNodes() === false) { // prepare the various elements we need var axes = document.createElementNS(xmlns, 'g'); axes.setAttribute('fill', 'none'); axes.setAttribute('stroke', 'grey'); axes.setAttribute('class', 'certaintrust-tviz-axes'); var desc = document.createElementNS(xmlns, 'g'); for (var a = 0; a < this.certainTrusts.length; ++a) { var rotation = (rotate + (rotationstep * a)) % 360; // an axis for each arc to show of how big each arc could be var axis = this._generateAxis(xmlns, dotx, doty, circle_height, circle_y, rotation); axes.appendChild(axis); // the arc representing the CertainTrust element var arc = this._generateArc(xmlns, this.certainTrusts[a], dotx, doty, circle_height, circle_y, rotation, rotationstep); arcs.appendChild(arc); // label for each arc var label = this._generateArcLabel(xmlns, this.certainTrusts[a], dotx, doty, circle_height, circle_y, rotation, rotationstep); desc.appendChild(label); } if (this.config.middle !== 'NONE') { var total = this._generateTotalInTheMiddle(xmlns, dotx, doty); desc.appendChild(total); } // draw circles to indicate how large the arcs could be var circles = this._generateCircles(xmlns, circle_y, circle_height); // our new tviz var svg = document.createElementNS(xmlns, 'svg'); svg.setAttribute('height', this.config.canvas.height); svg.setAttribute('width', this.config.canvas.width); svg.appendChild(circles); svg.appendChild(arcs); svg.appendChild(axes); svg.appendChild(desc); // finally, insert tviz element.appendChild(svg); } else { // we have most of the image, so just change what has changed element = element.firstChild; // get the new arcs set for (var a = 0; a < this.certainTrusts.length; ++a) { var rotation = (rotate + (rotationstep * a)) % 360; var arc = this._generateArc(xmlns, this.certainTrusts[a], dotx, doty, circle_height, circle_y, rotation, rotationstep); arcs.appendChild(arc); } // find the old arcs set and exchange it with the new set var old_arcs = document.getElementById(this.ID + '-arcs'); element.insertBefore(arcs, old_arcs); element.removeChild(old_arcs); if (this.config.middle !== 'NONE') { // and now change the text of label in the middle var total = document.getElementById(this.ID + '-middle'); total.firstChild.nodeValue = this._calcMiddleLabel(this.certainTrusts); } } }; CertainTrustTViz.prototype.add = function() { for (var i = 0; i < arguments.length; ++i) { var ct = arguments[i]; if (ct instanceof Array) { for (var j = 0; j < ct.length; ++j) { var c = ct[j]; this.certainTrusts.push(c); c.addObserver(this); } } else { this.certainTrusts.push(ct); ct.addObserver(this); } } // don't trigger on first call from constructor if (this.ID !== undefined) this.update(); }; CertainTrustTViz.prototype.remove = function() { for (var i = 0; i < arguments.length; ++i) { var ct = arguments[i]; if (ct instanceof CertainTrust) { for (var j = 0; j < ct.length; ++j) { for (var k = 0; k < this.certainTrusts.length; ++k) { if (this.certainTrusts[k] !== ct[j]) continue; ct[j].deleteObserver(this); this.certainTrusts.splice(k, 1); break; } } } else { for (var k = 0; k < this.certainTrusts.length; ++k) { if (this.certainTrusts[k].getName() !== ct) continue; this.certainTrusts[k].deleteObserver(this); this.certainTrusts.splice(k, 1); break; } } } this.update(); }; CertainTrustTViz.prototype._generateAxis = function(xmlns, dotx, doty, circle_height, circle_y, rotation) { var point = this.certainTrusts[0]._pointOnCircle(dotx, doty, rotation, circle_y); var axis = document.createElementNS(xmlns, 'g'); axis.setAttribute('fill', 'none'); axis.setAttribute('stroke', 'grey'); var placement; if (rotation >= 270 && rotation < 360 || rotation >= 0 && rotation < 90) { axis.setAttribute('transform', 'translate(' + point[1] + ',' + point[0] + ') rotate(' + (rotation) + ')'); axis.setAttribute('class', 'certaintrust-tviz-axis-left'); placement = -1; } else { axis.setAttribute('transform', 'translate(' + point[1] + ',' + point[0] + ') rotate(' + (rotation - 180) + ')'); axis.setAttribute('class', 'certaintrust-tviz-axis-right'); placement = 1; } var axis_line = document.createElementNS(xmlns, 'line'); axis_line.setAttribute('fill', 'inherit'); axis_line.setAttribute('stroke', 'inherit'); axis_line.setAttribute('x1', 0); axis_line.setAttribute('y1', 0); axis_line.setAttribute('x2', 0); axis_line.setAttribute('y2', placement * circle_height); axis.appendChild(axis_line); for (var t = 0; t < 6; ++t) { var y = placement * (circle_height * (t / 5)); var tick_line = document.createElementNS(xmlns, 'line'); tick_line.setAttribute('fill', 'inherit'); tick_line.setAttribute('stroke', 'inherit'); tick_line.setAttribute('x1', 0); tick_line.setAttribute('y1', y); tick_line.setAttribute('x2', placement * 6); tick_line.setAttribute('y2', y); var tick_text = document.createElementNS(xmlns, 'text'); tick_text.setAttribute('fill', 'grey'); tick_text.setAttribute('x', placement * 9); tick_text.setAttribute('y', y); tick_text.setAttribute('dy', '.5em'); tick_text.setAttribute('font-size', '.5em'); var msg = document.createTextNode(t * 20); tick_text.appendChild(msg); var tick = document.createElementNS(xmlns, 'g'); tick.setAttribute('fill', 'inherit'); tick.setAttribute('stroke', 'inherit'); tick.appendChild(tick_line); tick.appendChild(tick_text); axis.appendChild(tick); } return axis; }; CertainTrustTViz.prototype._generateArc = function(xmlns, certainTrust, dotx, doty, circle_height, circle_y, rotation, rotationstep) { var arc_width = rotationstep * certainTrust.getC(); var arc_free = (rotationstep - arc_width) / 2; var arc_low_left = certainTrust._pointOnCircle(dotx, doty, (rotation + arc_free), circle_y); var arc_low_right = certainTrust._pointOnCircle(dotx, doty, (rotation + rotationstep - arc_free), circle_y); var arc_height = (circle_height * certainTrust.getT()) + circle_y; var arc_high_left = certainTrust._pointOnCircle(dotx, doty, (rotation + arc_free), arc_height); var arc_high_right = certainTrust._pointOnCircle(dotx, doty, (rotation + rotationstep - arc_free), arc_height); var color = certainTrust._getColor(certainTrust.getC(), certainTrust.getT(), certainTrust.getF()); var arc = document.createElementNS(xmlns, 'path'); arc.setAttribute('fill', 'rgb(' + Math.round(color[0]) + ',' + Math.round(color[1]) + ',' + Math.round(color[2]) + ')'); arc.setAttribute('stroke', 'inherit'); arc.setAttribute('d', 'M' + arc_low_left[1] + ' ' + arc_low_left[0] + 'A' + circle_y + ',' + circle_y + ' 0 0,1 ' + arc_low_right[1] + ',' + arc_low_right[0] + 'L' + arc_high_right[1] + ' ' + arc_high_right[0] + 'A' + arc_height + ',' + arc_height + ' 0 0,0 ' + arc_high_left[1] + ',' + arc_high_left[0] + 'z'); arc.setAttribute('title', 'Trust: ' + certainTrust.getT() + '\nCertainty: ' + certainTrust.getC()); if (this.config.onClick !== undefined) { var onClick = this.config.onClick; arc.addEventListener("click", function(e) { onClick(certainTrust); }, false); } return arc; }; CertainTrustTViz.prototype._generateArcLabel = function(xmlns, certainTrust, dotx, doty, circle_height, circle_y, rotation, rotationstep) { var label = document.createElementNS(xmlns, 'text'); label.setAttribute('fill', 'black'); var outerpoint = this.certainTrusts[0]._pointOnCircle(dotx, doty, (rotation + (rotationstep / 2)), (circle_height + circle_y)); label.setAttribute('x', outerpoint[1]); label.setAttribute('y', outerpoint[0]); label.setAttribute('dy', '.5em'); label.setAttribute('text-anchor', 'middle'); var msg = document.createTextNode(certainTrust.getName()); label.appendChild(msg); return label; }; CertainTrustTViz.prototype._generateTotalInTheMiddle = function(xmlns, dotx, doty) { var total = document.createElementNS(xmlns, 'text'); total.setAttribute('fill', 'grey'); total.setAttribute('x', dotx); total.setAttribute('y', doty); total.setAttribute('dy', '.5em'); total.setAttribute('text-anchor', 'middle'); total.setAttribute('id', this.ID + '-middle'); var msg = document.createTextNode(this._calcMiddleLabel(this.certainTrusts)); total.appendChild(msg); return total; }; CertainTrustTViz.prototype._generateCircles = function(xmlns, circle_y, circle_height) { var circles = document.createElementNS(xmlns, 'g'); circles.setAttribute('fill', 'none'); circles.setAttribute('stroke', 'lightgrey'); circles.setAttribute('class', 'certaintrust-tviz-circles'); for (var i = 0; i < 11; ++i) { var circle = document.createElementNS(xmlns, 'circle'); circle.setAttribute('cx', '50%'); circle.setAttribute('cy', '50%'); var r = circle_y + (circle_height * (i / 10)); circle.setAttribute('r', r); circle.setAttribute('fill', 'inherit'); circle.setAttribute('stroke', 'inherit'); circles.appendChild(circle); } return circles; }; // Calculate the average over all included CertainTrusts CertainTrustTViz.prototype._calcMiddleLabel = function(indata) { if (this.config.middle === "NONE") return ""; else if (this.config.middle === "AVERAGE") { var avg = 0.0; for (var i = 0; i < indata.length; i++) { avg += indata[i].getExpectation(); } return (Math.round(avg/indata.length*1000)/10) + "%"; } else if (this.config.middle === "AND") { var result = indata[0]; for (var i = 1; i < indata.length; i++) { result = result.AND(indata[i]); } return (Math.round(result.getExpectation()*1000)/10) + "%"; } else return this.config.middle(indata); }; // global var for storing and accessing all the widgets var CertainTrustTVizElement = { _elements: [], ById: function(id) { for (var i = 0; i < CertainTrustTVizElement._elements.length; ++i) { if (CertainTrustTVizElement._elements[i].ID === id) return CertainTrustTVizElement._elements[i]; } return null; }, ByNr: function(nr) { return CertainTrustTVizElement._elements[nr]; }, push: function(cte) { CertainTrustTVizElement._elements.push(cte); }, length: function() { return CertainTrustTVizElement._elements.length; } };