Smits = {};Smits.Common = {
	nodeIdIncrement : 0,
	activeNode: 0,
	
	/* Rounds float to a defined number of decimal places */
	roundFloat : function(num, digits){
		var i = 0, 
			dec = 1;
		while(i < digits){
			dec *= 10;
			i++;
		}
		return Math.round(num*dec)/dec; 
	},
	
	/* Copies properties from one object to another */
	apply : function(obj, extObj){
		if (obj && typeof extObj == 'object') {
			for (var key in extObj) {
				obj[key] = extObj[key];
			}
		}
		return obj;	
	},
	
	addEventHandler : function(el, eventType, fn, paramsObj){
		try{
			el.addEventListener(
				eventType,
				(function(fn, args){
					return(
						function(e,o) {
							var params = paramsObj;
							params.e = e;
							fn(params);
						}
					);
				}(fn, paramsObj)),
				false
			);
		} catch (err){}
	},
	
	isInteger : function(s) {
		return !isNaN(parseInt(s));
	},

	isXMLSerializerAvailable : function(){
		if (typeof(XMLSerializer) == "function"){
			return true;
		} else {
			return false;
		}
	}

};Smits.PhyloCanvas = function(){
	var phylogram,
		divId,
		newickObject,
		svg,
		dataObject;

	return function(inputFormat, sDivId, canvasWidth, canvasHeight, type){
		/* Privileged Methods */
		this.getNewickObject = function(){
			return newickObject;
		};
		this.clear = function(){
		
		};
		this.scale = function(multiplier){
			svg.svg.scale(multiplier);
		};
		this.getSvg = function(){
			return svg;
		};
		this.getPhylogram = function(){
			return phylogram;
		};
		this.getSvgSource = function(){
			if(Raphael.svg && Smits.Common.isXMLSerializerAvailable()){
				var serialize = new XMLSerializer();
				return serialize.serializeToString(svg.svg.canvas);
			} else {
				return false;
			}
		}
	
		/* CONSTRUCTOR */

		// Process dataset -- assume newick format, else needs to provide format
		if(typeof inputFormat === "object"){
			if(inputFormat.xml){	// default xml format is phyloXML
				if(!inputFormat.fileSource){
					var xj = XMLObjectifier.textToXML(inputFormat.xml); 			// assume we need to clean it up
				} else {
					var xj = inputFormat.xml;
				}
				xj = XMLObjectifier.xmlToJSON(xj);
				dataObject = new Smits.PhyloCanvas.PhyloxmlParse(xj);
			} else if(inputFormat.phyloxml){
				if(!inputFormat.fileSource){
					var xj = XMLObjectifier.textToXML(inputFormat.phyloxml); 			// assume we need to clean it up
				} else {
					var xj = inputFormat.phyloxml;
				}
				xj = XMLObjectifier.xmlToJSON(xj);
				dataObject = new Smits.PhyloCanvas.PhyloxmlParse(xj);
			} else if(inputFormat.nexml){
				if(!inputFormat.fileSource){
					var xj = XMLObjectifier.textToXML(inputFormat.nexml); 			// assume we need to clean it up
				} else {
					var xj = inputFormat.nexml;
				}
				xj = XMLObjectifier.xmlToJSON(xj);
				dataObject = new Smits.PhyloCanvas.NexmlParse(xj, inputFormat)
			} else if(inputFormat.json){
				dataObject = new Smits.PhyloCanvas.PhyloxmlParse(inputFormat.json);
			} else if(inputFormat.newick){
				dataObject = new Smits.PhyloCanvas.NewickParse(inputFormat.newick);
			} else if(inputFormat.nexmlJson){
				console.log('load nexmlJson');
				dataObject = new Smits.PhyloCanvas.NexmlJsonParse(inputFormat);				
			} else {
				alert('Please set the format of input data');
			}
		} else {
			dataObject = new Smits.PhyloCanvas.NewickParse(inputFormat);
		}

		divId = sDivId;
		svg = new Smits.PhyloCanvas.Render.SVG( divId, canvasWidth, canvasHeight );
		
			/* FACTORY */
		if(type == "circular"){
			phylogram = new Smits.PhyloCanvas.Render.CircularPhylogram(
				svg, 
				dataObject
			);		
		} else {
			phylogram = new Smits.PhyloCanvas.Render.Phylogram(
				svg,
				dataObject
			);			
		}		
		
	}
	
}();

Smits.PhyloCanvas.prototype = {
};Smits.PhyloCanvas.Node = function(){
	
	/**
	* Node Class
	* Allows objects to be traversed across children
	*
	*/
	return function(o, parentInstance){
		// initiate object
		this.id = Smits.Common.nodeIdIncrement += 1;
		this.level = 0;
		this.len = 0;
		this.newickLen = 0;
		this.name = '';
		this.type = '';
		this.chart = {};
		this.img = [];
		
		if(o) Smits.Common.apply(this, o);
			
		this.children = new Array();
		
		if(parentInstance){
			parentInstance.children.push(this); 
		}
	}
}();


Smits.PhyloCanvas.Node.prototype = {
	
	getCountAllChildren : function(){
		var nodeCount = 0;

		for (var key in this.children) {
			if(Smits.Common.isInteger(key)){
				var child = this.children[key];
				if(child.children && child.children.length > 0){
					nodeCount += child.getCountAllChildren();
				} else {
					nodeCount ++;
				}			
			}
		}
		return nodeCount;
	},
	
	getCountImmediateChildren : function(){
		var nodeCount = 0;

		for (var key in this.children) {
			var child = this.children[key];
			nodeCount += child.length;
		}
		return nodeCount;
	},
	
	getMidbranchPosition : function(){
		var y = [0,0];  // bounds
		
		for (var i = 0; i < this.children.length; i++) {
			var child = this.children[i];
			
			if(child.children && child.children.length > 0){
				if(i == 0){
					y[0] = child.getMidbranchPosition();				
					y[1] += child.getCountAllChildren();	
				} else if (i == this.children.length - 1){
					y[1] += child.getMidbranchPosition();
				} else {
					y[1] += child.getCountAllChildren();				
				}
			} else {
				if(i == 0){
					y[0] = 1;
					y[1] += 1;	
				} else if (i == this.children.length - 1){
					y[1] += 1;
				} else {
					y[1] += 1;
				}
			}
		}
		
		return y[1] >= y[0] ? ((y[1] - y[0]) / 2) + y[0] : y[0];
	}
	
};Smits.PhyloCanvas.NewickParse = function(){

	var text,
	ch,
	pos,
	mLevel = 0,
	mNewickLen = 0,
	root,
	validate,
		
	object = function (parentNode) {
		var node  = new Smits.PhyloCanvas.Node();
		
		while (ch !== ')' && ch !== ',') {
			if (ch === ':'){
				next();
				node.len = Smits.Common.roundFloat(string(), 4);			// round to 4 decimal places
				if(node.len == 0){
					node.len = 0.0001;
				}
			} else if (ch === "'" || ch === '"'){ 
				node.type = "label";
				node.name = quotedString(ch);
			} else {
				node.type = "label";
				node.name = string();
			}
		}
		node.level = parentNode.level + 1;
		return node;
	},
	
	objectIterate = function(parentNode){
		var node = new Smits.PhyloCanvas.Node();
		if(parentNode){
			node.level = parentNode.level + 1;
		}
		
		while( ch !== ')' ){
			next()
			if( ch === '(' ) {
				node.children.push(objectIterate(node));
			} else {
				node.children.push(object(node));
			}
		}
		
		next();
		if(ch !== ':' && ch !== ')' && ch !== ',' && ch !== ';'){
			node.type = "label";
			node.name = string();
		}
		if(ch === ':'){
			next();
			node.len = Smits.Common.roundFloat(string(), 4);
			if(node.len == 0){
				node.len = 0.0001;
			}
			node.type = "stem";

		}
		return node;		
	},
	
	string = function(){
		var string = '';
		
		while (ch !== ':' && ch !== ')' && ch !== ',' && ch !== ';'){
			string += ch;
			next();
		}
		return string;
	},

	quotedString = function(quoteType){
		var string = '';
		
		while (ch !== quoteType){
			string += ch;
			next();
		}
		return string;
	},	
	
	next = function() {
		ch = text.charAt(pos);
		pos += 1;
		return ch;
	},
	
	recursiveProcessRoot = function(node, parentNode){
		
		if(node.children && node.children.length){
			for( var i = 0; i < node.children.length; i++ ){
				var child = node.children[i];
				if(child.len === 0) {	// Dendogram
					child.len = 1;	
				}
				child.newickLen = Smits.Common.roundFloat(child.len + node.newickLen, 4);
				if(child.level > mLevel) mLevel = child.level;
				if(child.newickLen > mNewickLen) mNewickLen = child.newickLen;
				if(child.children.length > 0){
					recursiveProcessRoot(child, node); 
				}				
			}
		}
		return node;
	};

	return function(parseText){
		/* Privileged Methods */
		this.getRoot = function(){
			return root;
		};
		this.getLevels = function(){
			return mLevel;
		};
		this.getNewickLen = function(){
			return mNewickLen;
		};		
		this.getValidate = function(){
			return validate;
		};		
		
		
		/* CONSTRUCTOR */	
		text = parseText;
		pos = 0;
		
		next();
		root = objectIterate();
		root = recursiveProcessRoot(root);
	}

}();

