
var p;        
// --------------------------------		
function load() {
// --------------------------------		
    p = new Plotter('cv');
}


// --------------------------------		
function Plotter( id ) {
// --------------------------------		
    this.id = id;
    this.oCanvas = document.getElementById( id );
    this.oCanvas.oPlotter = this;
    this.oContext = this.oCanvas.getContext("2d");
    
    // --------------------------------		
    this.Init = function(  ) {
    // --------------------------------		
        this.dragOffsetX = 0;
        this.dragOffsetY = 0;
        this.bDragging = false;
        this.aPlots = [
            '1 / x + sin(x)', 
            'sin(pow(x,2))',
            'sin(x) + sin(pow(x,2))',
            'sin(x) - sin(pow(x,2))'
        ]
        this.aColors = [
            [255,128,0],
            [128,255,0],
            [255,0,128],
            [0,128,255],
            [255,255,0],
        ];
        
        this.iInterval = 3;
        this.setZoom(100);
        this.setOffsetX(-3);
        this.setOffsetY(-3);
        this.Draw();
    }


    // --------------------------------		
    this.setZoom = function( zoom ) {
    // --------------------------------		
        this.zoom = Math.max(1,Math.round(zoom));
        var oOutput = document.getElementById(this.id + '-zoom');
        oOutput.innerHTML = this.zoom;
    }
    
    // --------------------------------		
    this.setOffsetX = function( offsetX ) {
    // --------------------------------		
        this.offsetX = Math.round(offsetX * 100) / 100;
        var oOutput = document.getElementById(this.id + '-offsetX');
        oOutput.innerHTML = this.offsetX;
    }
    
    // --------------------------------		
    this.setOffsetY = function( offsetY ) {
    // --------------------------------		
        this.offsetY = Math.round(offsetY * 100) / 100;
        var oOutput = document.getElementById(this.id + '-offsetY');
        oOutput.innerHTML = this.offsetY;
    }


    // --------------------------------		
    this.oCanvas.onmousedown = function( e ) {
    // --------------------------------		
        if(!e) e = window.event;
        this.oPlotter.InitDrag(e);
    }

    // --------------------------------		
    this.InitDrag = function( e ) {
    // --------------------------------		
        this.dragStartOffsetX = e.clientX;
        this.dragStartOffsetY = e.clientY;
        //enable drag
        if(e.shiftKey) {
            this.bZooming = true;
        }
        else {
            this.bDragging = true;
        }
    }


    // --------------------------------		
    this.oCanvas.ondblclick = function( e ) {
    // --------------------------------		
        if(!e) e = window.event;
        this.oPlotter.CenterOnClick(e);
    }
    
    // --------------------------------		
    this.CenterOnClick = function(e) {
    // --------------------------------		
        this.setZoom(e.shiftKey ? this.zoom / 2 : this.zoom * 2);
        var xRange = Math.floor(this.oCanvas.width / this.zoom)
        var yRange = Math.floor(this.oCanvas.height / this.zoom)
        
        this.setOffsetX(this.mouseX - (xRange / 2));
        this.setOffsetY(this.mouseY - (yRange / 2));
        this.Draw();
    }
    
    // --------------------------------		
    this.AddPlot = function( ) {
    // --------------------------------		
    var sPlot = prompt('Enter a plot function:\nUse x and the native Math methods (eg. sin, cos, abs, sqrt, pow).\n\nExample: sin(x) + pow(x, 2)\nLeave empty to clear the canvas.');
        if(!sPlot) this.aPlots.length = 0;
        else this.aPlots.push(sPlot);
        this.Draw();
    }


    // --------------------------------		
    this.oCanvas.onmouseup = function( e ) {
    // --------------------------------		
        if(!e) e = window.event;
        this.oPlotter.StopDrag(e);
    }
    
    // --------------------------------		
    this.StopDrag = function( e ) {
    // --------------------------------		
        this.bDragging = false;
        this.bZooming = false;
        this.iInterval = 3;
        this.Draw();
    }


    // --------------------------------		
    this.oCanvas.onmousemove = function( e ) {
    // --------------------------------		
        if(!e) e = window.event;
        this.oPlotter.Drag(e);
    }
    
    // --------------------------------		
    this.Drag = function( e ) {
    // --------------------------------		
        var oOutput = document.getElementById(this.id + '-mouse');
        if(e.offsetX) {
            var posX = e.offsetX;
            var posY = e.offsetY;
        }
        else {
            var posX = e.clientX - e.target.offsetLeft;
            var posY = this.oCanvas.height - e.clientY + e.target.offsetTop;
        }
        var coord = this.px2coord(posX, posY);
        this.mouseX = Math.round(coord.x * 100) / 100;
        this.mouseY = Math.round(coord.y * 100) / 100;
        oOutput.innerHTML = '(' + this.mouseX + ',' + this.mouseY  + ')';
        
        if(!this.bDragging && !this.bZooming) return;
        
        if(this.bDragging) {
            this.dragOffsetX = e.clientX - this.dragStartOffsetX;
            this.dragOffsetY = e.clientY - this.dragStartOffsetY;
                
            this.setOffsetX(this.offsetX - (this.dragOffsetX / this.zoom));
            this.setOffsetY(this.offsetY + (this.dragOffsetY / this.zoom));
            
            if(e.shiftKey) {
                this.setOffsetX(Math.round(10*this.offsetX)/10);
                this.setOffsetY(Math.round(10*this.offsetY)/10);
            }                                                                                                       
        }
        else if(this.bZooming) {
            this.zoomOffsetX = e.clientX - this.dragStartOffsetX;
            this.zoomOffsetY = e.clientY - this.dragStartOffsetY;
            
            var xRange = this.oCanvas.width / this.zoom;
            var yRange = this.oCanvas.height / this.zoom;
            
            this.setZoom(this.zoom + this.zoomOffsetY);
            
            var xNewRange = this.oCanvas.width / this.zoom;
            var yNewRange = this.oCanvas.height / this.zoom;
            
            var xOffset = ((xNewRange - xRange) / 2);
            var yOffset = ((yNewRange - xRange) / 2);
            
            this.setOffsetX(this.offsetX - xOffset);
            this.setOffsetY(this.offsetY - yOffset);
        }
        
        this.Draw();
        this.dragStartOffsetX = e.clientX;
        this.dragStartOffsetY = e.clientY;
    }


    // --------------------------------		
    this.px2coord = function( x, y ) {
    // --------------------------------		
        return {
            x:(x / this.zoom) + this.offsetX,
            y:(y / this.zoom) + this.offsetY
        };
    }
    
    // --------------------------------		
    this.coord2px = function( x, y ) {
    // --------------------------------		
        return {
            x:Math.round((x - this.offsetX) * this.zoom),
            y:this.oCanvas.height - Math.round((y - this.offsetY) * this.zoom)
        };
    }


    // --------------------------------		
    this.DrawGrid = function( ) {
    // --------------------------------
        if(this.zoom > 5) {
            //this.oContext.strokeStyle = "rgb(64, 0, 0)";
            var xRange = Math.floor(this.oCanvas.width / this.zoom)
            var xMax = Math.ceil(xRange + this.offsetX);
            var xMin = Math.floor(xMax - xRange);
            
            var yRange = Math.floor(this.oCanvas.height / this.zoom)
            var yMax = Math.ceil(yRange + this.offsetY);
            var yMin = Math.floor(yMax - yRange);
            
            this.oContext.strokeStyle = 'rgb(64, 0, 0)';
            
            // X & Y-Axis
            this.oContext.beginPath();
            for(var x = xMin; x <= xMax; x++) {
                var grid = this.coord2px(x,0);
                this.oContext.moveTo(grid.x, 0);
                this.oContext.lineTo(grid.x, this.oCanvas.height);
            }
            for(var y = yMin; y <= yMax; y++) {
                var grid = this.coord2px(0,y);
                this.oContext.moveTo(0, grid.y);
                this.oContext.lineTo(this.oCanvas.width, grid.y);
            }
            this.oContext.stroke();
        }
        this.oContext.strokeStyle = "rgb(255, 0, 0)";
        // X & Y-Axis
        this.oContext.beginPath();
        var origin = this.coord2px(0,0);
        this.oContext.moveTo(0, origin.y);
        this.oContext.lineTo(this.oCanvas.width, origin.y);
        this.oContext.moveTo(origin.x, 0);
        this.oContext.lineTo(origin.x, this.oCanvas.height);
        this.oContext.stroke();
        this.oContext.closePath();
        
        // Write "o", "x" and "y" at the axes
        try {
            ctout_textout(this.oContext,origin.x + 5, origin.y + 1,'0');
            ctout_textout(this.oContext,origin.x + 5, 0,'y');
            ctout_textout(this.oContext,this.oCanvas.width-10, origin.y + 1,'x');
        }
        catch(e) {}
    }
    
    // --------------------------------		
    this.Draw = function( ) {
    // --------------------------------
        this.oContext.clearRect(0,0,this.oCanvas.width, this.oCanvas.height);

        
        this.DrawGrid();
        // Don't draw anything else but the grid if dragging or zooming
        if(this.bDragging || this.bZooming) return;
        
        var oPlotList = document.getElementById('cv-plots');
        while(oPlotList.firstChild) {
            oPlotList.removeChild(oPlotList.firstChild);
        }
        
        this.oContext.lineWidth = "1";

        var iColor = 0;
        for(var i = 0; i < this.aPlots.length; i++) {
            var plot = this.aPlots[i];
            
            // Create list items
            var oPlot = document.createElement('li');
            oPlot.appendChild(document.createTextNode('Y = ' + plot));
            oPlot.style.color = 'rgb(' + this.aColors[iColor].join(',') + ')';
            oPlotList.appendChild(oPlot);
            
            // Draw plot
            this.oContext.strokeStyle = this.oContext.fillStyle = 'rgb(' + this.aColors[iColor++].join(',') + ')';
            if(iColor == this.aColors.length) iColor = 0;
            this.oContext.beginPath();
            var y = 0;
            for(var X = 0; X <= this.oCanvas.width; X += this.iInterval) {
                var x = this.px2coord(X, 0).x;
                var y = eval('with(Math) { ' + plot + '}');
                var px = this.coord2px(x, y);
                
                try {
                    if(X == 0 || px.y < 0) {
                        this.oContext.moveTo(px.x, px.y);
                    }
                    else if(px.y > this.oCanvas.height) {
                        this.oContext.moveTo(px.x, px.y);
                    }
                    //else if(this.bDragging || this.bZooming) this.oContext.fillRect(px.x,px.y,2,2)
                    else {
                        this.oContext.lineTo(px.x, px.y);
                    }
                }
                catch(e) {
                }
            }
            this.oContext.stroke();
            
        }
    }
    
    this.Init();
}



