view war/scripts/sti/STITimeplot.js @ 10:bcc91da76221

fixed bug with timespans. git-svn-id: http://dev.dariah.eu/svn/repos/eu.dariah.de/ap1/sti-gwt-dariah-geobrowser@91 f2b5be40-def6-11e0-8a09-b3c1cc336c6b
author StefanFunk <StefanFunk@f2b5be40-def6-11e0-8a09-b3c1cc336c6b>
date Wed, 05 Sep 2012 14:23:44 +0000
parents cf06b77a8bbd
children
line wrap: on
line source

/**
 * defines the timeplot component of the Spatio Temporal Interface
 * it builds a timeplot context with the Simile Widget Timeplot JavaScript Framework
 * @param {STICore} core the sti core component, the timeplot component has to deal with
 * @param {String} window the div id for the window div for the container of the timeplot widget
 * @param {String} container the div id for the container of the timeplot widget
 *
 * @constructor
 */
function STITimeplot(core,window,container){

    this.core = core;
    this.window = window;
    this.container = container;
    this.timeplot;
    this.dataSources;
    this.eventSources;
    this.eds;
    this.timeGeometry;
    this.valueGeometry;
    this.canvas;
    
    this.leftFlagPole;
    this.rightFlagPole;
    this.rangeBox;
    this.leftFeather;
    this.rightFeather;
    this.leftHandle;
    this.rightHandle;
    
    this.leftFlagPos = null;
    this.leftFlagTime = null;
    this.rightFlagPos = null;
    this.rightFlagTime = null;
    this.leftFeatherTime = null;
    this.rightFeatherTime = null;
    
    this.mouseDownTime;
    this.mouseUpTime;
    this.mouseTempTime;    
    this.mouseDownPos;
    this.mouseUpPos;
    this.mouseTempPos;    
    
    this.placePoles;    
    this.hoverSlice;
    
    this.status;
    this.featherWidth;    
    this.slider;
    
    this.initialize();
    
}

