/************************************************************
***                                                       ***
***          Copyright 2009 by anema                      ***
***          www.anema.at                                 ***
***                                                       ***
************************************************************/
/*[version]2009-12-17 09:42[/version]*/

/**
 * Module Acu.js Anema Core Utils
 *
 */

Acu = {};

/**
 * Liefert ein Objekt mit den Properties
 * 	size		Größe in der angegebenen Dimension auf die angegebenen Präzision genau.
 *		dim		Die Dimension, also Byte, Kilobyte usw.
 *
 */
Acu.formatFileSize = function( filesize/*, precision=0, dims=['B', 'KB', 'MB', 'GB', 'TB']*/ ){
	var precision = arguments[1] ? arguments[1] : 0;
	var dimensions = arguments[2] ? arguments[2] : ['B', 'KB', 'MB', 'GB', 'TB'];

	index = 0;
	size = filesize;
	while( size > 1024 ){
		size /= 1024;
		index++;
	}

	size = Acu.round( size, precision );

	return { size : size, dim : dimensions[index] };
};


Acu.STR_PAD_LEFT  = 1;
Acu.STR_PAD_RIGHT = 2;
Acu.STR_PAD_BOTH  = 3;
Acu.str_pad = function( str, length/*, pad_string=" ", pad_type=Acu.STR_PAD_RIGHT*/ ){
	var pad_string = (typeof arguments[2]!="undefined") ? arguments[2] : " ";
	var pad_type = (typeof arguments[3]!="undefined") ? arguments[3] : Acu.STR_PAD_RIGHT;
	str = new String( str );	// Dann kann man die Funktion auch mit Zahlen usw. aufrufen.
	if( str.length >= length ){ return str; }
	if( pad_string.length != 1 ){ throw "Acu.str_pad: pad string length must be exactly 1"; }
	if( pad_type == Acu.STR_PAD_BOTH ){
		// Wenn ungerade, hängen wir rechts ein Zeichen mehr an.
		var padding = "";
		var diff = length - str.length;
		var extra = ((diff%2) > 0) ? pad_string : "";
		diff = Math.floor( diff / 2 );
		for( var i=0; i<diff; i++ ){ padding += pad_string; }
		return padding + str + padding + extra;
	}else{
		// Bastle das Padding und häng's an der richtigen Seite an.
		var padding = "";
		var diff = length - str.length;
		for( var i=0; i<diff; i++ ){ padding += pad_string; }
		if( pad_type == Acu.STR_PAD_LEFT ){
			return padding + str;
		}else{
			return str + padding;
		}
	}
};


/**
 * Die Sparversion von PHP-date().
 * Unterstützt nur folgende Kennzeichner:
 *		d		Tag des Monats, zweistellig
 *		m		Monat, zweistellig
 *		y		Jahr, zweistellig
 *		Y		Jahr, vierstellig
 *		H		Stunde (0-23), zweistellig
 *		i		Minuten, zweistellig
 *		s		Sekunden, zweistellig
 *
 * Die Zeitangabe ist in MILLISEKUNDEN (!).
 *
 */
Acu.date = function( format_str/*, timestamp*/ ){
	var timestamp = (typeof arguments[1]!="undefined") ? arguments[1] : new Date().getTime();
	var date = new Date( timestamp );
	var Y = date.getFullYear();
	if( Y > 99 ){
		var year_str = "" + Y;
		var y = year_str.substr( year_str.length-1, -2 );
	}else{
		var y = Y;
	}

	var d = date.getDate();
	var m = date.getMonth() + 1;	// getMonth() liefert 0..11.
	var H = date.getHours();
	var i = date.getMinutes();
	var s = date.getSeconds();

	var date_str = format_str.replace( /Y/, Y );
	date_str = date_str.replace( /y/, Acu.str_pad(y, 2, "0", Acu.STR_PAD_LEFT) );
	date_str = date_str.replace( /d/, Acu.str_pad(d, 2, "0", Acu.STR_PAD_LEFT) );
	date_str = date_str.replace( /m/, Acu.str_pad(m, 2, "0", Acu.STR_PAD_LEFT) );
	date_str = date_str.replace( /H/, Acu.str_pad(H, 2, "0", Acu.STR_PAD_LEFT) );
	date_str = date_str.replace( /i/, Acu.str_pad(i, 2, "0", Acu.STR_PAD_LEFT) );
	date_str = date_str.replace( /s/, Acu.str_pad(s, 2, "0", Acu.STR_PAD_LEFT) );

	return date_str;
};