Smits.PhyloCanvas.NewickParse.prototype = {

};Smits.PhyloCanvas.PhyloxmlParse = function(){

	var mLevel = 0,
	mNewickLen = 0,
	root,
	validate,
		
	recursiveParse = function(clade, parentNode){
		var node = new Smits.PhyloCanvas.Node();
		if(parentNode){
			node.level = parentNode.level + 1;
		}
		
		if(clade.clade && clade.clade.length){
			for(var i = 0; i < clade.clade.length; i++){
				var thisClade = clade.clade[i];
				node.children.push(recursiveParse(thisClade, node));
			}
		}
		if(clade.branch_length){	// Branches can be attributes or own element
			if(typeof clade.branch_length === 'object'){
				clade.branch_length = clade.branch_length[0].Text;
			}

			node.len = Smits.Common.roundFloat(clade.branch_length, 4);			// round to 4 decimal places
			if(node.len == 0){
				node.len = 0.0001;
			}			
		}
		if(clade.name){
			node.type = 'label';
			node.name = clade.name[0].Text;
			if(clade.name[0] && clade.name[0].style){
				node.style = clade.name[0].style;
			}
			if(clade.name[0] && clade.name[0].bgStyle){
				node.bgStyle = clade.name[0].bgStyle;
			}			
		} else if(clade.confidence){
			node.name = clade.confidence[0].Text;
		}

		/* Collect further info that might be used as a label */
		if (clade.sequence && clade.sequence[0] && clade.sequence[0].name && clade.sequence[0].name[0] && clade.sequence[0].name[0].Text){
			node.sequenceName = clade.sequence[0].name[0].Text;
		}
		if (clade.taxonomy && clade.taxonomy[0]){
			if(clade.taxonomy[0].scientific_name && clade.taxonomy[0].scientific_name[0] && clade.taxonomy[0].scientific_name[0].Text){
				node.taxonomyScientificName = clade.taxonomy[0].scientific_name[0].Text;
			}
			if (clade.taxonomy[0].common_name  && clade.taxonomy[0].common_name[0] && clade.taxonomy[0].common_name[0].Text){
				node.taxonomyCommonName = clade.taxonomy[0].common_name[0].Text;
			}
		}
		if (clade.sequence && clade.sequence[0] && clade.sequence[0].accession && clade.sequence[0].accession[0] && clade.sequence[0].accession[0].Text){
			node.sequenceAccession = clade.sequence[0].accession[0].Text;
		}
		if (clade.point ){
			node.LatLong = [clade.point[0].lat[0].Text, clade.point[0]['long'][0].Text];
		}		

		
		/* Prioritization of Label */
		if(!node.name){
			if(node.sequenceName){
				node.name = node.sequenceName;
			} else if (node.taxonomyScientificName){
				node.name = node.taxonomyScientificName;
			} else if (node.taxonomyCommonName){
				node.name = node.taxonomyCommonName;
			} else if (node.sequenceAccession){
				node.name = node.sequenceAccession;
			}
			if(node.name){	// if name is now set, type is 'label'
				node.type = 'label'; 
			}
		}
		
		if(clade.annotation){
			if(clade.annotation[0] && clade.annotation[0].desc && clade.annotation[0].desc[0] && clade.annotation[0].desc[0].Text){
				node.description = clade.annotation[0].desc[0].Text;
			}
			if(clade.annotation[0] && clade.annotation[0].uri && clade.annotation[0].uri[0] && clade.annotation[0].uri[0].Text){
				node.uri = clade.annotation[0].uri[0].Text;
			}			
			if(clade.annotation[0] && clade.annotation[0].img){
				for(var i in clade.annotation[0].img){
					if(Smits.Common.isInteger(i)){
						node.img[i] = clade.annotation[0].img[i].Text;
					}
				}
			}
		}
		if(clade.chart){
			if(clade.chart[0]){
				for(var i in clade.chart[0]){
					if(i != 'Text' && i != '_children'){
					node.chart[i] = clade.chart[0][i][0].Text;
					}
				}
			}
			
		}
		
		// Validation
		if(node && node.level > 1){
			if(!node.len){
				validate = 'Error. Please include Branch Lengths - we only draw rooted phylogenetic trees.';
			}
		}
			
		return node;
	},
	
	recursiveProcessRoot = function(node, parentNode){
		if(node.children && node.children.length){
			for( var i = 0; i < node.children.length; i++){
				var child = node.children[i];
				child.newickLen = Math.round( (child.len + node.newickLen) *10000)/10000;
				if(child.level > mLevel) mLevel = child.level;
				if(child.newickLen > mNewickLen) mNewickLen = child.newickLen;
				if(child.children.length > 0){
					recursiveProcessRoot(child, node); 
				}				
			}
		}
		return node;
	},
	
	recursiveProcessParameters = function(parametersEl, treeType){
		for (var i in parametersEl){
			if(i != '_children' && i != 'Text'){
				if(i == 'rectangular' || i == 'circular'){
					recursiveProcessParameters(parametersEl[i][0], i);
				} else {
					if(!Smits.PhyloCanvas.Render.Parameters[i]) {  Smits.PhyloCanvas.Render.Parameters[i] = {}; };
					Smits.PhyloCanvas.Render.Parameters.set(i, parametersEl[i][0].Text, treeType);
				}
			}
		}
		return;
	};

	return function(jsonString){
		/* Privileged Methods */
		this.getRoot = function(){
			return root;
		};
		this.getLevels = function(){
			return mLevel;
		};
		this.getNewickLen = function(){
			return mNewickLen;
		};		
		this.getValidate = function(){
			return validate;
		};
		
		
		/* CONSTRUCTOR */	
		if(jsonString.phylogeny && jsonString.phylogeny[0] && jsonString.phylogeny[0].clade){
			root = recursiveParse(jsonString.phylogeny[0].clade[0]);
		}
		
		if(jsonString.phylogeny && jsonString.phylogeny[0] && jsonString.phylogeny[0].render && jsonString.phylogeny[0].render[0]){
			var render = jsonString.phylogeny[0].render[0];
			
			// Custom Styles
			if(render && render.styles){
				var styles = render.styles[0];
				for (var i in styles){
					if(i != '_children' && i != 'Text'){
						if(!Smits.PhyloCanvas.Render.Style[i]) {  Smits.PhyloCanvas.Render.Style[i] = {}; };
						for(var j in styles[i][0]){
							if(j != '_attributes'){
								Smits.PhyloCanvas.Render.Style[i][j.replace('_', '-')] = styles[i][0][j];		// This is quite painful, as xml does not allow dashes
							}
						}
						
						
					}
				}
			}
			
			// Custom Parameters
			if(render && render.parameters){
				recursiveProcessParameters(render.parameters[0]);
			}			
			
			// Charts
			if(render && render.charts){
				var charts = render.charts[0];
				for (var i in charts){
					if(i != '_children' && i != 'Text'){
						for(var j in charts[i]){
							if(charts[i][j].type == "binary"){
								charts[i][j].chart = i;
								Smits.PhyloCanvas.Render.Parameters.binaryCharts.push(charts[i][j]);
							} else if (charts[i][j].type == "bar"){
								charts[i][j].chart = i;
								Smits.PhyloCanvas.Render.Parameters.barCharts.push(charts[i][j]);
							}
						}
					}
				}
			}			
			
		}
		
		root = recursiveProcessRoot(root);

	}

}();

