var addHandler = ( function(){
	if( document.addEventListener ) {
		return function( obj, name, func){
				obj.addEventListener( name, func, false );
			}
	} else if ( document.attachEvent ) {
		return function( obj, name, func ){
				obj.attachEvent( 'on' + name, func );
			}
	} else {
		return function() { throw 'Error' }
	}
})();

var removeHandler = ( function(){
	if( document.removeEventListener ) {
		return function( obj, name, func){
				obj.removeEventListener( name, func, false );
			}
	} else if ( document.detachEvent ) {
		return function( obj, name, func ){
				obj.detachEvent( 'on' + name, func );
			}
	} else {
		return function() { throw 'Error' }
	}
})();

var defPosition = ( function() {
	if (document.attachEvent != null) {
		return function( ) {
			return { x: window.event.clientX + document.documentElement.scrollLeft + document.body.scrollLeft,
					y: window.event.clientY + document.documentElement.scrollTop + document.body.scrollTop };
		};
	} else if (!document.attachEvent && document.addEventListener) {
		return function( event ) {
			return { x: event.clientX + window.scrollX,
					y: event.clientY + window.scrollY };
		}
	} else {
		return function() { throw 'Error' }
	}
})();

//	classExtends( child, parent, {}, full_copy = false );		- full_copy - use it carefully
//	child._parent.apply( this, arguments );						- parent::constructor();
//	child._parent.prototype.method.apply( this, [] );			- parent::method();		this = this
//	child._parent.prototype.method.apply( this._parent, [] );	- parent::methid();		this = parent
function classExtends( child, parent, methods, full_copy ) {
	
	var e = function() {};
	e.prototype = parent.prototype;
	
	child.prototype = new e();
	child.prototype.constructor = this;
	child._parent = parent;
	
	if( methods ) {
		for( m in methods ) {
			child.prototype[m] = methods[m];
		}
	}
	
	if( !!full_copy ) {
		for( p in parent ) {
			if( !child[p] )
				child[p] = parent[p];
		}
	}
	
}

function cancelEvent( event ){
	event.cancelBubble=true;
	event.returnValue = false;
	if (event.preventDefault) 
		event.preventDefault();
}

function log(s/*, l = 100*/) {
	//return;
	var dout = document.getElementById('out');
	if( !dout ){
		dout = document.body.appendChild(document.createElement('div'));
		dout.id = 'out';
		dout.style.position='fixed';
		dout.style.top='0';
		dout.style.right='0';
		dout.style.backgroundColor = 'white';
	}
		
	var l = arguments[1] || 100;
	dout.innerHTML = s + '<br _type="log">' + dout.innerHTML;
	dout.innerHTML = dout.innerHTML.split('<br _type="log">').splice(0, l).join('<br _type="log">');
}

function fetch(o) {
	var s = '';
	for(i in o){
	s += i + '=' + o[i] + '<br>';
	}
	return s;
}


function rm_Event () {
	this.initEvents();
}

rm_Event.prototype = {
	addListener : function( event, handler ) {
		if( !(handler instanceof Object) )
			return false;
		if( typeof( this.eventListners[event] ) == 'undefined' ) 
			this.eventListners[event] = [];
		this.eventListners[event][this.eventListners[event].length] = handler;
	},
	revomeListener : function( event, handler ) {
		if( typeof( this.eventListners[event] ) != 'undefined' && this.eventListners[event].indexOf(handler) != -1 )
			this.eventListners[event].splice( this.eventListners[event].indexOf(handler), this.eventListners[event].indexOf(handler), null );
	},
	send : function( event, param ) {
		if( !this.eventListners[event] )
			return true;
		var r, result = true;
		for( var i = 0; i < this.eventListners[event].length; i++ ) {
			if( this.eventListners[event][i] instanceof Function ) {
				r = this.eventListners[event][i]( param, event, this );
				if( typeof( r ) != 'undefined' && !r ) {
					result = false;
				}
			}
		}
		return result;
	},
	initEvents: function() {
		this.eventListners = {};
	}
};


function rm_DragNDrop ( obj/*, bound=null, direction_type=rm_DragNDrop.BOTH_DIR*/ ) {
	
	if( !obj ) {
		return;
	} else if( typeof(obj) == 'string' ) {
		this.subject = document.getElementById( obj );
	} else {
		this.subject = obj;
	}
	if( !this.subject )
		return null;
		
	this.subject.ondragstart = function() { return false; };
		
	var bound = arguments[1] || null;
	if( !bound )
		this.boundary = false;
	else if( typeof(bound) == 'string' ) {
		this.boundary = document.getElementById( bound );
	} else {
		this.boundary = bound;
	}
	this.direction_type = arguments[2] || rm_DragNDrop.BOTH_DIR;
	
	this.initSubject();
	this.initEvents();
	
}

