function pc(v) {
    return (v|0)+0.5;    
};

function mv(o,n) {
    if(n > 0) {
        for(var i = 0;i<n;i++) {
            if(!o.next) {
                break;
            }
            o = o.next;
        }
    } else {
        for(var i = n;i<0;i++) {
            if(!o.prev) {
                break;
            }
            o = o.prev;
        }
    }
    return o;
}; 

 function chartNF( number, decimals, dec_point, thousands_sep ) {
    var n = number, prec = decimals;
    n = !isFinite(+n) ? 0 : +n;
    prec = !isFinite(+prec) ? 0 : Math.abs(prec);
    var sep = (typeof thousands_sep == "undefined") ? ',' : thousands_sep;
    var dec = (typeof dec_point == "undefined") ? '.' : dec_point;

    var s = (prec > 0) ? n.toFixed(prec) : Math.round(n).toFixed(prec); //fix for IE parseFloat(0.55).toFixed(0) = 0;

    var abs = Math.abs(n).toFixed(prec);
    var _, i;

    if (abs >= 1000) {
        _ = abs.split(/\D/);
        i = _[0].length % 3 || 3;

        _[0] = s.slice(0,i + (n < 0)) +
              _[0].slice(i).replace(/(\d{3})/g, sep+'$1');

        s = _.join(dec);
    } else {
        s = s.replace('.', dec);
    }

    return s;
};

