/*!
* Magic Board v1
* Copyright 2015-2016 BinaryCodon, Inc.
* Licensed under GNU Affero General Public License
* Author - Rajeev Shrivastava
*/
var inheritsFrom = function (child, parent) {
child.prototype = Object.create(parent.prototype);
};
var MagicBoard = {sheetBook:null,"indicators":{"mouseDown":false,"mouseover":[],"click":false,"doubleClick":0,"resize":-1},"boardPos":{x:0,y:0}};
/*
"background-color"
"border-color"
"border-width"
"border-style"
"text-color"
"font-size"
"font-weight"
"line-type"
"line-color"
"end-marker"
"start-marker"
"mid-marker"
*/
MagicBoard.properties = {
"background-color":{"propType":"attribute","propName":"fill","label":"Background Color","field":"input","values":[{"name":"","value":"#1b8d11","type":"color"}]},
"border-color":{"propName":"stroke","propType":"attribute","label":"Border Color","field":"input","values":[{"name":"","value":"#1b8d11","type":"color"}]},
"border-width":{"propName":"stroke-width","propType":"attribute","label":"Border Width","field":"input","values":[{"name":"","value":"1","type":"text"}]},
"border-style":{"propName":"stroke-dasharray","propType":"attribute","label":"Border Style","field":"select","values":[{"name":"Dash","value":"5,5","type":""},{"name":"Solid","value":"","type":""},{"name":"Dotted","value":"1,1","type":""}]},
"text-color":{"propName":"fill","propType":"attribute","label":"Text Color","field":"input","values":[{"name":"","value":"#ffffff","type":"color"}]},
"font-size":{"propName":"font-size","propType":"attribute","label":"Font Color","field":"input",values:[{"name":"","value":"14","type":"text"}]},
"font-weight":{"propName":"font-weight","propType":"attribute","label":"Font Weight","field":"select",values:[{"name":"Regular","value":"regular"},{"name":"Bold","value":"bold"},{"name":"Italic","value":"italic"}]},
"text-content":{"propName":"innerHTML","propType":"dom","label":"Text","field":"input","values":[{value:""}]},
"line-type":{"propName":"","propType":"function","field":"select","label":"Line Type",values:[{"name":"Straight",value:"straight"},{"name":"Zig Zag",value:"zig-zag"},{"name":"Brezier Curve",value:"brezier"},{"name":"Quadratic Curve",value:"quadratic"}]},
"line-color":{"propName":"stroke","propType":"attribute","label":"Line Color","field":"input","values":[{"name":"","value":"#1b8d11","type":"color"}]},
"line-width":{"propName":"stroke-width","propType":"attribute","label":"Line Width","field":"input","values":[{"name":"","value":"1","type":"text"}]},
"line-style":{"propName":"stroke-dasharray","propType":"attribute","label":"Line Style","field":"select","values":[{"name":"Solid","value":"","type":""},{"name":"Dash","value":"5,5","type":""},{"name":"Dotted","value":"1,1","type":""}]},
"end-marker":{"propName":"marker-end","propType":"attribute","label":"End Marker","field":"select","values":[{"name":"Filled Arrow",value:"url(#fillArrowE)"},{"name":"Hollow Arrow",value:"url(#hollowArrowE)"},{"name":"Regular Arrow",value:"url(#lineArrowE)"},{"name":"Cicle",value:"url(#dot)"},{"name":"Hollow Diamond",value:"url(#hollowDiamond)"},{"name":"Filled Diamond",value:"url(#fillDiamond)"},{"name":"No Arrow",value:""}]},
"start-marker":{"propName":"marker-start","propType":"attribute","label":"Start Marker","field":"select","values":[{"name":"Filled Arrow",value:"url(#fillArrowS)"},{"name":"Hollow Arrow",value:"url(#hollowArrowS)"},{"name":"Regular Arrow",value:"url(#lineArrowS)"},{"name":"Cicle",value:"url(#dot)"},{"name":"Hollow Diamond",value:"url(#hollowDiamond)"},{"name":"Filled Diamond",value:"url(#fillDiamond)"},{"name":"No Arrow",value:""}]},
"mid-marker":{"propName":"marker-mid","propType":"attribute","field":"select","label":"Mid Marker","values":[{"name":"Dot",value:"url(#dot)"}]}
}
/**
* SheetBook consists of many sheets, each sheet contains shapes, images and drawing etc.
* @constructor
*/
var SheetBook = function(_anchorElement,_width,_height)
{
this.cheight = 400;this.cwidth = 400;
this.sheets = []; this.currentSheet = null;
this.anchor = document.body; // the can be changed
this.alignments = {"x":[],"y":[] };
this.maxRedo = 5;
this.garbage = document.createElement("div"); this.garbage.setAttribute("style","display:none");
document.body.appendChild(this.garbage);
if (_height) this.cheight = _height; if (_width) this.cwidth = _width; if (_anchorElement) this.anchor = _anchorElement;
var anchorDim = _anchorElement.getBoundingClientRect();
MagicBoard.boardPos = {x:anchorDim.left,y:anchorDim.top};
Utility.SheetBook.createWorkItems(this);
// attach mouse movements
document.onmousedown = function(e) {
return MagicBoard.eventStart(e);
};
document.onmousemove = function(e) {
MagicBoard.eventContinue(e);
};
document.onmouseup = function(e) {
MagicBoard.eventStop(e);
};
document.onkeyup = function(e) {
event.preventDefault();
MagicBoard.keyUp(e);
};
}
/**
* This Function allows propotionate zoom of the entire SheetBook
* Underconstruction - not ready yet
*/
SheetBook.prototype.zoom = function()
{
}
/**
* This Function sets HTML Dom element as parent anchor
* This is useful to put a sheetbook inside external HTML
* @param {HTMLElement} _anchor
* @returns - nothing
*/
SheetBook.prototype.setAnchorElement = function(_anchor)
{
//if (typeof(anchor) === "HTMLDomElement")
this.anchor = _anchor;
}
/**
* This Function add sheets to existing sheetbook
* @param {Sheet} _sheet
* @returns - nothing
*/
SheetBook.prototype.addSheet = function(_sheet)
{
this.sheets.push(_sheet);
var canvas = _sheet.getCanvas();
canvas.setAttribute("height",this.cheight);
canvas.setAttribute("width",this.cwidth);
canvas.style["width"] = this.cwidth+"px";
canvas.style["height"] = this.cheight+"px";
this.anchor.appendChild(canvas);
this.setCurrentSheet(_sheet);
}
/**
* This Function retrieves a sheet from SheetBook with a given name
* @param {String} _sheetName
* @returns - nothing
*/
SheetBook.prototype.getSheet = function(_sheetName)
{
for (var i = this.sheets.length - 1; i > -1;i--)
{
var sheet = this.sheets[i];
if (sheet.name === _sheetName)
{
return sheet;
}
}
return null;
}
/**
* This Function retrieves currently active Sheet in the SheetBook
* @returns - {Sheet} currentSheet
*/
SheetBook.prototype.getCurrentSheet = function()
{
return this.currentSheet;
}
/**
* This Function sets a sheet as active and current Sheet within the SheetBook
* @param {Sheet} _sheet
* @returns - nothing
*/
SheetBook.prototype.setCurrentSheet = function(_sheet)
{
var currentSheet = this.currentSheet;
if (currentSheet) currentSheet.canvas.style["visibility"] = "hidden";
var name = null; var nameSearch = false; var found;
if (typeof(_sheet) === "string")
{
name = _sheet;
nameSearch = true;
}
for (var i = this.sheets.length - 1; i > -1;i--)
{
var sheet = this.sheets[i];
if (nameSearch)
{
if (sheet.name === name)
{
break;
}
} else if (sheet === _sheet)
{
break;
}
}
this.currentSheet = _sheet;
// the below is no longer needed
//Utility.SheetBook.attachWorkItems(_sheet);
_sheet.canvas.style["visibility"] = "visible";
// clean connect Canvas
var connectCanvas = this.connectCanvas;
MagicBoard.sheetBook.connectCtx.clearRect(0,0,connectCanvas.width,connectCanvas.height);
return;
}
/**
* This Function converts all the sheets within a SheetBook into a single combined image
* @returns - {String} dataURL - This Data Url can be directly used within an image element of an HTML
*/
SheetBook.prototype.getCombinedImage = function()
{
var tempCtx = this.scratchCtx;
if (!tempCtx) return;
tempCtx.clearRect(0, 0, this.cwidth, this.cheight);
// redraw all objects
var sLen = this.shapes.length;
for (var i = 0; i < sLen;i++)
{
var _shape = this.shapes[i];
_shape.draw(tempCtx);
}
var dataURL = this.scratchCanvas.toDataURL();
return dataURL;
}
/**
* This Function sets the height in pixel of the SheetBook
* @param {Number} _height
* @returns - nothing
*/
SheetBook.prototype.setHeight = function(_height)
{
this.cheight = _height;
this.currentCanvas.setAttribute("height",this.cheight);
}
/**
* This Function sets the width in pixel for the SheetBook
* @param {Number} _width
* @returns - nothing
*/
SheetBook.prototype.setWidth = function(_width)
{
this.cwidth = _width;
this.currentCanvas.setAttribute("width",_width);
}
/**
* A Sheet is collection of drawingObjects
* @constructor
*/
var Sheet = function(_options)
{
this.options = {};
if (_options) this.options = _options;
this.name = "Sheet1";
if (_options.name) this.name = _options.name;
this.canvas = null;
this.init();
}
/**
* This Function is for internal use only, it initalizes the Sheet
* @returns - nothing
*/
Sheet.prototype.init = function()
{
this.removedShapes = []; // keep only last 5
this.canvas = document.createElementNS("http://www.w3.org/2000/svg", "svg");
this.canvas.setAttribute("id",this.name);
this.canvas.style["position"] = "absolute";
this.canvas.style["left"] = "0px";
this.canvas.style["top"] = "0px";
this.canvas.style["z-index"] = "1";
this.canvas.style["visibility"] = "visible";
var backgroundColor = this.options["background-color"] ;
if (backgroundColor) this.canvas.style["fill"] = backgroundColor;
this.connections = [];
Utility.Sheet.Markers(this);
// create grids
this.courseGridSize = {"x":100,"y":100};
this.noOfXcourseGrids = Math.floor(MagicBoard.sheetBook.cwidth / this.courseGridSize.x);
this.noOfYcourseGrids = Math.floor(MagicBoard.sheetBook.cheight / this.courseGridSize.y);
this.courseGrids = []; var gNo = 0;
for (var y = 0; y < this.noOfYcourseGrids;y++)
{
var y1 = y * this.courseGridSize.y;
var y2 = (y+1) * this.courseGridSize.y;
for (var x = 0 ; x < this.noOfXcourseGrids; x++)
{
var x1 = x * this.courseGridSize.x;
var x2 = (x+1) * this.courseGridSize.x;
var grid = {"filled":false,shapes:[],"x1":x1,"x2":x2,"y1":y1,"y2":y2,"index":gNo++};
this.courseGrids.push(grid);
}
}
MagicBoard.sheetBook.currentSheet = this;
this.shapes = [];
if (this.options.shapes)
{
for (var s = 0, sLen = this.options.shapes.length;s < sLen ;s++)
{
var shape = new Shape(this.options.shapes[s]);
shape.draw();
shape.addHover();
}
}
}
/**
* This Function returns symbolic canvas. A Canvas does not necessarily be an HTML Canvas element.
* @returns - nothing
*/
Sheet.prototype.getCanvas = function()
{
return this.canvas;
}
/**
* This Function wipes any drawing, object etc within a Sheet
* @returns - nothing
*/
Sheet.prototype.wipe = function()
{
var garbage = MagicBoard.sheetBook.garbage;
var children = this.canvas.children;
for (var i = children.length - 1; i > -1;i--)
{
var child = children[i];
if (child.getAttribute("name") === "workItem") continue;
garbage.appendChild(child);
}
garbage.innerHTML = "";
}
/**
* This Function Calculates and Aggregates the area of all the shapes within a Sheet
* Underconstruction
* @returns - {float} - area
*/
Sheet.prototype.totalShapeArea = function()
{
}
/**
* This Function adds shapes and registers it for the sheet
* @param {Shape} _shape
* @returns - nothing
*/
Sheet.prototype.addShape = function(_shape)
{
this.shapes.push(_shape);
}
/**
* This Function removes the last added Shape in the Sheet
* @returns - nothing
*/
Sheet.prototype.removeLastShape = function()
{
var maxRedo = MagicBoard.sheetBook.maxRedo;
if (this.shapes.length === 0) return;
var info = this.shapes[this.shapes.length - 1];
this.removedShapes.push(info);
if (this.removedShapes.length > maxRedo)
{
this.removedShapes.splice(0,1); // remove the oldest
}
this.shapes.pop();
}
/**
* This Function removes a given Shape from the SheetBook
* @param {Shape} _shape
* @returns - nothing
*/
Sheet.prototype.removeShape = function(_shape)
{
if (this.shapes.length === 0) return;
for (var i = this.shapes.length - 1;i > -1;i--)
{
var shape = this.shapes[i];
if (shape === _shape)
{
this.shapes.splice(i,1);
return;
}
}
}
/**
* This Function redraws (or repaints) the Sheet
* @returns - nothing
*/
Sheet.prototype.reDraw = function()
{
this.wipe();
var sLen = this.shapes.length;
for (var i = 0; i < sLen;i++)
{
var _shape = this.shapes[i];
_shape.createCanvas();
_shape.draw();
}
}
/**
* This Function undoes the last added or deleted Shape
* Property changes are undone yet, will be added in the next upcoming versions
* @returns - nothing
*/
Sheet.prototype.undo = function()
{
this.removeLastShape();
this.reDraw();
}
/**
* This Function redoes the last undo invokation
* Upto 5 redos are allowed and this is controled by maxRedo variable
* @returns - nothing
*/
Sheet.prototype.redo = function()
{
if (this.removedShapes.length === 0) return; // nothing to redo
this.shapes.push(this.removedShapes[0]);
this.removedShapes.splice(0,1);
this.reDraw();
}
/**
* This Function rebuilds the connections (connecting lines between Shapes)
* @returns - nothing
*/
Sheet.prototype.refreshConnections = function()
{
var sLen = this.shapes.length;
for (var i = 0; i < sLen;i++)
{
var _shape = this.shapes[i];
_shape.refreshConnection();
}
}
/**
* This Function converts the Sheet into an image
* @returns - {DataURL} dataURL
*/
Sheet.prototype.getImage = function()
{
var canvas = document.createElement("canvas");
canvas.height = MagicBoard.sheetBook.cheight; canvas.width = MagicBoard.sheetBook.cwidth;
var context = canvas.getContext("2d");
var shapes = this.shapes;var sLen = shapes.length;
for (var s = 0; s < sLen;s++)
{
var shape = shapes[s];
shape.drawOnCanvas(context);
}
var dataURL = canvas.toDataURL();
return dataURL;
}
/**
* This Function adds connections between two shapes
* @param {Object} _cInfo - Has the following format {"beginShape":shape,"endShape":shape,pos:{x1:0,y1:0,x2:100,y2:100}}
* @returns - nothing
*/
Sheet.prototype.addConnections = function(cInfo)
{
var beginShape = cInfo.beginShape; var endShape = cInfo.endShape;
var pos = cInfo.pos;
var conn = this.connections;
var cLen = conn.length;
var found = false;
for (var i = 0; i < cLen;i++)
{
var cI = conn[i];
var pos = cInfo.pos;
if (cI.beginShape === beginShape && cI.endShape === endShape)
{
found = true;
cI.pos = pos; // update new pos
cI.orientation = cInfo.orientation;
return cI;
}
}
if (!found)
conn.push(cInfo);
return cInfo;
}
/**
* This function returns saved json format
* @return {Object} saved JSON
*/
Sheet.prototype.save = function()
{
var saved = {};
saved.options = this.options;
saved.shapes = [];
for (var s = 0,sLen = this.shapes.length; s < sLen;s++)
{
var shape = this.shapes[s];
var shapeJson = shape.save();
if (shapeJson) saved.shapes.push(shapeJson);
}
return saved;
}
/**
* This Function removes any connection to the shape. The connection can be incoming or outgoing
* @param {Shape} _shape
* @returns - nothing
*/
Sheet.prototype.removeConnections = function(_shape)
{
//
var beginShapes = _shape.connectedFrom;
var found = false;
var bLen = beginShapes.length;
for (var b = 0; b < eLen ; b++)
{
var beginShape = beginShapes[b];
Utility.Sheet.removeConnection(this,beginShape,_shape);
}
var endShapes = _shape.connectedTo;
var eLen = endShapes.length;
for (var e = 0; e < eLen ; e++)
{
var endShape = endShapes[e];
Utility.Sheet.removeConnection(this,_shape,endShape);
}
return ;
}
/**
* This Function draws or paint all the connecting lines between the Shapes
* This function has undergone changes hence may not be working at this point. Will fix it in the next release
* @returns - nothing
*/
Sheet.prototype.drawConnections = function(_context)
{
var ctx = _context;
if (!_context)
{
var sctx = MagicBoard.sheetBook.scratchCtx;
var scanvas = MagicBoard.sheetBook.scratchCanvas
sctx.clearRect(0,0,scanvas.width,scanvas.height);
/*
var canvas = MagicBoard.sheetBook.connectCanvas;
ctx = MagicBoard.sheetBook.connectCtx;
ctx.clearRect(0,0,canvas.width,canvas.height);
*/
}
/*
var garbage = MagicBoard.sheetBook.garbage;
var children = this.canvas.children;
for (var c = children.length -1;c > -1;c--)
{
var child = children[c];
if (child && child.nodeType === 1 && child.getAttribute("name") == "connection" )
{
garbage.appendChild(child);
}
}
garbage.innerHTML = "";
*/
//ctx.strokeStyle = "rgb(27,141,17)";
//ctx.lineWidth = 2;
//ctx.setLineDash([5, 0]);
var conn = this.connections;
var cLen = conn.length;
var angle = null;
for (var i = 0; i < cLen;i++)
{
var cInfo = conn[i];
/*
var pos = cInfo.pos;
var midX = (pos.x1+pos.x2)/2;
var midY = (pos.y1+pos.y2)/2;
ctx.beginPath();
ctx.moveTo(pos.x1,pos.y1);
if (cInfo.orientation === "vert")
{
ctx.lineTo(pos.x1,midY);
ctx.lineTo(pos.x2,midY);
angle = Drawing.getLineAngle(pos.x2,pos.y2,pos.x2,midY);
} else
{
ctx.lineTo(midX,pos.y1);
ctx.lineTo(midX,pos.y2);
angle = Drawing.getLineAngle(pos.x2,pos.y2,midX,pos.y2);
}
ctx.lineTo(pos.x2,pos.y2);
ctx.stroke();
*/
//var arrowCoord = Drawing.drawArrow(ctx,pos.x2,pos.y2,angle); // arrow for criss cross line
if (cInfo.shape) {
cInfo.shape.deleteShape();
cInfo.shape = null;
}
var cLine = new ConnectorLine(cInfo);
cLine.draw();
}
/*
for (var i = 0; i < cLen;i++)
{
var cInfo = conn[i];
var pos = cInfo.pos;
ctx.beginPath();
ctx.moveTo(pos.x1,pos.y1);
ctx.lineTo(pos.x2,pos.y2); // direct line
//ctx.moveTo(pos.x1,pos.y1);
//ctx.lineTo(pos.x1,pos.y2);
//ctx.lineTo(pos.x2,pos.y2);
ctx.stroke();
var angle = Drawing.getLineAngle(pos.x2,pos.y2,pos.x1,pos.y1);
var arrowCoord = Drawing.drawArrow(ctx,pos.x2,pos.y2,angle); // arrow for direct line
//var angle = Drawing.getLineAngle(pos.x2,pos.y2,pos.x1,pos.y2);
//var arrowCoord = Drawing.drawArrow(ctx,pos.x2,pos.y2,angle); // arrow for criss cross line
}
*/
}
/**
* Point represents 2d coordinate for any point in the space
* @constructor
* @param {Number} _x - represents X Coordinate
* @param {Number} _y - represents Y Coordinate
* @returns - nothing
*/
var Point = function(_x,_y)
{
this.x = _x;
this.y = _y;
}
/**
* This Class is super class for all Shapes that can be drawn
* @constructor
*/
var DrawingObject = function()
{
this.draw = function()
{
}
}
/**
* This Class represents any Shape that can be added to the Sheet.
* @constructor
*/
var Shape = function(_desc) {
this.param = JSON.parse(JSON.stringify(_desc.param));
this.frame = JSON.parse(JSON.stringify(_desc.frame));
if (_desc.connectedFromIds) this.connectedFromIds = _desc.connectedFromIds;
if (_desc.connectedToIds) this.connectedToIds = _desc.connectedToIds;
if (_desc.id) this.id = _desc.id;
this.properties = {};
this.components = [];
var cLen = _desc.componentParms.length;
for (var c = 0; c < cLen;c++)
{
var component = new ShapeComponent(_desc.componentParms[c]);
this.components.push(component);
for (var k in component.properties)
{
this.properties[k] = true;
}
}
this.init();
}
inheritsFrom(Shape,DrawingObject);
Shape.prototype.init = function()
{
if (!this.children) this.children = [];
if (!this.components) this.components = [];
if (!this.param.alignmentRails) this.param.alignmentRails = true;
this.occupiedGrids = [];
this.currentSheet = MagicBoard.sheetBook.getCurrentSheet();
this.sheetCanvas = this.currentSheet.getCanvas();
if (this.frame)
{
this.createCanvas();
}
var dim = {"misc":{"unit":null,"key":[]}}; var borderWidth = null; var strokeStyle = null;var fillStyle = null;
this.dimension = this.frame;
if (!this.id) this.id = this.currentSheet.shapes.length;
this.currentSheet.addShape(this);
this.connectedTo = [];
this.connectedFrom = [];
}
Shape.prototype.createCanvas = function()
{
var width = Utility.Shape.dataFormatter(this.frame.width,"width",this);this.frame.width = width;
var height = Utility.Shape.dataFormatter(this.frame.height,"height",this);this.frame.height = height;
var domParent = document.createElementNS("http://www.w3.org/2000/svg", "g");
this.dom = document.createElementNS("http://www.w3.org/2000/svg", "svg");
this.dom.setAttribute("draggable","false");
this.dom.setAttribute("width",width);
this.dom.setAttribute("height",height);
this.dom.setAttribute("pointer-events","all");
this.dom.setAttribute("style","position:absolute;-ms-user-select:none;-webkit-user-select:none;z-index:10;transform:translate(1.5,1.5);z-index:1");
this.dom.setAttribute("x", Utility.Shape.dataFormatter(this.frame.left,"left",this)) ;
this.dom.setAttribute("y",Utility.Shape.dataFormatter(this.frame.top,"top",this));
domParent.appendChild(this.dom);
this.sheetCanvas.appendChild(domParent);
}
/**
* This Function adds shape component to a shape
* A Shape consists of one or many shape components such as text, rectangle, line, path & ellipse etc
* @param {ShapeComponent} _component
* @returns - nothing
*/
Shape.prototype.addComponent = function(_component)
{
this.components.push(_component);
}
// events
/**
* This Function adds characterstics to shape during hover
* This dictates when mouse goes over and out of a shape
* @returns - nothing
*/
Shape.prototype.addHover = function()
{
var shape = this;
var dom = this.dom;
dom.onmouseover = function () {
MagicBoard.indicators.mouseover.push(shape);
//console.log("mouse over "+event.target+" current "+event.currentTarget);
}
dom.onmouseout = function () {
event.preventDefault();
// find the shape and remove it
for (var i = MagicBoard.indicators.mouseover.length -1;i > -1;i--)
{
if (MagicBoard.indicators.mouseover[i] === shape)
{
MagicBoard.indicators.mouseover.splice(i,1);
return;
}
}
//console.log("mouse out "+event.target+" current "+event.currentTarget);
}
}
/**
* This Function defines action during a single click on a shape
* @returns - nothing
*/
Shape.prototype.click = function()
{
this.selectToggle();
}
/**
* This Function defines action during double click on a shape
* @returns - nothing
*/
Shape.prototype.doubleClick = function()
{
var targetShape = this;
var target = event.target;
if (target instanceof SVGTextElement)
{
var dim = target.getBoundingClientRect();
var editor = MagicBoard.sheetBook.textEditor;
editor.style.left = dim.left - 2;editor.style.top = dim.top - 2;
editor.style.width = dim.width + 4;editor.style.height = dim.height + 4;
editor.style.display = "block";
editor.innerHTML = target.innerHTML;
editor.targetShape = targetShape;
editor.focus();
// updates are reflected in a blur function that was created in Utility.SheetBook.createWorkItems
}
}
/**
* This Function defines behavior of Shape when user is moving it
* @param {Point} pos - contains current mouse position {x,y}
* @param {Point} clickPos - contains start position of the mouse
* @returns - nothing
*/
Shape.prototype.move = function(pos,clickPos)
{
var dim = this.getDimension();
var diffY = pos.y - clickPos.y;
var diffX = pos.x - clickPos.x;
var top = dim.top + diffY; var bottom = dim.bottom + diffY;
var left = dim.left + diffX; var right = dim.right + diffX;
var ctx = MagicBoard.sheetBook.scratchCtx;
var canvas = MagicBoard.sheetBook.scratchCanvas
ctx.clearRect(0,0,canvas.width,canvas.height);
ctx.beginPath();
ctx.strokeStyle = "green";
ctx.setLineDash([2, 2]);
ctx.lineWidth = 4;
ctx.rect(left - 2,top - 2,(dim.width+4),(dim.height+4));
ctx.stroke();
// check for alignments
this.alignmentCheck(ctx,canvas.width,canvas.height,top,left,bottom,right);
// done with the alignments
}
/**
* This Function is invoked from while resizing event (continue) is going on
* It dectates the behavior during resizing continuation
* @param {Point} pos - contains current mouse position {x,y}
* @param {Point} clickPos - contains start position of the mouse
* @returns - nothing
*/
Shape.prototype.resizeContinue = function(pos,clickPos)
{
var dim = this.getDimension();
//var stretcher = MagicBoard.sheetBook.stretcher;
var hilighter = MagicBoard.sheetBook.hilighter;
if (MagicBoard.indicators.resize < 2)
{
var w = this.dimension.width;
var diffX = pos.x - clickPos.x;
var newW = w;
if (MagicBoard.indicators.resize === 0) newW = w-diffX;
else newW = w+diffX;
var zoom = newW/w;
var transformString = "translateX("+diffX/2+"px) scaleX("+zoom+")";
this.dom.style["transform"] = transformString;
hilighter.style["transform"] = transformString;
} else if (MagicBoard.indicators.resize > 1)
{
var h = this.dimension.height;
var diffY = pos.y - clickPos.y;
var newH = h;
if (MagicBoard.indicators.resize === 2) newH = h-diffY;
else newH = h+diffY;
var zoom = newH/h;
var transformString = "translateY("+diffY/2+"px) scaleY("+zoom+")";
this.dom.style["transform"] = transformString;
hilighter.style["transform"] = transformString;
}
}
/**
* This Function dictates the behavior after resize event is finished
* @returns - nothing
*/
Shape.prototype.resizeStop = function()
{
var pos = MagicBoard.indicators.resizeStarted;
var clickPos = MagicBoard.indicators.click;
var newPos = {"x":this.dimension.left,"y":this.dimension.top};
var hilighter = MagicBoard.sheetBook.hilighter;
hilighter.style["visibility"] = "hidden";
hilighter.style["transform"] = "";
this.dom.style["transform"] = "";
if (MagicBoard.indicators.resize < 2)
{
var w = this.dimension.width;
var diffX = pos.x - clickPos.x;
var newW = w;
if (MagicBoard.indicators.resize === 0) {
newW = w-diffX;
newPos.x = pos.x;
}
else newW = w+diffX;
this.dimension.width = newW;
this.dom.setAttribute("width",newW);
} else
{
var h = this.dimension.height;
var diffY = pos.y - clickPos.y;
var newH = h;
if (MagicBoard.indicators.resize === 2) {
newH = h-diffY;
newPos.y = pos.y;
} else newH = h+diffY;
this.dimension.height = newH;
this.dom.setAttribute("height",newH);
}
this.setPosition(newPos);
this.reDraw();
MagicBoard.indicators.resize = -1;
MagicBoard.indicators.resizeStarted = null;
MagicBoard.indicators.hilight = false;
}
/**
* This Function draws a temporary line on scratch canvas from the shape on a sheet
* @param {Point} pos - represents the point where current mouse position is
* @returns - nothing
*/
Shape.prototype.lineTo = function(pos)
{
var dim = this.getDimension();
var ctx = MagicBoard.sheetBook.scratchCtx;
var canvas = MagicBoard.sheetBook.scratchCanvas
ctx.clearRect(0,0,canvas.width,canvas.height);
ctx.beginPath();
ctx.strokeStyle = "gray";
ctx.lineWidth = 1;
ctx.setLineDash([5, 5]);
ctx.moveTo(dim.cx,dim.cy);
ctx.lineTo(pos.x,pos.y);
ctx.stroke();
ctx.setLineDash([5, 0]);
var angle = Drawing.getLineAngle(pos.x,pos.y,dim.left,dim.top);
Drawing.drawArrow(ctx,pos.x,pos.y,angle);
// draw arrow
//
}
// _context could be external canvas context to draw on
/**
* This Function refreshes Connections between shapes
* @param {2D_Context} _context - this is not needed in the current version
* @returns - nothing
*/
Shape.prototype.refreshConnection = function(_context)
{
var currentSheet = this.currentSheet;
var cInfos = [];
var beginShapes = this.connectedFrom;
var bLen = beginShapes.length;
for (var b = 0; b < bLen;b++)
{
var beginShape = beginShapes[b];
var cInfo = Utility.Shape.calculateConnectionPoints(beginShape,this);
currentSheet.addConnections(cInfo);
currentSheet.drawConnections(_context);
}
var endShapes = this.connectedTo;
var eLen = endShapes.length;
for (var e = 0; e < eLen ; e++)
{
endShape = endShapes[e];
var cInfo = Utility.Shape.calculateConnectionPoints(this,endShape);
currentSheet.addConnections(cInfo);
currentSheet.drawConnections( _context);
}
}
/**
* This Function creates an incoming connection from another shape to this shape
* @param {Shape} _beginShape - provide the shape from where the connection is originating
* @returns - nothing
*/
Shape.prototype.connectFrom = function(_beginShape)
{
var beginShapes = this.connectedFrom;
var found = false;
var bLen = beginShapes.length;
for (var b = 0; b < bLen ; b++)
{
var beginShape = beginShapes[b];
if (_beginShape === beginShape) break;
}
if (!found) this.connectedFrom.push(_beginShape);
}
/**
* This Function creates an outgoing connection from this shape to another shape
* @param {Shape} _endShape - provide the shape where the connection is terminating
* @returns - nothing
*/
Shape.prototype.connectTo = function(_endShape)
{
var _beginShape = this; if (this.parentShape) _beginShape = this.parentShape;
if (_endShape.parentShape) _endShape = _endShape.parentShape;
var endShapes = this.connectedTo;
var found = false;
var eLen = endShapes.length;
for (var e = 0; e < eLen ; e++)
{
var endShape = endShapes[e];
if (_endShape === endShape) break;
}
if (!found) _beginShape.connectedTo.push(_endShape);
_endShape.connectFrom(_beginShape);
var cInfo = Utility.Shape.calculateConnectionPoints(_beginShape,_endShape);
var currentSheet = MagicBoard.sheetBook.currentSheet;
currentSheet.addConnections(cInfo);
currentSheet.drawConnections();
}
/**
* This Function is used to create alignment rail
* @param {2D_Context} ctx - this is the context of scratch canvas, this may not be needed in future releases
* @param {Number} cw - width of the shape
* @param {Number} ch - height of the shape
* @param {Number} top - top coordinate of the shape
* @param {Number} left - left coordinate of the shape
* @param {Number} bottom - bottom coordinate of the shape
* @param {Number} right - right coordinate of the shape
* @param {Number} bound - is not currently used
* @returns - nothing
*/
Shape.prototype.alignmentCheck = function(ctx,cw,ch,top,left,bottom,right,bound)
{
ctx.beginPath();
ctx.strokeStyle = "gray";
ctx.lineWidth = 1;
ctx.setLineDash([5, 5]);
if (MagicBoard.sheetBook.alignments.x[top])
{
ctx.moveTo(0,top);
ctx.lineTo(cw,top);
}
if (MagicBoard.sheetBook.alignments.x[bottom])
{
ctx.moveTo(0,bottom);
ctx.lineTo(cw,bottom);
}
if (MagicBoard.sheetBook.alignments.y[left])
{
ctx.moveTo(left,0);
ctx.lineTo(left,ch);
}
if (MagicBoard.sheetBook.alignments.y[right])
{
ctx.moveTo(right,0);
ctx.lineTo(right,ch);
}
ctx.stroke();
ctx.setLineDash([5, 0]);
}
/**
* This Function is used to set left,top position of the shape
* @param {Point} pos - position where left and top will be position
* @param {boolean} align - change the position to align with the nearest alignment within 50px (if exists)
* @returns - nothing
*/
Shape.prototype.setPosition = function(pos,align)
{
if (this.param.alignmentRails)
{
this.removeAlignments();
}
var dim = this.frame;
//if (dim.left === pos.x && dim.top === pos.y) return;
/*
var child = this.dom.firstElementChild;
child.setAttribute("x",pos.x);
child.setAttribute("y",pos.y);
*/
if (align)
{
// find nearest alignment and return coordinate
pos = Utility.Shape.align(pos);
this.dom.setAttribute("x",pos.x );
this.dom.setAttribute("y",pos.y);
} else
{
this.dom.setAttribute("x",pos.x );
this.dom.setAttribute("y",pos.y);
}
dim.left = pos.x;
dim.top = pos.y;
dim.right = dim.left + dim.width;
dim.bottom = dim.top + dim.height;
dim.cx = dim.left + (dim.width)/2;
dim.cy = dim.top + (dim.height)/2;
var edgePoints = {
"c1":{"x":dim.left,"y":dim.top},
"c2":{"x":dim.right,"y":dim.top},
"c3":{"x":dim.right,"y":dim.bottom},
"c4":{"x":dim.left,"y":dim.bottom},
"m12":{"x":dim.cx,"y":dim.top},
"m23":{"x":dim.right,"y":dim.cy},
"m34":{"x":dim.cx,"y":dim.bottom},
"m41":{"x":dim.left,"y":dim.cy}
};
dim.edgePoints = edgePoints;
this.refreshConnection();
if (this.param.alignmentRails)
{
this.addAlignments();
}
if (!this.param.noGridBlock) Utility.Shape.blockGrids(this);
}
/**
* This Function is used to setDimension of the shape
* @param {Object} dim - provide dimension object for the shape
* @returns - nothing
*/
Shape.prototype.setDimension = function(dim)
{
this.dimension = {"left":dim.left,"right":dim.right,"top":dim.top,"bottom":dim.bottom,"width":dim.width,"height":dim.height};
this.addAlignments();
}
/**
* This Function is removes alignment coordinates for this shape so they will not be used for any alignment checks
* @returns - nothing
*/
Shape.prototype.removeAlignments = function()
{
var removeItem = function(_array,_item)
{
if (!_array) return false;
var aLen = _array.length;
if (aLen < 2) {return false;}
for (var i = aLen - 1 ; i > -1 ; i--)
{
var item = _array[i];
if (item === _item)
{
_array.splice(i,1);
return;
}
}
}
var dim = this.dimension;
var ind = removeItem(MagicBoard.sheetBook.alignments.y[dim.left],this) ; if (!ind) MagicBoard.sheetBook.alignments.y[dim.left] = null;
ind = removeItem(MagicBoard.sheetBook.alignments.y[dim.right],this) ;if (!ind) MagicBoard.sheetBook.alignments.y[dim.right] = null;
ind = removeItem(MagicBoard.sheetBook.alignments.y[dim.cx],this) ;if (!ind) MagicBoard.sheetBook.alignments.y[dim.cx] = null;
ind = removeItem(MagicBoard.sheetBook.alignments.x[dim.top],this) ;if (!ind) MagicBoard.sheetBook.alignments.x[dim.top] = null;
ind = removeItem(MagicBoard.sheetBook.alignments.x[dim.bottom],this) ;if (!ind) MagicBoard.sheetBook.alignments.x[dim.bottom] = null;
ind = removeItem(MagicBoard.sheetBook.alignments.x[dim.cy],this) ;if (!ind) MagicBoard.sheetBook.alignments.x[dim.cy] = null;
}
/**
* This Function adds alignment coordinate to sheet memory for any alignment check that other shapes can use
* @returns - nothing
*/
Shape.prototype.addAlignments = function()
{
var dim = this.dimension;
if (!MagicBoard.sheetBook.alignments.y[dim.left]) MagicBoard.sheetBook.alignments.y[dim.left] = [];
MagicBoard.sheetBook.alignments.y[dim.left].push(this);
if (!MagicBoard.sheetBook.alignments.y[dim.right]) MagicBoard.sheetBook.alignments.y[dim.right] = [];
MagicBoard.sheetBook.alignments.y[dim.right].push(this);
if (!MagicBoard.sheetBook.alignments.y[dim.cx]) MagicBoard.sheetBook.alignments.y[dim.cx] = [];
MagicBoard.sheetBook.alignments.y[dim.cx].push(this);
if (!MagicBoard.sheetBook.alignments.x[dim.top]) MagicBoard.sheetBook.alignments.x[dim.top] = [];
MagicBoard.sheetBook.alignments.x[dim.top].push(this);
if (!MagicBoard.sheetBook.alignments.x[dim.bottom]) MagicBoard.sheetBook.alignments.x[dim.bottom] = [];
MagicBoard.sheetBook.alignments.x[dim.bottom].push(this);
if (!MagicBoard.sheetBook.alignments.x[dim.cy]) MagicBoard.sheetBook.alignments.x[dim.cy] = [];
MagicBoard.sheetBook.alignments.x[dim.cy].push(this);
}
/**
* This Function gets the dimension object of the shape
* @returns - {Object} dimension - of the shape
*/
Shape.prototype.getDimension = function(dim)
{
if (!this.dimension) this.setDimension(this.dom.getBoundingClientRect());
return this.dimension;
}
Shape.prototype.applyProperty = function(_propKey,_propName,_propType,_value)
{
//{"attribute":"fill","label":"Background Color","field":"input","values":[{"name":"","value":"#1b8d11","type":"color"}]}
// look for all components that have that property and modify them
for (var c = 0, cLen = this.components.length;c < cLen;c++)
{
var component = this.components[c];
if (component.properties[_propKey]) component.applyProperty(_propName,_propType,_value);
}
};
/**
* This Function returns the underlying HTML Dom for this shape
* @returns - {HTMLElement} - This element can be an SVG element too
*/
Shape.prototype.getDom = function()
{
return this.dom;
}
/**
* This Function is used to set points
* @param {Point} ctx - this is the context of scratch canvas, this may not be needed in future releases
* @returns - nothing
*/
Shape.prototype.setPoints = function(_points)
{
// need to do closed shape check so as not to note down the last point again -- which is begin point as well
this.points = _points;
};
/**
* This Function returns the underlying area of the shape
* under construction
* @returns - nothing
*/
Shape.prototype.getArea = function()
{
}
/**
* This Function deletes the shape
* @returns - nothing
*/
Shape.prototype.deleteShape = function()
{
MagicBoard.sheetBook.currentSheet.removeShape(this);
var garbage = MagicBoard.sheetBook.garbage;
garbage.appendChild(this.dom);
garbage.innerHTML = "";
// delete all objects
for (var k in this)
{
if (typeof(this[k]) === "object")
{
if (k === "connectedTo")
{
var connected = this.connectedTo;
MagicBoard.sheetBook.currentSheet.removeConnections(this);
MagicBoard.sheetBook.currentSheet.drawConnections();
} else if (k === "connectedFrom")
{
MagicBoard.sheetBook.currentSheet.removeConnections(this);
MagicBoard.sheetBook.currentSheet.drawConnections();
}
this[k] = null;
}
}
}
/**
* This Function is used to hilight or remove hilight from the Shape. Usually invoked during click
* @returns - nothing
*/
Shape.prototype.selectToggle = function()
{
var dim = this.getDimension();
var ctx = MagicBoard.sheetBook.scratchCtx;
var canvas = MagicBoard.sheetBook.scratchCanvas
ctx.clearRect(0,0,canvas.width,canvas.height);
// hilight the shape or remove hilite.
var hilighter = MagicBoard.sheetBook.hilighter;
if (MagicBoard.indicators.hilight)
{
hilighter.style["visibility"] = "hidden";
setTimeout(function(){MagicBoard.indicators.hilight = false;},100);
this.addAlignments();
} else
{
var x = dim.left ; var y = dim.top; var parent = this.parentShape;
while (parent)
{
x = x + parent.dimension.left; y = y + parent.dimension.top;
parent = parent.parentShape;
}
Utility.SheetBook.activateHilighter({"left":x,"top":y,"width":dim.width,"height":dim.height});
MagicBoard.indicators.hilight = this;
this.removeAlignments();
}
}
/**
* This Function is used to text component within the shape
* @param {String} _text - new text string
* @param {Number} _index - (optional) sequence of the text to be updated (starts with 0)
* @returns - nothing
*/
Shape.prototype.updateText = function(_text,_index)
{
if (_index == undefined) _index = 0; var counter = 0;
for (var c = 0, cLen = this.components.length;c < cLen;c++)
{
var component = this.components[c];
if (component.type === "text") {
if (counter === _index)
{
component.updateText(_text);
break;
}
counter++;
}
}
}
/**
* This function returns saved json format
* @return {Object} saved JSON
*/
Shape.prototype.save = function()
{
var saved = {};
for (var k in this)
{
if (k === "cInfo") continue;
if (k === "components")
{
saved["componentParms"] = [];
var cLen = this.components.length;
for (var c = 0; c < cLen ; c++)
{
var shapeComponent = this.components[c];
saved["componentParms"].push(shapeComponent.save());
}
continue;
}
if (k === "connectedFrom" || k === "connectedTo")
{
saved[k+"Ids"] = [];
var cLen = this[k].length;
for (var c = 0; c < cLen ; c++)
{
var shape = this[k][c];
saved[k+"Ids"].push(shape.id);
}
continue;
}
var name = this[k].constructor.name
if ( name === "Number" || name === "String" || name === "Object" )
saved[k] = this[k];
}
return saved;
}
/**
* This Function is used to draw or paint the shape
* @returns - nothing
*/
Shape.prototype.draw = function() {
var svg = this.dom; svg.innerHTML = "";
var g = document.createElementNS("http://www.w3.org/2000/svg", "g");
svg.appendChild(g);
this.reDraw(g);
var cLen = this.components.length;
for (var c = 0; c < cLen ; c++)
{
var shapeComponent = this.components[c];
shapeComponent.parentShape = this;
var d = shapeComponent.construct();
if (d) {
g.appendChild(d);
}
}
}
/**
* This Function is used to redraw or repaint the shape on the sheet
* @returns - nothing
*/
Shape.prototype.reDraw = function(g)
{
var first = true;
if (!g) {g = this.dom.firstElementChlid;first = false;}
var cLen = this.components.length;
for (var c = 0; c < cLen ; c++)
{
var shapeComponent = this.components[c];
shapeComponent.parentShape = this;
var d = shapeComponent.construct();
if (first && d) {
g.appendChild(d);
}
}
this.refreshConnection();
}
/**
* This Function is used to paint the shape on HTML Canvas element
* @param {2D_Context} _context - provide 2d context for the canvas
* @returns - nothing
*/
Shape.prototype.drawOnCanvas = function(_context)
{
_context.beginPath();
var cLen = this.components.length;
for (var c = 0; c < cLen ; c++)
{
var shapeComponent = this.components[c];
shapeComponent.drawOnCanvas(_context);
}
_context.stroke();
this.refreshConnection(_context);
}
/**
* Connector Line Class is used draw connection between two shapes
* The class itself inherits from Shape Class
* @constructor
* @param _cInfo - is an object that consists of the following pos (x1,y1,x2,y2)
* - properties
* - param
*/
var ConnectorLine = function(_cInfo)
{
//
var angleStart,angleEnd;
//angleStart = Drawing.getLineAngle(x2,y2,x1,y1),angleEnd = angleStart;
//,"angleStart":angleStart,"angleEnd":angleEnd
//
var arrowLen = 0;
var cLine = this;
var pos = _cInfo.pos;
var x1 = pos.x1; var y1 = pos.y1; var left = x1;var top = y1;
var x2 = pos.x2;var y2 = pos.y2 ; // to compensate for arrow head
if (x2 < x1) left = x2; if (y2< y1) top = y2;
left = left - 10; top = top - 10;
var width = Math.abs(pos.x2 - pos.x1) + 20;
var height = Math.abs(pos.y2 - pos.y1) + 20;
this.frame = {"width":width,"height":height,"unit":"px","left":left,"top":top};
var midX = (pos.x1+pos.x2)/2;
var midY = (pos.y1+pos.y2)/2;
var lines = []; var d = [];
lines.push({"op":"M","x":pos.x1,"y":pos.y1});
d.push({"op":"M","x":(pos.x1 - left)*100/width,"y":(pos.y1 - top)*100/height});
//var dString = "M"+pos.x1 +" "+pos.y1;
var cx1,cx2,cy1,cy2,x1,y1;
this.cInfo = _cInfo;
if (_cInfo.orientation === "vert")
{
lines.push({"op":"L","x":pos.x1,"y":midY});
lines.push({"op":"L","x":x2,"y":midY});
angleStart = Drawing.getLineAngle(x1,y1,x1,midY);
angleEnd = Drawing.getLineAngle(x2,y2,x2,midY);
d.push({"op":"L","x":(pos.x1- left)*100/width,"y":(midY - top)*100/height});
d.push({"op":"L","x":(x2- left)*100/width,"y":(midY - top)*100/height});
//dString += " L"+pos.x1+" "+midY;
//dString += " L"+x2+" "+midY;
if (y2 > pos.y1) { y2 = y2 - arrowLen; cy1 = y1 + arrowLen; cy2 = y2 - arrowLen;}
else { y2 = y2 + arrowLen; cy1 = y1 - arrowLen; cy2 = y2 + arrowLen;}
cx1 = x1 ; cx2 = x2 ;
} else if (_cInfo.orientation === "horizvert")
{
lines.push({"op":"L","x":pos.x2,"y":pos.y1});
angleStart = Drawing.getLineAngle(x1,y1,pos.x2,pos.y1);
angleEnd = Drawing.getLineAngle(x2,y2,pos.x2,pos.y1);
d.push({"op":"L","x":(pos.x2- left)*100/width,"y":(pos.y1 - top)*100/height});
//dString += " L"+pos.x2+" "+pos.y1;
if (y2 > pos.y1) { y2 = y2 - arrowLen; cy1 = y1;cy2 = y2 - arrowLen;}
else {y2 = y2 + arrowLen; cy1 = y1; cy2 = y2 + arrowLen;}
if (x2 > pos.x1) {cx1 = x1 + arrowLen; cx2 = x2 ; }
else {cx1 = x1 - arrowLen; cx2 = x2 ;}
} else
{
lines.push({"op":"L","x":midX,"y":pos.y1});
lines.push({"op":"L","x":midX,"y":y2});
angleStart = Drawing.getLineAngle(x1,y1,midX,pos.y1);
angleEnd = Drawing.getLineAngle(x2,y2,midX,y2);
d.push({"op":"L","x":(midX - left)*100/width,"y":(pos.y1 - top)*100/height});
d.push({"op":"L","x":(midX - left)*100/width,"y":(y2 - top)*100/height});
//dString += " L"+midX+" "+pos.y1;
//dString += " L"+midX+" "+y2;
if (x2 > pos.x1) {x2 = x2 - arrowLen;cx1 = x1 + arrowLen; cx2 = x2 - arrowLen; }
else {x2 = x2 + arrowLen;cx1 = x1 - arrowLen; cx2 = x2 + arrowLen;}
cy1 = y1; cy2 = y2;
}
lines.push({"op":"L","x":x2,"y":y2});
d.push({"op":"L","x":(x2 - left)*100/width,"y":(y2 - top)*100/height});
//dString += " L"+x2+" "+y2;
this.param = {"alignmentRails":false,"noGridBlock":true};
this.components = [];
var conn = {"type":"path","origDim":{},"dimension":{"d":d},"param":{"fill":"none","stroke":"rgb(27,141,17)","stroke-miterlimit":"10","stroke-width":2,"cursor":"hand","marker-end":"url(#fillArrowE)"},"lines":lines,properties:{"line-style":true,"line-type":true,"line-color":true,"start-marker":true,"end-marker":true,"mid-marker":true}}
this.cInfo.angleStart = angleStart; this.cInfo.angleEnd = angleEnd;
if (_cInfo.param) conn.param = _cInfo.param;
if (_cInfo.properties) conn.properties = _cInfo.properties;
var component = new ShapeComponent(conn);
this.components.push(component);
this.properties = component.properties;
var cdom = component.dom;
cdom.onmouseover = function () {
MagicBoard.indicators.mouseover.push(cLine);
var pos = MagicBoard.getPos(event);
var starStyle = MagicBoard.sheetBook.star.style;
starStyle["display"] = "block";starStyle["left"] = pos.x - 15; starStyle["top"] = pos.y - 15;
MagicBoard.sheetBook.star.cLine = cLine;
//console.log("mouse over "+event.target+" current "+event.currentTarget);
}
cdom.onmouseout = function () {
event.preventDefault();
setTimeout(function(){MagicBoard.sheetBook.star.style["display"] = "none";},800);
// find the shape and remove it
for (var i = MagicBoard.indicators.mouseover.length -1;i > -1;i--)
{
if (MagicBoard.indicators.mouseover[i] === cLine)
{
MagicBoard.indicators.mouseover.splice(i,1);
return;
}
}
}
this.cx1 = cx1; this.cx2 = cx2;this.cy1 = cy1;this.cy2 = cy2;
this.init();
_cInfo.shape = this;
_cInfo.properties = component.properties;
_cInfo.param = component.param;
}
inheritsFrom(ConnectorLine,Shape);
/*
Override functions
*/
ConnectorLine.prototype.save = function()
{
// do not save connectors, let it rebuild
return null;
}
ConnectorLine.prototype.drawOnCanvas = function(_context)
{
_context.beginPath();
var cLen = this.components.length;
for (var c = 0; c < cLen ; c++)
{
var shapeComponent = this.components[c];
shapeComponent.drawOnCanvas(_context);
if (shapeComponent.param["marker-end"])
{
var type = shapeComponent.param["marker-end"];
if (type === "url(#fillArrowE)") type = "Filled";
else if (type === "url(#hollowArrowE)") type = "Hollow";
else if (type === "url(#hollowDiamond)") type = "DiamondHollow";
else if (type === "url(#fillDiamond)") type = "DiamondFilled";
else type = "Regular";
var lines = shapeComponent.lines; var last = lines[lines.length - 1];
// hoping that first of line is lineTo if not we have to fix it in future
Drawing.drawArrow(_context,(last.x + this.dimension.left ),(last.y + this.dimension.top),this.cInfo.angleEnd,type,shapeComponent.param.fill,shapeComponent.param.stroke);
}
if (shapeComponent.param["marker-start"])
{
var type = shapeComponent.param["marker-start"];
if (type === "url(#fillArrowS)") type = "Filled";
else if (type === "url(#hollowArrowS)") type = "Hollow";
else if (type === "url(#hollowDiamond)") type = "DiamondHollow";
else if (type === "url(#fillDiamond)") type = "DiamondFilled";
else type = "Regular";
var lines = shapeComponent.lines; var first = lines[0];
// hoping that first of line is Move
Drawing.drawArrow(_context,(first.x + this.dimension.left ),(first.y + this.dimension.top),this.cInfo.angleStart,type,shapeComponent.param.fill,shapeComponent.param.stroke);
}
}
_context.stroke();
}
/*
ConnectorLine.prototype.click = function()
{
if (MagicBoard.indicators.hilight)
{
MagicBoard.indicators.hilight = false;
var circles = this.dom.getElementsByTagName("circle");
var garbage = MagicBoard.sheetBook.garbage;
for (var i = circles.length - 1;i > -1;i--) {garbage.appendChild(circles[i]);}
garbage.innerHTML = "";
} else
{
MagicBoard.indicators.hilight = this;
var startDot = document.createElementNS("http://www.w3.org/2000/svg", "circle");
startDot.setAttribute("cx",this.cx1);
startDot.setAttribute("cy",this.cy1);
startDot.setAttribute("r","7");
startDot.setAttribute("style","fill:red;visibility:visible");
this.dom.appendChild(startDot);
var endDot = document.createElementNS("http://www.w3.org/2000/svg", "circle");
endDot.setAttribute("cx",this.cx2);
endDot.setAttribute("cy",this.cy2);
endDot.setAttribute("r","7");
endDot.setAttribute("style","fill:red;visibility:visible");
this.dom.appendChild(endDot);
}
}
*/
/**
* This Function overrides deleteShape function from parent Shape Class to delete the connection
* @returns - nothing
*/
ConnectorLine.prototype.deleteShape = function()
{
MagicBoard.sheetBook.currentSheet.removeShape(this);
var garbage = MagicBoard.sheetBook.garbage;
garbage.appendChild(this.dom);
garbage.innerHTML = "";
// delete all objects
for (var k in this)
{
if (typeof(this[k]) === "object")
{
this[k] = null;
}
}
}
/**
* ShapeComponent Class - represents individual component for a shape. A Shape can contain many shape components
* such as - line, rectangle, polygon, circle, path etc.
* @constructor
* @param {object} _desc - is object that has the following structure
* {"dimension":{}, "param":{}, "properties":{}}
*/
var ShapeComponent = function(_desc) {
for (var k in _desc)
{
this[k] = _desc[k];
}
this.parentShape = null;
this.dom = document.createElementNS("http://www.w3.org/2000/svg", this.type);
for (var k in this.param)
{
if (k === "border-radius")
{
this.dom.setAttribute("rx",this.param[k]);
this.dom.setAttribute("ry",this.param[k]);
} else
this.dom.setAttribute(k,this.param[k]);
}
if (this.innerHTML) this.dom.innerHTML = this.innerHTML;
this.dom.setAttribute("pointer-events","all");
this.dom.setAttribute("draggable","false");
this.dom.setAttribute("transform","translate(1,1)");
this.derivedDimension = {};
if (!this.properties)
{
switch (this.type)
{
case "text":
this.properties = { "text-color":true,"font-size":true,"font-weight":true,"text-content":true};
break;
case "path":
this.properties = { "background-color":true, "line-color":true,"line-width":true,"line-style":true};
break;
default:
this.properties = { "background-color":true, "border-color":true,"border-width":true,"border-style":true};
break;
}
}
}
/**
* This will paint the component. This method is invoked from Shape.draw
* @return {HTMLElement} dom
*/
ShapeComponent.prototype.construct = function()
{
if (!this.parentShape) return;
var dom = this.dom;
this.calculateDimensions();
for (var k in this.derivedDimension)
{
this.dom.setAttribute(k,this.derivedDimension[k]);
}
if (this.type === "image")
{
var href = this.xlink;
this.dom.setAttributeNS("http://www.w3.org/1999/xlink","href",href);
//.setAttributeNS('http://www.w3.org/1999/xlink','href', 'myimage.jpg');
}
return dom;
}
/**
* This function returns saved json format
* @return {Object} saved JSON
*/
ShapeComponent.prototype.save = function()
{
var saved = {};
for (var k in this)
{
var name = this[k].constructor.name
if ( name === "String" || name === "Object" )
saved[k] = this[k];
}
return saved;
}
/**
* This function converts the % dimensions to real pixel
* @return nothing
*/
ShapeComponent.prototype.calculateDimensions = function()
{
//if (!this.dimensions) return; // no need to calculate it
var margin = 0;
if (this.param["stroke-width"])
{
margin = parseInt(this.param["stroke-width"]);
}
pw = this.parentShape.frame.width -2*margin;
ph = this.parentShape.frame.height -2*margin;
for (var k in this.dimension)
{
var val = "";
var percentVal = this.dimension[k]
switch (k)
{
case "width":
val = (percentVal * pw / 100) - 2 * margin;
break;
case "x":
case "x1":
case "x2":
case "cx":
val = (percentVal * pw / 100) + margin;
break;
case "rx":
case "r":
val = (percentVal * pw / 100) - margin;
break;
case "height":
val = (percentVal * ph / 100) - 2*margin;
break;
case "y":
case "y1":
case "y2":
case "cy":
val = (percentVal * ph / 100) + margin;
break;
case "ry":
val = (percentVal * ph / 100) - margin;
break;
case "d":
// put path logic here
if (!this.lines) this.lines = [];
var dArray = percentVal;var dLen = dArray.length;
for (var i = 0; i < dLen ;i++)
{
var item = dArray[i];
if (item.op.toUpperCase() === "Z") {
val += " Z ";
break;
}
//val += item.op.toUpperCase() +Math.round( item.x * pw / 100)+" "+Math.round( item.y * ph / 100)+" ";
for (var ik in item)
{
var vl = item[ik];
if (ik.indexOf("x") > -1) vl = Math.round( item[ik] * pw / 100) ;
else if (ik.indexOf("y") > -1) vl = Math.round( item[ik] * ph / 100) ;
this.lines[i][ik] = vl;
val += vl+" ";
}
}
break;
}
if (val) this.derivedDimension[k] = val;
}
if (this.type === "text" || this.type === "rect")
{
if (this.dimension.cx)
{
var x = parseInt(dom.getAttribute("cx")) - parseInt(dom.getAttribute("width"))/2;
this.derivedDimension.x = x;
}
if (this.dimension.cy)
{
var y = parseInt(dom.getAttribute("cy")) - parseInt(dom.getAttribute("height"))/2;
this.derivedDimension.y = y;
}
}
}
/**
* This function is used to convert svg to canvas
* @return {HTMLElement} - dom
*/
ShapeComponent.prototype.drawOnCanvas = function(_context)
{
var left = this.parentShape.dimension.left;
var top = this.parentShape.dimension.top;
var margin = 0;
if (this.param["stroke-width"])
{
margin = parseInt(this.param["stroke-width"]);
}
var x = this.derivedDimension.x + left;
var y = this.derivedDimension.y + top;
var fill = this.param["fill"];
if (fill === "none") fill = "";
var dashSet = false;
if (this.param["stroke-dasharray"]) {
var str = this.param["stroke-dasharray"]; var arr = str.split(",");
_context.setLineDash(arr); dashSet = true;
//setLineDash([1, 15]);
}
switch (this.type)
{
case "rect":
if (fill)
{
_context.fillRect(x,y,this.derivedDimension.width,this.derivedDimension.height,margin);
} else _context.strokeRect(x,y,this.derivedDimension.width,this.derivedDimension.height,margin);
break;
case "circle":
break;
case "ellipse":
var cx = this.derivedDimension.cx + left;
var cy = this.derivedDimension.cy + top;
Drawing.drawEllipse(_context, cx , cy , this.derivedDimension.rx , this.derivedDimension.ry, fill, this.param["stroke"] );
break;
case "path":
Drawing.drawLines(_context,left,top,this.derivedDimension.d,fill,this.param["stroke"],margin );
break;
case "polyline":
break;
case "text":
if (fill) _context.fillStyle = fill;
if (this.param["stroke"] ) _context.strokeStyle = this.param["stroke"] ;
_context.fillText(this.innerHTML,x,y);
break;
}
if (dashSet) _context.setLineDash([]);
}
/**
* This function applies any changes to component property
* @return nothing
*/
ShapeComponent.prototype.applyProperty = function(_name,_type,_value)
{
if (_type === "attribute")
{
if (!_value)
{
this.dom.removeAttribute(_name);
delete this.param[_name];
} else {
this.dom.setAttribute(_name,_value);
this.param[_name] = _value;
}
} else if (_type === "dom")
{
this.dom.innerHTML = _value;
}
}
/**
* This function is used to update any text. Only applies if component type is text
* @param {String} _text
*/
ShapeComponent.prototype.updateText = function(_text)
{
this.innerHTML = _text;
this.dom.innerHTML = _text;
}
/**
* This Class was originally created to draw extra shapes but it is not used in this version currently
* @namespace Drawing
*/
var Drawing = {};
/**
* This Function returns angle between two points
* @static
* @param {Number} x2
* @param {Number} y2
* @param {Number} x1
* @param {Number} y1
* @returns - {Number} angle in radian
*/
Drawing.getLineAngle = function(x2,y2,x1,y1)
{
return Math.atan2(y2-y1,x2-x1);
}
/**
* This Function coordinates for arrow head
* @static
* @param {Number} _x
* @param {Number} _y
* @param {Number} _angle in radian
* @returns - {Object} - containing coodinates {x1,x2,y1,y2}
*/
Drawing.getArrowHead = function(_x,_y,_angle)
{
var headlen = 10; // length of head in pixels
//var angle = Math.atan2(yMid-y1,xMid-x1);
var x1,x2,y1,y2;
x1 = _x-headlen*Math.cos(_angle-Math.PI/6);
y1 = _y-headlen*Math.sin(_angle-Math.PI/6);
x2 = _x-headlen*Math.cos(_angle+Math.PI/6);
y2 = _y-headlen*Math.sin(_angle+Math.PI/6);
return {x1:x1,x2:x2,y1:y1,y2:y2};
}
/**
* This Function draws arrow head into canvas for a given coordinates
* @static
* @param {2D_Context} _cntx
* @param {Number} _x
* @param {Number} _y
* @param {Number} _angle
* @param {String} _type (type of arrows - Regular, Filled, Hollow, DiamondFilled, DiamondHollow, Dot
* @param {Color} _fillColor
* @param {Color} _strokeColor
* @returns - {Object} - containing coordinates {x1,x2,y1,y2}
*/
Drawing.drawArrow = function(_cntx,_x,_y,_angle,_type,_fillColor,_strokeColor)
{
if (!_type) _type = "Regular";
if (!_fillColor || _fillColor === "none") _fillColor = "";
var headlen = 15; // length of head in pixels
//var angle = Math.atan2(yMid-y1,xMid-x1);
var x1,x2,y1,y2;
x1 = _x-headlen*Math.cos(_angle-Math.PI/6);
y1 = _y-headlen*Math.sin(_angle-Math.PI/6);
x2 = _x-headlen*Math.cos(_angle+Math.PI/6);
y2 = _y-headlen*Math.sin(_angle+Math.PI/6);
_cntx.beginPath();
if (_strokeColor) _cntx.strokeStyle = _strokeColor;
switch (_type)
{
case "Regular":
_cntx.moveTo(_x, _y);
_cntx.lineTo(x1,y1);
_cntx.moveTo(_x, _y);
_cntx.lineTo(x2,y2);
break;
case "Filled":
_cntx.moveTo(_x, _y);
_cntx.lineTo(x1,y1);
_cntx.lineTo(x2,y2);
_cntx.lineTo(_x, _y);
if (!_fillColor && _strokeColor) _fillColor = _strokeColor;
_cntx.fillStyle = _fillColor;
_cntx.fill();
break;
case "Hollow":
_cntx.moveTo(_x, _y);
_cntx.lineTo(x1,y1);
_cntx.lineTo(x2,y2);
_cntx.lineTo(_x, _y);
break;
case "DiamondFilled":
var y3 = y1 + y2 - _y; var x3 = x1 + x2 - _x;
_cntx.moveTo(_x, _y);
_cntx.lineTo(x1,y1);
_cntx.lineTo(x3,y3);
_cntx.lineTo(x2,y2);
_cntx.lineTo(_x, _y);
if (!_fillColor && _strokeColor) _fillColor = _strokeColor;
_cntx.fillStyle = _fillColor;
_cntx.fill();
break;
case "DiamondHollow": // to be done later
var y3 = y1 + y2 - _y; var x3 = x1 + x2 - _x;
_cntx.moveTo(_x, _y);
_cntx.lineTo(x1,y1);
_cntx.lineTo(x3,y3);
_cntx.lineTo(x2,y2);
_cntx.lineTo(_x, _y);
break;
case "Dot": // to be done later
var xMid = (x2-x1), yMid = (y2-y1),cx,cy,r;
_cntx.moveTo(_x, _y);
_cntx.lineTo(x1,y1);
_cntx.lineTo(x2,y2);
_cntx.moveTo(_x, _y);
_cntx.fillStyle = _fillColor;
_cntx.fill();
break;
}
_cntx.stroke();
return {x1:x1,x2:x2,y1:y1,y2:y2};
}
/**
* This Function draws rounded corner rectangle into canvas context
* @static
* @param {2D_Context} cntx
* @param {Number} x
* @param {Number} y
* @param {Number} width
* @param {Number} height
* @param {Number} radius
* @param {Color} fill
* @param {Color} stroke
* @returns - nothing
*/
Drawing.roundRect = function(ctx, x, y, width, height, radius, fill, stroke) {
if (typeof stroke == 'undefined') {
stroke = true;
}
if (typeof radius === 'undefined') {
radius = 5;
}
if (typeof radius === 'number') {
radius = {tl: radius, tr: radius, br: radius, bl: radius};
} else {
var defaultRadius = {tl: 0, tr: 0, br: 0, bl: 0};
for (var side in defaultRadius) {
radius[side] = radius[side] || defaultRadius[side];
}
}
ctx.beginPath();
ctx.moveTo(x + radius.tl, y);
ctx.lineTo(x + width - radius.tr, y);
ctx.quadraticCurveTo(x + width, y, x + width, y + radius.tr);
ctx.lineTo(x + width, y + height - radius.br);
ctx.quadraticCurveTo(x + width, y + height, x + width - radius.br, y + height);
ctx.lineTo(x + radius.bl, y + height);
ctx.quadraticCurveTo(x, y + height, x, y + height - radius.bl);
ctx.lineTo(x, y + radius.tl);
ctx.quadraticCurveTo(x, y, x + radius.tl, y);
ctx.closePath();
if (fill) {
ctx.fill();
}
if (stroke) {
ctx.stroke();
}
}
/**
* This Function draws ellipse into canvas context
* @static
* @param {2D_Context} _ctx
* @param {Number} _cx center of ellipse
* @param {Number} _cy center of ellipse
* @param {Number} _rx x radius of ellipse
* @param {Number} _ry y radius of ellipse
* @param {Color} _fill
* @param {Color} _stroke
* @returns - nothing
*/
Drawing.drawEllipse = function(_ctx, _cx , _cy , _rx , _ry, _fill, _stroke ) {
var x = _cx - _rx;
var y = _cy - _ry;
var w = _rx * 2;
var h = _ry * 2;
var kappa = .5522848,
ox = _rx * kappa, // control point offset horizontal
oy = _ry * kappa, // control point offset vertical
xe = x + w, // x-end
ye = y + h; // y-end
_ctx.beginPath();
_ctx.moveTo(x, _cy);
_ctx.bezierCurveTo(x, _cy - oy, _cx - ox, y, _cx, y);
_ctx.bezierCurveTo(_cx + ox, y, xe, _cy - oy, xe, _cy);
_ctx.bezierCurveTo(xe, _cy + oy, _cx + ox, ye, _cx, ye);
_ctx.bezierCurveTo(_cx - ox, ye, x, _cy + oy, x, _cy);
//ctx.closePath(); // not used correctly, see comments (use to close off open path)
if (_fill) {
_ctx.fillStyle = _fill;
_ctx.fill();
}
if (_stroke) {
_ctx.strokeStyle = _stroke;
}
_ctx.stroke();
}
/**
* This Function draws multiple lines into canvas context
* @static
* @param {2D_Context} _ctx
* @param {Number} _offsetX - offset any X coordinate by this number
* @param {Number} _offsetY - offset any Y coordinate by this number
* @param {String} _d contains d parameter as used in SVG Path
* @param {Color} _fill
* @param {Color} _stroke
* @returns - nothing
*/
Drawing.drawLines = function(_ctx,_offsetX,_offsetY,_d,_fill,_stroke)
{
_ctx.beginPath();
if (_stroke) {
_ctx.strokeStyle = _stroke;
}
var words = _d.split(" ");
for (var w = 0, wLen = words.length;w < wLen;w++)
{
var letter = words[w++];
switch (letter)
{
case "M":
var x = parseInt(words[w++])+_offsetX, y = parseInt(words[w])+_offsetY;
_ctx.moveTo(x,y);
break;
case "L":
var x = parseInt(words[w++])+_offsetX, y = parseInt(words[w])+_offsetY;
_ctx.lineTo(x,y);
break;
}
}
if (_fill) {
_ctx.fillStyle = _fill;
_ctx.fill();
}
_ctx.stroke();
}
// --- base.js ends here
// Global Mouse Events
document.addEventListener("dragstart", function( event ) {
if (event.target.nodeType === 3)
{
event.preventDefault();
event.stopPropagation();
}
}, false);
/** @namespace MagicBoard **/
/**
* This function is used to trap any mouse start event
* @param {Event} e
*/
MagicBoard.eventStart = function(e)
{
MagicBoard.indicators.mouseDown = true;
var pos = MagicBoard.getPos(e);
MagicBoard.indicators.click = pos; var mLen = MagicBoard.indicators.mouseover.length;
if (!MagicBoard.indicators.hilight && mLen > 0)
{
mLen--;
var beginShape = MagicBoard.indicators.mouseover[mLen];
while (beginShape.parentShape) beginShape = MagicBoard.indicators.mouseover[mLen--];
if (beginShape.parentShape) beginShape = beginShape.parentShape;
MagicBoard.indicators.lineActive = beginShape;
//console.log("start b-"+beginShape.id);
} else
{
/*
if (MagicBoard.indicators.resize > -1)
{
//_sheetBook.stretcher.
var shape = MagicBoard.indicators.hilight;
var stretcher = MagicBoard.sheetBook.stretcher;
var sStyle = stretcher.style; sStyle.width = shape.dimension.width; sStyle.height = shape.dimension.height;sStyle.visibility = "visible";
var sCanvas = shape.dom.firstElementChild;
sCanvas.style.visibility = "hidden";
stretcher.src = sCanvas.toDataURL();
shape.dom.appendChild(stretcher);
}
*/
}
MagicBoard.indicators.doubleClick++;
setTimeout(function() {MagicBoard.indicators.doubleClick--;},800) ; // turn off doubleclick;
}
/**
* This function is used to trap any mouse down continuation event
* @param {Event} e
*/
MagicBoard.eventContinue = function(e)
{
if (MagicBoard.indicators.mouseDown)
{
var clickPos = MagicBoard.indicators.click;
var shape = null;
var pos = MagicBoard.getPos(e);
shape = MagicBoard.indicators.hilight;
if (shape)
{
if (MagicBoard.indicators.resize > -1)
{
shape.resizeContinue(pos,clickPos);
MagicBoard.indicators.resizeStarted = pos;
} else
{
MagicBoard.indicators.moveStarted = pos;
shape.move(pos,clickPos);
}
} else
{
shape = MagicBoard.indicators.lineActive;
if (shape)
{
shape.lineTo(pos);
}
}
}
}
/**
* This function is used to trap any mouse movement stop event
* @param {Event} e
*/
MagicBoard.eventStop = function(e)
{
MagicBoard.indicators.mouseDown = false;
if (MagicBoard.indicators.moveStarted)
{
// reposition the shape here
var shape = MagicBoard.indicators.hilight;
var pos = MagicBoard.indicators.moveStarted; var clickPos = MagicBoard.indicators.click;
var dim = shape.getDimension();
var diffX = pos.x - clickPos.x;var diffY = pos.y - clickPos.y;
var top = dim.top + diffY; var left = dim.left + diffX;
shape.setPosition({"x":left,"y":top});
//Utility.Shape.calculateConnectionPoints
MagicBoard.indicators.moveStarted = null;
shape.selectToggle();
} else
{
var mLen = MagicBoard.indicators.mouseover.length;
if ( mLen > 0)
{
// it could be single click
mLen--;
var endShape = MagicBoard.indicators.mouseover[mLen];
while (endShape.parentShape) endShape = MagicBoard.indicators.mouseover[mLen--];
var beginShape = MagicBoard.indicators.lineActive;
//console.log("stop b-"+beginShape.id);
//console.log("stop e-"+endShape.id);
if (MagicBoard.indicators.resize > -1)
{
var shape = MagicBoard.indicators.hilight;
shape.resizeStop();
}
else if ( !beginShape || beginShape === endShape || beginShape === endShape.parentShape)
{
endShape.click();
}
else
{
beginShape.connectTo(endShape);
}
} else if (MagicBoard.indicators.lineActive)
{
// clear scratch canvas
var ctx = MagicBoard.sheetBook.scratchCtx;
var canvas = MagicBoard.sheetBook.scratchCanvas
ctx.clearRect(0,0,canvas.width,canvas.height);
} else if (MagicBoard.indicators.resize > -1)
{
shape = MagicBoard.indicators.hilight;
shape.resizeStop();
}
}
MagicBoard.indicators.lineActive = null;
MagicBoard.indicators.click = null;
if (MagicBoard.indicators.doubleClick > 1)
{
var mLen = MagicBoard.indicators.mouseover.length;
if ( mLen > 0)
{
for (var m = 0 ; m < mLen;m++ )
{
var shape = MagicBoard.indicators.mouseover[m];
shape.doubleClick();
}
}
}
}
/**
* This function is used to trap a few keys such as "delete", control+z etc.
* @param {Event} e
*/
MagicBoard.keyUp = function(e)
{
// check to see if it is delete key
var keycode = e.which;
if (e.ctrlKey)
MagicBoard.indicators.keyType = "ctrlKey";
else if (e.shiftKey)
MagicBoard.indicators.keyType = "shiftKey";
switch (keycode)
{
case 8: // delete or backspace
case 46:
// delete selected object
if (MagicBoard.indicators.hilight)
{
var shape = MagicBoard.indicators.hilight;
shape.click(); // force a click to remove hilight
shape.deleteShape(); // then delete the shape
}
case 90: // z
case 122: // Z
if (MagicBoard.indicators.keyType === "ctrlKey")
MagicBoard.sheetBook.currentSheet.undo();
default:
return;
}
}
/**
* This function is used to trap any mouse position
* @param {Event} e
* @return {Point} pos
*/
MagicBoard.getPos = function(e) {
var x; var xOff = 0;
var y; var yOff = 0;
if (e.pageX || e.pageY) {
x = e.pageX - document.body.scrollTop;
y = e.pageY - document.body.scrollTop;
}
else {
x = e.clientX + document.body.scrollLeft +
document.documentElement.scrollLeft;
y = e.clientY + document.body.scrollTop +
document.documentElement.scrollTop;
}
return {x: (x - xOff - MagicBoard.boardPos.x), y:(y-yOff - MagicBoard.boardPos.y)};
}
/** @namespace Utility **/
var Utility = {"Shape":{},"Sheet":{},"SheetBook":{}};
/**
* This Utility function is for internal use
* @static
* @param {SheetBook} _sheetBook
*/
Utility.SheetBook.createWorkItems = function(_sheetBook)
{
_sheetBook.connectCanvas = document.createElement("canvas");_sheetBook.connectCanvas.setAttribute("style","position:absolute;left:0px;top:0px;z-index:1;"); this.connectCtx = null;
_sheetBook.connectCanvas.setAttribute("name","workItem");
_sheetBook.connectCanvas.height = _sheetBook.cheight; _sheetBook.connectCanvas.width = _sheetBook.cwidth;
//this.anchor.appendChild(this.connectCanvas);
_sheetBook.connectCtx = _sheetBook.connectCanvas.getContext("2d");
_sheetBook.connectCtx.translate(0.5,0.5);
_sheetBook.scratchCanvas = document.createElement("canvas");_sheetBook.scratchCanvas.setAttribute("style","position:absolute;left:0px;top:0px;z-index:1;"); _sheetBook.scratchCtx = null;
_sheetBook.scratchCanvas.setAttribute("name","workItem");
// work on the canvas
_sheetBook.scratchCanvas.height = _sheetBook.cheight; _sheetBook.scratchCanvas.width = _sheetBook.cwidth;
//this.anchor.appendChild(this.scratchCanvas);
_sheetBook.scratchCtx = _sheetBook.scratchCanvas.getContext("2d");
_sheetBook.scratchCtx.translate(0.5,0.5);
/*
_sheetBook.hilighter = document.createElementNS("http://www.w3.org/2000/svg", "svg");
var rect = "<rect x=\"5\" y=\"5\" width=\"120\" height=\"80\" fill-opacity=\"0\" style=\"stroke-width:5;stroke:rgb(27,141,17)\" ></rect>";
_sheetBook.hilighter.innerHTML = rect;
_sheetBook.hilighter.setAttribute("style","visibility:hidden;height:"+_sheetBook.cheight+"px;width:"+_sheetBook.cwidth+"px;z-index:10;");
_sheetBook.hilighter.setAttribute("name","workItem");
*/
_sheetBook.hilighter = document.createElement("div");
_sheetBook.hilighter.setAttribute("style","visibility:hidden;height:100px;width:100px;left:0px;top:0px;z-index:10");
_sheetBook.hilighter.setAttribute("class","hilight");
_sheetBook.hilighter.onmouseover = function()
{
if (MagicBoard.indicators.hilight)
MagicBoard.indicators.mouseover.push(MagicBoard.indicators.hilight); // push the hilighted shape
}
_sheetBook.hilighter.onmouseout = function () {
event.preventDefault();
MagicBoard.indicators.mouseover = [];
}
_sheetBook.hilighter.innerHTML = "<div class='stretcher'><div class='red'></div></div>" +
"<div class='stretcher'><div class='red'></div></div>" +
"<div class='stretcher'><div class='red'></div></div>" +
"<div class='stretcher'><div class='red'></div></div>" +
"<div class='clickPrompt' onclick='Utility.Shape.showProperty()'></div>"
;
var children = _sheetBook.hilighter.children;
var mOut = function() {
if (! MagicBoard.indicators.mouseDown) MagicBoard.indicators.resize = -1;
}
children[0].onmouseover = function() {if (! MagicBoard.indicators.mouseDown) MagicBoard.indicators.resize = 0;}
children[0].onmouseout = mOut;
children[1].onmouseover = function() {if (! MagicBoard.indicators.mouseDown) MagicBoard.indicators.resize = 1;}
children[1].onmouseout = mOut;
children[2].onmouseover = function() {if (! MagicBoard.indicators.mouseDown) MagicBoard.indicators.resize = 2;}
children[2].onmouseout = mOut;
children[3].onmouseover = function() {if (! MagicBoard.indicators.mouseDown) MagicBoard.indicators.resize = 3;}
children[3].onmouseout = mOut;
_sheetBook.textEditor = document.createElement("div");
_sheetBook.textEditor.setAttribute("contenteditable","true");
_sheetBook.textEditor.setAttribute("style","position:absolute;left:0px;top:0px;width:10px;height:14px;display:none;background:white;z-index:100");
document.body.appendChild(_sheetBook.textEditor);
_sheetBook.textEditor.onblur = function()
{
_sheetBook.textEditor.style.display = "none";
var targetShape = _sheetBook.textEditor.targetShape;
targetShape.updateText(_sheetBook.textEditor.innerHTML);
/*
var owner = Utility.Sheet.findOwner(target);
owner.innerHTML = _sheetBook.textEditor.innerHTML;
target.innerHTML = _sheetBook.textEditor.innerHTML;
*/
targetShape.selectToggle(); // remove any hilites
_sheetBook.textEditor.targetShape = null;
}
// create mouseover star
_sheetBook.star = document.createElementNS("http://www.w3.org/2000/svg", "svg");
_sheetBook.star.setAttribute("style","display:none;position:absolute;left:0px;top:0px;z-index:10;width:50px;height:21px");
_sheetBook.star.innerHTML = "<polygon points=\"10,1 4,20 19,8 1,8 16,20\" style=\"fill:red;stroke:red;stroke-width:1;fill-rule:nonzero;\" />";
_sheetBook.star.onclick = function()
{
var cLine = _sheetBook.star.cLine;
if (cLine)
{
cLine.click();
}
}
// attach it to the anchor
Utility.SheetBook.attachWorkItems(_sheetBook);
/*
//this.anchor.appendChild(this.hilighter);
_sheetBook.stretcher = document.createElement("img");
_sheetBook.stretcher.setAttribute("style","z-index:10;position:absolute;left:0px;top:0px;height:100px;width:100px;visibility:hidden");
_sheetBook.stretcher.setAttribute("name","workItem");
*/
}
/**
* This Utility function is for internal use
* @static
* @param {SheetBook} _sheetBook
*/
Utility.SheetBook.attachWorkItems = function(_sheetBook)
{
//var sheetBook = MagicBoard.sheetBook;
//var sheetCanvas = _sheet.canvas;
sheetCanvas = _sheetBook.anchor;
sheetCanvas.appendChild(_sheetBook.scratchCanvas);
sheetCanvas.appendChild(_sheetBook.connectCanvas);
sheetCanvas.appendChild(_sheetBook.hilighter);
sheetCanvas.appendChild(_sheetBook.star);
}
/**
* This Utility function is for activating hilighter around a shape
* @static
* @param {Object} _dimension
*/
Utility.SheetBook.activateHilighter = function(_dimension)
{
var x = _dimension.left - 4; var y = _dimension.top - 4;
var w = _dimension.width + 8; var h = _dimension.height + 8;
var hilighter = MagicBoard.sheetBook.hilighter;
hilighter.style["visibility"] = "visible";
hilighter.style.left = x +"px";hilighter.style.top = y +"px";
hilighter.style.width = w +"px";hilighter.style.height = h+"px";
var children = hilighter.children;
var s1 = children[0].style; s1.left = "-12px"; s1.top = (h/2 - 12)+"px";s1.cursor = "ew-resize";
var s2 = children[1].style; s2.left = (w-14)+"px"; s2.top = (h/2 - 12)+"px";s2.cursor = "ew-resize";
var s3 = children[2].style; s3.left = (w/2 - 12)+"px"; s3.top = "-12px";s3.cursor = "ns-resize";
var s4 = children[3].style; s4.left = (w/2 - 12)+"px"; s4.top = (h - 14)+"px";s4.cursor = "ns-resize";
}
/**
* This Utility function is for internal use
* @static
* @param {Sheet} _sheet
*/
Utility.Sheet.Markers = function(_sheet)
{
var defString = "<defs>"
+"<marker id='fillArrowE' markerWidth='10' markerHeight='10' refx='8' refy='3' orient='auto' markerUnits='strokeWidth' ><path d='M0,0 L0,6 L9,3 Z' fill='rgb(27,141,17)'></path></marker>"
+"<marker id='fillArrowS' markerWidth='10' markerHeight='10' refx='0' refy='3' orient='auto' markerUnits='strokeWidth' ><path d='M0,3 L9,0 L9,6 Z' fill='rgb(27,141,17)'></path></marker>"
+"<marker id='hollowArrowE' markerWidth='10' markerHeight='10' refx='8' refy='3' orient='auto' markerUnits='strokeWidth' ><path d='M0,0 L0,6 L9,3 Z' fill='white' stroke='rgb(27,141,17)' stroke-width='1'></path></marker>"
+"<marker id='hollowArrowS' markerWidth='10' markerHeight='10' refx='0' refy='3' orient='auto' markerUnits='strokeWidth' ><path d='M0,3 L9,0 L9,6 Z' fill='white' stroke='rgb(27,141,17)' stroke-width='1'></path></marker>"
+"<marker id='hollowDiamond' markerWidth='10' markerHeight='10' refx='0' refy='3' orient='auto' markerUnits='strokeWidth' ><path d='M0,3 L5,0 L10,3 L5,6 Z' fill='white' stroke='rgb(27,141,17)' stroke-width='1'></path></marker>"
+"<marker id='fillDiamond' markerWidth='10' markerHeight='10' refx='0' refy='3' orient='auto' markerUnits='strokeWidth' ><path d='M0,3 L5,0 L10,3 L5,6 Z' fill='rgb(27,141,17)' ></path></marker>"
+"<marker id='dot' markerWidth='10' markerHeight='10' refx='0' refy='3' orient='auto' markerUnits='strokeWidth' ><circle r='3' cx='3' cy='3' fill='rgb(27,141,17)' ></circle></marker>"
+"<marker id='lineArrowE' markerWidth='10' markerHeight='10' refx='8' refy='3' orient='auto' markerUnits='strokeWidth' ><path d='M0,0 L9,3 L0,6' fill='none' stroke='rgb(27,141,17)' stroke-width='1'></path></marker>"
+"<marker id='lineArrowS' markerWidth='10' markerHeight='10' refx='0' refy='3' orient='auto' markerUnits='strokeWidth' ><path d='M9,0 L0,3 L9,6' fill='none' stroke='rgb(27,141,17)' stroke-width='1'></path></marker>"
+"</defs>";
/*
+"<marker id='startArrow1' markerWidth='10' markerHeight='10' refx='0' refy='3' orient='auto' markerUnits='strokeWidth' >"
+ "<path d='M0,0 L9,-3 L9,3 Z' fill='#000000'></path>"
+ "</marker>"
*/
_sheet.canvas.innerHTML = defString;
}
/**
* This Utility function is for internal use
* to figure out if any a portion of the area is already occupied
* @static
*/
Utility.Sheet.isGridAvailable = function(startGridSeq,_sheet,_shape)
{
// temporary code
//Utility.Sheet.drawCourseGrid(_sheet);
var dim = _shape.dimension;
var allGrids = _sheet.courseGrids;
var gLen = allGrids.length;
var horizGrids = _sheet.noOfXcourseGrids;
var vertGrids = _sheet.noOfYcourseGrids;
var margin = 10;
var maxWidth = MagicBoard.sheetBook.cwidth;
var maxHeight = MagicBoard.sheetBook.cheight;
// prime it for the first time only
if (startGridSeq === -1)
{
var left = 0; var top = 0;
// give preference to dim.left and dim.top
if (dim.left) left = dim.left;
startGridSeq = Utility.Sheet.LeftAlignedFreeGrid(left,top,_sheet);
for (var g = startGridSeq; g < gLen;g++)
{
var grid = allGrids[g];
if (grid.filled) continue;
return Utility.Sheet.isGridAvailable(g,_sheet,_shape);
}
}
// priming complete
var startGrid = allGrids[startGridSeq];
var startX = startGrid.x1 + margin;
var startY = startGrid.y1 + margin;
var c1 = {x:startX,y:startY};
var c2 = {x:(startX + dim.width),y:startY};
if (c2.x > maxWidth)
{
return Utility.Sheet.isGridAvailable((startGridSeq+1),_sheet,_shape);
}
var c3 = {x:startX,y:(startY+dim.height)};
if (c3.y > maxHeight)
{
return []; // it is full
}
var c4 = {x:(startX + dim.width),y:(startY+dim.height)};
// to be done later if startGrid is bad.. means cannot accomodate rectangle
// skip through untill you find the right startGrid
var x = Math.floor(startGridSeq / horizGrids) + 1;
var y = startGridSeq / horizGrids;
// find the top right, bottom right, bottom left corner grids
// make sure all of them are free
var busyGrids = [];
for (var iX = startGridSeq; iX < gLen; iX++ )
{
var grid = allGrids[iX];
var x1,x2,y1,y2;
x1 = grid.x1; x2 = grid.x2; y1 = grid.y1; y2 = grid.y2;
// possibilities 1. x2 < c1.x -- outside
// 2. x1 > c2.x -- outside
// 3 y2 < c1.y -- outside
// 4 y1 > c3.y -- outside
// remaining all are in scope grids
if (x2 < c1.x) continue;
if (x1 > c2.x) continue;
if (y1 > c3.y) break;
if (y2 < c1.y) continue; // this will never happen, I think, because we are only going forward
if (grid.filled)
{
var iX1 = iX;
// we got to again start looking
// call this routine recursively
// loop through to find first empty grid
for (; iX1 < gLen; iX1++)
{
if (iX1 >=gLen)
{
return []; // all filled;
}
if (!allGrids[iX1].filled)
break;
}
var _busyGrids = Utility.Sheet.isGridAvailable(iX1,_sheet,_shape);
if (_busyGrids.length > 0) return _busyGrids;
}
busyGrids.push(iX);
continue;
}
return busyGrids;
}
/**
* This Utility function is for internal use
* @static
*/
Utility.Sheet.LeftAlignedFreeGrid = function(left,top,_sheet)
{
var horizGrids = _sheet.noOfXcourseGrids;
var allGrids = _sheet.courseGrids;
startGridSeq = Math.floor(top/_sheet.courseGridSize.y) * horizGrids + Math.floor(left/_sheet.courseGridSize.x);
if (startGridSeq > allGrids.length) return 0;
if (allGrids[startGridSeq].filled)
{
// find the next preferred one
if (left)
{
return Utility.Sheet.LeftAlignedFreeGrid(left,(top + _sheet.courseGridSize.y),_sheet);
} else return startGridSeq++;
} else return startGridSeq;
}
/**
* This Utility function to find owner shape any dom in a sheet
* @static
* @param {HTMLElement} _dom
* @return {ShapeComponent} component or null (if not found)
*/
Utility.Sheet.findOwner = function(_dom)
{
var _sheet = MagicBoard.sheetBook.currentSheet;
var sLen = _sheet.shapes.length;
for (var s = 0; s < sLen;s++)
{
var _shape = _sheet.shapes[s];
if (_shape.dom === _dom) return _shape.dom;
var cLen = _shape.components.length;
for (var c = 0; c < cLen ;c++)
{
var component = _shape.components[c];
if (component.dom === _dom) return component;
}
}
return null;
}
/**
* This Utility function is for internal use only
* @static
*/
Utility.Sheet.connectIds = function()
{
var _sheet = MagicBoard.sheetBook.currentSheet;
var sLen = _sheet.shapes.length;
for (var s = 0; s < sLen;s++)
{
var shape = _sheet.shapes[s];
if (shape.connectedFromIds)
{
for (var c = 0, cLen = shape.connectedFromIds.length; c < cLen;c++)
{
var bId = shape.connectedFromIds[c];
// find beginShapes
shape.connectedFrom.push(Utility.Sheet.findShapeById(bId));
}
}
if (shape.connectedToIds)
{
for (var c = 0, cLen = shape.connectedToIds.length; c < cLen;c++)
{
var eId = shape.connectedToIds[c];
// find endShapes
shape.connectedTo.push(Utility.Sheet.findShapeById(eId));
}
}
}
for (var s = 0; s < sLen;s++)
{
var shape = _sheet.shapes[s];
shape.refreshConnection();
}
//refreshConnection
}
/**
* This Utility function is for internal use only
* @static
*/
Utility.Sheet.findShapeById = function(_id)
{
var _sheet = MagicBoard.sheetBook.currentSheet;
var sLen = _sheet.shapes.length;
for (var s = 0; s < sLen;s++)
{
var shape = _sheet.shapes[s];
if (shape.id === _id) return shape;
}
}
/**
* This Utility function to apply changes to internal properties of the shape.
* e.g. properties such as "Background Color", "Border Width" etc.
* @static
*/
Utility.Shape.applyProperty = function()
{
var propPrompt = document.getElementById("propPrompt");
var shape = propPrompt.shape;
propPrompt.shape = null;
var inps = propPrompt.getElementsByClassName("prop");
var iLen = inps.length;
for (var i = 0; i < iLen;i++)
{
var field = inps[i];
var dirty = field.getAttribute("data-dirty");
if (dirty && dirty === "1")
{
var propKey = field.getAttribute("data-propKey");
var propType = field.getAttribute("data-propType");
var propName = field.getAttribute("data-propName");
var val;
if (field.nodeName === "INPUT") val = field.value;
else if (field.nodeName === "SELECT") val = field.options[field.selectedIndex].value;
shape.applyProperty(propKey,propName,propType,val);
}
}
Utility.destroyDom(propPrompt,"dom");
}
/**
* This Utility function is for internal use only
* @static
*/
Utility.addChangeFlag = function()
{
var target = event.target;
target.setAttribute("data-dirty","1");
}
/**
* This Utility function to show internal properties of the shape for possible modifications.
* e.g. properties such as "Background Color", "Border Width" etc.
* @static
*/
Utility.Shape.showProperty = function()
{
var disp = "";
var shape = MagicBoard.indicators.hilight;
var properties = shape.properties;
var pLen = properties.length;
for (var p in properties)
{
var pDetail = MagicBoard.properties[p];
//{"attribute":"fill","label":"Background Color","field":"input","values":[{"name":"","value":"#1b8d11","type":"color"}]}
if (pDetail.field === "input")
{
disp += "<label class='propLabel'>"+pDetail.label+":</label><input class='propInput prop' data-propKey='"+p+"' class='prop' data-propType='"+pDetail.propType+"' data-propName='"+pDetail.propName+"' type='"+pDetail.values[0].type+"' value='"+pDetail.values[0].value+"' onchange='Utility.addChangeFlag()'/><BR/>";
} else if (pDetail.field === "select")
{
disp += "<label class='propLabel' >"+pDetail.label+":</label><select onchange='Utility.addChangeFlag()' data-propKey='"+p+"' class='prop' data-propType='"+pDetail.propType+"' data-propName='"+pDetail.propName+"' >";
for (var v = 0, vLen = pDetail.values.length; v < vLen;v++)
{
disp += "<option value='"+pDetail.values[v].value+"' >"+pDetail.values[v].name+"</option>";
}
disp += "</select><BR/>";
}
}
if (disp)
{
disp += "<br/><button class='apply button' onclick='Utility.Shape.applyProperty()'>Apply</button><button class='cancel button' onclick='Utility.destroyDom(\"propPrompt\",\"id\")'>Cancel</button>"
var div = document.createElement("div");
div.setAttribute("class","prompt");
div.setAttribute("style","");
div.setAttribute("id","propPrompt");
div.innerHTML = disp;
div.shape = shape;
document.body.appendChild(div);
}
}
/**
* this function is for internal use to show nearest aligned horizontal and vertical lines
* sample structure for internal use only
* MagicBoard.sheetBook.alignments[101] = [shape1,shape2]
* MagicBoard.sheetBook.alignments[103] = [] <-- once there was something here but no more
* @static
**/
Utility.Shape.align = function(pos)
{
// go through all alignments and check which one is nearest, then return the best possible coordinates
var xArray = MagicBoard.sheetBook.alignments.x;
var yArray = MagicBoard.sheetBook.alignments.y;
var minXdistance = 9999; var minYdistance = 9999;
var nearestX = pos.x; nearestY = pos.y;
for (var y in xArray) // This is not traditional array, it has holes // x contains y coordinates
{
if (xArray[y] && xArray[y].length > 0) {
var diff = Math.abs(pos.y - y) ;
if (diff < minYdistance)
{ minYdistance = diff;nearestY = y;}
}
}
for (var x in yArray) // This is not traditional array, it has holes
{
if ( yArray[x] && yArray[x].length > 0) {
var diff = Math.abs(pos.x - x) ;
if (diff < minXdistance)
{ minXdistance = diff;nearestX = x; }
}
}
if (Math.abs(pos.x - nearestX) < 20) {if (typeof(nearestX) === "string") nearestX = parseInt(nearestX);pos.x = nearestX;}
if (Math.abs(pos.y - nearestY) < 20) {if (typeof(nearestY) === "string") nearestY = parseInt(nearestY);pos.y = nearestY;}
return pos;
}
/**
* This Utility function for internal use only (mostly for testing)
* @static
*/
Utility.Sheet.drawCourseGrid = function(_sheet)
{
var sctx = MagicBoard.sheetBook.scratchCtx;
var scanvas = MagicBoard.sheetBook.scratchCanvas;
sctx.clearRect(0,0,scanvas.width,scanvas.height);
sctx.setLineDash([1, 15]);
sctx.beginPath();
var gNo = 0;
// temporary code ends
for (var y = 0; y < _sheet.noOfYcourseGrids;y++)
{
var y1 = y * _sheet.courseGridSize.y;
sctx.moveTo(0,y1); sctx.lineTo(MagicBoard.sheetBook.cwidth,y1);
for (var x = 0 ; x < _sheet.noOfXcourseGrids; x++)
{
var x1 = x * _sheet.courseGridSize.x;
sctx.moveTo(x1,0); sctx.lineTo(x1,MagicBoard.sheetBook.cheight);
var txt = "Filled "+gNo;
var grid = _sheet.courseGrids[gNo++];
if (grid.filled) sctx.fillText(txt,x1+20,y1+20);
}
}
sctx.stroke();
}
/**
* This Utility function to block grids to show them occupied for future shape to know
* @param {Shape} _shape - Provide shape for which occupied positions to be marked
* @static
*/
Utility.Shape.blockGrids = function(_shape)
{
// temporary code
//Utility.Sheet.drawCourseGrid(_shape.currentSheet);
var _sheet = _shape.currentSheet;
var _dim = _shape.dimension;
var allGrids = _sheet.courseGrids;
Utility.Shape.unblockGrids(_shape,allGrids);
var startX = _dim.left; var startY = _dim.top;
var xCoord = Math.floor(startX/_sheet.courseGridSize.x);
var yCoord = Math.floor(startY/_sheet.courseGridSize.y);
var startGridSeq = yCoord*_sheet.noOfXcourseGrids + xCoord;
var c1 = {x:startX,y:startY};
var c2 = {x:(startX + _dim.width),y:startY};
var c3 = {x:startX,y:(startY+_dim.height)};
var c4 = {x:(startX + _dim.width),y:(startY+_dim.height)};
var allGrids = _sheet.courseGrids;var gLen = allGrids.length;
for (var iX = startGridSeq; iX < gLen; iX++ )
{
var grid = allGrids[iX];
var x1,x2,y1,y2;
x1 = grid.x1; x2 = grid.x2; y1 = grid.y1; y2 = grid.y2;
// possibilities 1. x2 < c1.x -- outside
// 2. x1 > c2.x -- outside
// 3 y2 < c1.y -- outside
// 4 y1 > c3.y -- outside
// remaining all are in scope grids
if (x2 < c1.x) continue;
if (x1 > c2.x) continue;
if (y1 > c3.y) break;
if (y2 < c1.y) continue; // this will never happen, I think, because we are only going forward
grid.filled = true;
grid.shapes.push(_shape);
_shape.occupiedGrids.push(iX);
}
}
/**
* This Utility function to unblock grids that was previously marked by a shape as occupied
* @param {Shape} _shape - Provide shape for which occupied positions to be unmarked
* @parm {Array} allGrids - Contains array of all the grids
* @static
*/
Utility.Shape.unblockGrids = function(_shape,allGrids)
{
var occupiedGrids = _shape.occupiedGrids;
var oLen = occupiedGrids.length;
for (var i = 0; i < oLen;i++)
{
var gridIndex = occupiedGrids[i];
var grid = allGrids[gridIndex];
var shapes = grid.shapes;
var sLen = shapes.length;
for (var s = 0; s < sLen;s++)
{
var sh = shapes[s];
if (sh === _shape)
{
shapes.splice(s,1);
break;
}
}
if (shapes.length === 0) grid.filled = false;
}
_shape.occupiedGrids = [];
// temporary code
//Utility.Sheet.drawCourseGrid(_shape.currentSheet);
}
/**
* This Utility function is used to calculate the best connection points between two shapes
* @param {Shape} beginShape
* @param {Shape} endShape
* @return {Object} connectionInfo - is used to create ConnectorLine
* @static
*/
Utility.Shape.calculateConnectionPoints = function(beginShape,endShape)
{
/*
if (this.connected && this.connected.connectorShape)
{
this.connected.connectorShape.deleteShape();
}
*/
var beginDim = beginShape.getDimension();
var endDim = endShape.getDimension();
var connectingPoints = null; var bEdge = beginDim.edgePoints; var eEdge = endDim.edgePoints;
var x1,x2,y1,y2;
// always prefer vertical face of the originator
// if the terminator's vertical face is available then use it
// else use the closest horizontal face
// find the closest connection point and join them
// the shapes could in be any 8 positions based on center point
//
// 1 | 2 | 3 | 4 | 5
// -----------------------------------
// 6 | 7 | X | 8 | 9
// -----------------------------------
// 10 | 11 | 12 | 13 | 14
var orientation = "horiz";
if (bEdge.c1.x > eEdge.c2.x) // the endShape is on the left (1 6,7, 10)
{
var diffX_41_23 = bEdge.m41.x - eEdge.m23.x;
if (diffX_41_23 > 50)
{
x1 = bEdge.m41.x;y1 = bEdge.m41.y;
x2 = eEdge.m23.x;y2 = eEdge.m23.y;
} else
{
var diffY_41_23 = bEdge.m41.y - eEdge.m23.y;
var diffX_12_23 = bEdge.m12.x - eEdge.m23.x;
var diffY_12_23 = bEdge.m12.y - eEdge.m23.y;
var diffX_34_23 = bEdge.m12.x - eEdge.m23.x;
var diffY_34_23 = bEdge.m12.y - eEdge.m23.y;
if (diffY_12_23 > 50)
{
if ((bEdge.m12.x - eEdge.m34.x) > 50)
{
x1 = bEdge.m12.x;y1 = bEdge.m12.y;
x2 = eEdge.m34.x;y2 = eEdge.m34.y;
orientation = "vert";
} else
{
x1 = bEdge.m12.x;y1 = bEdge.m12.y;
x2 = eEdge.m23.x;y2 = eEdge.m23.y;
orientation = "horizvert";
}
} else if (diffY_34_23 > 50)
{
x1 = bEdge.m34.x;y1 = bEdge.m34.y;
x2 = eEdge.m23.x;y2 = eEdge.m23.y;
orientation = "horizvert";
} else
{
if (diffY_41_23 > 50)
{
var diffY_41_34 = bEdge.m41.y - eEdge.m34.y;
if (diffY_41_34 > 50)
{
x1 = bEdge.m41.x;y1 = bEdge.m41.y;
x2 = eEdge.m34.x;y2 = eEdge.m34.y;
} else
{
x1 = bEdge.m41.x;y1 = bEdge.m41.y;
x2 = eEdge.m23.x;y2 = eEdge.m23.y;
}
} else
{
x1 = bEdge.m41.x;y1 = bEdge.m41.y;
x2 = eEdge.m23.x;y2 = eEdge.m23.y;
}
}
}
} else if (bEdge.c2.x < eEdge.c1.x) // end shape is on the right (5,8, 9 14)
{
var diffX_23_41 = bEdge.m41.x - eEdge.m23.x;
if (diffX_23_41 > 50)
{
x1 = bEdge.m23.x;y1 = bEdge.m23.y;
x2 = eEdge.m41.x;y2 = eEdge.m41.y;
} else
{
var diffY_23_41 = bEdge.m23.y - eEdge.m41.y;
var diffX_23_12 = bEdge.m23.x - eEdge.m12.x;
var diffY_23_12 = bEdge.m23.y - eEdge.m12.y;
var diffX_23_34 = bEdge.m23.x - eEdge.m34.x;
var diffY_23_34 = bEdge.m23.y - eEdge.m34.y;
if (Math.abs(diffY_23_34) > 50)
{
x1 = bEdge.m23.x;y1 = bEdge.m23.y;
if (diffY_23_34 > 0)
{
x2 = eEdge.m34.x;y2 = eEdge.m34.y;
} else
{
x2 = eEdge.m12.x;y2 = eEdge.m12.y;
}
orientation = "horizvert";
} else if (diffY_23_12 > 50)
{
if ( (eEdge.m41.x - bEdge.m23.x) > 50)
{
x1 = bEdge.m23.x;y1 = bEdge.m23.y;
x2 = eEdge.m41.x;y2 = eEdge.m41.y;
} else
{
x1 = bEdge.m23.x;y1 = bEdge.m23.y;
x2 = eEdge.m12.x;y2 = eEdge.m12.y;
orientation = "horizvert";
}
} else
{
x1 = bEdge.m23.x;y1 = bEdge.m23.y;
x2 = eEdge.m41.x;y2 = eEdge.m41.y;
}
}
//x1 = bEdge.m23.x;y1 = bEdge.m23.y;
//x2 = eEdge.m41.x;y2 = eEdge.m41.y;
} else if (bEdge.c1.y > eEdge.c3.y) // the endShape is on the top (2,3,4)
{
x1 = bEdge.m12.x;y1 = bEdge.m12.y;
x2 = eEdge.m34.x;y2 = eEdge.m34.y;
orientation = "vert";
} else // end shape is in the bottom (11,12,13)
{
x1 = bEdge.m34.x;y1 = bEdge.m34.y;
x2 = eEdge.m12.x;y2 = eEdge.m12.y;
orientation = "vert";
}
return {beginShape:beginShape,endShape:endShape,pos:{x1:x1,x2:x2,y1:y1,y2:y2},"orientation":orientation}
}
/**
* This Utility function is used to remove connection between two shapes
* @static
* @param {Shape} _sheet - Sheet for which the connection to be removed
* @param {Shape} _beginShape
* @param {Shape} _endShape
*/
Utility.Sheet.removeConnection = function(_sheet,_beginShape,_endShape)
{
var conn = _sheet.connections;
var cLen = conn.length;
for (var i = 0; i < cLen;i++)
{
var cI = conn[i];
if (cI.beginShape === _beginShape && cI.endShape === _endShape)
{
conn.splice(i,1);
return ;
}
}
}
/**
* This Utility function is for internal use to format dimension data that come in px or % format
* @static
*/
Utility.Shape.dataFormatter = function(_dimensionData,_type,_shape)
{
if (_dimensionData == undefined) return 0;
if (typeof(_dimensionData) === "number") return _dimensionData;
else if (typeof(_dimensionData) === "string")
{
if (_dimensionData.indexOf("px") > -1) return parseInt(_dimensionData.replace("px",""));
if (_dimensionData.indexOf("%") > -1)
{
if (!_shape) {
console.log(" In order to calculate %, we need the shape, that is missing in the call");
return 0;
}
var val = parseInt(_dimensionData.replace("%",""));
// need to know parent's dimension to calculate pixel
var parentDimension = null;
if (_shape.parentShape) parentDimension = _shape.parentShape.dimension;
else {
var sheetBook = MagicBoard.sheetBook;
parentDimension = {left:0,top:0,width:sheetBook.cwidth,height:sheetBook.cheight};
}
var parentType = _type;
if (_type === "left") parentType = "width";
else if (_type === "top") parentType = "height";
return parentDimension[parentType] * val/100
}
}
}
/**
* This Utility function is for internal use
* @static
*/
Utility.Shape.recalculateDimensions = function(_shape)
{
var dimension = _shape.dimension;
if (!dimension.misc.unit) return;
if (dimension.misc.unit === "%")
{
var types = dimension.misc.key
var tLen = types.length;
for (var i = 0; i < tLen ; i++)
{
var type = types[i];
var dimensionData = _shape.style[type];
var val = Utility.Shape.dataFormatter(dimensionData,type,_shape);
dimension[type] = val;
}
}
}
/**
* This Utility function is for internal use to destroy a HTML Dom
* @static
*/
Utility.destroyDom = function(_item,_type)
{
var garbage = MagicBoard.sheetBook.garbage;
var dom = null;
if (_type === "id")
{
dom = document.getElementById(_item);
} else if (_type === "dom") dom = _item;
garbage.appendChild(dom);
garbage.innerHTML = "";
}
/**
* This is for internal logging use
* @constructor
*/
var Logger = function()
{
var console = function(msg)
{
console.log(msg);
}
}
/**
* This is for internal logging use
* @constructor
*/
var Info = function()
{
}
inheritsFrom(Info,Logger);
Info.prototype.console = function(msg)
{
console.log("Info - "+msg);
}
/**
* This is for internal logging use
* @constructor
*/
var Warning = function()
{
}
inheritsFrom(Warning,Logger);
Warning.prototype.console = function(msg)
{
console.log("Warning - "+msg);
}
/**
* This is for internal logging use
* @constructor
*/
var Error = function()
{
}
inheritsFrom(Error,Logger);
Error.prototype.console = function(msg)
{
Error.log("Error - "+msg);
}
var info = new Info();var warning = new Warning(); var error = new Error();