Smits.PhyloCanvas.PhyloxmlParse.prototype = {

};Smits.PhyloCanvas.NexmlParse = function(){

	var mLevel = 0,
	mNewickLen = 0,
	root,
	validate,
	nexEdges,
	nexNodes,
		
	recursiveParse = function(nexnode, nexlen, parentNode){
		var node = new Smits.PhyloCanvas.Node();
		if(parentNode){
			node.level = parentNode.level + 1;
		}
		
		for(var i = 0; i < nexEdges.length; i++){
			if(nexEdges[i].source == nexnode.id){
				for(var j = 0; j < nexNodes.length; j++){
					if(nexEdges[i].target == nexNodes[j].id){
						node.children.push(recursiveParse(nexNodes[j], nexEdges[i].length, node));
					}
				}
			}
		}
		
		if(nexlen) {
			node.len = Smits.Common.roundFloat(nexlen, 4);			// round to 4 decimal places
			if(node.len == 0){
				node.len = 0.0001;
			}			
		}

		if(nexnode.label){
			node.type = 'label';
			node.name = nexnode.label;
			if(nexnode.style){
				node.style = nexnode.style;
			}
		} 

	
		// Validation
		if(node && node.level > 1){
			if(!node.len){
				validate = 'Error. Please include Branch Lengths - we only draw rooted phylogenetic trees.';
			}
		} 
			
		return node;
	},
	
	recursiveProcessRoot = function(node, parentNode){
		if(node.children && node.children.length){
			for( var i = 0; i < node.children.length; i++){
				var child = node.children[i];
				child.newickLen = Math.round( (child.len + node.newickLen) *10000)/10000;
				if(child.level > mLevel) mLevel = child.level;
				if(child.newickLen > mNewickLen) mNewickLen = child.newickLen;
				if(child.children.length > 0){
					recursiveProcessRoot(child, node); 
				}				
			}
		}
		return node;
	},
	recursiveProcessParameters = function(parametersEl, treeType){
		for (var i in parametersEl){
			if(i != '_children' && i != 'Text'){
				if(i == 'rectangular' || i == 'circular'){
					recursiveProcessParameters(parametersEl[i][0], i);
				} else {
					if(!Smits.PhyloCanvas.Render.Parameters[i]) {  Smits.PhyloCanvas.Render.Parameters[i] = {}; };
					Smits.PhyloCanvas.Render.Parameters.set(i, parametersEl[i][0].Text, treeType);
				}
			}
		}
		return;
	};

	return function(jsonString, inputFormat){
		/* Privileged Methods */
		this.getRoot = function(){
			return root;
		};
		this.getLevels = function(){
			return mLevel;
		};
		this.getNewickLen = function(){
			return mNewickLen;
		};		
		this.getValidate = function(){
			return validate;
		};
		
		
		if(inputFormat.tree && jsonString.trees[0] && jsonString.trees[0].tree[(inputFormat.tree-1)]){
			nexEdges = jsonString.trees[0].tree[(inputFormat.tree-1)].edge;
			nexNodes = jsonString.trees[0].tree[(inputFormat.tree-1)].node;
		} else {
			nexEdges = jsonString.trees[0].tree[0].edge;
			nexNodes = jsonString.trees[0].tree[0].node;
		}
		
		for(i = 0; i < nexNodes.length; i++){
			if(nexNodes[i].root && nexNodes[i].root == "true"){
				root = nexNodes[i];
			}
		}
		
		if(root){
			root = recursiveParse(root);
		
			root = recursiveProcessRoot(root);
		} else {
			validate = 'Error. Currently, only rooted NeXML trees are supported.';
		}

	};

}();

Smits.PhyloCanvas.NexmlParse.prototype = {

};Smits.PhyloCanvas.NexmlJsonParse = function(){

	var mLevel = 0,
	mNewickLen = 0,
	root,
	validate,
	nexEdges = [], nexNodes = [],
		
	recursiveParse = function(nexnode, nexlen, parentNode){
		var node = new Smits.PhyloCanvas.Node();
		if(parentNode){
			node.level = parentNode.level + 1;
		}
		
		for(var i = 0; i < nexEdges.length; i++){
			if(nexEdges[i].source == nexnode.id){
				for(var j = 0; j < nexNodes.length; j++){
					if(nexEdges[i].target == nexNodes[j].id){
						node.children.push(recursiveParse(nexNodes[j], nexEdges[i].length, node));
					}
				}
			}
		}
		
		if(nexlen) {
			node.len = Smits.Common.roundFloat(nexlen, 4);			// round to 4 decimal places
			if(node.len == 0){
				node.len = 0.0001;
			}			
		}

		if(nexnode.label){
			node.type = 'label';
			node.name = nexnode.label;
			if(nexnode.style){
				node.style = nexnode.style;
			}
			if(nexnode.bgStyle){
				node.bgStyle = nexnode.bgStyle;
			}
		} 

	
		// Validation
		if(node && node.level > 1){
			if(!node.len){
				validate = 'Error. Please include Branch Lengths - we only draw rooted phylogenetic trees.';
			}
		} 
			
		return node;
	},
	
	recursiveProcessRoot = function(node, parentNode){
		if(node.children && node.children.length){
			for( var i = 0; i < node.children.length; i++){
				var child = node.children[i];
				child.newickLen = Math.round( (child.len + node.newickLen) *10000)/10000;
				if(child.level > mLevel) mLevel = child.level;
				if(child.newickLen > mNewickLen) mNewickLen = child.newickLen;
				if(child.children.length > 0){
					recursiveProcessRoot(child, node); 
				}				
			}
		}
		return node;
	},
	recursiveProcessParameters = function(parametersEl, treeType){
		for (var i in parametersEl){
			if(i != '_children' && i != 'Text'){
				console.log([i, parametersEl]);
				if(i == 'rectangular' || i == 'circular'){
					recursiveProcessParameters(parametersEl[i], i);
				} else {
					if(!Smits.PhyloCanvas.Render.Parameters[i]) {  Smits.PhyloCanvas.Render.Parameters[i] = {}; };
					console.log([i, parametersEl[i]]);
					//Smits.PhyloCanvas.Render.Parameters.set(i, parametersEl[i][0].Text, treeType);
				}
			}
		}
		return;
	};

	return function(inputFormat){
		/* Privileged Methods */
		this.getRoot = function(){
			return root;
		};
		this.getLevels = function(){
			return mLevel;
		};
		this.getNewickLen = function(){
			return mNewickLen;
		};		
		this.getValidate = function(){
			return validate;
		};
		
		var jsonString = inputFormat.nexmlJson.nexml;
		

		/* RENDER STYLES */
		var render = jsonString.render;
		
		// Custom Styles
		if(render && render.styles){
			var styles = render.styles;
			for (var i in styles){
				if(i != '_children' && i != 'Text'){
					if(!Smits.PhyloCanvas.Render.Style[i]) {  Smits.PhyloCanvas.Render.Style[i] = {}; };
					for(var j in styles[i]['@attributes']){
						if(j != '_attributes'){
							Smits.PhyloCanvas.Render.Style[i][j.replace('_', '-')] = styles[i]['@attributes'][j];		// This is quite painful, as xml does not allow dashes
						}
					}
					
					
				}
			}
		}
		// Custom Parameters
		if(render && render.parameters){
			recursiveProcessParameters(render.parameters);
		}
		
			
			
		
		
		
		if(inputFormat.tree && jsonString.trees[0] && jsonString.trees[0].tree[(inputFormat.tree-1)]){
			nexEdges = jsonString.trees[0].tree[(inputFormat.tree-1)].edge;
			nexNodes = jsonString.trees[0].tree[(inputFormat.tree-1)].node;
		} else {
			for(var i = 0; i < jsonString.trees.tree.edge.length; i++){
				nexEdges.push(jsonString.trees.tree.edge[i]['@attributes']);
			}
			for(var i = 0; i < jsonString.trees.tree.node.length; i++){
				nexNodes.push(jsonString.trees.tree.node[i]['@attributes']);
			}
		}
		
		for(var i = 0; i < nexNodes.length; i++){
			if(nexNodes[i].root && nexNodes[i].root == "true"){
				root = nexNodes[i];
			}
		}
		
		if(root){
			root = recursiveParse(root);
		
			root = recursiveProcessRoot(root);
		} else {
			validate = 'Error. Currently, only rooted NeXML trees are supported.';
		}

	};

}();

