/** * Place here all tests constans. * */ var g_textsSelectAndMove = "Drag objects"; var g_moveCursorForMoving = "Move cursor"; var g_clickToAddVertex = "Click to add vertex"; var g_selectFisrtVertexToConnect = "Select first vertex to connect"; var g_selectSecondVertexToConnect = "Select second vertex to connect"; var g_selectStartVertexForShortPath = "Select start vertex for shortest path"; var g_selectFinishVertexForShortPath = "Select finish vertex for shortest path"; var g_shortestPathResult = "Shortest path is %d"; var g_pathNotExists = "Path does not exists"; var g_selectObjectToDelete = "Select object to delete"; var g_addEdge = "Add edge"; var g_orintEdge = "Orient"; var g_notOrintEdge = "not Orient"; var g_adjacencyMatrixText = "Adjacency Matrix"; var g_save = "Save"; var g_cancel = "Cancel"; var g_shortestDistance = "lowest-distance is "; var g_incidenceMatrixText = "Incidence Matrix"; var g_save_dialog = "Save dialog"; var g_close = "close"; var g_sickConnectedComponent = "Sick connected component is "; var g_connectedComponent = "Connected component is "; var g_what_do_you_think = "What do you think about site?"; var g_name = "Name"; var g_feedback = "Feedback"; var g_send = "Send"; var g_write_to_us = "Write to us"; var g_fixMatrix = "Fix matrix"; var g_readMatrixHelp = "Matrix format help"; var g_matrixWrongFormat = "Matrix is wrong"; var g_save_image_dialog = "Save graph image"; var g_fullReport = "Full report"; var g_shortReport = "Short report"; var g_hasEulerianLoop = "Graph has Eulerian Loop"; var g_hasNotEulerianLoop = "Graph has not Eulerian Loop"; var g_processing = "Processing..."; var g_customEnumVertex = "Custom"; var g_addVertex = "Add vertex"; var g_renameVertex = "Rename vertex"; var g_rename = "Rename"; var g_language = "en"; var g_editWeight = "Edit weight"; var g_noWeight = "No weight"; var g_groupRename = "Group rename"; function loadTexts() { g_textsSelectAndMove = document.getElementById("SelectAndMoveObject").innerHTML; g_moveCursorForMoving = document.getElementById("MoveCursorForMoving").innerHTML; g_clickToAddVertex = document.getElementById("clickToAddVertex").innerHTML; g_selectFisrtVertexToConnect = document.getElementById("selectFisrtVertextToConnect").innerHTML; g_selectSecondVertexToConnect = document.getElementById("selectSecondVertextToConnect").innerHTML; g_selectStartVertexForShortPath = document.getElementById("selectStartShortPathVertex").innerHTML; g_selectFinishVertexForShortPath = document.getElementById("selectFinishShortPathVertex").innerHTML; g_shortestPathResult = document.getElementById("shortPathResult").innerHTML; g_pathNotExists = document.getElementById("pathNotExists").innerHTML; g_selectObjectToDelete = document.getElementById("selectObjectToDelete").innerHTML; g_addEdge = document.getElementById("AddEdge").innerHTML; g_orintEdge = document.getElementById("OrintEdge").innerHTML; g_notOrintEdge = document.getElementById("NotOrintdge").innerHTML; g_adjacencyMatrixText = document.getElementById("AdjacencyMatrixText").innerHTML; g_save = document.getElementById("Save").innerHTML; g_cancel = document.getElementById("Cancel").innerHTML; g_shortestDistance = document.getElementById("shortestDist").innerHTML; g_incidenceMatrixText = document.getElementById("IncidenceMatrixText").innerHTML; g_save_dialog = document.getElementById("saveDialogTitle").innerHTML; g_close = document.getElementById("closeButton").innerHTML; g_sickConnectedComponent = document.getElementById("sickConnectedComponentResult").innerHTML; g_connectedComponent = document.getElementById("connectedComponentResult").innerHTML; g_what_do_you_think = document.getElementById("whatDoYouThink").innerHTML; g_name = document.getElementById("name").innerHTML; g_feedback = document.getElementById("feedback").innerHTML; g_send = document.getElementById("send").innerHTML; g_write_to_us = document.getElementById("writeToUs").innerHTML; g_fixMatrix = document.getElementById("fixMatrixButton").innerHTML; g_readMatrixHelp = document.getElementById("matrixHelp").innerHTML; g_matrixWrongFormat = document.getElementById("wronMatrixTitle").innerHTML; g_save_image_dialog = document.getElementById("saveImageDialogTitle").innerHTML; g_fullReport = document.getElementById("fullReport").innerHTML; g_shortReport = document.getElementById("shortReport").innerHTML; g_hasEulerianLoop = document.getElementById("hasEulerianLoop").innerHTML; g_hasNotEulerianLoop = document.getElementById("hasNotEulerianLoop").innerHTML; g_processing = document.getElementById("processing").innerHTML; g_customEnumVertex = document.getElementById("customEnumVertex").innerHTML; g_addVertex = document.getElementById("addVertexText").innerHTML; g_renameVertex = document.getElementById("renameVertex").innerHTML; g_rename = document.getElementById("renameText").innerHTML; g_language = document.getElementById("currentLanguage").innerHTML; g_editWeight = document.getElementById("editWeight").innerHTML; g_noWeight = document.getElementById("noWeight").innerHTML; g_groupRename = document.getElementById("groupeRenameText").innerHTML; } function Point(x, y){ this.x = x || 0; this.y = y || 0; }; Point.prototype.x = null; Point.prototype.y = null; Point.prototype.add = function(v){ return new Point(this.x + v.x, this.y + v.y); }; Point.prototype.addValue = function(v){ return new Point(this.x + v, this.y + v); }; Point.prototype.clone = function(){ return new Point(this.x, this.y); }; Point.prototype.degreesTo = function(v){ var dx = this.x - v.x; var dy = this.y - v.y; var angle = Math.atan2(dy, dx); // radians return angle * (180 / Math.PI); // degrees }; Point.prototype.distance = function(v){ return Math.sqrt(this.distanceSqr(v)); }; Point.prototype.distanceSqr = function(v){ var x = this.x - v.x; var y = this.y - v.y; return x * x + y * y; }; Point.prototype.equals = function(toCompare){ return this.x == toCompare.x && this.y == toCompare.y; }; Point.prototype.interpolate = function(v, f){ return new Point((this.x + v.x) * f, (this.y + v.y) * f); }; Point.prototype.length = function(){ return Math.sqrt(this.x * this.x + this.y * this.y); }; Point.prototype.normalize = function(thickness){ var l = this.length(); this.x = this.x / l * thickness; this.y = this.y / l * thickness; return new Point(this.x, this.y); }; Point.prototype.normalizeCopy = function(thickness){ var l = this.length(); return new Point(this.x / l * thickness, this.y / l * thickness); }; Point.prototype.orbit = function(origin, arcWidth, arcHeight, degrees){ var radians = degrees * (Math.PI / 180); this.x = origin.x + arcWidth * Math.cos(radians); this.y = origin.y + arcHeight * Math.sin(radians); }; Point.prototype.rotate = function(center, degrees){ var radians = degrees * (Math.PI / 180); offset = this.subtract(center); this.x = offset.x * Math.cos(radians) - offset.y * Math.sin(radians); this.y = offset.x * Math.sin(radians) + offset.y * Math.cos(radians); this.x = this.x + center.x; this.y = this.y + center.y; }; Point.prototype.offset = function(dx, dy){ this.x += dx; this.y += dy; }; Point.prototype.subtract = function(v){ return new Point(this.x - v.x, this.y - v.y); }; Point.prototype.subtractValue = function(value){ return new Point(this.x - value, this.y - value); }; Point.prototype.multiply = function(value){ return new Point(this.x * value, this.y * value); }; Point.prototype.toString = function(){ return "(x=" + this.x + ", y=" + this.y + ")"; }; Point.prototype.normal = function(){ return new Point(-this.y, this.x); }; Point.prototype.min = function(point) { return new Point(Math.min(this.x, point.x), Math.min(this.y, point.y)); }; Point.prototype.max = function(point) { return new Point(Math.max(this.x, point.x), Math.max(this.y, point.y)); }; Point.prototype.inverse = function() { return new Point(-this.x, -this.y); }; Point.interpolate = function(pt1, pt2, f){ return new Point(pt1.x * (1.0 - f) + pt2.x * f, pt1.y * (1.0 - f) + pt2.y * f); }; Point.polar = function(len, angle){ return new Point(len * Math.cos(angle), len * Math.sin(angle)); }; Point.distance = function(pt1, pt2){ var x = pt1.x - pt2.x; var y = pt1.y - pt2.y; return Math.sqrt(x * x + y * y); }; Point.center = function(pt1, pt2){ return new Point((pt1.x + pt2.x) / 2.0, (pt1.y + pt2.y) / 2.0); }; Point.toString = function(){ return x + " " + y; } function Rect(minPoint, maxPoint){ this.minPoint = minPoint; this.maxPoint = maxPoint; }; Rect.prototype.center = function() { return Point.center(this.minPoint, this.maxPoint); }; Rect.prototype.size = function() { return this.maxPoint.subtract(this.minPoint); }; /** * This is edge model * */ function EdgeModel() { this.width = 4; } /** * This is graph model used for hit test and draw. * */ function VertexModel() { this.diameter = 30; } /** * Base node class. * */ function BaseVertex(x, y, vertexEnumType) { this.position = new Point(x, y); this.id = 0; this.mainText = ""; this.upText = ""; this.vertexEnumType = vertexEnumType; }; BaseVertex.prototype.position = new Point(0, 0); BaseVertex.prototype.model = new VertexModel(); BaseVertex.prototype.SaveToXML = function () { return ""; } BaseVertex.prototype.LoadFromXML = function (xml) { this.position = new Point(parseFloat(xml.attr('positionX')), parseFloat(xml.attr('positionY'))); this.id = parseInt(xml.attr('id')); this.mainText = xml.attr('mainText'); this.upText = xml.attr('upText'); } BaseVertex.prototype.SetId = function (id) { this.id = id; this.mainText = this.vertexEnumType.GetVertexText(id); } BaseVertex.prototype.diameterFactor = function () { return 1.0 + (this.mainText.length ? this.mainText.length / 8.0 : 0); } /** * This is base arc. * * */ function BaseEdge(vertex1, vertex2, isDirect, weight, useWeight) { this.vertex1 = vertex1; this.vertex2 = vertex2; this.isDirect = isDirect; this.weight = Number(weight); // For direct graph, has pair edge or not. this.hasPair = false; this.useWeight = useWeight; this.id = 0; } BaseEdge.prototype.model = new EdgeModel(); BaseEdge.prototype.SaveToXML = function () { return ""; } BaseEdge.prototype.LoadFromXML = function (xml, graph) { var attr = xml.attr('vertex1'); this.vertex1 = graph.FindVertex(parseInt(typeof attr !== 'undefined' ? attr : xml.attr('graph1'))); var attr = xml.attr('vertex2'); this.vertex2 = graph.FindVertex(parseInt(typeof attr !== 'undefined' ? attr : xml.attr('graph2'))); this.isDirect = xml.attr('isDirect') == "true"; this.weight = parseFloat(xml.attr('weight')); this.hasPair = xml.attr('hasPair') == "true"; this.useWeight = xml.attr('useWeight') == "true"; this.id = parseInt(xml.attr('id')); } BaseEdge.prototype.GetPixelLength = function () { if (this.vertex1 == this.vertex2) { return (new CommonEdgeStyle()).sizeOfLoop * 2 * Math.PI; } else { return Point.distance(this.vertex1.position, this.vertex2.position); } } /** * Graph drawer. */ // Common style of Graphs. function CommonVertexStyle() { this.lineWidth = 1; this.strokeStyle = '#c7b7c7'; this.fillStyle = '#68aeba'; this.mainTextColor = '#f0d543'; } // Selected style of Graphs. function SelectedVertexStyle0() { CommonVertexStyle.apply(this, arguments); this.strokeStyle = '#f0d543'; this.mainTextColor = '#f0d543'; this.fillStyle = '#c7627a'; } SelectedVertexStyle0.prototype = Object.create(CommonVertexStyle.prototype); function SelectedVertexStyle1() { CommonVertexStyle.apply(this, arguments); this.strokeStyle = '#7a9ba0'; this.mainTextColor = '#7a9ba0'; this.fillStyle = '#534641'; } SelectedVertexStyle1.prototype = Object.create(CommonVertexStyle.prototype); function SelectedVertexStyle2() { CommonVertexStyle.apply(this, arguments); this.strokeStyle = '#8C4C86'; this.mainTextColor = '#8C4C86'; this.fillStyle = '#253267'; } SelectedVertexStyle2.prototype = Object.create(CommonVertexStyle.prototype); function SelectedVertexStyle3() { CommonVertexStyle.apply(this, arguments); this.strokeStyle = '#6188FF'; this.mainTextColor = '#6188FF'; this.fillStyle = '#E97CF9'; } SelectedVertexStyle3.prototype = Object.create(CommonVertexStyle.prototype); function SelectedVertexStyle4() { CommonVertexStyle.apply(this, arguments); this.strokeStyle = '#C6B484'; this.mainTextColor = '#C6B484'; this.fillStyle = '#E0DEE1'; } SelectedVertexStyle4.prototype = Object.create(CommonVertexStyle.prototype); var selectedGraphStyles = [new SelectedVertexStyle0(), new SelectedVertexStyle1(), new SelectedVertexStyle2(), new SelectedVertexStyle3(), new SelectedVertexStyle4()]; function BaseVertexDrawer(context) { this.context = context; } BaseVertexDrawer.prototype.Draw = function(baseGraph, graphStyle) { this.SetupStyle(graphStyle); this.DrawShape(baseGraph); this.context.stroke(); this.context.fill(); this.DrawCenterText(baseGraph.position, baseGraph.mainText, graphStyle.mainTextColor, graphStyle.fillStyle, true, true, 16); // Top text this.DrawCenterText(baseGraph.position.add(new Point(0, - baseGraph.model.diameter / 2.0 - 9.0)), baseGraph.upText, graphStyle.fillStyle, graphStyle.strokeStyle, false, false, 12.0); /* // Bottom text this.DrawCenterText(baseGraph.position.add(new Point(0, + baseGraph.model.diameter / 2.0 + 7.0)), "Text 2", graphStyle.fillStyle, false, 12.0); */ } BaseVertexDrawer.prototype.SetupStyle = function(style) { this.context.lineWidth = style.lineWidth; this.context.strokeStyle = style.strokeStyle; this.context.fillStyle = style.fillStyle; } BaseVertexDrawer.prototype.DrawShape = function(baseGraph) { this.context.lineWidth = 2; this.context.beginPath(); this.context.arc(baseGraph.position.x, baseGraph.position.y, baseGraph.model.diameter / 2.0, 0, 2 * Math.PI); this.context.closePath(); } BaseVertexDrawer.prototype.DrawText = function(position, text, color, outlineColor, outline, font) { this.context.fillStyle = color; this.context.font = font; this.context.lineWidth = 4; this.context.strokeStyle = outlineColor; if (outline) { this.context.save(); this.context.lineJoin = 'round'; this.context.strokeText(text, position.x, position.y); this.context.restore(); } this.context.fillText(text, position.x, position.y); } BaseVertexDrawer.prototype.DrawCenterText = function(position, text, color, outlineColor, bold, outline, size) { this.context.textBaseline="middle"; this.context.font = (bold ? "bold " : "") + size + "px sans-serif"; var textWidth = this.context.measureText(text).width; this.DrawText(new Point(position.x - textWidth / 2, position.y), text, color, outlineColor, outline, this.context.font); } /** * Graph drawer. */ function CommonEdgeStyle() { this.strokeStyle = '#c7b7c7'; this.weightText = '#f0d543'; this.fillStyle = '#68aeba'; this.textPadding = 4; this.textStrockeWidth = 2; this.sizeOfLoop = 24; this.loopShiftAngel = Math.PI / 6; } function SelectedEdgeStyle0() { CommonEdgeStyle.apply(this, arguments); this.strokeStyle = '#f0d543'; this.weightText = '#f0d543'; this.fillStyle = '#c7627a'; } SelectedEdgeStyle0.prototype = Object.create(CommonEdgeStyle.prototype); function ProgressEdgeStyle() { CommonEdgeStyle.apply(this, arguments); var selectedStyle = new SelectedEdgeStyle0(); this.strokeStyle = selectedStyle.fillStyle; this.weightText = '#000000'; this.fillStyle = '#000000'; } ProgressEdgeStyle.prototype = Object.create(CommonEdgeStyle.prototype); function SelectedEdgeStyle1() { CommonEdgeStyle.apply(this, arguments); this.strokeStyle = '#8FBF83'; this.weightText = '#8FBF83'; this.fillStyle = '#F9F9D5'; } SelectedEdgeStyle1.prototype = Object.create(CommonEdgeStyle.prototype); function SelectedEdgeStyle2() { CommonEdgeStyle.apply(this, arguments); this.strokeStyle = '#8C4C86'; this.weightText = '#8C4C86'; this.fillStyle = '#253267'; } SelectedEdgeStyle2.prototype = Object.create(CommonEdgeStyle.prototype); function SelectedEdgeStyle3() { CommonEdgeStyle.apply(this, arguments); this.strokeStyle = '#6188FF'; this.weightText = '#6188FF'; this.fillStyle = '#E97CF9'; } SelectedEdgeStyle3.prototype = Object.create(CommonEdgeStyle.prototype); function SelectedEdgeStyle4() { CommonEdgeStyle.apply(this, arguments); this.strokeStyle = '#C6B484'; this.weightText = '#C6B484'; this.fillStyle = '#E0DEE1'; } SelectedEdgeStyle4.prototype = Object.create(CommonEdgeStyle.prototype); var selectedEdgeStyles = [new SelectedEdgeStyle0(), new SelectedEdgeStyle1(), new SelectedEdgeStyle2(), new SelectedEdgeStyle3(), new SelectedEdgeStyle4()]; function BaseEdgeDrawer(context) { this.context = context; } BaseEdgeDrawer.prototype.Draw = function(baseEdge, arcStyle) { this.SetupStyle(baseEdge, arcStyle); var positions = this.GetArcPositions(baseEdge.vertex1.position, baseEdge.vertex2.position, baseEdge.vertex1.model.diameter); this.DrawArc (positions[0], positions[1], arcStyle); if (baseEdge.useWeight) { this.DrawWeight(positions[0], positions[1], baseEdge.weight, arcStyle, baseEdge.hasPair); } } BaseEdgeDrawer.prototype.SetupStyle = function(baseEdge, arcStyle) { this.context.lineWidth = baseEdge.model.width; this.context.strokeStyle = arcStyle.strokeStyle; this.context.fillStyle = arcStyle.fillStyle; } BaseEdgeDrawer.prototype.DrawArc = function(position1, position2, arcStyle) { if (position1.equals(position2)) { this.context.beginPath(); this.context.arc(position1.x - Math.cos(arcStyle.loopShiftAngel) * arcStyle.sizeOfLoop, position1.y - Math.sin(arcStyle.loopShiftAngel) * arcStyle.sizeOfLoop, arcStyle.sizeOfLoop, 0, 2 * Math.PI); this.context.closePath(); this.context.stroke(); } else { this.context.beginPath(); this.context.moveTo(position1.x, position1.y); this.context.lineTo(position2.x, position2.y); this.context.closePath(); this.context.stroke(); } } BaseEdgeDrawer.prototype.DrawWeight = function(position1, position2, text, arcStyle, hasPair) { var textShift = Math.min(12 / position1.subtract(position2).length(), 0.4); var centerPoint = Point.interpolate(position1, position2, 0.5 + (hasPair ? textShift : 0)); if (position1.equals(position2)) { centerPoint.y = centerPoint.y - Math.cos(arcStyle.loopShiftAngel) * arcStyle.sizeOfLoop * 2; centerPoint.x = centerPoint.x - Math.sin(arcStyle.loopShiftAngel) * arcStyle.sizeOfLoop * 2; } this.context.font = "bold 16px sans-serif"; this.context.textBaseline = "middle"; this.context.lineWidth = arcStyle.textStrockeWidth; this.context.fillStyle = arcStyle.fillStyle; var widthText = this.context.measureText(text).width; this.context.beginPath(); this.context.rect(centerPoint.x - widthText / 2 - arcStyle.textPadding / 2, centerPoint.y - 8 - arcStyle.textPadding / 2, widthText + arcStyle.textPadding, 16 + arcStyle.textPadding); this.context.closePath(); this.context.fill(); this.context.stroke (); this.context.fillStyle = arcStyle.weightText; this.context.fillText(text, centerPoint.x - widthText / 2, centerPoint.y); } BaseEdgeDrawer.prototype.GetArcPositions = function(position1, position2, diameter) { var direction = position1.subtract(position2); direction.normalize(1.0); direction = direction.multiply(0.5); var res = []; res.push(position1.subtract(direction.multiply(diameter))); res.push(position2.subtract(direction.multiply(-diameter))); return res; } BaseEdgeDrawer.prototype.GetArcPositionsShift = function(position1, position2, diameter, shift) { if (shift == 0) { return this.GetArcPositions(position1, position2, diameter); } else { var direction = position1.subtract(position2); direction.normalize(1.0); var normal = direction.normal(); direction = direction.multiply(0.5); position1 = position1.subtract(normal.multiply(shift)); position2 = position2.subtract(normal.multiply(shift)); diameter = Math.sqrt(diameter * diameter - shift * shift); var res = []; res.push(position1.subtract(direction.multiply(diameter))); res.push(position2.subtract(direction.multiply(-diameter))); return res; } } /** * Direct Arc drawer. */ function DirectArcDrawer(context) { this.context = context; } DirectArcDrawer.prototype = Object.create(BaseEdgeDrawer.prototype); DirectArcDrawer.prototype.Draw = function(baseEdge, arcStyle) { baseDrawer = this.CreateBase(); baseDrawer.SetupStyle(baseEdge, arcStyle); var length = baseEdge.model.width * 4; var width = baseEdge.model.width * 2; var position1 = baseEdge.vertex1.position; var position2 = baseEdge.vertex2.position; var direction = position1.subtract(position2); var pairShift = baseEdge.vertex1.model.diameter * 0.25; var realShift = (baseEdge.hasPair ? pairShift : 0); direction.normalize(1.0); var positions = this.GetArcPositionsShift(baseEdge.vertex1.position, baseEdge.vertex2.position, baseEdge.vertex1.model.diameter, realShift); baseDrawer.DrawArc (positions[0], positions[1].subtract(direction.multiply(-length / 2)), arcStyle); this.context.fillStyle = this.context.strokeStyle; this.context.lineWidth = 0; this.DrawArrow(positions[0], positions[1], length, width); if (baseEdge.useWeight) { baseDrawer.DrawWeight(positions[0], positions[1], baseEdge.weight, arcStyle, baseEdge.hasPair); } } DirectArcDrawer.prototype.DrawArrow = function(position1, position2, length, width) { var direction = position2.subtract(position1); direction.normalize(1.0); var normal = direction.normal(); var pointOnLine = position2.subtract(direction.multiply(length)); var point1 = pointOnLine.add(normal.multiply(width)); var point2 = pointOnLine.add(normal.multiply(-width)); this.context.beginPath(); this.context.moveTo(position2.x, position2.y); this.context.lineTo(point1.x, point1.y); this.context.lineTo(point2.x, point2.y); this.context.lineTo(position2.x, position2.y); this.context.closePath(); this.context.fill(); } DirectArcDrawer.prototype.CreateBase = function() { return new BaseEdgeDrawer(this.context); } function ProgressArcDrawer(context, baseDrawer, progress) { this.context = context; this.baseDrawer = baseDrawer; this.progress = progress; } ProgressArcDrawer.prototype = Object.create(BaseEdgeDrawer.prototype); ProgressArcDrawer.prototype.Draw = function(baseEdge, arcStyle) { this.baseDrawer.Draw(baseEdge, arcStyle); this.SetupStyle(baseEdge, new ProgressEdgeStyle()); this.context.lineWidth = 10; var pairShift = baseEdge.vertex1.model.diameter * 0.25; var realShift = (baseEdge.hasPair ? pairShift : 0); var positions = this.GetArcPositionsShift(baseEdge.vertex1.position, baseEdge.vertex2.position, baseEdge.vertex1.model.diameter, realShift); var progressSize = 10; if (positions[0].equals(positions[1])) { var sizeInRadian = progressSize / (2 * Math.PI * arcStyle.sizeOfLoop) * 6; this.context.beginPath(); this.context.arc(positions[0].x - Math.cos(arcStyle.loopShiftAngel) * arcStyle.sizeOfLoop, positions[0].y - Math.sin(arcStyle.loopShiftAngel) * arcStyle.sizeOfLoop, arcStyle.sizeOfLoop, this.progress * 2 * Math.PI, this.progress * 2 * Math.PI + sizeInRadian); this.context.closePath(); this.context.stroke(); } else { var startPosition = Point.interpolate(positions[0], positions[1], this.progress); var vectorOffset = positions[0].subtract(positions[1]).normalizeCopy(progressSize); var finishPosition = startPosition.add(vectorOffset); this.context.beginPath(); this.context.moveTo(startPosition.x, startPosition.y); this.context.lineTo(finishPosition.x, finishPosition.y); this.context.closePath(); this.context.stroke(); } } /** * File for algorithms. * */ // Return list of vertex with connected vertex. function getVertexToVertexArray(graph, ignoryDirection) { res = {}; for (var i = 0; i < graph.edges.length; i ++) { edge = graph.edges[i]; if (!res.hasOwnProperty(edge.vertex1.id)) { res[edge.vertex1.id] = []; } res[edge.vertex1.id].push(edge.vertex2); if (!edge.isDirect || ignoryDirection) { if (!res.hasOwnProperty(edge.vertex2.id)) { res[edge.vertex2.id] = []; } res[edge.vertex2.id].push(edge.vertex1); } } return res; } // Global array of all algorithms. var g_Algorithms = []; var g_AlgorithmIds = []; // Call this function to register your factory algoritm. function RegisterAlgorithm (factory) { g_Algorithms.push(factory); g_AlgorithmIds.push(factory(null).getId()); } // Base algorithm class. function BaseAlgorithm (graph, app) { this.graph = graph; this.app = app; } // @return name of algorthm. For now we supports only 2 locals: "ru" and "en" BaseAlgorithm.prototype.getName = function(local) { return "unknown_name_" + local; } // @return id of algorthm. Please use format: "your id"."algorithm id". Ex. "OlegSh.ConnectedComponent" BaseAlgorithm.prototype.getId = function() { return "unknown.unknown"; } // @return message for user. BaseAlgorithm.prototype.getMessage = function(local) { return "unknown_message_" + local; } // calls when user select vertex. // @return true if you allow to select this object or false. BaseAlgorithm.prototype.selectVertex = function(vertex) { return false; } // calls when user select edge. // @return true if you allow to select this object or false. BaseAlgorithm.prototype.selectEdge = function(edge) { return false; } // user click to workspace. // @return true if you allow to deselect all BaseAlgorithm.prototype.deselectAll = function() { return true; } // get result of algorithm. // If result if not ready, please return null. // It will be called after each user action. // Please return true, if you done. BaseAlgorithm.prototype.result = function(resultCallback) { return null; } // If you no need to get feedback from user, return true. // In this case result will calls once. BaseAlgorithm.prototype.instance = function() { return true; } // @return false, if you change up text and do not want to restore it back. BaseAlgorithm.prototype.needRestoreUpText = function() { return true; } // @return 0, if object is not selected, in other case return groupe of selection. BaseAlgorithm.prototype.getObjectSelectedGroup = function(object) { return 0; } // This methos is called, when messages was updated on html page. BaseAlgorithm.prototype.messageWasChanged = function() {} /** * Default handler. * Select using mouse, drag. * */ function BaseAlgorithmEx(graph, app) { BaseAlgorithm.apply(this, arguments); } // inheritance. BaseAlgorithmEx.prototype = Object.create(BaseAlgorithm.prototype); BaseAlgorithmEx.prototype.CalculateAlgorithm = function(queryString, resultCallback) { var graph = this.graph; var creator = new GraphMLCreater(graph.vertices, graph.edges); var pathObjects = []; var properties = {}; var result = []; $.ajax({ type: "POST", url: "/cgi-bin/GraphCGI.exe?" + queryString, data: creator.GetXMLString(), dataType: "text", }) .done(function( msg ) { console.log(msg); $('#debug').text(msg); xmlDoc = $.parseXML( msg ); var $xml = $( xmlDoc ); $results = $xml.find( "result" ); $results.each(function(){ $values = $(this).find( "value" ); $values.each(function(){ var type = $(this).attr('type'); var value = $(this).text(); var res = {}; res.type = type; res.value = value; result.push(res); }); }); $nodes = $xml.find( "node" ); $nodes.each(function(){ var id = $(this).attr('id'); $data = $(this).find("data"); $data.each(function(){ if ("hightlightNode" == $(this).attr('key') && $(this).text() == "1") { pathObjects.push(graph.FindVertex(id)); } else { if (!properties[id]) { properties[id] = {}; } properties[id][$(this).attr('key')] = $(this).text(); } }); }); $edges = $xml.find( "edge" ); $edges.each(function(){ var source = $(this).attr('source'); var target = $(this).attr('target'); pathObjects.push(graph.FindEdge(source, target)); }); console.log(result); resultCallback(pathObjects, properties, result); }); return true; } BaseAlgorithmEx.prototype.GetNodesPath = function(array, start, count) { var res = []; for (var index = start; index < start + count; index++) { res.push(array[index].value); } return res; } /** * * This event handlers. * * */ /** * Base Handler. * */ function BaseHandler(app) { this.app = app; this.app.setRenderPath([]); } // Need redraw or nor. BaseHandler.prototype.needRedraw = false; BaseHandler.prototype.objects = []; BaseHandler.prototype.message = ""; BaseHandler.prototype.IsNeedRedraw = function(object) { return this.needRedraw; } BaseHandler.prototype.RestRedraw = function(object) { this.needRedraw = false; } BaseHandler.prototype.SetObjects = function(objects) { this.objects = objects; } BaseHandler.prototype.GetSelectedGraph = function(pos) { // Selected Graph. for (var i = 0; i < this.app.graph.vertices.length; i ++) { if (this.app.graph.vertices[i].position.distance(pos) < this.app.graph.vertices[i].model.diameter / 2.0) { return this.app.graph.vertices[i]; } } return null; } BaseHandler.prototype.GetSelectedArc = function(pos) { // Selected Arc. for (var i = 0; i < this.app.graph.edges.length; i ++) { var pos1 = this.app.graph.edges[i].vertex1.position; var pos2 = this.app.graph.edges[i].vertex2.position; var pos0 = new Point(pos.x, pos.y); var r1 = pos0.distance(pos1); var r2 = pos0.distance(pos2); var r12 = pos1.distance(pos2); if (r1 >= (new Point(r2, r12)).length() || r2 >= (new Point(r1,r12)).length()) { } else { var distance = ((pos1.y - pos2.y) * pos0.x + (pos2.x - pos1.x) * pos0.y + (pos1.x * pos2.y - pos2.x * pos1.y)) / r12; if (Math.abs(distance) <= this.app.graph.edges[i].model.width * 1.5) { return this.app.graph.edges[i]; } } } return null; } BaseHandler.prototype.GetSelectedObject = function(pos) { var graphObject = this.GetSelectedGraph(pos); if (graphObject) { return graphObject; } var arcObject = this.GetSelectedArc(pos); if (arcObject) { return arcObject; } return null; } BaseHandler.prototype.GetUpText = function(object) { return ""; } BaseHandler.prototype.GetMessage = function() { return this.message; } BaseHandler.prototype.MouseMove = function(pos) {;} BaseHandler.prototype.MouseDown = function(pos) {;} BaseHandler.prototype.MouseUp = function(pos) {;} BaseHandler.prototype.GetSelectedGroup = function(object) { return 0; } BaseHandler.prototype.InitControls = function() { } BaseHandler.prototype.GetNodesPath = function(array, start, count) { var res = []; for (var index = start; index < start + count; index++) { res.push(this.app.graph.FindVertex(array[index].value)); } return res; } BaseHandler.prototype.RestoreAll = function() { } /** * Default handler. * Select using mouse, drag. * */ function DefaultHandler(app) { BaseHandler.apply(this, arguments); this.message = g_textsSelectAndMove; } // inheritance. DefaultHandler.prototype = Object.create(BaseHandler.prototype); // Current drag object. DefaultHandler.prototype.dragObject = null; // Selected object. DefaultHandler.prototype.selectedObject = null; // Is pressed DefaultHandler.prototype.pressed = false; // Prev position. DefaultHandler.prototype.prevPosition = false; // Is binded event for rename DefaultHandler.prototype.bindedRename = false; // Is binded event for edit edge DefaultHandler.prototype.editEdgeRename = false; DefaultHandler.prototype.MouseMove = function(pos) { if (this.dragObject) { this.dragObject.position.x = pos.x; this.dragObject.position.y = pos.y; this.needRedraw = true; } else if (this.pressed) { this.app.onCanvasMove((new Point(pos.x, pos.y)).subtract(this.prevPosition).multiply(this.app.canvasScale)); this.needRedraw = true; //this.prevPosition = pos; } } DefaultHandler.prototype.MouseDown = function(pos) { this.selectedObject = null; this.dragObject = null; var selectedObject = this.GetSelectedObject(pos); if (selectedObject != null) { this.selectedObject = selectedObject; } if ((selectedObject instanceof BaseVertex) && selectedObject != null) { this.dragObject = selectedObject; this.message = g_moveCursorForMoving; } this.needRedraw = true; this.pressed = true; this.prevPosition = pos; this.app.canvas.style.cursor = "move"; } DefaultHandler.prototype.RenameVertex = function(text) { if (this.selectedObject != null && (this.selectedObject instanceof BaseVertex)) { this.selectedObject.mainText = text; this.app.redrawGraph(); } } DefaultHandler.prototype.MouseUp = function(pos) { this.message = g_textsSelectAndMove; this.dragObject = null; this.pressed = false; this.app.canvas.style.cursor = "auto"; if (this.selectedObject != null && (this.selectedObject instanceof BaseVertex)) { this.message = g_textsSelectAndMove + " "; var handler = this; if (!this.bindedRename) { var callback = function (enumType) { handler.RenameVertex(enumType.GetVertexText(0)); userAction("RenameVertex"); }; $('#message').on('click', '#renameButton', function(){ var customEnum = new TextEnumVertexsCustom(); customEnum.ShowDialog(callback, g_rename, g_renameVertex, handler.selectedObject.mainText); }); this.bindedRename = true; } } else if (this.selectedObject != null && (this.selectedObject instanceof BaseEdge)) { this.message = g_textsSelectAndMove + " "; var handler = this; if (!this.editEdgeRename) { $('#message').on('click', '#editEdge', function(){ var direct = false; var dialogButtons = {}; dialogButtons[g_save] = function() { handler.app.DeleteObject(handler.selectedObject); handler.selectedObject = handler.app.graph.edges[handler.app.CreateNewArc(handler.selectedObject.vertex1, handler.selectedObject.vertex2, handler.selectedObject.isDirect, document.getElementById('EdgeWeight').value)]; handler.needRedraw = true; handler.app.redrawGraph(); userAction("ChangeWeight"); $( this ).dialog( "close" ); }; document.getElementById('EdgeWeight').value = handler.selectedObject.useWeight ? handler.selectedObject.weight : g_noWeight; document.getElementById('EdgeWeightSlider').value = handler.selectedObject.useWeight ? handler.selectedObject.weight : 0; $( "#addEdge" ).dialog({ resizable: false, height: "auto", width: "auto", modal: true, title: g_editWeight, buttons: dialogButtons, dialogClass: 'EdgeDialog', open: function () { $(handler).off('submit').on('submit', function () { return false; }); } }); }); this.editEdgeRename = true; } } } DefaultHandler.prototype.GetSelectedGroup = function(object) { return (object == this.dragObject) || (object == this.selectedObject) ? 1 : 0; } /** * Add Graph handler. * */ function AddGraphHandler(app) { BaseHandler.apply(this, arguments); this.message = g_clickToAddVertex; } // inheritance. AddGraphHandler.prototype = Object.create(BaseHandler.prototype); AddGraphHandler.prototype.MouseDown = function(pos) { this.app.CreateNewGraph(pos.x, pos.y); this.needRedraw = true; this.inited = false; } AddGraphHandler.prototype.InitControls = function() { var enumVertexsText = document.getElementById("enumVertexsText"); if (enumVertexsText) { var enumsList = this.app.GetEnumVertexsList(); for (var i = 0; i < enumsList.length; i ++) { var option = document.createElement('option'); option.text = enumsList[i]["text"]; option.value = enumsList[i]["value"]; enumVertexsText.add(option, i); if (enumsList[i]["select"]) { enumVertexsText.selectedIndex = i; } } var addGraphHandler = this; enumVertexsText.onchange = function () { addGraphHandler.ChangedType(); }; } } AddGraphHandler.prototype.ChangedType = function() { var enumVertexsText = document.getElementById("enumVertexsText"); this.app.SetEnumVertexsType(enumVertexsText.options[enumVertexsText.selectedIndex].value); } /** * Connection Graph handler. * */ function ConnectionGraphHandler(app) { BaseHandler.apply(this, arguments); this.message = g_selectFisrtVertexToConnect; } // inheritance. ConnectionGraphHandler.prototype = Object.create(BaseHandler.prototype); // First selected. ConnectionGraphHandler.prototype.firstObject = null; ConnectionGraphHandler.prototype.AddNewEdge = function(selectedObject, isDirect) { this.app.CreateNewArc(this.firstObject, selectedObject, isDirect, document.getElementById('EdgeWeight').value); this.firstObject = null; this.message = g_selectFisrtVertexToConnect; this.app.NeedRedraw(); } ConnectionGraphHandler.prototype.MouseDown = function(pos) { var selectedObject = this.GetSelectedGraph(pos); if (selectedObject && (selectedObject instanceof BaseVertex)) { if (this.firstObject) { var direct = false; var handler = this; var dialogButtons = {}; dialogButtons[g_orintEdge] = function() { handler.AddNewEdge(selectedObject, true); $( this ).dialog( "close" ); }; dialogButtons[g_notOrintEdge] = function() { handler.AddNewEdge(selectedObject, false); $( this ).dialog( "close" ); }; $( "#addEdge" ).dialog({ resizable: false, height: "auto", width: "auto", modal: true, title: g_addEdge, buttons: dialogButtons, dialogClass: 'EdgeDialog', open: function () { $(this).off('submit').on('submit', function () { return false; }); } }); } else { this.firstObject = selectedObject; this.message = g_selectSecondVertexToConnect; } this.needRedraw = true; } } ConnectionGraphHandler.prototype.GetSelectedGroup = function(object) { return (object == this.firstObject) ? 1 : 0; } /** * Delete Graph handler. * */ function DeleteGraphHandler(app) { BaseHandler.apply(this, arguments); this.message = g_selectObjectToDelete; } // inheritance. DeleteGraphHandler.prototype = Object.create(BaseHandler.prototype); DeleteGraphHandler.prototype.MouseDown = function(pos) { var selectedObject = this.GetSelectedObject(pos); this.app.DeleteObject(selectedObject); this.needRedraw = true; } /** * Delete Graph handler. * */ function DeleteAllHandler(app) { BaseHandler.apply(this, arguments); } // inheritance. DeleteAllHandler.prototype = Object.create(BaseHandler.prototype); DeleteAllHandler.prototype.clear = function() { // Selected Graph. this.app.graph = new Graph(); this.app.savedGraphName = ""; this.needRedraw = true; } /** * Save/Load graph from matrix. * */ function ShowAdjacencyMatrix(app) { BaseHandler.apply(this, arguments); this.message = ""; } // inheritance. ShowAdjacencyMatrix.prototype = Object.create(BaseHandler.prototype); // First selected. ShowAdjacencyMatrix.prototype.firstObject = null; // Path ShowAdjacencyMatrix.prototype.pathObjects = null; ShowAdjacencyMatrix.prototype.show = function() { var handler = this; var dialogButtons = {}; $( "#AdjacencyMatrixField" ).on('keyup change', function (eventObject) { if (!handler.app.TestAdjacencyMatrix($( "#AdjacencyMatrixField" ).val(), [], [])) { $( "#BadMatrixFormatMessage" ).show(); } else { $( "#BadMatrixFormatMessage" ).hide(); } }); dialogButtons[g_save] = function() { handler.app.SetAdjacencyMatrixSmart($( "#AdjacencyMatrixField" ).val()); $( this ).dialog( "close" ); }; dialogButtons[g_cancel] = function() { $( this ).dialog( "close" ); }; $( "#AdjacencyMatrixField" ).val(this.app.GetAdjacencyMatrix()); $( "#BadMatrixFormatMessage" ).hide(); $( "#adjacencyMatrix" ).dialog({ resizable: false, height: "auto", width: "auto", modal: true, title: g_adjacencyMatrixText, buttons: dialogButtons, dialogClass: 'EdgeDialog' }); } /** * Save/Load graph from Incidence matrix. * */ function ShowIncidenceMatrix(app) { BaseHandler.apply(this, arguments); this.message = ""; } // inheritance. ShowIncidenceMatrix.prototype = Object.create(BaseHandler.prototype); // First selected. ShowIncidenceMatrix.prototype.firstObject = null; // Path ShowIncidenceMatrix.prototype.pathObjects = null; ShowIncidenceMatrix.prototype.show = function() { var handler = this; var dialogButtons = {}; $( "#IncidenceMatrixField" ).on('keyup change', function (eventObject) { if (!handler.app.TestIncidenceMatrix($( "#IncidenceMatrixField" ).val(), [], [])) { $( "#BadIncidenceMatrixFormatMessage" ).show(); } else { $( "#BadIncidenceMatrixFormatMessage" ).hide(); } }); dialogButtons[g_save] = function() { handler.app.SetIncidenceMatrixSmart($( "#IncidenceMatrixField" ).val()); $( this ).dialog( "close" ); }; dialogButtons[g_cancel] = function() { $( this ).dialog( "close" ); }; $( "#IncidenceMatrixField" ).val(this.app.GetIncidenceMatrix()); $( "#BadIncidenceMatrixFormatMessage" ).hide(); $( "#incidenceMatrix" ).dialog({ resizable: false, height: "auto", width: "auto", modal: true, title: g_incidenceMatrixText, buttons: dialogButtons, dialogClass: 'EdgeDialog' }); } /** * Save dialog Graph handler. * */ function SavedDialogGraphHandler(app) { BaseHandler.apply(this, arguments); this.message = ""; } // inheritance. SavedDialogGraphHandler.prototype = Object.create(BaseHandler.prototype); // First selected. SavedDialogGraphHandler.prototype.firstObject = null; // Path SavedDialogGraphHandler.prototype.pathObjects = null; // Objects. SavedDialogGraphHandler.prototype.objects = null; SavedDialogGraphHandler.prototype.show = function(object) { this.app.SaveGraphOnDisk(); var dialogButtons = {}; dialogButtons[g_close] = function() { $( this ).dialog( "close" ); }; document.getElementById('GraphName').value = "http://" + window.location.host + window.location.pathname + "?graph=" + this.app.GetGraphName(); document.getElementById('GraphName').select(); document.getElementById("ShareSavedGraph").innerHTML = document.getElementById("ShareSavedGraph").innerHTML.replace(/graph=([A-Za-z]*)/g, "graph=" + this.app.GetGraphName()); $( "#saveDialog" ).dialog({ resizable: false, height: "auto", width: "auto", modal: true, title: g_save_dialog, buttons: dialogButtons, dialogClass: 'EdgeDialog' }); } /** * Save dialog Graph handler. * */ function SavedDialogGraphImageHandler(app) { BaseHandler.apply(this, arguments); this.message = ""; } // inheritance. SavedDialogGraphImageHandler.prototype = Object.create(BaseHandler.prototype); // First selected. SavedDialogGraphImageHandler.prototype.firstObject = null; // Path SavedDialogGraphImageHandler.prototype.pathObjects = null; // Objects. SavedDialogGraphImageHandler.prototype.objects = null; SavedDialogGraphImageHandler.prototype.show = function(object) { var imageName = this.app.SaveGraphImageOnDisk(); var dialogButtons = {}; dialogButtons[g_close] = function() { $( this ).dialog( "close" ); }; var fileLocation = "tmp/saved/" + imageName.substr(0, 2) + "/"+ imageName + ".png" document.getElementById("ShareSavedImageGraph").innerHTML = document.getElementById("ShareSavedImageGraph").innerHTML.replace(/tmp\/saved\/([A-Za-z]*)\/([A-Za-z]*).png/g, fileLocation); document.getElementById("SaveImageLinks").innerHTML = document.getElementById("SaveImageLinks").innerHTML.replace(/tmp\/saved\/([A-Za-z]*)\/([A-Za-z]*).png/g, fileLocation); $( "#saveImageDialog" ).dialog({ resizable: false, height: "auto", width: "auto", modal: true, title: g_save_image_dialog, buttons: dialogButtons, dialogClass: 'EdgeDialog' }); } /** * Algorithm Graph handler. * */ function AlgorithmGraphHandler(app, algorithm) { BaseHandler.apply(this, arguments); this.algorithm = algorithm; this.SaveUpText(); this.UpdateResultAndMesasge(); } // inheritance. AlgorithmGraphHandler.prototype = Object.create(BaseHandler.prototype); // Rest this handler. AlgorithmGraphHandler.prototype.MouseMove = function(pos) {} AlgorithmGraphHandler.prototype.MouseDown = function(pos) { this.app.setRenderPath([]); if (this.algorithm.instance()) { this.app.SetDefaultHandler(); } else { var selectedObject = this.GetSelectedGraph(pos); if (selectedObject && (selectedObject instanceof BaseVertex)) { if (this.algorithm.selectVertex(selectedObject)) { this.needRedraw = true; } this.UpdateResultAndMesasge(); } else if (selectedObject && (selectedObject instanceof BaseEdge)) { if (this.algorithm.selectEdge(selectedObject)) { this.needRedraw = true; } this.UpdateResultAndMesasge(); } else { if (this.algorithm.deselectAll()) { this.needRedraw = true; this.UpdateResultAndMesasge(); } } } } AlgorithmGraphHandler.prototype.MouseUp = function(pos) {} AlgorithmGraphHandler.prototype.GetSelectedGroup = function(object) { return this.algorithm.getObjectSelectedGroup(object); } AlgorithmGraphHandler.prototype.RestoreAll = function() { this.app.setRenderPath([]); if (this.algorithm.needRestoreUpText()) { this.RestoreUpText(); } } AlgorithmGraphHandler.prototype.SaveUpText = function() { this.vertexUpText = {}; var graph = this.app.graph; for (i = 0; i < graph.vertices.length; i ++) { this.vertexUpText[graph.vertices[i].id] = graph.vertices[i].upText; } } AlgorithmGraphHandler.prototype.RestoreUpText = function() { var graph = this.app.graph; for (i = 0; i < graph.vertices.length; i ++) { if (graph.vertices[i].id in this.vertexUpText) { graph.vertices[i].upText = this.vertexUpText[graph.vertices[i].id]; } } } AlgorithmGraphHandler.prototype.UpdateResultAndMesasge = function() { var self = this; result = this.algorithm.result(function (result) { self.message = self.algorithm.getMessage(g_language); self.app.resultCallback(result); }); this.app.resultCallback(result); this.message = this.algorithm.getMessage(g_language); } AlgorithmGraphHandler.prototype.InitControls = function() { this.algorithm.messageWasChanged(); } /** * Groupe rename vertices. * */ function GroupRenameVertices(app) { BaseHandler.apply(this, arguments); this.message = ""; } // inheritance. GroupRenameVertices.prototype = Object.create(BaseHandler.prototype); // First selected. GroupRenameVertices.prototype.firstObject = null; // Path GroupRenameVertices.prototype.pathObjects = null; GroupRenameVertices.prototype.show = function() { var handler = this; var dialogButtons = {}; var graph = this.app.graph; var app = this.app; dialogButtons[g_save] = function() { var titlesList = $( "#VertextTitleList" ).val().split('\n'); for (i = 0; i < Math.min(graph.vertices.length, titlesList.length); i ++) { graph.vertices[i].mainText = titlesList[i]; } app.redrawGraph(); $( this ).dialog( "close" ); }; dialogButtons[g_cancel] = function() { $( this ).dialog( "close" ); }; var titleList = ""; for (i = 0; i < graph.vertices.length; i ++) { titleList = titleList + graph.vertices[i].mainText + "\n"; } $( "#VertextTitleList" ).val(titleList); $( "#GroupRenameDialog" ).dialog({ resizable: false, height: "auto", width: "auto", modal: true, title: g_groupRename, buttons: dialogButtons, dialogClass: 'EdgeDialog' }); } /** * This class creates GraphML xml. * */ function GraphMLCreater(nodes, arcs) { this.nodes = nodes; this.arcs = arcs; } GraphMLCreater.prototype.GetXMLString = function() { var mainHeader = ""; var directedHeader = ""; var undirectedHeader = ""; var defaultWeight = 1.0; var weightKeyId = "\"d0\""; var weightNode = "" + "" + defaultWeight + "" + ""; var xmlBoby = ""; for (var i = 0; i < this.nodes.length; i++) { xmlBoby = xmlBoby + ""; } var hasDirected = false; for (var i = 0; i < this.arcs.length; i++) { if (this.arcs[i].isDirect) { hasDirected = true; break; } } for (var i = 0; i < this.arcs.length; i++) { var weightData = ""; if (this.arcs[i].weight != defaultWeight) { weightData = ""+ this.arcs[i].weight + ""; } xmlBoby = xmlBoby + "" + weightData + "" : "/>") } xml = mainHeader + weightNode + (hasDirected ? directedHeader : undirectedHeader) + xmlBoby + "" return xml; } /** * Graph class. * */ function Graph() { // List of vertex. this.vertices = []; // List of arcs. this.edges = []; // Unique Id of new graph. this.uidGraph = 0; // Unique Id of new edge. this.uidEdge = 10000; // Has direction edge. this.hasDirect = false; }; Graph.prototype.AddNewVertex = function(vertex) { if (this.vertices.length < 300) { vertex.SetId (this.uidGraph); this.uidGraph = this.uidGraph + 1; this.vertices.push(vertex); } return this.vertices.length - 1; } Graph.prototype.AddNewEdgeSafe = function(graph1, graph2, isDirect, weight) { var useWeight = false; if (!isNaN(parseInt(weight, 10))) { useWeight = true; } weight = (!isNaN(parseInt(weight, 10)) && weight >= 0) ? weight : 1; return this.AddNewEdge(new BaseEdge(graph1, graph2, isDirect, weight, useWeight, 0)); } Graph.prototype.AddNewEdge = function(edge) { edge.id = this.uidEdge; this.uidEdge = this.uidEdge + 1; var edge1 = this.FindEdge(edge.vertex1.id, edge.vertex2.id); var edgeRevert = this.FindEdge(edge.vertex2.id, edge.vertex1.id); if (!edge.isDirect) { if (edge1 != null) { this.DeleteEdge(edge1); } if (edgeRevert != null) { this.DeleteEdge(edgeRevert); } this.edges.push(edge); } else { if (edge1 != null) { this.DeleteEdge(edge1); } if (edgeRevert != null && !edgeRevert.isDirect) { this.DeleteEdge(edgeRevert); } else if (edgeRevert != null) { edgeRevert.hasPair = true; edge.hasPair = true; } this.edges.push(edge); } return this.edges.length - 1; } Graph.prototype.DeleteEdge = function(edgeObject) { var index = this.edges.indexOf(edgeObject); if (index > -1) { var edgeRevert = this.FindEdge(edgeObject.vertex2, edgeObject.vertex1); if (edgeRevert != null && edgeRevert.isDirect) { edgeRevert.isPair = false; } this.edges.splice(index, 1); } } Graph.prototype.DeleteVertex = function(vertexObject) { var index = this.vertices.indexOf(vertexObject); if (index > -1) { for (var i = 0; i < this.edges.length; i++) { if (this.edges[i].vertex1 == vertexObject || this.edges[i].vertex2 == vertexObject) { this.DeleteEdge(this.edges[i]); i--; } } this.vertices.splice(index, 1); } } Graph.prototype.FindVertex = function(id) { var res = null; for (var i = 0; i < this.vertices.length; i++) { if (this.vertices[i].id == id) { res = this.vertices[i]; break; } } return res; } Graph.prototype.FindEdge = function(id1, id2) { var res = null; for (var i = 0; i < this.edges.length; i++) { if ((this.edges[i].vertex1.id == id1 && this.edges[i].vertex2.id == id2) || (!this.edges[i].isDirect && this.edges[i].vertex1.id == id2 && this.edges[i].vertex2.id == id1)) { res = this.edges[i]; break; } } return res; } Graph.prototype.GetAdjacencyMatrix = function () { var matrix = ""; for (var i = 0; i < this.vertices.length; i++) { for (var j = 0; j < this.vertices.length; j++) { var edge = this.FindEdge (this.vertices[i].id, this.vertices[j].id); if (edge != null) { matrix += edge.weight; } else { matrix += "0"; } if (j != this.vertices.length) { matrix += ", "; } } matrix = matrix + "\n"; } return matrix; } Graph.prototype.TestAdjacencyMatrix = function (matrix, rowsObj, colsObj, separator = ",") { var bGoodFormat = true; rowsObj.rows = []; rowsObj.rows = matrix.split ("\n"); for (j = 0; j < rowsObj.rows.length; ++j) { //rowsObj.rows[j] = rowsObj.rows[j].replace(/ /g,''); if (rowsObj.rows[j] === "") { rowsObj.rows.splice(j--, 1); } } colsObj.cols = []; for (var i = 0; i < rowsObj.rows.length; i++) { colsObj.cols[i] = this.SplitMatrixString(rowsObj.rows[i], separator);//rowsObj.rows[i].split (","); for (j = 0; j < colsObj.cols[i].length; ++j) { if (colsObj.cols[i][j] === "") { colsObj.cols[i].splice(j--, 1); } } if (colsObj.cols[i].length != rowsObj.rows.length) { bGoodFormat = false; break; } } return bGoodFormat; } Graph.prototype.IsVertexesHasSamePosition = function (position, vertexCount) { var res = false; for (var j = 0; j < Math.min(this.vertices.length, vertexCount); j++) { if (position.distance(this.vertices[j].position) < this.vertices[j].model.diameter * 2) { res = true; break; } } return res; } Graph.prototype.GetRandomPositionOfVertex = function (matrix, vertexIndex, viewportSize) { var point = new Point(0, 0); var relatedVertex = []; for (var j = 0; j < matrix.length; j++) { if (j < this.vertices.length && (cols[vertexIndex][j] > 0 || cols[j][vertexIndex] > 0) && j != vertexIndex) { relatedVertex.push(this.vertices[j]); } } var diameter = (new VertexModel()).diameter; if (relatedVertex.length > 1) { for (var j = 0; j < relatedVertex.length; j++) { point = point.add(relatedVertex[j].position); } point = point.multiply(1 / relatedVertex.length); point.offset (Math.random() * diameter + (Math.random() ? -1 : 1) * 2 * diameter, Math.random() * diameter + (Math.random() ? -1 : 1) * 2 * diameter); } else { point = new Point(Math.random() * viewportSize.x, Math.random() * viewportSize.y); } if (this.IsVertexesHasSamePosition (point, matrix.length)) { point.offset (Math.random() * diameter + + (Math.random() ? -1 : 1) * 4 * diameter, Math.random() * diameter + + (Math.random() ? -1 : 1) * 4 * diameter); } // Clamp point.x = Math.min(Math.max(point.x, diameter), viewportSize.x); point.y = Math.min(Math.max(point.y, diameter), viewportSize.y); return point; } Graph.prototype.VertexesReposition = function (viewportSize, newVertexes) { var maxGravityDistanceSqr = Math.max(viewportSize.x, viewportSize.y) / 5.0; maxGravityDistanceSqr = maxGravityDistanceSqr * maxGravityDistanceSqr; //Math.min(viewportSize.x, viewportSize.y) / 2.0; var velocityDamping = 0.85; var diameter = (new VertexModel()).diameter; var maxDistance = diameter * 3; var gravityDistanceSqr = 10 * (maxDistance * maxDistance); var edgeGravityKof = 10 / (maxDistance); var kCenterForce = 10 / (maxDistance * 10); var centerPoint = viewportSize.multiply(0.5); var velocityMax = maxDistance * 10; var edgesMatrix = {}; for (var i = 0; i < this.edges.length; i++) { edgesMatrix[this.edges[i].vertex1.id + this.edges[i].vertex2.id * 1000] = 1; edgesMatrix[this.edges[i].vertex2.id + this.edges[i].vertex1.id * 1000] = 1; } var startAngel = Math.random() * 180.0; for(i = 0; i < newVertexes.length; i++) // loop through vertices { newVertexes[i].position.orbit(new Point(viewportSize.x / 2, viewportSize.y / 2), (viewportSize.x - diameter * 2) / 2, (viewportSize.y - diameter * 2) / 2, 360 * i / newVertexes.length + startAngel); } var k = 0; var bChanged = true; while (k < 1000 && bChanged) { var vertexData = []; for(i = 0; i < newVertexes.length; i++) // loop through vertices { // Has no in newVertexes. var currentVertex = {}; currentVertex.object = newVertexes[i]; currentVertex.net_force = new Point (0, 0); currentVertex.velocity = new Point (0, 0); vertexData.push(currentVertex); for(j = 0; j < this.vertices.length; j++) // loop through other vertices { otherVertex = this.vertices[j]; if (otherVertex == currentVertex.object) continue; // squared distance between "u" and "v" in 2D space var rsq = currentVertex.object.position.distanceSqr(otherVertex.position); { // counting the repulsion between two vertices var force = (currentVertex.object.position.subtract(otherVertex.position)).normalize(gravityDistanceSqr / rsq); currentVertex.net_force = currentVertex.net_force.add(force); } } for(j = 0; j < this.vertices.length; j++) // loop through edges { otherVertex = this.vertices[j]; if (edgesMatrix.hasOwnProperty(currentVertex.object.id + 1000 * otherVertex.id)) { var distance = currentVertex.object.position.distance(otherVertex.position); if (distance > maxDistance) { // countin the attraction var force = (otherVertex.position.subtract(currentVertex.object.position)).normalize(edgeGravityKof * (distance - maxDistance)); currentVertex.net_force = currentVertex.net_force.add(force); } } } // Calculate force to center of world. var distanceToCenter = centerPoint.distance(currentVertex.object.position); var force = centerPoint.subtract(currentVertex.object.position).normalize(distanceToCenter * kCenterForce); currentVertex.net_force = currentVertex.net_force.add(force); // counting the velocity (with damping 0.85) currentVertex.velocity = currentVertex.velocity.add(currentVertex.net_force); } bChanged = false; for(i = 0; i < vertexData.length; i++) // set new positions { var v = vertexData[i]; var velocity = v.velocity; if (velocity.length() > velocityMax) { velocity = velocity.normalize(velocityMax); } v.object.position = v.object.position.add(velocity); if (velocity.length() >= 1) { bChanged = true; } } k++; } // Looks like somthing going wrong and will use circle algorithm for reposition. var bbox = this.getGraphBBox(); if (bbox.size().length() > viewportSize.length() * 1000) { for(i = 0; i < newVertexes.length; i++) // loop through vertices { newVertexes[i].position.orbit(new Point(viewportSize.x / 2, viewportSize.y / 2), (viewportSize.x - diameter * 2) / 2, (viewportSize.y - diameter * 2) / 2, 360 * i / newVertexes.length + startAngel); } } else { // Try to rotate graph to fill small area. var count = 10; var agnle = 360.0 / count; var viewportAspect = viewportSize.x / viewportSize.y; var bestIndex = 0; var graphSize = bbox.size(); var bestAspect = graphSize.x / graphSize.y; var center = bbox.center(); for (var i = 1; i < count; i++) { for(j = 0; j < newVertexes.length; j++) // loop through vertices { newVertexes[j].position.rotate(center, agnle); } var newBBox = this.getGraphBBox(); var newAspect = newBBox.size().x / newBBox.size().y; if (Math.abs(newAspect - viewportAspect) < Math.abs(bestAspect - viewportAspect)) { bestAspect = newAspect; bestIndex = i; } } // Rotate to best aspect. for(j = 0; j < newVertexes.length; j++) // loop through vertices { newVertexes[j].position.rotate(center, - agnle * (count - bestIndex - 1)); } } } Graph.prototype.SetAdjacencyMatrix = function (matrix, viewportSize, currentEnumVertesType, separator = ",") { var rowsObj = {}; var colsObj = {}; //ViewportSize = viewportSize.subtract(new Point((new VertexModel()).diameter * 2, (new VertexModel()).diameter * 2)); if (this.TestAdjacencyMatrix(matrix, rowsObj, colsObj, separator)) { rows = rowsObj.rows; cols = colsObj.cols; for (var i = 0; i < this.edges.length; i++) { this.DeleteEdge (this.edges[i]); } var newVertexes = []; var bWeightGraph = false; for (var i = 0; i < rows.length; i++) { for (var j = 0; j < rows.length; j++) { if (j >= this.vertices.length) { var newPos = this.GetRandomPositionOfVertex (matrix, j, viewportSize); newVertexes.push(new BaseVertex(newPos.x, newPos.y, currentEnumVertesType)); this.AddNewVertex(newVertexes[newVertexes.length - 1]); } if (cols[i][j] > 0) { var nEdgeIndex = this.AddNewEdgeSafe(this.vertices[i], this.vertices[j], cols[i][j] != cols[j][i], cols[i][j]); if (nEdgeIndex >= 0) { bWeightGraph = bWeightGraph || this.edges[nEdgeIndex].weight != 1; } } } } // Set use weight false, because we have unwieghts graph. if (!bWeightGraph) { this.edges.forEach(function(part, index, theArray) { theArray[index].useWeight = false; }); } for (var i = rows.length; i < Math.max(this.vertices.length, rows.length); i++) { this.DeleteVertex(this.vertices[i]); i--; } this.VertexesReposition(viewportSize, newVertexes); } } Graph.prototype.TestIncidenceMatrix = function (matrix, rowsObj, colsObj, separator = ",") { var bGoodFormat = true; rowsObj.rows = []; rowsObj.rows = matrix.split ("\n"); for (j = 0; j < rowsObj.rows.length; ++j) { if (rowsObj.rows[j] === "") { rowsObj.rows.splice(j--, 1); } } colsObj.cols = []; var columnCount = 0; for (var i = 0; i < rowsObj.rows.length; i++) { colsObj.cols[i] = this.SplitMatrixString(rowsObj.rows[i], separator);//rowsObj.rows[i].split (","); for (j = 0; j < colsObj.cols[i].length; ++j) { if (colsObj.cols[i][j] === "") { colsObj.cols[i].splice(j--, 1); } } if (i == 0) { columnCount = colsObj.cols[i].length; } if (colsObj.cols[i].length != columnCount) { bGoodFormat = false; break; } } if (bGoodFormat) { for (var i = 0; i < colsObj.cols[0].length; i++) { var values = []; for (j = 0; j < colsObj.cols.length; ++j) { if (colsObj.cols[j][i] != 0) { values.push(colsObj.cols[j][i]); } } if (!(values.length <= 1 || (values.length == 2 && (values[0] == values[1] || values[0] == -values[1])))) { bGoodFormat = false; break; } } } return bGoodFormat; } Graph.prototype.SetIncidenceMatrix = function (matrix, viewportSize, currentEnumVertesType) { var rowsObj = {}; var colsObj = {}; //ViewportSize = viewportSize.subtract(new Point((new VertexModel()).diameter * 2, (new VertexModel()).diameter * 2)); if (this.TestIncidenceMatrix(matrix, rowsObj, colsObj)) { rows = rowsObj.rows; cols = colsObj.cols; for (var i = 0; i < this.edges.length; i++) { this.DeleteEdge (this.edges[i]); } var newVertexes = []; var bWeightGraph = false; for (var i = 0; i < cols[0].length; i++) { var edgeValue = []; var edgeIndex = []; for (var j = 0; j < cols.length; j++) { if (j >= this.vertices.length) { var newPos = new Point(0, 0);//this.GetRandomPositionOfVertex (matrix, j, viewportSize); newVertexes.push(new BaseVertex(newPos.x, newPos.y, currentEnumVertesType)); this.AddNewVertex(newVertexes[newVertexes.length - 1]); } if (cols[j][i] != 0) { edgeValue.push(cols[j][i]); edgeIndex.push(j); } } if (edgeIndex.length == 1) { edgeValue.push(edgeValue[0]); edgeIndex.push(edgeIndex[0]); } if (edgeIndex.length == 2) { if (edgeValue[0] != edgeValue[1]) { if (edgeValue[1] > 0) { edgeValue = edgeValue.swap(0, 1); edgeIndex = edgeIndex.swap(0, 1); } } var nEdgeIndex = this.AddNewEdgeSafe(this.vertices[edgeIndex[0]], this.vertices[edgeIndex[1]], edgeValue[0] != edgeValue[1], Math.abs(edgeValue[1])); if (nEdgeIndex >= 0) { bWeightGraph = bWeightGraph || this.edges[nEdgeIndex].weight != 1; } } } // Set use weight false, because we have unwieghts graph. if (!bWeightGraph) { this.edges.forEach(function(part, index, theArray) { theArray[index].useWeight = false; }); } for (var i = cols.length; i < Math.max(this.vertices.length, cols.length); i++) { this.DeleteVertex(this.vertices[i]); i--; } this.VertexesReposition(viewportSize, newVertexes); } } Graph.prototype.GetIncidenceMatrix = function () { var matrix = ""; for (var i = 0; i < this.vertices.length; i++) { for (var j = 0; j < this.edges.length; j++) { if (this.edges[j].vertex1 == this.vertices[i]) { matrix += this.edges[j].weight; } else if (this.edges[j].vertex2 == this.vertices[i] && !this.edges[j].isDirect) { matrix += this.edges[j].weight; } else if (this.edges[j].vertex2 == this.vertices[i] && this.edges[j].isDirect) { matrix += -this.edges[j].weight; } else { matrix += "0"; } if (j != this.edges.length - 1) { matrix += ", "; } } matrix = matrix + "\n"; } return matrix; } Graph.prototype.SplitMatrixString = function (line, separator = ",") { var res = []; var i = 0; // For case: 00110101101 var isZeroOneLine = true; for (i = 0; i < line.length; i++) { if (line.charAt(i) != '0' && line.charAt(i) != '1') { isZeroOneLine = false; break; } } if (!isZeroOneLine) { if (separator != ",") { line = line.replace(/,/g, "."); } for (i = 0; i < line.length; i++) { // add , if we use space as separator if (("0123456789.-e").indexOf(line.charAt(i)) < 0 ) { if (i > 0) { res.push(line.substr(0, i)); } if (i == 0) { i = 1; } line = line.substr(i, line.length - i); i = -1; } } if (line.length > 0) { res.push(line); } } else { for (i = 0; i < line.length; i++) { res.push(line.charAt(i)); } } console.log(res); return res; } Graph.prototype.SaveToXML = function () { var mainHeader = ""; var header = ""; var xmlBoby = ""; for (var i = 0; i < this.vertices.length; i++) { xmlBoby = xmlBoby + this.vertices[i].SaveToXML(); } xmlBoby = xmlBoby + ""; for (var i = 0; i < this.edges.length; i++) { xmlBoby = xmlBoby + this.edges[i].SaveToXML(); } xmlBoby = xmlBoby + ""; return mainHeader + header + xmlBoby + ""; } Graph.prototype.LoadFromXML = function (xmlText) { xmlDoc = $.parseXML( xmlText ); var $xml = $( xmlDoc ); $graphs = $xml.find( "graph" ); var loadedGraphId = 0; var loadedEdgeId = 0; $graphs.each(function(){ loadedGraphId = parseInt($(this).attr('uidGraph')); loadedEdgeId = parseInt($(this).attr('uidEdge')); }); // Back comportebility. if (isNaN(loadedEdgeId)) { loadedEdgeId = 10000; } this.uidGraph = loadedGraphId; this.uidEdge = loadedEdgeId; $nodes = $xml.find( "node" ); var vertexs = []; $nodes.each(function(){ var vertex = new BaseVertex(); vertex.LoadFromXML($(this)); vertexs.push(vertex); }); this.vertices = vertexs; $edges = $xml.find( "edge" ); var edges = []; var graph = this; $edges.each(function(){ var edge = new BaseEdge(); edge.LoadFromXML($(this), graph); edges.push(edge); }); this.edges = edges; } Graph.prototype.hasDirectEdge = function () { var res = false; for (var i = 0; i < this.edges.length; i++) { if(this.edges[i].isDirect) { res = true; break; } } return res; } Graph.prototype.clampPositions = function (viewportSize) { var diameter = (new VertexModel()).diameter; for(i = 0; i < this.vertices.length; i++) // set new positions { this.vertices[i].position.x = Math.min(Math.max(this.vertices[i].position.x, diameter), viewportSize.x - diameter); this.vertices[i].position.y = Math.min(Math.max(this.vertices[i].position.y, diameter), viewportSize.y - diameter); } } // Use to setup scaling. Graph.prototype.getGraphBBox = function (viewportSize) { var pointMin = new Point(1e5, 1e5); var pointMax = new Point(-1e5, -1e5); var diameter = (new VertexModel()).diameter; for(i = 0; i < this.vertices.length; i++) { var vertex = this.vertices[i]; var deltaVector = new Point(vertex.diameterFactor() * diameter, diameter); pointMin = pointMin.min(vertex.position.subtract(deltaVector)); pointMax = pointMax.max(vertex.position.add(deltaVector)); } return new Rect(pointMin, pointMax); } /* Classes for create text for vertexs. */ /** * Base Enum Vertexs. * */ function BaseEnumVertices(app) { this.app = app; } BaseEnumVertices.prototype.GetVertexText = function(id) { return id; } BaseEnumVertices.prototype.GetVertexTextAsync = function(callback) { callback (this); } BaseEnumVertices.prototype.GetText = function() { return "1, 2, 3..."; } BaseEnumVertices.prototype.GetValue = function() { return "Numbers"; } function TextEnumTitle(app, title) { BaseEnumVertices.apply(this, arguments); this.pattern = ""; this.title = title; } // inheritance. TextEnumTitle.prototype = Object.create(BaseEnumVertices.prototype); TextEnumTitle.prototype.GetVertexText = function(id) { return this.title; } /** * Text Enum * */ function TextEnumVertexs(app) { BaseEnumVertices.apply(this, arguments); this.pattern = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; } // inheritance. TextEnumVertexs.prototype = Object.create(BaseEnumVertices.prototype); TextEnumVertexs.prototype.GetVertexText = function(id) { var res = ""; res = this.pattern[id % this.pattern.length] + res; while (id >= this.pattern.length) { id = Math.floor(id / this.pattern.length) - 1; res = this.pattern[id % this.pattern.length] + res; } return res; } TextEnumVertexs.prototype.GetText = function() { return "A, B, ... Z"; } TextEnumVertexs.prototype.GetValue = function() { return "Latin"; } /** * Text Enum * */ function TextEnumVertexsCyr(app) { TextEnumVertexs.apply(this, arguments); this.pattern = "АБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ"; } // inheritance. TextEnumVertexsCyr.prototype = Object.create(TextEnumVertexs.prototype); TextEnumVertexsCyr.prototype.GetText = function() { return "А, Б, ... Я"; } TextEnumVertexsCyr.prototype.GetValue = function() { return "Cyrillic"; } /** * Text Enum * */ function TextEnumVertexsGreek(app) { TextEnumVertexs.apply(this, arguments); this.pattern = "ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩ"; } // inheritance. TextEnumVertexsGreek.prototype = Object.create(TextEnumVertexs.prototype); TextEnumVertexsGreek.prototype.GetText = function() { return "Α, Β, ... Ω"; } TextEnumVertexsGreek.prototype.GetValue = function() { return "Greek"; } /** * Text Enum * */ function TextEnumVertexsCustom(app) { BaseEnumVertices.apply(this, arguments); this.pattern = ""; } // inheritance. TextEnumVertexsCustom.prototype = Object.create(BaseEnumVertices.prototype); TextEnumVertexsCustom.prototype.GetText = function() { return g_customEnumVertex; } TextEnumVertexsCustom.prototype.GetValue = function() { return "Custom"; } TextEnumVertexsCustom.prototype.GetVertexTextAsync = function(callback) { this.ShowDialog(callback, g_addVertex, g_addVertex, "A"); } TextEnumVertexsCustom.prototype.ShowDialog = function(callback, buttonText, titleTitle, title) { var dialogButtons = {}; app = this.app; dialogButtons[buttonText] = function() { callback(new TextEnumTitle(app, $("#VertexTitle").val())); $( this ).dialog( "close" ); }; $( "#addVertex" ).dialog({ resizable: false, height: "auto", width: "auto", modal: true, title: titleTitle, buttons: dialogButtons, dialogClass: 'EdgeDialog', open: function () { $(this).off('submit').on('submit', function () { return false; }); $("#VertexTitle").val(title); $("#VertexTitle").focus(); } }); } /** * This is main application class. * */ var globalApplication = null; function Application(document, window) { this.document = document; this.canvas = this.document.getElementById('canvas'); this.handler = new AddGraphHandler(this); this.savedGraphName = ""; this.currentEnumVertesType = new BaseEnumVertices(this);//this.enumVertexesTextList[0]; this.findPathReport = 1; this.isTimerRender = false; globalApplication = this; this.renderPath = []; this.renderTimer = 0; this.renderPathLength = 0; this.renderPathCounter = 0; this.renderPathLoops = 0; this.enumVertexesTextList = [new BaseEnumVertices(this), new TextEnumVertexs(this), new TextEnumVertexsCyr(this), new TextEnumVertexsGreek(this), new TextEnumVertexsCustom(this)]; this.SetDefaultTransformations(); this.algorithmsValues = {}; this.userAction = function(){}; }; // List of graph. //Application.prototype.graph.vertices = []; // Current draged object. Application.prototype.graph = new Graph(); Application.prototype.dragObject = -1; // List of graph.edges. //Application.prototype.graph.edges = []; // User handler. Application.prototype.handler = null; // Hold status. Application.prototype.status = {}; // Graph name length Application.prototype.graphNameLength = 16; Application.prototype.getMousePos = function(canvas, e) { /// getBoundingClientRect is supported in most browsers and gives you /// the absolute geometry of an element var rect = canvas.getBoundingClientRect(); /// as mouse event coords are relative to document you need to /// subtract the element's left and top position: return {x: (e.clientX - rect.left) / this.canvasScale - this.canvasPosition.x, y: (e.clientY - rect.top) / this.canvasScale - this.canvasPosition.y}; } Application.prototype.redrawGraph = function() { if (!this.isTimerRender) { this._redrawGraph(); } } Application.prototype.redrawGraphTimer = function() { if (this.isTimerRender) { var context = this._redrawGraph(); // Render path if (this.renderPath.length > 1) { context.save(); context.scale(this.canvasScale, this.canvasScale); context.translate(this.canvasPosition.x, this.canvasPosition.y); var movePixelStep = 16; var currentLength = 0; var i = 0 for (i = 0; i < this.renderPath.length - 1; i++) { var edge = this.graph.FindEdge(this.renderPath[i], this.renderPath[i + 1]); currentLength += edge.GetPixelLength(); if (currentLength > this.renderPathCounter) { currentLength -= edge.GetPixelLength(); break; } } if (i >= this.renderPath.length - 1) { i = 0; this.renderPathCounter = 0; currentLength = 0; this.renderPathLoops += 1; } var edge = this.graph.FindEdge(this.renderPath[i], this.renderPath[i + 1]); var progress = (this.renderPathCounter - currentLength) / edge.GetPixelLength(); this.RedrawEdgeProgress(context, edge, edge.vertex1.id == this.renderPath[i] ? progress : 1.0 - progress); this.renderPathCounter += movePixelStep; context.restore(); } } if (this.renderPathLoops >= 5) { this.stopRenderTimer(); } } Application.prototype._redrawGraph = function() { var context = this.canvas.getContext('2d'); context.save(); context.clearRect(0, 0, Math.max(this.canvas.width, this.GetRealWidth()), Math.max(this.canvas.height, this.GetRealHeight())); context.scale(this.canvasScale, this.canvasScale); context.translate(this.canvasPosition.x, this.canvasPosition.y); this.RedrawEdges(context); this.RedrawNodes(context); context.restore(); return context; } Application.prototype.updateRenderPathLength = function() { this.renderPathLength = 0; this.renderPathCounter = 0; if (this.renderPath.length > 1) { for (var i = 0; i < this.renderPath.length - 1; i++) { var edge = this.graph.FindEdge(this.renderPath[i], this.renderPath[i + 1]); this.renderPathLength += edge.GetPixelLength(); } } } Application.prototype.startRenderTimer = function() { this.updateRenderPathLength(); this.renderTimer = window.setInterval(function(){globalApplication.redrawGraphTimer();}, 50); this.isTimerRender = true; this.renderPathLoops = 0; } Application.prototype.stopRenderTimer = function() { if (this.isTimerRender) { window.clearInterval(this.renderTimer); this.isTimerRender = false; this.renderPathLoops = 0; } } Application.prototype.setRenderPath = function(renderPath) { this.renderPath = renderPath; if (this.renderPath.length > 0) { this.startRenderTimer(); } else { this.stopRenderTimer(); } } Application.prototype.RedrawEdge = function(context, edge) { var arcDrawer = new BaseEdgeDrawer(context); var directArcDrawer = new DirectArcDrawer(context); var commonStyle = new CommonEdgeStyle(context); var selectedStyles = selectedEdgeStyles; this._RedrawEdge(edge, arcDrawer, directArcDrawer, commonStyle, selectedStyles); } Application.prototype._RedrawEdge = function(edge, arcDrawer, directArcDrawer, commonStyle, selectedStyles) { var selectedGroup = this.handler.GetSelectedGroup(edge); var currentStyle = selectedGroup > 0 ? selectedStyles[(selectedGroup - 1) % selectedStyles.length] : commonStyle; this._RedrawEdgeWithStyle(edge, currentStyle, arcDrawer, directArcDrawer, commonStyle, selectedStyles); } Application.prototype._RedrawEdgeWithStyle = function(edge, style, arcDrawer, directArcDrawer, commonStyle, selectedStyles) { if (!edge.isDirect) { arcDrawer.Draw(edge, style); } else { directArcDrawer.Draw(edge, style); } } Application.prototype.RedrawEdgeProgress = function(context, edge, progress) { var arcDrawer = new ProgressArcDrawer(context, new BaseEdgeDrawer(context), progress); var directArcDrawer = new ProgressArcDrawer(context, new DirectArcDrawer(context), progress); var commonStyle = new CommonEdgeStyle(context); var selectedStyles = selectedEdgeStyles; this._RedrawEdge(edge, arcDrawer, directArcDrawer, commonStyle, selectedStyles); } Application.prototype.RedrawEdges = function(context) { for (i = 0; i < this.graph.edges.length; i ++) { this.RedrawEdge(context, this.graph.edges[i]); } } Application.prototype.RedrawNodes = function(context) { var graphDrawer = new BaseVertexDrawer(context); var commonGraphDrawer = new CommonVertexStyle(); var selectedGraphDrawer = selectedGraphStyles; for (i = 0; i < this.graph.vertices.length; i ++) { var selectedGroup = this.handler.GetSelectedGroup(this.graph.vertices[i]); var currentStyle = selectedGroup > 0 ? selectedGraphDrawer[(selectedGroup - 1) % selectedGraphDrawer.length] : commonGraphDrawer; //this.graph.vertices[i].upText = this.handler.GetUpText(this.graph.vertices[i]); graphDrawer.Draw(this.graph.vertices[i], currentStyle); } } Application.prototype.updateMessage = function() { this.document.getElementById('message').innerHTML = this.handler.GetMessage(); this.handler.InitControls(); } Application.prototype.CanvasOnMouseMove = function(e) { // X,Y position. var pos = this.getMousePos(this.canvas, e); this.handler.MouseMove(pos); if (this.handler.IsNeedRedraw()) { this.handler.RestRedraw(); this.redrawGraph(); } this.updateMessage(); } Application.prototype.CanvasOnMouseDown = function(e) { var pos = this.getMousePos(this.canvas, e); /// provide this canvas and event this.handler.MouseDown(pos); if (this.handler.IsNeedRedraw()) { this.handler.RestRedraw(); this.redrawGraph(); } this.updateMessage(); } Application.prototype.CanvasOnMouseUp = function(e) { // this.dragObject = -1; var pos = this.getMousePos(this.canvas, e); this.handler.MouseUp(pos); if (this.handler.IsNeedRedraw()) { this.handler.RestRedraw(); this.redrawGraph(); } this.updateMessage(); } Application.prototype.multCanvasScale = function(factor) { var oldRealWidth = this.GetRealWidth(); var oldRealHeight = this.GetRealHeight(); this.canvasScale *= factor; this.canvasPosition = this.canvasPosition.add(new Point((this.GetRealWidth() - oldRealWidth) / 2.0, (this.GetRealHeight() - oldRealHeight) / 2.0)); this.redrawGraph(); } Application.prototype.setCanvasScale = function(factor) { var oldRealWidth = this.GetRealWidth(); var oldRealHeight = this.GetRealHeight(); this.canvasScale = factor; this.canvasPosition = this.canvasPosition.add(new Point((this.GetRealWidth() - oldRealWidth) / 2.0, (this.GetRealHeight() - oldRealHeight) / 2.0)); this.redrawGraph(); } Application.prototype.onCanvasMove = function(point) { this.canvasPosition = this.canvasPosition.add(point.multiply(1 / this.canvasScale)); this.redrawGraph(); } Application.prototype.AddNewVertex = function(vertex) { return this.graph.AddNewVertex(vertex); } Application.prototype.AddNewEdge = function(edge) { return this.graph.AddNewEdge(edge); } Application.prototype.CreateNewGraph = function(x, y) { var app = this; this.currentEnumVertesType.GetVertexTextAsync( function (enumType) { app.graph.AddNewVertex(new BaseVertex(x, y, enumType)); app.redrawGraph(); }); } Application.prototype.CreateNewGraphEx = function(x, y, vertexEnume) { return this.graph.AddNewVertex(new BaseVertex(x, y, vertexEnume)); } Application.prototype.CreateNewArc = function(graph1, graph2, isDirect, weight) { var useWeight = false; if (!isNaN(parseInt(weight, 10))) { useWeight = true; } weight = (!isNaN(parseInt(weight, 10)) && weight >= 0) ? weight : 1; return this.AddNewEdge(new BaseEdge(graph1, graph2, isDirect, weight, useWeight)); } Application.prototype.DeleteEdge = function(edgeObject) { this.graph.DeleteEdge(edgeObject); } Application.prototype.DeleteVertex = function(graphObject) { this.graph.DeleteVertex(graphObject); } Application.prototype.DeleteObject = function(object) { if (object instanceof BaseVertex) { this.DeleteVertex(object); } else if (object instanceof BaseEdge) { this.DeleteEdge(object); } } Application.prototype.FindVertex = function(id) { return this.graph.FindVertex(id); } Application.prototype.FindEdge = function(id1, id2) { return this.graph.FindEdge(id1, id2); } Application.prototype.FindPath = function(graph1, graph2) { var creator = new GraphMLCreater(this.graph.vertices, this.graph.edges); var app = this; $.ajax({ type: "POST", url: "/cgi-bin/GraphCGI.exe?dsp=cgiInput&start=" + graph1.id + "&finish=" + graph2.id + "&report=xml", data: creator.GetXMLString(), dataType: "text" }) .done(function( msg ) { $('#debug').text(msg); xmlDoc = $.parseXML( msg ); var $xml = $( xmlDoc ); $nodes = $xml.find( "node" ); var pathObjects = new Array(); var shortDistObjects = {}; $nodes.each(function(){ var id = $(this).attr('id'); $data = $(this).find("data"); $data.each(function(){ if ("hightlightNode" == $(this).attr('key') && $(this).text() == "1") { pathObjects.push(app.FindVertex(id)); } if ("lowestDistance" == $(this).attr('key')) { shortDistObjects[id] = $(this).text(); } }); }); $edges = $xml.find( "edge" ); $edges.each(function(){ var source = $(this).attr('source'); var target = $(this).attr('target'); pathObjects.push(app.FindEdge(source, target)); }); var $graph = $xml.find( "graph" ); $graph.each(function(){ var shortPathResult = $(this).attr('result'); app.handler.SetShortPath(shortPathResult); }); app.handler.SetObjects(pathObjects); app.handler.SetShortDist(shortDistObjects); app.redrawGraph(); app.updateMessage(); }); // return empty, will set later. return []; } Application.prototype.SetHandlerMode = function(mode) { if (this.handler) { this.handler.RestoreAll(); } if (mode == "default") { this.handler = new DefaultHandler(this); } else if (mode == "addGraph") { this.handler = new AddGraphHandler(this); } else if (mode == "addArc") { this.handler = new ConnectionGraphHandler(this); } else if (mode == "delete") { this.handler = new DeleteGraphHandler(this); } else if (mode == "deleteAll") { var removeAll = new DeleteAllHandler(this); removeAll.clear(); } else if (mode == "findPath") { this.handler = new FindPathGraphHandler(this); } else if (mode == "showAdjacencyMatrix") { var showAdjacencyMatrix = new ShowAdjacencyMatrix(this); showAdjacencyMatrix.show(); } else if (mode == "showIncidenceMatrix") { var showIncidenceMatrix = new ShowIncidenceMatrix(this); showIncidenceMatrix.show(); } else if (mode == "connectedComponent") { this.handler = new ConnectedComponentGraphHandler(this); } else if (mode == "saveDialog") { var savedDialogGraphHandler = new SavedDialogGraphHandler(this); savedDialogGraphHandler.show(); } else if (mode == "saveDialogImage") { var savedDialogGraphImageHandler = new SavedDialogGraphImageHandler(this); savedDialogGraphImageHandler.show(); } else if (mode == "eulerianLoop") { this.handler = new EulerianLoopGraphHandler(this); } else if (mode == "GroupRename") { var groupRenameVertices = new GroupRenameVertices(this); groupRenameVertices.show(); } else if (g_AlgorithmIds.indexOf(mode) >= 0) { this.handler = new AlgorithmGraphHandler(this, g_Algorithms[g_AlgorithmIds.indexOf(mode)](this.graph, this)); } console.log(mode); this.setRenderPath([]); this.updateMessage(); this.redrawGraph(); } Application.prototype.getParameterByName = function (name) { name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]"); var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"), results = regex.exec(location.search); return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " ")); } Application.prototype.onPostLoadEvent = function() { this.SetEnumVertexsType(document.cookie.replace(/(?:(?:^|.*;\s*)enumType\s*\=\s*([^;]*).*$)|^.*$/, "$1")); var wasLoad = false; var matrix = document.getElementById("inputMatrix").innerHTML; var separator = document.getElementById("separator").innerHTML == "space" ? " " : ","; console.log(matrix); console.log("separator: \"" + separator + "\""); matrix = (matrix.length <= 0) ? this.getParameterByName("matrix") : matrix; if (matrix.length > 0) { if (!this.SetAdjacencyMatrixSmart(matrix, separator)) { this.userAction("AdjacencyMatrix.Failed"); this.ShowAdjacencyMatrixErrorDialog(matrix); } else { this.userAction("AdjacencyMatrix.Success"); } this.updateMessage(); this.redrawGraph(); wasLoad = true; } var matrix = document.getElementById("inputIncidenceMatrix").innerHTML; matrix = (matrix.length <= 0) ? this.getParameterByName("incidenceMatrix") : matrix; if (matrix.length > 0) { if (!this.SetIncidenceMatrixSmart(matrix)) { this.userAction("IncidenceMatrix.Failed"); this.ShowIncidenceMatrixErrorDialog(matrix); } else { this.userAction("IncidenceMatrix.Success"); } this.updateMessage(); this.redrawGraph(); wasLoad = true; } if (!wasLoad) { var graphName = this.getParameterByName("graph"); if (graphName.length <= 0) { graphName = document.cookie.replace(/(?:(?:^|.*;\s*)graphName\s*\=\s*([^;]*).*$)|^.*$/, "$1"); } if (graphName.length > 0) { this.userAction("LoadGraphFromDisk"); this.LoadGraphFromDisk(graphName); } } this.updateMessage(); this.redrawGraph(); } Application.prototype.onLoad = function() { this.canvas = this.document.getElementById('canvas'); this.handler = new AddGraphHandler(this); this.updateMessage(); this.redrawGraph(); } Application.prototype.NeedRedraw = function() { //TODO this.updateMessage(); this.redrawGraph(); } Application.prototype.SetStatus = function(name, value) { this.status[name] = value; } Application.prototype.GetStatus = function() { return this.status[name]; } Application.prototype.GetAdjacencyMatrix = function () { return this.graph.GetAdjacencyMatrix(); } Application.prototype.TestAdjacencyMatrix = function (matrix, rowsObj, colsObj, separator = ",") { return this.graph.TestAdjacencyMatrix(matrix, rowsObj, colsObj, separator); } Application.prototype.SetAdjacencyMatrix = function (matrix, separator = ",") { var res = true; var r = {}; var c = {}; if (!this.TestAdjacencyMatrix(matrix, r, c, separator)) { $.get( "/cgi-bin/addFailedMatrix.php?text=adjacency&matrix=" + encodeURIComponent(matrix), function( data ) {;}); res = false; } this.graph.SetAdjacencyMatrix(matrix, new Point(this.GetRealWidth(), this.GetRealHeight()), this.currentEnumVertesType, separator); this.AutoAdjustViewport(); this.redrawGraph(); return res; } Application.prototype.GetIncidenceMatrix = function () { return this.graph.GetIncidenceMatrix(); } Application.prototype.TestIncidenceMatrix = function (matrix, rowsObj, colsObj) { return this.graph.TestIncidenceMatrix(matrix, rowsObj, colsObj); } Application.prototype.SetIncidenceMatrix = function (matrix) { var res = true; var r = {}; var c = {}; if (!this.TestIncidenceMatrix(matrix, r, c)) { $.get( "/cgi-bin/addFailedMatrix.php?text=incidence&matrix=" + encodeURIComponent(matrix), function( data ) {;}); res = false; } this.graph.SetIncidenceMatrix(matrix, new Point(this.GetRealWidth(), this.GetRealHeight()), this.currentEnumVertesType); this.AutoAdjustViewport(); this.redrawGraph(); return res; } Application.prototype.Test = function () { this.graph.VertexesReposition(new Point(this.GetRealWidth(), this.GetRealHeight()), this.graph.vertices); this.redrawGraph(); } Application.prototype.SetAdjacencyMatrixSmart = function (matrix, separator = ",") { var res = false; if (this.TestAdjacencyMatrix(matrix, {}, {}, separator)) { res = this.SetAdjacencyMatrix(matrix, separator); } else if (this.TestIncidenceMatrix(matrix, {}, {})) { res = this.SetIncidenceMatrix(matrix); } else { res = this.SetAdjacencyMatrix(matrix); } return res; } Application.prototype.SetIncidenceMatrixSmart = function (matrix) { var res = false; if (this.TestIncidenceMatrix(matrix, {}, {})) { res = this.SetIncidenceMatrix(matrix); } else if (this.TestAdjacencyMatrix(matrix, {}, {})) { res = this.SetAdjacencyMatrix(matrix); } else { res = this.SetIncidenceMatrix(matrix); } return res; } Application.prototype.SaveGraphOnDisk = function () { var graphAsString = this.graph.SaveToXML(); if (this.savedGraphName.length <= 0) { this.savedGraphName = this.GetNewGraphName(); } var app = this; $.ajax({ type: "POST", url: "/cgi-bin/saveGraph.php?name=" + this.savedGraphName, data: graphAsString, dataType: "text" }) .done(function( msg ) { document.cookie = "graphName=" + app.savedGraphName; }); } Application.prototype.SaveGraphImageOnDisk = function () { var imageName = this.GetNewGraphName(); this.stopRenderTimer(); this.redrawGraph(); var bbox = this.graph.getGraphBBox(); var rectParams = ""; if (this.IsGraphFitOnViewport()) { var canvasWidth = this.GetRealWidth(); var canvasHeight = this.GetRealHeight(); var canvasPositionInverse = this.canvasPosition.inverse(); var pos = bbox.minPoint.subtract(canvasPositionInverse); rectParams = "&x=" + Math.round(pos.x * this.canvasScale) + "&y=" + Math.round(pos.y * this.canvasScale) + "&width=" + Math.round(bbox.size().x * this.canvasScale) + "&height=" + Math.round(bbox.size().y * this.canvasScale); //console.log(rectParams); } var imageBase64Data = this.canvas.toDataURL(); $.ajax({ type: "POST", url: "/cgi-bin/saveImage.php?name=" + imageName + rectParams, data: { base64data : imageBase64Data }, dataType: "text" }); return imageName; } Application.prototype.LoadGraphFromDisk = function (graphName) { var app = this; $.ajax({ type: "GET", url: "/cgi-bin/loadGraph.php?name=" + graphName }) .done(function( msg ) { var graph = new Graph(); graph.LoadFromXML(msg); app.SetDefaultTransformations(); app.graph = graph; app.AutoAdjustViewport(); app.updateMessage(); app.redrawGraph(); }); } Application.prototype.GetNewGraphName = function() { var name = ""; var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; for (var i = 0; i < this.graphNameLength; i++ ) { name += possible.charAt(Math.floor(Math.random() * possible.length)); } return name; } Application.prototype.GetGraphName = function() { return this.savedGraphName; } Application.prototype.SetDefaultHandler = function() { restButtons ('Default'); this.SetHandlerMode("default"); } Application.prototype.GetEnumVertexsList = function() { var res = []; for (var i = 0; i < this.enumVertexesTextList.length; i ++) { var one = {}; one["text"] = this.enumVertexesTextList[i].GetText(); one["value"] = this.enumVertexesTextList[i].GetValue(); one["select"] = this.enumVertexesTextList[i].GetValue() == this.currentEnumVertesType.GetValue(); res.push(one); } return res; } Application.prototype.SetEnumVertexsType = function(value) { for (var i = 0; i < this.enumVertexesTextList.length; i ++) { if (this.enumVertexesTextList[i].GetValue() == value) { this.currentEnumVertesType = this.enumVertexesTextList[i]; document.cookie = "enumType=" + value; break; } } } Application.prototype.ShowAdjacencyMatrixErrorDialog = function(matrix) { var dialogButtons = {}; matrixRes = matrix.replace(/\n/g,'%0A'); dialogButtons[g_readMatrixHelp] = function() { window.location.assign(g_language == "ru" ? "./wiki/Справка/МатрицаСмежности#matrixFormat" : "./wiki/Help/AdjacencyMatrix#matrixFormat"); }; dialogButtons[g_fixMatrix] = function() { window.location.assign("./create_graph_by_matrix?matrix=" + matrixRes); }; dialogButtons[g_close] = function() { $( this ).dialog( "close" ); }; $( "#matrixError" ).dialog({ resizable: false, title: g_matrixWrongFormat, width: 400, modal: true, dialogClass: 'EdgeDialog', buttons: dialogButtons, }); } Application.prototype.ShowIncidenceMatrixErrorDialog = function(matrix) { var dialogButtons = {}; matrixRes = matrix.replace(/\n/g,'%0A'); dialogButtons[g_readMatrixHelp] = function() { window.location.assign(g_language == "ru" ? "./wiki/Справка/МатрицаИнцидентности#matrixFormat" : "./wiki/Help/IncidenceMatrix#matrixFormat"); }; dialogButtons[g_fixMatrix] = function() { window.location.assign("./create_graph_by_incidence_matrix?incidenceMatrix=" + matrixRes); }; dialogButtons[g_close] = function() { $( this ).dialog( "close" ); }; $( "#matrixErrorInc" ).dialog({ resizable: false, title: g_matrixWrongFormat, width: 400, modal: true, dialogClass: 'EdgeDialog', buttons: dialogButtons, }); } Application.prototype.SetFindPathReport = function (value) { this.findPathReport = value; } Application.prototype.GetFindPathReport = function () { return this.findPathReport; } Application.prototype.CalculateAlgorithm = function(queryString, callbackObject) { var app = this; var creator = new GraphMLCreater(app.graph.vertices, app.graph.edges); var pathObjects = []; var properties = {}; var result = []; $.ajax({ type: "POST", url: "/cgi-bin/GraphCGI.exe?" + queryString, data: creator.GetXMLString(), dataType: "text", }) .done(function( msg ) { console.log(msg); $('#debug').text(msg); xmlDoc = $.parseXML( msg ); var $xml = $( xmlDoc ); $results = $xml.find( "result" ); $results.each(function(){ $values = $(this).find( "value" ); $values.each(function(){ var type = $(this).attr('type'); var value = $(this).text(); var res = {}; res.type = type; res.value = value; result.push(res); }); }); $nodes = $xml.find( "node" ); $nodes.each(function(){ var id = $(this).attr('id'); $data = $(this).find("data"); $data.each(function(){ if ("hightlightNode" == $(this).attr('key') && $(this).text() == "1") { pathObjects.push(app.FindVertex(id)); } else { if (!properties[id]) { properties[id] = {}; } properties[id][$(this).attr('key')] = $(this).text(); } }); }); $edges = $xml.find( "edge" ); $edges.each(function(){ var source = $(this).attr('source'); var target = $(this).attr('target'); pathObjects.push(app.FindEdge(source, target)); }); console.log(result); callbackObject.CalculateAlgorithmCallback(pathObjects, properties, result); }); return true; } Application.prototype.GetRealWidth = function () { return this.canvas.width / this.canvasScale; } Application.prototype.GetRealHeight = function () { return this.canvas.height / this.canvasScale; } Application.prototype.SetDefaultTransformations = function() { this.canvasScale = 1.0; this.canvasPosition = new Point(0, 0); } Application.prototype.AutoAdjustViewport = function() { graphBBox = this.graph.getGraphBBox(); bboxCenter = graphBBox.center(); bboxSize = graphBBox.size(); if (bboxSize.length() > 0) { // Setup size if (bboxSize.x > this.GetRealWidth() || bboxSize.y > this.GetRealHeight()) { this.canvasScale = Math.min(this.GetRealWidth() / bboxSize.x, this.GetRealHeight() / bboxSize.y); } // Setup position. if (graphBBox.minPoint.x < 0.0 || graphBBox.minPoint.y < 0.0 || graphBBox.maxPoint.x > this.GetRealWidth() || graphBBox.maxPoint.y > this.GetRealHeight()) { // Move center. this.canvasPosition = graphBBox.minPoint.inverse(); } } } Application.prototype.OnAutoAdjustViewport = function() { this.SetDefaultTransformations(); this.AutoAdjustViewport(); this.redrawGraph(); } Application.prototype.getAlgorithmNames = function() { var res = []; for (var i = 0; i < g_Algorithms.length; i++) { factory = g_Algorithms[i]; var obj = {}; oneFactory = factory(this.graph); obj.name = oneFactory.getName(g_language); obj.id = oneFactory.getId(); res.push(obj); } return res; } Application.prototype.resultCallback = function(paths) { console.log(paths); if ((paths instanceof Object) && "paths" in paths) { this.setRenderPath(paths["paths"][0]); } this.updateMessage(); this.redrawGraph(); } Application.prototype.GetCurrentValue = function(paramName, defaultValue) { return (paramName in this.algorithmsValues) ? this.algorithmsValues[paramName] : defaultValue; } Application.prototype.SetCurrentValue = function(paramName, value) { this.algorithmsValues[paramName] = value; } Application.prototype.IsGraphFitOnViewport = function() { res = true; graphBBox = this.graph.getGraphBBox(); var canvasWidth = this.GetRealWidth();// * this.canvasScale; var canvasHeight = this.GetRealHeight();// * this.canvasScale; var canvasPositionInverse = this.canvasPosition./*multiply(this.canvasScale).*/inverse(); //console.log("BBox_min = " + graphBBox.minPoint.toString() + " - BBox_max = " + graphBBox.maxPoint.toString() // + " Position" + canvasPositionInverse.toString() + " - cw = " + canvasWidth + " ch = " + canvasHeight); return (Math.floor(canvasPositionInverse.x) <= Math.floor(graphBBox.minPoint.x) && Math.floor(canvasPositionInverse.y) <= Math.floor(graphBBox.minPoint.y) && Math.floor(canvasPositionInverse.x + canvasWidth) >= Math.floor(graphBBox.maxPoint.x) && Math.floor(canvasPositionInverse.y + canvasHeight) >= Math.floor(graphBBox.maxPoint.y)); } var application = new Application(document, window); var waitCounter = false; var userAction = function(str) { if (typeof window.yaCounter25827098 !== "undefined") { console.log(str); window.yaCounter25827098.hit("http://" + window.location.hostname + "/UserAction#" + str); } else if (!waitCounter) { waitCounter = true; setTimeout(function() { userAction(str); }, 2000); } } var isIe = (navigator.userAgent.toLowerCase().indexOf("msie") != -1 || navigator.userAgent.toLowerCase().indexOf("trident") != -1); var buttonsList = ['AddGraph', 'ConnectGraphs', 'DeleteObject', 'Default']; function restButtons (me) { var needSetDefault = false; for (var i = 0; i < buttonsList.length; i ++) { if (buttonsList[i] != me) { document.getElementById(buttonsList[i]).className = "btn btn-default btn-sm"; } else { if (document.getElementById(buttonsList[i]).className != "btn btn-default btn-sm") { needSetDefault = true; } } } if (needSetDefault) { document.getElementById(buttonsList[i]).className = "btn btn-primary btn-sm"; } else { document.getElementById(me).className = "btn btn-primary btn-sm"; } } var single = 0; function resizeCanvas() { var adv = document.getElementById('adv'); var canvas = document.getElementById('canvas'); canvas.width = document.getElementById('canvasSection').offsetWidth; //canvas.height = document.getElementById('footer').offsetTop - document.getElementById('canvasSection').offsetTop - (adv && $("#adv").css("display") === 'block' ? document.getElementById('adv').offsetHeight : 0); canvas.height = $(window).height() - document.getElementById('canvas').offsetTop - (adv && $("#adv").css("display") === 'block' ? document.getElementById('adv').offsetHeight : 0) - ($("#footer").css("display") === 'block' ? document.getElementById('footer').offsetHeight : 0) - (document.documentElement.clientWidth < 650 ? 20 : 0); application.redrawGraph(); } function touchHandler(event) { var touches = event.changedTouches, first = touches[0], type = ""; switch(event.type) { case "touchstart": type = "mousedown"; break; case "touchmove": type="mousemove"; break; case "touchend": type="mouseup"; break; default: return; } var simulatedEvent = document.createEvent("MouseEvent"); simulatedEvent.initMouseEvent(type, true, true, window, 1, first.screenX, first.screenY, first.clientX, first.clientY, false, false, false, false, 0/*left*/, null); first.target.dispatchEvent(simulatedEvent); event.preventDefault(); } function preLoadPage() { loadTexts(); application.onLoad(); } function createAlgorithmMenu() { var algorihtmsBaseId = "Algo"; var algorithms = application.getAlgorithmNames(); var index = 0; for (var i = 0; i < algorithms.length; i++) { algorithm = algorithms[i]; var list = document.getElementById("algorithmList"); var item = list.lastElementChild; var clone = item.cloneNode(true); var button = clone.getElementsByTagName("button")[0]; var textSpan = button.getElementsByTagName("span")[1]; button.id = algorithm.id; textSpan.innerHTML = algorithm.name; clone.style.display = "block"; buttonsList.push(algorithm.id); button.onclick = function () { userAction(this.id); restButtons (this.id); application.SetHandlerMode(this.id); } list.appendChild(clone); index++; } } function postLoadPage() { application.userAction = userAction; application.canvas.onmousemove = function (e) { return application.CanvasOnMouseMove(e); }; application.canvas.onmousedown = function (e) { return application.CanvasOnMouseDown(e); }; application.canvas.onmouseup = function (e) { return application.CanvasOnMouseUp(e); } application.canvas.onmousewheel = function (e) { var e = window.event || e; // old IE support var delta = Math.max(-1, Math.min(1, (e.wheelDelta || -e.detail))); if (delta > 0) { application.multCanvasScale(1.3); } else { application.multCanvasScale(1.0 / 1.3); } } document.onkeypress = function (e) { if (event.defaultPrevented || ($('#addVertex').hasClass('ui-dialog-content') && $('#addVertex').dialog('isOpen')) || ($('#adjacencyMatrix').hasClass('ui-dialog-content') && $('#adjacencyMatrix').dialog('isOpen')) || ($('#addEdge').hasClass('ui-dialog-content') && $('#addEdge').dialog('isOpen')) || ($('#incidenceMatrix').hasClass('ui-dialog-content') && $('#incidenceMatrix').dialog('isOpen')) || ($('#saveDialog').hasClass('ui-dialog-content') && $('#saveDialog').dialog('isOpen')) || ($('#saveImageDialog').hasClass('ui-dialog-content') && $('#saveImageDialog').dialog('isOpen')) || ($('#GroupRenameDialog').hasClass('ui-dialog-content') && $('#GroupRenameDialog').dialog('isOpen')) || $('#developerTools').css("display") != "none") { console.log("prevent"); return; // Should do nothing if the default action has been cancelled } var key = 0; if(window.event) { key = event.keyCode; } else if(event.which) { key = event.which; } console.log(key); var moveValue = 10; if (key == 61 || key == 43) // + { application.multCanvasScale(1.5); } else if (key == 45) // - { application.multCanvasScale(1 / 1.5); } else if (key == 119 || key == 1094) // up { application.onCanvasMove(new Point(0, moveValue)); } else if (key == 115 || key == 1099) // down { application.onCanvasMove(new Point(0, -moveValue)); } else if (key == 97 || key == 1092) // left { application.onCanvasMove(new Point(moveValue, 0)); } else if (key == 100 || key == 1074) // right { application.onCanvasMove(new Point(-moveValue, 0)); } } document.getElementById('ShowAdjacencyMatrix').onclick = function () { userAction(this.id); application.SetHandlerMode("showAdjacencyMatrix"); } document.getElementById('ShowIncidenceMatrix').onclick = function () { userAction(this.id); application.SetHandlerMode("showIncidenceMatrix"); } document.getElementById('GroupRename').onclick = function () { userAction(this.id); application.SetHandlerMode("GroupRename"); } document.getElementById('groupRenameButton').onclick = function () { userAction(this.id); application.SetHandlerMode("GroupRename"); } document.getElementById('Default').onclick = function () { userAction(this.id); restButtons ('Default'); application.SetHandlerMode("default"); document.getElementById('Default').className = "btn btn-primary btn-sm"; } document.getElementById('AddGraph').onclick = function () { userAction(this.id); restButtons ('AddGraph'); application.SetHandlerMode(document.getElementById('AddGraph').className != "" ? "addGraph" : "default"); } document.getElementById('ConnectGraphs').onclick = function () { userAction(this.id); restButtons ('ConnectGraphs'); application.SetHandlerMode(document.getElementById('ConnectGraphs').className != "" ? "addArc" : "default"); } document.getElementById('DeleteObject').onclick = function () { userAction(this.id); restButtons ('DeleteObject'); application.SetHandlerMode(document.getElementById('DeleteObject').className != "" ? "delete" : "default"); } document.getElementById('DeleteAll').onclick = function () { userAction(this.id); application.SetHandlerMode("deleteAll"); } document.getElementById('SaveGraph').onclick = function () { userAction(this.id); application.SetHandlerMode("saveDialog"); } document.getElementById('NewGraph').onclick = function () { userAction(this.id); application.SetHandlerMode("deleteAll"); application.SetDefaultTransformations(); } document.getElementById('SaveGraphImage').onclick = function () { userAction(this.id); application.SetHandlerMode("saveDialogImage"); } document.getElementById('Zoom100').onclick = function () { userAction(this.id); application.setCanvasScale(1.0); } document.getElementById('Zoom50').onclick = function () { userAction(this.id); application.setCanvasScale(50 / 100); } document.getElementById('Zoom25').onclick = function () { userAction(this.id); application.setCanvasScale(25 / 100); } document.getElementById('ZoomFit').onclick = function () { userAction(this.id); application.OnAutoAdjustViewport(); } document.getElementById('ZoomIn').onclick = function () { userAction(this.id); application.multCanvasScale(1.5); } document.getElementById('ZoomOut').onclick = function () { userAction(this.id); application.multCanvasScale(1.0 / 1.5); } document.getElementById('MoveWorspace').onclick = function () { userAction(this.id); restButtons ('Default'); application.SetHandlerMode("default"); document.getElementById('Default').className = "btn btn-primary btn-sm"; } document.getElementById('runUserScript').onclick = function () { var el = document.getElementById('userScript'); var oldScript = document.getElementById("userScriptSource"); if (oldScript) { document.head.removeChild(oldScript); } var script = document.createElement('script'); script.type = "text/javascript"; script.innerHTML = el.value; script.id = "userScriptSource"; document.head.appendChild(script); application.SetHandlerMode("user.algorithm"); } document.getElementById('submitUserScript').onclick = function () { var script = document.getElementById('userScript'); var data = "message=" + script.value + "&"; $.ajax({ type: "POST", url: "/feedback", data: data }); $( "#sentAlgorithm" ).dialog({ resizable: false, height: "auto", width: 400, modal: true, dialogClass: 'EdgeDialog' }); } document.getElementById('devToolsZoom').onclick = function () { var devTools = document.getElementById('developerTools'); if (devTools.hasOwnProperty("isMin") && !devTools["isMin"]) { devTools["isMin"] = true; devTools.style.width = "30%"; } else { devTools["isMin"] = false; devTools.style.width = "100%"; } } // Get algorithms list and load it. $.get( "/cgi-bin/getPluginsList.php", function( data ) { var scriptList = JSON.parse(data); var loadOneScript = function() { if (scriptList.length == 0) { createAlgorithmMenu(); } else { var script = document.createElement('script'); script.src = scriptList[0]; scriptList.shift(); script.onload = loadOneScript; script.onerror = loadOneScript; document.head.appendChild(script); } } loadOneScript(); }); var devTools = document.getElementById('developerTools'); devTools.style.left = 0; resizeCanvas(); application.onPostLoadEvent(); } //window.onload = function () $(document).ready(function () { window.onresize = function(event) { resizeCanvas(); } document.getElementById('canvas').addEventListener("touchstart", touchHandler, true); document.getElementById('canvas').addEventListener("touchmove", touchHandler, true); document.getElementById('canvas').addEventListener("touchend", touchHandler, true); document.getElementById('canvas').addEventListener("touchcancel", touchHandler, true); /* $(document).ready(function(){ //set up some basic options for the feedback_me plugin fm_options = { position: "left-bottom", message_placeholder: g_what_do_you_think, message_required: true, name_label: g_name, message_label: g_feedback, trigger_label: g_feedback, submit_label: g_send, title_label: g_write_to_us, feedback_url: "/feedback", }; //init feedback_me plugin fm.init(fm_options); }); */ }); Array.prototype.swap = function (x,y) { var b = this[x]; this[x] = this[y]; this[y] = b; return this; }