function XYChart(set, source, allowScroll) {
    /**
    * mountain gradient
    */
    this._gradient = null;
    /**
    * parent chartset
    */               
    this._parent = set;
    /**
    * type
    */ 
    this._type = 'candle';
    this._content           = 1;
    this._showMarkers       = set._style.showMarkers;
    this._showInformation   = set._style.showInformation;
    this._showXLabels       = set._style.showXLabels;
    this._showYLabels       = set._style.showYLabels;
    this._showCursor        = set._style.showCursor;
    this._showBackgroundImage = true;
    
    this._informationLength = null;
    /**
    * canvas context
    * @var CanvasRenderingContext2D
    */
    this._context = set._getContext();
    
    /**
    * data source
    */
    this._source = source;
    /**
    * flash
    */
    this._flash = 0;
    this._flashTimeout = null;
    this._flashInterval = null;
    this._flashTimer = 0;
    
    /**
    * last value
    */
    this._lastValue = (source.buffer && source.buffer.end) ? source.buffer.end.close : 0;
    
    /**
    * chart size
    * @var array
    */
    this._size = 200;
    this._yAxisMargins = [set._style.yAxisMargins[0],set._style.yAxisMargins[1]];
    this._tickWidth = null;
    
    /**
    * chart position
    */
    this._position = 50;
    
    /**
    * Y axis bounds
    * @var array
    */
    this._yAxisBounds = [0,0];
    
    /**
    * value precision
    */
    this._precision = 2;
    
    /**
    * benchmarks
    */
    this._benchmarks = [];
    /**
    * indicators
    */
    this._indicators = [];
    
    /**
    * tools
    */
    this._tools = [];
    
    /**
    * selected tool/action/handle
    */
    this._selectedHandle = null;
    this._selectedTool = null;
    
    /**
    * marker areas
    */
    this._markerAreas = [null,null,null,null];
    
    /**
    * cursor position
    */
    this._cursor    = null;
    this._cursorX   = null;
    
    //==============================================
    // public functions
    //==============================================
    /**
    * set y axis margins
    */
    this.setYAxisMargins = function(bottom, top) {
        this._yAxisMargins = [bottom, top];    
    }
    
    /**
    * set height
    */
    this.setHeight = function(h) {
        this._size = h;
        this._parent._updateLayout();
    }
    
    /**
    * set chart type
    */
    this.setType = function(type) {
        if(!(type == 'candle' || type == 'mountain' || type == 'line' || type == 'ohlc')) {
            return;
        }
        if(type == this._type) {
            return;
        }
        
        this._type = type;
        
        var i = this._parent._detectInterval();
        if(i != this._parent._interval) {
            this._parent.setInterval(i);    
        } else {
            this.redraw();
        }
    }
    
    /**
    * show cursor
    */
    this.showCursor = function(flag) {
        this._showCursor = flag;
        this._cursor = null;
        this.redraw();
    }
    
    /**
    * show current value
    */
    this.showMarkers = function(flag) {
        this._showMarkers = flag;
        this.redraw();
    }
    
    /**
    * show information text
    */
    this.showInformation = function(flag) {
        this._showInformation = flag;
        this.redraw();
    }
    
    /**
    * show labels
    */
    this.showLabels = function(x,y) {
        this._showXLabels = x;
        this._showYLabels = y;
        this.redraw();
    }
    
    /**
    * show data
    */
    this.showData = function(flag) {
        if(flag === true) {
            this._content = 1;
        } else if(typeof flag == 'number') {
            this._content = flag;
        } else {
            this._content = 2;
        }
        this._updateYAxisBounds();
        this.redraw();
    }
    
    /**
    * add benchmark
    */
    this.addBenchmark = function(instrumentId, exchangeId, quoteSource) {
        // cancel tool action
        if(this._selectedTool) {
            this._selectedTool = null;
        }
        
        var source = this._parent._getSource(instrumentId,exchangeId,quoteSource);
        this._benchmarks.push(source);
        
        this.OnDataUpdated();
    }
    
    /**
    * remove benchmark by id
    */
    this.removeBenchmark = function(index) {
        if(index < 0 || index >= this._benchmarks.length) {
            return;
        }    
        this._benchmarks.splice(index, 1);
        
        this.OnDataUpdated();
    }
    
    /**
    * return number of active benchmarks
    */
    this.getNumBenchmarks = function() {
        return this._benchmarks.length;
    }
    
    /**
    * add a tool
    */
    this.addTool = function(tool) {
        // tools disabled in benchmark mode
        if(this._benchmarks.length > 0) {
            return;
        }
        this._selectedTool = tool;
    }
    this._addTool = function(x,y) {
        console.log(x,y);
        var t = this._getTimestamp(x);
        var v = this._getValue(y);
        
        var tool = new ToolLine(t,v);
        
        for(var i=0;i<tool._handles.length;i++) {
            this._updateHandle(tool._handles[i]);
        }
        
        this._tools.push(tool);
        
        return tool._handles[0];    
    }
    
    /**
    * remove a tool
    */
    this.removeTool = function(i) {
        if(i < 0 || i >= this._tools.length) {
            return;
        }
        this._tools.splice(i,1);
        
        this.redraw();
    }
    
    /**
    * get amount of active tools
    */
    this.getNumTools = function() {
        return this._tools.length;
    }
    
    /**
    * get tool by index
    */
    this.getTool = function() {
        if(i < 0 || i >= this._tools.length) {
            return null;
        }              
        return this._tools[i];
    }
    
    /**
    * get amount of active indicators
    */
    this.getNumIndicators = function() {
        return this._indicators.length;    
    }
    
    /**
    * get indicator by index
    */
    this.getIndicator = function(i) {
        if(i < 0 || i >= this._indicators.length) {
            return null;
        }              
        return this._indicators[i];
    }
    
    /**
    * get indicator color
    */
    this.getIndicatorColor = function(i) {
        if(i < 0) {
            return '';
        }
        return this._parent._style.indicatorColor[i<this._parent._style.indicatorColor.length?i:0];
    }
    
    /**
    * add an indicator
    */
    this.addIndicator = function(type, param1, param2, param3) {
        // fetch indicator from source
        var object = this._source.source.addIndicator(type, param1, param2, param3);
        if(!object) {
            return;
        }
        
        // check if it is already registered
        for(var j=0;j<this._indicators.length;j++) {
            if(this._indicators[j] == object) {
                this._source.source.removeIndicator(object);
                return;
            }
        }    
        
        // add to indicator list
        this._indicators.push(object);
      
        this._updateYAxisBounds();  
        
        // redraw
        this.redraw();
    };
    
    /**
    * remove an indicator
    */
    this.removeIndicator = function(index) {
        if(index < 0 || index >= this._indicators.length) {
            return;
        }
        
        this._source.source.removeIndicator(this._indicators[index]);
        this._indicators.splice(index, 1);
        
        // redraw
        this.redraw();
    };
    
    /**
    * check if end is visible
    */
    this.isEndVisible = function(end) {
        if(!this._source.buffer || !this._source.span) {
            return false;
        }
        if(typeof end == 'undefined') {
            return this._source.span[1] && !this._source.span[1].next;
        }
        if(this._source) {
            return tickT(end, this._source.buffer.interval) >= tickT(this._source.getBuffer(this._source.buffer.interval).getLastTimestamp(),this._source.buffer.interval);
        }
        return false;
    };
    
    //==============================================
    // callbacks
    //==============================================
    /**
    * update style
    */
    this._updateStyle           = function() {
        var endX = this._parent._size[0];
        if(this.isEndVisible()) {
            endX = this._getX(this._source.span[1].index - this._source.span[0].index);
        }
        
        this._showMarkers       = this._parent._style.showMarkers;
        this._showInformation   = this._parent._style.showInformation;
        this._showXLabels       = this._parent._style.showXLabels;
        this._showYLabels       = this._parent._style.showYLabels;
        this._showCursor        = this._parent._style.showCursor;
    
        if(typeof this._parent._style.mountainColor == 'string') {
            this._gradient = this._parent._style.mountainColor;    
        } else {
            this._gradient = this._context.createLinearGradient(0, this._parent._size[0]/2, endX, this._size/2);
            this._gradient.addColorStop(0, this._parent._style.mountainColor[0]);
            this._gradient.addColorStop(1, this._parent._style.mountainColor[1]);            
        }
    }
    
    /**
    *
    */
    this._updateFlash         = function() {
        if(!this._source.span) {
            return;
        }
        
        var lastValue = this._source.buffer.end.close;
        
        if(!this._lastValue || lastValue == this._lastValue) {
            this._flash = 0;
        } else if(lastValue > this._lastValue) {
            this._flash = 1;
        } else if(lastValue < this._lastValue){
            this._flash = -1;
        }
        
        if(this._flash != 0) {
            if(this._flashTimeout !== null) {
                clearTimeout(this._flashTimeout);
            }
            var me = this;
            this._flashTimeout = setTimeout(function() {me._updateFlash(); me.redraw();},(this._chartType=='candle'||this._chartType=='ohlc')?1000:1500);
            this._flashTimer = 0;
            if(this._flashInterval === null) {
                this._flashInterval = setInterval(function() {me._flashTimer += 1; me.redraw();},150);
            }
        } else {
            clearInterval(this._flashInterval);
            this._flashInterval = null;
        }
        
        this._lastValue = lastValue;        
    };
    
    this.OnSourceChanged = function() {
        this._informationLength = null;
        
        this._lastValue = null;
        if(this._flashTimeout !== null) {
            clearTimeout(this._flashTimeout);
        }
        if(this._flashInterval !== null) {
            clearInterval(this._flashInterval);
        }
        
        if(this._source.span) {
            this.OnDataUpdated();
        }
    }
    
    this.OnDataUpdated = function() {    
        // if cursor selection changed, update it
        if(this._cursor) {
            var newCursor = this._getChartValue(this._cursorX);
            this._cursor = newCursor;
        }
        
        this._tickWidth = this._getTickWidth();
        
        this._updatePrecision();
        
        this._updateYAxisBounds();
        
        this._updateFlash();
        
        // update tools
        if(this._benchmarks.length == 0) {
            var h = null;
            for(var i=0;i<this._tools.length;i++) {
                for(var j=0;j<this._tools[i]._handles.length;j++) {
                    this._updateHandle(this._tools[i]._handles[j]);
                }
            }              
        }
        
        return true;
    };
    
    //==============================================
    // private functions
    //==============================================
    /**
    * get plotarea size
    */
    this._getPlotarea = function() {
        return [this._parent._size[0],
                this._size];
    }
    
    /**
    * move handle
    */
    this._moveHandle = function(h,x,y) {
        h.index     = this._source.span[0].index+this._getIndex(x);
        h.timestamp = this._getTimestamp(x);
        h.value     = this._getValue(y);
    }
    
    /**
    * update handle
    */
    this._updateHandle = function(h) {
        h.index     = this._getIndexByTimestamp(h.timestamp);
    }
    
    /**
    * get value for y coordinate
    */
    this._getValue = function(y) {
        y -= this._yAxisMargins[1];
        var height = this._getPlotarea()[1]-this._yAxisMargins[0]-this._yAxisMargins[1];
        return (1-(y/height))*(this._yAxisBounds[1]-this._yAxisBounds[0]) + this._yAxisBounds[0];    
    }
    
    /**
    * get index for timestamp
    */
    this._getIndexByTimestamp = function(t) {
        var cur = this._source.buffer.begin;
        while(cur) {
            if(cur.timestamp >= t) {
                return cur.index;
            }
            cur = cur.next;
        }
        return 0;
    }
    
    /**
    * get index for x coordinate
    */
    this._getIndex = function(x) {
        var n = (this._source.span[1].index - this._source.span[0].index) + 1 + this._source.offsetL + this._source.offsetR;
        var o = -this._tickWidth*(this._source.offset-this._source.offsetL);
        var step = this._parent._size[0] /(n-1);
        
        return Math.round((x-o)/step);
    }
    
    /**
    * get value object for x coordinate
    */
    this._getChartValue = function(x) {
        if(!this._source || !this._source.span) {
            return null;
        }
        var i = this._getIndex(x)+this._source.span[0].index;
        var cur = this._source.span[0];
        while(cur) {
            if(cur.index == i) {
                return cur;
            }
            cur = cur.next;
        }
        return null;
    }
    
    /**
    * get timestamp for x coordinate
    */
    this._getTimestamp = function(x) {
        var i = this._getIndex(x)+this._source.span[0].index;
        var cur = this._source.span[0];
        while(cur) {
            if(cur.index == i) {
                return cur.timestamp;
            }
            cur = cur.next;
        }
        return 0;
    }
    
    /**
    * get tick width
    */
    this._getTickWidth = function(i) {
        if(!this._source || !this._source.span) {
            return 1;
        }
        
        var n = (this._source.span[1].index - this._source.span[0].index) + 1 + this._source.offsetL + this._source.offsetR;
        
        if(n==1) {
            return this._parent._size[0];
        }
        var step = this._parent._size[0] / (n-1);
        
        if(typeof i != "number") {
            return step;
        }
        
        return ((step*(i+1))|0) - ((step*(i))|0);
    };
    
    /** 
    * get y coordinate for value
    */
    this._getX = function(value) {
        if(!this._source || !this._source.span) {
            return 1;
        }
        
        var n = (this._source.span[1].index - this._source.span[0].index) + 1 + this._source.offsetL + this._source.offsetR;
        var o = -this._tickWidth*this._source.offset;
        o += this._tickWidth*this._source.offsetL;
        var step = (this._parent._size[0]-2) / (n-1);
        return 1 + ((value*step)|0) + (o|0) + 0.5;
    };
    
    /** 
    * get y coordinate for value
    */
    this._getY = function(value) {
        var height = this._size-this._yAxisMargins[0]-this._yAxisMargins[1];
        return this._yAxisMargins[1] + ((( 1 - ( ( value - this._yAxisBounds[0] ) / ( this._yAxisBounds[1] - this._yAxisBounds[0]) ) ) * height)|0) + 0.5;        
    };
    
    /**
    * update yAxisBounds
    */
    this._updateYAxisBounds = function() {
        if(this._source.buffer === null) {
            return;
        }
        
        var s = this._source.span[0];
        var e = this._source.span[1];
        
        if(s.prev) { s = s.prev; };
        if(e.next) { e = e.next; };
        
        var cur = s;
        var low     = 1E+18;
        var high    = 0;
        var i;
        
        // include zero in volume charts
        if(this._content == 3) {
            low = 0;
        }
        
        var indicators = [];
        for(var index in this._indicators) {
            for(var index2 in this._indicators[index].id) {
                indicators.push(this._indicators[index].id[index2]);
            }    
        }
        
        if(this._benchmarks.length > 0) {
            var v = 0;
            if(this._content == 1) {
                do {
                    v = (cur.close/this._source.relative)*100;
                    if(v < low) {
                        low = v;
                    }
                    if(v > high) {
                        high = v;
                    }
                    cur = cur.next;
                } while(cur && cur != e.next);
            } 
                
            for(var i=0;i<this._benchmarks.length;i++) {
                if(!this._benchmarks[i].span) {
                    continue;
                }
                cur = this._benchmarks[i].span[0];
                e = this._benchmarks[i].span[1];
                do {
                    v = (cur.close/this._benchmarks[i].relative)*100;
                    if(v < low) {
                        low = v;
                    }
                    if(v > high) {
                        high = v;
                    }
                    cur = cur.next;
                } while(cur && cur != e.next);    
            }
        } else {
            do {
                if(this._content == 1) {
                    if(cur.low < low) {
                        low = cur.low;
                    }
                    if(cur.high > high) {
                        high = cur.high;
                    }
                } else if(this._content == 3) {
                    if(cur.volume < low) {
                        low = cur.volume;
                    }
                    if(cur.volume > high) {
                        high = cur.volume;
                    }
                }
                
                for(i in indicators) {
                    if(cur.indicator[i] === null) {
                        continue;
                    }
                    if(cur.indicator[i] < low) {
                        low = cur.indicator[i];
                    }
                    if(cur.indicator[i] > high) {
                        high = cur.indicator[i];
                    }    
                }
                
                cur = cur.next;
            } while(cur && cur != e.next);
        } 
        var range = (high-low)*0.1;
        if(range == 0) {
            range = low*0.1;
        }
        
        if(low-range < 0) {
            this._yAxisBounds[0] = 0;
        } else {
            this._yAxisBounds[0] = low-range;
        }
        
        this._yAxisBounds[1] = high+range;
    }
    
    /**
    * update precision
    */
    this._updatePrecision = function() {
        if(!this._source || !this._source.buffer || !this._source.buffer.begin) {
            this._precision = 2;
            return;
        }
        
        // update precision
        var s = this._source.buffer.begin.close.toString();
        var p = s.indexOf('.');
        if(p == -1) {
            p = 2;
        } else {
            p = (s.length - s.indexOf('.') - 1);
        }
        if(p > 4) {
            p = 4;
        } else if(p < 2) {
            p = 2;
        }
        this._precision = p;
    }
    
    /**
    * redraw
    */
    this.redraw = function() {
        //this._draw();
        this._parent.redraw();    
    }
    
    /**
    * draw
    */
    this._draw = function(isMoving) {
        
        var c = this._context;
        
        c.save();
       // c.clearRect(0,0,this._parent._size[0],this._size);
        c.beginPath();
        c.rect(0, this._position, this._parent._size[0], this._size);
        c.clip();
        
        c.translate(0, this._position);
        
        var x = 0, y = 0;
        
        var pa = this._getPlotarea();
        var valueRange = (this._yAxisBounds[1] - this._yAxisBounds[0]);
        
        var s,s2,e,e2;
        // chart is empty
        if(this._source.buffer === null || !this._source.span){
            s = s2 = e = e2 = null;
        } else {
            s = s2 = this._source.span[0];
            e = e2 = this._source.span[1];
            
            while(s2 && this._getX(s2.index - s.index) < 0) {
                s2 = s2.next;
            }
            
            while(e2 && this._getX(e2.index - s.index) > this._parent._size[0]) {
                e2 = e2.prev;
            }
            
            if(s2 && s2.prev) { s2 = s2.prev; };
            if(e2 && e2.next) { e2 = e2.next; };
        }
              
        var sy = this._yAxisBounds[0], ey = this._yAxisBounds[1];
        
        var v = 0, l = null;
        // calculate stepping
        var xLabelStep = 0;
            
        if(!s2 || !e2) {
            // draw info
            c.textAlign = 'center';
            c.textBaseline = 'middle';
            c.font = this._parent._style.infoLabelFontStyle  + ' ' +  this._parent._style.infoLabelFontSize + 'px \''  + this._parent._style.infoLabelFontFamily + '\'';
            c.fillStyle = this._parent._style.infoLabelColor;
            c.strokeStyle = this._parent._style.infoLabelColor;
            c.fillText(this._parent._isLoading()?'Daten werden geladen':'Keine Daten gefunden', this._parent._size[0]/2, this._size/2 + 50);
        
            c.restore();
            return;
        } else {
            //===============================================================
            // draw grid x
            //===============================================================
            var n = 0, span = (e2.index - s2.index)*this._source.buffer.interval, size = this._getPlotarea()[0] - (this._source.offsetL+this._source.offset+this._source.offsetR)*tw;
            if(this._source.buffer.interval < 86400) {
                n = span/ESpans.HOUR;
            } else if(this._source.buffer.interval < ESpans.WEEK){
                n = span/ESpans.WEEK;
            } else {
                n = span/ESpans.MONTH;
            }
            n = Math.ceil(n); 
            if(n >= 1) {
                xLabelStep = 1;
                var ls = (this._parent._style.xLabelFontSize+this._parent._style.labelSpacing);
                var fit = (this._parent._size[0]-this._parent._style.xAxisMargins[0]-this._parent._style.xAxisMargins[1])/ls;
                xLabelStep = Math.ceil(n/fit);
                if(xLabelStep < 1) {
                    xLabelStep = 1;
                }
            }
            
            if(this._parent._style.showVGrid && xLabelStep > 0) {
                c.strokeStyle = this._parent._style.vGridColor;
                
                var cur = s2;
                while(cur && cur != e2.next) {
                    if(!cur.label || (cur.labelIndex%xLabelStep!=0)) {
                        cur = cur.next;
                        continue;                    
                    }
                    
                    x = this._getX(cur.index - s.index);
                    
                    c.beginPath();
                    c.moveTo(x,1.5);
                    c.lineTo(x,this._size-1.5);
                    c.stroke();
                    
                    cur = cur.next;
                }
            }
            
            //===============================================================
            // draw grid y
            //===============================================================
            var height = this._getPlotarea()[1]-this._yAxisMargins[0]-this._yAxisMargins[1];
            // calculate number of labels to show
            var yLabels = Math.floor(Math.min(height / (this._parent._style.yLabelFontSize+this._parent._style.labelSpacing),5));
            if(yLabels < 2 && height/this._parent._style.yLabelFontSize >= 2) {
                yLabels = 2;
            }     
            var step = valueRange/(yLabels-1);
            if(valueRange>0 && this._parent._style.showHGrid) {
                c.strokeStyle = this._parent._style.hGridColor;
                
                x = this._parent._size[0]-1;
                v = this._yAxisBounds[0];
                for(var i=0;i<yLabels;i++) {
                    y = this._getY(v);
                    // render grid
                    c.beginPath();
                    c.moveTo(0.5,y);
                    c.lineTo(x,y);
                    c.stroke();
                    
                    v+=step;
                }
            }
        }
        
        var tw = this._tickWidth;
                
        if(this._source.offsetL > 0) {
            c.fillStyle = this._parent._style.inactiveColor;
            c.fillRect(0,0,this._getX(0),this._size);
        }
        //===============================================================
        // flash new quotes
        //===============================================================
        var endVisible = this.isEndVisible();
        
        if(this._content == 1) {
            if((this._type == 'candle'|| this._type == 'ohlc') && this._flash != 0 && 
                endVisible) {
                
                x = this._getX(e.index - s.index);
                
                if(this._flash < 0) {
                    c.fillStyle = this._parent._style.flashDownColor;
                } else {
                    c.fillStyle = this._parent._style.flashUpColor;
                }
                c.fillRect(x-tw/2,0,tw,this._getPlotarea()[1]);
            }
        }
        
        if(this._benchmarks.length > 0) {
            c.lineWidth = this._parent._style.lineWidth;
            var b = null;
            for(var i=0;i<this._benchmarks.length;i++) {
                if(this._parent._style.benchmarkColor.length <= i) {
                    c.strokeStyle = this._parent._style.benchmarkColor[this._parent._style.benchmarkColor.length-1];
                } else {
                    c.strokeStyle = this._parent._style.benchmarkColor[i];
                }
                
                b = this._benchmarks[i];
                if(!b.span) {
                    continue;
                }
                
                c.beginPath();
                
                var begin = b.span[0], end = b.span[1];
                if(begin.prev) { begin = begin.prev; }
                if(end.next) { end = end.next; }
                var cur = s2,cur2 = begin;
                var first = false;
                while(cur && cur != e2.next) {
                    while(cur2.next && cur2.next.timestamp <= cur.timestamp) {
                        cur2 = cur2.next;
                    }
                    if(cur2.timestamp <= cur.timestamp) {
                        x = this._getX(cur.index - s.index);
                        y = this._getY((cur2.close/b.relative)*100);
                        
                        if(!first) {
                            c.moveTo(x,y);
                            first = true;
                        } else {
                            c.lineTo(x,y);
                        }
                    }
                    
                    cur = cur.next;
                }
                
                c.stroke();    
            }
        }
        
        // only render tools if benchmark mode is disabled
        if(this._benchmarks.length == 0 && this._tools.length > 0) {
            var h = null;
            for(var i=0;i<this._tools.length;i++) {
                this._tools[i].draw(this,s,s2,e2);
                c.fillStyle = '#000000';
                for(var j=0;j<this._tools[i]._handles.length;j++) {
                    h = this._tools[i]._handles[j];
                    
                    c.beginPath();
                    c.arc(this._getX(h.index-s.index),this._getY(h.value), 4, 0,6.28,false);
                    c.fill();
                }
            }        
        }
            
        if(s2 && e2 && s2.index < e2.index) {         
            if(this._indicators.length > 0) {
                c.lineWidth = 2;
                
                var i;
                for(var j=0;j<this._indicators.length;j++) {
                    i = this._indicators[j].id;
                    this._indicators[j].draw(this, s, s2, e2);
                }
                c.lineWidth = 1;
            }
            
            if(this._content == 3 && valueRange>0) {
                var base = this._getY(0);
                var v,x,y,w,sx,h,ox;
                var cur = s2;
                ox = Math.floor(this._getTickWidth()/2);
                while(cur && cur != e2.next) {
                    v = cur.volume;
                    x = this._getX(cur.index - s.index);
                    y = this._getY(v)|0;
                    w = this._getTickWidth(cur.index- s.index);
                    if(w <= 0) {
                        cur = cur.next;
                        continue;
                    }
                    
                    if(!cur.prev || cur.close == cur.prev.close) {
                        c.strokeStyle = c.fillStyle = this._parent._style.volumeColor;
                    } else if(cur.close > cur.prev.close) {
                        c.strokeStyle = c.fillStyle = this._parent._style.volumeUpColor;
                    } else {
                        c.fillStyle = c.fillStyle = this._parent._style.volumeDownColor;
                    }
                    
                    if((x|0) <= sx+1) {
                        cur = cur.next;
                        continue;
                    }
                     
                    h = base - y;
                    if(w > 1) {
                        w -= 1;
                    }
                    sx = ((x-ox)|0)+0.5;
                                        
                    c.beginPath();
                    if(h >= 2 && w >= 2) {
                        c.rect(sx|0,y,w|0,h+1);
                        c.fill();
                    } else {
                        if(h==1) {
                            y=base;
                        }
                        c.moveTo(sx,y);
                        c.lineTo(sx+((w-1)|0),base);
                        c.stroke();
                    } 
                    
                    cur = cur.next;
                }
            } else if(this._content == 1) {
                //===============================================================
                // benchmark mode
                //===============================================================
                if(this._benchmarks.length > 0) {
                    c.lineWidth     = this._parent._style.lineWidth;
                    c.strokeStyle   = this._parent._style.lineColor;
                    c.beginPath();
                    
                    var ref = null;
                    var cur = s2;
                    while(cur && cur != e2.next) {
                        x = this._getX(cur.index - s.index);
                        y = this._getY((cur.close/this._source.relative)*100);
                        
                        if((!cur.prev || cur.prev.timestamp < this._parent._referenceSpan) && cur.timestamp >= this._parent._referenceSpan) {
                            ref = cur;    
                        }
                        
                        if(cur == s2) {
                            c.moveTo(x,y);
                        } else {
                            c.lineTo(x,y);
                        }
                        
                        cur = cur.next;
                    }
                     
                    c.stroke();    
                    
                    if(ref) {
                        var endX = this._getX(ref.index - s.index);
                        c.strokeStyle = this._parent._style.referenceLineColor;
                        c.beginPath();
                        c.moveTo(endX,1.5);
                        c.lineTo(endX,((this._size-1)|0)+0.5);
                        c.stroke();
                    }
                }
                //===============================================================
                // chartType: mountain
                //===============================================================
                else if(this._type == 'mountain') {
                    var endX = this._parent._size[0]-1;
                    var beginX = this._getX(0-(s.index-s2.index));
                    
                    if(endVisible) {
                        endX = this._getX(e.index-s.index);
                    }
                              
                    c.fillStyle = this._gradient;
                    c.strokeStyle = this._parent._style.lineColor;
                    c.lineWidth = this._parent._style.lineWidth;
                    
                    for(var i=0;i<2-isMoving;i++) {
                        c.beginPath();
                    
                        // bottom end
                        y = this._size+1;
                        c.moveTo(endX,y);
                        // bottom begin
                        if(s2.prev) {
                            y = this._getY(s2.close);
                            c.lineTo(beginX-2,this._size+1); 
                            c.lineTo(beginX-2,y); 
                        } else {
                            c.lineTo(beginX,this._size+1);
                        }
                        
                        // line
                        var cur = s2;
                        while(cur && cur != e2.next) {
                            x = this._getX(cur.index - s.index);
                            y = this._getY(cur.close);
                            c.lineTo(x,y);
                            
                            cur = cur.next;
                        } 
                        if(!i && !isMoving) {
                            c.fill();
                        } else {
                            c.stroke();
                        }
                    }
                //===============================================================
                // chartType: line
                //===============================================================
                } else if(this._type == 'line') {
                    c.lineWidth = this._parent._style.lineWidth;
                    c.strokeStyle = this._parent._style.lineColor;
                    c.beginPath();
                    
                    var cur = s2;
                    while(cur && cur != e2.next) {
                        x = this._getX(cur.index - s.index);
                        y = this._getY(cur.close);
                        
                        if(cur == s2) {
                            c.moveTo(x,y);
                        } else {
                            c.lineTo(x,y);
                        }
                        
                        cur = cur.next;
                    }
                    
                    c.stroke();
                //===============================================================
                // chartType: candle
                //===============================================================
                } else if(this._type == 'candle'){
                    var y2 = 0;
                    c.strokeStyle = this._parent._style.candleColor;
                    
                    var cur = s2;
                    while(cur && cur != e2.next) {
                        x = this._getX(cur.index - s.index);
                        
                        //stick
                        y = this._getY(cur.low);
                        y2 = this._getY(cur.high);
                        c.beginPath();
                        c.moveTo(x,y);
                        c.lineTo(x,y2);
                        c.stroke();
                        
                        // candle
                        y   = this._getY(cur.open);
                        y2  = this._getY(cur.close);
                        c.strokeRect(x-2,Math.min(y,y2),5,Math.abs(y2-y));
                        if(y < y2) {
                            c.fillStyle = this._parent._style.candleDownColor;
                        } else {
                            c.fillStyle = this._parent._style.candleUpColor;
                        }
                        c.fillRect(x-2,Math.min(y,y2),5,Math.abs(y2-y));
                        
                        cur = cur.next;
                    }
                //===============================================================
                // chartType: ohlc
                //===============================================================
                } else {
                    var y2 = 0;
                    c.strokeStyle = this._parent._style.candleColor;
                    
                    var cur = s2;
                    while(cur && cur != e2.next) {
                        x = this._getX(cur.index - s.index);
                        
                        // open & close
                        y   = this._getY(cur.open);
                        y2  = this._getY(cur.close);
                        if(y < y2) {
                            c.strokeStyle = this._parent._style.candleDownColor;
                        } else {
                            c.strokeStyle = this._parent._style.candleUpColor;
                        }

                        c.beginPath();
                        c.moveTo(x-5,y);
                        c.lineTo(x,y);
                        c.stroke();
                        c.beginPath();
                        c.moveTo(x,y2);
                        c.lineTo(x+5,y2);
                        c.stroke();
                                        
                        // stick
                        y   = this._getY(cur.low);
                        y2  = this._getY(cur.high);
                        c.beginPath();
                        c.moveTo(x,y);
                        c.lineTo(x,y2);
                        c.stroke();
                        
                        cur = cur.next;
                    }
                }
                
                if((this._type == 'line' || this._type == 'mountain') && endVisible) {
                    if(this._flash < 0) {
                        c.strokeStyle = c.fillStyle = this._parent._style.lineFlashDownColor;
                    } else if(this._flash > 0) {
                        c.strokeStyle = c.fillStyle = this._parent._style.lineFlashUpColor;
                    } else {
                        c.fillStyle = this._parent._style.lineColor;
                    }
                    c.beginPath();
                    c.arc(x,y, 4, 0,6.28,false);
                    c.fill();
                    
                    if(this._flash != 0) {
                        y-=this._flash*4;
                        for(var i=0;i<this._flashTimer%4;i++) {
                            c.beginPath();
                            c.moveTo(x-5,y-this._flash*(i*3));
                            c.lineTo(x,y-this._flash*(5+i*3));
                            c.lineTo(x+5,y-this._flash*(i*3));
                            c.stroke();
                        }
                    }
                }
            }
        }
        
        //c.restore();   
        
        y = ((this._size-1)|0)+0.5;
        x = ((this._parent._size[0]-1)|0)+0.5;

        //===============================================================
        // draw labels y
        //===============================================================
        if(valueRange>0 && this._showYLabels) {
           
            var dir = 1;
            if(this._parent._style.yAxisInside) {
                dir = -1;
                c.textAlign = 'right';
            } else {
                c.textAlign = 'left';
            }
            c.textBaseline = 'middle';
            
            c.strokeStyle = this._parent._style.axisColor;
            c.fillStyle = this._parent._style.yLabelColor;
            c.font = this._parent._style.yLabelFontStyle + ' ' + this._parent._style.yLabelFontSize + "px '" + this._parent._style.yLabelFontFamily + "'";
            v = this._yAxisBounds[0];
            var hs = this._parent._style.yLabelFontSize/2;
            for(var i=0;i<yLabels;i++) {
                y = this._getY(v);
               
                // render tick
                c.beginPath();
                c.moveTo(x,y);
                c.lineTo(x+4*dir,y);
                c.stroke();
                
                // render label
                c.fillText(chartNF(v,this._precision,',','.'),x+5*dir,y);
                v+=step;
            }  
        }
        
        //===============================================================
        // draw labels x
        //===============================================================
        if(this._showXLabels && xLabelStep > 0) {
            y = ((this._size-1)|0)+0.5;

            var h;
            c.fillStyle = this._parent._style.xLabelColor;
            c.font = this._parent._style.xLabelFontStyle + ' ' + this._parent._style.xLabelFontSize + "px '" + this._parent._style.xLabelFontFamily + "'";
            if(this._parent._style.xAxisOnTop) {
                y = 0;
                h = pa[1];
                c.textAlign = 'right';
                c.textBaseline = 'middle';
            } else {
                c.textAlign = 'center';
                c.textBaseline = 'top';
                y = ((this._size-1)|0)+0.5;
                h = 12;
            }
                    
            var lx = -100;
            
            var cur = s2;
            while(cur && cur != e2.next) {
                if(!cur.label || (cur.labelIndex%xLabelStep!=0)) {
                    cur = cur.next;
                    continue;                    
                }
                
                x = this._getX(cur.index - s.index);
                 
                // render tick
                c.strokeStyle = this._parent._style.axisColor;
                c.beginPath();
                c.moveTo(x,y);
                c.lineTo(x,y+4);
                c.stroke();
                              
                // render label
                if(x < this._parent._style.xAxisMargins[0]+6) {
                    if(x < this._parent._style.xAxisMargins[0]) {
                        cur = cur.next;
                        continue;
                    }
                    x = this._parent._style.xAxisMargins[0]+6;
                } else if(x > pa[0]-6-this._parent._style.xAxisMargins[1]) {
                    if(x > pa[0]-this._parent._style.xAxisMargins[1]) {
                        cur = cur.next;
                        continue;
                    }
                    x = pa[0]-6-this._parent._style.xAxisMargins[1];
                }
                 
                c.save();
                c.translate(x,y+5);
                c.rotate(-90 * Math.PI / 180);    
                c.fillText(cur.label, 0,0);
                c.restore();
                
                cur = cur.next;
            }
            
        }
        
        //===============================================================
        // show markers
        //===============================================================
        if(this._showMarkers == 1) {
            if(this._parent._style.yAxisInside) {
                dir = -1;
                c.textAlign = 'right';
            } else {
                c.textAlign = 'left';
            }
            c.textBaseline = 'middle';
            var padding = 2, fontSize = this._parent._style.markerFontSize, spike = 8, spikeOffset = 0, markerOffset=[0,0];
            var markerValues        = [this._lastValue,this._source.buffer.high.high,this._source.buffer.low.low];
            var markerColors        = [this._parent._style.markerColor,this._parent._style.markerColorUp,this._parent._style.markerColorDown];
            var markerFontColors    = [this._parent._style.markerFontColor,this._parent._style.markerFontColorUp,this._parent._style.markerFontColorDown];
            var markerFontSizes     = [this._parent._style.markerFontSize,this._parent._style.markerFontSizeUp,this._parent._style.markerFontSizeDown];
            var markerFontFamilies  = [this._parent._style.markerFontFamily,this._parent._style.markerFontFamilyUp,this._parent._style.markerFontFamilyDown];
            var markerFontStyles    = [this._parent._style.markerFontStyle,this._parent._style.markerFontStyleUp,this._parent._style.markerFontStyleDown];
           
            if(this._flash < 0) {
                markerColors[0]     = markerColors[2];
                markerFontColors[0] = markerFontColors[2];
            } else if(this._flash > 0) {
                markerColors[0]     = markerColors[1];
                markerFontColors[0] = markerFontColors[1];
            }
            
            var prevClose = this._source.source.getPreviousClose();
            if(this._source.buffer.interval < ESpans.DAY && prevClose != null) {
                markerValues.push(prevClose.close);
                markerColors.push(this._parent._style.markerColorClose);
                markerFontColors.push(this._parent._style.markerFontColorClose);
                markerFontSizes.push(this._parent._style.markerFontSizeClose);
                markerFontFamilies.push(this._parent._style.markerFontFamilyClose);
                markerFontStyles.push(this._parent._style.markerFontStyleClose);
            } else {
                this._markerAreas[i] = null;
            }
            for(var i=markerValues.length-1;i>=0;i--) {
                c.font = markerFontStyles[i] + ' ' + markerFontSizes[i] + 'px \''  + markerFontFamilies[i] + '\'';
                
                if(this._benchmarks.length > 0) {
                    markerValues[i] = (markerValues[i]/this._source.relative)*100;
                }
                
                var text = chartNF(markerValues[i],this._precision,',','.');
                var rw = c.measureText(text).width;
                
                x = ((this._parent._size[0]-1)|0)+0.5;
                var w =rw+padding*2, h = markerFontSizes[i]+padding*2;
                                        
                // skip invisible markers
                if(markerValues[i] < this._yAxisBounds[0]) {
                    y = ((this._size-1-(fontSize/2)-padding)|0)+0.5;
                    spikeOffset = (h/2);
                    x-=markerOffset[0];
                    markerOffset[0] += rw + spike + padding*2;
                } else if(markerValues[i] > this._yAxisBounds[1]) {
                    y = (((fontSize/2)+padding)|0)+0.5;
                    spikeOffset = -(h/2);
                    x-=markerOffset[1];
                    markerOffset[1] += rw + spike + padding*2;
                } else {
                    y = this._getY(markerValues[i]);
                    spikeOffset = 0;
                }
                
                var x1 = ((x-5-rw-padding)|0)+0.5, y1 = ((y-(markerFontSizes[i]/2)-padding)|0)+0.5;
                
                c.fillStyle = markerColors[i];
                
                if(dir == 1) {
                    c.fillRect(x+5,y-8,this._parent._size[0]-x,16);
                } else {
                    c.beginPath();
                    c.moveTo(x1-spike,y1+(h/2)+spikeOffset);
                    c.lineTo(x1,y1);
                    c.lineTo(x1+w,y1);
                    c.lineTo(x1+w,y1+h);
                    c.lineTo(x1,y1+h);
                    c.lineTo(x1-spike,y1+(h/2)+spikeOffset);
                    c.fill();
                }
                this._markerAreas[i] = [x1,y1,w,h];
                
                c.fillStyle = markerFontColors[i];
                c.fillText(text, x+5*dir,y);
            }
        }
        
       // c.restore();
        
        // draw info
        if(this._showInformation) {
            c.textAlign = 'left';
            c.textBaseline = 'bottom';
            
            c.strokeStyle = '#000';
            c.font = this._parent._style.legendLabelFontStyle + ' ' + this._parent._style.legendLabelFontSize + 'px \''  + this._parent._style.legendLabelFontFamily + '\'';
            
            var y = this._size - 2 - this._parent._style.spanLabelFontSize - 4 - this._parent._style.infoLabelFontSize;
            for(var i=0;i<this._benchmarks.length;i++) {
                if(this._parent._style.benchmarkColor.length <= i) {
                    c.fillStyle = this._parent._style.benchmarkColor[this._parent._style.benchmarkColor.length-1];
                } else {
                    c.fillStyle = this._parent._style.benchmarkColor[i];
                }
                c.fillRect(13,y-(((this._parent._style.legendLabelFontSize/2)-5)|0),10,10);
                c.strokeRect(13.5,((y-(this._parent._style.legendLabelFontSize/2)-5)|0),10,10);
                
                c.fillStyle = this._parent._style.legendLabelColor;
                c.fillText(this._benchmarks[i].source._instrumentName, 25, y);
                y -= 10;
            }
            
            c.font = this._parent._style.infoLabelFontStyle + ' ' + this._parent._style.infoLabelFontSize + 'px \''  + this._parent._style.infoLabelFontFamily + '\'';
            c.fillStyle = this._parent._style.infoLabelColor;
            c.strokeStyle = this._parent._style.infoLabelColor;
            if(this._showInformation != 2) {
                var text;
                if(this._source.source._instrumentName) {
                    if(this._informationLength === null) {
                        this._informationLength = this._source.source._instrumentName.length+1;
                        var w;
                        do {
                            this._informationLength--;
                            text = this._source.source._instrumentName.substr(0,this._informationLength) + (this._informationLength!=this._source.source._instrumentName.length?'...':'') + ': ' + chartNF(this._lastValue,this._precision,',','.');
                            w = c.measureText(text).width;
                        } while(this._parent._size[0]-10-this._parent._style.offset < w);
                    } else {  
                        text = this._source.source._instrumentName.substr(0,this._informationLength) + (this._informationLength!=this._source.source._instrumentName.length?'...':'') + ': ' + chartNF(this._lastValue,this._precision,',','.');
                    }
                } else {
                    text = this._lastValue.toString();
                }
            
                c.fillText(text, 10.5, ((this._size - 2 - this._parent._style.spanLabelFontSize - 2)|0)+0.5);
            }
            
            c.font = this._parent._style.spanLabelFontStyle + ' ' + this._parent._style.spanLabelFontSize + 'px \''  + this._parent._style.spanLabelFontFamily + '\'';
            text = makeLabel(this._source.span[0].timestamp,0,this._source.buffer.interval)+' - '+makeLabel(this._source.span[1].timestamp,0,this._source.buffer.interval) +
                    ' (' + tickName(this._source.buffer.interval) + ')';
            c.fillText(text,10.5,((this._size - 2)|0)+0.5);
        }
        
        //===============================================================
        // show cursor
        //===============================================================
        if(!this._parent._moving && this._showCursor == 1 && this._cursor) {
            c.font = this._parent._style.markerFontStyleCursorX + ' ' + this._parent._style.markerFontSizeCursorX + 'px \''  + this._parent._style.markerFontFamilyCursorX + '\'';
            
            var label = strftime(this._cursor.timestamp,this._source.source.getFormatString(this._source.buffer.interval));
            var h = this._parent._style.markerFontSizeCursorX + 8;
            var w = c.measureText(label).width + 4,wh = (w/2)|0;
            var spike = 8;
            var value = this._content==3?this._cursor.volume:this._cursor.close;
            if(this._benchmarks.length > 0) {
                value = (this._cursor.close/this._source.relative)*100;
            }
                
            x = this._getX(this._cursor.index - s.index);
            y = this._getY(value);
            
            c.textBaseline  = 'middle';
            c.strokeStyle = this._parent._style.cursorColor;
            
            // draw line
            c.beginPath();
            c.moveTo(x,0.5);
            c.lineTo(x,((this._size-1)|0)+0.5);
            c.stroke();
            
            // show time label
            c.fillStyle = this._parent._style.markerColorCursorX;
            
            c.textAlign = 'center';
            c.beginPath();
            c.moveTo(x,0.5+3);
            c.lineTo(x-(spike/2),3.5+spike);
            c.lineTo(x-wh,3.5+spike);
            c.lineTo(x-wh,3.5+spike+h);
            c.lineTo(x+wh,3.5+spike+h);
            c.lineTo(x+wh,3.5+spike);
            c.lineTo(x+(spike/2),3.5+spike);
            c.lineTo(x,3.5);
            c.fill();
            
            c.fillStyle = this._parent._style.markerFontColorCursorX;
            c.fillText(label,x,3+spike+(h/2));
            
            // show value label
            if(this._type == 'line' || this._type == 'mountain') {
                c.fillStyle = this._parent._style.lineColor;
                c.beginPath();
                c.arc(x,y, 4, 0,6.28,false);
                c.fill();
            }
                    
            c.font = this._parent._style.markerFontStyleCursorY + ' ' + this._parent._style.markerFontSizeCursorY + 'px \''  + this._parent._style.markerFontFamilyCursorY + '\'';
            
            var text = chartNF(value,this._precision,',','.');
            h = this._parent._style.markerFontSizeCursorY + 8; 
            w = c.measureText(text).width + 4;
            y-=h/2;
             
            c.fillStyle = this._parent._style.markerColorCursorY;
            c.beginPath();
            if(x < this._parent._size[0]/2) {
                x+=14;
            
                c.moveTo(x-spike,y+(h/2));
                c.lineTo(x,y);
                c.lineTo(x+w,y);
                c.lineTo(x+w,y+h);
                c.lineTo(x,y+h);
                c.closePath();
            } else {
                x -= w+14;
                
                c.moveTo(x+w+spike,y+(h/2));
                c.lineTo(x+w,y);
                c.lineTo(x,y);
                c.lineTo(x,y+h);
                c.lineTo(x+w,y+h);
                c.closePath();
            }
            
            c.fill();
            
            c.textAlign     = 'left';                
            c.fillStyle = this._parent._style.markerFontColorCursorY;
            c.fillText(text, x+2,y+(h/2));
        }
        
        //===============================================================
        // draw axes
        //===============================================================
        c.strokeStyle = this._parent._style.axisColor;
        
        y = ((this._size-1)|0)+0.5;
        x = ((this._parent._size[0]-1)|0)+0.5;
        // x
        c.beginPath();
        c.moveTo(0.5,y);
        c.lineTo(x,y);
        c.stroke();
        c.beginPath();
        c.moveTo(0.5,0.5);
        c.lineTo(x,0.5);
        c.stroke();
        // y
        c.beginPath();
        c.moveTo(x,0.5);
        c.lineTo(x,y);
        c.stroke();
        c.beginPath();
        c.moveTo(0.5,0.5);
        c.lineTo(0.5,y);
        c.stroke();
        
        c.restore();  
    };
    
    
    /**
    * handle canvas events
    */
    this._scrollId = null;
    this._scrollStart = [0,0];
    
    this.handleEvent = function(e,button,layerX,layerY) {
        switch(e.type) {
         case 'mouseup':
            if(button == this._scrollId) {
                this._scrollId = null;
                this._parent._toggleMove(false);
            }
            break;  
        }
       
        if(!this._parent._isLoading()) {
            switch(e.type) {
                default:
                    return;
                case 'mouseout':
                    this._scrollId = null;
                    this._cursor = null;
                    this.redraw();
                    break;
                case 'DOMMouseScroll':
                case 'mousewheel':
                     var delta = 0;
                     if (e.wheelDelta) { 
                        delta = e.wheelDelta/120;
                        if (window.opera) {
                            delta = -delta;
                        }
                    } else if (e.detail) { 
                        delta = -e.detail/2;
                    }
                    delta = delta < 0 ? -1 : 1;
                    var l = delta, r = -delta;
                    var p = this._getPlotarea();
                    var tw = this._getTickWidth();
                    var s = Math.ceil((p[0]/tw)*0.1);
                    l *= s;
                    r *= s;
                          
                    if(this._source.offsetR >= (this._parent._style.offset/tw) && this.isEndVisible()) {
                        r = 0;
                    }              
                    this._parent.zoom(l,r);
                    
                    break;
                case 'mouseup':
                    this._selectedHandle = null;
                    this._scrollId = null;
                    this._parent._toggleMove(false);
                    break;
                case 'mousedown':
                    if(button == 1) {
                        if(this._selectedTool) {
                            this._selectedTool = null;
                            this._selectedHandle = this._addTool(layerX,layerY-this._position);
                        } else {
                            // check for markers
                            var area = null;
                            for(var i=0;i<this._markerAreas.length;i++) {
                                area = this._markerAreas[i];
                                if(area == null) {
                                    continue;
                                }
                                if(layerX >= area[0] && layerX <= area[0]+area[2] && layerY-this._position >= area[1] && layerY-this._position <= area[1]+area[3]) {
                                    
                                    var value = null;
                                    switch(i) {
                                        // current value
                                        case 0: value = this._source.buffer.end; break;
                                        // high
                                        case 1: value = this._source.buffer.high; break;
                                        // low
                                        case 2: value = this._source.buffer.low; break;
                                        // prev close
                                        case 3: value = this._source.source.getPreviousClose(this._source.buffer.interval); break;
                                    }
                                    if(value) {
                                        this._parent._focus(value);
                                    }
                                    
                                    return;
                                }
                            }
                            
                            // check for handles
                            var sh = null,h = null, x, y;
                            for(var i=0;i<this._tools.length;i++) {
                                for(var j=0;j<this._tools[i]._handles.length;j++) {
                                    h = this._tools[i]._handles[j];
                                    x = this._getX(h.index-this._source.span[0].index);
                                    y = this._getY(h.value);
                                    if(Math.abs(layerX-x)<=5 && Math.abs(layerY-this._position-y)<=5) {
                                        sh = h;
                                        break;    
                                    }
                                }
                                if(sh) {
                                    break;
                                }
                            }
                            
                            if(sh) {
                                this._selectedHandle = sh;
                            } else {
                                this._scrollId = button;
                                this._scrollStart[0]= layerX;
                                this._scrollStart[1]= layerY;
                                this._parent._toggleMove(true);
                            }
                        }
                    }
                    break;
               case 'mousemove':
                    // store cursor position
                    var redraw = false;
                    
                    // update cursor
                    if(this._showCursor) {
                        var newCursor = this._getChartValue(layerX);
                        if(newCursor != this._cursor) {
                            this._cursor = newCursor;
                            this._cursorX = layerX;
                            redraw = true;
                        }
                    }
                    
                    if(this._selectedHandle) {
                        this._moveHandle(this._selectedHandle,layerX,layerY-this._position);
                        redraw = true;
                    } else if(button == this._scrollId) {
                        var x = layerX - this._scrollStart[0];
                        var y = layerY - this._scrollStart[1];

                        this._scrollStart[0] = layerX;
                        this._scrollStart[1] = layerY;
                    
                        var t = this._getTickWidth();
                        
                        this._parent.scroll(-(x/t));
                    }
                    if(redraw) {
                        this.redraw();
                    }
                    break;
            }
        }
    };
    
    // constructor
    this._updateYAxisBounds();
    this._updateStyle();
};