var debug = false;

var ScrollArrowType = {
    ARROW_TOP       : 0,
    ARROW_RIGHT     : 1,
    ARROW_BOTTOM    : 2,
    ARROW_LEFT      : 3
};
var ScrollOverType = {
    NONE    : 0,
    OVER    : 1,
    ACTIVE  : 2
};

function CArrowDrawer()
{
    // размер квадратика в пикселах
    this.Size = 16;

    // просто рисовать - неправильно. рисуется с антиалиазингом - и получается некрасиво
    this.ColorGradStart = {R: 69, G: 70, B: 71};
    this.ColorGradEnd = {R: 116, G: 117, B: 118};

    this.ColorBorder = "#929292";

    this.ColorBackNone = "#FCFCFC";
    this.ColorBackOver = "#EDEDED";
    this.ColorBackActive = "#CCCCCC";

    // вот такие мега настройки для кастомизации)
    this.IsDrawBorderInNoneMode = false;

    // имя - направление стрелки
    this.ImageLeft  = null;
    this.ImageTop   = null;
    this.ImageRight = null;
    this.ImageBottom = null;

    this.InitSize = function(size)
    {
        if (size == this.Size && null != this.ImageLeft)
            return;

        this.Size = size;

        this.ImageLeft = document.createElement('canvas');
        this.ImageTop = document.createElement('canvas');
        this.ImageRight = document.createElement('canvas');
        this.ImageBottom = document.createElement('canvas');

        var ctx_l = this.ImageLeft.getContext('2d');
        var ctx_t = this.ImageTop.getContext('2d');
        var ctx_r = this.ImageRight.getContext('2d');
        var ctx_b = this.ImageBottom.getContext('2d');

        var len = 5;
        if (this.Size < 5)
            return;
        else (this.Size > 12)
            len = this.Size - 8;

        // теперь делаем нечетную длину
        if (0 == (len & 1))
            len += 1;

        var countPart = (len + 1) >> 1;
        var plusColor = (this.ColorGradEnd.R - this.ColorGradStart.R) / countPart;

        var _data = ctx_l.createImageData(this.Size, this.Size);
        var px = _data.data;

        var _x = (this.Size - len) >> 1;
        var _y = this.Size - ((this.Size - countPart) >> 1) - 1;

        var _radx = _x + (len >> 1) + 0.5;
        var _rady = _y - (countPart >> 1) - 0.5;

        var r = this.ColorGradStart.R;
        var g = this.ColorGradStart.G;
        var b = this.ColorGradStart.B;
        while (len > 0)
        {
            var ind = 4 * this.Size * _y + 4 * _x;
            for (var i = 0; i < len; i++)
            {
                px[ind++] = r;
                px[ind++] = g;
                px[ind++] = b;
                px[ind++] = 255;
            }

            r = (r + countPart) >> 0;
            g = (g + countPart) >> 0;
            b = (b + countPart) >> 0;

            _x += 1;
            _y -= 1;
            len -= 2;
        }

        ctx_t.putImageData(_data, 0, 0);

        ctx_l.translate(_radx-1, _rady);
        ctx_l.rotate(-Math.PI / 2);
        ctx_l.translate(-_radx, -_rady);
        ctx_l.drawImage(this.ImageTop, 0, 0);

        ctx_r.translate(_radx+2, _rady);
        ctx_r.rotate(Math.PI / 2);
        ctx_r.translate(-_radx, -_rady);
        ctx_r.drawImage(this.ImageTop, 0, 0);

        ctx_b.translate(_radx, _rady+2);
        ctx_b.rotate(Math.PI);
        ctx_b.translate(-_radx, -_rady);
        ctx_b.drawImage(this.ImageTop, 0, 0);
    }

    this.drawArrow = function(type, mode, ctx, w, h)
    {
        var img = this.ImageTop;
        var x = 0;
        var y = 0;
        var is_vertical = true;
        switch (type)
        {
            case ScrollArrowType.ARROW_LEFT:
            {
                is_vertical = false;
                img = this.ImageLeft;
                break;
            }
            case ScrollArrowType.ARROW_RIGHT:
            {
                is_vertical = false;
                x = w - this.Size;
                img = this.ImageRight;
                break;
            }
            case ScrollArrowType.ARROW_BOTTOM:
            {
                y = h - this.Size;
                img = this.ImageBottom;
                break;
            }
            default:
                break;
        }

        ctx.lineWidth = 1;
        var strokeW = is_vertical ? this.Size - 2 : this.Size - 1;
        var strokeH = is_vertical ? this.Size - 1: this.Size - 2;

        ctx.fillStyle = this.ColorBackNone;
        ctx.fillRect(x, y, this.Size, this.Size);
        ctx.beginPath();

        switch (mode)
        {
            case ScrollOverType.NONE:
            {
                ctx.drawImage(img, x, y);
                if (this.IsDrawBorderInNoneMode)
                {
                    ctx.strokeStyle = this.ColorBorder;
                    ctx.rect(x + 0.5, y + 0.5, strokeW, strokeH);
                    ctx.stroke();
                }
                break;
            }
            case ScrollOverType.OVER:
            {
                ctx.fillStyle = this.ColorBackOver;
                ctx.rect(x + 0.5, y + 0.5, strokeW, strokeH);
                ctx.fill();
                ctx.drawImage(img, x, y);
                ctx.strokeStyle = this.ColorBorder;
                ctx.rect(x + 0.5, y + 0.5, strokeW, strokeH);
                ctx.stroke();
                break;
            }
            case ScrollOverType.ACTIVE:
            {
                ctx.fillStyle = this.ColorBackActive;
                ctx.rect(x + 0.5, y + 0.5, strokeW, strokeH);
                ctx.fill();
                ctx.drawImage(img, x, y);
                ctx.strokeStyle = this.ColorBorder;
                ctx.rect(x + 0.5, y + 0.5, strokeW, strokeH);
                ctx.stroke();
                break;
            }
            default:
                break;
        }

        ctx.beginPath();
    }
}


