| // Copyright (c) 2000-2019 Ericsson Telecom AB Telecom AB // |
| // All rights reserved. This program and the accompanying materials are made available under the // |
| // terms of the Eclipse Public License v2.0 which accompanies this distribution, and is available at // |
| // https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html // |
| /////////////////////////////////////////////////////////////////////////////////////////////////////// |
| function LineDrawer(p_parentId, p_id, p_from, p_to, p_options) {
|
| "use strict";
|
|
|
| var v_this = this;
|
|
|
| // params
|
| var v_parentId = p_parentId;
|
| var v_id = p_id;
|
| var v_from = p_from;
|
| var v_to = p_to;
|
| var v_options = {
|
| "stroke" : "#456",
|
| "strokeWidth" : 3,
|
| "arrowForward" : true,
|
| "markerStroke" : "#456",
|
| "arrowHead" : "target",
|
| "z-index" : 0
|
| };
|
| var zIndex = 0;
|
| setOptions(p_options);
|
|
|
| var v_svg;
|
|
|
| function setOptions(p_options) {
|
| // copy given options to the default
|
| for (var key in p_options) {
|
| if (p_options.hasOwnProperty(key) && v_options.hasOwnProperty(key)) {
|
| v_options[key] = p_options[key];
|
| }
|
| }
|
|
|
| // settings whose value depends on others
|
| v_options.markerStroke = '-' + v_options.stroke.replace('#', '');
|
| }
|
|
|
| this.init = function() {
|
| createSVG();
|
| createMarkers();
|
| createPath();
|
| v_this.update();
|
| };
|
|
|
| function createSVG() {
|
| v_svg = $("#" + v_parentId).createSVGNode({
|
| "id": v_id,
|
| "position": "absolute",
|
| "style": "position: absolute; z-index: " + v_options["z-index"] + ";",
|
| "pointer-events": "none",
|
| "version": "1.1",
|
| "z-index": v_options["z-index"]
|
| });
|
| }
|
|
|
| function setZIndex(svgElement) {
|
| var amount;
|
| if (v_from.getZIndex != undefined) {
|
| amount = v_from.getZIndex();
|
| }
|
| if (v_to.getZIndex != undefined) {
|
| var newAmount = v_to.getZIndex();
|
| if (amount == undefined || newAmount > amount) {
|
| amount = newAmount;
|
| }
|
| }
|
| if (amount != undefined && amount != zIndex) {
|
| svgElement.setAttribute("z-index", amount);
|
| svgElement.style["z-index"] = amount;
|
| zIndex = amount;
|
| }
|
| };
|
|
|
| function createMarkers() {
|
| // the marker head descriptor
|
| var markerHead = {
|
| "id": v_id + "_head",
|
| "orient": "auto",
|
| "markerWidth": 4,
|
| "markerHeight": 4,
|
| "refX": 2,
|
| "refY": 2
|
| };
|
| var markerFoot = shallowcopy(markerHead);
|
| markerFoot.id = v_id + "_foot";
|
|
|
| // we also need the marker path descriptor
|
| var pathHead = {
|
| "fill": v_options.stroke,
|
| "stroke": v_options.stroke,
|
| "stroke-linejoin": "miter",
|
| "stroke-linecap": "round",
|
| "d": "M0,0 V4 L2,2 Z"
|
| };
|
| var pathFoot = shallowcopy(pathHead);
|
| pathFoot.d = "M4,0 V4 L2,2 Z";
|
|
|
| // finally we crete the defs element and fill it up
|
| var defs = v_svg.createSVGNode("defs");
|
| defs.createSVGNode("marker", markerHead)
|
| .createSVGNode("path", pathHead);
|
| defs.createSVGNode("marker", markerFoot)
|
| .createSVGNode("path", pathFoot);
|
| }
|
|
|
| function createPath() {
|
| var svgattrs = {
|
| "id": v_id + "_path",
|
| "fill": "none",
|
| "stroke": v_options.stroke,
|
| "stroke-width": v_options.strokeWidth,
|
| "stroke-linejoin": "round",
|
| "stroke-linecap": "round",
|
| "pointer-events": "none",
|
| "shape-rendering": "geometricPrecision",
|
| "stroke-antialiasing": "true"
|
| };
|
|
|
| if(!isIE11 && !isIE10) {
|
| // In IE10 and up, the url() virtual function crashes the SVG engine, hence nothing gets drawn in that <svg> element.
|
| if (v_options.arrowHead === "target") {
|
| svgattrs["marker-end"] = "url(#" + v_id + "_head)";
|
| } else if (v_options.arrowHead === "source") {
|
| svgattrs["marker-start"] = "url(#" + v_id + "_foot)";
|
| } else if (v_options.arrowHead === "both") {
|
| svgattrs["marker-end"] = "url(#" + v_id + "_head)";
|
| svgattrs["marker-start"] = "url(#" + v_id + "_foot)";
|
| }
|
| }
|
|
|
| v_svg.createSVGNode("path", svgattrs);
|
| }
|
|
|
| this.remove = function() {
|
| $("#" + v_id).remove();
|
| };
|
|
|
| function findMinOffsetFromList(list, element) {
|
| var minAmount = Math.abs(list[0].top - element.top) + Math.abs(list[0].left - element.left);
|
| var min = 0;
|
| for (var i = 1; i < list.length; ++i) {
|
| var amount = Math.abs(list[1].top - element.top) + Math.abs(list[1].left - element.left);
|
| if (amount < minAmount) {
|
| minAmount = amount;
|
| min = i;
|
| }
|
| }
|
| return [min, minAmount];
|
| }
|
|
|
| function getFinalCoordinates(sourceOffset, targetOffset) {
|
| var x1 = Math.round(sourceOffset.top);
|
| var y1 = Math.round(sourceOffset.left);
|
|
|
| var x2 = Math.round(targetOffset.top);
|
| if (x2 === x1) {
|
| x2++;
|
| }
|
| var y2 = Math.round(targetOffset.left);
|
| if (y2 === y1) {
|
| y2++;
|
| }
|
| return [x1, y1, x2, y2];
|
| }
|
|
|
| function getOffsetCoordinates() {
|
| if (!v_from.multiple && !v_to.multiple) {
|
| // only one source and one target
|
| return getFinalCoordinates(v_from.getOffset(), v_to.getOffset());
|
| } else if (v_from.multiple && !v_to.multiple) {
|
| // multiple sources and one target
|
| var list = v_from.getOffsets();
|
| var element = v_to.getOffset();
|
| var min = findMinOffsetFromList(list, element);
|
| return getFinalCoordinates(list[min[0]], element);
|
| } else if (!v_from.multiple && v_to.multiple) {
|
| // one surce and multiple targets
|
| var list = v_to.getOffsets();
|
| var element = v_from.getOffset();
|
| var min = findMinOffsetFromList(list, element);
|
| return getFinalCoordinates(element, list[min[0]]);
|
| } else {
|
| // multiple source and multiple targets
|
| var sourceList = v_from.getOffsets();
|
| var targetList = v_to.getOffsets();
|
| // assume the first is the minimum
|
| var minsource = 0;
|
| var min = findMinOffsetFromList(targetList, sourceList[0]);
|
| // we need the index of the target whose distance from the current source element is minimal
|
| var mintarget = min[0];
|
| // and the distance itself
|
| var minAmount = min[1];
|
| // find the real minimum from the rest
|
| for (var i = 1; i < sourceList.length; ++i) {
|
| var element = sourceList[i];
|
| min = findMinOffsetFromList(targetList, element);
|
| if (min[1] < minAmount) {
|
| minsource = i;
|
| mintarget = min[0];
|
| minAmount = min[1];
|
| }
|
| }
|
| return getFinalCoordinates(sourceList[minsource], targetList[mintarget]);
|
| }
|
| }
|
|
|
| function getStyle(fromX, fromY, toX, toY) {
|
| var d = 'M' + fromX + ',' + fromY + ' C';
|
|
|
| if (v_from.style == "vertical") {
|
| d += fromX + ',' + ( (fromY + toY) / 2 ) + ' ';
|
| } else {
|
| d += ( (fromX + toX) / 2 ) + ',' + fromY + ' ';
|
| }
|
|
|
| if (v_to.style == "vertical") {
|
| d += toX + ',' + ( (fromY + toY) / 2 );
|
| } else {
|
| d += ( (fromX + toX) / 2 ) + ',' + toY;
|
| }
|
|
|
| d += ' ' + toX + ',' + toY;
|
| return d;
|
| }
|
|
|
| this.update = function() {
|
| if ((v_from.isEnabled != undefined && !v_from.isEnabled()) || (v_to.isEnabled != undefined && !v_to.isEnabled())) {
|
| // check whether one of the endpoints is disabled
|
| $("#" + v_id).css("display", "none");
|
| return;
|
| } else {
|
| $("#" + v_id).css("display", "block");
|
| }
|
|
|
| var offsets = getOffsetCoordinates();
|
| var x1 = offsets[0];
|
| var y1 = offsets[1];
|
| var x2 = offsets[2];
|
| var y2 = offsets[3];
|
|
|
| // these are the only values we need, and we need them all
|
| // the bounding box position
|
| var boundingBoxOffset = {"top" : 0, "left" : 0};
|
| var boundingBoxWidth = 0;
|
| var boundingBoxHeight = 0;
|
|
|
| // the corners of the bounding box we should connect
|
| var fromX = 0;
|
| var fromY = 0;
|
| var toX = 0;
|
| var toY = 0;
|
|
|
| if (x2 > x1 && y2 > y1) {
|
| boundingBoxOffset.top = x1 - 5;
|
| boundingBoxOffset.left = y1 - 5;
|
| boundingBoxWidth = y2 - y1 + 10;
|
| boundingBoxHeight = x2 - x1 + 10;
|
| fromX = 0 + 5;
|
| fromY = 0 + 5;
|
| toX = boundingBoxWidth - 5;
|
| toY = boundingBoxHeight - 5;
|
| } else if (x2 > x1 && y2 < y1) {
|
| boundingBoxOffset.top = x1 - 5;
|
| boundingBoxOffset.left = y2 - 5;
|
| boundingBoxWidth = y1 - y2 + 10;
|
| boundingBoxHeight = x2 - x1 + 10;
|
| fromX = boundingBoxWidth - 5;
|
| fromY = 0 + 5;
|
| toX = 0 + 5;
|
| toY = boundingBoxHeight - 5;
|
| } else if (x2 < x1 && y2 > y1) {
|
| boundingBoxOffset.top = x2 - 5;
|
| boundingBoxOffset.left = y1 - 5;
|
| boundingBoxWidth = y2 - y1 + 10;
|
| boundingBoxHeight = x1 - x2 + 10;
|
| fromX = 0 + 5;
|
| fromY = boundingBoxHeight - 5;
|
| toX = boundingBoxWidth - 5;
|
| toY = 0 + 5;
|
| } else if (x2 < x1 && y2 < y1) {
|
| boundingBoxOffset.top = x2 - 5;
|
| boundingBoxOffset.left = y2 - 5;
|
| boundingBoxWidth = y1 - y2 + 10;
|
| boundingBoxHeight = x1 - x2 + 10;
|
| fromX = boundingBoxWidth - 5;
|
| fromY = boundingBoxHeight - 5;
|
| toX = 0 + 5;
|
| toY = 0 + 5;
|
| }
|
|
|
| v_svg.offset(boundingBoxOffset);
|
| // in IE and chrome we need to set the offset twice so it gets calculated correctly
|
| v_svg.offset(boundingBoxOffset);
|
| var svgElement = document.getElementById(v_id);
|
| svgElement.setAttribute("height", boundingBoxHeight);
|
| svgElement.setAttribute("width", boundingBoxWidth);
|
| svgElement.style.height = boundingBoxHeight;
|
| svgElement.style.width = boundingBoxWidth;
|
|
|
| //var d = 'M' + fromX + ',' + fromY + ' C' + ( (fromX + toX) / 2 ) + ',' + fromY + ' ' + ( (fromX + toX) / 2 ) + ',' + toY + ' ' + toX + ',' + toY;
|
|
|
| var d = getStyle(fromX, fromY, toX, toY);
|
| document.getElementById(v_id + "_path").setAttribute("d", d);
|
|
|
| setZIndex(svgElement);
|
| };
|
| }
|
|
|
| //# sourceURL=Utils\LineDrawer.js
|