/**
 * Math.round rundet immer auf eine Ganzzahl.
 * Mit der Funktion kann man die Nachkommastellen
 * angeben und sogar auf ganze Zehner- (precision=-1)
 * oder Hunderterstellen (precision=-2) usw. runden.
 *
 */
Acu.round = function( num, precision ){
	// Versetze den Dezimalpunkt um die gewünschte Präzision,
	// dann runde und verschiebe ihn noch einmal.
	var factor = 1;
	if( precision < 0 ){
		for( i=-1; i>=precision; i-- ){ factor /= 10; }
	}else{
		for( i=0; i<precision; i++ ){ factor *= 10; }
	}
	num = Math.round( num * factor );
	return num / factor;
};

Acu.registerEvent = function( element, eventType, handler ){
	if( !element ) return;
	if( typeof element == "string" ) element = document.getElementById( element );

	if( element.addEventListener ){
		element.addEventListener( eventType, handler, false );
	}else if( element.attachEvent ){
		element.attachEvent( "on" + eventType, handler );
	}else{
		element["on"+eventType] = handler;
	}
};

Acu.unregisterEvent = function( element, eventType, handler ){
	if( !element ) return;

	if( element.removeEventListener ){
		element.removeEventListener( eventType, handler, false );
	}else if( element.detachEvent ){
		element.detachEvent( "on" + eventType, handler );
	}else{
		element["on"+eventType] = null;
	}
};

Acu.getHttpRequest = function(){
    if( window.XMLHttpRequest ){
        return new XMLHttpRequest();
    } else if( window.ActiveXObject ){
        return new ActiveXObject( "Microsoft.XMLHTTP" );
    }else{
	    return null;
    }
};


// Die folgenden Methoden sind vom Definitive Guide geklaut (CSSClass.js).
Acu.hasCssClass = function( e, c ){
	if( typeof e == "string" ) e = document.getElementById( e );	// element id

	// Before doing a regexp search, optimize for a couple of common cases.
	var classes = e.className;
	if( !classes ) return false;	// Not a member of any class
	if( classes == c ) return true;	// Member of just this one class

	// Otherwise, use a regular expression to search for c as a word by itself
	// \b in a regular expression requires a match at a word boundary.
	return e.className.search( "\\b" + c + "\\b" ) != -1;
};

// Add class c to the className of element e if it is not already there.
Acu.addCssClass = function( e, c ){
	if( typeof e == "string" ) e = document.getElementById( e );	// element id
	if( Acu.hasCssClass(e, c) ) return;	// If already a member, do nothing
	if( e.className ) c = " " + c;	// Whitespace separator, if needed
	e.className += c;				// Append the new class to the end
};

// Remove all occurrences (if any) of class c from the className of element e
Acu.removeCssClass = function( e, c ){
	if( typeof e == "string" ) e = document.getElementById( e );	// element id
	// Search the className for all occurrences of c and replace with "".
	// \s* matches any number of whitespace characters.
	// "g" makes the regular expression match any number of occurrences
	e.className = e.className.replace( new RegExp("\\b"+c+"\\b\\s*", "g"), "" );
};

/**
Opacity wird als Dezimalwert angegeben, z.B. "0.7".
*/
Acu.setOpacity = function( element, opacity ){
	if( element && element.style ){
		var style = element.style;
		var ie_opacity = Math.round( opacity * 100 );
		style.opacity = "" + opacity;
		style.filter = "alpha(opacity=" + ie_opacity + ")";
	}
};



Acu.hideDiv = function( obj ){
	if( typeof obj == "string" ) obj = document.getElementById( obj );
	if( !obj ) return;
	obj.style.display = 'none';
};

Acu.showDiv = function( obj ){
	if( typeof obj == "string" ) obj = document.getElementById( obj );
	if( !obj ) return;
	obj.style.display = '';
};

Acu.toggle = function( obj ){
	if( typeof obj == "string" ) obj = document.getElementById( obj );	// element id
	var status = obj.style.display;
	if (status == 'none') {
		obj.style.display = '';
	}else{
		obj.style.display = 'none';
	}
};

Acu.in_array = function( value, arr ){
	for( var i=0; i<arr.length; i++ ){
		if( value == arr[i] ){
			return true;
		}
	}
	return false;
};