Smits.PhyloCanvas.NexmlParse.prototype = {

};Smits.PhyloCanvas.Render = {};Smits.PhyloCanvas.Render.Style = {

	/* Default Styles */
	
	line: {
		"stroke": 		'rgb(0,0,0)',
		"stroke-width":	1
	},
	
	text: {
		"font-family":	'Verdana',
		"font-size":	12,
		"text-anchor":	'start'
	},
	
	path: {
		"stroke": 		'rgb(0,0,0)',
		"stroke-width":	1	
	},
	
	connectedDash : {
		"stroke": 			'rgb(200,200,200)',
		"stroke-dasharray":	". "
	},
	
	textSecantBg : {
		"fill": 	'#EEE',
		"stroke":	'#DDD'
	},
	
	highlightedEdgeCircle : {
		"fill": 	'red'
	},
	
	barChart : {
		fill:		'#003300',
		stroke:		'#DDD'
	},
	
	getStyle : function(requestStyle, fallbackStyle){
		if(this[requestStyle]){
			return this[requestStyle];
		} else {
			return this[fallbackStyle];
		}
	
	}



};Smits.PhyloCanvas.Render.Parameters = {

	/* DEFAULT PARAMETERS */
	jsOverride: 0,				// If set, js will override chart's file setting
	
	/** Phylogram parameters are separated because they behave very differently **/
	
	/* Rectangular Phylogram */
	Rectangular : {
		bufferX			: 200, 			// Reduces the available canvas space for tree branches, allowing
										// for more space for the textual/charting components
		bufferY			: 40,
		bufferInnerLabels : 10, 		// Pixels
		bufferOuterLabels : 5, 			// Pixels
		minHeightBetweenLeaves : 10,  	// Should probably set pretty low, as clipping may occur if it needs to be implemented		
		
		alignPadding	: 0,			// Pixels to push the labels out by - this extension should be 
										// compensated by an increase in bufferX too
		alignRight		: false,
		
		showScaleBar	: false			// (STRING,  e.g. "0.05") Shows a scale bar at the bottom of the tree
	},
	
	/* Circular Phylogram */
	Circular : {
		bufferRadius 		: 0.33,		// Margins of Tree Circle
										// If > 1, it is in pixels
										// If < 1, it is a percentage of the full canvas size		
		bufferAngle 		: 20,		// controls split size in circle		
		initStartAngle 		: 160,		
		innerCircleRadius 	: 0,
		minHeightBetweenLeaves : 5,

		/* Labels */
		bufferInnerLabels : 2, 			// Pixels
		bufferOuterLabels : 5 			// Pixels
	},
	
	/* Charts */
	binaryCharts : [],
	barCharts : [],

		/* Binary Defaults */
		binaryChartBufferInner : 5, 
		binaryChartBufferSiblings : 0.01,
		binaryChartThickness : 15,
		binaryChartDisjointed : false,
			
		/* Bar Defaults */
		barChartBufferInner : 3,
		barChartHeight : 50,
		barChartWidth : 0.5,	// If > 1, it is in pixels
								// If < 1, it is a percentage of the node width 
						
		/* 
			Rollover Events 
				At minimum, the params object has the following properties:
					.svg
					.node
					.x
					.y
					.textEl
		*/
		mouseRollOver : function(params) {
			if(params.node.edgeCircleHighlight){
				params.node.edgeCircleHighlight.show();
			} else {
				var circleObject = params.svg.draw(
					new Smits.PhyloCanvas.Render.Circle(
						params.x, params.y, 5,
						{ attr: Smits.PhyloCanvas.Render.Style.highlightedEdgeCircle }
					)
				);
				params.node.edgeCircleHighlight = circleObject[0];
			}					
			params.textEl.attr({ fill: 'red' });
		},
		mouseRollOut : function(params) {
			params.node.edgeCircleHighlight.hide();
			params.textEl.attr({ fill: '#000' });
		},

	set : function(param, value, treeType){
		if(!this.jsOverride){
			if(treeType){
				if(treeType == 'circular'){				
					this['Circular'][param] = parseFloat(value);
				} else if (treeType == 'rectangular'){
					this['Rectangular'][param] = parseFloat(value);
				}
			} else {
				this[param] = parseFloat(value);
			}
		}
	}
};Smits.PhyloCanvas.Render.Line = function(){

	return function(x1, x2, y1, y2, params){
		/* Defaults */	
		this.type = 'line';
		this.attr = Smits.PhyloCanvas.Render.Style.line;
		
		this.x1 = x1;
		this.x2 = x2;
		this.y1 = y1;
		this.y2 = y2;
		
		if(params) {
			Smits.Common.apply(this, params);
			if(params.attr) this.attr = params.attr;
		}
		
	}
}();Smits.PhyloCanvas.Render.Text = function(){

	return function(x, y, text, params){
		/* Defaults */
		this.type = 'text';
		this.attr = Smits.PhyloCanvas.Render.Style.text;
		
		this.x = x;
		this.y = y;
		this.text = text;
		
		if(params) {
			Smits.Common.apply(this, params);
			if(params.attr) this.attr = params.attr;
		}
	}
}();Smits.PhyloCanvas.Render.Path = function(){
	var attr = Smits.PhyloCanvas.Render.Style.path;
	
	return function(path, params){
		/* Defaults */
		this.type = 'path';
		this.attr = Smits.PhyloCanvas.Render.Style.path;
		
		this.path = path;
		if(params) {
			Smits.Common.apply(this, params);
			if(params.attr) this.attr = params.attr;
		}
		
	}
}();Smits.PhyloCanvas.Render.Circle = function(){

	return function(x, y, radius, params){
		/* Defaults */	
		this.type = 'circle';
	
		this.x = x;
		this.y = y;
		this.radius = radius;
		
		if(params) {
			Smits.Common.apply(this, params);
			if(params.attr) this.attr = params.attr;
		}
		
	}
}();Smits.PhyloCanvas.Render.SVG = function(){
	var divId,
		canvasSize;
		
	return function(sDivId, canvasWidth, canvasHeight){
	
		/* CONSTRUCTOR */
		divId = sDivId;
		this.canvasSize = [canvasWidth, canvasHeight];
		
		this.svg = Raphael(sDivId, this.canvasSize[0], this.canvasSize[1]);
		
	}
	
}();