/**
 * @constructor
 */
function ScrollObject(elemID,settings,dbg){
	if (dbg)
		debug = dbg;
	var that = this;
	this.that = this;

	var extendSettings = function(settings1,settings2){
		var _st = {};
		if (settings1 == null || settings1 == undefined)
			return settings2;
		else for (var _item in settings1){
			if (typeof settings1[_item] === "object")
				_st[_item] = extendSettings(_st,settings1[_item]);
			else
				_st[_item] = settings1[_item];
		}
		for (var _item in settings2){
			if (!_st.hasOwnProperty(_item)){
				if (typeof settings2[_item] === "object")
					_st[_item] = extendSettings(_st,settings2[_item]);
				else
					_st[_item] = settings2[_item];
			}
		}
		return _st;
	}

	var scrollSettings = {
		showArrows				: false,
		screenW					: -1,
		screenH					: -1,
		scrollerMinHeight 		: 20,
		scrollerMaxHeight 		: 99999,
		scrollerMinWidth 		: 20,
		scrollerMaxWidth 		: 99999,
		initialDelay			: 300,
		arrowRepeatFreq			: 50,
		trackClickRepeatFreq	: 70,
		scrollPagePercent		: 1./8,
		arrowDim 				: 16,
		scrollerColor 			: "#D3D3D3",
		scrollBackgroundColor 	: "#fff",
		vscrollStep			: 10,
		hscrollStep			: 10,
		wheelScrollLines: 1
	};

	this.settings = extendSettings(settings,scrollSettings);

	this.ArrowDrawer = new CArrowDrawer();
	this.ArrowDrawer.ColorBackNone = this.settings.scrollBackgroundColor;

	this.mouseUp = false;
	this.mouseDown = false;

	this.scrollerMouseDown = false;
	this.scrollerMouseUp = false;

	this.moveble = false;
	this.lock = false;

	this.StartMousePosition = {x:0,y:0};
	this.EndMousePosition = {x:0,y:0};

	this.dragMinY = 0;
	this.dragMaxY = 0;

	this.scrollVCurrentY = 0;
	this.scrollHCurrentX = 0;
	this.arrowPosition = 0;

	this.verticalTrackHeight = 0;
	this.horizontalTrackWidth = 0;

	this.paneHeight = 0;
	this.paneWidth = 0;

	this.maxScrollY = 2000;
	this.maxScrollX = 2000;

	this.scrollCoeff = 0;

	this.scroller = {x:0,y:1,h:0,w:0};

	this.canvas = null;
	this.context = null;

	this.eventListeners = [];

	this.nonePointer = false;

	this._init(elemID);
}

