All Downloads are FREE. Search and download functionalities are using the official Maven repository.

de.uniks.networkparser.graph.graph.js Maven / Gradle / Ivy

/*
 NetworkParser
 Copyright (c) 2011 - 2014, Stefan Lindel
 All rights reserved.

 Licensed under the EUPL, Version 1.1 or (as soon they
 will be approved by the European Commission) subsequent
 versions of the EUPL (the "Licence");
 You may not use this work except in compliance with the Licence.
 You may obtain a copy of the Licence at:

 http://ec.europa.eu/idabc/eupl5

 Unless required by applicable law or agreed to in
 writing, software distributed under the Licence is
 distributed on an "AS IS" basis,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 express or implied.
 See the Licence for the specific language governing
 permissions and limitations under the Licence.
*/
// VERSION: 2015.11.09 20:21

//var uniId = 0;
//function generateId() { return uniId++; };
//Object.prototype.uniId = function () {
//	var newId = generateId();
//	this.uniId = function () { return newId; };
//	return newId;
//};vars: true
/*jslint forin:true, newcap:true, node: true, continue: true */
/*jshint forin:true, laxbreak: true, newcap: false, node: true, nomen: true, -W089, -W079 */

/*global document: false, window: false, navigator: false, unescape: false, Edge: false, DagreLayout: false, Drawer: false */
/*global jsPDF: false, svgConverter: false, Image: false, Blob: false, dagre: false, SymbolLibary: false */
/*global FileReader:false, java:false, ChoiceBox:false */
"use strict";
var ObjectCreate = Object.create || function (o) {var F = function () {}; F.prototype = o; return new F(); };