Smits.PhyloCanvas.Render.SVG.prototype = {

	render : function(){
		var instructs = this.phylogramObject.getDrawInstructs();
		
		for (var i = 0; i < instructs.length; i++) {
		   if(instructs[i].type == 'line'){
				var line = this.svg.path(["M", instructs[i].x1, instructs[i].y1, "L", instructs[i].x2, instructs[i].y2]).attr(Smits.PhyloCanvas.Render.Style.line);
			} else if(instructs[i].type == 'path'){
				var path = this.svg.path(instructs[i].path).attr(instructs[i].attr);			
			} else if(instructs[i].type == 'circle'){
				var path = this.svg.circle(instructs[i].x, instructs[i].y, instructs[i].radius).attr({
					"stroke": 'red'
				});
			} else {
				var text = this.svg.text(instructs[i].x, instructs[i].y, instructs[i].text).attr(Smits.PhyloCanvas.Render.Style.text);
				if(instructs[i].attr){
					text.attr(instructs[i].attr);
				}
				if(instructs[i].rotate){
					text.rotate(instructs[i].rotate);
				}
				
				var bbox = text.getBBox();
				var hyp = Math.sqrt( (bbox.height * bbox.height) + (bbox.width * bbox.width) );	// get hypotenuse
				
			} 
		}
	},
	
	draw : function(instruct){
		var obj, 
			param;
			
	   if(instruct.type == 'line'){
			obj = this.svg.path(["M", instruct.x1, instruct.y1, "L", instruct.x2, instruct.y2]).attr(Smits.PhyloCanvas.Render.Style.line);
		} else if(instruct.type == 'path'){
			obj = this.svg.path(instruct.path).attr(instruct.attr);			
		} else if(instruct.type == 'circle'){
			obj = this.svg.circle(instruct.x, instruct.y, instruct.radius).attr({
				"stroke": 'red'
			});
		} else if(instruct.type == 'text'){
			obj = this.svg.text(instruct.x, instruct.y, instruct.text).attr(Smits.PhyloCanvas.Render.Style.text);
			if(instruct.attr){
				obj.attr(instruct.attr);
			}
			if(instruct.rotate){
				obj.rotate(instruct.rotate);
			}
			
			var bbox = obj.getBBox();
			param = Math.sqrt( (bbox.height * bbox.height) + (bbox.width * bbox.width) );	// get hypotenuse
		} 
			
		return [obj, param];
	}

};Smits.PhyloCanvas.Render.Phylogram = function(){

	var svg,
	sParams = Smits.PhyloCanvas.Render.Parameters.Rectangular, 	// Easy Reference
	canvasX, canvasY,
	scaleX, scaleY, maxBranch,
	minHeightBetweenLeaves,
	firstBranch = true,
	absoluteY = 0, maxLabelLength = 0,
	outerX, outerY, outerRadius,
	x1, x2, y1, y2, 
	positionX, positionY,
	bufferX, bufferY, labelsHold = [],
	
	textPadding = function (y){
		return y + Math.round(y / 4);
	},
	
	rectLinePathArray = function (x1, y1, x2, y2){
		return ["M", x1, y1, "L", x2, y1, "L", x2, y2, "L", x1, y2, "Z"];
	},
	
	recursiveCalculateNodePositions = function (node, positionX){
		if(node.len){ 
			if(firstBranch){
				firstBranch = false;
			} else {
				if(node.children.length == 0) absoluteY = Smits.Common.roundFloat(absoluteY + scaleY, 4);
			}
		}
		
		if(node.children.length > 0){
			var nodeCoords = [], x1,x2,y1,y2;
			if(node.len){ // draw stem
				x1 = positionX;
				x2 = positionX = Smits.Common.roundFloat(positionX + (scaleX * node.len), 4);
				y1 = absoluteY + (node.getMidbranchPosition() * scaleY);
				y2 = y1;

				svg.draw(new Smits.PhyloCanvas.Render.Line(x1, x2, y1, y2));
			}
			
			if(node.name){ // draw bootstrap values
				var attr = {};
				attr = Smits.PhyloCanvas.Render.Style.getStyle('bootstrap', 'text');
				if(node.uri) { attr.href = node.uri };
				if(node.description) {attr.title = node.description };
				if(node.level == 0){ 
					var innerX2 = 2;				
					var innerY2 = absoluteY + (node.getMidbranchPosition() * scaleY);
				} else {
					var innerX2 = x2;
					var innerY2 = y2;
				}
				
				svg.draw(
					new Smits.PhyloCanvas.Render.Text(
						innerX2 + 5, innerY2,
						node.name,
						{
							attr: attr
						}
					)
				);			
				//svg.draw(new Smits.PhyloCanvas.Render.Text(x2 + 5, y2, node.name));
			}
			
			if(node.children && node.children.length){
				for(var i = 0; i < node.children.length; i++){
					var child = node.children[i];
					nodeCoords.push(recursiveCalculateNodePositions(child, positionX));
				}
			}
			
			var flatNodeCoords = []; // establish vertical bounds
			for ( var i = 0; i < nodeCoords.length; i++ ){
				if(nodeCoords[i][0]) flatNodeCoords.push(nodeCoords[i][0]);
				if(nodeCoords[i][1]) flatNodeCoords.push(nodeCoords[i][1]);
			}
			var verticalY1 = Math.min.apply(null, flatNodeCoords );
			var verticalY2 = Math.max.apply(null, flatNodeCoords);
			
			// draw vertical
			// hack: little elbows at ends in order to prevent stair-effects at edges
			svg.draw( 
				new Smits.PhyloCanvas.Render.Path( 
					[
						"M", positionX + 0.0001, verticalY1,
						"L", positionX, verticalY1,
						"L", positionX, verticalY2,
						"L", positionX + 0.0001, verticalY2
					],
					{ attr : Smits.PhyloCanvas.Render.Style.line }
				)				
			);
			
		} else {
			// label
			x1 = positionX;
			x2 = Smits.Common.roundFloat(positionX + (scaleX * node.len), 2);
			y1 = absoluteY;
			y2 = absoluteY;
				
			// preserve for later processing
			node.y = absoluteY;
			labelsHold.push(node);				
				
			svg.draw(new Smits.PhyloCanvas.Render.Line(x1, x2, y1, y2));
			if(sParams.alignRight){
				svg.draw(
					new Smits.PhyloCanvas.Render.Path(
						["M", x2, y1, "L", sParams.alignPadding + maxBranch, y2],
						{ attr : Smits.PhyloCanvas.Render.Style.connectedDash }
					)
				);			
			}
			
			if(node.name){
				var attr = {};
				if(node.style){
					attr = Smits.PhyloCanvas.Render.Style.getStyle(node.style, 'text');
				}
				attr["text-anchor"] = 'start';
				if(node.uri) { attr.href = node.uri };
				if(node.description) {attr.title = node.description };
				
				var draw = svg.draw(
					new Smits.PhyloCanvas.Render.Text(
						sParams.alignRight ? maxBranch + sParams.bufferInnerLabels + sParams.alignPadding : x2 + sParams.bufferInnerLabels, y2,
						node.name,
						{
							attr: attr
						}
					)
				);				
				if(node.style){
					console.log([node, attr]);
				}
				maxLabelLength = Math.max(draw[1], maxLabelLength);
				
				// Rollover, Rollout and Click Events
				if(Smits.PhyloCanvas.Render.Parameters.mouseRollOver){
					Smits.Common.addEventHandler(
						draw[0].node, 
						'mouseover', 
						Smits.PhyloCanvas.Render.Parameters.mouseRollOver, 
						{ svg: svg, node: node, x: x2, y: y2, textEl: draw[0] }
					);
				}
				if(Smits.PhyloCanvas.Render.Parameters.mouseRollOut){
					Smits.Common.addEventHandler(
						draw[0].node, 
						'mouseout', 
						Smits.PhyloCanvas.Render.Parameters.mouseRollOut, 
						{ svg: svg, node: node, x: x2, y: y2, textEl: draw[0] }
					);				
				}
				if(Smits.PhyloCanvas.Render.Parameters.onClickAction){
					Smits.Common.addEventHandler(
						draw[0].node, 
						'click', 
						Smits.PhyloCanvas.Render.Parameters.onClickAction, 
						{ svg: svg, node: node, x: x2, y: y2, textEl: draw[0] }
					);				
				}
			}
		
		}
		
		return [y1, y2];

	},
	
	drawScaleBar = function (){
		y = absoluteY + scaleY;
		x1 = 0;
		x2 = sParams.showScaleBar * scaleX;
		svg.draw(new Smits.PhyloCanvas.Render.Line(x1, x2, y, y));
		svg.draw(new Smits.PhyloCanvas.Render.Text(
			(x1+x2)/2, 
			y-8, 
			sParams.showScaleBar)
		);
	},
	
	renderBinaryChart = function(x, groupName, params){
		var bufferInner = (params && params.bufferInner ? params.bufferInner : 0) | Smits.PhyloCanvas.Render.Parameters.binaryChartBufferInner,
			bufferSiblings = (params && params.bufferSiblings ? params.bufferSiblings * scaleY : 0) | (Smits.PhyloCanvas.Render.Parameters.binaryChartBufferSiblings < 1 ? scaleY * Smits.PhyloCanvas.Render.Parameters.binaryChartBufferSiblings : Smits.PhyloCanvas.Render.Parameters.binaryChartBufferSiblings),		
			thickness = (params && params.thickness ? params.thickness : 0) | Smits.PhyloCanvas.Render.Parameters.binaryChartThickness,
			beginY;
			
		for(var i = 0; i < labelsHold.length; i++){
			var node = labelsHold[i];
			svg.draw(
				new Smits.PhyloCanvas.Render.Path(
					rectLinePathArray(
						x + bufferInner,
						node.y - (scaleY/2) + (bufferSiblings/2),
						x + bufferInner + thickness, 
						node.y + (scaleY/2) - (bufferSiblings/2)
					),
					{ attr: Smits.PhyloCanvas.Render.Style.getStyle(node.chart[groupName], 'textSecantBg') }
				)
			);			
		}
		return x + bufferInner + thickness;
	},
	
	renderBarChart = function(x, groupName, params){
		var allValues = [], maxValue,
			bufferInner = params && params.bufferInner ? params.bufferInner : 0 | Smits.PhyloCanvas.Render.Parameters.barChartBufferInner,
			height = params && params.height ? params.height : 0 | Smits.PhyloCanvas.Render.Parameters.barChartHeight,
			width = params && params.width ? (params.width < 1 ? scaleY * params.width : params.width ) : 0 | (Smits.PhyloCanvas.Render.Parameters.barChartWidth < 1 ? scaleY * Smits.PhyloCanvas.Render.Parameters.barChartWidth : Smits.PhyloCanvas.Render.Parameters.barChartWidth),
			scaleHeight = 0;
		
		// Need to get max value
		for(var i = 0; i < labelsHold.length; i++){
			allValues.push(labelsHold[i].chart[groupName]);
		}
		maxValue = Math.max.apply(null, allValues);
		scaleHeight = Smits.Common.roundFloat(height / maxValue, 4);
		
		for(var i = 0; i < labelsHold.length; i++){
			var node = labelsHold[i];
			svg.draw(
					new Smits.PhyloCanvas.Render.Path(
						rectLinePathArray(
							x + bufferInner,
							node.y - (width/2),
							x + bufferInner + (scaleHeight * node.chart[groupName]), 
							node.y + (width/2)
						),					
						{ attr: Smits.PhyloCanvas.Render.Style.getStyle(node.chart[groupName], 'barChart') }
					)
				);					
		}
		
		return x + bufferInner + height;
	};
	
	return function(sSvg, dataObject){	
		absoluteY = 0;
		
		/* Privileged Methods */
		this.getCanvasSize = function(){
			return [canvasX, canvasY];
		};		
		this.getRoot = function(){
			return dataObject.getRoot();
		};
		
		/* CONSTRUCTOR */
		if(dataObject.getValidate()){   // Validate
			svg.draw(0,0, dataObject.getValidate());
		}
		
		svg = sSvg;
		var node = dataObject.getRoot();
		var mNewickLen = dataObject.getNewickLen();
		
		canvasX = svg.canvasSize[0];			// Full Canvas Width
		canvasY = svg.canvasSize[1];			// Full Canvas Height
		
		bufferX = sParams.bufferX;
		bufferY = sParams.bufferY;
		minHeightBetweenLeaves = sParams.minHeightBetweenLeaves;
				
		scaleX = Math.round((canvasX - bufferX) / mNewickLen);
		scaleY = Math.round((canvasY - bufferY) / (sParams.showScaleBar ? node.getCountAllChildren() + 1 : node.getCountAllChildren()) );
		if(scaleY < minHeightBetweenLeaves){
			scaleY = minHeightBetweenLeaves;
		}
		maxBranch = Math.round( canvasX - bufferX );	
		
		if(Smits.PhyloCanvas.Render.Parameters.binaryCharts.length || Smits.PhyloCanvas.Render.Parameters.barCharts.length){
			sParams.alignRight = true;
		}
		
		recursiveCalculateNodePositions(node, 0);
		
		// Draw Scale Bar
		if(sParams.showScaleBar){
			drawScaleBar();
		}
		
		outerX = maxBranch + maxLabelLength + sParams.bufferInnerLabels;
		// Draw secant highlights
		if(Smits.PhyloCanvas.Render.Parameters.binaryCharts.length){
			var binaryCharts = Smits.PhyloCanvas.Render.Parameters.binaryCharts;
			for(var i in binaryCharts){
				outerX = renderBinaryChart(outerX, binaryCharts[i].chart, binaryCharts[i]);
			}
		}		
		
		// Draw Bar Chart
		if(Smits.PhyloCanvas.Render.Parameters.barCharts.length){
			var barCharts = Smits.PhyloCanvas.Render.Parameters.barCharts;
			for(var i in barCharts){
				outerRadius = renderBarChart(outerX, barCharts[i].chart, barCharts[i]);
			}
		}				

	}
}();

