/*
Scripts to create interactive sliders in SVG using ECMA script
Copyright (C) <2010>  <Andreas Neumann>
Version 1.01, 2010-03-17
neumann@karto.baug.ethz.ch
http://www.carto.net/
http://www.carto.net/neumann/

Credits:
* Kevin Lindsey for ideas how to implement such a slider

----

Documentation: http://www.carto.net/papers/svg/gui/slider/

----

current version: 1.0.

version history:
0.99 (not documented, somewhere around 2001)

1.0 (2006-08-04)
changed constructor parameters (added id, parentNode), removed parameter sliderGroupId, added documentation, added method .moveTo()

1.01 (2010-03-17)
fixed a problem with Firefox. It wouldn't react on horizontal or vertical lines if the stroke-opacity is zero. Now the opacity is set 0.001
also added evt.preventDefault() to supress dragging of SVG graphics

-------

This ECMA script library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This library 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
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library (lesser_gpl.txt); if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

----

original document site: http://www.carto.net/papers/svg/gui/slider/
Please contact the author in case you want to use code or ideas commercially.
If you use this code, please include this copyright header, the included full
LGPL 2.1 text and read the terms provided in the LGPL 2.1 license
(http://www.gnu.org/copyleft/lesser.txt)

-------------------------------

Please report bugs and send improvements to neumann@karto.baug.ethz.ch
If you use this control, please link to the original (http://www.carto.net/papers/svg/gui/slider/)
somewhere in the source-code-comment or the "about" of your project and give credits, thanks!

*/

//slider properties
function slider(id,parentNode,x1,y1,value1,x2,y2,value2,startVal,sliderStyles,invisSliderWidth,sliderSymb,functionToCall,mouseMoveBool) {
	var nrArguments = 14;
	var createSlider= true;
	if (arguments.length == nrArguments) {	
		this.id = id; //an internal id, this id is not used in the SVG Dom tree
		this.parentNode = parentNode; //the parentNode, string or nodeReference
		this.x1 = x1; //the start point x of the slider
		this.y1 = y1; //the start point y of the slider
		this.value1 = value1; //the value at the start point, min slider value
		this.x2 = x2; //the end point x of the slider 
		this.y2 = y2; //the end point y of the slider
		this.value2 = value2; //the value at the end point, max slider value
		this.startVal = startVal; //the initial value of the slider
		this.value = startVal; //the current value of the slider
		this.sliderStyles = sliderStyles; //the color of the slider line
		this.invisSliderWidth = invisSliderWidth; //the width of invisible part of the slider
		this.sliderSymb = sliderSymb; //the id for the movable symbol to be placed on the slider line 
		this.functionToCall = functionToCall; //the callback function
		this.mouseMoveBool = mouseMoveBool; //boolean value to indicate if the slider gives immediate feedback or not
		this.length = toPolarDist((this.x2 - this.x1),(this.y2 - this.y1));
		this.direction = toPolarDir((this.x2 - this.x1),(this.y2 - this.y1));
		this.invisSliderLine = null;
		this.slideStatus = 0;
	}
	else {
		createSlider = false;
		alert("Error in slider ("+id+") constructor: wrong nr of arguments! You have to pass over "+nrArguments+" parameters.");
	}
	if (createSlider) {
		this.createSlider();
	}
	else {
		alert("Could not create slider with id '"+id+"' due to errors in the constructor parameters");		
	}
}

