/**
* 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;
}