/*
	rubber.js
	
	Version 2.1 / 12:48 05.11.2010
	* kleine Änderungen
	
	Version 2.0 / 23:12 11.01.2009
	
	Ein Skript für ein "Rubberband", mit dem sich Bereiche markieren lassen.
	
	Funktionen:
	
	Rubber.ini([EventHandler])
	
		EventHandler (optional) ist eine Funktionsreferenz,
		die folgendendermaßen aufgebaut ist:
		
		EventHandler(object, evt, function)
		
		Parameter:
		**********
		object   = Objekt, das den Event empfangen soll
		evt      = Der Name des Events, ohne on
		function = Funktionsreferenz 
		
		Wird die Funktionsreferenz weggelassen, werden die nötigen Events in der Form:
		obj['on'+ evt] = function;
		initialisiert. Das kann dazu führen, dass das Skript mit anderen Skripten,
		die ebenfalls diese Schreibweise verwenden, nicht funktioniert.
		
	Rubber.bound(Rect|top,left, width, height)
	
		Begrenzen des Rubberbandes.
		
		Parameter:
		**********
		Entweder
		Rect = ein Rect Objekt oder
        ein beliebiges Objekt mit den Eigenschaften top, left, width, height. Oder
		top, left, width, height = Pixelwerte im Dokument
		
	Rubber.getSize() => Object(width, height)

	Rubber.getPos() => Object(top, left)

	Rubber.onstart(rect)
	Rubber.onmove(rect)
	Rubber.onend(rect)
		Eventfunktionen, 
		die beim klicken, beim ziehen bzw. beim beenden aufgerufen werden. 
		Der Parameter, der den Funktionen übergeben wird, 
		ist das Rect-Objekt des Rubberbandes
*/

var Rubber = new function() {// Singleton - es kann immer nur ein Rubberband geben
	
    // öffentliche Attribute
	this.borderSize = '1px';
	this.borderColor = '#ff0';
	this.borderStyle = 'dashed'; // dotted, dashed oder solid

    // private Attribute
    var 
    body, isActive,
    rubberband = bound = null, 
    self = this,
    doc = window.document
    start = { top: 0, left: 0},
    mouse = { top: 0, left: 0},
    eventHandler = function(obj, evt, func) { obj['on' + evt] = func; return false;}
    ;
	
	this.ini = function(myHandler) {
		// Ini wird nur einmal aufgerufen
		if(rubberband) return;
		rubberband = createBox();
		
		if(doc.body) {
			body = doc.documentElement || doc.body;
		} else {
			return alert('found no document.body :-(');
		}
		bound = new Rect(0, 0, docSize()[0], docSize()[1]);
		
		// Eventhandler initialisieren
		if(myHandler) eventHandler = myHandler;
		
		eventHandler(doc, 'mousedown', down_func);
		eventHandler(doc, 'mousemove', move_func);
		eventHandler(doc, 'mouseup', up_func);
		
		isActive = false;
    };
	// 
	this.bound = function() {
		if(arguments[0] instanceof  Rect) {
			bound.copy(arguments[0]);
		} else {
			bound.top = arguments[0];
			bound.left = arguments[1];
			bound.width = arguments[2];
			bound.height = arguments[3];
		}
		bound.border(parseInt(this.borderSize));
	}

    ////////////////////////////////////////////////////
	// Event Funktionen
	
	/*
		down_func(event)
		Startet das Rubberband
	*/
	function down_func(e) {
		if(isActive) return;
		if(!e) e = window.event;
		if(!rubberband) self.ini();
		
		calc_size(e, true);

		if(!rubberband.boundTo(bound) || !self.onstart(rubberband)) return true;
		isActive = true;
			
		// Rubberband positionieren und zeigen
		rubberband.show( { 
			borderColor: self.borderColor, 
			borderWidth: self.borderSize,
			borderStyle: self.borderStyle
		});
		rubberband.draw();

		// markieren verhindern
		return false;
    }
	/*
     * up_func(event)
     * Beendet das Rubberband
	*/
	function up_func (e) {
		if(!isActive) return;

		calc_size(e);
		rubberband.boundTo(bound);
		if(!self.onend(rubberband)) return true;
		rubberband.hide();
		isActive = false;
		return false;
	}
	/*
     * move_func(event)
     * Bewegt das Rubberband
	*/
    function move_func(e) {
		if(!isActive) return;

		calc_size(e);
		rubberband.boundTo(bound);
		if(self.onmove(rubberband)) rubberband.draw();
		return false;
	}
	/*
     * interne Funktion: calc_size()
     * setzt die Position des Box Objektes berechnet anhand der Mausposition
    */ 
	function calc_size(e, calc_start) {
		get_mouse_pos(e);
		if(calc_start) { // Startposition
			start.top = mouse.top;
			start.left = mouse.left;
		}
		rubberband.top = Math.min(mouse.top, start.top);
		rubberband.left = Math.min( mouse.left, start.left);
		rubberband.width = Math.abs(mouse.left - start.left);
		rubberband.height = Math.abs(mouse.top - start.top);
	}
	/*
     * interne Funktion: get_mouse_pos(e)
     * berechnet die Position des Mausevents im Dokument
     * und setzt die Eigenschaften von 'mouse'
     * http://www.quirksmode.org/js/events_properties.html
	*/
	function get_mouse_pos(e) {
		if(!e) e = window.event;
		mouse.top = e.pageY  || 
		(e.clientY + doc.documentElement.scrollTop + doc.body.scrollTop);
		
		mouse.left = e.pageX || 
		(e.clientX + doc.documentElement.scrollLeft  + document.body.scrollLeft);
	}
	// Dummyfunktionen
	this.onmove = this.onend = this.onstart = function() {return true;};
    
	// Zugriffsfunktionen
	this.getSize = function() { return rubberband.getSize();};
	this.getPos = function() { return rubberband.getPos(); };
};