ScrollObject.prototype = {

	_init: function(elemID){
		if (!elemID) return false;
		var holder = document.getElementById(elemID);

		if (holder.getElementsByTagName('canvas').length == 0)
			this.canvas = holder.appendChild(document.createElement("CANVAS"));
		else {
			this.canvas = holder.children[1];
		}
		this.canvas.that = this;
		this.canvas.style.zIndex = 100;
		this.canvas.style.position = "absolute";
		this.canvas.style.top = "0px";
		if(navigator.userAgent.toLowerCase().indexOf("webkit") != -1)
			this.canvas.style.webkitUserSelect = "none";
		this.context = this.canvas.getContext('2d');
		if (this.settings.showArrows)
			this.arrowPosition = this.settings.arrowDim;

		this._setDimension(holder.clientHeight,holder.clientWidth );
		this.maxScrollY = holder.firstElementChild.clientHeight - this.settings.screenH > 0 ? holder.firstElementChild.clientHeight - this.settings.screenH : 0;
		this.maxScrollX = holder.firstElementChild.clientWidth - this.settings.screenW > 0? holder.firstElementChild.clientWidth - this.settings.screenW : 0 ;

		this.isVerticalScroll = holder.firstElementChild.clientHeight/this.canvas.height > 1
		this.isHorizontalScroll = holder.firstElementChild.clientWidth/this.canvas.width > 1
		this._setScrollerHW()

		this.paneHeight = this.canvas.height - this.arrowPosition * 2;
		this.paneWidth = this.canvas.width - this.arrowPosition * 2;

		this.RecalcScroller();

		this.canvas.onmousemove = this.evt_mousemove;
		this.canvas.onmouseout = this.evt_mouseout;
		this.canvas.onmouseup = this.evt_mouseup;
		this.canvas.onmousedown = this.evt_mousedown;
		this.canvas.onmousewheel = this.evt_mousewheel;

		if (this.canvas.addEventListener)
			this.canvas.addEventListener('DOMMouseScroll', this.evt_mousewheel,false);
		this._draw();
		this._drawArrow();

		return true;
	},
	getMousePosition:function(evt){
		// get canvas position
		var obj = this.canvas;
		var top = 0;
		var left = 0;
		while (obj && obj.tagName != 'BODY') {
			top += obj.offsetTop;
			left += obj.offsetLeft;
			obj = obj.offsetParent;
		}

		// return relative mouse position
		var mouseX = evt.clientX - left + window.pageXOffset;
		var mouseY = evt.clientY - top + window.pageYOffset;
		return {
			x: mouseX,
			y: mouseY
		};
	},
	RecalcScroller : function(startpos){
		if (this.isVerticalScroll){
			if (this.settings.showArrows){
				this.verticalTrackHeight = this.canvas.height - this.arrowPosition * 2;
				this.scroller.y = this.arrowPosition + 1;
			}
			else{
				this.verticalTrackHeight = this.canvas.height;
				this.scroller.y = 1;
			}
			var percentInViewV;

			percentInViewV = (this.maxScrollY + this.paneHeight )/ this.paneHeight;
			this.scroller.h = Math.ceil( 1 / percentInViewV * this.verticalTrackHeight );

			if (this.scroller.h < this.settings.scrollerMinHeight)
				this.scroller.h = this.settings.scrollerMinHeight
			else if (this.scroller.h > this.settings.scrollerMaxHeight)
					this.scroller.h = this.settings.scrollerMaxHeight;
			this.scrollCoeff = ( this.paneHeight == this.scroller.h ? 1 : this.maxScrollY / ( this.paneHeight - this.scroller.h ) );
			if (startpos){
				this.scroller.y = startpos/this.scrollCoeff + this.arrowPosition;
			}
			this.dragMaxY = this.canvas.height - this.arrowPosition - this.scroller.h;
			this.dragMinY = this.arrowPosition;
		}

		if (this.isHorizontalScroll){
			if (this.settings.showArrows){
				this.horizontalTrackWidth = this.canvas.width - this.arrowPosition * 2;
				this.scroller.x = this.arrowPosition + 1;
			}
			else{
				this.horizontalTrackWidth = this.canvas.width;
				this.scroller.x = 1;
			}
			var percentInViewH;
			percentInViewH = ( this.maxScrollX + this.paneWidth )/ this.paneWidth;
			this.scroller.w = Math.ceil( 1 / percentInViewH * this.horizontalTrackWidth );

			if (this.scroller.w < this.settings.scrollerMinWidth)
				this.scroller.w = this.settings.scrollerMinWidth
			else if (this.scroller.w > this.settings.scrollerMaxWidth)
					this.scroller.w = this.settings.scrollerMaxWidth;
			this.scrollCoeff = ( this.paneWidth == this.scroller.w ? 1 : this.maxScrollX / ( this.paneWidth - this.scroller.w ) );
			if (typeof startpos !== "undefined"){
				this.scroller.x = startpos/this.scrollCoeff + this.arrowPosition;
			}
			this.dragMaxX = this.canvas.width - this.arrowPosition - this.scroller.w;
			this.dragMinX = this.arrowPosition;
		}
	},
	Reinit : function(settings, pos){
		this._setDimension(this.canvas.parentNode.clientHeight,this.canvas.parentNode.clientWidth );
		this.maxScrollY = this.canvas.parentNode.firstElementChild.clientHeight - (settings.screenH || this.canvas.parentNode.offsetHeight)> 0 ?
											this.canvas.parentNode.firstElementChild.clientHeight - (settings.screenH || this.canvas.parentNode.offsetHeight) :
											0;

		this.maxScrollX = this.canvas.parentNode.firstElementChild.clientWidth - (settings.screenH || this.canvas.parentNode.offsetWidth) > 0 ?
											this.canvas.parentNode.firstElementChild.clientWidth - (settings.screenH || this.canvas.parentNode.offsetWidth) :
											0 ;

		this.isVerticalScroll = this.canvas.parentNode.firstElementChild.clientHeight/this.canvas.height > 1 || this.isVerticalScroll;
		this.isHorizontalScroll = this.canvas.parentNode.firstElementChild.clientWidth/this.canvas.width > 1 || this.isHorizontalScroll;
		this._setScrollerHW();

		this.paneHeight = this.canvas.height - this.arrowPosition * 2;
		this.paneWidth = this.canvas.width - this.arrowPosition * 2;
		this.RecalcScroller();
		this.reinit = true;
		if (this.isVerticalScroll){
			pos !== undefined ? this.scrollByY(pos - this.scrollVCurrentY) : this.scrollToY(this.scrollVCurrentY);
		}

		if (this.isHorizontalScroll){
			pos !== undefined ? this.scrollByX(pos - this.scrollHCurrentX) : this.scrollToX(this.scrollHCurrentX);
		}
		this.reinit = false;
		this._draw();
		this._drawArrow();
	},
	_scrollV  : function(that,evt,pos,isTop,isBottom){
		if (!this.isVerticalScroll) {
			return;
		}

		if( that.scrollVCurrentY !== pos){
			that.scrollVCurrentY = pos;
			evt.scrollD = evt.scrollPositionY = that.scrollVCurrentY;
			evt.maxScrollY = that.maxScrollY;

			that._draw();
			that._drawArrow();
			that.handleEvents("onscrollvertical", evt);
		}
		else if(that.scrollVCurrentY === pos && pos > 0 && !this.reinit && !this.moveble && !this.lock){
			evt.pos = pos;
			that.handleEvents("onscrollVEnd",evt);
		}
	},
	_scrollH  : function(that,evt,pos,isTop,isBottom){
		if (!this.isHorizontalScroll) {
			return;
		}
		if( that.scrollHCurrentX !== pos){
			that.scrollHCurrentX = pos;
			evt.scrollD  = evt.scrollPositionX = that.scrollHCurrentX;
			evt.maxScrollX = that.maxScrollX;

			that._draw();
			that._drawArrow();
			that.handleEvents("onscrollhorizontal", evt);
		}
		else if(that.scrollHCurrentX === pos && pos > 0 && !this.reinit && !this.moveble && !this.lock){
			evt.pos = pos;
			that.handleEvents("onscrollHEnd",evt);
		}
	},
	scrollByY : function(delta){
		if (!this.isVerticalScroll) {
			return;
		}
		var destY = this.scrollVCurrentY + delta, isTop = false, isBottom = false, vend = false;

		if (destY < 0){
			destY = 0;
			isTop = true;
			isBottom = false;
		}
		else if (destY > this.maxScrollY) {
			// Новое смещение превышает maxScroll, надо вызвать ивент, спрашивающий что делать.
			// Чтобы не создавать новый, использую onscrollVEnd, он все равно больше нигде не используется
			// 50 = max число wheelScrollLine, если она больше, то будет работать неправильно
			for (var c = 50; destY > this.maxScrollY && c > 0; --c) {
				this.handleEvents("onscrollVEnd", {});
				vend = true;
			}
			if (destY > this.maxScrollY) {
				// Обработчик onscrollVEnd решил, что расширение области скрола не нужно, изменяем destY
				destY = this.maxScrollY;
			}
			isTop = false, isBottom = true;
		}

		this.scroller.y = destY / this.scrollCoeff + this.arrowPosition;
		if (this.scroller.y < this.dragMinY)
			this.scroller.y = this.dragMinY + 1;
		else if (this.scroller.y > this.dragMaxY)
			this.scroller.y = this.dragMaxY;

		if (vend) { this.moveble = true; }
		this._scrollV(this, {}, destY,isTop,isBottom);
		if (vend) { this.moveble = false; }
	},
	scrollToY : function(destY){
		if (!this.isVerticalScroll) {
			return;
		}

		this.scroller.y = destY / this.scrollCoeff + this.arrowPosition;
		if (this.scroller.y < this.dragMinY)
			this.scroller.y = this.dragMinY + 1;
		else if (this.scroller.y > this.dragMaxY)
			this.scroller.y = this.dragMaxY;

		this._scrollV(this, {}, destY,false,false);
	},
	scrollByX : function(delta){
		if (!this.isHorizontalScroll) {
			return;
		}
		var destX = this.scrollHCurrentX + delta, isTop = false, isBottom = false, hend = false;

		if (destX < 0){
			destX = 0;
			isTop = true;
			isBottom = false;
		}
		else if (destX > this.maxScrollX){
			for (var c = 50; destX > this.maxScrollX && c > 0; --c) {
				this.handleEvents("onscrollHEnd", {});
				hend = true;
			}
			if (destX > this.maxScrollX) {
				destX = this.maxScrollX;
			}
			isTop = false, isBottom = true;
		}

		this.scroller.x = destX / this.scrollCoeff + this.arrowPosition;
		if (this.scroller.x < this.dragMinX)
			this.scroller.x = this.dragMinX + 1;
		else if (this.scroller.x > this.dragMaxX)
			this.scroller.x = this.dragMaxX;

		if (hend) { this.moveble = true; }
		this._scrollH(this, {}, destX,isTop,isBottom);
		if (hend) { this.moveble = true; }
	},
	scrollToX : function(destX){
		if (!this.isHorizontalScroll) {
			return;
		}

		this.scroller.x = destX / this.scrollCoeff + this.arrowPosition;
		if (this.scroller.x < this.dragMinX)
			this.scroller.x = this.dragMinX + 1;
		else if (this.scroller.x > this.dragMaxX)
			this.scroller.x = this.dragMaxX;

		this._scrollH(this, {}, destX,false,false);
	},
	scrollTo  : function(destX,destY){
		this.scrollToX(destX);
		this.scrollToY(destY);
	},
	scrollBy : function(deltaX, deltaY){
		this.scrollByX(deltaX);
		this.scrollByY(deltaY);
	},

	_clearContent : function(){
		this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
	},
	_draw : function(){
		this._clearContent();

		this.context.beginPath();
		this.context.rect(0, 0, this.canvas.width, this.canvas.height);
		this.context.lineWidth = 1;
		this.context.strokeStyle = this.settings.scrollBackgroundColor;
		this.context.stroke();
		this.context.fillStyle = this.settings.scrollBackgroundColor;
		this.context.fill();

		this.context.beginPath();

        if (this.isVerticalScroll)
        {
            var _y = this.scroller.y >> 0;
            if (_y < this.ArrowDrawer.Size)
                _y = this.ArrowDrawer.Size;
            var _b = (this.scroller.y + this.scroller.h) >> 0;
            if (_b > (this.canvas.height - this.ArrowDrawer.Size - 2))
                _b = this.canvas.height - this.ArrowDrawer.Size - 2;

            if (_b > _y)
                this.context.rect(0.5, _y + 0.5, this.canvas.width - 2, _b - _y + 1);
        }
        else if(this.isHorizontalScroll)
        {
            var _x = this.scroller.x >> 0;
            if (_x < this.ArrowDrawer.Size)
                _x = this.ArrowDrawer.Size;
            var _r = (this.scroller.x + this.scroller.w) >> 0;
            if (_r > (this.canvas.width - this.ArrowDrawer.Size - 2))
                _r = this.canvas.width - this.ArrowDrawer.Size - 2;

            if (_r > _x)
                this.context.rect(_x + 0.5, 0.5, _r - _x + 1, this.canvas.height - 2);
        }

		this.context.lineWidth = 1;
		this.context.strokeStyle = "#7F7F7F";
		this.context.fillStyle = this.settings.scrollerColor;
		this.context.fill();
		this.context.stroke();
	},

	_setDimension : function(h,w){
		this.canvas.height = h;
		this.canvas.width = w;
	},
	_setScrollerHW : function(){
		if (this.isVerticalScroll){
			this.scroller.x = 0;
			this.scroller.w = this.canvas.width-1;

            this.ArrowDrawer.InitSize(this.canvas.width);
		}
		else if (this.isHorizontalScroll){
			this.scroller.y = 0;
			this.scroller.h = this.canvas.height-1;

            this.ArrowDrawer.InitSize(this.canvas.height);
		}
	},
	_MouseHoverOnScroller : function(mp){
		if (	mp.x>=this.scroller.x &&
				mp.x<=this.scroller.x+this.scroller.w &&
				mp.y>=this.scroller.y &&
				mp.y<=this.scroller.y+this.scroller.h	)
			return true;
		else return false;
	},
	_MouseHoverOnArrowUp : function(mp){
		if(this.isVerticalScroll){
			if (
				mp.x>=0 &&
				mp.x<=this.canvas.width &&
				mp.y>=0 &&
				mp.y<=this.settings.arrowDim
			){
			return true;
			}
			else return false;
		}
		if (this.isHorizontalScroll){
			if (
				mp.x>=0 &&
				mp.x<=this.settings.arrowDim &&
				mp.y>=0 &&
				mp.y<=this.canvas.height
			){
			return true;
			}
			else return false;
		}
	},
	_MouseHoverOnArrowDown : function(mp){
		if(this.isVerticalScroll){
			if (
				mp.x>=0 &&
				mp.x<=this.canvas.width &&
				mp.y>=this.canvas.height - this.settings.arrowDim &&
				mp.y<=this.canvas.height
			){
			return true;
			}
			else return false;
		}
		if (this.isHorizontalScroll){
			if (
				mp.x>=this.canvas.width - this.settings.arrowDim &&
				mp.x<=this.canvas.width &&
				mp.y>=0 &&
				mp.y<=this.canvas.height
			){
			return true;
			}
			else return false;
		}
	},

	_drawArrow : function(type){
		if(this.settings.showArrows)
        {
            var w = this.canvas.width;
            var h = this.canvas.height;
			if(this.isVerticalScroll)
            {
				switch(type)
                {
				case 0://upArrow mouse hover, downArrow stable
                    this.ArrowDrawer.drawArrow(ScrollArrowType.ARROW_TOP, ScrollOverType.OVER, this.context, w, h);
                    this.ArrowDrawer.drawArrow(ScrollArrowType.ARROW_BOTTOM, ScrollOverType.NONE, this.context, w, h);
					break;
				case 1://upArrow mouse down, downArrow stable
                    this.ArrowDrawer.drawArrow(ScrollArrowType.ARROW_TOP, ScrollOverType.ACTIVE, this.context, w, h);
                    this.ArrowDrawer.drawArrow(ScrollArrowType.ARROW_BOTTOM, ScrollOverType.NONE, this.context, w, h);
					break;
				case 2://upArrow stable, downArrow mouse hover
                    this.ArrowDrawer.drawArrow(ScrollArrowType.ARROW_TOP, ScrollOverType.NONE, this.context, w, h);
                    this.ArrowDrawer.drawArrow(ScrollArrowType.ARROW_BOTTOM, ScrollOverType.OVER, this.context, w, h);
					break;
				case 3://upArrow stable, downArrow mouse down
					this.ArrowDrawer.drawArrow(ScrollArrowType.ARROW_TOP, ScrollOverType.NONE, this.context, w, h);
                    this.ArrowDrawer.drawArrow(ScrollArrowType.ARROW_BOTTOM, ScrollOverType.ACTIVE, this.context, w, h);
					break;
				default ://upArrow stable, downArrow stable
					this.ArrowDrawer.drawArrow(ScrollArrowType.ARROW_TOP, ScrollOverType.NONE, this.context, w, h);
                    this.ArrowDrawer.drawArrow(ScrollArrowType.ARROW_BOTTOM, ScrollOverType.NONE, this.context, w, h);
					break;
				}
			}
			if (this.isHorizontalScroll)
            {
				switch(type)
                {
				case 0://leftArrow mouse hover, rightArrow stable
                    this.ArrowDrawer.drawArrow(ScrollArrowType.ARROW_LEFT, ScrollOverType.OVER, this.context, w, h);
                    this.ArrowDrawer.drawArrow(ScrollArrowType.ARROW_RIGHT, ScrollOverType.NONE, this.context, w, h);
					break;
				case 1://leftArrow mouse down, rightArrow stable
					this.ArrowDrawer.drawArrow(ScrollArrowType.ARROW_LEFT, ScrollOverType.ACTIVE, this.context, w, h);
                    this.ArrowDrawer.drawArrow(ScrollArrowType.ARROW_RIGHT, ScrollOverType.NONE, this.context, w, h);
					break;
				case 2://leftArrow stable, rightArrow mouse hover
                    this.ArrowDrawer.drawArrow(ScrollArrowType.ARROW_LEFT, ScrollOverType.NONE, this.context, w, h);
                    this.ArrowDrawer.drawArrow(ScrollArrowType.ARROW_RIGHT, ScrollOverType.OVER, this.context, w, h);
					break;
				case 3://leftArrow stable, rightArrow mouse down
					this.ArrowDrawer.drawArrow(ScrollArrowType.ARROW_LEFT, ScrollOverType.NONE, this.context, w, h);
                    this.ArrowDrawer.drawArrow(ScrollArrowType.ARROW_RIGHT, ScrollOverType.ACTIVE, this.context, w, h);
					break;
				default ://leftArrow stable, rightArrow stable
					this.ArrowDrawer.drawArrow(ScrollArrowType.ARROW_LEFT, ScrollOverType.NONE, this.context, w, h);
                    this.ArrowDrawer.drawArrow(ScrollArrowType.ARROW_RIGHT, ScrollOverType.NONE, this.context, w, h);
					break;
				}
			}
		}

	},
	_arrowDownMouseDown : function(){
		var that = this,scrollTimeout,isFirst = true,
		doScroll = function()
		{
			if (that.isVerticalScroll)
				that.scrollByY(that.settings.vscrollStep);
			else if(that.isHorizontalScroll)
				that.scrollByX(that.settings.hscrollStep);
			that._drawArrow(3);
			scrollTimeout = setTimeout(doScroll, isFirst ? that.settings.initialDelay : that.settings.arrowRepeatFreq );
			isFirst = false;
		};
		doScroll();
		this.bind( "mouseup mouseout",function(){
			scrollTimeout && clearTimeout(scrollTimeout);
			scrollTimeout = null;
		});
	},
	_arrowUpMouseDown : function(){
		var that = this,scrollTimeout,isFirst = true,
		doScroll = function()
		{
			if (that.isVerticalScroll)
				that.scrollByY(-that.settings.vscrollStep);
			else if(that.isHorizontalScroll)
				that.scrollByX(-that.settings.hscrollStep);
			that._drawArrow(1);
			scrollTimeout = setTimeout(doScroll, isFirst ? that.settings.initialDelay : that.settings.arrowRepeatFreq );
			isFirst = false;
		};
		doScroll();
		this.bind( "mouseup mouseout",function(){
			scrollTimeout && clearTimeout(scrollTimeout);
			scrollTimeout = null;
		})
	},


	getMaxScrolledY : function(){
		return this.maxScrollY;
	},
	getMaxScrolledX : function(){
		return this.maxScrollX;
	},
	/************************************************************************/
	/*events*/
	evt_mousemove: function(e){
		var evt = e||windows.event;
		var mousePos = this.that.getMousePosition(evt);
		this.that.EndMousePosition.x = mousePos.x;
		this.that.EndMousePosition.y = mousePos.y;
		var downHover = this.that._MouseHoverOnArrowDown(mousePos);
		var upHover = this.that._MouseHoverOnArrowUp(mousePos);
		var scrollerHover = this.that._MouseHoverOnScroller(mousePos);
		if ( scrollerHover ){
			this.that.canvas.style.cursor = "pointer";
		}
		else if ( this.that.settings.showArrows && (downHover || upHover) ) {
			this.that.canvas.style.cursor = "pointer";
			if (upHover && this.that.settings.showArrows && !this.that.mouseDownArrow && !this.that.nonePointer){
				this.that._drawArrow(0);
				this.that.nonePointer = true;
			}
			if (downHover && this.that.settings.showArrows && !this.that.mouseDownArrow && !this.that.nonePointer){
				this.that._drawArrow(2);
				this.that.nonePointer = true;
			}
		}
		else {
			this.that.canvas.style.cursor = "default";
			if (this.that.nonePointer){
				this.that._drawArrow();
				this.that.nonePointer = false;
			}
		}
		if(this.that.mouseDown && this.that.scrollerMouseDown)
			this.that.moveble = true;
		else
			this.that.moveble = false;

		if (this.that.isVerticalScroll){
			if (this.that.moveble && this.that.scrollerMouseDown){
				var isTop = false, isBottom = false;
				var _dlt = this.that.EndMousePosition.y - this.that.StartMousePosition.y;
				if (this.that.EndMousePosition.y == this.that.StartMousePosition.y)
					return;
				else if (this.that.EndMousePosition.y < this.that.arrowPosition){
					this.that.EndMousePosition.y = this.that.arrowPosition;
					_dlt = 0;
					this.that.scroller.y = this.that.arrowPosition;
				}
				else if (this.that.EndMousePosition.y > this.that.canvas.height - this.that.arrowPosition){
					this.that.EndMousePosition.y = this.that.canvas.height - this.that.arrowPosition;
					_dlt = 0;
					this.that.scroller.y = this.that.canvas.height - this.that.arrowPosition - this.that.scroller.h;
				}
				else {
					if( (_dlt > 0 && this.that.scroller.y + _dlt + this.that.scroller.h <= this.that.canvas.height - this.that.arrowPosition ) ||
					(_dlt < 0 && this.that.scroller.y + _dlt >= this.that.arrowPosition) )
						this.that.scroller.y += _dlt;
				}

				var destY = (this.that.scroller.y - this.that.arrowPosition) * this.that.scrollCoeff

				this.that._scrollV(this.that, evt, destY, isTop, isBottom);
				this.that.moveble = false;
				this.that.StartMousePosition.x = this.that.EndMousePosition.x;
				this.that.StartMousePosition.y = this.that.EndMousePosition.y;
			}
		}
		else if (this.that.isHorizontalScroll){
			if (this.that.moveble && this.that.scrollerMouseDown) {

				var isTop = false, isBottom = false;
				var _dlt = this.that.EndMousePosition.x - this.that.StartMousePosition.x;
				if (this.that.EndMousePosition.x == this.that.StartMousePosition.x)
					return;
				else if (this.that.EndMousePosition.x < this.that.arrowPosition){
					this.that.EndMousePosition.x = this.that.arrowPosition;
					_dlt = 0;
					this.that.scroller.x = this.that.arrowPosition;
				}
				else if (this.that.EndMousePosition.x > this.that.canvas.width - this.that.arrowPosition){
					this.that.EndMousePosition.x = this.that.canvas.width - this.that.arrowPosition;
					_dlt = 0;
					this.that.scroller.x = this.that.canvas.width - this.that.arrowPosition - this.that.scroller.w;
				}
				else {
				if( (_dlt > 0 && this.that.scroller.x + _dlt + this.that.scroller.w <= this.that.canvas.width - this.that.arrowPosition ) ||
					(_dlt < 0 && this.that.scroller.x + _dlt >= this.that.arrowPosition) )
						this.that.scroller.x += _dlt;
				}
				var destX = (this.that.scroller.x - this.that.arrowPosition) * this.that.scrollCoeff

				this.that._scrollH(this.that, evt, destX, isTop, isBottom);
				this.that.moveble = false;

				this.that.StartMousePosition.x = this.that.EndMousePosition.x;
				this.that.StartMousePosition.y = this.that.EndMousePosition.y;
			}
		}
	},
	evt_mouseout : function(e){
		var evt = e||windows.event;
		if (this.that.settings.showArrows){
			this.that.mouseDownArrow = false;
			if (this.that.nonePointer){
				this.that._drawArrow();
				this.that.nonePointer = false;
			}
			this.that.handleEvents("onmouseout",evt);
		}
	},
	evt_mouseup : function(e){
		var evt = e||windows.event;
		var mousePos = this.that.getMousePosition(evt);
		if (!this.that.scrollerMouseDown){
			if (this.that.settings.showArrows && this.that._MouseHoverOnArrowDown(mousePos)){
				this.that.handleEvents("onmouseup",evt);
				this.that._drawArrow(2);
			}
			else if (this.that.settings.showArrows && this.that._MouseHoverOnArrowUp(mousePos)) {
				this.that.handleEvents("onmouseup",evt);
				this.that._drawArrow(0);
			}
		}
		else {
			this.that.mouseDown = false;
			this.that.mouseUp = true;
			this.that.scrollerMouseDown = false;
			this.that.mouseDownArrow = false;
		}

		//for unlock global mouse event
		if(this.that.onLockMouse && this.that.offLockMouse){
			this.that.offLockMouse(evt);
		}
		this.that.handleEvents("onmouseup",evt);
	},
	evt_mousedown : function(e){
		var evt = e||windows.event;
		var mousePos = this.that.getMousePosition(evt);
		var downHover = this.that._MouseHoverOnArrowDown(mousePos);
		var upHover = this.that._MouseHoverOnArrowUp(mousePos);
		if (this.that.settings.showArrows && downHover){this.that.mouseDownArrow = true;
			this.that._arrowDownMouseDown();
		}
		else if (this.that.settings.showArrows && upHover) {this.that.mouseDownArrow = true;
			this.that._arrowUpMouseDown();
		}
		else {
			this.that.mouseDown = true;
			this.that.mouseUp = false;

			if ( this.that._MouseHoverOnScroller(mousePos) ){
				this.that.scrollerMouseUp = false;
				this.that.scrollerMouseDown = true;

				if(this.that.onLockMouse){
					this.that.onLockMouse(evt);
				}
				this.that.StartMousePosition.x = mousePos.x;
				this.that.StartMousePosition.y = mousePos.y;
			}
			else {
				if (this.that.isVerticalScroll){
					var _tmp = this,
						direction = mousePos.y - this.that.scroller.y - this.that.scroller.h/2,
						step = this.that.paneHeight*this.that.settings.scrollPagePercent,
						verticalDragPosition = this.that.scroller.y,
						scrollTimeout,
						isFirst = true,
						doScroll = function()
						{
							_tmp.that.lock = true;
							if (direction > 0){
								if(_tmp.that.scroller.y + _tmp.that.scroller.h/2 + step < mousePos.y){
									_tmp.that.scrollByY(step*_tmp.that.scrollCoeff);
								}
								else {
									var _step = Math.abs(_tmp.that.scroller.y + _tmp.that.scroller.h/2 - mousePos.y);
									_tmp.that.scrollByY(_step*_tmp.that.scrollCoeff);
									cancelClick();
									return;
								}
							}
							else if(direction < 0){
								if(_tmp.that.scroller.y + _tmp.that.scroller.h/2 - step > mousePos.y){
									_tmp.that.scrollByY(-step*_tmp.that.scrollCoeff);
								}
								else {
									var _step = Math.abs(_tmp.that.scroller.y + _tmp.that.scroller.h/2 - mousePos.y);
									_tmp.that.scrollByY(-_step*_tmp.that.scrollCoeff);
									cancelClick();
									return;
								}
							}
							scrollTimeout = setTimeout(doScroll, isFirst ? _tmp.that.settings.initialDelay : _tmp.that.settings.trackClickRepeatFreq);
							isFirst = false;
						},
						cancelClick = function()
						{
							scrollTimeout && clearTimeout(scrollTimeout);
							scrollTimeout = null;
							_tmp.that.unbind( "mouseup",cancelClick);
							_tmp.that.lock = false;
						};
					doScroll();
					this.that.bind( "mouseup",cancelClick);
				}
				if(this.that.isHorizontalScroll){
					var _tmp = this,
						direction = mousePos.x - this.that.scroller.x - this.that.scroller.w/2,
						step = this.that.paneWidth*this.that.settings.scrollPagePercent,
						horizontalDragPosition = this.that.scroller.x,
						scrollTimeout,
						isFirst = true,
						doScroll = function()
						{
							_tmp.that.lock = true;
							if (direction > 0){
								if(_tmp.that.scroller.x + _tmp.that.scroller.w/2 + step < mousePos.x){
									_tmp.that.scrollByX(step*_tmp.that.scrollCoeff);
								}
								else {
									var _step = Math.abs(_tmp.that.scroller.x + _tmp.that.scroller.w/2 - mousePos.x);
									_tmp.that.scrollByX(_step*_tmp.that.scrollCoeff);
									cancelClick();
									return;
								}
							}
							else if(direction < 0){
								if(_tmp.that.scroller.x + _tmp.that.scroller.w/2 - step > mousePos.x){
									_tmp.that.scrollByX(-step*_tmp.that.scrollCoeff);
								}
								else {
									var _step = Math.abs(_tmp.that.scroller.x + _tmp.that.scroller.w/2 - mousePos.x);
									_tmp.that.scrollByX(-_step*_tmp.that.scrollCoeff);
									cancelClick();
									return;
								}
							}
							scrollTimeout = setTimeout(doScroll, isFirst ? _tmp.that.settings.initialDelay : _tmp.that.settings.trackClickRepeatFreq);
							isFirst = false;
						},
						cancelClick = function()
						{
							scrollTimeout && clearTimeout(scrollTimeout);
							scrollTimeout = null;
							_tmp.that.unbind( "mouseup",cancelClick);
							_tmp.that.lock = false;
						};
					doScroll();
					this.that.bind( "mouseup",cancelClick);
				}
			}
		}
	},
	evt_mousewheel : function(e){
		var evt = e||windows.event, delta = 1;
		if (this.that.isHorizontalScroll) return;
		var mp = {}, isTop = false, isBottom = false;
		if (undefined != evt.wheelDelta)
			delta = (evt.wheelDelta > 0) ? -this.that.settings.vscrollStep : this.that.settings.vscrollStep;
		else
			delta = (evt.detail > 0) ? this.that.settings.vscrollStep : -this.that.settings.vscrollStep;
		delta *= this.that.settings.wheelScrollLines;
		this.that.scroller.y += delta;
		if (this.that.scroller.y<0){
			this.that.scroller.y = 0;
			isTop = true, isBottom = false;
		}
		else if (this.that.scroller.y+this.that.scroller.h>this.that.canvas.height){
			this.that.scroller.y = this.that.canvas.height - this.that.arrowPosition - this.that.scroller.h;
			isTop = false, isBottom = true;
		}
		this.that.scrollByY(delta)
	},
	evt_click: function(e){
		var evt = e||windows.event;
		var mousePos = this.that.getMousePosition(evt);
		if (this.that.isHorizontalScroll){
			if (mousePos.x > this.arrowPosition && mousePos.x<this.that.canvas.width - this.that.arrowPosition){
				if(this.that.scroller.x > mousePos.x){
					this.that.scrollByX(-this.that.settings.vscrollStep);
				}
				if(this.that.scroller.x < mousePos.x && this.that.scroller.x + this.that.scroller.w > mousePos.x){
					return false;
				}
				if(this.that.scroller.x + this.that.scroller.w < mousePos.x){
					this.that.scrollByX(this.that.settings.hscrollStep);
				}
			}
		}
		if (this.that.isVerticalScroll){
			if (mousePos.y > this.that.arrowPosition && mousePos.y<this.that.canvas.height - this.that.arrowPosition){
				if(this.that.scroller.y > mousePos.y){
					this.that.scrollByY(-this.that.settings.vscrollStep);
				}
				if(this.that.scroller.y < mousePos.y && this.that.scroller.y + this.that.scroller.h > mousePos.y){
					return false;
				}
				if(this.that.scroller.y + this.that.scroller.h < mousePos.y){
					this.that.scrollByY(this.that.settings.hscrollStep);
				}
			}
		}
	},

	/************************************************************************/
	/*for events*/
	bind: function(typesStr, handler){
        var types = typesStr.split(" ");
        /*
         * loop through types and attach event listeners to
         * each one.  eg. "click mouseover.namespace mouseout"
         * will create three event bindings
         */
        for (var n = 0; n < types.length; n++) {
            var type = types[n];
            var event = (type.indexOf('touch') == -1) ? 'on' + type : type;
            var parts = event.split(".");
            var baseEvent = parts[0];
            var name = parts.length > 1 ? parts[1] : "";

            if (!this.eventListeners[baseEvent]) {
                this.eventListeners[baseEvent] = [];
            }

            this.eventListeners[baseEvent].push({
                name: name,
                handler: handler
            });
        }
    },
	unbind: function(typesStr){
			var types = typesStr.split(" ");

			for (var n = 0; n < types.length; n++) {
					var type = types[n];
					var event = (type.indexOf('touch') == -1) ? 'on' + type : type;
					var parts = event.split(".");
					var baseEvent = parts[0];

					if (this.eventListeners[baseEvent] && parts.length > 1) {
							var name = parts[1];

							for (var i = 0; i < this.eventListeners[baseEvent].length; i++) {
									if (this.eventListeners[baseEvent][i].name == name) {
											this.eventListeners[baseEvent].splice(i, 1);
											if (this.eventListeners[baseEvent].length === 0) {
													this.eventListeners[baseEvent] = undefined;
											}
											break;
									}
							}
					}
					else {
							this.eventListeners[baseEvent] = undefined;
					}
			}
	},
	handleEvents: function(eventType, evt, p){
		var that = this;
        // generic events handler
        function handle(obj){
            var el = obj.eventListeners;
            if (el[eventType]) {
                var events = el[eventType];
                for (var i = 0; i < events.length; i++) {
                    events[i].handler.apply(obj, [evt]);
                }
            }
        }
        /*
         * simulate bubbling by handling shape events
         * first, followed by group events, followed
         * by layer events
         */
        handle(that);
    }

}