Smits.PhyloCanvas.Render.Phylogram.prototype = {

};Smits.PhyloCanvas.Render.CircularPhylogram = (function(){

	var svg,
		sParams = Smits.PhyloCanvas.Render.Parameters.Circular, 	// Easy Reference
		canvasX, canvasY, canvasMinEdge,
		scaleRadius, scaleAngle,
		minHeightBetweenLeaves,
		innerCircleRadius,
		firstBranch = true,
		absoluteY = 0, cx, cy, maxBranch, 
		labelsHold = [], bgLabelsHold = [],
		bufferRadius, bufferAngle, outerRadius,
		maxLabelLength = 0,
		initStartAngle,
		rad = (Math.PI / 180);

	function secPosition(r, deg){
		deg += initStartAngle;
		return [ 
			Smits.Common.roundFloat(cx + r * Math.sin(deg * rad), 4), 
			Smits.Common.roundFloat(cy + r * Math.cos(deg * rad), 4)
		]; // x,y
	};
	function secant(r, startAngle, endAngle, params){
		var startPos = secPosition(r, startAngle);
		var endPos = secPosition(r, endAngle);
		var arr = [],
			n, inv = 0;
		
		if(Math.abs(normalizeAngle(endAngle-startAngle)) > 180) {
			n = 1;
		} else {
			n = -1;
		}
		
		// Parameter changes
		if(params && params.invertSecant){
			n *= -1;
			inv = 1;
		}
		if(params && params.noMove){
		} else {
			arr.push('M');
		}
		
		arr.push(startPos[0], startPos[1], "A", r, r, 0, n < 1 ? 0 : 1, inv, endPos[0], endPos[1]);
		return arr;
	};
	function secLinePath(deg, x1, x2, params){
		var arr = [];
		var startPos = secPosition(x1, deg);
		var endPos = secPosition(x2, deg);
		
		if(params && params.noMove){
		} else {
			arr.push('M');
		}
		arr.push(startPos[0], startPos[1], "L", endPos[0], endPos[1]);
		return arr;
	};
	function normalizeAngle(ang){
		while(ang > 360 || ang < 0){
			if(ang > 360){
				ang -= 360;
			} else if (ang < 0){
				ang += 360;
			}
		}
		return ang;
	};
	function sector(r1, r2, y1, y2){
		var arr = array_merge( "M",
			secant(
				r1, 
				y1,
				y2, 
				{ noMove: 1, invertSecant: 0}
			), "L",
			secant(
				r2, 
				y2, 
				y1, 
				{ noMove: 1, invertSecant: 1}
			),
			'Z'
		);	
		return arr;
	}
	
	function recursiveCalculateNodePositions(node, positionX){
		positionX = positionX;

		if(node.len){ // If first branch, pad only margin
			if(firstBranch){
				absoluteY = bufferAngle | 1;		// Has to be at least 1
				firstBranch = false;
			} else {
				if(node.children.length == 0) absoluteY = Smits.Common.roundFloat(absoluteY + scaleAngle, 4);
			}
		}
		
		if(node.children.length > 0){
			var nodeCoords = [], x1,x2,y1,y2;
			x1 = positionX;
			x2 = positionX += Smits.Common.roundFloat(scaleRadius * node.len, 4);
				
		
			if(node.name){ // draw bootstrap values
				
			}
			
			if(node.children && node.children.length){
				for(var i = 0; i < node.children.length; i++){
					var child = node.children[i];
					var y = recursiveCalculateNodePositions(child, positionX);
					if(y > 0) nodeCoords.push(y);					
				}
			}
			
			var minAngle = Smits.Common.roundFloat(Math.min.apply(null, nodeCoords ), 4);
			var maxAngle = Smits.Common.roundFloat(Math.max.apply(null, nodeCoords ), 4);
			
			// hack: little elbows at ends in order to prevent stair-effects at edges
			svg.draw(
				new Smits.PhyloCanvas.Render.Path(
					array_merge(
						"M", secPosition(positionX + 0.01, minAngle), 
						"L", secant(positionX, minAngle, maxAngle, {noMove: true}),
						"L", secPosition(positionX + 0.01, maxAngle)
					)
				)
			);
			
			if(node.len){ // draw stem
				y1 = Smits.Common.roundFloat( minAngle + (maxAngle-minAngle)/2, 4 );
				svg.draw(new Smits.PhyloCanvas.Render.Path(secLinePath(y1, x1, x2)));
			}			
			
		} else {			
			// LABEL
			
			// preserve for later processing
			node.y = absoluteY;
			labelsHold.push(node);
			
			x1 = positionX;
			x2 = positionX =  Smits.Common.roundFloat(positionX + (scaleRadius * node.len));
			y1 = absoluteY;
				
			svg.draw(new Smits.PhyloCanvas.Render.Path(secLinePath(y1, x1, x2)));
			svg.draw(
				new Smits.PhyloCanvas.Render.Path(
					secLinePath(y1, x2, maxBranch), 
					{ attr : Smits.PhyloCanvas.Render.Style.connectedDash }
				)
			);
			
			
			
			if(node.name){
				var pos = secPosition(maxBranch + sParams.bufferInnerLabels, y1);
				var rotateAngle = normalizeAngle( 90 + 1 - y1 - initStartAngle );
				
				if(rotateAngle > 90 && rotateAngle < 270){
					rotateAngle += 180;
					alignment = "end";
				} else {
					alignment = "start";
				}
				
				var attr = {};
				if(node.style){
					Smits.Common.apply(attr, Smits.PhyloCanvas.Render.Style.getStyle(node.style, 'text'));
				}
				attr["text-anchor"] = alignment;
				if(node.uri) { attr.href = node.uri };
				if(node.description) {attr.title = node.description };
				
				var draw = svg.draw(
					new Smits.PhyloCanvas.Render.Text(
						pos[0], pos[1], 
						node.name,
						{
							attr: attr,
							rotate: [rotateAngle, pos[0], pos[1]]
						}
					)
				);
				
				
				// Background Style
				if(node.bgStyle){
					bgLabelsHold.push([node.bgStyle, y1]);
				}
				
				// Rollover, Rollout and Click Events
				var pos = secPosition(x2, y1);
				if(Smits.PhyloCanvas.Render.Parameters.mouseRollOver){
					Smits.Common.addEventHandler(
						draw[0].node, 
						'mouseover', 
						Smits.PhyloCanvas.Render.Parameters.mouseRollOver, 
						{ svg: svg, node: node, x: pos[0], y: pos[1], textEl: draw[0] }
					);
				}
				if(Smits.PhyloCanvas.Render.Parameters.mouseRollOut){
					Smits.Common.addEventHandler(
						draw[0].node, 
						'mouseout', 
						Smits.PhyloCanvas.Render.Parameters.mouseRollOut, 
						{ svg: svg, node: node, x: pos[0], y: pos[1], textEl: draw[0] }
					);				
				}
				if(Smits.PhyloCanvas.Render.Parameters.onClickAction){
					Smits.Common.addEventHandler(
						draw[0].node, 
						'click', 
						Smits.PhyloCanvas.Render.Parameters.onClickAction, 
						{ svg: svg, node: node, x: pos[0], y: pos[1], textEl: draw[0] }
					);							
				}
				
				maxLabelLength = Math.max(draw[1], maxLabelLength);
			}
		}
		return y1;
	};

	
	function array_merge(arr) {
		var merged = arr;
		for (var i = 1; i < arguments.length; i++) {
			merged = merged.concat(arguments[i]);
		}
		return merged;
	};
	
	function renderBackground(){
		var arr = [];
		
		if(bgLabelsHold.length > 0){
			for(var i = 0; i < bgLabelsHold.length; i++){
				var arr = sector(
					maxBranch, 
					maxBranch + maxLabelLength + sParams.bufferOuterLabels, 
					bgLabelsHold[i][1] - scaleAngle/2, 
					bgLabelsHold[i][1] + scaleAngle/2
				);			
				var bgObj = svg.draw(
					new Smits.PhyloCanvas.Render.Path(
						arr, 
						{ attr: Smits.PhyloCanvas.Render.Style.getStyle(bgLabelsHold[i][0], 'textSecantBg') }
					)
				);
				
				bgObj[0].toBack(); 		// Put it behind the labels
			}
		}
		
		
		
		// Neutral Background
		var arr = sector(
			maxBranch, 
			maxBranch + maxLabelLength + sParams.bufferOuterLabels, 
			(bufferAngle | 1) + (scaleAngle/2), 
			360  + (scaleAngle/2) + 0.999
		);
		var bgObj = svg.draw(
			new Smits.PhyloCanvas.Render.Path(
				arr, 
				{ attr: Smits.PhyloCanvas.Render.Style.textSecantBg }
			)
		);
		
		bgObj[0].toBack(); 		// Put it behind the labels
		
		return maxBranch + maxLabelLength + sParams.bufferOuterLabels;
	};
	
	function renderBinaryChart(outerRadius, groupName, params){
		var bufferInner = (params && params.bufferInner ? params.bufferInner : 0) | Smits.PhyloCanvas.Render.Parameters.binaryChartBufferInner,
			bufferSiblings = (params && params.bufferSiblings ? params.bufferSiblings * scaleAngle : 0) | (Smits.PhyloCanvas.Render.Parameters.binaryChartBufferSiblings < 1 ? scaleAngle * Smits.PhyloCanvas.Render.Parameters.binaryChartBufferSiblings : Smits.PhyloCanvas.Render.Parameters.binaryChartBufferSiblings),
			thickness = (params && params.thickness ? params.thickness : 0) | Smits.PhyloCanvas.Render.Parameters.binaryChartThickness,
			disjointed = (params && params.disjointed ? params.disjointed : false) | Smits.PhyloCanvas.Render.Parameters.binaryChartDisjointed,
			isFirst = true,
			beginY;
			
		for(var i = 0; i < labelsHold.length; i++){
			var node = labelsHold[i];
			if( !labelsHold[i+1] || node.chart[groupName] !== labelsHold[i+1].chart[groupName] || disjointed){
				svg.draw(
					new Smits.PhyloCanvas.Render.Path(
						sector( 
							outerRadius + bufferInner,  
							outerRadius + bufferInner + thickness, 
							(beginY ? beginY : node.y) - (scaleAngle/2) + (isFirst && !disjointed ? 0 : (bufferSiblings/2)), 
							node.y + (scaleAngle/2) - (i == labelsHold.length-1 && !disjointed ? 0 : (bufferSiblings/2))
						),
						{ attr: Smits.PhyloCanvas.Render.Style.getStyle(node.chart[groupName], 'textSecantBg') }
					)
				);			
				beginY = 0;
				isFirst = false;
			} else {
				if(!beginY){ beginY = node.y; }
			}
			isFirst = false;
		}
		return outerRadius + bufferInner + thickness;
	};
	
	function renderBarChart(outerRadius, groupName, params){
		var allValues = [], maxValue,
			bufferInner = params && params.bufferInner ? params.bufferInner : 0 | Smits.PhyloCanvas.Render.Parameters.barChartBufferInner,
			height = params && params.height ? params.height : 0 | Smits.PhyloCanvas.Render.Parameters.barChartHeight,
			width = params && params.width ? (params.width < 1 ? scaleAngle * params.width : params.width ) : 0 | (Smits.PhyloCanvas.Render.Parameters.barChartWidth < 1 ? scaleAngle * Smits.PhyloCanvas.Render.Parameters.barChartWidth : Smits.PhyloCanvas.Render.Parameters.barChartWidth),
			scaleHeight = 0;
		
		// Need to get max value
		for(var i = 0; i < labelsHold.length; i++){
			allValues.push(labelsHold[i].chart[groupName]);
		}
		maxValue = Math.max.apply(null, allValues);
		scaleHeight = Smits.Common.roundFloat(height / maxValue, 4);
		
		for(var i = 0; i < labelsHold.length; i++){
			var node = labelsHold[i];
			svg.draw(
					new Smits.PhyloCanvas.Render.Path(
						sector( 
							outerRadius + bufferInner,  
							outerRadius + bufferInner + (scaleHeight * node.chart[groupName]), 
							node.y - (width/2), 
							node.y + (width/2)
						),
						{ attr: Smits.PhyloCanvas.Render.Style.getStyle(node.chart[groupName], 'barChart') }
					)
				);					
		}
		
		return outerRadius + bufferInner + height;
	};
	
	return function(sSvg, dataObject, bufferRadius){
		/* Privileged Methods */
		this.getCanvasSize = function(){
			return [canvasX, canvasY];
		};
		this.getRoot = function(){
			return dataObject.getRoot();
		};
	
		/* CONSTRUCTOR */
		// Validation
		if(dataObject.getValidate()){   
			sSvg.draw({type: 'text', x: 0, y: sSvg.canvasSize[1] / 3, text: dataObject.getValidate() });
			return
		}				
		
		// Properties Setup
		svg 			= sSvg;
		var node 		= dataObject.getRoot();
		var mNewickLen 	= dataObject.getNewickLen();
		canvasX 		= svg.canvasSize[0];															// Full Canvas Width
		canvasY 		= svg.canvasSize[1];															// Full Canvas Height
		cx 				= canvasX / 2;																	// Set Center Position
		cy 				= canvasY / 2;
		canvasMinEdge 	= Math.min.apply(null, [canvasX,canvasY]);
		
		bufferRadius		= (sParams.bufferRadius > 1) ? sParams.bufferRadius : Smits.Common.roundFloat(canvasMinEdge * sParams.bufferRadius, 4);
		bufferAngle 		= sParams.bufferAngle;							// controls split size in circle		
		innerCircleRadius	= sParams.innerCircleRadius;
		minHeightBetweenLeaves	= sParams.minHeightBetweenLeaves;
		initStartAngle		= sParams.initStartAngle;						// Angle at which the entire tree is rotated
		
		maxBranch			= Math.round( (canvasMinEdge - bufferRadius - innerCircleRadius) / 2);		// maximum branch length
		scaleRadius			= (maxBranch - innerCircleRadius) / mNewickLen;								// scale multiplier to use
		scaleAngle 			= Smits.Common.roundFloat( (360 - bufferAngle) / node.getCountAllChildren(), 4 );		

		// Draw Nodes and Labels
		recursiveCalculateNodePositions(node, innerCircleRadius);

		// Draw Background behind labels
		outerRadius = renderBackground();
		
		// Draw secant highlights
		if(Smits.PhyloCanvas.Render.Parameters.binaryCharts.length){
			var binaryCharts = Smits.PhyloCanvas.Render.Parameters.binaryCharts;
			for(var i in binaryCharts){
				outerRadius = renderBinaryChart(outerRadius, binaryCharts[i].chart, binaryCharts[i]);
			}
		}

		// Draw Bar Chart
		if(Smits.PhyloCanvas.Render.Parameters.barCharts.length){
			var barCharts = Smits.PhyloCanvas.Render.Parameters.barCharts;
			for(var i in barCharts){
				outerRadius = renderBarChart(outerRadius, barCharts[i].chart, barCharts[i]);
			}
		}		
	}
})();