rm_DragNDrop.BOTH_DIR = 'both_direction';
rm_DragNDrop.VERT_DIR = 'vertical_direction';
rm_DragNDrop.HOR_DIR = 'horizontal_direction';

rm_DragNDrop.ON_MOVE = 'onMove';
rm_DragNDrop.ON_START_DRAG = 'onStartDrag';
rm_DragNDrop.ON_STOP_DRAG = 'onStopDrag';

classExtends( rm_DragNDrop, rm_Event, {
	cbOnDraging : {},
	cbOnMouseUp : {},
	tmr: null,
	direction_type: '',
	
	initSubject : function() {
		var _this = this;
		this.cbOnDraging = function( ev ){ _this.onDraging( ev ) };
		this.cbOnMouseUp = function( event ){ _this.stopDrag( event ); }
		addHandler( this.subject, 'mousedown', function(event){ _this.startDrag(event); } );
	},
	stopDrag : function( ev ) {
		removeHandler( document.body, 'mousemove', this.cbOnDraging );
		removeHandler( document.body, 'mouseup', this.cbOnMouseUp );
		if( document.all && !window.opera ) {
			var doc = (document.compatMode && document.compatMode.toLowerCase() != "backcompat")? document.documentElement : (document.body || null);
			doc.onselectstart = null;
		}
		this.send( rm_DragNDrop.ON_STOP_DRAG, this.stateDigest );
	},
	onDraging : function( ev ) {
		var event = ev || window.event;
		event.cancelBubble=true;
		if (event.preventDefault) 
			event.preventDefault();
		var nx = parseInt(this.stateDigest.subject_left);
		var ny = parseInt(this.stateDigest.subject_top);
		var mx = defPosition( ev ).x;
		var my = defPosition( ev ).y;
		if( this.direction_type == rm_DragNDrop.BOTH_DIR || this.direction_type == rm_DragNDrop.HOR_DIR ) {
			nx = mx - this.stateDigest.delta_left;
			if( this.boundary )
				nx = nx < 0 ? 0 : nx + this.subject.offsetWidth > this.boundary.offsetWidth ? this.boundary.offsetWidth - this.subject.offsetWidth : nx;
		}
		if( this.direction_type == rm_DragNDrop.BOTH_DIR || this.direction_type == rm_DragNDrop.VERT_DIR ) {
			ny = my - this.stateDigest.delta_top;
			if( this.boundary )
				ny = ny < 0 ? 0 : ny + this.subject.offsetHeight > this.boundary.offsetHeight ? this.boundary.offsetHeight - this.subject.offsetHeight : ny;
		}
		if( this.stateDigest.subject_left == nx && this.stateDigest.subject_top == ny )
			return;
		this.stateDigest.subject_left = nx;
		this.stateDigest.subject_top = ny;
		this.stateDigest.mouse_x = mx - this.stateDigest.bound_left;
		this.stateDigest.mouse_y = my - this.stateDigest.bound_top;
		if( this.send( rm_DragNDrop.ON_MOVE, this.stateDigest ) ) {
			this.subject.style.top = ny + 'px';
			this.subject.style.left = nx + 'px';
		}
	},
	startDrag : function( ev ) {
		var event = ev || window.event;
		event.cancelBubble=true;
		if (event.preventDefault) 
			event.preventDefault();
		if( document.all && !window.opera ) {
			var doc = (document.compatMode && document.compatMode.toLowerCase() != "backcompat")? document.documentElement : (document.body || null);
			doc.onselectstart = function(){
				event.returnValue = false;
			};
		}
		var inx = document.all || event.offsetX ? event.offsetX : event.layerX;
		var iny = document.all || event.offsetY ? event.offsetY : event.layerY;
		var sx = this.subject.offsetLeft;
		var sy = this.subject.offsetTop;
		var lx = inx + sx;
		var ly = iny + sy;
		var pos = defPosition( ev );
		var bleft = pos.x - lx;
		var btop = pos.y - ly;
		var dox = bleft + inx;
		var doy = btop + iny;
		this.stateDigest = { target : this,
			start_left : sx,
			start_top : sy,
			inside_left : inx, 
			inside_top : iny,
			inbound_left : lx,
			inbound_top : ly,
			delta_left : dox,
			delta_top : doy,
			bound_left : bleft,
			bound_top : btop,
			mouse_x : pos.x - bleft,
			mouse_y : pos.y - btop,
			subject_width : this.subject.offsetWidth,
			subject_height : this.subject.offsetHeight,
			subject_left : this.subject.offsetLeft,
			subject_top : this.subject.offsetTop
		};
		this.send( rm_DragNDrop.ON_START_DRAG, this.stateDigest );
		var _this = this;
		addHandler( document.body, 'mousemove', this.cbOnDraging );
		addHandler( document.body, 'mouseup', this.cbOnMouseUp );
	}
});