/* Die XML-Funktionen sind wieder Mal aus dem schlauen Buch geklaut. */
Acu.createNewXmlDocument = function( rootTagName ){
	if( !rootTagName ) rootTagName = "";

	if( document.implementation && document.implementation.createDocument ){
		// "" statt einer namespaceURL
		return document.implementation.createDocument( "", rootTagName, null );
	}else{
		var doc = new ActiveXObject( "MSXML2.DOMDocument" );
		var text = "<" + rootTagName + " />";
		doc.loadXML( text );
		return doc;
	}
};

Acu.xmlNodeToString = function( node ){
	if( typeof XMLSerializer != "undefined" ){
		return (new XMLSerializer()).serializeToString( node );
	}else if( node.xml ){	// IE
		return node.xml;
	}else{
		throw "Acu.xmlNodeToString is not supported.";
	}
};

Acu.parseXmlStringIntoDocument = function( text ){
	if( typeof DOMParser != "undefined" ){
		// Mozilla, Firefox and related browsers.
		return (new DOMParser()).parseFromString( text, "application/xml" );
	}else if( typeof ActiveXObject != "undefined" ){
		// Internet Explorer.
		//var doc = XML.newDocument();
		var doc = new ActiveXObject( "MSXML2.DOMDocument" );
/*		if( doc.documentElement ){
			alert( 'hat root node' );
		}else{
			alert( 'nix root node' );
		}*/
//		text = '< ?xml version="1.0" encoding="utf-8" ?>' + text;
		doc.loadXML( text );
		return doc;
	}else{
		// As a last resort, try loading the document from a data: URL
		var url = "data:text/xml;charset=utf-8," + encodeURIComponent( text );
		var request = new XMLHttpRequest();
		request.open( "GET", url, false );
		request.send( null );
		return request.responseXML;
	}
};


Acu.prepareForAjaxTransmit = function( data ){
	//data = Acu.replaceAjaxProblemChars( data );
	//data = escape( data );
	//data = encodeURI( data );
	var regexp = /%20/g;	// A regular expression to match an encoded space
	// The global function encodeURIComponent does almost what we want,
	// but it encodes spaces as %20 instead of as "+". We have to
	// fix that with String.replace()
	data = encodeURIComponent( data ).replace( regexp, "+" );
	return data;
};

Acu.phpUrlEncode = function( text ){
	if( !text ){ return ""; }
	var regexp = /%20/g;	// A regular expression to match an encoded space
	// The global function encodeURIComponent does almost what we want,
	// but it encodes spaces as %20 instead of as "+". We have to
	// fix that with String.replace()
	text = encodeURIComponent( text ).replace( regexp, "+" );
	return text;
};

Acu.phpUrlDecode = function( text ){
	if( !text ){ return ""; }
	var regexp = /\+/g;
	text = decodeURIComponent( text.replace( regexp, "%20" ) );
	return text;
};



Acu.swapZIndex = function( obj1, obj2 ){
	if( typeof obj1 == "string" ) obj1 = document.getElementById( obj1 );
	if( typeof obj2 == "string" ) obj2 = document.getElementById( obj2 );
	if( !obj1 || !obj2 ) return false;

	var styles = Acu.getCurrentStyles( obj1 );
	if( !styles ) return false;
	var z1 = styles.zIndex;

	styles = Acu.getCurrentStyles( obj2 );
	if( !styles ) return false;
	var z2 = styles.zIndex;

	obj1.style.zIndex = z2;
	obj2.style.zIndex = z1;

	return true;
};

Acu.getSelectedOptionValue = function( select ){
	if( typeof select == "string" ) select = document.getElementById( select );

	/*if( !select || select.tagName != "select" ){
		return null;
	}

	var options = select.options;
	for( var i=0; i<options.length; i++ ){
		if( options[i].selected ) return options[i].value;
	}

	return null;*/

	return select.options[select.selectedIndex].value;
};

Acu.selectDropdownOption = function( select, value ){
	if( typeof select == "string" ) select = document.getElementById( select );
	if( !select ){ return false; }

	var options = select.options;
	for( var i=0; i<options.length; i++ ){
		if( options[i].value == value ){
			options[i].selected = true;
			return true;
		}
	}
	return false;
};

Acu.setValue = function( obj, value ){
	if( typeof obj == "string" ) obj = document.getElementById( obj );	// element id
	if( !obj ) return;

	obj.value = value;
};

/**
 * Brute-force -Suche!
 */
Acu.indexOf = function( value, array/*, isSorted */ ){
	// Wenn sortiert, binäre Suche anwenden.
	for( var i=0; i<array.length; i++ ){
		if( array[i] == value ){
			return i;
		}
	}
	return -1;
};