function docSize() {
	var doc = window.document;
	var mode = (doc.compatMode == "CSS1Compat");
	
	var body = mode? doc.documentElement : doc.body;
	var width, height;
	if(body) {
		var test1, test2;
		
		test1 = body.scrollHeight;
		test2 = body.offsetHeight || body.clientHeight;
		
		height = test1 > test2 ?  test1 : test2;
		
		test1 = body.scrollWidth;
		test2 = body.offsetWidth || body.clientWidth;
		
		width = (test1 > test2) ? test1 : test2;
		
	} else if( doc.height ) {
		width = doc.width;
		height = doc.height;
	}
	return [ height, width ];
}

/*
	Helperfunktion: createBox()
	
	Rückgabe: Box-Objekt
		erzeugt ein DIV Element und hängt es im Dokumentenbaum ein.
		
		Box erbt von Rect
		
	Funktionen:
	draw()
	show()
	hide()
	getSize()
	getPos()
*/
function createBox() {
	function Box() {
	var obj = document.createElement('div');
	var isAdd = false;
	with(obj.style) {
		display = 'none';
		position = 'absolute';
		border = '1px dotted black';
		overflow = 'hidden';
		padding = margin = 0;
		background = 'none';
	}
	this.draw = function() {
		if(!isAdd) { document.body.appendChild(obj); isAdd = true;}
	
		obj.style.top = this.top +  'px';
		obj.style.left = this.left +  'px';
		obj.style.width = this.width +  'px';
		obj.style.height = this.height +  'px';
	};
	this.show = function(s) { 
		for(var a in s) obj.style[a] = s[a];
		obj.style.display = 'block';
	};
	this.hide = function() { obj.style.display = 'none';};
	this.getSize = function() {return {width: obj.offsetWidth, height: obj.offsetHeight}};
	this.getPos = function() {
		var top = 0;
		var left = 0;
		var o = obj;
		for(var top = o.offsetTop, left = o.offsetLeft; o = o.offsetParent; top += o.offsetTop, left += o.offsetLeft) {}
		return {top:top, left:left};
	}
	}
	Box.prototype = new Rect();
	Box.constructor = Box;
	return new Box;
}


/*
	HilfsObjekt: Rect()
	
	Eigenschaften 
	top, left, width, height
	
	Funktionen:
	bottom()
	right()
	copy(Rect)
	border(Pixel)
	boundTo(Rect)
*/
function Rect(t, l, h, w) {
	this.top = t || 0;
	this.left = l || 0;
	this.height = h || 0;
	this.width = w || 0;

	this.right = function(val) {
		if(typeof val != 'undefined') this.width = this.left + val;
		return this.left + this.width;
	};
	this.bottom = function(val) {
		if(typeof val != 'undefined') this.height = this.top + val;
		return this.top + this.height;
	};
	this.copy = function(r) {
		this.top = r.top;
		this.left = r.left;
		this.height = r.height;
		this.width = r.width;
	}
	this.border = function(px) {
		this.height -= 2*px;
		this.width -= 2*px;
	}
	this.boundTo = function(r) {
		var isIn = true;
		if(this.top < r.top) {
			var d = this.height - (r.top - this.top);
			this.top = r.top;
			this.height = d;
			isIn = false;
		}
		if(this.left < r.left) {
			var d = r.left - this.left;
			this.left = r.left;
			this.width -= d;
			isIn = false;
		}
		if(this.bottom() > r.bottom()) {
			this.height = r.bottom() - this.top;
			isIn = false;
		}
		if(this.right() > r.right()) {
			this.width = r.right() - this.left;
			isIn = false;
		}
		return isIn;
	};
	this.info = function() {
		return 't/l:' + this.top + '/' + this.left 
		+ '\n' + 'w/h:' + this.width + '/' + this.height 
	};
}