function rm_SelRange( bound, range, value, parts ){
	var _t = this;
	_t.initEvents();
	_t.bound = bound;
	_t.range = range;
	_t.value = value;
	_t.parts = parts;
  _t.params = {};
	_t.initMechanics();
}

rm_SelRange.ON_CHANGE = 'on_change';

classExtends( rm_SelRange, rm_Event, {
	dnd: {},
	params: {},
	
	initMechanics: function( ) {
		var _t = this;
		var _p = _t.parts;
		
		// Иногда в IE6-7 блок не успевает отрисовать и мы не можем узнать его размеры
		if (!_t.bound.offsetWidth) {
			web.attachEventListener(window, 'load', function(){
				_t.initMechanics();
			});
			return false;
		}
		
		var bmax = _t.bound.offsetWidth;
		var amax = bmax - _t.parts.left.offsetWidth;
		var rmax = _t.range[1] - _t.range[0];
		
		var updateParts = function( ) {
			
			var min = Math.min( _t.params.x1, _t.params.x2 );
			var max = Math.max( _t.params.x1, _t.params.x2 );
			
			var lft = amax * min / bmax;
			var rgt = amax * max / bmax;
			
			_t.parts.left.style.left = lft + 'px';
			_t.parts.right.style.left = rgt + 'px';
			
			_t.parts.middle.style.left = lft + _t.parts.left.offsetWidth/2 + 'px';
			_t.parts.middle.style.width = rgt - lft + 'px';
			
			
			
		}
		var updateValue = function() {
			
			var min = Math.min( _t.params.x1, _t.params.x2 );
			var max = Math.max( _t.params.x1, _t.params.x2 );

			var m = _t.range[1] - _t.range[0];
			
			_t.value[0] = m * min / bmax + _t.range[0];
			_t.value[1] = m * max / bmax + _t.range[0];
			
			_t.send( rm_SelRange.ON_CHANGE, _t.value );
			
		}
		
		_t.setValue = function( value ) {
			if( value[0] > _t.range[1] )
				value[0] = _t.range[1];
			if( value[1] > _t.range[1] )
				value[1] = _t.range[1];
				
			if( value[0] < _t.range[0] )
				value[0] = _t.range[0];
			if( value[1] < _t.range[0] )
				value[1] = _t.range[0];
				
      _t.params.x1 = Math.round( bmax * (value[0] - _t.range[0]) / rmax );
      _t.params.x2 = Math.round( bmax * (value[1] - _t.range[0]) / rmax );
			_t.params.w = Math.abs( _t.params.x2 - _t.params.x1 );
			_t.correctParams();
			updateParts();
			
		}
		
		_t.setValue( _t.value );
		
		var pLeft = new rm_DragNDrop( _p.left, _t.bound, rm_DragNDrop.HOR_DIR );
		pLeft.addListener( rm_DragNDrop.ON_MOVE, function( d ) {
			_t.params.x1 = d.subject_left * bmax / amax;
			_t.params.w = Math.abs( _t.params.x2 - _t.params.x1 );
			
			updateParts();
			updateValue();
			return false;
		});
		pLeft.addListener( rm_DragNDrop.ON_STOP_DRAG, function( ) {
			_t.correctParams();
		});
		
		var pRight = new rm_DragNDrop( _p.right, _t.bound, rm_DragNDrop.HOR_DIR );
		pRight.addListener( rm_DragNDrop.ON_MOVE, function( d ) {
			_t.params.x2 = d.subject_left * bmax / amax;
			_t.params.w = Math.abs( _t.params.x2 - _t.params.x1 );
			updateParts();
			updateValue();
			return false;
		});
		pRight.addListener( rm_DragNDrop.ON_STOP_DRAG, function( ) {
			_t.correctParams();
		});
		
		var pMiddle = new rm_DragNDrop( _p.middle, _t.bound, rm_DragNDrop.HOR_DIR );
		pMiddle.addListener( rm_DragNDrop.ON_MOVE, function( d ) {
			
			var lft = d.subject_left * bmax / amax - _t.parts.left.offsetWidth / 2;
			var rgt = ((d.subject_left + d.subject_width) * bmax / amax) - _t.parts.left.offsetWidth / 2;
			
			if( lft < 0 ) {
				rgt -= lft;
				lft = 0;
			}
			
			if( rgt > bmax ) {
				lft -= rgt - bmax;
				rgt = bmax;
			}
			
			_t.params.x1 = lft;
			_t.params.x2 = rgt;
			updateParts();
			updateValue();
			
			return false;
		});
		
	},
	correctParams: function() {
		var _t = this;
		var min = Math.min( _t.params.x1, _t.params.x2 );
		var max = Math.max( _t.params.x1, _t.params.x2 );
		_t.params.x1 = min;
		_t.params.x2 = max;
	}
	
});