//create slider
slider.prototype.createSlider = function() {
	var result = this.testParent();
	if (result) {
		this.invisSliderLine = document.createElementNS(svgNS,"line");
		this.invisSliderLine.setAttributeNS(null,"x1",this.x1);
		this.invisSliderLine.setAttributeNS(null,"y1",this.y1);
		this.invisSliderLine.setAttributeNS(null,"x2",this.x2);
		this.invisSliderLine.setAttributeNS(null,"y2",this.y2);
		this.invisSliderLine.setAttributeNS(null,"stroke","black");
		this.invisSliderLine.setAttributeNS(null,"stroke-width",this.invisSliderWidth);
		//there is a bug in Firefox 3.6 that events aren't triggered if stroke-opacity is 0
		this.invisSliderLine.setAttributeNS(null,"stroke-opacity","0.001");
		this.invisSliderLine.setAttributeNS(null,"pointer-events","stroke");
		this.invisSliderLine.setAttributeNS(null,"stroke-linecap","square");
		this.invisSliderLine.addEventListener("mousedown",this,false);
		this.parentGroup.appendChild(this.invisSliderLine);
		this.visSliderLine = document.createElementNS(svgNS,"line");
		this.visSliderLine.setAttributeNS(null,"x1",this.x1);
		this.visSliderLine.setAttributeNS(null,"y1",this.y1);
		this.visSliderLine.setAttributeNS(null,"x2",this.x2);
		this.visSliderLine.setAttributeNS(null,"y2",this.y2);
		for (var attrib in this.sliderStyles) {
			this.visSliderLine.setAttributeNS(null,attrib,this.sliderStyles[attrib]);
		}
		this.visSliderLine.setAttributeNS(null,"pointer-events","none");
		this.parentGroup.appendChild(this.visSliderLine);
		this.sliderSymbol = document.createElementNS(svgNS,"use");
		this.sliderSymbol.setAttributeNS(xlinkNS,"xlink:href","#"+this.sliderSymb);
		var myStartDistance = this.length - ((this.value2 - this.startVal) / (this.value2 - this.value1)) * this.length;
		var myPosX = this.x1 + toRectX(this.direction,myStartDistance);
		var myPosY = this.y1 + toRectY(this.direction,myStartDistance);
		var myTransformString = "translate("+myPosX+","+myPosY+") rotate(" + Math.round(this.direction / Math.PI * 180) + ")";
		this.sliderSymbol.setAttributeNS(null,"transform",myTransformString);
		this.sliderSymbol.setAttributeNS(null,"pointer-events","none");
		this.parentGroup.appendChild(this.sliderSymbol);
	}
	else {
		alert("could not create or reference 'parentNode' of slider with id '"+this.id+"'");			
	}
}

//test if parent group exists
slider.prototype.testParent = function() {
    //test if of type object
    var nodeValid = false;
    if (typeof(this.parentNode) == "object") {
    	if (this.parentNode.nodeName == "svg" || this.parentNode.nodeName == "g" || this.parentNode.nodeName == "svg:svg" || this.parentNode.nodeName == "svg:g") {
    		this.parentGroup = this.parentNode;
    		nodeValid = true;
    	}
    }
    else if (typeof(this.parentNode) == "string") { 
    	//first test if button group exists
    	if (!document.getElementById(this.parentNode)) {
        	this.parentGroup = document.createElementNS(svgNS,"g");
        	this.parentGroup.setAttributeNS(null,"id",this.parentNode);
        	document.documentElement.appendChild(this.parentGroup);
        	nodeValid = true;
   		}
   		else {
       		this.parentGroup = document.getElementById(this.parentNode);
       		nodeValid = true;
   		}
   	}
   	return nodeValid;
}

//remove all slider elements
slider.prototype.removeSlider = function() {
	this.parentGroup.removeChild(this.sliderSymbol);
	this.parentGroup.removeChild(this.visSliderLine);
	this.parentGroup.removeChild(this.invisSliderLine);
}

//handle events
slider.prototype.handleEvent = function(evt) {
	this.drag(evt);
	evt.preventDefault();
}