Smits.PhyloCanvas.Render.CircularPhylogram.prototype = {

};/*
	This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
var XMLObjectifier = (function() {
	var _clone = function(obj){
		if(!!obj && typeof(obj)==="object"){
			function F(){}
			F.prototype = obj;
			return new F();
		}		
	};
	//Is Numeric check
	var isNumeric = function(s) {
		var testStr = "";
		if(!!s && typeof(s) === "string") { testStr = s; }
		var pattern = /^((-)?([0-9]*)((\.{0,1})([0-9]+))?$)/;
		return pattern.test(testStr);
	};
	var _self = {
	xmlToJSON: function(xdoc) {
		try {
			if(!xdoc){ return null; }
			var tmpObj = {};
				tmpObj.typeOf = "JSXBObject";
			var xroot = (xdoc.nodeType == 9)?xdoc.documentElement:xdoc;
				tmpObj.RootName = xroot.nodeName || "";
			if(xdoc.nodeType == 3 || xdoc.nodeType == 4) {
				return xdoc.nodeValue;
			}
			//Trim function
			function trim(s) {
				return s.replace(/^\s+|\s+$/gm,'');
			}						
			//Alters attribute and collection names to comply with JS
			function formatName(name) {
				var regEx = /-/g;
				var tName = String(name).replace(regEx,"_");
				return tName;
			}
			//Set Attributes of an object
			function setAttributes(obj, node) {
				if(node.attributes.length > 0) {
					var a = node.attributes.length-1;
					var attName;
					obj._attributes = [];
					do { //Order is irrelevant (speed-up)
						attName = String(formatName(node.attributes[a].name));
						obj._attributes.push(attName);				
						obj[attName] = trim(node.attributes[a].value);
					} while(a--);
				}
			}
			
			//Node Prototype
			var _node = (function() {
					var _self = {
						activate: function() {
							var nodes = [];
							if(!!nodes) {
									nodes.getNodesByAttribute = function(attr, obj) {
										if(!!nodes && nodes.length > 0) {
											var out = [];
											var cNode;
											var maxLen = nodes.length -1;
											try {
												do {
													cNode = nodes[maxLen];
													if(cNode[attr] === obj) {
														out.push(cNode);
													}
												} while(maxLen--);
												out.reverse();
												return out;
											} catch(e) {return null;}
											return null;
										}
									};
									nodes.getNodeByAttribute = function(attr, obj) {
										if(!!nodes && nodes.length > 0) {
											var cNode;
											var maxLen = nodes.length -1;
											try {
												do {
													cNode = nodes[maxLen];
													if(cNode[attr] === obj) {
														return cNode;
													}
												} while(maxLen--);
											} catch(e) {return null;}
											return null;
										}
									};
									nodes.getNodesByValue = function(obj) {
										if(!!nodes && nodes.length > 0) {
											var out = [];
											var cNode;
											var maxLen = nodes.length -1;
											try {
												do {
													cNode = nodes[maxLen];
													if(!!cNode.Text && cNode.Text === obj) {
														out.push(cNode);
													}
												} while(maxLen--);
												return out;
											} catch(e) {return null;}
											return null;
										}
									};
									nodes.contains = function(attr, obj) {
										if(!!nodes && nodes.length > 0) {
											var maxLen = nodes.length -1;
											try {
												do {
													if(nodes[maxLen][attr] === obj) {
														return true;
													}
												} while(maxLen--);
											} catch(e) {return false;}
											return false;
										}
									};
									nodes.indexOf = function(attr, obj) {
										var pos = -1;
										if(!!nodes && nodes.length > 0) {
											var maxLen = nodes.length -1;
											try {
												do {
													if(nodes[maxLen][attr] === obj) {
														pos = maxLen;
													}
												} while(maxLen--);
											} catch(e) {return -1;}
											return pos;
										}
									};
									nodes.SortByAttribute = function(col, dir) {
										if(!!nodes && nodes.length > 0) {				
											function getValue(pair, idx) {
												var out = pair[idx];
												out = (bam.validation.isNumeric(out))?parseFloat(out):out;
												return out;
											}
											function sortFn(a, b) {
												var tA, tB;
												tA = getValue(a, col);
												tB = getValue(b, col);
												var res = (tA<tB)?-1:(tB<tA)?1:0;
												if(!!dir) {
													res = (dir.toUpperCase() === "DESC")?(0 - res):res;
												}
												return res;
											}
											nodes.sort(sortFn);
										}
									};
									nodes.SortByValue = function(dir) {
										if(!!nodes && nodes.length > 0) {
											function getValue(pair) {
												var out = pair.Text;
												out = (bam.validation.isNumeric(out))?parseFloat(out):out;
												return out;
											}
											function sortFn(a, b) {
												var tA, tB;
												tA = getValue(a);
												tB = getValue(b);
												var res = (tA<tB)?-1:(tB<tA)?1:0;
												if(!!dir) {
													res = (dir.toUpperCase() === "DESC")?(0 - res):res;
												}
												return res;
											}
											nodes.sort(sortFn);
										}
									};
									nodes.SortByNode = function(node, dir) {
										if(!!nodes && nodes.length > 0) {
											function getValue(pair, node) {
												var out = pair[node][0].Text;
												out = (bam.validation.isNumeric(out))?parseFloat(out):out;
												return out;
											}
											function sortFn(a, b) {										
												var tA, tB;
												tA = getValue(a, node);
												tB = getValue(b, node);
												var res = (tA<tB)?-1:(tB<tA)?1:0;
												if(!!dir) {
													res = (dir.toUpperCase() === "DESC")?(0 - res):res;
												}
												return res;
											}
											nodes.sort(sortFn);
										}
								  };
							}
							return nodes;
						}
					};
					return _self;
			})();
			//Makes a new node of type _node;
			var makeNode = function() {
				var _fn = _clone(_node);					
				return _fn.activate();
			}
			//Set collections
			function setHelpers(grpObj) {
				//Selects a node withing array where attribute = value
				grpObj.getNodeByAttribute = function(attr, obj) {
					if(this.length > 0) {
						var cNode;
						var maxLen = this.length -1;
						try {
							do {
								cNode = this[maxLen];
								if(cNode[attr] == obj) {
									return cNode;
								}
							} while(maxLen--);
						} catch(e) {return false;}
						return false;
					}
				};
				
				grpObj.contains = function(attr, obj) {
					if(this.length > 0) {
						var maxLen = this.length -1;
						try {
							do {
								if(this[maxLen][attr] == obj) {
									return true;
								}
							} while(maxLen--);
						} catch(e) {return false;}
						return false;
					}
				};
				
				grpObj.indexOf = function(attr, obj) {
					var pos = -1;
					if(this.length > 0) {
						var maxLen = this.length -1;
						try {
							do {
								if(this[maxLen][attr] == obj) {
									pos = maxLen;
								}
							} while(maxLen--);
						} catch(e) {return -1;}
						return pos;
					}
				};
				
				grpObj.SortByAttribute = function(col, dir) {
					if(this.length) {				
						function getValue(pair, idx) {
							var out = pair[idx];
							out = (isNumeric(out))?parseFloat(out):out;
							return out;
						}
						function sortFn(a, b) {
							var res = 0;
							var tA, tB;						
							tA = getValue(a, col);
							tB = getValue(b, col);
							if(tA < tB) { res = -1;	} else if(tB < tA) { res = 1; }
							if(dir) {
								res = (dir.toUpperCase() == "DESC")?(0 - res):res;
							}
							return res;
						}
						this.sort(sortFn);
					}
				};
				
				grpObj.SortByValue = function(dir) {
					if(this.length) {
						function getValue(pair) {
							var out = pair.Text;
							out = (isNumeric(out))?parseFloat(out):out;
							return out;
						}
						function sortFn(a, b) {
							var res = 0;
							var tA, tB;
							tA = getValue(a);
							tB = getValue(b);
							if(tA < tB) { res = -1;	} else if(tB < tA) { res = 1; }
							if(dir) {
								res = (dir.toUpperCase() == "DESC")?(0 - res):res;
							}
							return res;
						}
						this.sort(sortFn);
					}
				};
				
				grpObj.SortByNode = function(node, dir) {
					if(this.length) {
						function getValue(pair, node) {
							var out = pair[node][0].Text;
							out = (isNumeric(out))?parseFloat(out):out;
							return out;
						}
						function sortFn(a, b) {
							var res = 0;
							var tA, tB;
							tA = getValue(a, node);
							tB = getValue(b, node);
							if(tA < tB) { res = -1;	} else if(tB < tA) { res = 1; }
							if(dir) {
								res = (dir.toUpperCase() == "DESC")?(0 - res):res;
							}
							return res;
						}
						this.sort(sortFn);
					}
				};
			}
			//Recursive JSON Assembler
			//Set Object Nodes
			function setObjects(obj, node) {
				var elemName;	//Element name
				var cnode;	//Current Node
				var tObj;	//New subnode
				var cName = "";
				if(!node) { return null; }				
				//Set node attributes if any
				if(node.attributes.length > 0){setAttributes(obj, node);}				
				obj.Text = "";
				if(node.hasChildNodes()) {
					var nodeCount = node.childNodes.length - 1;	
					var n = 0;
					do { //Order is irrelevant (speed-up)
						cnode = node.childNodes[n];
						switch(cnode.nodeType) {
							case 1: //Node
							//Process child nodes
							obj._children = [];
							//SOAP XML FIX to remove namespaces (i.e. soapenv:)
							elemName = (cnode.localName)?cnode.localName:cnode.baseName;
							elemName = formatName(elemName);
							if(cName != elemName) { obj._children.push(elemName); }
								//Create sub elemns array
								if(!obj[elemName]) {
									obj[elemName] = []; //Create Collection
								}
								tObj = {};
								obj[elemName].push(tObj);
								if(cnode.attributes.length > 0) {
									setAttributes(tObj, cnode);
								}
								//Set Helper functions (contains, indexOf, sort, etc);
								if(!obj[elemName].contains) {
									setHelpers(obj[elemName]);
								}	
							cName = elemName;
							if(cnode.hasChildNodes()) {
								setObjects(tObj, cnode); //Recursive Call
							}
							break;
							case 3: //Text Value
							obj.Text += trim(cnode.nodeValue);
							break;
							case 4: //CDATA
							obj.Text += (cnode.text)?trim(cnode.text):trim(cnode.nodeValue);
							break;
						}
					} while(n++ < nodeCount);
				}
			}			
			//RUN
			setObjects(tmpObj, xroot);
			//Clean-up memmory
			xdoc = null;
			xroot = null;
			return tmpObj;	
		} catch(e) {
				return null;	
		}	
	},

	//Converts Text to XML DOM
	textToXML: function(strXML) {
		var xmlDoc = null;
		try {
			xmlDoc = (document.all)?new ActiveXObject("Microsoft.XMLDOM"):new DOMParser();
			xmlDoc.async = false;
		} catch(e) {throw new Error("XML Parser could not be instantiated");}
		var out;
		try {
			if(document.all) {
				out = (xmlDoc.loadXML(strXML))?xmlDoc:false;
			} else {		
				out = xmlDoc.parseFromString(strXML, "text/xml");
			}
		} catch(e) { throw new Error("Error parsing XML string"); }
		return out;
	}
	};
	return _self;
})();