/* Pos */
var Pos = function (x, y, id) {this.x = Math.round(x || 0); this.y = Math.round(y || 0); if (id) {this.$id = id; } };
/* GraphUtil */
var GraphUtil = function (ns) {if (ns) {this.ns = ns; } };
GraphUtil.prototype.copy = function (ref, src, full, replace) {
	if (src) {
		var i;
		for (i in src) {
			if (!src.hasOwnProperty(i) || typeof (src[i]) === "function") {
				continue;
			}
			if (i.charAt(0) === "$") {
				if (full) {ref[i] = src[i]; }
				continue;
			}
			if (typeof (src[i]) === "object") {
				if (replace) {
					ref[i] = src[i];
					continue;
				}
				if (!ref[i]) {
					if (src[i] instanceof Array) {
						ref[i] = [];
					} else {
						ref[i] = {};
					}
				}
				this.copy(ref[i], src[i], full);
			} else {
				if (src[i] === "") {
					continue;
				}
				ref[i] = src[i];
			}
		}
		if (src.width) {ref.$startWidth = src.width; }
		if (src.height) {ref.$startHeight = src.height; }
	}
	return ref;
};
GraphUtil.prototype.minJson = function (target, src, ref) {
	var i, temp, value;
	for (i in src) {
		if (!src.hasOwnProperty(i) || typeof (src[i]) === "function") {
			continue;
		}
		if (src[i] === null || src[i] === "" || src[i] === 0 || src[i] === false || i.charAt(0) === "$") {
			continue;
		}
		value = src[i];
		if (value instanceof GraphUtil.Options || ref !== null) {
			if (typeof (value) === "object") {
				temp = (value instanceof Array) ? [] : {};
				if (ref) {
					value = this.minJson(temp, value, ref[i]);
				} else {
					value = this.minJson(temp, value, new GraphUtil.Options());
				}
			}
			if (ref && value === ref[i]) {
				continue;
			}
		}
		if (typeof (value) === "object") {
			if (value instanceof Array && value.length < 1) {
				continue;
			}
			if (value instanceof Array) {
				target[i] = this.minJson([], value);
			} else {
				temp = this.minJson({}, value);
				if (JSON.stringify(temp, null, "") === "{}") {
					continue;
				}
				target[i] = temp;
			}
		} else {
			target[i] = value;
		}
	}
	return target;
};
GraphUtil.prototype.bind = function (el, eventName, eventHandler) {
	if (el.addEventListener) {
		el.addEventListener(eventName, eventHandler, false);
	} else if (el.attachEvent) {
		el.attachEvent('on' + eventName, eventHandler);
	}
};
GraphUtil.prototype.create = function (node) {
	var style, item, xmlns, key, tag, k;
	if (document.createElementNS && (node.xmlns || this.ns)) {
		if (node.xmlns) {
			xmlns = node.xmlns;
		} else {
			xmlns = this.ns;
		}
		if (node.tag === "img" && xmlns === "http://www.w3.org/2000/svg") {
			item = document.createElementNS(xmlns, "image");
			item.setAttribute('xmlns:xlink', "http://www.w3.org/1999/xlink");
			item.setAttributeNS("http://www.w3.org/1999/xlink", 'href', node.src);
		} else {
			item = document.createElementNS(xmlns, node.tag);
		}
	} else {
		item = document.createElement(node.tag);
	}

	tag = node.tag.toLowerCase();
	for (key in node) {
		if (!node.hasOwnProperty(key)) {
			continue;
		}
		k = key.toLowerCase();
		if (node[key] === null) {
			continue;
		}
		if (k === 'tag' || k.charAt(0) === '$' || k === 'model') {
			continue;
		}
		if (k.charAt(0) === '#') {
			item[k.substring(1)] = node[key];
			continue;
		}
		if (k === 'rotate') {
			item.setAttribute("transform", "rotate(" + node[key] + "," + node.model.x + "," + node.model.y + ")");
			continue;
		}
		if (k === 'value') {
			if (!node[key]) {
				continue;
			}
			if (tag !== "input") {
				if (tag === "text") {// SVG
					item.appendChild(document.createTextNode(node[key]));
				} else {
					item.innerHTML = node[key];
				}
			} else {
				item[key] = node[key];
			}
			continue;
		}
		if (k.indexOf("on") === 0) {
			this.bind(item, k.substring(2), node[key]);
			continue;
		}
		if (k.indexOf("-") >= 0) {
			item.style[key] = node[key];
		} else {
			if (k === "style" && typeof (node[key]) === "object") {
				for (style in node[key]) {
					if (!node[key].hasOwnProperty(style)) {
						continue;
					}
					if (node[key][style]) {
						if ("transform" === style) {
							item.style.transform = node[key][style];
							item.style.msTransform = item.style.MozTransform = item.style.WebkitTransform = item.style.OTransform = node[key][style];
						} else {
							item.style[style] = node[key][style];
						}
					}
				}
			} else {
				item.setAttribute(key, node[key]);
			}
		}
	}
	if (node.$font) {
		if (this.model && this.model.options && this.model.options.font) {
			for (key in this.model.options.font) {
				if (!this.model.options.font.hasOwnProperty(key)) {
					continue;
				}
				if (this.model.options.font[key]) {
					if (item.style) {
						item.style[key] = this.model.options.font[key];
					} else {
						item.setAttribute(key, this.model.options.font[key]);
					}
				}
			}
		}
	}
	if (node.$parent) {
		node.$parent.appendChild(item);
	}

	if (node.model) {
		item.model = node.model;
	}
	return item;
};
GraphUtil.prototype.getModelNode = function (element) {
	if (!element.model) {
		if (element.parentElement) {
			return this.getModelNode(element.parentElement);
		}
		return null;
	}
	return element;
};
GraphUtil.prototype.getValue = function (value) {return parseInt(("0" + value).replace("px", ""), 10); };
GraphUtil.prototype.isIE = function () {return document.all && !window.opera; };
GraphUtil.prototype.isFireFox = function () {return navigator.userAgent.toLowerCase().indexOf('firefox') > -1; };
GraphUtil.prototype.isOpera = function () {return navigator.userAgent.indexOf("Opera") > -1; };
GraphUtil.prototype.getEventX = function (event) {return (this.isIE) ? window.event.clientX : event.pageX; };
GraphUtil.prototype.getEventY = function (event) {return (this.isIE) ? window.event.clientY : event.pageY; };
GraphUtil.prototype.set = function (id, value) {if (value) {this[id] = value; } };
GraphUtil.prototype.selectText = function (text) {
	var selection, range;
	if (this.isIE()) {
		range = document.body.createTextRange();
		range.moveToElementText(text);
		range.select();
	} else if (this.isFireFox() || this.isOpera()) {
		selection = window.getSelection();
		range = document.createRange();
		range.selectNodeContents(text);
		selection.removeAllRanges();
		selection.addRange(range);
	}
};
GraphUtil.prototype.sizeHTML = function (html, node) {
	if (!html) {return; }
	if (this.$parent) {
		return this.$parent.sizeHTML(html, node);
	}
	this.board.appendChild(html);
	var rect = html.getBoundingClientRect();
	this.board.removeChild(html);
	if (node) {
		if (!node.$startWidth) {
			node.width = Math.round(rect.width);
		}
		if (!node.$startHeight) {
			node.height = Math.round(rect.height);
		}
	}
	return rect;
};
GraphUtil.prototype.hasClass = function (ele, cls) {return ele.className.indexOf(cls) > 0; };
GraphUtil.prototype.addClass = function (ele, cls) {if (!this.hasClass(ele, cls)) {
	ele.className = ele.className + " " + cls;
} };
GraphUtil.prototype.removeClass = function (ele, cls) {
	if (this.hasClass(ele, cls)) {
		var reg = new RegExp('(\\s|^)' + cls + '(\\s|$)');
		ele.className = ele.className.replace(reg, ' ');
	}
};
/* Info */
GraphUtil.Info = function (info, parent, edge) {
	this.type = "Info";
	if (typeof (info) === "string") {
		this.id = info;
	} else {
		if (info.property) {this.property = info.property; }
		if (info.cardinality) {this.cardinality = info.cardinality; }
		this.id = info.id;
	}
	this.x = this.y = this.width = this.height = 0;
	this.$center = new Pos();
	this.custom = false;
	this.$parent = parent;
	this.$edge = edge;
	this.$isdraggable = true;
};
GraphUtil.Info.prototype.getX = function () {return this.x; };
GraphUtil.Info.prototype.getY = function () {return this.y; };