//drag slider
slider.prototype.drag = function(evt) {
	if (evt.type == "mousedown" || (evt.type == "mousemove" && this.slideStatus == 1)) {
		//get coordinate in slider coordinate system
		var coordPoint = myMapApp.calcCoord(evt,this.invisSliderLine);
		//draw normal line for first vertex
		var ax = this.x2 - this.x1;
		var ay = this.y2 - this.y1;
		//normal vector 1
		var px1 = parseFloat(this.x1) + ay * -1;
		var py1 = parseFloat(this.y1) + ax;
		//normal vector 2
		var px2 = parseFloat(this.x2) + ay * -1;
		var py2 = parseFloat(this.y2) + ax;
				
		if (leftOfTest(coordPoint.x,coordPoint.y,this.x1,this.y1,px1,py1) == 0 && leftOfTest(coordPoint.x,coordPoint.y,this.x2,this.y2,px2,py2) == 1) {
			if (evt.type == "mousedown" && (evt.detail == 1 || evt.detail == 0)) {
				this.slideStatus = 1;
				document.documentElement.addEventListener("mousemove",this,false);
				document.documentElement.addEventListener("mouseup",this,false);
			}
			myNewPos = intersect2lines(this.x1,this.y1,this.x2,this.y2,coordPoint.x,coordPoint.y,coordPoint.x + ay * -1,coordPoint.y + ax);
			var myPercentage = toPolarDist(myNewPos['x'] - this.x1,myNewPos['y'] - this.y1) / this.length;
			this.value = this.value1 + myPercentage * (this.value2 - this.value1);
		}
		else {
			var myNewPos = new Array();
			if (leftOfTest(coordPoint.x,coordPoint.y,this.x1,this.y1,px1,py1) == 0 && leftOfTest(coordPoint.x,coordPoint.y,this.x2,this.y2,px2,py2) == 0) {
				//more than max
				this.value = this.value2;
				myNewPos['x'] = this.x2;
				myNewPos['y'] = this.y2;
			}
			if (leftOfTest(coordPoint.x,coordPoint.y,this.x1,this.y1,px1,py1) == 1 && leftOfTest(coordPoint.x,coordPoint.y,this.x2,this.y2,px2,py2) == 1) {
				//less than min
				this.value = this.value1;
				myNewPos['x'] = this.x1;
				myNewPos['y'] = this.y1;
			}
		}
		var myTransformString = "translate("+myNewPos['x']+","+myNewPos['y']+") rotate(" + Math.round(this.direction / Math.PI * 180) + ")";
		this.sliderSymbol.setAttributeNS(null,"transform",myTransformString);
		this.fireFunction();
	}
	if (evt.type == "mouseup" && (evt.detail == 1 || evt.detail == 0)) {
		if (this.slideStatus == 1) {
			this.slideStatus = 2;
			document.documentElement.removeEventListener("mousemove",this,false);
			document.documentElement.removeEventListener("mouseup",this,false);
			this.fireFunction();
		}
		this.slideStatus = 0;
	}
}

//this code is executed, after the slider is released
//you can use switch/if to detect which slider was used (use this.id) for that
slider.prototype.fireFunction = function() {
	if (this.slideStatus == 1 && this.mouseMoveBool == true) {
		if (typeof(this.functionToCall) == "function") {
			this.functionToCall("change",this.id,this.value);
		}
		if (typeof(this.functionToCall) == "object") {
			this.functionToCall.getSliderVal("change",this.id,this.value);
		}
		if (typeof(this.functionToCall) == undefined) {
			return;
		}
	}
	if (this.slideStatus == 2) {
		if (typeof(this.functionToCall) == "function") {
			this.functionToCall("release",this.id,this.value);
		}
		if (typeof(this.functionToCall) == "object") {
			this.functionToCall.getSliderVal("release",this.id,this.value);
		}
		if (typeof(this.functionToCall) == undefined) {
			return;
		}
	}
}

slider.prototype.getValue = function() {
	return this.value;
}

//this is to set the value from other scripts
slider.prototype.setValue = function(value,fireFunction) {
	this.value = value;
	var myPercAlLine = (this.value - this.value1) / (this.value2 - this.value1);
	var myPosX = this.x1 + toRectX(this.direction,this.length * myPercAlLine);
	var myPosY = this.y1 + toRectY(this.direction,this.length * myPercAlLine);
	var myTransformString = "translate("+myPosX+","+myPosY+") rotate(" + Math.round(this.direction / Math.PI * 180) + ")";
	this.sliderSymbol.setAttributeNS(null,"transform",myTransformString);
	if (fireFunction) {
		//temporary set slideStatus
		this.slideStatus = 2;
		this.fireFunction();
		this.slideStatus = 0;
	}
}

slider.prototype.moveTo = function(x1,y1,x2,y2) {
	this.x1 = x1;
	this.y1 = y1;
	this.x2 = x2;
	this.y2 = y2;
	this.invisSliderLine.setAttributeNS(null,"x1",this.x1);
	this.invisSliderLine.setAttributeNS(null,"y1",this.y1);
	this.invisSliderLine.setAttributeNS(null,"x2",this.x2);
	this.invisSliderLine.setAttributeNS(null,"y2",this.y2);
	this.visSliderLine.setAttributeNS(null,"x1",this.x1);
	this.visSliderLine.setAttributeNS(null,"y1",this.y1);
	this.visSliderLine.setAttributeNS(null,"x2",this.x2);
	this.visSliderLine.setAttributeNS(null,"y2",this.y2);
	//reset direction and length of the slider
	this.length = toPolarDist((this.x2 - this.x1),(this.y2 - this.y1));
	this.direction = toPolarDir((this.x2 - this.x1),(this.y2 - this.y1));
	//reset the value to correctly reposition element
	this.setValue(this.getValue(),false);
}