/**
 * FireFox hat eine maximale Länge von 4096 Bytes für den NodeValue
 * eines XML-Knotens. Um größere Datenmengen zu verstauen, werden
 * Kindknoten erzeugt.
 *
 * Die Funktion sammelt die Daten aller Kindknoten und liefert sie
 * als Gesamtheit zurück.
 *
 */
Acu.extractCompleteXMLNodeData = function( xmlNode ){
	var data = "";

	for( var i=0; i<xmlNode.childNodes.length; i++ ){
		data += xmlNode.childNodes[i].nodeValue;
	}

	return data;
};

/**
 * Zeigt Infos aus dem window.navigator-Objekt.
 * Über den optionalem Parameter obj kann ein DOM-Element
 * angegeben werden, als dessen innerHTML die Infos angezeigt
 * werden sollen.
 * Wird obj nicht angegeben, werden die Infos mittels alert()
 * ausgegeben.
 *
 */
Acu.displayNavigatorData = function( /*obj=null*/ ){
	var info = "";
	var obj = arguments[0] ? arguments[0] : null;
	for( var p in window.navigator ){
		info += p + ": " + window.navigator[p];
		if( obj ){	// Wenn obj gesetzt, soll in einem DOM-Element ausgegeben werden.
			info += "<br />";
		}else{
			info += "\n";
		}
	}
	if( obj ){
		obj.innerHTML = info;
	}else{
		alert( info );
	}
};

Acu.detectEnterKeyPressed = function( e ){
	if( !e ) e = window.event;
	var code = 0;
	if( e.which ){
		code = e.which;
	}else if( e.keyCode ){
		code = e.keyCode;
	}
	if( code == 13 ){
		return true;
	}
	return false;
};

Acu.nl2br = function( str ){
	if( typeof str != "string" ) str = "" + str;
	str = str.replace( /\n\r/g, "<br />" );
	str = str.replace( /\r\n/g, "<br />" );
	str = str.replace( /\r/g, "<br />" );
	str = str.replace( /\n/g, "<br />" );
	return str;
}

// Zahl wenn nicht keine Zahl..
Acu.isNumeric = function( val ){ return !isNaN( val ); };

// Integer ist's wenn numerisch und keine Nachkommastellen.
Acu.isInt = function( val ){ return !isNaN(val) && Math.floor(val)==Number(val); };

Acu.sort = function( obj, direction /*, first=null, last=null*/ ){
	if( !obj ) return;
	var first = arguments[2] ? arguments[2] : null;
	var last = arguments[3] ? arguments[3] : null;

	if( last && typeof last == "string" ) last = document.getElementById( last );
	if( first && typeof first == "string" ) first = document.getElementById( first );

	if( direction == "up" ){
		var prev = $( obj ).prev()[0];
		if( !prev || prev == first ){
			// Ist das erste Element. Wir rutschen daher ans Ende der Liste.
			if( last ){
				$( last ).before( obj );
			}else{
				obj.parentNode.appendChild( obj );
			}
		}else{
			// Tausche mit dem Vorgänger.
			$( prev ).before( obj );
		}
	}else{
		// Runterreihen.
		var next = $( obj ).next()[0];
		if( !next || next == last ){
			// Ist das letzte Element. Wir rutschen daher an den Anfang der Liste.
			if( first ){
				$( first ).after( obj );
			}else{
				$( obj.parentNode ).prepend( obj );
			}
		}else{
			// Tausche mit dem Nachfolger.
			$( obj ).before( next );
		}
	}
};

/**
 * Sucht im übergebenen Array a nach dem ersten Objekt,
 * dessen Property p dem Wert v entspricht und liefert
 * es zurück.
 * Wird kein passendes Objekt gefunden, liefert die
 * Funktion null.
 * Über den optionalen Parameter s kann die Startposition
 * anegegben werden, ab der im Array gesucht werden soll.
 *
 */
Acu.searchObjectArray = function( a, p, v/*, s=0*/ ){
	var s = isNaN(arguments[3]) ? 0 : Math.floor(arguments[3]);	// floor: just in case it's not an integer
	for( var i=s; i<a.length; i++ ){
		var e = a[i];
		if( e[p] == v ){ return e; }
	}
	return null;
};

Acu.activateInput = function( selector, cssClass ){
	$( selector ).removeClass( cssClass );
	$( selector ).removeAttr( "readonly" );
};

Acu.deactivateInput = function( selector, cssClass ){
	$( selector ).addClass( cssClass );
	$( selector ).attr( "readonly", "readonly" );
};