GraphUtil.Line = function (source, target, line, style) {this.source = source; this.target = target; this.line = line; this.style = style; };
GraphUtil.Line.Format = {SOLID: "SOLID", DOTTED: "DOTTED"};
/* Options */
GraphUtil.Options = function () {
	this.raster = false;
	this.addBorder = true;
	this.display = "svg";
	this.font = {"font-size": "10px", "font-family": "Verdana"};
	this.layout = {name: "Dagre", rankDir: "TB", nodesep: 10};	// Dagre TB, LR
	this.CardinalityInfo = true;
	this.propertyinfo = true;
	this.rotatetext = true;
	this.linetyp = "center";
	this.buttons = ["HTML", "SVG"];	// ["HTML", "SVG", "PNG", "PDF"]
};
/* Node */
var GraphNode = function (id) {
	this.type = "node";
	this.id = id;
	this.$edges = [];
	this.attributes = [];
	this.methods = [];
	this.$parent = null;
	this.x = this.y = this.width = this.height = 0;
	this.$isdraggable = true;
};
GraphNode.prototype = ObjectCreate(GraphUtil.prototype);
GraphNode.prototype.getX = function () {if (this.$parent) {return this.$parent.getX() + this.x; } return this.x; };
GraphNode.prototype.getY = function () {if (this.$parent) {return this.$parent.getY() + this.y; } return this.y; };
GraphNode.prototype.getEdges = function () {return this.$edges; };
GraphNode.prototype.clear = function () {this.$RIGHT = this.$LEFT = this.$UP = this.$DOWN = 0; };
GraphNode.prototype.removeFromBoard = function (board) {if (this.$gui) {board.removeChild(this.$gui); this.$gui = null; } };
GraphNode.prototype.isClosed = function () {
	if (this.status === "close") {
		return true;
	}
	if (this.$parent) {return this.$parent.isClosed(); }
	return false;
};
GraphNode.prototype.getShowed = function () {
	if (this.status === "close") {
		if (!this.$parent.isClosed()) {
			return this;
		}
	}
	if (this.isClosed()) {
		return this.$parent.getShowed();
	}
	return this;
};
var GraphModel = function (json, options) {
	this.type = "classdiagram";
	this.$isdraggable = true;
	json = json || {};
	this.left = json.left || 0;
	this.top = json.top || 0;
	this.x = this.y = this.width = this.height = 0;
	if (json.minid) {
		this.minid = json.minid;
	}
	this.$nodeCount = 0;
	this.nodes = {};
	this.edges = [];
	json = json || {};
	this.type = json.type || "classdiagram";
	this.set("id", json.id);
	this.options = this.copy(this.copy(new GraphUtil.Options(), json.options), options, true, true);
	this["package"] = "";
	this.set("info", json.info);
	this.set("style", json.style);
	var i;
	if (json.nodes) {
		for (i = 0; i < json.nodes.length; i += 1) {
			this.addNode(json.nodes[i]);
		}
	}
	if (json.edges) {
		for (i = 0; i < json.edges.length; i += 1) {
			this.addEdgeModel(json.edges[i]);
		}
	}
};
GraphModel.prototype = ObjectCreate(GraphNode.prototype);
GraphModel.prototype.clear = function () {
	var i;
	GraphNode.prototype.clear.call(this);
	for (i in this.nodes) {
		if (!this.nodes.hasOwnProperty(i)) {
			continue;
		}
		this.nodes[i].clear();
	}
};
GraphModel.prototype.addEdgeModel = function (e) {
	var edge, type = e.type || "edge";
	type = type.charAt(0).toUpperCase() + type.substring(1).toLowerCase();
	if (typeof window[type] === "function") {
		edge = new window[type]();
	} else {
		edge = new Edge();
	}
	edge.source = new GraphUtil.Info(e.source, this, edge);
	edge.target = new GraphUtil.Info(e.target, this, edge);
	edge.$sNode = this.getNode(edge.source.id, true);
	edge.$sNode.$edges.push(edge);

	if (e.info) {
		if (typeof (e.info) === "string") {
			edge.info = {id: e.info};
		} else {
			edge.info = {id: e.info.id, property: e.info.property, cardinality: e.info.cardinality};
		}
	}
	edge.$parent = this;
	edge.set("style", e.style);
	edge.set("counter", e.counter);

	edge.$tNode = this.getNode(edge.target.id, true);
	edge.$tNode.$edges.push(edge);

	this.edges.push(edge);
	return edge;
};
GraphModel.prototype.addEdge = function (source, target) {
	var edge = new Edge();
	edge.source = this.addNode(source);
	edge.target = this.addNode(target);
	return this.addEdgeModel(edge);
};
GraphModel.prototype.addNode = function (node) {
	/* testing if node is already existing in the graph */
	if (typeof (node) === "string") {
		node = {id: node, type: "node"};
	}
	node.type = node.type || "node";
	node.type = node.type.toLowerCase();
	if (!(node.id)) {
		node.id = node.type + "$" + (this.$nodeCount + 1);
	}
	if (this.nodes[node.id] !== undefined) {
		return this.nodes[node.id];
	}
	if (node.type.indexOf("diagram", node.type.length - 7) !== -1) {
		node = new GraphModel(node, new GraphUtil.Options());
	} else {
		node = this.copy(new GraphNode(), node);
	}
	this.nodes[node.id] = node;
	node.$parent = this;
	this.$nodeCount += 1;
	return this.nodes[node.id];
};
GraphModel.prototype.removeEdge = function (idSource, idTarget) {
	var z, e;
	for (z = 0; z < this.edges.length; z += 1) {
		e = this.edges[z];
		if (e.$sNode.id === idSource && e.$tNode.id === idTarget) {
			this.edges.splice(z, 1);
			z -= 1;
		} else if (e.$tNode.id === idSource && e.$sNode.id === idTarget) {
			this.edges.splice(z, 1);
			z -= 1;
		}
	}
};
GraphModel.prototype.removeNode = function (id) {
	delete (this.nodes[id]);
	var i;
	for (i = 0; i < this.edges.length; i += 1) {
		if (this.edges[i].$sNode.id === id || this.edges[i].$tNode.id === id) {
			this.edges.splice(i, 1);
			i -= 1;
		}
	}
};
GraphModel.prototype.getNode = function (id, isSub, deep) {
	var n, i, r;
	deep = deep || 0;
	if (this.nodes[id]) {
		return this.nodes[id];
	}
	if (!isSub) {
		return this.addNode(id);
	}
	for (i in this.nodes) {
		if (!this.nodes.hasOwnProperty(i)) {
			continue;
		}
		n = this.nodes[i];
		if (n instanceof GraphModel) {
			r = n.getNode(id, isSub, deep + 1);
			if (r) {
				return r;
			}
		}
	}
	if (deep === 0) {
		return this.addNode(id);
	}
	return null;
};
GraphModel.prototype.toJson = function () {return this.copy({}, this); };
GraphModel.prototype.createElement = function (element, typ) {this.$parent.createElement(element, typ); };
GraphModel.prototype.removeFromBoard = function (board) {
	if (this.$gui) {
		board.removeChild(this.$gui);
		this.$gui = null;
	}
};
GraphModel.prototype.resize = function (mode) {};
GraphModel.prototype.getEdges = function () {return this.edges; };
GraphModel.prototype.calcLines = function (drawer) {
	var i, n, sourcePos, e, ownAssoc = [];
	for (i in this.nodes) {
		if (!this.nodes.hasOwnProperty(i) || typeof (this.nodes[i]) === "function") {
			continue;
		}
		this.nodes[i].clear();
	}
	for (i = 0; i < this.edges.length; i += 1) {
		e = this.edges[i];
		if (!e.calculate(this.$gui, drawer)) {
			ownAssoc.push(e);
		}
	}
	for (i = 0; i < ownAssoc.length; i += 1) {
		ownAssoc[i].calcOwnEdge();
		sourcePos = ownAssoc[i].getCenterPosition(ownAssoc[i].$sNode, ownAssoc[i].$start);
		ownAssoc[i].calcInfoPos(sourcePos, ownAssoc[i].$sNode, ownAssoc[i].source);

		sourcePos = ownAssoc[i].getCenterPosition(ownAssoc[i].$tNode, ownAssoc[i].$end);
		ownAssoc[i].calcInfoPos(sourcePos, ownAssoc[i].$tNode, ownAssoc[i].target);
	}
};
GraphModel.prototype.validateModel = function () {
	var e, z, n, id, node, list;
	if (this.type === "classdiagram") {
		list = this.edges;
		for (e = 0; e < list.length; e += 1) {
			node = list[e].$sNode;
			z = node.id.indexOf(":");
			if (z > 0) {
				id = node.id.substring(z + 1);
				n = this.getNode(id, true, 1);
				delete (this.nodes[node.id]);
				this.edges[e].source.id = id;
				if (n) {
					this.edges[e].$sNode = n;
				} else {
					node.id = id;
					this.nodes[node.id] = node;
				}
			}
			node = list[e].$tNode;
			z = node.id.indexOf(":");
			if (z > 0) {
				id = node.id.substring(z + 1);
				n = this.getNode(id, true, 1);
				delete (this.nodes[node.id]);
				this.edges[e].target.id = id;
				if (n) {
					this.edges[e].$tNode = n;
				} else {
					node.id = id;
					this.nodes[node.id] = node;
				}
			}
			if (!list[e].source.cardinality) {
				list[e].source.cardinality = "one";
			}
			if (!list[e].target.cardinality) {
				list[e].target.cardinality = "one";
			}
			// Refactoring Edges for same property and type set cardinality
			for (z = e + 1; z < list.length; z += 1) {
				id = typeof (java);
				if (!(id === typeof list[z])) {
					continue;
				}
				if (this.validateEdge(list[e], list[z])) {
					list[e].target.cardinality = "many";
					list.splice(z, 1);
					z -= 1;
				} else if (this.validateEdge(list[z], list[e])) {
					list[e].source.cardinality = "many";
					list.splice(z, 1);
					z -= 1;
				}
			}
		}
	}
};
GraphModel.prototype.validateEdge = function (sEdge, tEdge) {
	return (sEdge.source.id === tEdge.source.id && sEdge.target.id === tEdge.target.id) && (sEdge.source.property === tEdge.source.property && sEdge.target.property === tEdge.target.property);
};
//				######################################################### Graph #########################################################
var Graph = function (json, options) {
	this.x = this.y = this.width = this.height = 0;
	json = json || {};
	json.top = json.top || 50;
	json.left = json.left || 10;
	this.model = new GraphModel(json, options);
	this.initLayouts();
	this.loader = new GraphUtil.Loader(this);
	this.initOption();
};
Graph.prototype = ObjectCreate(GraphNode.prototype);
Graph.prototype.initOption = function (typ, value) {
	this.init = true;
	if (this.model.options.display.toLowerCase() === "html") {
		this.drawer = new Drawer.HTMLDrawer();
	} else {
		this.initDrawer("svg");
	}
	var i, layout = this.layouts[0];

	for (i = 0; i < this.layouts.length; i += 1) {
		if (this.layouts[i].name === this.model.options.layout.name.toLowerCase()) {
			layout = this.layouts[i];
			break;
		}
	}
	this.layouter = layout.value;
	if (this.model.options.canvasid) {
		this.root = document.getElementById(this.model.options.canvasid);
	}
	if (this.root) {
		if (this.model.options.clearCanvas) {
			for (i = this.root.children.length - 1; i >= 0; i -= 1) {
				this.root.removeChild(this.root.children[i]);
			}
		}
	} else {
		this.root = document.createElement("div");
		if (this.model.options.canvasid) {
			this.root.id = this.model.options.canvasid;
		}
		document.body.appendChild(this.root);
	}
};
Graph.prototype.addOption = function (typ, value) {
	this.model.options[typ] = value;
	this.init = false;
};
Graph.prototype.initLayouts = function () { this.layouts = [{name: "dagre", value: new DagreLayout()}]; };
Graph.prototype.initInfo = function (edge, info) {
	if (!this.model.options.CardinalityInfo && !this.model.options.propertyinfo) {
		return null;
	}
	var infoTxt = edge.getInfo(info);
	if (infoTxt.length > 0) {
		this.sizeHTML(this.drawer.getInfo(info, infoTxt, 0), info);
	}
	return infoTxt;
};
Graph.prototype.clearBoard = function (onlyElements) {
	var i, n;
	if (this.board) {
		this.clearLines(this.model);
		for (i in this.model.nodes) {
			if (!this.model.nodes.hasOwnProperty(i)) {
				continue;
			}
			n = this.model.nodes[i];
			if (this.board.children.length > 0) {
				n.removeFromBoard(this.board);
			}
			n.$RIGHT = n.$LEFT = n.$UP = n.$DOWN = 0;
		}
		if (!onlyElements) {
			this.root.removeChild(this.board);
		}
	}
	if (!onlyElements && this.drawer) {
		this.drawer.clearBoard();
	}
};
Graph.prototype.addNode = function (node) {return this.model.addNode(node); };
Graph.prototype.addEdge = function (source, target) {return this.model.addEdge(source, target); };
Graph.prototype.removeNode = function (id) {return this.model.removeNode(id); };
Graph.prototype.calcLines = function (model) {
	model = model || this.model;
	model.calcLines(this.drawer);
};
Graph.prototype.drawLines = function (model) {
	this.clearLines(model);
	var i, e, startShow, endShow, items = [], id;
	for (i = 0; i < model.edges.length; i += 1) {
		e = model.edges[i];
		startShow = !e.$sNode.isClosed();
		endShow = !e.$tNode.isClosed();
		if (startShow && endShow) {
			e.draw(this.board, this.drawer);
		} else if ((startShow && !endShow) || (!startShow && endShow)) {
			id = e.$sNode.getShowed().id + "-" + e.$tNode.getShowed().id;
			if (items.indexOf(id) < 0) {
				items.push(id);
				e.draw(this.board, this.drawer);
			}
		}
	}
};
Graph.prototype.clearLines = function (model) {
	var i;
	for (i = 0; i < model.edges.length; i += 1) {
		model.edges[i].removeFromBoard(this.board);
	}
};
Graph.prototype.MinMax = function (node, min, max) {
	max.x = Math.max(max.x, node.x + Number(node.width) + 10);
	max.y = Math.max(max.y, node.y + Number(node.height) + 10);
	min.x = Math.max(min.x, node.x);
	min.y = Math.max(min.y, node.y);
};
Graph.prototype.resize = function (model) {
	var nodes, n, max, i, min = new Pos();
	max = new Pos(model.minSize.x, model.minSize.y);
	nodes = model.nodes;
	for (i in nodes) {
		if (!nodes.hasOwnProperty(i) || typeof (nodes[i]) === "function") {
			continue;
		}
		n = nodes[i];
		this.moveToRaster(n);
		this.MinMax(n, min, max);
	}
	this.calcLines(model);
	for (i = 0; i < model.edges.length; i += 1) {
		n = model.edges[i];
		this.MinMax(n.source, min, max);
		this.MinMax(n.target, min, max);
	}
	model.height = max.y;
	model.width = max.x;
	this.drawer.setSize(model.$gui, max.x, max.y);
	if (model.options.raster) {
		this.drawRaster();
	}
	this.drawLines(model);
	return max;
};
Graph.prototype.drawRaster = function () {
	var width, height, line, i;
	while (this.board.rasterElements.length > 0) {
		this.board.removeChild(this.board.rasterElements.pop());
	}
	width = this.board.style.width.replace("px", "");
	height = this.board.style.height.replace("px", "");

	for (i = 10; i < width; i += 10) {
		line = this.drawer.getLine(i, 0, i, height, null, "#ccc");
		line.setAttribute("className", "lineRaster");
		this.board.rasterElements.push(line);
		this.board.appendChild(line);
	}
	for (i = 10; i < height; i += 10) {
		line = this.drawer.getLine(0, i, width, i, null, "#ccc");
		line.setAttribute("className", "lineRaster");
		this.board.rasterElements.push(line);
		this.board.appendChild(line);
	}
};
Graph.prototype.draw = function (model, width, height) {
	var i, n, nodes = model.nodes;
	if (model.options.addBorder) {
		for (i in nodes) {
			if (!nodes.hasOwnProperty(i) || typeof (nodes[i]) === "function") {
				continue;
			}
			n = nodes[i];
			if (model.left > 0 || model.top > 0) {
				n.x += model.left;
				n.y += model.top;
			}
		}
		model.options.addBorder = false;
	}
	model.minSize = new Pos(width || 0, height || 0);
	if (this.loader.abort && this.loader.images.length > 0) {
		return;
	}
	this.resize(model);
	for (i in nodes) {
		if (!nodes.hasOwnProperty(i) || typeof (nodes[i]) === "function") {
			continue;
		}
		n = nodes[i];
		n.$gui = this.drawer.getNode(n, true);
		model.$gui.appendChild(n.$gui);
	}
};
Graph.prototype.moveToRaster = function (node) {
	if (this.model.options.raster) {
		node.x = parseInt(node.x / 10, 10) * 10;
		node.y = parseInt(node.y / 10, 10) * 10;
		if (node.$gui) {
			this.drawer.setPos(node.$gui, node.x, node.y);
		}
	}
};
Graph.prototype.initGraph = function (model) {
	var i, n, isDiag, html, e;
	model.validateModel();
	for (i in model.nodes) {
		if (typeof (model.nodes[i]) === "function") {
			continue;
		}
		n = model.nodes[i];
		isDiag = n.type.indexOf("diagram", n.type.length - 7) !== -1;
		if (isDiag) {
			this.initGraph(n);
		}
		html = this.drawer.getNode(n);
		if (html) {
			this.sizeHTML(html, n);
			if (isDiag) {
				n.$center = new Pos(n.x + (n.width / 2), n.y + (n.height / 2));
			}
		}
	}
	for (i = 0; i < model.edges.length; i += 1) {
		e = model.edges[i];
		this.initInfo(e, e.source);
		this.initInfo(e, e.target);
	}
};
Graph.prototype.layout = function (minwidth, minHeight, model) {
	if (!this.init) {
		this.initOption();
	}
	if (model) {
		this.initGraph(model);
	} else {
		model = this.model;
		this.initDrawer();
		this.initGraph(model);
	}
	if (this.loader.images.length < 1) {
		this.layouter.layout(this, model, Math.max(minwidth || 0, 100), Math.max(minHeight || 0, 100));
	} else {
		this.loader.width = minwidth;
		this.loader.height = minHeight;
	}
};
Graph.prototype.createElement = function (element, typ, node) {
	var that = this;
	element.node = node;
	this.bind(element, "mousedown", function (e) {that.startDrag(e); });
};
Graph.prototype.appendImage = function (img) {
	this.loader.add(img);
};
//				######################################################### DRAG AND DROP #########################################################
Graph.prototype.initDragAndDrop = function () {
	this.objDrag = null;
	this.mouse = new Pos();
	this.offset = new Pos();
	this.startObj = new Pos();
	var that = this;
	this.bind(this.board, "mousemove", function (e) {that.doDrag(e); });
	this.bind(this.board, "mouseup", function (e) {that.stopDrag(e); });
	this.bind(this.board, "mouseout", function (e) {that.stopDrag(e); });
};
Graph.prototype.setSelectable = function (node, value) {
	if (node.nodeType === 1) {
		if (value) {
			node.setAttribute("unselectable", value);
		} else {
			node.removeAttribute("unselectable");
		}
	}
	var child = node.firstChild;
	while (child) {
		this.setSelectable(child, value);
		child = child.nextSibling;
	}
};
Graph.prototype.getDragNode = function (node) {
	if (node.model) {
		if (!node.model.$isdraggable) {
			return null;
		}
		return node;
	}
	if (node.parentElement.model) {
		if (!node.parentElement.model.$isdraggable) {
			return null;
		}
		return node.parentElement;
	}
	return null;
};
Graph.prototype.startDrag = function (event) {
	var graph, i, n = this.getDragNode(event.currentTarget);
	if (!n) {
		return;
	}
	if (this.objDrag) {
		return;
	}
	this.objDrag = n;
	graph = this.objDrag.parentElement;
	if (graph) {
		for (i = 0; i < graph.children.length; i += 1) {
			this.setSelectable(graph.children[i], "on");
		}
	}
	this.offset.x = this.isIE ? window.event.clientX : event.pageX;
	this.offset.y = this.isIE ? window.event.clientY : event.pageY;
	this.startObj.x = this.objDrag.model.x;
	this.startObj.y = this.objDrag.model.y;
};
Graph.prototype.doDrag = function (event) {
	var x, y;
	this.mouse.x = this.isIE ? window.event.clientX : event.pageX;
	this.mouse.y = this.isIE ? window.event.clientY : event.pageY;

	if (this.objDrag !== null) {
		x = (this.mouse.x - this.offset.x) + this.startObj.x;
		y = (this.mouse.y - this.offset.y) + this.startObj.y;

		if (this.model.options.display === "svg") {
			x = x - this.startObj.x;
			y = y - this.startObj.y;
			this.objDrag.setAttribute('transform', "translate("  + x + " "  + y + ")");
		} else {
			this.drawer.setPos(this.objDrag, x, y);
			if (this.objDrag.model) {
				this.objDrag.model.x = x;
				this.objDrag.model.y = y;
				this.objDrag.model.$parent.resize(this.model);
			}
		}
	}
};
Graph.prototype.stopDrag = function (event) {
	var x, y, z, item, entry, parent, pos;
	if (!this.objDrag) {
		return;
	}
	if (!(event.type === "mouseup" || event.type === "mouseout") && !event.currentTarget.isdraggable) {
		return;
	}
	if (event.type === "mouseout") {
		x = this.isIE ? window.event.clientX : event.pageX;
		y = this.isIE ? window.event.clientY : event.pageY;
		if (x < this.board.offsetWidth && y < this.board.offsetHeight) {
			return;
		}
	}
	item = this.objDrag;
	this.objDrag = null;
	entry = item.parentElement;
	if (entry) {
		for (z = 0; z < entry.children.length; z += 1) {
			this.setSelectable(entry.children[z], null);
		}
	}
	parent = item.parentElement;
	if (item.model) {
		if (this.model.options.display === "svg") {
			if (item.getAttributeNS(null, "transform")) {
				z = item.getAttributeNS(null, "transform");
				if (z.substring(0, 6) !== "rotate") {
					pos = z.slice(10, -1).split(' ');
					item.model.x = item.model.x + Number(pos[0]);
					item.model.y = item.model.y + Number(pos[1]);
				}
			}
			item.model.$center = new Pos(item.model.x + (item.model.width / 2), item.model.y + (item.model.height / 2));
			parent.removeChild(item);
			if (item.model.board) {
				item.model.board = null;
			}
		} else {
			this.board.removeChild(item);
		}

		if (item.model.type === "Info") {
			item.model.custom = true;
			item.model.$edge.removeElement(item);
			entry = item.model.$edge.getInfo(item.model);
			item.model.$edge.drawText(this.board, this.drawer, entry, item.model);
		} else {
			item.model.$gui = this.drawer.getNode(item.model, true);
			if (item.model.$gui) {
				parent.appendChild(item.model.$gui);
			}
			entry = item.model.getEdges();
			for (z = 0; z < entry.length; z += 1) {
				entry[z].source.custom = false;
				entry[z].target.custom = false;
			}
		}
		parent = item.model.$parent;
		entry = parent;
		while (entry) {
			this.resize(entry);
			entry = entry.$parent;
		}
		if (parent.$parent) {
			this.redrawNode(parent, true);
			this.resize(this.model);
		} else {
			this.resize(parent);
		}
	}
};
Graph.prototype.redrawNode = function (node, draw) {
	var infoTxt, parent = node.$gui.parentElement;
	parent.removeChild(node.$gui);
	if (node.board) {
		node.board = null;
	}
	if (node.type === "Info") {
		infoTxt = node.edge.getInfo(node.node);
		node.edge.drawText(this.board, this.drawer, infoTxt, node.node);
	} else {
		node.$gui = this.drawer.getNode(node, draw);
		if (node.$gui) {
			parent.appendChild(node.$gui);
		}
	}
	node.$center = new Pos(node.x + (node.width / 2), node.y + (node.height / 2));
	this.resize(node.$parent);
};
Graph.prototype.initDrawer = function (typ) {
	if (typ) {
		typ = typ.toLowerCase();
		if (this.model.options.display === typ) {
			return;
		}
		this.model.options.display = typ;
	} else {
		typ = this.model.options.display;
	}
	this.clearBoard();
	if (typ === "html") {
		this.drawer = new Drawer.HTMLDrawer();
	} else if (typ === "svg") {
		this.drawer = new Drawer.SVGDrawer();
	}
	this.board = this.drawer.getBoard(this);
	this.model.$gui = this.board;
	this.initDragAndDrop();
	this.root.appendChild(this.board);
};
Graph.prototype.serializeXmlNode = function (xmlNode) {
	if (window.XMLSerializer !== undefined) {
		return (new window.XMLSerializer()).serializeToString(xmlNode);
	}
	if (xmlNode.xml !== undefined) {
		return xmlNode.xml;
	}
	return xmlNode.outerHTML;
};
Graph.prototype.utf8$to$b64 = function (str) {
	return window.btoa(unescape(encodeURIComponent(str)));
};
Graph.prototype.ExportPDF = function () {
	var converter, pdf = new jsPDF('l','px',[this.model.width, this.model.height]);
	converter = new svgConverter(this.board, pdf, {removeInvalid: false});
	pdf.save('Download.pdf');
};
Graph.prototype.ExportEPS = function () {
	var converter, doc = new svgConverter.jsEPS({inverting: true});
	converter = new svgConverter(this.board, doc, {removeInvalid: false});
	doc.save();
};
Graph.prototype.ExportPNG = function () {
	var canvas, context, a, image = new Image();
	image.src = 'data:image/svg+xml;base64,' + this.utf8$to$b64(this.serializeXmlNode(this.board));
	image.onload = function () {
		canvas = document.createElement('canvas');
		canvas.width = image.width;
		canvas.height = image.height;
		context = canvas.getContext('2d');
		context.drawImage(image, 0, 0);
		a = document.createElement('a');
		a.download = "download.png";
		a.href = canvas.toDataURL('image/png');
		a.click();
	};
};
Graph.prototype.SaveAs = function (typ) {
	typ = typ.toLowerCase();
	if (typ === "svg") {
		this.Save("image/svg+xml", this.serializeXmlNode(this.board), "download.svg");
	} else if (typ === "html") {
		this.ExportHTML();
	} else if (typ === "png") {
		this.ExportPNG();
	} else if (typ === "pdf") {
		this.ExportPDF();
	} else if (typ === "eps") {
		this.ExportEPS();
	}
};
Graph.prototype.SavePosition = function () {
	var data = [], node, id;
	for (id in this.model.nodes) {
		node = this.model.nodes[id];
		data.push({id: node.id, x: node.x, y: node.y});
	}
	if (window.localStorage && this.model.id) {
		window.localStorage.setItem(this.model.id, JSON.stringify(data));
	}
};
Graph.prototype.LoadPosition = function () {
	if (this.model.id && window.localStorage) {
		var node, id, data = window.localStorage.getItem(this.model.id);
		if (data) {
			data = JSON.parse(data);
			for (id in data) {
				node = data[id];
				if (this.model.nodes[node.id]) {
					this.model.nodes[node.id].x = node.x;
					this.model.nodes[node.id].y = node.y;
				}
			}
			this.clearBoard(true);
			this.draw(this.model);
		}
	}
};
Graph.prototype.Save = function (typ, data, name) {
	var a = document.createElement("a");
	a.href = window.URL.createObjectURL(new Blob([data], {type: typ}));
	a.download = name;
	a.click();
};
Graph.prototype.ExportHTML = function () {
	var data, json = this.model.toJson();
	data = "" + document.head.innerHTML.trim() + "