diff --git a/bower.json b/bower.json index 7c5566f..dcf23f5 100644 --- a/bower.json +++ b/bower.json @@ -8,7 +8,7 @@ "private": false, "dependencies": { "angular": "~1.4.9", - "d3": "~3.4.4" + "d3": "^4.2.2" }, "devDependencies": { "angular-mocks": "~1.4.0" diff --git a/components/forceHorse/forceHorse-es5.js b/components/forceHorse/forceHorse-es5.js index e093393..a8fe80a 100644 --- a/components/forceHorse/forceHorse-es5.js +++ b/components/forceHorse/forceHorse-es5.js @@ -132,7 +132,7 @@ angular.module('forceHorse', []) myInstance.initLayout(json); // The force simulation has to started before drawing nodes and links, // because it computes some drawing-relevant properties (node weight) - myInstance.startForceSimulation(); + myInstance.restartForceSimulation(); myInstance.draw(); }; // $http.get(helper.getCurrentDirectory() + constants.CONFIG_FILE_NAME) @@ -150,7 +150,7 @@ angular.module('forceHorse', []) // } // myInstance.initLayout(json); // myInstance.draw(); - // myInstance.startForceSimulation(); + // myInstance.restartForceSimulation(); //}); return this; }; @@ -211,41 +211,50 @@ angular.module('forceHorse', []) Object.assign(this.config, config); // Create a forceLayout instance - myInstance.force = d3.layout.force().size([constants.INNER_SVG_WIDTH, constants.INNER_SVG_HEIGHT]).on("start", function () { - myInstance.onForceStart(); - }); + myInstance.force = d3.forceSimulation(); + // .size([constants.INNER_SVG_WIDTH, constants.INNER_SVG_HEIGHT]) + // .on("start", function () { + // myInstance.onForceStart(); + // }); + // todo: start event replacement? + var p; - if (angular.isDefined(p = myInstance.config.forceParameters.linkDistance)) myInstance.force.linkDistance(p); - if (angular.isDefined(p = myInstance.config.forceParameters.linkStrength)) myInstance.force.linkStrength(p); - //if (angular.isDefined(p = myInstance.config.forceParameters.charge)) myInstance.force.charge(p); - if (angular.isDefined(p = myInstance.config.forceParameters.gravity)) myInstance.force.gravity(p); - if (angular.isDefined(p = myInstance.config.forceParameters.charge)) { - myInstance.force.charge(p); - } else { + + var forceCenter = d3.forceCenter(constants.INNER_SVG_WIDTH / 2, constants.INNER_SVG_HEIGHT / 2); + myInstance.force.force("center", forceCenter); + // Todo: add center coordinates? + // Todo: a gravity measure replacement? + // if (angular.isDefined(p = myInstance.config.forceParameters.gravity)) myInstance.force.gravity(p); + + if (angular.isDefined(p = myInstance.config.forceParameters.charge)) {} else { if (myInstance.numOfNodes < constants.HEAVY_SIMULATION_NUM_OF_NODES) { - myInstance.force.charge(function (d) { + p = function p(d) { return d.weight * constants.DEFAULT_CHARGE_LIGHT; - }); + }; } else { - myInstance.force.charge(constants.DEFAULT_CHARGE_HEAVY); + p = constants.DEFAULT_CHARGE_HEAVY; } } - if (angular.isDefined(p = myInstance.config.forceParameters.friction)) { - myInstance.force.friction(p); - } else { - myInstance.force.friction(helper.computeFrictionParameter(constants.INNER_SVG_WIDTH, constants.INNER_SVG_HEIGHT, this.nodeDataArray.length)); - } + myInstance.force.force("charge", d3.forceManyBody().strength(p)); + // myInstance.force.charge(p); - myInstance.drag = myInstance.force.drag().on("drag", function (d) { - myInstance.onDrag(d); - }).on("dragend", function () { - myInstance.onDragEnd(); - }); + if (angular.isDefined(p = myInstance.config.forceParameters.friction)) {} else { + p = helper.computeFrictionParameter(constants.INNER_SVG_WIDTH, constants.INNER_SVG_HEIGHT, this.nodeDataArray.length); + } + myInstance.force.velocityDecay(p); + // myInstance.force.friction(p); - myInstance.force.nodes(myInstance.nodeDataArray).links(this.edgeDataArray); + myInstance.force.nodes(myInstance.nodeDataArray); + // .links(this.edgeDataArray); //.start(); - myInstance.zoom = d3.behavior.zoom().scaleExtent([constants.MAX_ZOOM, constants.MIN_ZOOM]).on("zoom", function () { + var linkForce = myInstance.force.force("link", d3.forceLink(myInstance.edgeDataArray).id(function (d, i) { + return i; + })); + if (angular.isDefined(p = myInstance.config.forceParameters.linkDistance)) linkForce.distance(p); + if (angular.isDefined(p = myInstance.config.forceParameters.linkStrength)) linkForce.strength(p); + + myInstance.zoom = d3.zoom().scaleExtent([constants.MAX_ZOOM, constants.MIN_ZOOM]).on("zoom", function () { myInstance.onZoom(); }); @@ -255,7 +264,9 @@ angular.module('forceHorse', []) d3.select(myInstance.element).select("div.svgWrapper").remove(); myInstance.svg = d3.select(myInstance.element).append("div").attr("class", "svgWrapper").append("svg").attr("class", "graph-svg").attr("viewBox", "0 0 " + constants.INNER_SVG_WIDTH + " " + constants.INNER_SVG_HEIGHT).attr("preserveAspectRatio", "none").on("click", function () { myInstance.onContainerClick(); - }).call(myInstance.zoom).call(myInstance.zoom.event) // Used in zoomToViewport() + }).call(myInstance.zoom) + // .call(myInstance.zoom.event) // Used in zoomToViewport() + // Todo: zoom.event replacement? ; // Set wrapper group, to use for pan & zoom transforms @@ -268,6 +279,16 @@ angular.module('forceHorse', []) myInstance.nodeGroup = myInstance.inSvgWrapper.append("g").attr("class", "nodes").attr("fill", constants.DEFAULT_NODE_COLOR); myInstance.labelGroup = myInstance.inSvgWrapper.append("g").attr("class", "labels").attr("fill", constants.DEFAULT_NODE_COLOR).classed("display_none", !myInstance.config.showLabels); + myInstance.drag = d3.drag() + // .container(myInstance.svg._groups[0][0]) + .on("drag", function (d) { + myInstance.onDrag(d); + }).on("start", function (d) { + myInstance.onDragStart(d); + }).on("end", function (d) { + myInstance.onDragEnd(d); + }); + return myInstance; }; // initLayout() @@ -301,7 +322,7 @@ angular.module('forceHorse', []) // draw nodes this.elements[constants.NODES] = this.nodeGroup.selectAll("." + constants.CSS_CLASS_NODE).data(this.nodeDataArray).enter().append("path") // Set node shape & size - .attr("d", d3.svg.symbol().type(function (d) { + .attr("d", d3.symbol().type(function (d) { return d.shape; }).size(function (d) { return myInstance.getNodeIconArea(d); @@ -345,12 +366,14 @@ angular.module('forceHorse', []) /** * @ngdoc method - * @name forceHorse.factory:ForceHorseFactory#startForceSimulation + * @name forceHorse.factory:ForceHorseFactory#restartForceSimulation * @description Restart the force simulation * @returns {ForceHorseFactory} current instance */ - proto.startForceSimulation = function () { - this.force.start(); + proto.restartForceSimulation = function () { + this.force.alpha(constants.MAX_ALPHA); + this.onForceStart(); + // this.force.restart(); return this; }; @@ -361,7 +384,7 @@ angular.module('forceHorse', []) * @returns {ForceHorseFactory} current instance */ proto.calcFixAspectRatio = function () { - var currentRect = this.svg[0][0].getBoundingClientRect(), + var currentRect = this.svg._groups[0][0].getBoundingClientRect(), currentHeight = currentRect.height, currentWidth = currentRect.width; this.fixAspectRatio = constants.INNER_SVG_WIDTH / constants.INNER_SVG_HEIGHT * (currentHeight / currentWidth); @@ -582,10 +605,10 @@ angular.module('forceHorse', []) */ proto.onForceStart = function () { // Prevent simulation when dragging a node - if (this.isDragging) { - this.force.stop(); - return this; - } + // if (this.isDragging) { + // this.force.stop(); + // return this; + // } // Proceed with simulation return this.calcFixAspectRatio()[this.numOfNodes < constants.HEAVY_SIMULATION_NUM_OF_NODES ? "runSimulation" : "runHeavySimulation"](); }; @@ -611,14 +634,14 @@ angular.module('forceHorse', []) // Do not accelerate the simulation during dragging, so as not to slow the dragging. ticksPerRender = myInstance.isDragging ? 1 : myInstance.numOfNodes / 7; calculationStart = performance.now(); - for (var i = 0; i < ticksPerRender && myInstance.force.alpha() > 0; i++) { + for (var i = 0; i < ticksPerRender && myInstance.force.alpha() > myInstance.force.alphaMin(); i++) { myInstance.force.tick(); ticks++; } calculationDuration += performance.now() - calculationStart; myInstance.updateGraphInDOM().updateProgressBar(); - if (myInstance.force.alpha() > 0) { + if (myInstance.force.alpha() > myInstance.force.alphaMin()) { requestAnimationFrame(render); } else { simulationDuration = performance.now() - simulationStart; @@ -727,7 +750,7 @@ angular.module('forceHorse', []) proto.updateProgressBar = function () { // Do not update progress bar in fixed nodes mode if (!this.fixedNodesMode) { - this.progressBar.attr('x2', constants.INNER_SVG_WIDTH * (1 - this.force.alpha() / constants.MAX_ALPHA)); + this.progressBar.attr('x2', constants.INNER_SVG_WIDTH * (1 - (this.force.alpha() - this.force.alphaMin()) / constants.MAX_ALPHA)); } return this; }; @@ -788,7 +811,8 @@ angular.module('forceHorse', []) scale = 1; translate = [0, 0]; } - this.svg.transition().duration(constants.ANIMATION_DURATION).call(this.zoom.translate(translate).scale(scale).event); + this.svg.transition().duration(constants.ANIMATION_DURATION).call(this.zoom.transform, d3.zoomIdentity.translate(translate[0], translate[1]).scale(scale)); + // .call(this.zoom.translate(translate).scale(scale).event); return this; }; @@ -998,15 +1022,30 @@ angular.module('forceHorse', []) * @returns {ForceHorseFactory} current instance */ proto.onZoom = function () { - var trans = d3.event.translate, - scale = d3.event.scale; + var trans = d3.event.transform; if (this.inSvgWrapper) { - this.inSvgWrapper.attr("transform", "translate(" + trans + ")" + " scale(" + scale + ")"); + this.inSvgWrapper.attr("transform", 'translate(' + trans.x + ', ' + trans.y + ') scale(' + trans.k + ')'); } return this; }; + /** + * @ngdoc method + * @name forceHorse.factory:ForceHorseFactory#onDragStart + * @description + * Event handler, called when a node-dragging starts + * *UPDATE* I avoid using it, because it is triggered by a normal mouse-down + * @returns {ForceHorseFactory} current instance + */ + proto.onDragStart = function () /*d*/{ + // Fix the dragged node (not moved by the simulation) + // d.fx = d.x; + // d.fy = d.y; + // Start the simulation + return this; + }; + /** * @ngdoc method * @name forceHorse.factory:ForceHorseFactory#onDrag @@ -1016,13 +1055,12 @@ angular.module('forceHorse', []) * @returns {ForceHorseFactory} current instance */ proto.onDrag = function (d) { - // Make the dragged node fixed (not moved by the simulation) - this.elements[constants.NODES].filter(function (nodeData) { - return nodeData.id === d.id; - }).classed("fixed", d.fixed = true); - + // Fix the dragged node (not moved by the simulation) + d.fx = d3.event.x; + d.fy = d3.event.y; if (!this.isDragging) { this.isDragging = true; + this.restartForceSimulation(); } return this; }; @@ -1034,8 +1072,15 @@ angular.module('forceHorse', []) * Event handler, called when a node-dragging ends * @returns {ForceHorseFactory} current instance */ - proto.onDragEnd = function () { + proto.onDragEnd = function (d) { this.isDragging = false; + // Cool the simulation + this.force.alpha(this.force.alphaMin()); + // Unfix the dragged node + if (!this.fixedNodesMode) { + d.fx = null; + d.fy = null; + } return this; }; @@ -1051,12 +1096,17 @@ angular.module('forceHorse', []) proto.toggleFixedNodesMode = function () { if (this.fixedNodesMode) { this.elements[constants.NODES].classed('fixed', function (d) { + d.fx = null; + d.fy = null; return d.fixed = false; }); this.fixedNodesMode = false; - this.force.start(); + this.restartForceSimulation(); + // this.force.start(); } else { this.elements[constants.NODES].classed('fixed', function (d) { + d.fx = d.x; + d.fy = d.y; return d.fixed = true; }); this.fixedNodesMode = true; @@ -1101,7 +1151,7 @@ angular.module('forceHorse', []) proto.onNodeWeightShowHideBtnClick = function () { var myInstance = this; this.config.showNodeWeight = !this.config.showNodeWeight; - this.elements[constants.NODES].attr("d", d3.svg.symbol().type(function (d) { + this.elements[constants.NODES].attr("d", d3.symbol().type(function (d) { return d.shape; }).size(function (d) { return myInstance.getNodeIconArea(d); @@ -1328,6 +1378,7 @@ angular.module('forceHorse', []) * If nodeData does not contain an id property, its id is set to its index in the array. * If nodeData does not contain a label property, it gets a default label. * A "class" property (node class) is also added to each nodeData. + * Set node shape * If linkData does not contain an id property, its id is set to its index in the array. * If linkData does not contain an sourceID property, sourceID is set to source. * If linkData does not contain an targetID property, targetID is set to target. @@ -1348,6 +1399,35 @@ angular.module('forceHorse', []) node.label = "" + node.id; } node.class = constants.CLASS_NODE; + + switch (node.shape) { + case undefined: + case "": + case "circle": + node.shape = d3.symbolCircle; + break; + case "cross": + node.shape = d3.symbolCross; + break; + case "diamond": + node.shape = d3.symbolDiamond; + break; + case "square": + node.shape = d3.symbolSquare; + break; + case "triangle-up": + case "triangle-down": + node.shape = d3.symbolTriangle; + break; + case "star": + node.shape = d3.symbolStar; + break; + case "wye": + node.shape = d3.symbolWye; + break; + default: + node.shape = d3.symbolCircle; + } }); // Process edges var edges = fileData.edges ? fileData.edges : fileData.links; diff --git a/components/forceHorse/forceHorse-es5.js.map b/components/forceHorse/forceHorse-es5.js.map index f79e5a6..f4a2f26 100644 --- a/components/forceHorse/forceHorse-es5.js.map +++ b/components/forceHorse/forceHorse-es5.js.map @@ -1 +1 @@ -{"version":3,"sources":["forceHorse.js"],"names":[],"mappings":"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","file":"forceHorse-es5.js","sourcesContent":["\"use strict\";\r\n/**\r\n * @ngdoc overview\r\n * @name forceHorse\r\n * @description A graph visualizer using a \"force layout\" engine (d3.js)\r\n */\r\nangular.module('forceHorse', [])\r\n\r\n //---------------------------------------------------------------//\r\n .run(function ($templateCache) {\r\n // cache our buttons template\r\n $templateCache.put('forceHorse/buttons',\r\n '
');\r\n })\r\n\r\n /**\r\n * @ngdoc directive\r\n * @name forceHorse.directive:forceHorse\r\n * @restrict EA\r\n * @scope\r\n * @priority 100\r\n * @description Directive definition for the forceHorse component\r\n */\r\n .directive('forceHorse', ['$compile', 'ForceHorseFactory', 'ForceHorseHelper', function ($compile, ForceHorseFactory, helper) {\r\n return {\r\n restrict: \"EA\",\r\n controllerAs: \"forceHorseCtrl\",\r\n priority: 100,\r\n scope: {\r\n options: \"=\"\r\n },\r\n bindToController: true,\r\n\r\n controller: function ($scope, $element) {\r\n var vm = this;\r\n // Create my instance\r\n // Also provide the caller with a reference to my instance, for API\r\n this.requestDigest = function () {\r\n try {\r\n $scope.$digest();\r\n } catch (e){}\r\n };\r\n\r\n this.options.forceHorseInstance =\r\n $scope.forceHorseInstance = new ForceHorseFactory($element, this.options, this.requestDigest)\r\n .redraw();\r\n\r\n // Clear the instance reference on destruction, to prevent memory leak\r\n $scope.$on(\"$destroy\", function () {\r\n console.log(\"Destroying forceHorse instance\");\r\n vm.options.forceHorseInstance =\r\n $scope.forceHorseInstance = null;\r\n });\r\n },\r\n\r\n link: function (scope, element) { //, attr, ctrl) {\r\n //console.log('In forceHorse link');\r\n\r\n // Add CSS class to set a CSS \"namespace\"\r\n element.addClass(\"force-horse\");\r\n // Add flex-box properties (moved to css)\r\n // element.attr(\"layout\", \"column\");\r\n // element.attr(\"flex\", \"\");\r\n // Add button bar\r\n helper.addButtons(scope, element);\r\n }\r\n };\r\n }])\r\n\r\n /**\r\n * @ngdoc factory\r\n * @name forceHorse.factory:ForceHorseFactory\r\n * @description Produces a class-instance for each instance of ForceHorse on a page\r\n */\r\n .factory('ForceHorseFactory', ['$http', '$log', 'ForceHorseConstants', 'ForceHorseHelper',\r\n function ($http, $log, constants, helper) {\r\n /**\r\n * @ngdoc method\r\n * @name ForceHorseFactory\r\n * @methodOf forceHorse.factory:ForceHorseFactory\r\n * @constructor\r\n * @description Constructor; initializes the eventListeners object\r\n * @param element A JSLite reference to the HTML container for this component\r\n * @param options An external options object\r\n * @param requestDigest callback function: request an angular digest on the element's scope\r\n */\r\n function ForceHorseFactory(element, options, requestDigest) {\r\n this.element = element[0];\r\n this.options = options;\r\n this.requestDigest = requestDigest;\r\n // Set a variable to hold references to registered event listeners\r\n this.eventListeners = {};\r\n }\r\n\r\n var proto = ForceHorseFactory.prototype;\r\n\r\n /**\r\n * @ngdoc method\r\n * @name forceHorse.factory:ForceHorseFactory#redraw\r\n * @description Draws a new graph, based on the input data\r\n * @returns {ForceHorseFactory} current instance\r\n */\r\n proto.redraw = function () {\r\n var myInstance = this;\r\n var proceed = function (json) {\r\n myInstance.initLayout(json);\r\n // The force simulation has to started before drawing nodes and links,\r\n // because it computes some drawing-relevant properties (node weight)\r\n myInstance.startForceSimulation();\r\n myInstance.draw();\r\n };\r\n // $http.get(helper.getCurrentDirectory() + constants.CONFIG_FILE_NAME)\r\n // Get init (forceHorse.json) file from app root dir\r\n $http.get(constants.CONFIG_FILE_NAME)\r\n .then(function (response) {\r\n proceed(response.data);\r\n }, function (response) {\r\n $log.warn(constants.CONFIG_FILE_NAME + ' ' + response.statusText);\r\n proceed({});\r\n });\r\n //d3.json(\"forceHorse.json\", function (error, json) {\r\n // if (error) {\r\n // console.warn(error);\r\n // json = {};\r\n // }\r\n // myInstance.initLayout(json);\r\n // myInstance.draw();\r\n // myInstance.startForceSimulation();\r\n //});\r\n return this;\r\n };\r\n\r\n /**\r\n * @ngdoc method\r\n * @name forceHorse.factory:ForceHorseFactory#initLayout\r\n * @description Init force layout & SVG\r\n * @param config an external configration object (typically from a json file)\r\n * @returns {ForceHorseFactory} current instance\r\n */\r\n proto.initLayout = function (config) {\r\n var myInstance = this;\r\n // Generate a random instance name, for a \"namespace\"\r\n this.instanceName = new Array(constants.INSTANCE_NAME_LENGTH).fill(null).map(function () {\r\n return constants.ALEPHBET.charAt(Math.floor(Math.random() * constants.ALEPHBET.length));\r\n }).join('');\r\n\r\n // Process input data\r\n var data = this.options.data;\r\n if (!(data instanceof Array)) {\r\n data = helper.convertFileDataFormat(data);\r\n }\r\n this.nodeDataArray = data[constants.NODES].data;\r\n this.edgeDataArray = data[constants.EDGES].data;\r\n this.processNodes();\r\n this.processEdges();\r\n\r\n // Some nodes-related fields\r\n // The size (area) of the containing circle\r\n this.numOfNodes = this.nodeDataArray.length;\r\n this.nodeIconAreaDefault = constants.INNER_SVG_WIDTH / 54 * constants.INNER_SVG_HEIGHT / 48 * 2;\r\n this.nodeIconRadius = Math.sqrt(this.nodeIconAreaDefault / Math.PI);\r\n this.selectedItems = [new Set(), new Set()]; // selected nodes, selected edges\r\n this.fixedNodesMode = false;\r\n //this.isBoundedGraphMode = false; // redundant?\r\n this.isFirstZoomDone = false; // Zooming to viewport after first simlation\r\n this.isDragging = false;\r\n\r\n // Set config parameters, which may be overwritten by the config argument\r\n // (that is, in fact, by an external json file)\r\n this.config = {\r\n showLabels: false,\r\n showNodeWeight: false,\r\n showEdgeWeight: false,\r\n showFilterButton: true,\r\n showLabelsButton: true,\r\n showNodeWeightButton: true,\r\n showEdgeWeightButton: true,\r\n useEdgesWeights: false,\r\n forceParameters: {\r\n //charge: -350,\r\n linkStrength: 1,\r\n gravity: 0.3,\r\n linkDistance: 10\r\n }\r\n };\r\n Object.assign(this.config, config);\r\n\r\n // Create a forceLayout instance\r\n myInstance.force = d3.layout.force()\r\n .size([constants.INNER_SVG_WIDTH, constants.INNER_SVG_HEIGHT])\r\n .on(\"start\", function () {\r\n myInstance.onForceStart();\r\n });\r\n var p;\r\n if (angular.isDefined(p = myInstance.config.forceParameters.linkDistance)) myInstance.force.linkDistance(p);\r\n if (angular.isDefined(p = myInstance.config.forceParameters.linkStrength)) myInstance.force.linkStrength(p);\r\n //if (angular.isDefined(p = myInstance.config.forceParameters.charge)) myInstance.force.charge(p);\r\n if (angular.isDefined(p = myInstance.config.forceParameters.gravity)) myInstance.force.gravity(p);\r\n if (angular.isDefined(p = myInstance.config.forceParameters.charge)) {\r\n myInstance.force.charge(p);\r\n } else {\r\n if (myInstance.numOfNodes < constants.HEAVY_SIMULATION_NUM_OF_NODES) {\r\n myInstance.force.charge(function (d) {\r\n return d.weight * constants.DEFAULT_CHARGE_LIGHT;\r\n });\r\n } else {\r\n myInstance.force.charge(constants.DEFAULT_CHARGE_HEAVY);\r\n }\r\n }\r\n if (angular.isDefined(p = myInstance.config.forceParameters.friction)) {\r\n myInstance.force.friction(p);\r\n } else {\r\n myInstance.force.friction(helper.computeFrictionParameter(constants.INNER_SVG_WIDTH, constants.INNER_SVG_HEIGHT, this.nodeDataArray.length))\r\n }\r\n\r\n myInstance.drag = myInstance.force.drag()\r\n .on(\"drag\", function (d) {\r\n myInstance.onDrag(d);\r\n })\r\n .on(\"dragend\", function () {\r\n myInstance.onDragEnd();\r\n });\r\n\r\n myInstance.force.nodes(myInstance.nodeDataArray)\r\n .links(this.edgeDataArray);\r\n //.start();\r\n\r\n myInstance.zoom = d3.behavior.zoom()\r\n .scaleExtent([constants.MAX_ZOOM, constants.MIN_ZOOM])\r\n .on(\"zoom\", function () {\r\n myInstance.onZoom();\r\n });\r\n\r\n // Create the main SVG (canvas).\r\n // If that element exists, remove it first.\r\n // TODO - is the element really filtered from memory (and not just the DOM)?\r\n d3.select(myInstance.element)\r\n .select(\"div.svgWrapper\")\r\n .remove();\r\n myInstance.svg = d3.select(myInstance.element)\r\n .append(\"div\")\r\n .attr(\"class\", \"svgWrapper\")\r\n .append(\"svg\")\r\n .attr(\"class\", \"graph-svg\")\r\n .attr(\"viewBox\", \"0 0 \" + constants.INNER_SVG_WIDTH + \" \" + constants.INNER_SVG_HEIGHT)\r\n .attr(\"preserveAspectRatio\", \"none\")\r\n .on(\"click\", function () {\r\n myInstance.onContainerClick()\r\n })\r\n .call(myInstance.zoom)\r\n .call(myInstance.zoom.event) // Used in zoomToViewport()\r\n ;\r\n\r\n // Set wrapper group, to use for pan & zoom transforms\r\n myInstance.inSvgWrapper = myInstance.svg.append(\"g\");\r\n\r\n // Set SVG groups, and through them default colors,\r\n // for nodes and edges (note: the edge group has to be inserted first, so that the nodes\r\n // will render above the edges).\r\n myInstance.edgeGroup = myInstance.inSvgWrapper.append(\"g\")\r\n .attr(\"class\", \"edges\")\r\n .attr(\"stroke\", constants.DEFAULT_EDGE_COLOR)\r\n .attr(\"stroke-width\", constants.DEFAULT_EDGE_WIDTH + 'px');\r\n myInstance.nodeGroup = myInstance.inSvgWrapper.append(\"g\")\r\n .attr(\"class\", \"nodes\")\r\n .attr(\"fill\", constants.DEFAULT_NODE_COLOR);\r\n myInstance.labelGroup = myInstance.inSvgWrapper.append(\"g\")\r\n .attr(\"class\", \"labels\")\r\n .attr(\"fill\", constants.DEFAULT_NODE_COLOR)\r\n .classed(\"display_none\", !myInstance.config.showLabels);\r\n\r\n return myInstance;\r\n }; // initLayout()\r\n\r\n /**\r\n * @ngdoc method\r\n * @name forceHorse.factory:ForceHorseFactory#draw\r\n * @description Set the graph in the DOM: nodes, edges, labels, progress bar\r\n * @returns {ForceHorseFactory} current instance\r\n */\r\n proto.draw = function () {\r\n var myInstance = this;\r\n myInstance.elements = new Array(2); // nodes, edges\r\n\r\n // draw edges\r\n this.elements[constants.EDGES] = this.edgeGroup.selectAll(\".\" + constants.CSS_CLASS_EDGE)\r\n .data(this.edgeDataArray)\r\n .enter()\r\n .append(\"line\")\r\n .attr(\"class\", constants.CSS_CLASS_EDGE)\r\n .attr(\"stroke\", function (d) {\r\n return d.color;\r\n })\r\n .attr(\"stroke-width\", (!this.config.showEdgeWeight ? null : function (d) {\r\n return myInstance.getEdgeWidth(d);\r\n }))\r\n .on(\"mouseenter\", function (d) {\r\n myInstance.onHoverInside(this, d, true);\r\n })\r\n .on(\"mouseleave\", function (d) {\r\n myInstance.onHoverInside(this, d, false);\r\n })\r\n .on(\"click\", function (d) {\r\n myInstance.onClick(d, this);\r\n })\r\n // Prevent panning when dragging a node\r\n .on(\"mousedown\", function () {\r\n d3.event.stopPropagation();\r\n })\r\n ;\r\n\r\n // draw nodes\r\n this.elements[constants.NODES] = this.nodeGroup.selectAll(\".\" + constants.CSS_CLASS_NODE)\r\n .data(this.nodeDataArray)\r\n .enter()\r\n .append(\"path\")\r\n // Set node shape & size\r\n .attr(\"d\", d3.svg.symbol()\r\n .type(function (d) {\r\n return d.shape;\r\n })\r\n .size(function (d) {\r\n return myInstance.getNodeIconArea(d);\r\n }))\r\n .attr(\"fill\", function (d) {\r\n return d.color;\r\n })\r\n .attr(\"class\", constants.CSS_CLASS_NODE)\r\n .on(\"mouseenter\", function (d) {\r\n myInstance.onHoverInside(this, d, true);\r\n })\r\n .on(\"mouseleave\", function (d) {\r\n myInstance.onHoverInside(this, d, false);\r\n })\r\n .on(\"click\", function (d) {\r\n myInstance.onClick(d, this);\r\n })\r\n .on(\"dblclick\", function (d) {\r\n myInstance.callEventListeners(\"dblclick\", d);\r\n })\r\n // Prevent panning when dragging a node\r\n .on(\"mousedown\", function () {\r\n d3.event.stopPropagation();\r\n })\r\n .call(this.drag);\r\n\r\n // draw node labels\r\n this.labels = this.labelGroup.selectAll(\"text.label\")\r\n .data(this.nodeDataArray)\r\n .enter()\r\n .append(\"text\")\r\n .attr(\"fill\", function (d) {\r\n return d.color;\r\n })\r\n .text(function (d) {\r\n return d.label;\r\n })\r\n .attr(\"dx\", function (d) {\r\n return (helper.isHebrewString(d.label) ? -1 : +1) * constants.LABEL_DISPLACEMENT;\r\n })\r\n .attr(\"text-anchor\", function (d) {\r\n return (helper.isHebrewString(d.label) ? \"end\" : \"start\");\r\n })\r\n ;\r\n\r\n // Draw progress bar\r\n this.progressBar = this.svg\r\n .append('line')\r\n .attr('class', 'progress')\r\n .attr('x1', '0')\r\n .attr('y1', '1')\r\n .attr('x2', '0')\r\n .attr('y2', '1');\r\n\r\n // set an on-resize event, to fix aspect ratios\r\n d3.select(window).on(`resize.${this.instanceName}`, function () {\r\n myInstance.onWindowResize();\r\n });\r\n\r\n return this;\r\n };\r\n\r\n /**\r\n * @ngdoc method\r\n * @name forceHorse.factory:ForceHorseFactory#startForceSimulation\r\n * @description Restart the force simulation\r\n * @returns {ForceHorseFactory} current instance\r\n */\r\n proto.startForceSimulation = function () {\r\n this.force.start();\r\n return this;\r\n };\r\n\r\n /**\r\n * @ngdoc method\r\n * @name forceHorse.factory:ForceHorseFactory#calcFixAspectRatio\r\n * @description Returns a number to be multiplied by an element's width, to fix aspect ratio deformation, due to the svg fixAspectRatio=\"none\"\r\n * @returns {ForceHorseFactory} current instance\r\n */\r\n proto.calcFixAspectRatio = function () {\r\n var currentRect = this.svg[0][0].getBoundingClientRect(),\r\n currentHeight = currentRect.height,\r\n currentWidth = currentRect.width;\r\n this.fixAspectRatio = (constants.INNER_SVG_WIDTH / constants.INNER_SVG_HEIGHT) * (currentHeight / currentWidth);\r\n return this;\r\n };\r\n\r\n /**\r\n * @ngdoc method\r\n * @name forceHorse.factory:ForceHorseFactory#getNodeIconArea\r\n * @description Calculates the desired node icon area (with or without showing weight)\r\n * @returns {number}\r\n */\r\n proto.getNodeIconArea = function (nodeData) {\r\n var myInstance = this;\r\n return myInstance.nodeIconAreaDefault +\r\n (myInstance.config.showNodeWeight\r\n ? (myInstance.config.useEdgesWeights\r\n ? nodeData.edgesWeight\r\n : angular.isDefined(nodeData.weight)\r\n ? nodeData.weight\r\n : 1)\r\n * constants.node_size_addition_per_weight_unit\r\n : 0);\r\n };\r\n\r\n /**\r\n * @ngdoc method\r\n * @name forceHorse.factory:ForceHorseFactory#getEdgeWidth\r\n * @description Calculates the desired edge width (with or without showing weight)\r\n * @returns {number}\r\n */\r\n proto.getEdgeWidth = function (edgeData) {\r\n return constants.DEFAULT_EDGE_WIDTH + (edgeData.weight / 3) + 'px';\r\n };\r\n\r\n /**\r\n * @ngdoc method\r\n * @name forceHorse.factory:ForceHorseFactory#onFilterInside\r\n * @description Filter button action: remove selected elements\r\n * @returns {ForceHorseFactory} current instance\r\n */\r\n proto.onFilterInside = function () {\r\n // Mark the selected items as filtered, and unselect them\r\n // Also clear the selected-items sets\r\n for (var itemType = constants.NODES; itemType <= constants.EDGES; itemType++) {\r\n this.elements[itemType].filter(function (item) {\r\n return item.selected;\r\n }).classed('filtered', function (d) {\r\n return d.filtered = true;\r\n }).classed('selected', function (d) {\r\n return d.selected = false;\r\n });\r\n this.selectedItems[itemType].clear();\r\n }\r\n\r\n // Remove the labels of filtered nodes\r\n this.labels.classed(\"selected\", \"false\")\r\n .classed(\"filtered\", function (d) {\r\n return d.filtered;\r\n });\r\n\r\n // Remove edges connected to filtered nodes\r\n this.elements[constants.EDGES].filter(function (d) {\r\n return d.source.filtered || d.target.filtered;\r\n }).classed(\"filtered\", function (d) {\r\n return d.filtered = true;\r\n });\r\n\r\n // Cancel selection mode\r\n this.svg.classed(\"selectionMode\", false);\r\n\r\n // Broadcast event\r\n this.callEventListeners('filter');\r\n\r\n return this;\r\n };\r\n\r\n /**\r\n * @ngdoc method\r\n * @name forceHorse.factory:ForceHorseFactory#onFilterOutside\r\n * @description API: some elements were filtered out, update the graph\r\n * @returns {ForceHorseFactory} current instance\r\n */\r\n proto.onFilterOutside = function () {\r\n var myInstance = this;\r\n // Give the filtered elements the approprite CSS class\r\n // If a filtered element was selected, mark it as unselected\r\n for (var itemType = constants.NODES; itemType <= constants.EDGES; itemType++) {\r\n this.elements[itemType].filter(function (item) {\r\n return item.filtered;\r\n }).classed('filtered', true)\r\n .classed('selected', false)\r\n .each(function (d) {\r\n let type = (d.class === constants.CLASS_NODE ? constants.NODES : constants.EDGES);\r\n myInstance.selectedItems[type].delete(d.id);\r\n });\r\n }\r\n\r\n // Remove the labels of filtered nodes\r\n this.labels.filter(function (item) {\r\n return item.filtered;\r\n }).classed('filtered', true)\r\n .classed('selected', false);\r\n\r\n // Remove edges connected to filtered nodes\r\n this.elements[constants.EDGES].filter(function (d) {\r\n return d.source.filtered || d.target.filtered;\r\n }).classed(\"filtered\", function (d) {\r\n return d.filtered = true;\r\n });\r\n\r\n // Update visual selection mode\r\n myInstance.svg.classed(\"selectionMode\",\r\n myInstance.selectedItems[constants.NODES].size + myInstance.selectedItems[constants.EDGES].size);\r\n\r\n return this;\r\n };\r\n\r\n /**\r\n * @ngdoc method\r\n * @name forceHorse.factory:ForceHorseFactory#processNodes\r\n * @description Graph initialization: add auxiliary properties and variables to the nodes array\r\n * @returns {ForceHorseFactory} current instance\r\n */\r\n proto.processNodes = function () {\r\n var myInstance = this;\r\n this.nodesById = {};\r\n this.nodeDataArray.forEach(function (val, idx) {\r\n if (angular.isUndefined(val.id)) {\r\n val.id = idx;\r\n //console.error(\"Undefined [id] in nodes array\");\r\n }\r\n myInstance.nodesById[val.id] = idx;\r\n if (angular.isUndefined(val.label)) {\r\n val.label = \"\" + val.id;\r\n }\r\n });\r\n return this;\r\n };\r\n\r\n /**\r\n * @ngdoc method\r\n * @name forceHorse.factory:ForceHorseFactory#processEdges\r\n * @description Graph initialization: add auxiliary properties and variables to the edges array\r\n * @returns {ForceHorseFactory} current instance\r\n */\r\n proto.processEdges = function () {\r\n //----------\r\n function calculateEdgesWeightsForNodes(edge){\r\n // calculate edges weight\r\n var sourceNode = myInstance.nodeDataArray[edge.source],\r\n targetNode = myInstance.nodeDataArray[edge.target];\r\n\r\n // protect in case undefined\r\n if (!sourceNode.edgesWeights){\r\n sourceNode.edgesWeights = 0;\r\n }\r\n\r\n if (!targetNode.edgesWeights){\r\n targetNode.edgesWeights = 0;\r\n }\r\n\r\n if (!edge.weight) {\r\n edge.weight = 1;\r\n }\r\n\r\n sourceNode.edgesWeights += edge.weight;\r\n targetNode.edgesWeights += edge.weight;\r\n }\r\n //----------\r\n\r\n var myInstance = this, sid, tid, key;\r\n this.edgesFromNodes = {};\r\n this.edgeDataArray.forEach(function (val, idx) {\r\n\r\n if (angular.isUndefined(val.id)) {\r\n val.id = idx;\r\n // console.warn(`Undefined [id] in edge ${val.sourceID} - ${val.targetID}`);\r\n }\r\n // Get nodes data from nodes id's\r\n if (angular.isUndefined(val.sourceID)) {\r\n val.sourceID = val.source;\r\n //console.error(\"Undefined [sourceID] in edge #\" + val.id);\r\n }\r\n val.source = myInstance.nodesById[val.sourceID];\r\n if (angular.isUndefined(val.targetID)) {\r\n val.targetID = val.target;\r\n //console.error(\"Undefined [targetID] in edges #\" + val.id);\r\n }\r\n val.target = myInstance.nodesById[val.targetID];\r\n\r\n calculateEdgesWeightsForNodes(val);\r\n\r\n // Build an index to help handle the case of multiple edges between two nodes\r\n if (angular.isDefined(val.sourceID) && angular.isDefined(val.targetID)) {\r\n sid = val.sourceID;\r\n tid = val.targetID;\r\n key = (sid < tid ? sid + \",\" + tid : tid + \",\" + sid);\r\n if (angular.isUndefined(myInstance.edgesFromNodes[key])) {\r\n myInstance.edgesFromNodes[key] = [idx];\r\n val.multiIdx = 1;\r\n } else {\r\n val.multiIdx = myInstance.edgesFromNodes[key].push(idx);\r\n }\r\n // Calculate base edge offset, from the index in the multiple-edges array:\r\n // 1 -> 0, 2 -> 2, 3-> -2, 4 -> 4, 5 -> -4, ...\r\n val.basicOffset = (val.multiIdx % 2 === 0 ? val.multiIdx * constants.DEFAULT_EDGE_WIDTH : (-val.multiIdx + 1) * constants.DEFAULT_EDGE_WIDTH);\r\n }\r\n });\r\n return this;\r\n };\r\n\r\n /**\r\n * @ngdoc method\r\n * @name forceHorse.factory:ForceHorseFactory#onWindowResize\r\n * @description Fix aspect ratios, when the window resizes\r\n * @returns {ForceHorseFactory} current instance\r\n */\r\n proto.onWindowResize = function () {\r\n return this.calcFixAspectRatio()\r\n .updateGraphInDOM();\r\n };\r\n\r\n /**\r\n * @ngdoc method\r\n * @name forceHorse.factory:ForceHorseFactory#onForceStart\r\n * @description Called when a force-simulation is supposed to start.\r\n * @returns {ForceHorseFactory} current instance\r\n */\r\n proto.onForceStart = function () {\r\n // Prevent simulation when dragging a node\r\n if (this.isDragging) {\r\n this.force.stop();\r\n return this;\r\n }\r\n // Proceed with simulation\r\n return this.calcFixAspectRatio()\r\n [this.numOfNodes < constants.HEAVY_SIMULATION_NUM_OF_NODES ?\r\n \"runSimulation\" : \"runHeavySimulation\"]();\r\n\r\n };\r\n\r\n /**\r\n * @ngdoc method\r\n * @name forceHorse.factory:ForceHorseFactory#runSimulation\r\n * @description\r\n * Run the force-simulation with control.\r\n * The DOM is not updated for every tick.\r\n * @returns {ForceHorseFactory} current instance\r\n */\r\n proto.runSimulation = function () {\r\n var myInstance = this;\r\n var ticksPerRender,\r\n simulationStart = performance.now(), simulationDuration, calculationStart, calculationDuration = 0,\r\n ticks = 0;\r\n\r\n requestAnimationFrame(function render() {\r\n // Do not accelerate the simulation during dragging, so as not to slow the dragging.\r\n ticksPerRender = (myInstance.isDragging ? 1 : myInstance.numOfNodes / 7);\r\n calculationStart = performance.now();\r\n for (let i = 0; i < ticksPerRender && myInstance.force.alpha() > 0; i++) {\r\n myInstance.force.tick();\r\n ticks++;\r\n }\r\n calculationDuration += (performance.now() - calculationStart);\r\n myInstance.updateGraphInDOM().updateProgressBar();\r\n\r\n if (myInstance.force.alpha() > 0) {\r\n requestAnimationFrame(render);\r\n } else {\r\n simulationDuration = performance.now() - simulationStart;\r\n console.log(`Force Simulation time = ${(simulationDuration / 1000).toFixed(2)}s, Calculation time = ${(calculationDuration / 1000).toFixed(2)}s, ${ticks} ticks`);\r\n myInstance.onForceEnd();\r\n }\r\n }); // render\r\n return this;\r\n };\r\n\r\n /**\r\n * @ngdoc method\r\n * @name forceHorse.factory:ForceHorseFactory#runHeavySimulation\r\n * @description\r\n * Heavy graphs version: run the force-simulation with control.\r\n * The DOM is not updated for every tick.\r\n * @returns {ForceHorseFactory} current instance\r\n */\r\n proto.runHeavySimulation = function () {\r\n var myInstance = this;\r\n var ticksPerRender,\r\n calculationStart, calculationDuration = 0,\r\n ticks = 0;\r\n\r\n requestAnimationFrame(function render() {\r\n // Do not accelerate the simulation during dragging, so as not to slow the dragging.\r\n ticksPerRender = (myInstance.isDragging ? 1 : 30);\r\n calculationStart = performance.now();\r\n for (let i = 0; i < ticksPerRender && myInstance.force.alpha() > 0; i++) {\r\n myInstance.force.tick();\r\n ticks++;\r\n }\r\n calculationDuration += (performance.now() - calculationStart);\r\n myInstance.updateProgressBar();\r\n if (myInstance.isDragging) {\r\n myInstance.updateGraphInDOM();\r\n }\r\n\r\n if (myInstance.force.alpha() > 0) {\r\n requestAnimationFrame(render);\r\n } else {\r\n console.log(`Calculation time = ${(calculationDuration / 1000).toFixed(2)}s, ${ticks} ticks`);\r\n myInstance.updateGraphInDOM().onForceEnd();\r\n }\r\n }); // render\r\n return this;\r\n };\r\n\r\n /**\r\n * @ngdoc method\r\n * @name forceHorse.factory:ForceHorseFactory#updateGraphInDOM\r\n * @description\r\n * Update the force simulation in the DOM\r\n * @returns {ForceHorseFactory} current instance\r\n */\r\n proto.updateGraphInDOM = function () {\r\n var myInstance = this;\r\n\r\n // Update nodes\r\n this.elements[constants.NODES]\r\n //.each(function (d) {\r\n // myInstance.preventNodesOverlap(1.0)(d);\r\n //})\r\n .attr('transform', function (d) {\r\n //if (myInstance.isBoundedGraphMode) {\r\n // // Force the nodes inside the visible area\r\n // var radius = myInstance.nodeIconRadius;\r\n // d.x = Math.max(radius, Math.min(constants.INNER_SVG_WIDTH - radius, d.x));\r\n // d.y = Math.max(radius, Math.min(constants.INNER_SVG_HEIGHT - radius, d.y));\r\n //}\r\n return `translate(${d.x},${d.y}) scale(${myInstance.fixAspectRatio},1)`;\r\n })\r\n ;\r\n\r\n // Update labels\r\n this.labels.attr(\"x\", function (d) {\r\n return d.x;\r\n })\r\n .attr(\"y\", function (d) {\r\n return d.y;\r\n })\r\n ;\r\n\r\n // Update edges\r\n this.elements[constants.EDGES].attr(\"x1\", function (d) {\r\n return d.source.x;\r\n })\r\n .attr(\"y1\", function (d) {\r\n return d.source.y;\r\n })\r\n .attr(\"x2\", function (d) {\r\n return d.target.x;\r\n })\r\n .attr(\"y2\", function (d) {\r\n return d.target.y;\r\n })\r\n // Add some translation, for the case of multiple edges between two nodes\r\n .attr('transform', function (d) {\r\n var offset = helper.calcRightAngledOffset(d.basicOffset, d.target.x - d.source.x, d.target.y - d.source.y);\r\n return \"translate(\" + offset.dx + \",\" + offset.dy + \")\";\r\n })\r\n ;\r\n\r\n return this;\r\n };\r\n\r\n /**\r\n * @ngdoc method\r\n * @name forceHorse.factory:ForceHorseFactory#updateProgressBar\r\n * @returns {ForceHorseFactory} current instance\r\n */\r\n proto.updateProgressBar = function () {\r\n // Do not update progress bar in fixed nodes mode\r\n if (!this.fixedNodesMode) {\r\n this.progressBar.attr('x2',\r\n constants.INNER_SVG_WIDTH * (1 - this.force.alpha() / constants.MAX_ALPHA));\r\n }\r\n return this;\r\n };\r\n\r\n /**\r\n * @ngdoc method\r\n * @name forceHorse.factory:ForceHorseFactory#onForceEnd\r\n * @description\r\n * Called whenever the d3 force-simulation comes to a halt.\r\n * @returns {ForceHorseFactory} current instance\r\n */\r\n proto.onForceEnd = function () {\r\n // Zoom out the graph, if needed, so that it is fully visible.\r\n // This is done only on the first time after component start.\r\n if (!this.isFirstZoomDone) {\r\n this.zoomToViewport();\r\n this.isFirstZoomDone = true;\r\n // Also make the graph fixed, after the first force-simulation\r\n this.toggleFixedNodesMode();\r\n this.requestDigest(); // To update the related button's display\r\n }\r\n return this;\r\n };\r\n\r\n /**\r\n * @ngdoc method\r\n * @name forceHorse.factory:ForceHorseFactory#zoomToViewport\r\n * @description\r\n * Zoom out the graph, if needed, so that it is fully visible.\r\n * @returns {ForceHorseFactory} current instance\r\n */\r\n proto.zoomToViewport = function () {\r\n var scale, translate,\r\n width = constants.INNER_SVG_WIDTH,\r\n height = constants.INNER_SVG_HEIGHT,\r\n radius = this.nodeIconRadius,\r\n maxMarginX = d3.max(this.nodeDataArray, function (d) {\r\n return Math.max(-d.x + radius, d.x + radius - width, 0);\r\n }),\r\n maxMarginY = d3.max(this.nodeDataArray, function (d) {\r\n return Math.max(-d.y + radius, d.y + radius - height, 0);\r\n });\r\n if (maxMarginX > 0 || maxMarginY > 0) {\r\n // If the graph (without the current zoom/pan) exceeds the view boundaries,\r\n // calculate the zoom/pan extent to return it to the viewport.\r\n var scaleX = width / (width + 2 * maxMarginX),\r\n scaleY = height / (height + 2 * maxMarginY);\r\n scale = Math.min(scaleX, scaleY) * 0.95;\r\n translate = [(width / 2) * (1 - scale), (height / 2) * (1 - scale)];\r\n // If the calculated zoom is bigger than the zoom limit, increase the limit\r\n if (scale < constants.MAX_ZOOM) {\r\n this.zoom.scaleExtent([scale, constants.MIN_ZOOM]);\r\n }\r\n } else {\r\n // If the graph, without the current zoom/pan, is within the view boundaries,\r\n // then simply reset the zoom/pan extent.\r\n scale = 1;\r\n translate = [0, 0];\r\n }\r\n this.svg.transition()\r\n .duration(constants.ANIMATION_DURATION)\r\n .call(this.zoom.translate(translate).scale(scale).event);\r\n return this;\r\n };\r\n\r\n //---------------------------------------------------\r\n // preventNodesOverlap\r\n // A collision-detection algorithm, Based on\r\n // http://www.coppelia.io/2014/07/an-a-to-z-of-extra-features-for-the-d3-force-layout/\r\n // and http://bl.ocks.org/mbostock/7881887\r\n //---------------------------------------------------\r\n //proto.preventNodesOverlap = function (alpha) {\r\n // var radius = this.nodeIconRadius,\r\n // padding = constants.NODE_MARGIN,\r\n // quadtree = d3.geom.quadtree(this.nodeDataArray);\r\n // return function (d) {\r\n // var rb = 2 * radius + padding,\r\n // nx1 = d.x - rb,\r\n // nx2 = d.x + rb,\r\n // ny1 = d.y - rb,\r\n // ny2 = d.y + rb;\r\n // quadtree.visit(function (quad, x1, y1, x2, y2) {\r\n // if (quad.point && (quad.point !== d)) {\r\n // var x = d.x - quad.point.x,\r\n // y = d.y - quad.point.y,\r\n // l = Math.sqrt(x * x + y * y);\r\n // if (l < rb) {\r\n // l = (l - rb) / l * alpha;\r\n // d.x -= x *= l;\r\n // d.y -= y *= l;\r\n // quad.point.x += x;\r\n // quad.point.y += y;\r\n // }\r\n // }\r\n // return x1 > nx2 || x2 < nx1 || y1 > ny2 || y2 < ny1;\r\n // });\r\n // };\r\n //};\r\n\r\n /**\r\n * @ngdoc method\r\n * @name forceHorse.factory:ForceHorseFactory#onClick\r\n * @description\r\n * Event handler. called when an element is clicked on\r\n * @returns {ForceHorseFactory} current instance\r\n */\r\n proto.onClick = function (item, element) {\r\n // Ignore the click event at the end of a drag\r\n if (!d3.event.defaultPrevented) {\r\n // If the Ctrl key was pressed during the click ..\r\n // If the clicked element was marked as selected, unselect it, and vice versa\r\n if (d3.event.ctrlKey) {\r\n this.onSelectInside(element, item, !item.selected);\r\n } else {\r\n // If the Ctrl key was not pressed ..\r\n // If the clicked element is selected, unselect the other elements\r\n // (if only the clicked element is selected, unselect it)\r\n // Else, clear the current selection, and select the clicked element\r\n if (item.selected && (this.selectedItems[constants.NODES].size + this.selectedItems[constants.EDGES].size) === 1) {\r\n this.onSelectInside(element, item, false);\r\n } else {\r\n this.onSelectInside(element, item, true, true);\r\n }\r\n }\r\n }\r\n // Prevent bubbling, so that we can separately detect a click on the container\r\n d3.event.stopPropagation();\r\n return this;\r\n };\r\n\r\n /**\r\n * @ngdoc method\r\n * @name forceHorse.factory:ForceHorseFactory#onContainerClick\r\n * @description\r\n * Event handler. on a click not on a node or edge\r\n * Cancel current selection\r\n * @returns {ForceHorseFactory} current instance\r\n */\r\n proto.onContainerClick = function () {\r\n //console.log(\"Container was clicked\");\r\n if (this.selectedItems[constants.NODES].size + this.selectedItems[constants.EDGES].size > 0) {\r\n this.onSelectInside(null, null, null, true);\r\n }\r\n return this;\r\n };\r\n\r\n /**\r\n * @ngdoc method\r\n * @name forceHorse.factory:ForceHorseFactory#onHoverInside\r\n * @description\r\n * An element was hovered inside this component.\r\n * @param item A data object\r\n * @param element The corresponding DOM element\r\n * @param {boolean} on\r\n * @returns {ForceHorseFactory} current instance\r\n */\r\n proto.onHoverInside = function (element, item, on) {\r\n d3.select(element).classed(\"hovered\", item.hovered = on);\r\n return this.callEventListeners('hover', item, on);\r\n };\r\n\r\n /**\r\n * @ngdoc method\r\n * @name forceHorse.factory:ForceHorseFactory#onHoverOutside\r\n * @description\r\n * An element was hovered outside this component.\r\n * @param item data object of the hovered element\r\n * @returns {ForceHorseFactory} current instance\r\n */\r\n proto.onHoverOutside = function (item) {\r\n var itemType = (item.class === constants.CLASS_NODE ?\r\n constants.NODES : constants.EDGES);\r\n this.elements[itemType].filter(function (d) {\r\n return d.id === item.id;\r\n })\r\n .classed(\"hovered\", item.hovered);\r\n return this;\r\n };\r\n\r\n /**\r\n * @ngdoc method\r\n * @name forceHorse.factory:ForceHorseFactory#onSelectInside\r\n * @description\r\n * Called when an element is meant to be selected inside this component.\r\n * @param item The data object bound to the selected element\r\n * @param element The DOM element\r\n * @param {boolean} on Select or Unselect\r\n * @param {boolean} clearOldSelection whether to clear first the current selection\r\n * @returns {ForceHorseFactory} current instance\r\n */\r\n proto.onSelectInside = function (element, item, on, clearOldSelection) {\r\n var myInstance = this;\r\n var itemType;\r\n\r\n if (clearOldSelection) {\r\n for (itemType = constants.NODES; itemType <= constants.EDGES; itemType++) {\r\n myInstance.elements[itemType].filter(function (d) {\r\n return myInstance.selectedItems[itemType].has(d.id);\r\n }).classed(\"selected\", function (d) {\r\n return d.selected = false;\r\n });\r\n myInstance.selectedItems[itemType].clear();\r\n }\r\n }\r\n\r\n // Update the DOM element\r\n if (element) {\r\n d3.select(element).classed(\"selected\", item.selected = on);\r\n }\r\n\r\n // Update the labels\r\n this.labels.classed(\"selected\", function (d) {\r\n return d.selected;\r\n });\r\n\r\n // Update the selectedItems set\r\n if (item) {\r\n itemType = (item.class === constants.CLASS_NODE ? constants.NODES : constants.EDGES);\r\n if (item.selected) {\r\n this.selectedItems[itemType].add(item.id);\r\n } else {\r\n this.selectedItems[itemType].delete(item.id);\r\n }\r\n }\r\n\r\n // In \"selectionMode\" the unselected nodes are visually marked\r\n this.svg.classed(\"selectionMode\",\r\n this.selectedItems[constants.NODES].size + myInstance.selectedItems[constants.EDGES].size);\r\n\r\n return this.callEventListeners('select');\r\n };\r\n\r\n /**\r\n * @ngdoc method\r\n * @name forceHorse.factory:ForceHorseFactory#onSelectOutside\r\n * @description\r\n * API: Called when elements were selected and/or unselected outside this component.\r\n * @returns {ForceHorseFactory} current instance\r\n */\r\n proto.onSelectOutside = function () {\r\n var myInstance = this;\r\n // Update the \"selected\" css class, and the selected-items sets\r\n for (var itemType = constants.NODES; itemType <= constants.EDGES; itemType++) {\r\n (function (mySet) {\r\n mySet.clear();\r\n myInstance.elements[itemType]\r\n .classed('selected', function (d) {\r\n if (d.selected) {\r\n mySet.add(d.id);\r\n return true;\r\n } else {\r\n return false;\r\n }\r\n });\r\n }(this.selectedItems[itemType]))\r\n }\r\n\r\n // Update the labels\r\n this.labels.classed(\"selected\", function (d) {\r\n return d.selected;\r\n });\r\n\r\n // In \"selectionMode\" the unselected nodes are visually marked\r\n this.svg.classed(\"selectionMode\",\r\n this.selectedItems[constants.NODES].size + myInstance.selectedItems[constants.EDGES].size);\r\n return this;\r\n };\r\n\r\n /**\r\n * @ngdoc method\r\n * @name forceHorse.factory:ForceHorseFactory#onZoom\r\n * @description\r\n * Perform pan/zoom\r\n * @returns {ForceHorseFactory} current instance\r\n */\r\n proto.onZoom = function () {\r\n var trans = d3.event.translate,\r\n scale = d3.event.scale;\r\n\r\n if (this.inSvgWrapper) {\r\n this.inSvgWrapper.attr(\"transform\",\r\n \"translate(\" + trans + \")\"\r\n + \" scale(\" + scale + \")\");\r\n }\r\n return this;\r\n };\r\n\r\n /**\r\n * @ngdoc method\r\n * @name forceHorse.factory:ForceHorseFactory#onDrag\r\n * @description\r\n * Node-dragging event handler\r\n * @param d The data item bound to the dragged node\r\n * @returns {ForceHorseFactory} current instance\r\n */\r\n proto.onDrag = function (d) {\r\n // Make the dragged node fixed (not moved by the simulation)\r\n this.elements[constants.NODES].filter(function (nodeData) {\r\n return nodeData.id === d.id;\r\n }).classed(\"fixed\", d.fixed = true);\r\n\r\n if (!this.isDragging) {\r\n this.isDragging = true;\r\n }\r\n return this;\r\n };\r\n\r\n /**\r\n * @ngdoc method\r\n * @name forceHorse.factory:ForceHorseFactory#onDragEnd\r\n * @description\r\n * Event handler, called when a node-dragging ends\r\n * @returns {ForceHorseFactory} current instance\r\n */\r\n proto.onDragEnd = function () {\r\n this.isDragging = false;\r\n return this;\r\n };\r\n\r\n /**\r\n * @ngdoc method\r\n * @name forceHorse.factory:ForceHorseFactory#toggleFixedNodesMode\r\n * @description\r\n * Called from Pause/Play button\r\n * Pause fixes all the nodes\r\n * Play unfixes all the nodes\r\n * @returns {ForceHorseFactory} current instance\r\n */\r\n proto.toggleFixedNodesMode = function () {\r\n if (this.fixedNodesMode) {\r\n this.elements[constants.NODES].classed('fixed', function (d) {\r\n return d.fixed = false;\r\n });\r\n this.fixedNodesMode = false;\r\n this.force.start();\r\n } else {\r\n this.elements[constants.NODES].classed('fixed', function (d) {\r\n return d.fixed = true;\r\n });\r\n this.fixedNodesMode = true;\r\n }\r\n return this;\r\n };\r\n\r\n /**\r\n * @ngdoc method\r\n * @name forceHorse.factory:ForceHorseFactory#onLabelsShowHideBtnClick\r\n * @description\r\n * Show or hide labels\r\n * Called when the labels button is clicked on\r\n * @returns {ForceHorseFactory} current instance\r\n */\r\n proto.onLabelsShowHideBtnClick = function () {\r\n //var myInstance = this;\r\n if (this.config.showLabels = !this.config.showLabels) {\r\n this.labelGroup.classed('display_none', false);\r\n //this.labelGroup.transition().attr(\"opacity\", \"0\");\r\n //setTimeout(function () {\r\n // myInstance.labelGroup.classed('display_none', true);\r\n //}, constants.ANIMATION_DELAY);\r\n } else { // show labels\r\n this.labelGroup.classed('display_none', true);\r\n //setTimeout(function () {\r\n // myInstance.labelGroup.transition().attr(\"opacity\", \"1\");\r\n //}, constants.ANIMATION_DELAY);\r\n }\r\n return this;\r\n };\r\n\r\n /**\r\n * @ngdoc method\r\n * @name forceHorse.factory:ForceHorseFactory#onNodeWeightShowHideBtnClick\r\n * @description\r\n * Show or hide node weights\r\n * Called when the node weight button is clicked on\r\n * @returns {ForceHorseFactory} current instance\r\n */\r\n proto.onNodeWeightShowHideBtnClick = function () {\r\n var myInstance = this;\r\n this.config.showNodeWeight = !this.config.showNodeWeight;\r\n this.elements[constants.NODES]\r\n .attr(\"d\", d3.svg.symbol()\r\n .type(function (d) {\r\n return d.shape;\r\n })\r\n .size(function (d) {\r\n return myInstance.getNodeIconArea(d);\r\n }));\r\n return this;\r\n };\r\n\r\n /**\r\n * @ngdoc method\r\n * @name forceHorse.factory:ForceHorseFactory#onEdgeWeightShowHideBtnClick\r\n * @description\r\n * Show or hide edge weights\r\n * Called when the edge weight button is clicked on\r\n * @returns {ForceHorseFactory} current instance\r\n */\r\n proto.onEdgeWeightShowHideBtnClick = function () {\r\n var myInstance = this;\r\n this.config.showEdgeWeight = !this.config.showEdgeWeight;\r\n this.elements[constants.EDGES]\r\n .attr(\"stroke-width\", (!this.config.showEdgeWeight ? null : function (d) {\r\n return myInstance.getEdgeWidth(d);\r\n }));\r\n return this;\r\n };\r\n\r\n /**\r\n * @ngdoc method\r\n * @name forceHorse.factory:ForceHorseFactory#addEventListener\r\n * @description\r\n * API: Register event callbacks with this component\r\n * @param type The event type\r\n * @param callback The event listener to register\r\n * @returns {ForceHorseFactory} current instance\r\n */\r\n proto.addEventListener = function (type, callback) {\r\n if (typeof this.eventListeners[type] === 'undefined'){\r\n this.eventListeners[type] = [];\r\n }\r\n this.eventListeners[type].push(callback);\r\n return this;\r\n };\r\n\r\n /**\r\n * @ngdoc method\r\n * @name forceHorse.factory:ForceHorseFactory#callEventListeners\r\n * @description\r\n * Call the registered event listeners, for an event type\r\n * @param type The event type (hover, select, ...)\r\n * @param args Arguments for the event listener\r\n * @returns {ForceHorseFactory} current instance\r\n */\r\n proto.callEventListeners = function (type, ...args) {\r\n if (typeof this.eventListeners[type] === 'undefined'){\r\n return;\r\n }\r\n this.eventListeners[type].forEach(function (callback) {\r\n callback(...args);\r\n });\r\n return this;\r\n };\r\n\r\n\r\n /**\r\n * @ngdoc method\r\n * @name forceHorse.factory:ForceHorseFactory#convertFileDataFormat\r\n * @description api. See the method in helper.\r\n * @param fileData\r\n * @returns {*|*[]}\r\n */\r\n proto.convertFileDataFormat = function (fileData) {\r\n return helper.convertFileDataFormat(fileData);\r\n };\r\n\r\n //---------------------------------------------------\r\n return ForceHorseFactory;\r\n }])\r\n\r\n\r\n /**\r\n * @ngdoc constant\r\n * @name forceHorse.constant:ForceHorseConstants\r\n * @description A constants object for the forceHorse component\r\n */\r\n .constant('ForceHorseConstants', {\r\n INNER_SVG_WIDTH: 540,\r\n INNER_SVG_HEIGHT: 480,\r\n NODES: 0,\r\n EDGES: 1,\r\n NODES_ID: 1,\r\n EDGES_ID: 2,\r\n CLASS_NODE: 'Node',\r\n CLASS_EDGE: 'Edge',\r\n CSS_CLASS_NODE: 'node',\r\n CSS_CLASS_EDGE: 'edge',\r\n DEFAULT_EDGE_WIDTH: 1.5,\r\n DEFAULT_EDGE_COLOR: 'brown',\r\n DEFAULT_NODE_COLOR: '#6060a0',\r\n NODE_MARGIN: 10,\r\n LABEL_DISPLACEMENT: 10,\r\n MAX_ZOOM: 0.5,\r\n MIN_ZOOM: 2,\r\n ANIMATION_DURATION: 1000,\r\n ANIMATION_DELAY: 200,\r\n ALEPHBET: 'abcdefghijklmnopqrstuvwxyz',\r\n INSTANCE_NAME_LENGTH: 5,\r\n MAX_ALPHA: 0.1,\r\n HEAVY_SIMULATION_NUM_OF_NODES: 420,\r\n DEFAULT_CHARGE_LIGHT: -350,\r\n DEFAULT_CHARGE_HEAVY: -15000,\r\n CONFIG_FILE_NAME: 'forceHorse.json',\r\n get node_size_addition_per_weight_unit() {\r\n return this.INNER_SVG_WIDTH * this.INNER_SVG_HEIGHT / (54 * 48 * 3);\r\n }\r\n })\r\n\r\n\r\n /**\r\n * @ngdoc service\r\n * @name forceHorse.service:ForceHorseHelper\r\n * @description A helper object with methods for the forceHorse component\r\n */\r\n //---------------------------------------------------------------//\r\n .service('ForceHorseHelper', ['ForceHorseConstants', '$templateCache', '$compile', function (constants, templates, $compile) {\r\n return {\r\n\r\n /**\r\n * @ngdoc method\r\n * @name forceHorse.service:ForceHorseHelper#addButtons\r\n * @description\r\n * Add a buttons bar, at the top of the container\r\n * @returns {ForceHorseHelper} current object\r\n */\r\n addButtons: function (scope, container) {\r\n var template = templates.get('forceHorse/buttons');\r\n var element = angular.element(template);\r\n var compiledElement = $compile(element)(scope);\r\n container.prepend(compiledElement);\r\n // console.log('Added buttons');\r\n return this;\r\n },\r\n\r\n /**\r\n * @ngdoc method\r\n * @name forceHorse.service:ForceHorseHelper#calcRightAngledOffset\r\n * @description\r\n * Calculate where to display edges, for the case of multiple edges between two nodes\r\n * @param basicOffset The desired distance from the parallel edge to the first edge\r\n * @param origDx The x-difference between the two end points of the first edge\r\n * @param origDy The y-difference between the two end points of the first edge\r\n * @returns {Object} {dx:dx, dy:dy} The calculated offset of the parallel edge from the first edge\r\n */\r\n calcRightAngledOffset: function (basicOffset, origDx, origDy) {\r\n var dx, dy;\r\n if (basicOffset === 0) {\r\n dx = dy = 0;\r\n } else if (origDy === 0 || Math.abs(origDx / origDy) > 1) {\r\n dy = -basicOffset * constants.INNER_SVG_WIDTH / constants.INNER_SVG_HEIGHT;\r\n dx = basicOffset * (origDy) / origDx;\r\n } else {\r\n dx = basicOffset;\r\n dy = basicOffset * (-origDx) / origDy;\r\n }\r\n if (!angular.isNumber(dx)) {\r\n console.warn(`calcRightAngledOffset: dx is not a number! basicOffset=${basicOffset} origDx=${origDx} origDy=${origDy}`);\r\n }\r\n return {dx: dx, dy: dy};\r\n },\r\n\r\n /**\r\n * @ngdoc method\r\n * @name forceHorse.service:ForceHorseHelper#computeFrictionParameter\r\n * @description\r\n * Compute the friction parameter for the force-simulation, with a mysterious formula supplied by Omer.\r\n * @param {number} width_in_pixels Width of the simulation area\r\n * @param {number} height_in_pixels Height of the simulation area\r\n * @param {number} number_of_nodes No. of nodes in the graph\r\n * @returns {number}\r\n */\r\n computeFrictionParameter: function (width_in_pixels, height_in_pixels, number_of_nodes) {\r\n var A = 0.0356,\r\n B = 1.162,\r\n x = 100 * number_of_nodes / (height_in_pixels * width_in_pixels);\r\n if (x < 0.0634) x = 0.0634;\r\n return A * Math.pow(x, -B);\r\n },\r\n\r\n /**\r\n * @ngdoc method\r\n * @name forceHorse.service:ForceHorseHelper#isHebrewString\r\n * @description\r\n * Does the given string start with a hebrew letter?\r\n * @param {string} s\r\n * @returns {boolean}\r\n */\r\n isHebrewString: function (s) {\r\n var c = s.charAt(0);\r\n return (c >= 'א' && c <= 'ת');\r\n },\r\n\r\n /**\r\n * @ngdoc method\r\n * @name forceHorse.service:ForceHorseHelper#getCurrentDirectory\r\n * @description\r\n * See http://stackoverflow.com/a/21103831/4402222\r\n * @returns {string}\r\n */\r\n/*\r\n getCurrentDirectory: () => {\r\n var scripts = document.getElementsByTagName(\"script\");\r\n var currentScriptPath = scripts[scripts.length-1].src;\r\n return currentScriptPath.substring(0,currentScriptPath.lastIndexOf(\"/\")+1 );\r\n },\r\n*/\r\n\r\n /**\r\n * @ngdoc method\r\n * @name forceHorse.service:ForceHorseHelper#convertFileDataFormat\r\n * @param fileData\r\n * @returns {*[]}\r\n * @description\r\n * fileData is supposed to be in the format\r\n * {nodes: [nodeData, nodeData, ...] links: [linkData, linkData, ...]}\r\n * \"edges\" are also allowed, in place of \"links\".\r\n * If nodeData does not contain an id property, its id is set to its index in the array.\r\n * If nodeData does not contain a label property, it gets a default label.\r\n * A \"class\" property (node class) is also added to each nodeData.\r\n * If linkData does not contain an id property, its id is set to its index in the array.\r\n * If linkData does not contain an sourceID property, sourceID is set to source.\r\n * If linkData does not contain an targetID property, targetID is set to target.\r\n * A \"class\" property (link class) is also added to each linkData.\r\n * Also sourceLabel, targetLabel.\r\n * The resulting data is returned restructured like:\r\n * [ {id: constants.NODES_ID, data: nodesArray}, {id: constants.LINKS_ID, data: linksArray} ]\r\n ]\r\n */\r\n convertFileDataFormat: function (fileData) {\r\n // Process nodes\r\n var nodes = fileData.nodes;\r\n nodes.forEach(function (node, idx) {\r\n if (angular.isUndefined(node.id)) {\r\n node.id = idx;\r\n }\r\n if (angular.isUndefined(node.label)) {\r\n node.label = \"\" + node.id;\r\n }\r\n node.class = constants.CLASS_NODE;\r\n });\r\n // Process edges\r\n var edges = (fileData.edges ? fileData.edges : fileData.links);\r\n edges.forEach( function(edge, idx) {\r\n if (angular.isUndefined(edge.id)) {\r\n edge.id = idx;\r\n }\r\n if (angular.isUndefined(edge.sourceID)) {\r\n edge.sourceID = edge.source;\r\n }\r\n if (angular.isUndefined(edge.targetID)) {\r\n edge.targetID = edge.target;\r\n }\r\n edge.sourceLabel = edge.sourceID;\r\n edge.targetLabel = edge.targetID;\r\n edge.class = constants.CLASS_EDGE;\r\n });\r\n // Return the (processed) data\r\n return [\r\n {id: constants.NODES_ID, data: nodes},\r\n {id: constants.EDGES_ID, data: edges}\r\n ];\r\n }\r\n\r\n\r\n }; // return {\r\n }]) // .service\r\n;\r\n"]} \ No newline at end of file +{"version":3,"sources":["forceHorse.js"],"names":[],"mappings":"AAAA;AACA;;;;;;AAKA,QAAQ,MAAR,CAAe,YAAf,EAA6B,EAA7B;;AAEI;AAFJ,CAGK,GAHL,CAGS,UAAU,cAAV,EAA0B;AAC3B;AACA,mBAAe,GAAf,CAAmB,oBAAnB,EACI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;mBADJ;AA+BH,CApCL;;AAsCI;;;;;;;;AAtCJ,CA8CK,SA9CL,CA8Ce,YA9Cf,EA8C6B,CAAC,UAAD,EAAa,mBAAb,EAAkC,kBAAlC,EAAsD,UAAU,QAAV,EAAoB,iBAApB,EAAuC,MAAvC,EAA+C;AAC1H,WAAO;AACH,kBAAU,IADP;AAEH,sBAAc,gBAFX;AAGH,kBAAU,GAHP;AAIH,eAAO;AACH,qBAAS;AADN,SAJJ;AAOH,0BAAkB,IAPf;;AASH,oBAAY,oBAAU,MAAV,EAAkB,QAAlB,EAA4B;AACpC,gBAAI,KAAK,IAAT;AACA;AACA;AACA,iBAAK,aAAL,GAAqB,YAAY;AAC7B,oBAAI;AACA,2BAAO,OAAP;AACH,iBAFD,CAEE,OAAO,CAAP,EAAS,CAAE;AAChB,aAJD;;AAMA,iBAAK,OAAL,CAAa,kBAAb,GACI,OAAO,kBAAP,GAA4B,IAAI,iBAAJ,CAAsB,QAAtB,EAAgC,KAAK,OAArC,EAA8C,KAAK,aAAnD,EACvB,MADuB,EADhC;;AAIA;AACA,mBAAO,GAAP,CAAW,UAAX,EAAuB,YAAY;AAC/B,wBAAQ,GAAR,CAAY,gCAAZ;AACA,mBAAG,OAAH,CAAW,kBAAX,GACI,OAAO,kBAAP,GAA4B,IADhC;AAEH,aAJD;AAKH,SA7BE;;AA+BH,cAAM,cAAU,KAAV,EAAiB,OAAjB,EAA0B;AAAE;AAC9B;;AAEA;AACA,oBAAQ,QAAR,CAAiB,aAAjB;AACA;AACA;AACA;AACA;AACA,mBAAO,UAAP,CAAkB,KAAlB,EAAyB,OAAzB;AACH;AAzCE,KAAP;AA2CH,CA5CwB,CA9C7B;;AA4FI;;;;;AA5FJ,CAiGK,OAjGL,CAiGa,mBAjGb,EAiGkC,CAAC,OAAD,EAAU,MAAV,EAAkB,qBAAlB,EAAyC,kBAAzC,EAC1B,UAAU,KAAV,EAAiB,IAAjB,EAAuB,SAAvB,EAAkC,MAAlC,EAA0C;AAC1C;;;;;;;;;;AAUA,aAAS,iBAAT,CAA2B,OAA3B,EAAoC,OAApC,EAA6C,aAA7C,EAA4D;AACxD,aAAK,OAAL,GAAe,QAAQ,CAAR,CAAf;AACA,aAAK,OAAL,GAAe,OAAf;AACA,aAAK,aAAL,GAAqB,aAArB;AACA;AACA,aAAK,cAAL,GAAsB,EAAtB;AACH;;AAED,QAAI,QAAQ,kBAAkB,SAA9B;;AAEA;;;;;;AAMA,UAAM,MAAN,GAAe,YAAY;AACvB,YAAI,aAAa,IAAjB;AACA,YAAI,UAAU,SAAV,OAAU,CAAU,IAAV,EAAgB;AAC1B,uBAAW,UAAX,CAAsB,IAAtB;AACA;AACA;AACA,uBAAW,sBAAX;AACA,uBAAW,IAAX;AACH,SAND;AAOA;AACA;AACA,cAAM,GAAN,CAAU,UAAU,gBAApB,EACC,IADD,CACM,UAAU,QAAV,EAAoB;AACtB,oBAAQ,SAAS,IAAjB;AACH,SAHD,EAGG,UAAU,QAAV,EAAoB;AACnB,iBAAK,IAAL,CAAU,UAAU,gBAAV,GAA6B,GAA7B,GAAmC,SAAS,UAAtD;AACA,oBAAQ,EAAR;AACH,SAND;AAOA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,eAAO,IAAP;AACH,KA5BD;;AA8BA;;;;;;;AAOA,UAAM,UAAN,GAAmB,UAAU,MAAV,EAAkB;AACjC,YAAI,aAAa,IAAjB;AACA;AACA,aAAK,YAAL,GAAoB,IAAI,KAAJ,CAAU,UAAU,oBAApB,EAA0C,IAA1C,CAA+C,IAA/C,EAAqD,GAArD,CAAyD,YAAY;AACrF,mBAAO,UAAU,QAAV,CAAmB,MAAnB,CAA0B,KAAK,KAAL,CAAW,KAAK,MAAL,KAAgB,UAAU,QAAV,CAAmB,MAA9C,CAA1B,CAAP;AACH,SAFmB,EAEjB,IAFiB,CAEZ,EAFY,CAApB;;AAIA;AACA,YAAI,OAAO,KAAK,OAAL,CAAa,IAAxB;AACA,YAAI,EAAE,gBAAgB,KAAlB,CAAJ,EAA8B;AAC1B,mBAAO,OAAO,qBAAP,CAA6B,IAA7B,CAAP;AACH;AACD,aAAK,aAAL,GAAqB,KAAK,UAAU,KAAf,EAAsB,IAA3C;AACA,aAAK,aAAL,GAAqB,KAAK,UAAU,KAAf,EAAsB,IAA3C;AACA,aAAK,YAAL;AACA,aAAK,YAAL;;AAEA;AACA;AACA,aAAK,UAAL,GAAkB,KAAK,aAAL,CAAmB,MAArC;AACA,aAAK,mBAAL,GAA2B,UAAU,eAAV,GAA4B,EAA5B,GAAiC,UAAU,gBAA3C,GAA8D,EAA9D,GAAmE,CAA9F;AACA,aAAK,cAAL,GAAsB,KAAK,IAAL,CAAU,KAAK,mBAAL,GAA2B,KAAK,EAA1C,CAAtB;AACA,aAAK,aAAL,GAAqB,CAAC,IAAI,GAAJ,EAAD,EAAY,IAAI,GAAJ,EAAZ,CAArB,CAtBiC,CAsBY;AAC7C,aAAK,cAAL,GAAsB,KAAtB;AACA;AACA,aAAK,eAAL,GAAuB,KAAvB,CAzBiC,CAyBH;AAC9B,aAAK,UAAL,GAAkB,KAAlB;;AAEA;AACA;AACA,aAAK,MAAL,GAAc;AACV,wBAAY,KADF;AAEV,4BAAgB,KAFN;AAGV,4BAAgB,KAHN;AAIV,8BAAkB,IAJR;AAKV,8BAAkB,IALR;AAMV,kCAAsB,IANZ;AAOV,kCAAsB,IAPZ;AAQV,6BAAiB,KARP;AASV,6BAAiB;AACb;AACA,8BAAc,CAFD;AAGb,yBAAS,GAHI;AAIb,8BAAc;AAJD;AATP,SAAd;AAgBA,eAAO,MAAP,CAAc,KAAK,MAAnB,EAA2B,MAA3B;;AAEA;AACA,mBAAW,KAAX,GAAmB,GAAG,eAAH,EAAnB;AACI;AACA;AACA;AACA;AACJ;;AAEA,YAAI,CAAJ;;AAEA,YAAI,cAAc,GAAG,WAAH,CAAe,UAAU,eAAV,GAA4B,CAA3C,EAA8C,UAAU,gBAAV,GAA6B,CAA3E,CAAlB;AACA,mBAAW,KAAX,CAAiB,KAAjB,CAAuB,QAAvB,EAAiC,WAAjC;AACA;AACA;AACA;;AAEA,YAAI,QAAQ,SAAR,CAAkB,IAAI,WAAW,MAAX,CAAkB,eAAlB,CAAkC,MAAxD,CAAJ,EAAqE,CACpE,CADD,MACO;AACH,gBAAI,WAAW,UAAX,GAAwB,UAAU,6BAAtC,EAAqE;AACjE,oBAAI,WAAU,CAAV,EAAa;AACb,2BAAO,EAAE,MAAF,GAAW,UAAU,oBAA5B;AACH,iBAFD;AAGH,aAJD,MAIO;AACH,oBAAI,UAAU,oBAAd;AACH;AACJ;AACD,mBAAW,KAAX,CAAiB,KAAjB,CAAuB,QAAvB,EAAiC,GAAG,aAAH,GAAmB,QAAnB,CAA4B,CAA5B,CAAjC;AACA;;AAEA,YAAI,QAAQ,SAAR,CAAkB,IAAI,WAAW,MAAX,CAAkB,eAAlB,CAAkC,QAAxD,CAAJ,EAAuE,CACtE,CADD,MACO;AACH,gBAAI,OAAO,wBAAP,CAAgC,UAAU,eAA1C,EAA2D,UAAU,gBAArE,EAAuF,KAAK,aAAL,CAAmB,MAA1G,CAAJ;AACH;AACD,mBAAW,KAAX,CAAiB,aAAjB,CAA+B,CAA/B;AACA;;AAEA,mBAAW,KAAX,CAAiB,KAAjB,CAAuB,WAAW,aAAlC;AACI;AACJ;;AAEA,YAAI,YAAY,WAAW,KAAX,CAAiB,KAAjB,CAAuB,MAAvB,EACZ,GAAG,SAAH,CAAa,WAAW,aAAxB,EAAuC,EAAvC,CAA0C,UAAS,CAAT,EAAY,CAAZ,EAAe;AAAE,mBAAO,CAAP;AAAW,SAAtE,CADY,CAAhB;AAEA,YAAI,QAAQ,SAAR,CAAkB,IAAI,WAAW,MAAX,CAAkB,eAAlB,CAAkC,YAAxD,CAAJ,EAA2E,UAAU,QAAV,CAAmB,CAAnB;AAC3E,YAAI,QAAQ,SAAR,CAAkB,IAAI,WAAW,MAAX,CAAkB,eAAlB,CAAkC,YAAxD,CAAJ,EAA2E,UAAU,QAAV,CAAmB,CAAnB;;AAE3E,mBAAW,IAAX,GAAkB,GAAG,IAAH,GACb,WADa,CACD,CAAC,UAAU,QAAX,EAAqB,UAAU,QAA/B,CADC,EAEb,EAFa,CAEV,MAFU,EAEF,YAAY;AACpB,uBAAW,MAAX;AACH,SAJa,CAAlB;;AAMA;AACA;AACA;AACA,WAAG,MAAH,CAAU,WAAW,OAArB,EACK,MADL,CACY,gBADZ,EAEK,MAFL;AAGA,mBAAW,GAAX,GAAiB,GAAG,MAAH,CAAU,WAAW,OAArB,EACZ,MADY,CACL,KADK,EAEZ,IAFY,CAEP,OAFO,EAEE,YAFF,EAGZ,MAHY,CAGL,KAHK,EAIZ,IAJY,CAIP,OAJO,EAIE,WAJF,EAKZ,IALY,CAKP,SALO,EAKI,SAAS,UAAU,eAAnB,GAAqC,GAArC,GAA2C,UAAU,gBALzD,EAMZ,IANY,CAMP,qBANO,EAMgB,MANhB,EAOZ,EAPY,CAOT,OAPS,EAOA,YAAY;AACrB,uBAAW,gBAAX;AACH,SATY,EAUZ,IAVY,CAUP,WAAW,IAVJ;AAWb;AACA;AAZJ;;AAeA;AACA,mBAAW,YAAX,GAA0B,WAAW,GAAX,CAAe,MAAf,CAAsB,GAAtB,CAA1B;;AAEA;AACA;AACA;AACA,mBAAW,SAAX,GAAuB,WAAW,YAAX,CAAwB,MAAxB,CAA+B,GAA/B,EAClB,IADkB,CACb,OADa,EACJ,OADI,EAElB,IAFkB,CAEb,QAFa,EAEH,UAAU,kBAFP,EAGlB,IAHkB,CAGb,cAHa,EAGG,UAAU,kBAAV,GAA+B,IAHlC,CAAvB;AAIA,mBAAW,SAAX,GAAuB,WAAW,YAAX,CAAwB,MAAxB,CAA+B,GAA/B,EAClB,IADkB,CACb,OADa,EACJ,OADI,EAElB,IAFkB,CAEb,MAFa,EAEL,UAAU,kBAFL,CAAvB;AAGA,mBAAW,UAAX,GAAwB,WAAW,YAAX,CAAwB,MAAxB,CAA+B,GAA/B,EACnB,IADmB,CACd,OADc,EACL,QADK,EAEnB,IAFmB,CAEd,MAFc,EAEN,UAAU,kBAFJ,EAGnB,OAHmB,CAGX,cAHW,EAGK,CAAC,WAAW,MAAX,CAAkB,UAHxB,CAAxB;;AAKA,mBAAW,IAAX,GAAkB,GAAG,IAAH;AACd;AADc,SAEb,EAFa,CAEV,MAFU,EAEF,UAAU,CAAV,EAAa;AACrB,uBAAW,MAAX,CAAkB,CAAlB;AACH,SAJa,EAKb,EALa,CAKV,OALU,EAKD,UAAU,CAAV,EAAa;AACtB,uBAAW,WAAX,CAAuB,CAAvB;AACH,SAPa,EAQb,EARa,CAQV,KARU,EAQH,UAAU,CAAV,EAAa;AACpB,uBAAW,SAAX,CAAqB,CAArB;AACH,SAVa,CAAlB;;AAYA,eAAO,UAAP;AACH,KAvJD,CAhE0C,CAuNvC;;AAEH;;;;;;AAMA,UAAM,IAAN,GAAa,YAAY;AACrB,YAAI,aAAa,IAAjB;AACA,mBAAW,QAAX,GAAsB,IAAI,KAAJ,CAAU,CAAV,CAAtB,CAFqB,CAEe;;AAEpC;AACA,aAAK,QAAL,CAAc,UAAU,KAAxB,IAAiC,KAAK,SAAL,CAAe,SAAf,CAAyB,MAAM,UAAU,cAAzC,EAC5B,IAD4B,CACvB,KAAK,aADkB,EAE5B,KAF4B,GAG5B,MAH4B,CAGrB,MAHqB,EAI5B,IAJ4B,CAIvB,OAJuB,EAId,UAAU,cAJI,EAK5B,IAL4B,CAKvB,QALuB,EAKb,UAAU,CAAV,EAAa;AACzB,mBAAO,EAAE,KAAT;AACH,SAP4B,EAQ5B,IAR4B,CAQvB,cARuB,EAQN,CAAC,KAAK,MAAL,CAAY,cAAb,GAA8B,IAA9B,GAAqC,UAAU,CAAV,EAAa;AACrE,mBAAO,WAAW,YAAX,CAAwB,CAAxB,CAAP;AACH,SAV4B,EAW5B,EAX4B,CAWzB,YAXyB,EAWX,UAAU,CAAV,EAAa;AAC3B,uBAAW,aAAX,CAAyB,IAAzB,EAA+B,CAA/B,EAAkC,IAAlC;AACH,SAb4B,EAc5B,EAd4B,CAczB,YAdyB,EAcX,UAAU,CAAV,EAAa;AAC3B,uBAAW,aAAX,CAAyB,IAAzB,EAA+B,CAA/B,EAAkC,KAAlC;AACH,SAhB4B,EAiB5B,EAjB4B,CAiBzB,OAjByB,EAiBhB,UAAU,CAAV,EAAa;AACtB,uBAAW,OAAX,CAAmB,CAAnB,EAAsB,IAAtB;AACH,SAnB4B;AAoB7B;AApB6B,SAqB5B,EArB4B,CAqBzB,WArByB,EAqBZ,YAAY;AACzB,eAAG,KAAH,CAAS,eAAT;AACH,SAvB4B,CAAjC;;AA0BA;AACA,aAAK,QAAL,CAAc,UAAU,KAAxB,IAAiC,KAAK,SAAL,CAAe,SAAf,CAAyB,MAAM,UAAU,cAAzC,EAC5B,IAD4B,CACvB,KAAK,aADkB,EAE5B,KAF4B,GAG5B,MAH4B,CAGrB,MAHqB;AAI7B;AAJ6B,SAK5B,IAL4B,CAKvB,GALuB,EAKlB,GAAG,MAAH,GACN,IADM,CACD,UAAU,CAAV,EAAa;AACf,mBAAO,EAAE,KAAT;AACH,SAHM,EAIN,IAJM,CAID,UAAU,CAAV,EAAa;AACf,mBAAO,WAAW,eAAX,CAA2B,CAA3B,CAAP;AACH,SANM,CALkB,EAY5B,IAZ4B,CAYvB,MAZuB,EAYf,UAAU,CAAV,EAAa;AACvB,mBAAO,EAAE,KAAT;AACH,SAd4B,EAe5B,IAf4B,CAevB,OAfuB,EAed,UAAU,cAfI,EAgB5B,EAhB4B,CAgBzB,YAhByB,EAgBX,UAAU,CAAV,EAAa;AAC3B,uBAAW,aAAX,CAAyB,IAAzB,EAA+B,CAA/B,EAAkC,IAAlC;AACH,SAlB4B,EAmB5B,EAnB4B,CAmBzB,YAnByB,EAmBX,UAAU,CAAV,EAAa;AAC3B,uBAAW,aAAX,CAAyB,IAAzB,EAA+B,CAA/B,EAAkC,KAAlC;AACH,SArB4B,EAsB5B,EAtB4B,CAsBzB,OAtByB,EAsBhB,UAAU,CAAV,EAAa;AACtB,uBAAW,OAAX,CAAmB,CAAnB,EAAsB,IAAtB;AACH,SAxB4B,EAyB5B,EAzB4B,CAyBzB,UAzByB,EAyBb,UAAU,CAAV,EAAa;AACzB,uBAAW,kBAAX,CAA8B,UAA9B,EAA0C,CAA1C;AACH,SA3B4B;AA4B7B;AA5B6B,SA6B5B,EA7B4B,CA6BzB,WA7ByB,EA6BZ,YAAY;AACzB,eAAG,KAAH,CAAS,eAAT;AACH,SA/B4B,EAgC5B,IAhC4B,CAgCvB,KAAK,IAhCkB,CAAjC;;AAkCA;AACA,aAAK,MAAL,GAAc,KAAK,UAAL,CAAgB,SAAhB,CAA0B,YAA1B,EACT,IADS,CACJ,KAAK,aADD,EAET,KAFS,GAGT,MAHS,CAGF,MAHE,EAIT,IAJS,CAIJ,MAJI,EAII,UAAU,CAAV,EAAa;AACvB,mBAAO,EAAE,KAAT;AACH,SANS,EAOT,IAPS,CAOJ,UAAU,CAAV,EAAa;AACf,mBAAO,EAAE,KAAT;AACH,SATS,EAUT,IAVS,CAUJ,IAVI,EAUE,UAAU,CAAV,EAAa;AACrB,mBAAO,CAAC,OAAO,cAAP,CAAsB,EAAE,KAAxB,IAAiC,CAAC,CAAlC,GAAsC,CAAC,CAAxC,IAA6C,UAAU,kBAA9D;AACH,SAZS,EAaT,IAbS,CAaJ,aAbI,EAaW,UAAU,CAAV,EAAa;AAC9B,mBAAQ,OAAO,cAAP,CAAsB,EAAE,KAAxB,IAAiC,KAAjC,GAAyC,OAAjD;AACH,SAfS,CAAd;;AAkBA;AACA,aAAK,WAAL,GAAmB,KAAK,GAAL,CACd,MADc,CACP,MADO,EAEd,IAFc,CAET,OAFS,EAEA,UAFA,EAGd,IAHc,CAGT,IAHS,EAGH,GAHG,EAId,IAJc,CAIT,IAJS,EAIH,GAJG,EAKd,IALc,CAKT,IALS,EAKH,GALG,EAMd,IANc,CAMT,IANS,EAMH,GANG,CAAnB;;AAQA;AACA,WAAG,MAAH,CAAU,MAAV,EAAkB,EAAlB,aAA+B,KAAK,YAApC,EAAoD,YAAY;AAC5D,uBAAW,cAAX;AACH,SAFD;;AAIA,eAAO,IAAP;AACH,KApGD;;AAsGA;;;;;;AAMA,UAAM,sBAAN,GAA+B,YAAY;AACvC,aAAK,KAAL,CAAW,KAAX,CAAiB,UAAU,SAA3B;AACA,aAAK,YAAL;AACA;AACA,eAAO,IAAP;AACH,KALD;;AAOA;;;;;;AAMA,UAAM,kBAAN,GAA2B,YAAY;AACnC,YAAI,cAAc,KAAK,GAAL,CAAS,OAAT,CAAiB,CAAjB,EAAoB,CAApB,EAAuB,qBAAvB,EAAlB;AAAA,YACI,gBAAgB,YAAY,MADhC;AAAA,YAEI,eAAe,YAAY,KAF/B;AAGA,aAAK,cAAL,GAAuB,UAAU,eAAV,GAA4B,UAAU,gBAAvC,IAA4D,gBAAgB,YAA5E,CAAtB;AACA,eAAO,IAAP;AACH,KAND;;AAQA;;;;;;AAMA,UAAM,eAAN,GAAwB,UAAU,QAAV,EAAoB;AACxC,YAAI,aAAa,IAAjB;AACA,eAAO,WAAW,mBAAX,IACF,WAAW,MAAX,CAAkB,cAAlB,GACK,CAAC,WAAW,MAAX,CAAkB,eAAlB,GACG,SAAS,WADZ,GAEG,QAAQ,SAAR,CAAkB,SAAS,MAA3B,IACI,SAAS,MADb,GAEI,CAJR,IAKI,UAAU,kCANnB,GAOK,CARH,CAAP;AASH,KAXD;;AAaA;;;;;;AAMA,UAAM,YAAN,GAAqB,UAAU,QAAV,EAAoB;AACrC,eAAO,UAAU,kBAAV,GAAgC,SAAS,MAAT,GAAkB,CAAlD,GAAuD,IAA9D;AACH,KAFD;;AAIA;;;;;;AAMA,UAAM,cAAN,GAAuB,YAAY;AAC/B;AACA;AACA,aAAK,IAAI,WAAW,UAAU,KAA9B,EAAqC,YAAY,UAAU,KAA3D,EAAkE,UAAlE,EAA8E;AAC1E,iBAAK,QAAL,CAAc,QAAd,EAAwB,MAAxB,CAA+B,UAAU,IAAV,EAAgB;AAC3C,uBAAO,KAAK,QAAZ;AACH,aAFD,EAEG,OAFH,CAEW,UAFX,EAEuB,UAAU,CAAV,EAAa;AAChC,uBAAO,EAAE,QAAF,GAAa,IAApB;AACH,aAJD,EAIG,OAJH,CAIW,UAJX,EAIuB,UAAU,CAAV,EAAa;AAChC,uBAAO,EAAE,QAAF,GAAa,KAApB;AACH,aAND;AAOA,iBAAK,aAAL,CAAmB,QAAnB,EAA6B,KAA7B;AACH;;AAED;AACA,aAAK,MAAL,CAAY,OAAZ,CAAoB,UAApB,EAAgC,OAAhC,EACK,OADL,CACa,UADb,EACyB,UAAU,CAAV,EAAa;AAC9B,mBAAO,EAAE,QAAT;AACH,SAHL;;AAKA;AACA,aAAK,QAAL,CAAc,UAAU,KAAxB,EAA+B,MAA/B,CAAsC,UAAU,CAAV,EAAa;AAC/C,mBAAO,EAAE,MAAF,CAAS,QAAT,IAAqB,EAAE,MAAF,CAAS,QAArC;AACH,SAFD,EAEG,OAFH,CAEW,UAFX,EAEuB,UAAU,CAAV,EAAa;AAChC,mBAAO,EAAE,QAAF,GAAa,IAApB;AACH,SAJD;;AAMA;AACA,aAAK,GAAL,CAAS,OAAT,CAAiB,eAAjB,EAAkC,KAAlC;;AAEA;AACA,aAAK,kBAAL,CAAwB,QAAxB;;AAEA,eAAO,IAAP;AACH,KAlCD;;AAoCA;;;;;;AAMA,UAAM,eAAN,GAAwB,YAAY;AAChC,YAAI,aAAa,IAAjB;AACA;AACA;AACA,aAAK,IAAI,WAAW,UAAU,KAA9B,EAAqC,YAAY,UAAU,KAA3D,EAAkE,UAAlE,EAA8E;AAC1E,iBAAK,QAAL,CAAc,QAAd,EAAwB,MAAxB,CAA+B,UAAU,IAAV,EAAgB;AAC3C,uBAAO,KAAK,QAAZ;AACH,aAFD,EAEG,OAFH,CAEW,UAFX,EAEuB,IAFvB,EAGK,OAHL,CAGa,UAHb,EAGyB,KAHzB,EAIK,IAJL,CAIU,UAAU,CAAV,EAAa;AACf,oBAAI,OAAQ,EAAE,KAAF,KAAY,UAAU,UAAtB,GAAmC,UAAU,KAA7C,GAAqD,UAAU,KAA3E;AACA,2BAAW,aAAX,CAAyB,IAAzB,EAA+B,MAA/B,CAAsC,EAAE,EAAxC;AACH,aAPL;AAQH;;AAED;AACA,aAAK,MAAL,CAAY,MAAZ,CAAmB,UAAU,IAAV,EAAgB;AAC/B,mBAAO,KAAK,QAAZ;AACH,SAFD,EAEG,OAFH,CAEW,UAFX,EAEuB,IAFvB,EAGK,OAHL,CAGa,UAHb,EAGyB,KAHzB;;AAKA;AACA,aAAK,QAAL,CAAc,UAAU,KAAxB,EAA+B,MAA/B,CAAsC,UAAU,CAAV,EAAa;AAC/C,mBAAO,EAAE,MAAF,CAAS,QAAT,IAAqB,EAAE,MAAF,CAAS,QAArC;AACH,SAFD,EAEG,OAFH,CAEW,UAFX,EAEuB,UAAU,CAAV,EAAa;AAChC,mBAAO,EAAE,QAAF,GAAa,IAApB;AACH,SAJD;;AAMA;AACA,mBAAW,GAAX,CAAe,OAAf,CAAuB,eAAvB,EACI,WAAW,aAAX,CAAyB,UAAU,KAAnC,EAA0C,IAA1C,GAAiD,WAAW,aAAX,CAAyB,UAAU,KAAnC,EAA0C,IAD/F;;AAGA,eAAO,IAAP;AACH,KAjCD;;AAmCA;;;;;;AAMA,UAAM,YAAN,GAAqB,YAAY;AAC7B,YAAI,aAAa,IAAjB;AACA,aAAK,SAAL,GAAiB,EAAjB;AACA,aAAK,aAAL,CAAmB,OAAnB,CAA2B,UAAU,GAAV,EAAe,GAAf,EAAoB;AAC3C,gBAAI,QAAQ,WAAR,CAAoB,IAAI,EAAxB,CAAJ,EAAiC;AAC7B,oBAAI,EAAJ,GAAS,GAAT;AACA;AACH;AACD,uBAAW,SAAX,CAAqB,IAAI,EAAzB,IAA+B,GAA/B;AACA,gBAAI,QAAQ,WAAR,CAAoB,IAAI,KAAxB,CAAJ,EAAoC;AAChC,oBAAI,KAAJ,GAAY,KAAK,IAAI,EAArB;AACH;AACJ,SATD;AAUA,eAAO,IAAP;AACH,KAdD;;AAgBA;;;;;;AAMA,UAAM,YAAN,GAAqB,YAAY;AAC7B;AACA,iBAAS,6BAAT,CAAuC,IAAvC,EAA4C;AACxC;AACA,gBAAI,aAAa,WAAW,aAAX,CAAyB,KAAK,MAA9B,CAAjB;AAAA,gBACI,aAAa,WAAW,aAAX,CAAyB,KAAK,MAA9B,CADjB;;AAGA;AACA,gBAAI,CAAC,WAAW,YAAhB,EAA6B;AACzB,2BAAW,YAAX,GAA0B,CAA1B;AACH;;AAED,gBAAI,CAAC,WAAW,YAAhB,EAA6B;AACzB,2BAAW,YAAX,GAA0B,CAA1B;AACH;;AAED,gBAAI,CAAC,KAAK,MAAV,EAAkB;AACd,qBAAK,MAAL,GAAc,CAAd;AACH;;AAED,uBAAW,YAAX,IAA2B,KAAK,MAAhC;AACA,uBAAW,YAAX,IAA2B,KAAK,MAAhC;AACH;AACD;;AAEA,YAAI,aAAa,IAAjB;AAAA,YAAuB,GAAvB;AAAA,YAA4B,GAA5B;AAAA,YAAiC,GAAjC;AACA,aAAK,cAAL,GAAsB,EAAtB;AACA,aAAK,aAAL,CAAmB,OAAnB,CAA2B,UAAU,GAAV,EAAe,GAAf,EAAoB;;AAE3C,gBAAI,QAAQ,WAAR,CAAoB,IAAI,EAAxB,CAAJ,EAAiC;AAC7B,oBAAI,EAAJ,GAAS,GAAT;AACA;AACH;AACD;AACA,gBAAI,QAAQ,WAAR,CAAoB,IAAI,QAAxB,CAAJ,EAAuC;AACnC,oBAAI,QAAJ,GAAe,IAAI,MAAnB;AACA;AACH;AACD,gBAAI,MAAJ,GAAa,WAAW,SAAX,CAAqB,IAAI,QAAzB,CAAb;AACA,gBAAI,QAAQ,WAAR,CAAoB,IAAI,QAAxB,CAAJ,EAAuC;AACnC,oBAAI,QAAJ,GAAe,IAAI,MAAnB;AACA;AACH;AACD,gBAAI,MAAJ,GAAa,WAAW,SAAX,CAAqB,IAAI,QAAzB,CAAb;;AAEA,0CAA8B,GAA9B;;AAEA;AACA,gBAAI,QAAQ,SAAR,CAAkB,IAAI,QAAtB,KAAmC,QAAQ,SAAR,CAAkB,IAAI,QAAtB,CAAvC,EAAwE;AACpE,sBAAM,IAAI,QAAV;AACA,sBAAM,IAAI,QAAV;AACA,sBAAO,MAAM,GAAN,GAAY,MAAM,GAAN,GAAY,GAAxB,GAA8B,MAAM,GAAN,GAAY,GAAjD;AACA,oBAAI,QAAQ,WAAR,CAAoB,WAAW,cAAX,CAA0B,GAA1B,CAApB,CAAJ,EAAyD;AACrD,+BAAW,cAAX,CAA0B,GAA1B,IAAiC,CAAC,GAAD,CAAjC;AACA,wBAAI,QAAJ,GAAe,CAAf;AACH,iBAHD,MAGO;AACH,wBAAI,QAAJ,GAAe,WAAW,cAAX,CAA0B,GAA1B,EAA+B,IAA/B,CAAoC,GAApC,CAAf;AACH;AACD;AACA;AACA,oBAAI,WAAJ,GAAmB,IAAI,QAAJ,GAAe,CAAf,KAAqB,CAArB,GAAyB,IAAI,QAAJ,GAAe,UAAU,kBAAlD,GAAuE,CAAC,CAAC,IAAI,QAAL,GAAgB,CAAjB,IAAsB,UAAU,kBAA1H;AACH;AACJ,SAnCD;AAoCA,eAAO,IAAP;AACH,KAhED;;AAkEA;;;;;;AAMA,UAAM,cAAN,GAAuB,YAAY;AAC/B,eAAO,KAAK,kBAAL,GACF,gBADE,EAAP;AAEH,KAHD;;AAKA;;;;;;AAMA,UAAM,YAAN,GAAqB,YAAY;AAC7B;AACA;AACA;AACA;AACA;AACA;AACA,eAAO,KAAK,kBAAL,GACF,KAAK,UAAL,GAAkB,UAAU,6BAA5B,GACD,eADC,GACiB,oBAFf,GAAP;AAIH,KAXD;;AAaA;;;;;;;;AAQA,UAAM,aAAN,GAAsB,YAAY;AAC9B,YAAI,aAAa,IAAjB;AACA,YAAI,cAAJ;AAAA,YACI,kBAAkB,YAAY,GAAZ,EADtB;AAAA,YACyC,kBADzC;AAAA,YAC6D,gBAD7D;AAAA,YAC+E,sBAAsB,CADrG;AAAA,YAEI,QAAQ,CAFZ;;AAIA,8BAAsB,SAAS,MAAT,GAAkB;AACpC;AACA,6BAAkB,WAAW,UAAX,GAAwB,CAAxB,GAA4B,WAAW,UAAX,GAAwB,CAAtE;AACA,+BAAmB,YAAY,GAAZ,EAAnB;AACA,iBAAK,IAAI,IAAI,CAAb,EAAgB,IAAI,cAAJ,IAAsB,WAAW,KAAX,CAAiB,KAAjB,KAA2B,WAAW,KAAX,CAAiB,QAAjB,EAAjE,EAA8F,GAA9F,EAAmG;AAC/F,2BAAW,KAAX,CAAiB,IAAjB;AACA;AACH;AACD,mCAAwB,YAAY,GAAZ,KAAoB,gBAA5C;AACA,uBAAW,gBAAX,GAA8B,iBAA9B;;AAEA,gBAAI,WAAW,KAAX,CAAiB,KAAjB,KAA2B,WAAW,KAAX,CAAiB,QAAjB,EAA/B,EAA4D;AACxD,sCAAsB,MAAtB;AACH,aAFD,MAEO;AACH,qCAAqB,YAAY,GAAZ,KAAoB,eAAzC;AACA,wBAAQ,GAAR,8BAAuC,CAAC,qBAAqB,IAAtB,EAA4B,OAA5B,CAAoC,CAApC,CAAvC,+BAAuG,CAAC,sBAAsB,IAAvB,EAA6B,OAA7B,CAAqC,CAArC,CAAvG,WAAoJ,KAApJ;AACA,2BAAW,UAAX;AACH;AACJ,SAlBD,EAN8B,CAwB1B;AACJ,eAAO,IAAP;AACH,KA1BD;;AA4BA;;;;;;;;AAQA,UAAM,kBAAN,GAA2B,YAAY;AACnC,YAAI,aAAa,IAAjB;AACA,YAAI,cAAJ;AAAA,YACI,gBADJ;AAAA,YACsB,sBAAsB,CAD5C;AAAA,YAEI,QAAQ,CAFZ;;AAIA,8BAAsB,SAAS,MAAT,GAAkB;AACpC;AACA,6BAAkB,WAAW,UAAX,GAAwB,CAAxB,GAA4B,EAA9C;AACA,+BAAmB,YAAY,GAAZ,EAAnB;AACA,iBAAK,IAAI,IAAI,CAAb,EAAgB,IAAI,cAAJ,IAAsB,WAAW,KAAX,CAAiB,KAAjB,KAA2B,CAAjE,EAAoE,GAApE,EAAyE;AACrE,2BAAW,KAAX,CAAiB,IAAjB;AACA;AACH;AACD,mCAAwB,YAAY,GAAZ,KAAoB,gBAA5C;AACA,uBAAW,iBAAX;AACA,gBAAI,WAAW,UAAf,EAA2B;AACvB,2BAAW,gBAAX;AACH;;AAED,gBAAI,WAAW,KAAX,CAAiB,KAAjB,KAA2B,CAA/B,EAAkC;AAC9B,sCAAsB,MAAtB;AACH,aAFD,MAEO;AACH,wBAAQ,GAAR,0BAAmC,CAAC,sBAAsB,IAAvB,EAA6B,OAA7B,CAAqC,CAArC,CAAnC,WAAgF,KAAhF;AACA,2BAAW,gBAAX,GAA8B,UAA9B;AACH;AACJ,SApBD,EANmC,CA0B/B;AACJ,eAAO,IAAP;AACH,KA5BD;;AA8BA;;;;;;;AAOA,UAAM,gBAAN,GAAyB,YAAY;AACjC,YAAI,aAAa,IAAjB;;AAEA;AACA,aAAK,QAAL,CAAc,UAAU,KAAxB;AACA;AACA;AACA;AAHA,SAIK,IAJL,CAIU,WAJV,EAIuB,UAAU,CAAV,EAAa;AAC5B;AACA;AACA;AACA;AACA;AACA;AACA,kCAAoB,EAAE,CAAtB,SAA2B,EAAE,CAA7B,gBAAyC,WAAW,cAApD;AACH,SAZL;;AAeA;AACA,aAAK,MAAL,CAAY,IAAZ,CAAiB,GAAjB,EAAsB,UAAU,CAAV,EAAa;AAC3B,mBAAO,EAAE,CAAT;AACH,SAFL,EAGK,IAHL,CAGU,GAHV,EAGe,UAAU,CAAV,EAAa;AACpB,mBAAO,EAAE,CAAT;AACH,SALL;;AAQA;AACA,aAAK,QAAL,CAAc,UAAU,KAAxB,EAA+B,IAA/B,CAAoC,IAApC,EAA0C,UAAU,CAAV,EAAa;AAC/C,mBAAO,EAAE,MAAF,CAAS,CAAhB;AACH,SAFL,EAGK,IAHL,CAGU,IAHV,EAGgB,UAAU,CAAV,EAAa;AACrB,mBAAO,EAAE,MAAF,CAAS,CAAhB;AACH,SALL,EAMK,IANL,CAMU,IANV,EAMgB,UAAU,CAAV,EAAa;AACrB,mBAAO,EAAE,MAAF,CAAS,CAAhB;AACH,SARL,EASK,IATL,CASU,IATV,EASgB,UAAU,CAAV,EAAa;AACrB,mBAAO,EAAE,MAAF,CAAS,CAAhB;AACH,SAXL;AAYI;AAZJ,SAaK,IAbL,CAaU,WAbV,EAauB,UAAU,CAAV,EAAa;AAC5B,gBAAI,SAAS,OAAO,qBAAP,CAA6B,EAAE,WAA/B,EAA4C,EAAE,MAAF,CAAS,CAAT,GAAa,EAAE,MAAF,CAAS,CAAlE,EAAqE,EAAE,MAAF,CAAS,CAAT,GAAa,EAAE,MAAF,CAAS,CAA3F,CAAb;AACA,mBAAO,eAAe,OAAO,EAAtB,GAA2B,GAA3B,GAAiC,OAAO,EAAxC,GAA6C,GAApD;AACH,SAhBL;;AAmBA,eAAO,IAAP;AACH,KAjDD;;AAmDA;;;;;AAKA,UAAM,iBAAN,GAA0B,YAAY;AAClC;AACA,YAAI,CAAC,KAAK,cAAV,EAA0B;AACtB,iBAAK,WAAL,CAAiB,IAAjB,CAAsB,IAAtB,EACI,UAAU,eAAV,IAA6B,IAAI,CAAC,KAAK,KAAL,CAAW,KAAX,KAAqB,KAAK,KAAL,CAAW,QAAX,EAAtB,IAA+C,UAAU,SAA1F,CADJ;AAEH;AACD,eAAO,IAAP;AACH,KAPD;;AASA;;;;;;;AAOA,UAAM,UAAN,GAAmB,YAAY;AAC3B;AACA;AACA,YAAI,CAAC,KAAK,eAAV,EAA2B;AACvB,iBAAK,cAAL;AACA,iBAAK,eAAL,GAAuB,IAAvB;AACA;AACA,iBAAK,oBAAL;AACA,iBAAK,aAAL,GALuB,CAKD;AACzB;AACD,eAAO,IAAP;AACH,KAXD;;AAaA;;;;;;;AAOA,UAAM,cAAN,GAAuB,YAAY;AAC/B,YAAI,KAAJ;AAAA,YAAW,SAAX;AAAA,YACI,QAAQ,UAAU,eADtB;AAAA,YAEI,SAAS,UAAU,gBAFvB;AAAA,YAGI,SAAS,KAAK,cAHlB;AAAA,YAII,aAAa,GAAG,GAAH,CAAO,KAAK,aAAZ,EAA2B,UAAU,CAAV,EAAa;AACjD,mBAAO,KAAK,GAAL,CAAS,CAAC,EAAE,CAAH,GAAO,MAAhB,EAAwB,EAAE,CAAF,GAAM,MAAN,GAAe,KAAvC,EAA8C,CAA9C,CAAP;AACH,SAFY,CAJjB;AAAA,YAOI,aAAa,GAAG,GAAH,CAAO,KAAK,aAAZ,EAA2B,UAAU,CAAV,EAAa;AACjD,mBAAO,KAAK,GAAL,CAAS,CAAC,EAAE,CAAH,GAAO,MAAhB,EAAwB,EAAE,CAAF,GAAM,MAAN,GAAe,MAAvC,EAA+C,CAA/C,CAAP;AACH,SAFY,CAPjB;AAUA,YAAI,aAAa,CAAb,IAAkB,aAAa,CAAnC,EAAsC;AAClC;AACA;AACA,gBAAI,SAAS,SAAS,QAAQ,IAAI,UAArB,CAAb;AAAA,gBACI,SAAS,UAAU,SAAS,IAAI,UAAvB,CADb;AAEA,oBAAQ,KAAK,GAAL,CAAS,MAAT,EAAiB,MAAjB,IAA2B,IAAnC;AACA,wBAAY,CAAE,QAAQ,CAAT,IAAe,IAAI,KAAnB,CAAD,EAA6B,SAAS,CAAV,IAAgB,IAAI,KAApB,CAA5B,CAAZ;AACA;AACA,gBAAI,QAAQ,UAAU,QAAtB,EAAgC;AAC5B,qBAAK,IAAL,CAAU,WAAV,CAAsB,CAAC,KAAD,EAAQ,UAAU,QAAlB,CAAtB;AACH;AACJ,SAXD,MAWO;AACH;AACA;AACA,oBAAQ,CAAR;AACA,wBAAY,CAAC,CAAD,EAAI,CAAJ,CAAZ;AACH;AACD,aAAK,GAAL,CAAS,UAAT,GACK,QADL,CACc,UAAU,kBADxB,EAEK,IAFL,CAEU,KAAK,IAAL,CAAU,SAFpB,EAE+B,GAAG,YAAH,CAAgB,SAAhB,CAA0B,UAAU,CAAV,CAA1B,EAAwC,UAAU,CAAV,CAAxC,EAAsD,KAAtD,CAA4D,KAA5D,CAF/B;AAGI;AACJ,eAAO,IAAP;AACH,KAjCD;;AAmCA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;;;;;;;AAOA,UAAM,OAAN,GAAgB,UAAU,IAAV,EAAgB,OAAhB,EAAyB;AACrC;AACA,YAAI,CAAC,GAAG,KAAH,CAAS,gBAAd,EAAgC;AAC5B;AACA;AACA,gBAAI,GAAG,KAAH,CAAS,OAAb,EAAsB;AAClB,qBAAK,cAAL,CAAoB,OAApB,EAA6B,IAA7B,EAAmC,CAAC,KAAK,QAAzC;AACH,aAFD,MAEO;AACH;AACA;AACA;AACA;AACA,oBAAI,KAAK,QAAL,IAAkB,KAAK,aAAL,CAAmB,UAAU,KAA7B,EAAoC,IAApC,GAA2C,KAAK,aAAL,CAAmB,UAAU,KAA7B,EAAoC,IAAhF,KAA0F,CAA/G,EAAkH;AAC9G,yBAAK,cAAL,CAAoB,OAApB,EAA6B,IAA7B,EAAmC,KAAnC;AACH,iBAFD,MAEO;AACH,yBAAK,cAAL,CAAoB,OAApB,EAA6B,IAA7B,EAAmC,IAAnC,EAAyC,IAAzC;AACH;AACJ;AACJ;AACD;AACA,WAAG,KAAH,CAAS,eAAT;AACA,eAAO,IAAP;AACH,KAtBD;;AAwBA;;;;;;;;AAQA,UAAM,gBAAN,GAAyB,YAAY;AACjC;AACA,YAAI,KAAK,aAAL,CAAmB,UAAU,KAA7B,EAAoC,IAApC,GAA2C,KAAK,aAAL,CAAmB,UAAU,KAA7B,EAAoC,IAA/E,GAAsF,CAA1F,EAA6F;AACzF,iBAAK,cAAL,CAAoB,IAApB,EAA0B,IAA1B,EAAgC,IAAhC,EAAsC,IAAtC;AACH;AACD,eAAO,IAAP;AACH,KAND;;AAQA;;;;;;;;;;AAUA,UAAM,aAAN,GAAsB,UAAU,OAAV,EAAmB,IAAnB,EAAyB,EAAzB,EAA6B;AAC/C,WAAG,MAAH,CAAU,OAAV,EAAmB,OAAnB,CAA2B,SAA3B,EAAsC,KAAK,OAAL,GAAe,EAArD;AACA,eAAO,KAAK,kBAAL,CAAwB,OAAxB,EAAiC,IAAjC,EAAuC,EAAvC,CAAP;AACH,KAHD;;AAKA;;;;;;;;AAQA,UAAM,cAAN,GAAuB,UAAU,IAAV,EAAgB;AACnC,YAAI,WAAY,KAAK,KAAL,KAAe,UAAU,UAAzB,GACZ,UAAU,KADE,GACM,UAAU,KADhC;AAEA,aAAK,QAAL,CAAc,QAAd,EAAwB,MAAxB,CAA+B,UAAU,CAAV,EAAa;AACpC,mBAAO,EAAE,EAAF,KAAS,KAAK,EAArB;AACH,SAFL,EAGK,OAHL,CAGa,SAHb,EAGwB,KAAK,OAH7B;AAIA,eAAO,IAAP;AACH,KARD;;AAUA;;;;;;;;;;;AAWA,UAAM,cAAN,GAAuB,UAAU,OAAV,EAAmB,IAAnB,EAAyB,EAAzB,EAA6B,iBAA7B,EAAgD;AACnE,YAAI,aAAa,IAAjB;AACA,YAAI,QAAJ;;AAEA,YAAI,iBAAJ,EAAuB;AACnB,iBAAK,WAAW,UAAU,KAA1B,EAAiC,YAAY,UAAU,KAAvD,EAA8D,UAA9D,EAA0E;AACtE,2BAAW,QAAX,CAAoB,QAApB,EAA8B,MAA9B,CAAqC,UAAU,CAAV,EAAa;AAC9C,2BAAO,WAAW,aAAX,CAAyB,QAAzB,EAAmC,GAAnC,CAAuC,EAAE,EAAzC,CAAP;AACH,iBAFD,EAEG,OAFH,CAEW,UAFX,EAEuB,UAAU,CAAV,EAAa;AAChC,2BAAO,EAAE,QAAF,GAAa,KAApB;AACH,iBAJD;AAKA,2BAAW,aAAX,CAAyB,QAAzB,EAAmC,KAAnC;AACH;AACJ;;AAED;AACA,YAAI,OAAJ,EAAa;AACT,eAAG,MAAH,CAAU,OAAV,EAAmB,OAAnB,CAA2B,UAA3B,EAAuC,KAAK,QAAL,GAAgB,EAAvD;AACH;;AAED;AACA,aAAK,MAAL,CAAY,OAAZ,CAAoB,UAApB,EAAgC,UAAU,CAAV,EAAa;AACzC,mBAAO,EAAE,QAAT;AACH,SAFD;;AAIA;AACA,YAAI,IAAJ,EAAU;AACN,uBAAY,KAAK,KAAL,KAAe,UAAU,UAAzB,GAAsC,UAAU,KAAhD,GAAwD,UAAU,KAA9E;AACA,gBAAI,KAAK,QAAT,EAAmB;AACf,qBAAK,aAAL,CAAmB,QAAnB,EAA6B,GAA7B,CAAiC,KAAK,EAAtC;AACH,aAFD,MAEO;AACH,qBAAK,aAAL,CAAmB,QAAnB,EAA6B,MAA7B,CAAoC,KAAK,EAAzC;AACH;AACJ;;AAED;AACA,aAAK,GAAL,CAAS,OAAT,CAAiB,eAAjB,EACI,KAAK,aAAL,CAAmB,UAAU,KAA7B,EAAoC,IAApC,GAA2C,WAAW,aAAX,CAAyB,UAAU,KAAnC,EAA0C,IADzF;;AAGA,eAAO,KAAK,kBAAL,CAAwB,QAAxB,CAAP;AACH,KAxCD;;AA0CA;;;;;;;AAOA,UAAM,eAAN,GAAwB,YAAY;AAChC,YAAI,aAAa,IAAjB;AACA;AACA,aAAK,IAAI,WAAW,UAAU,KAA9B,EAAqC,YAAY,UAAU,KAA3D,EAAkE,UAAlE,EAA8E;AACzE,uBAAU,KAAV,EAAiB;AACd,sBAAM,KAAN;AACA,2BAAW,QAAX,CAAoB,QAApB,EACK,OADL,CACa,UADb,EACyB,UAAU,CAAV,EAAa;AAC9B,wBAAI,EAAE,QAAN,EAAgB;AACZ,8BAAM,GAAN,CAAU,EAAE,EAAZ;AACA,+BAAO,IAAP;AACH,qBAHD,MAGO;AACH,+BAAO,KAAP;AACH;AACJ,iBARL;AASH,aAXA,EAWC,KAAK,aAAL,CAAmB,QAAnB,CAXD,CAAD;AAYH;;AAED;AACA,aAAK,MAAL,CAAY,OAAZ,CAAoB,UAApB,EAAgC,UAAU,CAAV,EAAa;AACzC,mBAAO,EAAE,QAAT;AACH,SAFD;;AAIA;AACA,aAAK,GAAL,CAAS,OAAT,CAAiB,eAAjB,EACI,KAAK,aAAL,CAAmB,UAAU,KAA7B,EAAoC,IAApC,GAA2C,WAAW,aAAX,CAAyB,UAAU,KAAnC,EAA0C,IADzF;AAEA,eAAO,IAAP;AACH,KA3BD;;AA6BA;;;;;;;AAOA,UAAM,MAAN,GAAe,YAAY;AACvB,YAAI,QAAQ,GAAG,KAAH,CAAS,SAArB;;AAEA,YAAI,KAAK,YAAT,EAAuB;AACnB,iBAAK,YAAL,CAAkB,IAAlB,CAAuB,WAAvB,iBACiB,MAAM,CADvB,UAC6B,MAAM,CADnC,gBAC+C,MAAM,CADrD;AAEH;AACD,eAAO,IAAP;AACH,KARD;;AAUI;;;;;;;;AAQA,UAAM,WAAN,GAAoB,YAAU,KAAO;AACjC;AACA;AACA;AACA;AACA,eAAO,IAAP;AACH,KAND;;AAQJ;;;;;;;;AAQA,UAAM,MAAN,GAAe,UAAU,CAAV,EAAa;AACxB;AACA,UAAE,EAAF,GAAO,GAAG,KAAH,CAAS,CAAhB;AACA,UAAE,EAAF,GAAO,GAAG,KAAH,CAAS,CAAhB;AACA,YAAI,CAAC,KAAK,UAAV,EAAsB;AAClB,iBAAK,UAAL,GAAkB,IAAlB;AACA,iBAAK,sBAAL;AACH;AACD,eAAO,IAAP;AACH,KATD;;AAWI;;;;;;;AAOA,UAAM,SAAN,GAAkB,UAAU,CAAV,EAAa;AAC3B,aAAK,UAAL,GAAkB,KAAlB;AACA;AACA,aAAK,KAAL,CAAW,KAAX,CAAiB,KAAK,KAAL,CAAW,QAAX,EAAjB;AACA;AACA,YAAI,CAAC,KAAK,cAAV,EAA0B;AACtB,cAAE,EAAF,GAAO,IAAP;AACA,cAAE,EAAF,GAAO,IAAP;AACH;AACD,eAAO,IAAP;AACH,KAVD;;AAYJ;;;;;;;;;AASA,UAAM,oBAAN,GAA6B,YAAY;AACrC,YAAI,KAAK,cAAT,EAAyB;AACrB,iBAAK,QAAL,CAAc,UAAU,KAAxB,EAA+B,OAA/B,CAAuC,OAAvC,EAAgD,UAAU,CAAV,EAAa;AACzD,kBAAE,EAAF,GAAO,IAAP;AACA,kBAAE,EAAF,GAAO,IAAP;AACA,uBAAO,EAAE,KAAF,GAAU,KAAjB;AACH,aAJD;AAKA,iBAAK,cAAL,GAAsB,KAAtB;AACA,iBAAK,sBAAL;AAChB;AACa,SATD,MASO;AACH,iBAAK,QAAL,CAAc,UAAU,KAAxB,EAA+B,OAA/B,CAAuC,OAAvC,EAAgD,UAAU,CAAV,EAAa;AACzD,kBAAE,EAAF,GAAO,EAAE,CAAT;AACA,kBAAE,EAAF,GAAO,EAAE,CAAT;AACA,uBAAO,EAAE,KAAF,GAAU,IAAjB;AACH,aAJD;AAKA,iBAAK,cAAL,GAAsB,IAAtB;AACH;AACD,eAAO,IAAP;AACH,KAnBD;;AAqBA;;;;;;;;AAQA,UAAM,wBAAN,GAAiC,YAAY;AACzC;AACA,YAAI,KAAK,MAAL,CAAY,UAAZ,GAAyB,CAAC,KAAK,MAAL,CAAY,UAA1C,EAAsD;AAClD,iBAAK,UAAL,CAAgB,OAAhB,CAAwB,cAAxB,EAAwC,KAAxC;AACA;AACA;AACA;AACA;AACH,SAND,MAMO;AAAE;AACL,iBAAK,UAAL,CAAgB,OAAhB,CAAwB,cAAxB,EAAwC,IAAxC;AACA;AACA;AACA;AACH;AACD,eAAO,IAAP;AACH,KAfD;;AAiBA;;;;;;;;AAQA,UAAM,4BAAN,GAAqC,YAAY;AAC7C,YAAI,aAAa,IAAjB;AACA,aAAK,MAAL,CAAY,cAAZ,GAA6B,CAAC,KAAK,MAAL,CAAY,cAA1C;AACA,aAAK,QAAL,CAAc,UAAU,KAAxB,EACK,IADL,CACU,GADV,EACe,GAAG,MAAH,GACN,IADM,CACD,UAAU,CAAV,EAAa;AACf,mBAAO,EAAE,KAAT;AACH,SAHM,EAIN,IAJM,CAID,UAAU,CAAV,EAAa;AACf,mBAAO,WAAW,eAAX,CAA2B,CAA3B,CAAP;AACH,SANM,CADf;AAQA,eAAO,IAAP;AACH,KAZD;;AAcA;;;;;;;;AAQA,UAAM,4BAAN,GAAqC,YAAY;AAC7C,YAAI,aAAa,IAAjB;AACA,aAAK,MAAL,CAAY,cAAZ,GAA6B,CAAC,KAAK,MAAL,CAAY,cAA1C;AACA,aAAK,QAAL,CAAc,UAAU,KAAxB,EACK,IADL,CACU,cADV,EAC2B,CAAC,KAAK,MAAL,CAAY,cAAb,GAA8B,IAA9B,GAAqC,UAAU,CAAV,EAAa;AACrE,mBAAO,WAAW,YAAX,CAAwB,CAAxB,CAAP;AACH,SAHL;AAIA,eAAO,IAAP;AACH,KARD;;AAUA;;;;;;;;;AASA,UAAM,gBAAN,GAAyB,UAAU,IAAV,EAAgB,QAAhB,EAA0B;AAC/C,YAAI,OAAO,KAAK,cAAL,CAAoB,IAApB,CAAP,KAAqC,WAAzC,EAAqD;AACjD,iBAAK,cAAL,CAAoB,IAApB,IAA4B,EAA5B;AACH;AACD,aAAK,cAAL,CAAoB,IAApB,EAA0B,IAA1B,CAA+B,QAA/B;AACA,eAAO,IAAP;AACH,KAND;;AAQA;;;;;;;;;AASA,UAAM,kBAAN,GAA2B,UAAU,IAAV,EAAyB;AAAA,0CAAN,IAAM;AAAN,gBAAM;AAAA;;AAChD,YAAI,OAAO,KAAK,cAAL,CAAoB,IAApB,CAAP,KAAqC,WAAzC,EAAqD;AACjD;AACH;AACD,aAAK,cAAL,CAAoB,IAApB,EAA0B,OAA1B,CAAkC,UAAU,QAAV,EAAoB;AAClD,sCAAY,IAAZ;AACH,SAFD;AAGA,eAAO,IAAP;AACH,KARD;;AAWI;;;;;;;AAOJ,UAAM,qBAAN,GAA8B,UAAU,QAAV,EAAoB;AAC9C,eAAO,OAAO,qBAAP,CAA6B,QAA7B,CAAP;AACH,KAFD;;AAIA;AACA,WAAO,iBAAP;AACH,CAhsC6B,CAjGlC;;AAoyCI;;;;;AApyCJ,CAyyCK,QAzyCL,CAyyCc,qBAzyCd,EAyyCqC;AAC7B,qBAAiB,GADY;AAE7B,sBAAkB,GAFW;AAG7B,WAAO,CAHsB;AAI7B,WAAO,CAJsB;AAK7B,cAAU,CALmB;AAM7B,cAAU,CANmB;AAO7B,gBAAY,MAPiB;AAQ7B,gBAAY,MARiB;AAS7B,oBAAgB,MATa;AAU7B,oBAAgB,MAVa;AAW7B,wBAAoB,GAXS;AAY7B,wBAAoB,OAZS;AAa7B,wBAAoB,SAbS;AAc7B,iBAAa,EAdgB;AAe7B,wBAAoB,EAfS;AAgB7B,cAAU,GAhBmB;AAiB7B,cAAU,CAjBmB;AAkB7B,wBAAoB,IAlBS;AAmB7B,qBAAiB,GAnBY;AAoB7B,cAAU,4BApBmB;AAqB7B,0BAAsB,CArBO;AAsB7B,eAAW,GAtBkB;AAuB7B,mCAA+B,GAvBF;AAwB7B,0BAAsB,CAAC,GAxBM;AAyB7B,0BAAsB,CAAC,KAzBM;AA0B7B,sBAAkB,iBA1BW;AA2B7B,QAAI,kCAAJ,GAAyC;AACrC,eAAO,KAAK,eAAL,GAAuB,KAAK,gBAA5B,IAAgD,KAAK,EAAL,GAAU,CAA1D,CAAP;AACH;AA7B4B,CAzyCrC;;AA00CI;;;;;AAKA;AA/0CJ,CAg1CK,OAh1CL,CAg1Ca,kBAh1Cb,EAg1CiC,CAAC,qBAAD,EAAwB,gBAAxB,EAA0C,UAA1C,EAAsD,UAAU,SAAV,EAAqB,SAArB,EAAgC,QAAhC,EAA0C;AACzH,WAAO;;AAEH;;;;;;;AAOA,oBAAY,oBAAU,KAAV,EAAiB,SAAjB,EAA4B;AACpC,gBAAI,WAAW,UAAU,GAAV,CAAc,oBAAd,CAAf;AACA,gBAAI,UAAU,QAAQ,OAAR,CAAgB,QAAhB,CAAd;AACA,gBAAI,kBAAkB,SAAS,OAAT,EAAkB,KAAlB,CAAtB;AACA,sBAAU,OAAV,CAAkB,eAAlB;AACA;AACA,mBAAO,IAAP;AACH,SAhBE;;AAkBH;;;;;;;;;;AAUA,+BAAuB,+BAAU,WAAV,EAAuB,MAAvB,EAA+B,MAA/B,EAAuC;AAC1D,gBAAI,EAAJ,EAAQ,EAAR;AACA,gBAAI,gBAAgB,CAApB,EAAuB;AACnB,qBAAK,KAAK,CAAV;AACH,aAFD,MAEO,IAAI,WAAW,CAAX,IAAgB,KAAK,GAAL,CAAS,SAAS,MAAlB,IAA4B,CAAhD,EAAmD;AACtD,qBAAK,CAAC,WAAD,GAAe,UAAU,eAAzB,GAA2C,UAAU,gBAA1D;AACA,qBAAK,cAAe,MAAf,GAAyB,MAA9B;AACH,aAHM,MAGA;AACH,qBAAK,WAAL;AACA,qBAAK,cAAe,CAAC,MAAhB,GAA0B,MAA/B;AACH;AACD,gBAAI,CAAC,QAAQ,QAAR,CAAiB,EAAjB,CAAL,EAA2B;AACvB,wBAAQ,IAAR,6DAAuE,WAAvE,gBAA6F,MAA7F,gBAA8G,MAA9G;AACH;AACD,mBAAO,EAAC,IAAI,EAAL,EAAS,IAAI,EAAb,EAAP;AACH,SA3CE;;AA6CH;;;;;;;;;;AAUA,kCAA0B,kCAAU,eAAV,EAA2B,gBAA3B,EAA6C,eAA7C,EAA8D;AACpF,gBAAI,IAAI,MAAR;AAAA,gBACI,IAAI,KADR;AAAA,gBAEI,IAAI,MAAM,eAAN,IAAyB,mBAAmB,eAA5C,CAFR;AAGA,gBAAI,IAAI,MAAR,EAAgB,IAAI,MAAJ;AAChB,mBAAO,IAAI,KAAK,GAAL,CAAS,CAAT,EAAY,CAAC,CAAb,CAAX;AACH,SA7DE;;AA+DH;;;;;;;;AAQA,wBAAgB,wBAAU,CAAV,EAAa;AACzB,gBAAI,IAAI,EAAE,MAAF,CAAS,CAAT,CAAR;AACA,mBAAQ,KAAK,GAAL,IAAY,KAAK,GAAzB;AACH,SA1EE;;AA4EH;;;;;;;AAOZ;;;;;;;;AAQY;;;;;;;;;;;;;;;;;;;;;;AAsBA,+BAAuB,+BAAU,QAAV,EAAoB;AACvC;AACA,gBAAI,QAAQ,SAAS,KAArB;AACA,kBAAM,OAAN,CAAc,UAAU,IAAV,EAAgB,GAAhB,EAAqB;AAC/B,oBAAI,QAAQ,WAAR,CAAoB,KAAK,EAAzB,CAAJ,EAAkC;AAC9B,yBAAK,EAAL,GAAU,GAAV;AACH;AACD,oBAAI,QAAQ,WAAR,CAAoB,KAAK,KAAzB,CAAJ,EAAqC;AACjC,yBAAK,KAAL,GAAa,KAAK,KAAK,EAAvB;AACH;AACD,qBAAK,KAAL,GAAa,UAAU,UAAvB;;AAEA,wBAAQ,KAAK,KAAb;AACI,yBAAK,SAAL;AACA,yBAAK,EAAL;AACA,yBAAK,QAAL;AACI,6BAAK,KAAL,GAAa,GAAG,YAAhB;AACA;AACJ,yBAAK,OAAL;AACI,6BAAK,KAAL,GAAa,GAAG,WAAhB;AACA;AACJ,yBAAK,SAAL;AACI,6BAAK,KAAL,GAAa,GAAG,aAAhB;AACA;AACJ,yBAAK,QAAL;AACI,6BAAK,KAAL,GAAa,GAAG,YAAhB;AACA;AACJ,yBAAK,aAAL;AACA,yBAAK,eAAL;AACI,6BAAK,KAAL,GAAa,GAAG,cAAhB;AACA;AACJ,yBAAK,MAAL;AACI,6BAAK,KAAL,GAAa,GAAG,UAAhB;AACA;AACJ,yBAAK,KAAL;AACI,6BAAK,KAAL,GAAa,GAAG,SAAhB;AACA;AACJ;AACI,6BAAK,KAAL,GAAa,GAAG,YAAhB;AA1BR;AA4BH,aArCD;AAsCA;AACA,gBAAI,QAAS,SAAS,KAAT,GAAiB,SAAS,KAA1B,GAAkC,SAAS,KAAxD;AACA,kBAAM,OAAN,CAAe,UAAS,IAAT,EAAe,GAAf,EAAoB;AAC/B,oBAAI,QAAQ,WAAR,CAAoB,KAAK,EAAzB,CAAJ,EAAkC;AAC9B,yBAAK,EAAL,GAAU,GAAV;AACH;AACD,oBAAI,QAAQ,WAAR,CAAoB,KAAK,QAAzB,CAAJ,EAAwC;AACpC,yBAAK,QAAL,GAAgB,KAAK,MAArB;AACH;AACD,oBAAI,QAAQ,WAAR,CAAoB,KAAK,QAAzB,CAAJ,EAAwC;AACpC,yBAAK,QAAL,GAAgB,KAAK,MAArB;AACH;AACD,qBAAK,WAAL,GAAmB,KAAK,QAAxB;AACA,qBAAK,WAAL,GAAmB,KAAK,QAAxB;AACA,qBAAK,KAAL,GAAa,UAAU,UAAvB;AACH,aAbD;AAcA;AACA,mBAAU,CACN,EAAC,IAAI,UAAU,QAAf,EAAyB,MAAM,KAA/B,EADM,EAEN,EAAC,IAAI,UAAU,QAAf,EAAyB,MAAM,KAA/B,EAFM,CAAV;AAIH;;AA/KE,KAAP,CADyH,CAmLtH;AACN,CApL4B,CAh1CjC,EAogDQ;AApgDR","file":"forceHorse-es5.js","sourcesContent":["\"use strict\";\r\n/**\r\n * @ngdoc overview\r\n * @name forceHorse\r\n * @description A graph visualizer using a \"force layout\" engine (d3.js)\r\n */\r\nangular.module('forceHorse', [])\r\n\r\n //---------------------------------------------------------------//\r\n .run(function ($templateCache) {\r\n // cache our buttons template\r\n $templateCache.put('forceHorse/buttons',\r\n ' ');\r\n })\r\n\r\n /**\r\n * @ngdoc directive\r\n * @name forceHorse.directive:forceHorse\r\n * @restrict EA\r\n * @scope\r\n * @priority 100\r\n * @description Directive definition for the forceHorse component\r\n */\r\n .directive('forceHorse', ['$compile', 'ForceHorseFactory', 'ForceHorseHelper', function ($compile, ForceHorseFactory, helper) {\r\n return {\r\n restrict: \"EA\",\r\n controllerAs: \"forceHorseCtrl\",\r\n priority: 100,\r\n scope: {\r\n options: \"=\"\r\n },\r\n bindToController: true,\r\n\r\n controller: function ($scope, $element) {\r\n var vm = this;\r\n // Create my instance\r\n // Also provide the caller with a reference to my instance, for API\r\n this.requestDigest = function () {\r\n try {\r\n $scope.$digest();\r\n } catch (e){}\r\n };\r\n\r\n this.options.forceHorseInstance =\r\n $scope.forceHorseInstance = new ForceHorseFactory($element, this.options, this.requestDigest)\r\n .redraw();\r\n\r\n // Clear the instance reference on destruction, to prevent memory leak\r\n $scope.$on(\"$destroy\", function () {\r\n console.log(\"Destroying forceHorse instance\");\r\n vm.options.forceHorseInstance =\r\n $scope.forceHorseInstance = null;\r\n });\r\n },\r\n\r\n link: function (scope, element) { //, attr, ctrl) {\r\n //console.log('In forceHorse link');\r\n\r\n // Add CSS class to set a CSS \"namespace\"\r\n element.addClass(\"force-horse\");\r\n // Add flex-box properties (moved to css)\r\n // element.attr(\"layout\", \"column\");\r\n // element.attr(\"flex\", \"\");\r\n // Add button bar\r\n helper.addButtons(scope, element);\r\n }\r\n };\r\n }])\r\n\r\n /**\r\n * @ngdoc factory\r\n * @name forceHorse.factory:ForceHorseFactory\r\n * @description Produces a class-instance for each instance of ForceHorse on a page\r\n */\r\n .factory('ForceHorseFactory', ['$http', '$log', 'ForceHorseConstants', 'ForceHorseHelper',\r\n function ($http, $log, constants, helper) {\r\n /**\r\n * @ngdoc method\r\n * @name ForceHorseFactory\r\n * @methodOf forceHorse.factory:ForceHorseFactory\r\n * @constructor\r\n * @description Constructor; initializes the eventListeners object\r\n * @param element A JSLite reference to the HTML container for this component\r\n * @param options An external options object\r\n * @param requestDigest callback function: request an angular digest on the element's scope\r\n */\r\n function ForceHorseFactory(element, options, requestDigest) {\r\n this.element = element[0];\r\n this.options = options;\r\n this.requestDigest = requestDigest;\r\n // Set a variable to hold references to registered event listeners\r\n this.eventListeners = {};\r\n }\r\n\r\n var proto = ForceHorseFactory.prototype;\r\n\r\n /**\r\n * @ngdoc method\r\n * @name forceHorse.factory:ForceHorseFactory#redraw\r\n * @description Draws a new graph, based on the input data\r\n * @returns {ForceHorseFactory} current instance\r\n */\r\n proto.redraw = function () {\r\n var myInstance = this;\r\n var proceed = function (json) {\r\n myInstance.initLayout(json);\r\n // The force simulation has to started before drawing nodes and links,\r\n // because it computes some drawing-relevant properties (node weight)\r\n myInstance.restartForceSimulation();\r\n myInstance.draw();\r\n };\r\n // $http.get(helper.getCurrentDirectory() + constants.CONFIG_FILE_NAME)\r\n // Get init (forceHorse.json) file from app root dir\r\n $http.get(constants.CONFIG_FILE_NAME)\r\n .then(function (response) {\r\n proceed(response.data);\r\n }, function (response) {\r\n $log.warn(constants.CONFIG_FILE_NAME + ' ' + response.statusText);\r\n proceed({});\r\n });\r\n //d3.json(\"forceHorse.json\", function (error, json) {\r\n // if (error) {\r\n // console.warn(error);\r\n // json = {};\r\n // }\r\n // myInstance.initLayout(json);\r\n // myInstance.draw();\r\n // myInstance.restartForceSimulation();\r\n //});\r\n return this;\r\n };\r\n\r\n /**\r\n * @ngdoc method\r\n * @name forceHorse.factory:ForceHorseFactory#initLayout\r\n * @description Init force layout & SVG\r\n * @param config an external configration object (typically from a json file)\r\n * @returns {ForceHorseFactory} current instance\r\n */\r\n proto.initLayout = function (config) {\r\n var myInstance = this;\r\n // Generate a random instance name, for a \"namespace\"\r\n this.instanceName = new Array(constants.INSTANCE_NAME_LENGTH).fill(null).map(function () {\r\n return constants.ALEPHBET.charAt(Math.floor(Math.random() * constants.ALEPHBET.length));\r\n }).join('');\r\n\r\n // Process input data\r\n var data = this.options.data;\r\n if (!(data instanceof Array)) {\r\n data = helper.convertFileDataFormat(data);\r\n }\r\n this.nodeDataArray = data[constants.NODES].data;\r\n this.edgeDataArray = data[constants.EDGES].data;\r\n this.processNodes();\r\n this.processEdges();\r\n\r\n // Some nodes-related fields\r\n // The size (area) of the containing circle\r\n this.numOfNodes = this.nodeDataArray.length;\r\n this.nodeIconAreaDefault = constants.INNER_SVG_WIDTH / 54 * constants.INNER_SVG_HEIGHT / 48 * 2;\r\n this.nodeIconRadius = Math.sqrt(this.nodeIconAreaDefault / Math.PI);\r\n this.selectedItems = [new Set(), new Set()]; // selected nodes, selected edges\r\n this.fixedNodesMode = false;\r\n //this.isBoundedGraphMode = false; // redundant?\r\n this.isFirstZoomDone = false; // Zooming to viewport after first simlation\r\n this.isDragging = false;\r\n\r\n // Set config parameters, which may be overwritten by the config argument\r\n // (that is, in fact, by an external json file)\r\n this.config = {\r\n showLabels: false,\r\n showNodeWeight: false,\r\n showEdgeWeight: false,\r\n showFilterButton: true,\r\n showLabelsButton: true,\r\n showNodeWeightButton: true,\r\n showEdgeWeightButton: true,\r\n useEdgesWeights: false,\r\n forceParameters: {\r\n //charge: -350,\r\n linkStrength: 1,\r\n gravity: 0.3,\r\n linkDistance: 10\r\n }\r\n };\r\n Object.assign(this.config, config);\r\n\r\n // Create a forceLayout instance\r\n myInstance.force = d3.forceSimulation();\r\n // .size([constants.INNER_SVG_WIDTH, constants.INNER_SVG_HEIGHT])\r\n // .on(\"start\", function () {\r\n // myInstance.onForceStart();\r\n // });\r\n // todo: start event replacement?\r\n\r\n var p;\r\n\r\n var forceCenter = d3.forceCenter(constants.INNER_SVG_WIDTH / 2, constants.INNER_SVG_HEIGHT / 2);\r\n myInstance.force.force(\"center\", forceCenter);\r\n // Todo: add center coordinates?\r\n // Todo: a gravity measure replacement?\r\n // if (angular.isDefined(p = myInstance.config.forceParameters.gravity)) myInstance.force.gravity(p);\r\n\r\n if (angular.isDefined(p = myInstance.config.forceParameters.charge)) {\r\n } else {\r\n if (myInstance.numOfNodes < constants.HEAVY_SIMULATION_NUM_OF_NODES) {\r\n p = function (d) {\r\n return d.weight * constants.DEFAULT_CHARGE_LIGHT;\r\n };\r\n } else {\r\n p = constants.DEFAULT_CHARGE_HEAVY;\r\n }\r\n }\r\n myInstance.force.force(\"charge\", d3.forceManyBody().strength(p));\r\n // myInstance.force.charge(p);\r\n\r\n if (angular.isDefined(p = myInstance.config.forceParameters.friction)) {\r\n } else {\r\n p = helper.computeFrictionParameter(constants.INNER_SVG_WIDTH, constants.INNER_SVG_HEIGHT, this.nodeDataArray.length)\r\n }\r\n myInstance.force.velocityDecay(p);\r\n // myInstance.force.friction(p);\r\n\r\n myInstance.force.nodes(myInstance.nodeDataArray);\r\n // .links(this.edgeDataArray);\r\n //.start();\r\n\r\n var linkForce = myInstance.force.force(\"link\",\r\n d3.forceLink(myInstance.edgeDataArray).id(function(d, i) { return i; }));\r\n if (angular.isDefined(p = myInstance.config.forceParameters.linkDistance)) linkForce.distance(p);\r\n if (angular.isDefined(p = myInstance.config.forceParameters.linkStrength)) linkForce.strength(p);\r\n\r\n myInstance.zoom = d3.zoom()\r\n .scaleExtent([constants.MAX_ZOOM, constants.MIN_ZOOM])\r\n .on(\"zoom\", function () {\r\n myInstance.onZoom();\r\n });\r\n\r\n // Create the main SVG (canvas).\r\n // If that element exists, remove it first.\r\n // TODO - is the element really filtered from memory (and not just the DOM)?\r\n d3.select(myInstance.element)\r\n .select(\"div.svgWrapper\")\r\n .remove();\r\n myInstance.svg = d3.select(myInstance.element)\r\n .append(\"div\")\r\n .attr(\"class\", \"svgWrapper\")\r\n .append(\"svg\")\r\n .attr(\"class\", \"graph-svg\")\r\n .attr(\"viewBox\", \"0 0 \" + constants.INNER_SVG_WIDTH + \" \" + constants.INNER_SVG_HEIGHT)\r\n .attr(\"preserveAspectRatio\", \"none\")\r\n .on(\"click\", function () {\r\n myInstance.onContainerClick()\r\n })\r\n .call(myInstance.zoom)\r\n // .call(myInstance.zoom.event) // Used in zoomToViewport()\r\n // Todo: zoom.event replacement?\r\n ;\r\n\r\n // Set wrapper group, to use for pan & zoom transforms\r\n myInstance.inSvgWrapper = myInstance.svg.append(\"g\");\r\n\r\n // Set SVG groups, and through them default colors,\r\n // for nodes and edges (note: the edge group has to be inserted first, so that the nodes\r\n // will render above the edges).\r\n myInstance.edgeGroup = myInstance.inSvgWrapper.append(\"g\")\r\n .attr(\"class\", \"edges\")\r\n .attr(\"stroke\", constants.DEFAULT_EDGE_COLOR)\r\n .attr(\"stroke-width\", constants.DEFAULT_EDGE_WIDTH + 'px');\r\n myInstance.nodeGroup = myInstance.inSvgWrapper.append(\"g\")\r\n .attr(\"class\", \"nodes\")\r\n .attr(\"fill\", constants.DEFAULT_NODE_COLOR);\r\n myInstance.labelGroup = myInstance.inSvgWrapper.append(\"g\")\r\n .attr(\"class\", \"labels\")\r\n .attr(\"fill\", constants.DEFAULT_NODE_COLOR)\r\n .classed(\"display_none\", !myInstance.config.showLabels);\r\n\r\n myInstance.drag = d3.drag()\r\n // .container(myInstance.svg._groups[0][0])\r\n .on(\"drag\", function (d) {\r\n myInstance.onDrag(d);\r\n })\r\n .on(\"start\", function (d) {\r\n myInstance.onDragStart(d);\r\n })\r\n .on(\"end\", function (d) {\r\n myInstance.onDragEnd(d);\r\n });\r\n\r\n return myInstance;\r\n }; // initLayout()\r\n\r\n /**\r\n * @ngdoc method\r\n * @name forceHorse.factory:ForceHorseFactory#draw\r\n * @description Set the graph in the DOM: nodes, edges, labels, progress bar\r\n * @returns {ForceHorseFactory} current instance\r\n */\r\n proto.draw = function () {\r\n var myInstance = this;\r\n myInstance.elements = new Array(2); // nodes, edges\r\n\r\n // draw edges\r\n this.elements[constants.EDGES] = this.edgeGroup.selectAll(\".\" + constants.CSS_CLASS_EDGE)\r\n .data(this.edgeDataArray)\r\n .enter()\r\n .append(\"line\")\r\n .attr(\"class\", constants.CSS_CLASS_EDGE)\r\n .attr(\"stroke\", function (d) {\r\n return d.color;\r\n })\r\n .attr(\"stroke-width\", (!this.config.showEdgeWeight ? null : function (d) {\r\n return myInstance.getEdgeWidth(d);\r\n }))\r\n .on(\"mouseenter\", function (d) {\r\n myInstance.onHoverInside(this, d, true);\r\n })\r\n .on(\"mouseleave\", function (d) {\r\n myInstance.onHoverInside(this, d, false);\r\n })\r\n .on(\"click\", function (d) {\r\n myInstance.onClick(d, this);\r\n })\r\n // Prevent panning when dragging a node\r\n .on(\"mousedown\", function () {\r\n d3.event.stopPropagation();\r\n })\r\n ;\r\n\r\n // draw nodes\r\n this.elements[constants.NODES] = this.nodeGroup.selectAll(\".\" + constants.CSS_CLASS_NODE)\r\n .data(this.nodeDataArray)\r\n .enter()\r\n .append(\"path\")\r\n // Set node shape & size\r\n .attr(\"d\", d3.symbol()\r\n .type(function (d) {\r\n return d.shape;\r\n })\r\n .size(function (d) {\r\n return myInstance.getNodeIconArea(d);\r\n }))\r\n .attr(\"fill\", function (d) {\r\n return d.color;\r\n })\r\n .attr(\"class\", constants.CSS_CLASS_NODE)\r\n .on(\"mouseenter\", function (d) {\r\n myInstance.onHoverInside(this, d, true);\r\n })\r\n .on(\"mouseleave\", function (d) {\r\n myInstance.onHoverInside(this, d, false);\r\n })\r\n .on(\"click\", function (d) {\r\n myInstance.onClick(d, this);\r\n })\r\n .on(\"dblclick\", function (d) {\r\n myInstance.callEventListeners(\"dblclick\", d);\r\n })\r\n // Prevent panning when dragging a node\r\n .on(\"mousedown\", function () {\r\n d3.event.stopPropagation();\r\n })\r\n .call(this.drag);\r\n\r\n // draw node labels\r\n this.labels = this.labelGroup.selectAll(\"text.label\")\r\n .data(this.nodeDataArray)\r\n .enter()\r\n .append(\"text\")\r\n .attr(\"fill\", function (d) {\r\n return d.color;\r\n })\r\n .text(function (d) {\r\n return d.label;\r\n })\r\n .attr(\"dx\", function (d) {\r\n return (helper.isHebrewString(d.label) ? -1 : +1) * constants.LABEL_DISPLACEMENT;\r\n })\r\n .attr(\"text-anchor\", function (d) {\r\n return (helper.isHebrewString(d.label) ? \"end\" : \"start\");\r\n })\r\n ;\r\n\r\n // Draw progress bar\r\n this.progressBar = this.svg\r\n .append('line')\r\n .attr('class', 'progress')\r\n .attr('x1', '0')\r\n .attr('y1', '1')\r\n .attr('x2', '0')\r\n .attr('y2', '1');\r\n\r\n // set an on-resize event, to fix aspect ratios\r\n d3.select(window).on(`resize.${this.instanceName}`, function () {\r\n myInstance.onWindowResize();\r\n });\r\n\r\n return this;\r\n };\r\n\r\n /**\r\n * @ngdoc method\r\n * @name forceHorse.factory:ForceHorseFactory#restartForceSimulation\r\n * @description Restart the force simulation\r\n * @returns {ForceHorseFactory} current instance\r\n */\r\n proto.restartForceSimulation = function () {\r\n this.force.alpha(constants.MAX_ALPHA);\r\n this.onForceStart();\r\n // this.force.restart();\r\n return this;\r\n };\r\n\r\n /**\r\n * @ngdoc method\r\n * @name forceHorse.factory:ForceHorseFactory#calcFixAspectRatio\r\n * @description Returns a number to be multiplied by an element's width, to fix aspect ratio deformation, due to the svg fixAspectRatio=\"none\"\r\n * @returns {ForceHorseFactory} current instance\r\n */\r\n proto.calcFixAspectRatio = function () {\r\n var currentRect = this.svg._groups[0][0].getBoundingClientRect(),\r\n currentHeight = currentRect.height,\r\n currentWidth = currentRect.width;\r\n this.fixAspectRatio = (constants.INNER_SVG_WIDTH / constants.INNER_SVG_HEIGHT) * (currentHeight / currentWidth);\r\n return this;\r\n };\r\n\r\n /**\r\n * @ngdoc method\r\n * @name forceHorse.factory:ForceHorseFactory#getNodeIconArea\r\n * @description Calculates the desired node icon area (with or without showing weight)\r\n * @returns {number}\r\n */\r\n proto.getNodeIconArea = function (nodeData) {\r\n var myInstance = this;\r\n return myInstance.nodeIconAreaDefault +\r\n (myInstance.config.showNodeWeight\r\n ? (myInstance.config.useEdgesWeights\r\n ? nodeData.edgesWeight\r\n : angular.isDefined(nodeData.weight)\r\n ? nodeData.weight\r\n : 1)\r\n * constants.node_size_addition_per_weight_unit\r\n : 0);\r\n };\r\n\r\n /**\r\n * @ngdoc method\r\n * @name forceHorse.factory:ForceHorseFactory#getEdgeWidth\r\n * @description Calculates the desired edge width (with or without showing weight)\r\n * @returns {number}\r\n */\r\n proto.getEdgeWidth = function (edgeData) {\r\n return constants.DEFAULT_EDGE_WIDTH + (edgeData.weight / 3) + 'px';\r\n };\r\n\r\n /**\r\n * @ngdoc method\r\n * @name forceHorse.factory:ForceHorseFactory#onFilterInside\r\n * @description Filter button action: remove selected elements\r\n * @returns {ForceHorseFactory} current instance\r\n */\r\n proto.onFilterInside = function () {\r\n // Mark the selected items as filtered, and unselect them\r\n // Also clear the selected-items sets\r\n for (var itemType = constants.NODES; itemType <= constants.EDGES; itemType++) {\r\n this.elements[itemType].filter(function (item) {\r\n return item.selected;\r\n }).classed('filtered', function (d) {\r\n return d.filtered = true;\r\n }).classed('selected', function (d) {\r\n return d.selected = false;\r\n });\r\n this.selectedItems[itemType].clear();\r\n }\r\n\r\n // Remove the labels of filtered nodes\r\n this.labels.classed(\"selected\", \"false\")\r\n .classed(\"filtered\", function (d) {\r\n return d.filtered;\r\n });\r\n\r\n // Remove edges connected to filtered nodes\r\n this.elements[constants.EDGES].filter(function (d) {\r\n return d.source.filtered || d.target.filtered;\r\n }).classed(\"filtered\", function (d) {\r\n return d.filtered = true;\r\n });\r\n\r\n // Cancel selection mode\r\n this.svg.classed(\"selectionMode\", false);\r\n\r\n // Broadcast event\r\n this.callEventListeners('filter');\r\n\r\n return this;\r\n };\r\n\r\n /**\r\n * @ngdoc method\r\n * @name forceHorse.factory:ForceHorseFactory#onFilterOutside\r\n * @description API: some elements were filtered out, update the graph\r\n * @returns {ForceHorseFactory} current instance\r\n */\r\n proto.onFilterOutside = function () {\r\n var myInstance = this;\r\n // Give the filtered elements the approprite CSS class\r\n // If a filtered element was selected, mark it as unselected\r\n for (var itemType = constants.NODES; itemType <= constants.EDGES; itemType++) {\r\n this.elements[itemType].filter(function (item) {\r\n return item.filtered;\r\n }).classed('filtered', true)\r\n .classed('selected', false)\r\n .each(function (d) {\r\n let type = (d.class === constants.CLASS_NODE ? constants.NODES : constants.EDGES);\r\n myInstance.selectedItems[type].delete(d.id);\r\n });\r\n }\r\n\r\n // Remove the labels of filtered nodes\r\n this.labels.filter(function (item) {\r\n return item.filtered;\r\n }).classed('filtered', true)\r\n .classed('selected', false);\r\n\r\n // Remove edges connected to filtered nodes\r\n this.elements[constants.EDGES].filter(function (d) {\r\n return d.source.filtered || d.target.filtered;\r\n }).classed(\"filtered\", function (d) {\r\n return d.filtered = true;\r\n });\r\n\r\n // Update visual selection mode\r\n myInstance.svg.classed(\"selectionMode\",\r\n myInstance.selectedItems[constants.NODES].size + myInstance.selectedItems[constants.EDGES].size);\r\n\r\n return this;\r\n };\r\n\r\n /**\r\n * @ngdoc method\r\n * @name forceHorse.factory:ForceHorseFactory#processNodes\r\n * @description Graph initialization: add auxiliary properties and variables to the nodes array\r\n * @returns {ForceHorseFactory} current instance\r\n */\r\n proto.processNodes = function () {\r\n var myInstance = this;\r\n this.nodesById = {};\r\n this.nodeDataArray.forEach(function (val, idx) {\r\n if (angular.isUndefined(val.id)) {\r\n val.id = idx;\r\n //console.error(\"Undefined [id] in nodes array\");\r\n }\r\n myInstance.nodesById[val.id] = idx;\r\n if (angular.isUndefined(val.label)) {\r\n val.label = \"\" + val.id;\r\n }\r\n });\r\n return this;\r\n };\r\n\r\n /**\r\n * @ngdoc method\r\n * @name forceHorse.factory:ForceHorseFactory#processEdges\r\n * @description Graph initialization: add auxiliary properties and variables to the edges array\r\n * @returns {ForceHorseFactory} current instance\r\n */\r\n proto.processEdges = function () {\r\n //----------\r\n function calculateEdgesWeightsForNodes(edge){\r\n // calculate edges weight\r\n var sourceNode = myInstance.nodeDataArray[edge.source],\r\n targetNode = myInstance.nodeDataArray[edge.target];\r\n\r\n // protect in case undefined\r\n if (!sourceNode.edgesWeights){\r\n sourceNode.edgesWeights = 0;\r\n }\r\n\r\n if (!targetNode.edgesWeights){\r\n targetNode.edgesWeights = 0;\r\n }\r\n\r\n if (!edge.weight) {\r\n edge.weight = 1;\r\n }\r\n\r\n sourceNode.edgesWeights += edge.weight;\r\n targetNode.edgesWeights += edge.weight;\r\n }\r\n //----------\r\n\r\n var myInstance = this, sid, tid, key;\r\n this.edgesFromNodes = {};\r\n this.edgeDataArray.forEach(function (val, idx) {\r\n\r\n if (angular.isUndefined(val.id)) {\r\n val.id = idx;\r\n // console.warn(`Undefined [id] in edge ${val.sourceID} - ${val.targetID}`);\r\n }\r\n // Get nodes data from nodes id's\r\n if (angular.isUndefined(val.sourceID)) {\r\n val.sourceID = val.source;\r\n //console.error(\"Undefined [sourceID] in edge #\" + val.id);\r\n }\r\n val.source = myInstance.nodesById[val.sourceID];\r\n if (angular.isUndefined(val.targetID)) {\r\n val.targetID = val.target;\r\n //console.error(\"Undefined [targetID] in edges #\" + val.id);\r\n }\r\n val.target = myInstance.nodesById[val.targetID];\r\n\r\n calculateEdgesWeightsForNodes(val);\r\n\r\n // Build an index to help handle the case of multiple edges between two nodes\r\n if (angular.isDefined(val.sourceID) && angular.isDefined(val.targetID)) {\r\n sid = val.sourceID;\r\n tid = val.targetID;\r\n key = (sid < tid ? sid + \",\" + tid : tid + \",\" + sid);\r\n if (angular.isUndefined(myInstance.edgesFromNodes[key])) {\r\n myInstance.edgesFromNodes[key] = [idx];\r\n val.multiIdx = 1;\r\n } else {\r\n val.multiIdx = myInstance.edgesFromNodes[key].push(idx);\r\n }\r\n // Calculate base edge offset, from the index in the multiple-edges array:\r\n // 1 -> 0, 2 -> 2, 3-> -2, 4 -> 4, 5 -> -4, ...\r\n val.basicOffset = (val.multiIdx % 2 === 0 ? val.multiIdx * constants.DEFAULT_EDGE_WIDTH : (-val.multiIdx + 1) * constants.DEFAULT_EDGE_WIDTH);\r\n }\r\n });\r\n return this;\r\n };\r\n\r\n /**\r\n * @ngdoc method\r\n * @name forceHorse.factory:ForceHorseFactory#onWindowResize\r\n * @description Fix aspect ratios, when the window resizes\r\n * @returns {ForceHorseFactory} current instance\r\n */\r\n proto.onWindowResize = function () {\r\n return this.calcFixAspectRatio()\r\n .updateGraphInDOM();\r\n };\r\n\r\n /**\r\n * @ngdoc method\r\n * @name forceHorse.factory:ForceHorseFactory#onForceStart\r\n * @description Called when a force-simulation is supposed to start.\r\n * @returns {ForceHorseFactory} current instance\r\n */\r\n proto.onForceStart = function () {\r\n // Prevent simulation when dragging a node\r\n // if (this.isDragging) {\r\n // this.force.stop();\r\n // return this;\r\n // }\r\n // Proceed with simulation\r\n return this.calcFixAspectRatio()\r\n [this.numOfNodes < constants.HEAVY_SIMULATION_NUM_OF_NODES ?\r\n \"runSimulation\" : \"runHeavySimulation\"]();\r\n\r\n };\r\n\r\n /**\r\n * @ngdoc method\r\n * @name forceHorse.factory:ForceHorseFactory#runSimulation\r\n * @description\r\n * Run the force-simulation with control.\r\n * The DOM is not updated for every tick.\r\n * @returns {ForceHorseFactory} current instance\r\n */\r\n proto.runSimulation = function () {\r\n var myInstance = this;\r\n var ticksPerRender,\r\n simulationStart = performance.now(), simulationDuration, calculationStart, calculationDuration = 0,\r\n ticks = 0;\r\n\r\n requestAnimationFrame(function render() {\r\n // Do not accelerate the simulation during dragging, so as not to slow the dragging.\r\n ticksPerRender = (myInstance.isDragging ? 1 : myInstance.numOfNodes / 7);\r\n calculationStart = performance.now();\r\n for (let i = 0; i < ticksPerRender && myInstance.force.alpha() > myInstance.force.alphaMin(); i++) {\r\n myInstance.force.tick();\r\n ticks++;\r\n }\r\n calculationDuration += (performance.now() - calculationStart);\r\n myInstance.updateGraphInDOM().updateProgressBar();\r\n\r\n if (myInstance.force.alpha() > myInstance.force.alphaMin()) {\r\n requestAnimationFrame(render);\r\n } else {\r\n simulationDuration = performance.now() - simulationStart;\r\n console.log(`Force Simulation time = ${(simulationDuration / 1000).toFixed(2)}s, Calculation time = ${(calculationDuration / 1000).toFixed(2)}s, ${ticks} ticks`);\r\n myInstance.onForceEnd();\r\n }\r\n }); // render\r\n return this;\r\n };\r\n\r\n /**\r\n * @ngdoc method\r\n * @name forceHorse.factory:ForceHorseFactory#runHeavySimulation\r\n * @description\r\n * Heavy graphs version: run the force-simulation with control.\r\n * The DOM is not updated for every tick.\r\n * @returns {ForceHorseFactory} current instance\r\n */\r\n proto.runHeavySimulation = function () {\r\n var myInstance = this;\r\n var ticksPerRender,\r\n calculationStart, calculationDuration = 0,\r\n ticks = 0;\r\n\r\n requestAnimationFrame(function render() {\r\n // Do not accelerate the simulation during dragging, so as not to slow the dragging.\r\n ticksPerRender = (myInstance.isDragging ? 1 : 30);\r\n calculationStart = performance.now();\r\n for (let i = 0; i < ticksPerRender && myInstance.force.alpha() > 0; i++) {\r\n myInstance.force.tick();\r\n ticks++;\r\n }\r\n calculationDuration += (performance.now() - calculationStart);\r\n myInstance.updateProgressBar();\r\n if (myInstance.isDragging) {\r\n myInstance.updateGraphInDOM();\r\n }\r\n\r\n if (myInstance.force.alpha() > 0) {\r\n requestAnimationFrame(render);\r\n } else {\r\n console.log(`Calculation time = ${(calculationDuration / 1000).toFixed(2)}s, ${ticks} ticks`);\r\n myInstance.updateGraphInDOM().onForceEnd();\r\n }\r\n }); // render\r\n return this;\r\n };\r\n\r\n /**\r\n * @ngdoc method\r\n * @name forceHorse.factory:ForceHorseFactory#updateGraphInDOM\r\n * @description\r\n * Update the force simulation in the DOM\r\n * @returns {ForceHorseFactory} current instance\r\n */\r\n proto.updateGraphInDOM = function () {\r\n var myInstance = this;\r\n\r\n // Update nodes\r\n this.elements[constants.NODES]\r\n //.each(function (d) {\r\n // myInstance.preventNodesOverlap(1.0)(d);\r\n //})\r\n .attr('transform', function (d) {\r\n //if (myInstance.isBoundedGraphMode) {\r\n // // Force the nodes inside the visible area\r\n // var radius = myInstance.nodeIconRadius;\r\n // d.x = Math.max(radius, Math.min(constants.INNER_SVG_WIDTH - radius, d.x));\r\n // d.y = Math.max(radius, Math.min(constants.INNER_SVG_HEIGHT - radius, d.y));\r\n //}\r\n return `translate(${d.x},${d.y}) scale(${myInstance.fixAspectRatio},1)`;\r\n })\r\n ;\r\n\r\n // Update labels\r\n this.labels.attr(\"x\", function (d) {\r\n return d.x;\r\n })\r\n .attr(\"y\", function (d) {\r\n return d.y;\r\n })\r\n ;\r\n\r\n // Update edges\r\n this.elements[constants.EDGES].attr(\"x1\", function (d) {\r\n return d.source.x;\r\n })\r\n .attr(\"y1\", function (d) {\r\n return d.source.y;\r\n })\r\n .attr(\"x2\", function (d) {\r\n return d.target.x;\r\n })\r\n .attr(\"y2\", function (d) {\r\n return d.target.y;\r\n })\r\n // Add some translation, for the case of multiple edges between two nodes\r\n .attr('transform', function (d) {\r\n var offset = helper.calcRightAngledOffset(d.basicOffset, d.target.x - d.source.x, d.target.y - d.source.y);\r\n return \"translate(\" + offset.dx + \",\" + offset.dy + \")\";\r\n })\r\n ;\r\n\r\n return this;\r\n };\r\n\r\n /**\r\n * @ngdoc method\r\n * @name forceHorse.factory:ForceHorseFactory#updateProgressBar\r\n * @returns {ForceHorseFactory} current instance\r\n */\r\n proto.updateProgressBar = function () {\r\n // Do not update progress bar in fixed nodes mode\r\n if (!this.fixedNodesMode) {\r\n this.progressBar.attr('x2',\r\n constants.INNER_SVG_WIDTH * (1 - (this.force.alpha() - this.force.alphaMin()) / constants.MAX_ALPHA));\r\n }\r\n return this;\r\n };\r\n\r\n /**\r\n * @ngdoc method\r\n * @name forceHorse.factory:ForceHorseFactory#onForceEnd\r\n * @description\r\n * Called whenever the d3 force-simulation comes to a halt.\r\n * @returns {ForceHorseFactory} current instance\r\n */\r\n proto.onForceEnd = function () {\r\n // Zoom out the graph, if needed, so that it is fully visible.\r\n // This is done only on the first time after component start.\r\n if (!this.isFirstZoomDone) {\r\n this.zoomToViewport();\r\n this.isFirstZoomDone = true;\r\n // Also make the graph fixed, after the first force-simulation\r\n this.toggleFixedNodesMode();\r\n this.requestDigest(); // To update the related button's display\r\n }\r\n return this;\r\n };\r\n\r\n /**\r\n * @ngdoc method\r\n * @name forceHorse.factory:ForceHorseFactory#zoomToViewport\r\n * @description\r\n * Zoom out the graph, if needed, so that it is fully visible.\r\n * @returns {ForceHorseFactory} current instance\r\n */\r\n proto.zoomToViewport = function () {\r\n var scale, translate,\r\n width = constants.INNER_SVG_WIDTH,\r\n height = constants.INNER_SVG_HEIGHT,\r\n radius = this.nodeIconRadius,\r\n maxMarginX = d3.max(this.nodeDataArray, function (d) {\r\n return Math.max(-d.x + radius, d.x + radius - width, 0);\r\n }),\r\n maxMarginY = d3.max(this.nodeDataArray, function (d) {\r\n return Math.max(-d.y + radius, d.y + radius - height, 0);\r\n });\r\n if (maxMarginX > 0 || maxMarginY > 0) {\r\n // If the graph (without the current zoom/pan) exceeds the view boundaries,\r\n // calculate the zoom/pan extent to return it to the viewport.\r\n var scaleX = width / (width + 2 * maxMarginX),\r\n scaleY = height / (height + 2 * maxMarginY);\r\n scale = Math.min(scaleX, scaleY) * 0.95;\r\n translate = [(width / 2) * (1 - scale), (height / 2) * (1 - scale)];\r\n // If the calculated zoom is bigger than the zoom limit, increase the limit\r\n if (scale < constants.MAX_ZOOM) {\r\n this.zoom.scaleExtent([scale, constants.MIN_ZOOM]);\r\n }\r\n } else {\r\n // If the graph, without the current zoom/pan, is within the view boundaries,\r\n // then simply reset the zoom/pan extent.\r\n scale = 1;\r\n translate = [0, 0];\r\n }\r\n this.svg.transition()\r\n .duration(constants.ANIMATION_DURATION)\r\n .call(this.zoom.transform, d3.zoomIdentity.translate(translate[0], translate[1]).scale(scale));\r\n // .call(this.zoom.translate(translate).scale(scale).event);\r\n return this;\r\n };\r\n\r\n //---------------------------------------------------\r\n // preventNodesOverlap\r\n // A collision-detection algorithm, Based on\r\n // http://www.coppelia.io/2014/07/an-a-to-z-of-extra-features-for-the-d3-force-layout/\r\n // and http://bl.ocks.org/mbostock/7881887\r\n //---------------------------------------------------\r\n //proto.preventNodesOverlap = function (alpha) {\r\n // var radius = this.nodeIconRadius,\r\n // padding = constants.NODE_MARGIN,\r\n // quadtree = d3.geom.quadtree(this.nodeDataArray);\r\n // return function (d) {\r\n // var rb = 2 * radius + padding,\r\n // nx1 = d.x - rb,\r\n // nx2 = d.x + rb,\r\n // ny1 = d.y - rb,\r\n // ny2 = d.y + rb;\r\n // quadtree.visit(function (quad, x1, y1, x2, y2) {\r\n // if (quad.point && (quad.point !== d)) {\r\n // var x = d.x - quad.point.x,\r\n // y = d.y - quad.point.y,\r\n // l = Math.sqrt(x * x + y * y);\r\n // if (l < rb) {\r\n // l = (l - rb) / l * alpha;\r\n // d.x -= x *= l;\r\n // d.y -= y *= l;\r\n // quad.point.x += x;\r\n // quad.point.y += y;\r\n // }\r\n // }\r\n // return x1 > nx2 || x2 < nx1 || y1 > ny2 || y2 < ny1;\r\n // });\r\n // };\r\n //};\r\n\r\n /**\r\n * @ngdoc method\r\n * @name forceHorse.factory:ForceHorseFactory#onClick\r\n * @description\r\n * Event handler. called when an element is clicked on\r\n * @returns {ForceHorseFactory} current instance\r\n */\r\n proto.onClick = function (item, element) {\r\n // Ignore the click event at the end of a drag\r\n if (!d3.event.defaultPrevented) {\r\n // If the Ctrl key was pressed during the click ..\r\n // If the clicked element was marked as selected, unselect it, and vice versa\r\n if (d3.event.ctrlKey) {\r\n this.onSelectInside(element, item, !item.selected);\r\n } else {\r\n // If the Ctrl key was not pressed ..\r\n // If the clicked element is selected, unselect the other elements\r\n // (if only the clicked element is selected, unselect it)\r\n // Else, clear the current selection, and select the clicked element\r\n if (item.selected && (this.selectedItems[constants.NODES].size + this.selectedItems[constants.EDGES].size) === 1) {\r\n this.onSelectInside(element, item, false);\r\n } else {\r\n this.onSelectInside(element, item, true, true);\r\n }\r\n }\r\n }\r\n // Prevent bubbling, so that we can separately detect a click on the container\r\n d3.event.stopPropagation();\r\n return this;\r\n };\r\n\r\n /**\r\n * @ngdoc method\r\n * @name forceHorse.factory:ForceHorseFactory#onContainerClick\r\n * @description\r\n * Event handler. on a click not on a node or edge\r\n * Cancel current selection\r\n * @returns {ForceHorseFactory} current instance\r\n */\r\n proto.onContainerClick = function () {\r\n //console.log(\"Container was clicked\");\r\n if (this.selectedItems[constants.NODES].size + this.selectedItems[constants.EDGES].size > 0) {\r\n this.onSelectInside(null, null, null, true);\r\n }\r\n return this;\r\n };\r\n\r\n /**\r\n * @ngdoc method\r\n * @name forceHorse.factory:ForceHorseFactory#onHoverInside\r\n * @description\r\n * An element was hovered inside this component.\r\n * @param item A data object\r\n * @param element The corresponding DOM element\r\n * @param {boolean} on\r\n * @returns {ForceHorseFactory} current instance\r\n */\r\n proto.onHoverInside = function (element, item, on) {\r\n d3.select(element).classed(\"hovered\", item.hovered = on);\r\n return this.callEventListeners('hover', item, on);\r\n };\r\n\r\n /**\r\n * @ngdoc method\r\n * @name forceHorse.factory:ForceHorseFactory#onHoverOutside\r\n * @description\r\n * An element was hovered outside this component.\r\n * @param item data object of the hovered element\r\n * @returns {ForceHorseFactory} current instance\r\n */\r\n proto.onHoverOutside = function (item) {\r\n var itemType = (item.class === constants.CLASS_NODE ?\r\n constants.NODES : constants.EDGES);\r\n this.elements[itemType].filter(function (d) {\r\n return d.id === item.id;\r\n })\r\n .classed(\"hovered\", item.hovered);\r\n return this;\r\n };\r\n\r\n /**\r\n * @ngdoc method\r\n * @name forceHorse.factory:ForceHorseFactory#onSelectInside\r\n * @description\r\n * Called when an element is meant to be selected inside this component.\r\n * @param item The data object bound to the selected element\r\n * @param element The DOM element\r\n * @param {boolean} on Select or Unselect\r\n * @param {boolean} clearOldSelection whether to clear first the current selection\r\n * @returns {ForceHorseFactory} current instance\r\n */\r\n proto.onSelectInside = function (element, item, on, clearOldSelection) {\r\n var myInstance = this;\r\n var itemType;\r\n\r\n if (clearOldSelection) {\r\n for (itemType = constants.NODES; itemType <= constants.EDGES; itemType++) {\r\n myInstance.elements[itemType].filter(function (d) {\r\n return myInstance.selectedItems[itemType].has(d.id);\r\n }).classed(\"selected\", function (d) {\r\n return d.selected = false;\r\n });\r\n myInstance.selectedItems[itemType].clear();\r\n }\r\n }\r\n\r\n // Update the DOM element\r\n if (element) {\r\n d3.select(element).classed(\"selected\", item.selected = on);\r\n }\r\n\r\n // Update the labels\r\n this.labels.classed(\"selected\", function (d) {\r\n return d.selected;\r\n });\r\n\r\n // Update the selectedItems set\r\n if (item) {\r\n itemType = (item.class === constants.CLASS_NODE ? constants.NODES : constants.EDGES);\r\n if (item.selected) {\r\n this.selectedItems[itemType].add(item.id);\r\n } else {\r\n this.selectedItems[itemType].delete(item.id);\r\n }\r\n }\r\n\r\n // In \"selectionMode\" the unselected nodes are visually marked\r\n this.svg.classed(\"selectionMode\",\r\n this.selectedItems[constants.NODES].size + myInstance.selectedItems[constants.EDGES].size);\r\n\r\n return this.callEventListeners('select');\r\n };\r\n\r\n /**\r\n * @ngdoc method\r\n * @name forceHorse.factory:ForceHorseFactory#onSelectOutside\r\n * @description\r\n * API: Called when elements were selected and/or unselected outside this component.\r\n * @returns {ForceHorseFactory} current instance\r\n */\r\n proto.onSelectOutside = function () {\r\n var myInstance = this;\r\n // Update the \"selected\" css class, and the selected-items sets\r\n for (var itemType = constants.NODES; itemType <= constants.EDGES; itemType++) {\r\n (function (mySet) {\r\n mySet.clear();\r\n myInstance.elements[itemType]\r\n .classed('selected', function (d) {\r\n if (d.selected) {\r\n mySet.add(d.id);\r\n return true;\r\n } else {\r\n return false;\r\n }\r\n });\r\n }(this.selectedItems[itemType]))\r\n }\r\n\r\n // Update the labels\r\n this.labels.classed(\"selected\", function (d) {\r\n return d.selected;\r\n });\r\n\r\n // In \"selectionMode\" the unselected nodes are visually marked\r\n this.svg.classed(\"selectionMode\",\r\n this.selectedItems[constants.NODES].size + myInstance.selectedItems[constants.EDGES].size);\r\n return this;\r\n };\r\n\r\n /**\r\n * @ngdoc method\r\n * @name forceHorse.factory:ForceHorseFactory#onZoom\r\n * @description\r\n * Perform pan/zoom\r\n * @returns {ForceHorseFactory} current instance\r\n */\r\n proto.onZoom = function () {\r\n var trans = d3.event.transform;\r\n\r\n if (this.inSvgWrapper) {\r\n this.inSvgWrapper.attr(\"transform\",\r\n `translate(${trans.x}, ${trans.y}) scale(${trans.k})`);\r\n }\r\n return this;\r\n };\r\n\r\n /**\r\n * @ngdoc method\r\n * @name forceHorse.factory:ForceHorseFactory#onDragStart\r\n * @description\r\n * Event handler, called when a node-dragging starts\r\n * *UPDATE* I avoid using it, because it is triggered by a normal mouse-down\r\n * @returns {ForceHorseFactory} current instance\r\n */\r\n proto.onDragStart = function (/*d*/) {\r\n // Fix the dragged node (not moved by the simulation)\r\n // d.fx = d.x;\r\n // d.fy = d.y;\r\n // Start the simulation\r\n return this;\r\n };\r\n\r\n /**\r\n * @ngdoc method\r\n * @name forceHorse.factory:ForceHorseFactory#onDrag\r\n * @description\r\n * Node-dragging event handler\r\n * @param d The data item bound to the dragged node\r\n * @returns {ForceHorseFactory} current instance\r\n */\r\n proto.onDrag = function (d) {\r\n // Fix the dragged node (not moved by the simulation)\r\n d.fx = d3.event.x;\r\n d.fy = d3.event.y;\r\n if (!this.isDragging) {\r\n this.isDragging = true;\r\n this.restartForceSimulation();\r\n }\r\n return this;\r\n };\r\n\r\n /**\r\n * @ngdoc method\r\n * @name forceHorse.factory:ForceHorseFactory#onDragEnd\r\n * @description\r\n * Event handler, called when a node-dragging ends\r\n * @returns {ForceHorseFactory} current instance\r\n */\r\n proto.onDragEnd = function (d) {\r\n this.isDragging = false;\r\n // Cool the simulation\r\n this.force.alpha(this.force.alphaMin());\r\n // Unfix the dragged node\r\n if (!this.fixedNodesMode) {\r\n d.fx = null;\r\n d.fy = null;\r\n }\r\n return this;\r\n };\r\n\r\n /**\r\n * @ngdoc method\r\n * @name forceHorse.factory:ForceHorseFactory#toggleFixedNodesMode\r\n * @description\r\n * Called from Pause/Play button\r\n * Pause fixes all the nodes\r\n * Play unfixes all the nodes\r\n * @returns {ForceHorseFactory} current instance\r\n */\r\n proto.toggleFixedNodesMode = function () {\r\n if (this.fixedNodesMode) {\r\n this.elements[constants.NODES].classed('fixed', function (d) {\r\n d.fx = null;\r\n d.fy = null;\r\n return d.fixed = false;\r\n });\r\n this.fixedNodesMode = false;\r\n this.restartForceSimulation();\r\n// this.force.start();\r\n } else {\r\n this.elements[constants.NODES].classed('fixed', function (d) {\r\n d.fx = d.x;\r\n d.fy = d.y;\r\n return d.fixed = true;\r\n });\r\n this.fixedNodesMode = true;\r\n }\r\n return this;\r\n };\r\n\r\n /**\r\n * @ngdoc method\r\n * @name forceHorse.factory:ForceHorseFactory#onLabelsShowHideBtnClick\r\n * @description\r\n * Show or hide labels\r\n * Called when the labels button is clicked on\r\n * @returns {ForceHorseFactory} current instance\r\n */\r\n proto.onLabelsShowHideBtnClick = function () {\r\n //var myInstance = this;\r\n if (this.config.showLabels = !this.config.showLabels) {\r\n this.labelGroup.classed('display_none', false);\r\n //this.labelGroup.transition().attr(\"opacity\", \"0\");\r\n //setTimeout(function () {\r\n // myInstance.labelGroup.classed('display_none', true);\r\n //}, constants.ANIMATION_DELAY);\r\n } else { // show labels\r\n this.labelGroup.classed('display_none', true);\r\n //setTimeout(function () {\r\n // myInstance.labelGroup.transition().attr(\"opacity\", \"1\");\r\n //}, constants.ANIMATION_DELAY);\r\n }\r\n return this;\r\n };\r\n\r\n /**\r\n * @ngdoc method\r\n * @name forceHorse.factory:ForceHorseFactory#onNodeWeightShowHideBtnClick\r\n * @description\r\n * Show or hide node weights\r\n * Called when the node weight button is clicked on\r\n * @returns {ForceHorseFactory} current instance\r\n */\r\n proto.onNodeWeightShowHideBtnClick = function () {\r\n var myInstance = this;\r\n this.config.showNodeWeight = !this.config.showNodeWeight;\r\n this.elements[constants.NODES]\r\n .attr(\"d\", d3.symbol()\r\n .type(function (d) {\r\n return d.shape;\r\n })\r\n .size(function (d) {\r\n return myInstance.getNodeIconArea(d);\r\n }));\r\n return this;\r\n };\r\n\r\n /**\r\n * @ngdoc method\r\n * @name forceHorse.factory:ForceHorseFactory#onEdgeWeightShowHideBtnClick\r\n * @description\r\n * Show or hide edge weights\r\n * Called when the edge weight button is clicked on\r\n * @returns {ForceHorseFactory} current instance\r\n */\r\n proto.onEdgeWeightShowHideBtnClick = function () {\r\n var myInstance = this;\r\n this.config.showEdgeWeight = !this.config.showEdgeWeight;\r\n this.elements[constants.EDGES]\r\n .attr(\"stroke-width\", (!this.config.showEdgeWeight ? null : function (d) {\r\n return myInstance.getEdgeWidth(d);\r\n }));\r\n return this;\r\n };\r\n\r\n /**\r\n * @ngdoc method\r\n * @name forceHorse.factory:ForceHorseFactory#addEventListener\r\n * @description\r\n * API: Register event callbacks with this component\r\n * @param type The event type\r\n * @param callback The event listener to register\r\n * @returns {ForceHorseFactory} current instance\r\n */\r\n proto.addEventListener = function (type, callback) {\r\n if (typeof this.eventListeners[type] === 'undefined'){\r\n this.eventListeners[type] = [];\r\n }\r\n this.eventListeners[type].push(callback);\r\n return this;\r\n };\r\n\r\n /**\r\n * @ngdoc method\r\n * @name forceHorse.factory:ForceHorseFactory#callEventListeners\r\n * @description\r\n * Call the registered event listeners, for an event type\r\n * @param type The event type (hover, select, ...)\r\n * @param args Arguments for the event listener\r\n * @returns {ForceHorseFactory} current instance\r\n */\r\n proto.callEventListeners = function (type, ...args) {\r\n if (typeof this.eventListeners[type] === 'undefined'){\r\n return;\r\n }\r\n this.eventListeners[type].forEach(function (callback) {\r\n callback(...args);\r\n });\r\n return this;\r\n };\r\n\r\n\r\n /**\r\n * @ngdoc method\r\n * @name forceHorse.factory:ForceHorseFactory#convertFileDataFormat\r\n * @description api. See the method in helper.\r\n * @param fileData\r\n * @returns {*|*[]}\r\n */\r\n proto.convertFileDataFormat = function (fileData) {\r\n return helper.convertFileDataFormat(fileData);\r\n };\r\n\r\n //---------------------------------------------------\r\n return ForceHorseFactory;\r\n }])\r\n\r\n\r\n /**\r\n * @ngdoc constant\r\n * @name forceHorse.constant:ForceHorseConstants\r\n * @description A constants object for the forceHorse component\r\n */\r\n .constant('ForceHorseConstants', {\r\n INNER_SVG_WIDTH: 540,\r\n INNER_SVG_HEIGHT: 480,\r\n NODES: 0,\r\n EDGES: 1,\r\n NODES_ID: 1,\r\n EDGES_ID: 2,\r\n CLASS_NODE: 'Node',\r\n CLASS_EDGE: 'Edge',\r\n CSS_CLASS_NODE: 'node',\r\n CSS_CLASS_EDGE: 'edge',\r\n DEFAULT_EDGE_WIDTH: 1.5,\r\n DEFAULT_EDGE_COLOR: 'brown',\r\n DEFAULT_NODE_COLOR: '#6060a0',\r\n NODE_MARGIN: 10,\r\n LABEL_DISPLACEMENT: 10,\r\n MAX_ZOOM: 0.5,\r\n MIN_ZOOM: 2,\r\n ANIMATION_DURATION: 1000,\r\n ANIMATION_DELAY: 200,\r\n ALEPHBET: 'abcdefghijklmnopqrstuvwxyz',\r\n INSTANCE_NAME_LENGTH: 5,\r\n MAX_ALPHA: 0.1,\r\n HEAVY_SIMULATION_NUM_OF_NODES: 420,\r\n DEFAULT_CHARGE_LIGHT: -350,\r\n DEFAULT_CHARGE_HEAVY: -15000,\r\n CONFIG_FILE_NAME: 'forceHorse.json',\r\n get node_size_addition_per_weight_unit() {\r\n return this.INNER_SVG_WIDTH * this.INNER_SVG_HEIGHT / (54 * 48 * 3);\r\n }\r\n })\r\n\r\n\r\n /**\r\n * @ngdoc service\r\n * @name forceHorse.service:ForceHorseHelper\r\n * @description A helper object with methods for the forceHorse component\r\n */\r\n //---------------------------------------------------------------//\r\n .service('ForceHorseHelper', ['ForceHorseConstants', '$templateCache', '$compile', function (constants, templates, $compile) {\r\n return {\r\n\r\n /**\r\n * @ngdoc method\r\n * @name forceHorse.service:ForceHorseHelper#addButtons\r\n * @description\r\n * Add a buttons bar, at the top of the container\r\n * @returns {ForceHorseHelper} current object\r\n */\r\n addButtons: function (scope, container) {\r\n var template = templates.get('forceHorse/buttons');\r\n var element = angular.element(template);\r\n var compiledElement = $compile(element)(scope);\r\n container.prepend(compiledElement);\r\n // console.log('Added buttons');\r\n return this;\r\n },\r\n\r\n /**\r\n * @ngdoc method\r\n * @name forceHorse.service:ForceHorseHelper#calcRightAngledOffset\r\n * @description\r\n * Calculate where to display edges, for the case of multiple edges between two nodes\r\n * @param basicOffset The desired distance from the parallel edge to the first edge\r\n * @param origDx The x-difference between the two end points of the first edge\r\n * @param origDy The y-difference between the two end points of the first edge\r\n * @returns {Object} {dx:dx, dy:dy} The calculated offset of the parallel edge from the first edge\r\n */\r\n calcRightAngledOffset: function (basicOffset, origDx, origDy) {\r\n var dx, dy;\r\n if (basicOffset === 0) {\r\n dx = dy = 0;\r\n } else if (origDy === 0 || Math.abs(origDx / origDy) > 1) {\r\n dy = -basicOffset * constants.INNER_SVG_WIDTH / constants.INNER_SVG_HEIGHT;\r\n dx = basicOffset * (origDy) / origDx;\r\n } else {\r\n dx = basicOffset;\r\n dy = basicOffset * (-origDx) / origDy;\r\n }\r\n if (!angular.isNumber(dx)) {\r\n console.warn(`calcRightAngledOffset: dx is not a number! basicOffset=${basicOffset} origDx=${origDx} origDy=${origDy}`);\r\n }\r\n return {dx: dx, dy: dy};\r\n },\r\n\r\n /**\r\n * @ngdoc method\r\n * @name forceHorse.service:ForceHorseHelper#computeFrictionParameter\r\n * @description\r\n * Compute the friction parameter for the force-simulation, with a mysterious formula supplied by Omer.\r\n * @param {number} width_in_pixels Width of the simulation area\r\n * @param {number} height_in_pixels Height of the simulation area\r\n * @param {number} number_of_nodes No. of nodes in the graph\r\n * @returns {number}\r\n */\r\n computeFrictionParameter: function (width_in_pixels, height_in_pixels, number_of_nodes) {\r\n var A = 0.0356,\r\n B = 1.162,\r\n x = 100 * number_of_nodes / (height_in_pixels * width_in_pixels);\r\n if (x < 0.0634) x = 0.0634;\r\n return A * Math.pow(x, -B);\r\n },\r\n\r\n /**\r\n * @ngdoc method\r\n * @name forceHorse.service:ForceHorseHelper#isHebrewString\r\n * @description\r\n * Does the given string start with a hebrew letter?\r\n * @param {string} s\r\n * @returns {boolean}\r\n */\r\n isHebrewString: function (s) {\r\n var c = s.charAt(0);\r\n return (c >= 'א' && c <= 'ת');\r\n },\r\n\r\n /**\r\n * @ngdoc method\r\n * @name forceHorse.service:ForceHorseHelper#getCurrentDirectory\r\n * @description\r\n * See http://stackoverflow.com/a/21103831/4402222\r\n * @returns {string}\r\n */\r\n/*\r\n getCurrentDirectory: () => {\r\n var scripts = document.getElementsByTagName(\"script\");\r\n var currentScriptPath = scripts[scripts.length-1].src;\r\n return currentScriptPath.substring(0,currentScriptPath.lastIndexOf(\"/\")+1 );\r\n },\r\n*/\r\n\r\n /**\r\n * @ngdoc method\r\n * @name forceHorse.service:ForceHorseHelper#convertFileDataFormat\r\n * @param fileData\r\n * @returns {*[]}\r\n * @description\r\n * fileData is supposed to be in the format\r\n * {nodes: [nodeData, nodeData, ...] links: [linkData, linkData, ...]}\r\n * \"edges\" are also allowed, in place of \"links\".\r\n * If nodeData does not contain an id property, its id is set to its index in the array.\r\n * If nodeData does not contain a label property, it gets a default label.\r\n * A \"class\" property (node class) is also added to each nodeData.\r\n * Set node shape\r\n * If linkData does not contain an id property, its id is set to its index in the array.\r\n * If linkData does not contain an sourceID property, sourceID is set to source.\r\n * If linkData does not contain an targetID property, targetID is set to target.\r\n * A \"class\" property (link class) is also added to each linkData.\r\n * Also sourceLabel, targetLabel.\r\n * The resulting data is returned restructured like:\r\n * [ {id: constants.NODES_ID, data: nodesArray}, {id: constants.LINKS_ID, data: linksArray} ]\r\n ]\r\n */\r\n convertFileDataFormat: function (fileData) {\r\n // Process nodes\r\n var nodes = fileData.nodes;\r\n nodes.forEach(function (node, idx) {\r\n if (angular.isUndefined(node.id)) {\r\n node.id = idx;\r\n }\r\n if (angular.isUndefined(node.label)) {\r\n node.label = \"\" + node.id;\r\n }\r\n node.class = constants.CLASS_NODE;\r\n\r\n switch (node.shape) {\r\n case undefined:\r\n case \"\":\r\n case \"circle\":\r\n node.shape = d3.symbolCircle;\r\n break;\r\n case \"cross\":\r\n node.shape = d3.symbolCross;\r\n break;\r\n case \"diamond\":\r\n node.shape = d3.symbolDiamond;\r\n break;\r\n case \"square\":\r\n node.shape = d3.symbolSquare;\r\n break;\r\n case \"triangle-up\":\r\n case \"triangle-down\":\r\n node.shape = d3.symbolTriangle;\r\n break;\r\n case \"star\":\r\n node.shape = d3.symbolStar;\r\n break;\r\n case \"wye\":\r\n node.shape = d3.symbolWye;\r\n break;\r\n default:\r\n node.shape = d3.symbolCircle;\r\n }\r\n });\r\n // Process edges\r\n var edges = (fileData.edges ? fileData.edges : fileData.links);\r\n edges.forEach( function(edge, idx) {\r\n if (angular.isUndefined(edge.id)) {\r\n edge.id = idx;\r\n }\r\n if (angular.isUndefined(edge.sourceID)) {\r\n edge.sourceID = edge.source;\r\n }\r\n if (angular.isUndefined(edge.targetID)) {\r\n edge.targetID = edge.target;\r\n }\r\n edge.sourceLabel = edge.sourceID;\r\n edge.targetLabel = edge.targetID;\r\n edge.class = constants.CLASS_EDGE;\r\n });\r\n // Return the (processed) data\r\n return [\r\n {id: constants.NODES_ID, data: nodes},\r\n {id: constants.EDGES_ID, data: edges}\r\n ];\r\n }\r\n\r\n\r\n }; // return {\r\n }]) // .service\r\n;\r\n"]} \ No newline at end of file diff --git a/components/forceHorse/forceHorse.js b/components/forceHorse/forceHorse.js index d795091..fe98d87 100644 --- a/components/forceHorse/forceHorse.js +++ b/components/forceHorse/forceHorse.js @@ -135,7 +135,7 @@ angular.module('forceHorse', []) myInstance.initLayout(json); // The force simulation has to started before drawing nodes and links, // because it computes some drawing-relevant properties (node weight) - myInstance.startForceSimulation(); + myInstance.restartForceSimulation(); myInstance.draw(); }; // $http.get(helper.getCurrentDirectory() + constants.CONFIG_FILE_NAME) @@ -154,7 +154,7 @@ angular.module('forceHorse', []) // } // myInstance.initLayout(json); // myInstance.draw(); - // myInstance.startForceSimulation(); + // myInstance.restartForceSimulation(); //}); return this; }; @@ -215,46 +215,51 @@ angular.module('forceHorse', []) Object.assign(this.config, config); // Create a forceLayout instance - myInstance.force = d3.layout.force() - .size([constants.INNER_SVG_WIDTH, constants.INNER_SVG_HEIGHT]) - .on("start", function () { - myInstance.onForceStart(); - }); + myInstance.force = d3.forceSimulation(); + // .size([constants.INNER_SVG_WIDTH, constants.INNER_SVG_HEIGHT]) + // .on("start", function () { + // myInstance.onForceStart(); + // }); + // todo: start event replacement? + var p; - if (angular.isDefined(p = myInstance.config.forceParameters.linkDistance)) myInstance.force.linkDistance(p); - if (angular.isDefined(p = myInstance.config.forceParameters.linkStrength)) myInstance.force.linkStrength(p); - //if (angular.isDefined(p = myInstance.config.forceParameters.charge)) myInstance.force.charge(p); - if (angular.isDefined(p = myInstance.config.forceParameters.gravity)) myInstance.force.gravity(p); + + var forceCenter = d3.forceCenter(constants.INNER_SVG_WIDTH / 2, constants.INNER_SVG_HEIGHT / 2); + myInstance.force.force("center", forceCenter); + // Todo: add center coordinates? + // Todo: a gravity measure replacement? + // if (angular.isDefined(p = myInstance.config.forceParameters.gravity)) myInstance.force.gravity(p); + if (angular.isDefined(p = myInstance.config.forceParameters.charge)) { - myInstance.force.charge(p); } else { if (myInstance.numOfNodes < constants.HEAVY_SIMULATION_NUM_OF_NODES) { - myInstance.force.charge(function (d) { + p = function (d) { return d.weight * constants.DEFAULT_CHARGE_LIGHT; - }); + }; } else { - myInstance.force.charge(constants.DEFAULT_CHARGE_HEAVY); + p = constants.DEFAULT_CHARGE_HEAVY; } } + myInstance.force.force("charge", d3.forceManyBody().strength(p)); + // myInstance.force.charge(p); + if (angular.isDefined(p = myInstance.config.forceParameters.friction)) { - myInstance.force.friction(p); } else { - myInstance.force.friction(helper.computeFrictionParameter(constants.INNER_SVG_WIDTH, constants.INNER_SVG_HEIGHT, this.nodeDataArray.length)) + p = helper.computeFrictionParameter(constants.INNER_SVG_WIDTH, constants.INNER_SVG_HEIGHT, this.nodeDataArray.length) } + myInstance.force.velocityDecay(p); + // myInstance.force.friction(p); - myInstance.drag = myInstance.force.drag() - .on("drag", function (d) { - myInstance.onDrag(d); - }) - .on("dragend", function () { - myInstance.onDragEnd(); - }); - - myInstance.force.nodes(myInstance.nodeDataArray) - .links(this.edgeDataArray); + myInstance.force.nodes(myInstance.nodeDataArray); + // .links(this.edgeDataArray); //.start(); - myInstance.zoom = d3.behavior.zoom() + var linkForce = myInstance.force.force("link", + d3.forceLink(myInstance.edgeDataArray).id(function(d, i) { return i; })); + if (angular.isDefined(p = myInstance.config.forceParameters.linkDistance)) linkForce.distance(p); + if (angular.isDefined(p = myInstance.config.forceParameters.linkStrength)) linkForce.strength(p); + + myInstance.zoom = d3.zoom() .scaleExtent([constants.MAX_ZOOM, constants.MIN_ZOOM]) .on("zoom", function () { myInstance.onZoom(); @@ -277,7 +282,8 @@ angular.module('forceHorse', []) myInstance.onContainerClick() }) .call(myInstance.zoom) - .call(myInstance.zoom.event) // Used in zoomToViewport() + // .call(myInstance.zoom.event) // Used in zoomToViewport() + // Todo: zoom.event replacement? ; // Set wrapper group, to use for pan & zoom transforms @@ -298,6 +304,18 @@ angular.module('forceHorse', []) .attr("fill", constants.DEFAULT_NODE_COLOR) .classed("display_none", !myInstance.config.showLabels); + myInstance.drag = d3.drag() + // .container(myInstance.svg._groups[0][0]) + .on("drag", function (d) { + myInstance.onDrag(d); + }) + .on("start", function (d) { + myInstance.onDragStart(d); + }) + .on("end", function (d) { + myInstance.onDragEnd(d); + }); + return myInstance; }; // initLayout() @@ -344,7 +362,7 @@ angular.module('forceHorse', []) .enter() .append("path") // Set node shape & size - .attr("d", d3.svg.symbol() + .attr("d", d3.symbol() .type(function (d) { return d.shape; }) @@ -411,12 +429,14 @@ angular.module('forceHorse', []) /** * @ngdoc method - * @name forceHorse.factory:ForceHorseFactory#startForceSimulation + * @name forceHorse.factory:ForceHorseFactory#restartForceSimulation * @description Restart the force simulation * @returns {ForceHorseFactory} current instance */ - proto.startForceSimulation = function () { - this.force.start(); + proto.restartForceSimulation = function () { + this.force.alpha(constants.MAX_ALPHA); + this.onForceStart(); + // this.force.restart(); return this; }; @@ -427,7 +447,7 @@ angular.module('forceHorse', []) * @returns {ForceHorseFactory} current instance */ proto.calcFixAspectRatio = function () { - var currentRect = this.svg[0][0].getBoundingClientRect(), + var currentRect = this.svg._groups[0][0].getBoundingClientRect(), currentHeight = currentRect.height, currentWidth = currentRect.width; this.fixAspectRatio = (constants.INNER_SVG_WIDTH / constants.INNER_SVG_HEIGHT) * (currentHeight / currentWidth); @@ -659,10 +679,10 @@ angular.module('forceHorse', []) */ proto.onForceStart = function () { // Prevent simulation when dragging a node - if (this.isDragging) { - this.force.stop(); - return this; - } + // if (this.isDragging) { + // this.force.stop(); + // return this; + // } // Proceed with simulation return this.calcFixAspectRatio() [this.numOfNodes < constants.HEAVY_SIMULATION_NUM_OF_NODES ? @@ -688,14 +708,14 @@ angular.module('forceHorse', []) // Do not accelerate the simulation during dragging, so as not to slow the dragging. ticksPerRender = (myInstance.isDragging ? 1 : myInstance.numOfNodes / 7); calculationStart = performance.now(); - for (let i = 0; i < ticksPerRender && myInstance.force.alpha() > 0; i++) { + for (let i = 0; i < ticksPerRender && myInstance.force.alpha() > myInstance.force.alphaMin(); i++) { myInstance.force.tick(); ticks++; } calculationDuration += (performance.now() - calculationStart); myInstance.updateGraphInDOM().updateProgressBar(); - if (myInstance.force.alpha() > 0) { + if (myInstance.force.alpha() > myInstance.force.alphaMin()) { requestAnimationFrame(render); } else { simulationDuration = performance.now() - simulationStart; @@ -811,7 +831,7 @@ angular.module('forceHorse', []) // Do not update progress bar in fixed nodes mode if (!this.fixedNodesMode) { this.progressBar.attr('x2', - constants.INNER_SVG_WIDTH * (1 - this.force.alpha() / constants.MAX_ALPHA)); + constants.INNER_SVG_WIDTH * (1 - (this.force.alpha() - this.force.alphaMin()) / constants.MAX_ALPHA)); } return this; }; @@ -873,7 +893,8 @@ angular.module('forceHorse', []) } this.svg.transition() .duration(constants.ANIMATION_DURATION) - .call(this.zoom.translate(translate).scale(scale).event); + .call(this.zoom.transform, d3.zoomIdentity.translate(translate[0], translate[1]).scale(scale)); + // .call(this.zoom.translate(translate).scale(scale).event); return this; }; @@ -1088,17 +1109,31 @@ angular.module('forceHorse', []) * @returns {ForceHorseFactory} current instance */ proto.onZoom = function () { - var trans = d3.event.translate, - scale = d3.event.scale; + var trans = d3.event.transform; if (this.inSvgWrapper) { this.inSvgWrapper.attr("transform", - "translate(" + trans + ")" - + " scale(" + scale + ")"); + `translate(${trans.x}, ${trans.y}) scale(${trans.k})`); } return this; }; + /** + * @ngdoc method + * @name forceHorse.factory:ForceHorseFactory#onDragStart + * @description + * Event handler, called when a node-dragging starts + * *UPDATE* I avoid using it, because it is triggered by a normal mouse-down + * @returns {ForceHorseFactory} current instance + */ + proto.onDragStart = function (/*d*/) { + // Fix the dragged node (not moved by the simulation) + // d.fx = d.x; + // d.fy = d.y; + // Start the simulation + return this; + }; + /** * @ngdoc method * @name forceHorse.factory:ForceHorseFactory#onDrag @@ -1108,28 +1143,34 @@ angular.module('forceHorse', []) * @returns {ForceHorseFactory} current instance */ proto.onDrag = function (d) { - // Make the dragged node fixed (not moved by the simulation) - this.elements[constants.NODES].filter(function (nodeData) { - return nodeData.id === d.id; - }).classed("fixed", d.fixed = true); - + // Fix the dragged node (not moved by the simulation) + d.fx = d3.event.x; + d.fy = d3.event.y; if (!this.isDragging) { this.isDragging = true; + this.restartForceSimulation(); } return this; }; - /** - * @ngdoc method - * @name forceHorse.factory:ForceHorseFactory#onDragEnd - * @description - * Event handler, called when a node-dragging ends - * @returns {ForceHorseFactory} current instance - */ - proto.onDragEnd = function () { - this.isDragging = false; - return this; - }; + /** + * @ngdoc method + * @name forceHorse.factory:ForceHorseFactory#onDragEnd + * @description + * Event handler, called when a node-dragging ends + * @returns {ForceHorseFactory} current instance + */ + proto.onDragEnd = function (d) { + this.isDragging = false; + // Cool the simulation + this.force.alpha(this.force.alphaMin()); + // Unfix the dragged node + if (!this.fixedNodesMode) { + d.fx = null; + d.fy = null; + } + return this; + }; /** * @ngdoc method @@ -1143,12 +1184,17 @@ angular.module('forceHorse', []) proto.toggleFixedNodesMode = function () { if (this.fixedNodesMode) { this.elements[constants.NODES].classed('fixed', function (d) { + d.fx = null; + d.fy = null; return d.fixed = false; }); this.fixedNodesMode = false; - this.force.start(); + this.restartForceSimulation(); +// this.force.start(); } else { this.elements[constants.NODES].classed('fixed', function (d) { + d.fx = d.x; + d.fy = d.y; return d.fixed = true; }); this.fixedNodesMode = true; @@ -1193,7 +1239,7 @@ angular.module('forceHorse', []) var myInstance = this; this.config.showNodeWeight = !this.config.showNodeWeight; this.elements[constants.NODES] - .attr("d", d3.svg.symbol() + .attr("d", d3.symbol() .type(function (d) { return d.shape; }) @@ -1422,6 +1468,7 @@ angular.module('forceHorse', []) * If nodeData does not contain an id property, its id is set to its index in the array. * If nodeData does not contain a label property, it gets a default label. * A "class" property (node class) is also added to each nodeData. + * Set node shape * If linkData does not contain an id property, its id is set to its index in the array. * If linkData does not contain an sourceID property, sourceID is set to source. * If linkData does not contain an targetID property, targetID is set to target. @@ -1442,6 +1489,35 @@ angular.module('forceHorse', []) node.label = "" + node.id; } node.class = constants.CLASS_NODE; + + switch (node.shape) { + case undefined: + case "": + case "circle": + node.shape = d3.symbolCircle; + break; + case "cross": + node.shape = d3.symbolCross; + break; + case "diamond": + node.shape = d3.symbolDiamond; + break; + case "square": + node.shape = d3.symbolSquare; + break; + case "triangle-up": + case "triangle-down": + node.shape = d3.symbolTriangle; + break; + case "star": + node.shape = d3.symbolStar; + break; + case "wye": + node.shape = d3.symbolWye; + break; + default: + node.shape = d3.symbolCircle; + } }); // Process edges var edges = (fileData.edges ? fileData.edges : fileData.links); diff --git a/simple-demo-app/index.html b/simple-demo-app/index.html index 77af263..992264b 100644 --- a/simple-demo-app/index.html +++ b/simple-demo-app/index.html @@ -4,7 +4,7 @@ - +