STITimeplot.prototype = {

    /**
     * clears the timeplot canvas and the timeGeometry properties
     */
	clearTimeplot: function(){
        this.timeplot._clearCanvas();        
        this.timeGeometry._earliestDate = null;
        this.timeGeometry._latestDate = null;
        this.timeGeometry._clearLabels();
	},

    /**
     * initializes the timeplot elements with arrays of objects, that have a specific time granularity
     * @param {Array} objects an array of object-array, which contain all elements that are in the actual display set
     * @param {SimileAjax.DateTime} granularity the time granularity of the objects
     */
    initTimeplot: function(dataSets, granularity){
		this.clearTimeplot();
    	this.resetTimeplot();	
        for (var i = 0; i < this.timeplot._plots.length; i++) {
            this.timeplot._plots[i].dispose();
        }
        this.timeplot._clearCanvas();        
        this.timeGeometry._earliestDate = null;
        this.timeGeometry._latestDate = null;
        this.valueGeometry._minValue = null;
        this.valueGeometry._maxValue = null;
        this.dataSources = new Array();
        this.plotInfos = new Array();
        this.eventSources = new Array();
        for (var i = 0; i < dataSets.length; i++) {
            	var eventSource = new Timeplot.DefaultEventSource();
            	var dataSource = new Timeplot.ColumnSource(eventSource, 1);
            	this.dataSources.push(dataSource);
            	this.eventSources.push(eventSource);
            	var c = colors[i];
            	var plotInfo = Timeplot.createPlotInfo({
                	id: "plot" + i,
                	dataSource: dataSource,
                	timeGeometry: this.timeGeometry,
                	valueGeometry: this.valueGeometry,
                	fillGradient: false,
                	lineColor: 'rgba(' + c.r1 + ',' + c.g1 + ',' + c.b1 + ', 1)',
                	fillColor: 'rgba(' + c.r0 + ',' + c.g0 + ',' + c.b0 + ', 0.3)',
                	showValues: true
            	});
            	this.plotInfos.push(plotInfo);
        }
        this.timeGeometry._granularity = granularity;
        this.timeGeometry._clearLabels();
        this.timeplot.resetPlots(this.plotInfos);
    	if( this.plotInfos.length == 0 ){
    		this.initLabels( this.timeplot.regularGrid() );
    		return;
    	}
        this.timeGeometry.extendedDataSource = this.eds;
        this.eds.initialize(this.dataSources, this.eventSources, dataSets, granularity);
       	this.timeplot.repaint();
        // set maximum number of slider steps
        var slices = this.eds.timeSlices.length;
        var numSlices = Math.floor(slices / this.canvas.width * this.canvas.height + 0.5);
        this.slider.setMaximum(numSlices);
        this.initLabels([]);
        this.initOverview();        
    },
    
    /**
     * initializes the timeplot for the Spatio Temporal Interface.
     * all elements (including their events) that are needed for user interaction are instantiated here, the slider element as well
     */
    initialize: function(){
    
        this.status = 0;
    	this.paused = true;
        this.dataSources = new Array();
        this.plotInfos = new Array();
        this.eventSources = new Array();
        this.timeGeometry = new Timeplot.DefaultTimeGeometry({
            gridColor: "#000000",
            axisLabelsPlacement: "top"
        });
        this.timeGeometry._hideLabels = true;
        this.timeGeometry._granularity = 0;
        this.valueGeometry = new Timeplot.DefaultValueGeometry({
            min: 0
        });
        this.placePoles = new Array();
        this.timeplot = Timeplot.create(document.getElementById(this.container), this.plotInfos);
        this.eds = new ExtendedDataSource();
        
        this.canvas = this.timeplot.getCanvas();
        
        this.leftFlagPole = this.timeplot.putDiv("leftflagpole", "timeplot-dayflag-pole");
        this.rightFlagPole = this.timeplot.putDiv("rightflagpole", "timeplot-dayflag-pole");
        SimileAjax.Graphics.setOpacity(this.leftFlagPole, 50);
        SimileAjax.Graphics.setOpacity(this.rightFlagPole, 50);

        this.rangeBox = this.timeplot.putDiv("rangebox", "range-box");
        this.leftFeather = this.timeplot.putDiv("leftfeather", "left-feather");
        this.rightFeather = this.timeplot.putDiv("rightfeather", "right-feather");
        
        var window = document.getElementById(this.window); 
        this.leftHandle = document.createElement("div");
        this.leftHandle.setAttribute('class','plotHandle');
        this.leftHandle.title = "Click and move mouse to change left border.";
       	this.rightHandle = document.createElement("div");
       	this.rightHandle.setAttribute('class','plotHandle');
        this.rightHandle.title = "Click and move mouse to change right border.";
        window.appendChild(this.leftHandle);
        window.appendChild(this.rightHandle);
        
		this.poles = this.timeplot.putDiv( "poles", "pole" );
        this.timeplot.placeDiv(this.poles, {
            left: 0,
            bottom: 0,
            width: this.canvas.width,
            height: this.canvas.height,
            display: "block"
        });
        this.poles.appendChild(document.createElement("canvas"));
        
        this.toolbar = document.createElement("div");
        this.toolbar.setAttribute('class','plotToolbar');
        window.appendChild(this.toolbar);
        
        this.toolbarAbsoluteDiv = document.createElement("div");
        this.toolbarAbsoluteDiv.setAttribute('class','absoluteToolbar');
        this.toolbar.appendChild(this.toolbarAbsoluteDiv);
        
        var drag = document.createElement("div");
        drag.title = "Click to drag the selection on the timeplot. Click again to deselect.";
        drag.setAttribute('class','dragRange');
        this.toolbarAbsoluteDiv.appendChild(drag);

        var zoom = document.createElement("div");
        zoom.title = "Zoom into selection. To undo, use the History button.";
        zoom.setAttribute('class','zoomRange');
        this.toolbarAbsoluteDiv.appendChild(zoom);
        zoom.onclick = function(){
        	plot.core.refine();
        }

		// displays the feather divs
        var displayFeather = function(){        
            plot.leftFeather.style.visibility = "visible";
            plot.rightFeather.style.visibility = "visible";            
            plot.timeplot.placeDiv(plot.leftFeather, {
                left: plot.leftFlagPos - plot.featherWidth,
                width: plot.featherWidth,
                bottom: 0,
                height: plot.canvas.height,
                display: "block"
            });
            plot.timeplot.placeDiv(plot.rightFeather, {
                left: plot.rightFlagPos,
                width: plot.featherWidth,
                bottom: 0,
                height: plot.canvas.height,
                display: "block"
            });            
            var leftCv = document.getElementById("leftFeatherCanvas");
            if (leftCv == null) {
                leftCv = document.createElement("canvas");
                leftCv.id = "leftFeatherCanvas";
                plot.leftFeather.appendChild(leftCv);
            }
            leftCv.width = plot.featherWidth;
            leftCv.height = plot.canvas.height;
 
           if (!leftCv.getContext && G_vmlCanvasManager) 
                leftCv = G_vmlCanvasManager.initElement(leftCv);
            var leftCtx = leftCv.getContext('2d');
            var leftGradient = leftCtx.createLinearGradient(plot.featherWidth, 0, 0, 0);
            leftGradient.addColorStop(0, 'rgba(95,136,178,0.6)');
            leftGradient.addColorStop(1, 'rgba(171,171,171,0)');
            leftCtx.fillStyle = leftGradient;
            leftCtx.fillRect(0, 0, plot.featherWidth, plot.canvas.height);   

            var rightCv = document.getElementById("rightFeatherCanvas");
            if (rightCv == null) {
                rightCv = document.createElement("canvas");
                rightCv.id = "rightFeatherCanvas";
                plot.rightFeather.appendChild(rightCv);
            }
            rightCv.width = plot.featherWidth;
            rightCv.height = plot.canvas.height;
            if (!rightCv.getContext && G_vmlCanvasManager) 
                rightCv = G_vmlCanvasManager.initElement(rightCv);
            var rightCtx = rightCv.getContext('2d');
            var rightGradient = rightCtx.createLinearGradient(0, 0, plot.featherWidth, 0);
            rightGradient.addColorStop(0, 'rgba(95,136,178,0.6)');
            rightGradient.addColorStop(1, 'rgba(171,171,171,0)');
            rightCtx.fillStyle = rightGradient;
            rightCtx.fillRect(0, 0, plot.featherWidth, plot.canvas.height);
        }         
                
        var featherSliderDiv = document.createElement("div");
		featherSliderDiv.setAttribute('class', 'featherSlider');
		featherSliderDiv.title = "Adjust the feather of the selection to smoothen the animations on the map.";
        var sliderInput = document.createElement("input");
        featherSliderDiv.appendChild(sliderInput);
        this.slider = new Slider(featherSliderDiv,sliderInput,"horizontal");
        this.slider.onchange = function(){
            if (plot.leftFlagPos != null) {
                plot.setFeather();
                var plots = plot.timeplot._plots;
                for (i = 0; i < plots.length; i++) {
                    plots[i].fullOpacityPlot(plot.leftFlagTime, plot.rightFlagTime, plot.leftFlagPos, plot.rightFlagPos, plot.leftFeatherTime, plot.rightFeatherTime, plot.featherWidth, colors[i]);
                    plots[i].opacityPlot.style.visibility = "visible";
                }
                displayFeather();
                plot.updateByTime();
            }
        }
        this.toolbarAbsoluteDiv.appendChild(featherSliderDiv);        
                
        var plot = this;
        
        var cancel = document.createElement("div");
        cancel.setAttribute('class','cancelRange');
        cancel.title = "Clear Selection";
        this.toolbarAbsoluteDiv.appendChild(cancel);
        cancel.onclick = function(){
        	plot.core.reset();
        }
        
        this.overview = document.createElement("div");
        this.overview.setAttribute('class','timeOverview');
        window.appendChild(this.overview);
        
		var mousedown = false;
        this.shift = function(shift){
        	if( !mousedown ){
        		return;
        	}
			if( plot.eds.setShift(shift) ){
				plot.redrawPlot();
			}
			setTimeout( function(){ plot.shift(shift); }, 100 );
        }
		
		var shiftPressed = function(shift){
        	mousedown = true;
        	document.onmouseup = function(){
        		mousedown = false;
        		document.onmouseup = null;
        	}
        	plot.shift(shift);
		}
		
        this.shiftLeft = document.createElement("div");
        this.shiftLeft.setAttribute('class','shiftLeft');
        window.appendChild(this.shiftLeft);
        this.shiftLeft.onmousedown = function(){
        	shiftPressed(1);
        }

        this.shiftRight = document.createElement("div");
        this.shiftRight.setAttribute('class','shiftRight');
        window.appendChild(this.shiftRight);
        this.shiftRight.onmousedown = function(){
        	shiftPressed(-1);
        }

        this.plotLabels = document.createElement("div");
        this.plotLabels.setAttribute('class','plotLabels');
        window.appendChild(this.plotLabels);
        
        this.initLabels( this.timeplot.regularGrid() );

        //Finds the time corresponding to the position x on the timeplot
        var getCorrelatedTime = function(x){
            if (x > plot.canvas.width) 
                x = plot.canvas.width;
            if (isNaN(x) || x < 0) 
                x = 0;
            var t = plot.timeGeometry.fromScreen(x);
            if (t == 0) 
                return;
            return plot.dataSources[0].getClosestValidTime(t);
        }
        
        //Finds the position corresponding to the time t on the timeplot
        var getCorrelatedPosition = function(t){
			var x = plot.timeGeometry.toScreen(t);
            if (x > plot.canvas.width) 
                x = plot.canvas.width;
            if (isNaN(x) || x < 0) 
                x = 0;
            return x;
        }

        //Maps the 2 positions in the right order to left and right bound of the chosen timeRange
        var mapPositions = function(pos1, pos2){
            if (pos1 > pos2) {
                plot.leftFlagPos = pos2;
                plot.rightFlagPos = pos1;
            }
            else 
                if (pos1 < pos2) {
                    plot.leftFlagPos = pos1;
                    plot.rightFlagPos = pos2;
                }
                else {
                    plot.leftFlagPos = pos1;
                    plot.rightFlagPos = pos1;
                }
            plot.leftFlagTime = plot.dataSources[0].getClosestValidTime(plot.timeGeometry.fromScreen(plot.leftFlagPos));
            plot.rightFlagTime = plot.dataSources[0].getClosestValidTime(plot.timeGeometry.fromScreen(plot.rightFlagPos));
        }
        
        //Sets the divs corresponding to the actual chosen timeRange
        var setRangeDivs = function(){
            plot.leftFlagPole.style.visibility = "visible";
            plot.rightFlagPole.style.visibility = "visible";
            plot.rangeBox.style.visibility = "visible";
            plot.timeplot.placeDiv(plot.leftFlagPole, {
                left: plot.leftFlagPos,
                bottom: 0,
                height: plot.canvas.height,
                display: "block"
            });
            plot.timeplot.placeDiv(plot.rightFlagPole, {
                left: plot.rightFlagPos,
                bottom: 0,
                height: plot.canvas.height,
                display: "block"
            });
            var boxWidth = plot.rightFlagPos - plot.leftFlagPos;
            plot.timeplot.placeDiv(plot.rangeBox, {
                left: plot.leftFlagPos,
                width: boxWidth + 1,
                height: plot.canvas.height,
                display: "block"
            });
           	plot.setFeather();
           	displayFeather();
           	var plots = plot.timeplot._plots;
           	for (i = 0; i < plots.length; i++) {
               	plots[i].fullOpacityPlot(plot.leftFlagTime, plot.rightFlagTime, plot.leftFlagPos, plot.rightFlagPos, plot.leftFeatherTime, plot.rightFeatherTime, plot.featherWidth, colors[i]);
               	plots[i].opacityPlot.style.visibility = "visible";
           	}
        	var unit = plot.eds.unit;
			plot.leftHandle.innerHTML = SimileAjax.DateTime.getTimeLabel(unit,plot.leftFlagTime);
			plot.rightHandle.innerHTML = SimileAjax.DateTime.getTimeLabel(unit,plot.rightFlagTime);
        	var leftPos = plot.leftFlagPole.offsetLeft + plot.timeplot.getElement().offsetLeft;
        	var rightPos = plot.rightFlagPole.offsetLeft + plot.timeplot.getElement().offsetLeft;
        	var topPos = plot.timeplot.getElement().offsetTop + plot.timeplot.getElement().offsetHeight;
        	
        	plot.leftHandle.style.visibility = "visible";        	
			plot.rightHandle.style.visibility = "visible";
        	plot.leftHandle.style.left = leftPos+"px";
        	plot.rightHandle.style.left = (rightPos-plot.rightHandle.offsetWidth+1)+"px";
        	plot.leftHandle.style.top = (plot.timeplot.getElement().offsetTop)+"px";
        	
        	if( rightPos-leftPos < plot.leftHandle.offsetWidth + plot.rightHandle.offsetWidth ){
				plot.rightHandle.style.top = (plot.timeplot.getElement().offsetTop+plot.rightHandle.offsetHeight)+"px";
        	}
        	else {
				plot.rightHandle.style.top = (plot.timeplot.getElement().offsetTop)+"px";
        	}

			var rW = rightPos-leftPos;
			var tW = plot.toolbarAbsoluteDiv.offsetWidth;
			var pW = plot.canvas.width;
			var pL = plot.timeplot.getElement().offsetLeft;
			if( rW >= tW ){
				plot.toolbar.style.left = leftPos+"px";
				plot.toolbar.style.width = (rW-1)+"px";
				plot.toolbarAbsoluteDiv.style.left = ((rW-tW)/2)+"px";
			} 
			else {
				plot.toolbar.style.left = (pL+plot.leftFlagPos*(pW-tW)/(pW-rW))+"px";
				plot.toolbar.style.width = tW+"px";
				plot.toolbarAbsoluteDiv.style.left = "0px";
			}
			plot.toolbar.style.top = topPos+"px";
			plot.toolbar.style.visibility = "visible";
			plot.toolbarAbsoluteDiv.style.visibility = "visible";          
        }

		var diff, startLeft, startRight;		

		// resets a pole after a mousemove event
		var resetPole = function( start, shift, pos, leftPole, opposite ){
			var newPos = start + shift;
			var newTime = getCorrelatedTime(newPos);
			var tickPos = plot.timeGeometry.toScreen(newTime);
			if( tickPos == pos ){
				return false;
			}
			var slice = plot.eds.getSliceIndex(newTime);
			if( leftPole ){
				plot.leftFlagPos = tickPos;
				if( opposite ){
					plot.rightFlagPos = plot.timeGeometry.toScreen(plot.eds.getSliceTime(slice+diff));
				}
			}
			else {
				plot.rightFlagPos = tickPos;
				if( opposite ){
					plot.leftFlagPos = plot.timeGeometry.toScreen(plot.eds.getSliceTime(slice-diff));
				}
			}
			return true;
		}

		// mousemove function that causes moving selection of objects and toolbar divs		
		var moveToolbar = function(start,actual){
			var pixelShift = actual-start;
			if( plot.status == 2 ){
				if( !resetPole( startLeft, pixelShift, plot.leftFlagPos, true, false ) ){
					return;
				}
			}
			else if( plot.status == 3 ){
				if( !resetPole( startRight, pixelShift, plot.rightFlagPos, false, false ) ){
					return;
				}
			}
			else if( plot.status == 4 ){
				var rangeWidth = plot.rightFlagPos - plot.leftFlagPos;
				var toolbarWidth = plot.toolbarAbsoluteDiv.offsetWidth;
				var plotWidth = plot.canvas.width;
				if( rangeWidth < toolbarWidth ){
					pixelShift *= (plotWidth-rangeWidth)/(plotWidth-toolbarWidth);
				}
				var plotPos = actual - plot.timeplot.getElement().offsetLeft;
				if( plotPos <= plotWidth/2 ){
					if( !resetPole( startLeft, pixelShift, plot.leftFlagPos, true, true ) ){
						return;
					}
				}
				else {
					if( !resetPole( startRight, pixelShift, plot.rightFlagPos, false, true ) ){
						return;
					}
				}
			}
			mapPositions(plot.leftFlagPos,plot.rightFlagPos);
			setRangeDivs();
           	plot.updateByTime();
		}

		// imitates user interaction mouse move
		var playIt = function(start,actual,reset){
			if( !plot.paused ){
				var pixel = plot.canvas.width / ( plot.eds.timeSlices.length - 1 ) / 5;
				var wait = 20 * pixel;
				if( reset ){
					actual = 0;
				}
				moveToolbar(start,actual);
				if( plot.rightFlagPos >= plot.canvas.width ){
					reset = true;
					wait = 1000;
				}
				else {
					reset = false;
				}
				setTimeout( function(){ playIt(start,actual+pixel,reset) }, wait );
			}
		}

		var deactivate;
		document.onclick = function(){
			if( plot.status > 1 ){
				if( deactivate ){
					plot.stop();
					document.onmousemove = null;
				}
				else {
					deactivate = true;
				}
			}
		}
		
    	/**
     	 * starts the animation
     	*/
		this.play = function(){
			if( this.leftFlagPos == null ){
				return;
			}
        	deactivate = false;
			plot.paused = false;
   			animationImage(true);
			plot.status = 4;
			startLeft = plot.leftFlagPos;
			startRight = plot.rightFlagPos;
   			diff = plot.eds.getSliceIndex(plot.rightFlagTime) - plot.eds.getSliceIndex(plot.leftFlagTime);
			var position = Math.round(plot.leftFlagPos);
			playIt(position,position+1,false);
		}
		
    	/**
     	 * stops the animation
     	*/
		this.stop = function(){
			plot.paused = true;
			plot.status = 0;
       		drag.style.backgroundImage = "url(images/dragger.png)";
   			animationImage(true);
		}

		// triggers the mousemove function to move the range and toolbar
        var toolbarEvent = function(evt){
        	deactivate = false;
  			var left = getMousePosition(evt).left;
   			startLeft = plot.leftFlagPos;
   			startRight = plot.rightFlagPos;
   			diff = plot.eds.getSliceIndex(plot.rightFlagTime) - plot.eds.getSliceIndex(plot.leftFlagTime);
   			document.onmousemove = function(evt){
    			moveToolbar(left,getMousePosition(evt).left);        	
      		}
        }
        
		// handles click on left handle
        this.leftHandle.onclick = function(evt){
        	if( plot.status != 2 ){
        		plot.status = 2;
				toolbarEvent(evt);
        	}
        }
        
		// handles click on right handle
        this.rightHandle.onclick = function(evt){
        	if( plot.status != 3 ){
        		plot.status = 3;
				toolbarEvent(evt);
        	}
        }
        
		// handles click on drag button
        drag.onclick = function(evt){
        	if( plot.status != 4 ){
        		plot.status = 4;
        		drag.style.backgroundImage = "url(images/dragger-click.png)";
				toolbarEvent(evt);
        	}
        }
                
        // handles mousedown-Event on timeplot
        var mouseDownHandler = function(elmt, evt, target){
        	if( plot.dataSources.length > 0 ){
            	var x = Math.round(SimileAjax.DOM.getEventRelativeCoordinates(evt, plot.canvas).x);
            	if (plot.status == 0 ) {
                	plot.core.reset();
                	plot.status = 1;
                	plot.mouseDownTime = getCorrelatedTime(x);
                	plot.mouseDownPos = plot.timeGeometry.toScreen(plot.mouseDownTime);
                	mapPositions(plot.mouseDownPos, plot.mouseDownPos, plot.mouseDownTime, plot.mouseDownTime);
            	}
            }
        }
        
        // handles mousemove-Event on timeplot
        var mouseMoveHandler = function(elmt, evt, target){        	
        	if( plot.dataSources.length > 0 ){
            	var x = Math.round(SimileAjax.DOM.getEventRelativeCoordinates(evt, plot.canvas).x);
            	if (plot.status == 1 && !plot.core.takeTime) {
                	plot.mouseTempTime = getCorrelatedTime(x);
                	plot.mouseTempPos = plot.timeGeometry.toScreen(plot.mouseTempTime);
                	mapPositions(plot.mouseDownPos, plot.mouseTempPos, plot.mouseDownTime, plot.mouseTempTime);
                	setRangeDivs();
            	}
            }
        }
                
	this.selectTimerange = function(min,max){
		var minTime = new Date(min);
		var maxTime = new Date(max);
		var minPos = getCorrelatedPosition(minTime);
		var maxPos = getCorrelatedPosition(maxTime);
                mapPositions(minPos, maxPos, minTime, maxTime);
                setRangeDivs();
                plot.updateByTime();
		animationImage(true);
	}

        // handles mouseup-Event on timeplot
        var mouseUpHandler = function(elmt, evt, target){
        	if( plot.dataSources.length > 0 ){
            var x = Math.round(SimileAjax.DOM.getEventRelativeCoordinates(evt, plot.canvas).x);
            if (plot.core.takeTime) {
                plot.core.setElementsTime(getCorrelatedTime(x));
            }
            else 
                if (plot.status == 1) {
                    plot.status = 0;
                    plot.mouseUpTime = getCorrelatedTime(x);
                    plot.mouseUpPos = plot.timeGeometry.toScreen(plot.mouseUpTime);
                    mapPositions(plot.mouseDownPos, plot.mouseUpPos, plot.mouseDownTime, plot.mouseUpTime);
                    setRangeDivs();
                    plot.updateByTime();
   					animationImage(true);
                    displayFeather();
                }
            }
        }
		
        // handles mouseout-Event on timeplot
		var mouseOutHandler = function(elmt, evt, target){
        	if( plot.dataSources.length > 0 ){		
        		var x = Math.round(SimileAjax.DOM.getEventRelativeCoordinates(evt, plot.canvas).x);
        		var y = Math.round(SimileAjax.DOM.getEventRelativeCoordinates(evt, plot.canvas).y);
            	if (x > plot.canvas.width-2 || isNaN(x) || x < 2 )
					plot.hoverUnselect(true);
            	if (y > plot.canvas.height-2 || isNaN(y) || y < 2 )
					plot.hoverUnselect(true);
			}
		}

        // handles mouse(h)over-Event on timeplot
        var mouseHoverHandler = function(elmt, evt, target){
        	if( plot.dataSources.length > 0 ){        
				plot.core.undoHover(false);
				var x = Math.round(SimileAjax.DOM.getEventRelativeCoordinates(evt, plot.canvas).x);
				var time = getCorrelatedTime(x);
        		var slices = plot.eds.timeSlices;
				for (var i = 0; i < slices.length; i++) {
            		if (slices[i].date.getTime() == time.getTime()){
						if( plot.hoverSlice != undefined && plot.hoverSlice.date.getTime() == slices[i].date.getTime() ){
							return;						
						}
						plot.hoverSlice = slices[i];
            			var objects = slices[i].elements;
            			for (var j = 0; j < objects.length; j++){
                			for (var k = 0; k < objects[j].length; k++){
                       			objects[j][k].setHover(true);
							}
						}
						break;
					}
				}
        		plot.core.updateTableAndMap(true);
        	}
		}		

		this.redrawPlot = function(){
			plot.clearTimeplot();				
			plot.eds.reset(this.timeGeometry);
	       	plot.timeplot.repaint();
			if( plot.leftFlagPos != null ){
				plot.leftFlagPos = getCorrelatedPosition(plot.leftFlagTime);
				plot.rightFlagPos = getCorrelatedPosition(plot.rightFlagTime);
				setRangeDivs();
			}
			else {
				plot.polesBySlices();
			}				
			plot.initLabels([]);
			plot.updateOverview();
		}

    	/**
     	 * handles zoom of the timeplot
     	 * @param {int} delta the change of zoom
     	 * @param {Date} time a time that corresponds to a slice, that was clicked
     	*/
		this.zoom = function(delta,time){
			if( plot.eventSources.length == 0 ){
				setTimeZoom(0);
				return false;
			}
			if( time == null ){
				time = getCorrelatedTime(plot.canvas.width/2);
			}
			if( plot.eds.setZoom(delta,time,plot.leftFlagTime,plot.rightFlagTime) ){
				this.redrawPlot();
			}
			setTimeZoom(plot.eds.getZoom());
			return true;
		}		

		// handles mousewheel event on the timeplot		
		var mouseWheelHandler = function(elmt, evt, target){
			if (evt.preventDefault){
				evt.preventDefault();
			}
			if( plot.dataSources.length == 0 ){
				return;
			}
			var delta = 0;
			if (!evt) evt = window.event;
			if (evt.wheelDelta) {
				delta = evt.wheelDelta/120; 
				if (window.opera) delta = -delta;
			}
			else if (evt.detail) {
				delta = -evt.detail/3;
			}
			if (delta){
				var x = Math.round(SimileAjax.DOM.getEventRelativeCoordinates(evt, plot.canvas).x);
				var time = getCorrelatedTime(x);
				plot.zoom(delta,time);
			}		
		}
				
        var timeplotElement = this.timeplot.getElement();
        SimileAjax.DOM.registerEvent(timeplotElement, "mousedown", mouseDownHandler);
        SimileAjax.DOM.registerEvent(timeplotElement, "mousemove", mouseMoveHandler);
        SimileAjax.DOM.registerEvent(timeplotElement, "mouseup", mouseUpHandler);
        SimileAjax.DOM.registerEvent(timeplotElement, "mousemove", mouseHoverHandler);
        SimileAjax.DOM.registerEvent(timeplotElement, "mouseout", mouseOutHandler);
        //SimileAjax.DOM.registerEvent(timeplotElement, "mousewheel", mouseWheelHandler);
        
        this.setCanvas();
        
    },
    
    /**
     * updates the data objects percentages after a selection on the timeplot had been performed
     */
    updateByTime: function(){
        this.setFeather();
        var slices = this.eds.timeSlices;
        var lfs, ls, rs, rfs;
        for (var i = 0; i < slices.length; i++) {
            if (slices[i].date.getTime() == this.leftFeatherTime.getTime()) 
                lfs = i;
            if (slices[i].date.getTime() == this.leftFlagTime.getTime()) 
                ls = i;
            if (slices[i].date.getTime() == this.rightFlagTime.getTime()) 
                rs = i;
            if (slices[i].date.getTime() == this.rightFeatherTime.getTime()) 
                rfs = i;
			if( slices[i].date.getTime() >= this.leftFlagTime.getTime() && slices[i].date.getTime() <= this.rightFlagTime.getTime() )
				slices[i].selected = true;
        }
        for (var i = 0; i < slices.length; i++) {
            var objects = slices[i].elements;
            for (var j = 0; j < objects.length; j++) 
                for (var k = 0; k < objects[j].length; k++) 
                    if (i > lfs && i < ls) 
                        objects[j][k].setPercentage((i - lfs) / (ls - lfs));
                    else 
                        if (i >= ls && i <= rs)
                            objects[j][k].setPercentage(1);
                        else 
                            if (i > rs && i < rfs) 
                                objects[j][k].setPercentage((rfs - i) / (rfs - rs));
                            else 
                                objects[j][k].setPercentage(0);
        }
        this.core.updateTableAndMap(false);
    },
    
    /**
     * sets the background canvas of the timeplot window (or resets it after resizing the browser window)
     */
    setCanvas: function(){
        var cv = document.createElement("canvas");
        cv.id = "plotCanvas";     
        var window = document.getElementById(this.window);    
        window.appendChild(cv);
        cv.width = window.clientWidth;
        cv.height = window.clientHeight;
        if (!cv.getContext && G_vmlCanvasManager) 
            cv = G_vmlCanvasManager.initElement(cv);
        var ctx = cv.getContext('2d');
        var gradient = ctx.createLinearGradient(0, 0, 0, this.canvas.height); 
        gradient.addColorStop(0, '#c9c9cb');
        gradient.addColorStop(1, '#ededed ');
        ctx.fillStyle = gradient;
        ctx.fillRect(0, 0, window.clientWidth, window.clientHeight);
    },
    
    /**
     * resets the timeplot to non selection status
     */
    resetTimeplot: function(){
    
        this.leftFlagPole.style.visibility = "hidden";
        this.rightFlagPole.style.visibility = "hidden";
        this.rangeBox.style.visibility = "hidden";
        this.leftFeather.style.visibility = "hidden";
        this.rightFeather.style.visibility = "hidden";
        this.leftHandle.style.visibility = "hidden";
        this.rightHandle.style.visibility = "hidden";
        this.toolbar.style.visibility = "hidden";
        this.toolbarAbsoluteDiv.style.visibility = "hidden";
        this.poles.style.visibility = "hidden";
                
        var plots = this.timeplot._plots;
        for (var i = 0; i < plots.length; i++) 
            plots[i].opacityPlot.style.visibility = "hidden";
        
        var slices = this.eds.timeSlices;
        if( slices != undefined ){
        	for (var i = 0; i < slices.length; i++){ 
            	slices[i].selected = false;
            }
        }

        this.status = 0;
        this.stop();
		document.onmousemove = null;
		animationImage(false);		        	
        
        this.leftFlagPos = null;
        this.leftFlagTime = null;
        this.rightFlagPos = null;
        this.rightFlagTime = null;
        
        this.mouseDownTime = null;
        this.mouseUpTime = null;
        this.mouseTempTime = null;
        
        this.mouseDownPos = null;
        this.mouseUpPos = null;
        this.mouseTempPos = null;
    },
    
    /**
     * sets a pole on the timeplot
     * @param {Date} time the time of the specific timeslice
     * @param {int[]} the number of selected elements per dataset
     */
	setPoles: function( poles ){
	
        this.poles.style.visibility = "visible";
        var cv = this.poles.getElementsByTagName("canvas")[0];
        cv.width = this.canvas.width;
        cv.height = this.canvas.height;
        if (!cv.getContext && G_vmlCanvasManager) 
            cv = G_vmlCanvasManager.initElement(cv);
        var ctx = cv.getContext('2d');
        ctx.clearRect(0,0,this.canvas.width,this.canvas.height);

		var plots = this.timeplot._plots;
		for( var z=0; z<poles.length; z++ ){ 
			var elements = poles[z].elements;
			var time = poles[z].time;
	        var pos = this.timeGeometry.toScreen(time);
			var heights = [];
			var h = 0;
			for ( var i = 0; i < elements.length; i++) {
				var data = plots[i]._dataSource.getData();
				for ( var j = 0; j < data.times.length; j++){ 
					if (data.times[j].getTime() == time.getTime()) {
						var height = plots[i]._valueGeometry.toScreen(plots[i]._dataSource.getData().values[j]) * elements[i];
						heights.push(height);
						if( height > h )
							h = height;
						break;
					}
				}
			}
			ctx.fillStyle = "rgb(102,102,102)";
        	ctx.beginPath();
			ctx.rect(pos-1,100-h,2,h);
      		ctx.fill();		
			for( var i=0; i<elements.length; i++ ){
				if( heights[i] > 0 ){
					ctx.fillStyle = "rgba("+colors[i].r1+","+colors[i].g1+","+colors[i].b1+",0.6)";
					ctx.beginPath();
					ctx.arc(pos, 100-heights[i], 2.5, 0, Math.PI*2, true);
					ctx.closePath();
					ctx.fill();
				}
			}
		}
	},
	
    /**
     * updates the timeplot by setting place poles, after selections had been executed in map or table.
     * its called by the core component.
     */
    polesBySlices: function(){
        var slices = this.eds.timeSlices;
        var poles = [];
        for (var i = 0; i < slices.length; i++) {
			var elements = [];
			var neededPole = false;
			for (var j = 0; j < slices[i].elements.length; j++){
				var count = 0;
				for (var k = 0; k < slices[i].elements[j].length; k++)
					if ( ( !slices[i].selected && slices[i].elements[j][k].percentage == 1 ) || slices[i].elements[j][k].hoverSelect) {
						neededPole = true;
						count++;
					}
				if( slices[i].elements[j].length == 0 )
					elements.push( slices[i].elements[j].length );
				else 
					elements.push( count / slices[i].elements[j].length );
			}
			if( neededPole ){
				poles.push( { time: slices[i].date, elements: elements } );
			}
		}
		this.setPoles(poles);
    },

    /**
     * calculates the new feather bounds
     */
    setFeather: function(){
		this.leftFeatherTime = this.leftFlagTime; 
		this.rightFeatherTime = this.rightFlagTime; 
        this.featherWidth = Math.floor(this.canvas.width / this.eds.timeSlices.length * this.slider.getValue());
        var slices = this.eds.timeSlices;
        for (var i = 0; i < slices.length; i++) {
            if (slices[i].date.getTime() == this.leftFlagTime.getTime()) {
                if (i - this.slider.getValue() >= 0) 
                    this.leftFeatherTime = slices[i - this.slider.getValue()].date;
                else 
                    this.leftFeatherTime = slices[0].date;
            }
            if (slices[i].date.getTime() == this.rightFlagTime.getTime()) {
                if (i + this.slider.getValue() < slices.length) 
                    this.rightFeatherTime = slices[i + this.slider.getValue()].date;
                else 
                    this.rightFeatherTime = slices[slices.length - 1].date;
            }
        }
    },
	
    /**
     * undo hover selection of elements in the hovered slice
     */
	hoverUnselect: function(update){
		if( this.hoverSlice != undefined ){
   			var objects = this.hoverSlice.elements;
   			for (var j = 0; j < objects.length; j++){
       			for (var k = 0; k < objects[j].length; k++){
           			objects[j][k].setHover(false);
				}
			}
			this.hoverSlice = undefined;
			if( update ){
        		this.core.updateTableAndMap(true);
        	}
		}
	},
	
    /**
     * returns the approximate left position of a slice inside the overview representation
     * @param {Date} time time of the slice
     */
	getOverviewLeft: function(time){
		var w = this.overview.offsetWidth;
		var s = this.eds.earliest().getTime();
		var e = this.eds.latest().getTime();
		var t = time.getTime();		
		return Math.round(w*(t-s)/(e-s));
	},
	
    /**
     * visualizes the overview div (shows viewable part of zoomed timeplot)
     */
	initOverview: function(){
        var labels = this.timeGeometry._grid;
        if( labels.length == 0 ){
        	var plot = this;
        	setTimeout( function(){ plot.initOverview(); }, 10 );
        	return;
        }

        this.overview.style.width = this.canvas.width+"px";

		this.overview.innerHTML = "";

        this.overviewRange = document.createElement("div");
        this.overviewRange.setAttribute('class','overviewRange');
        this.overview.appendChild(this.overviewRange);

        for( var i=0; i<labels.length; i++ ){
        	var label = document.createElement("div");
        	label.setAttribute('class','overviewLabel');
        	label.innerHTML = labels[i].label;
        	label.style.left = Math.floor(labels[i].x)+"px";
        	this.overview.appendChild(label);
        }

		this.updateOverview();
	},

    /**
     * visualizes the labels of the timeplot
     */
	initLabels: function(labels){
		if( labels.length == 0 ){
	        labels = this.timeGeometry._grid;
    	    if( labels.length == 0 ){
        		var plot = this;
        		setTimeout( function(){ plot.initLabels([]); }, 10 );
        		return;
        	}
        }
        this.plotLabels.style.width = this.canvas.width+"px";
		this.plotLabels.innerHTML = "";
        for( var i=0; i<labels.length; i++ ){
        	var label = document.createElement("div");
        	label.setAttribute('class','plotLabel');
        	label.innerHTML = labels[i].label;
        	label.style.left = Math.floor(labels[i].x)+"px";
        	this.plotLabels.appendChild(label);
        }
	},	
	
    /**
     * updates the overview div
     */
	updateOverview: function(){
		if( this.eds.getZoom() > 0 ){
			this.plotLabels.style.visibility = "hidden";
			this.timeGeometry._hideLabels = false;
			this.overview.style.visibility = "visible";
			this.shiftLeft.style.visibility = "visible";
			this.shiftRight.style.visibility = "visible";
			var left = this.getOverviewLeft( this.eds.timeSlices[this.eds.leftSlice].date );	
			var right = this.getOverviewLeft( this.eds.timeSlices[this.eds.rightSlice].date );
			this.overviewRange.style.left = left+"px";	
			this.overviewRange.style.width = (right-left)+"px";
		}
		else {
			this.timeGeometry._hideLabels = true;
			this.plotLabels.style.visibility = "visible";
			this.overview.style.visibility = "hidden";
			this.shiftLeft.style.visibility = "hidden";
			this.shiftRight.style.visibility = "hidden";
		}
	},
	
    /**
     * returns the time slices which are created by the extended data source
     */
	getSlices: function(){
		return this.eds.timeSlices;
	}
	
}