/*

gute funcs von prototype:

array.uniq(); //macht das mit array, was unique mit objekten macht
anything.inspect(); //liefert debug string zurück



INFO: mod für in original-jquery:

  // Get width or height on the element
	//(this.length ? jQuery.css( this[0], type ) : null) :

	//FLO MOD: add padding to width/height
	(this.length ? jQuery.css(this[0],type)+ (type=="height"?parseInt(this.css("padding-top"))+parseInt(this.css("padding-bottom")):parseInt(this.css("padding-left"))+parseInt(this.css("padding-right"))) : null) :

*/

//+++ General ++++++++++++++++++++++++++++++++++++++++++++++++++++++++

//General Object Extension 
Object.extend = function(destination, source){
  for (var property in source)
    destination[property] = source[property];
  return destination;
};

//Name Space
var JQXT = {}; //globale vair wg. namespace. "JQXT = JQuery Extension"



//+++ $-Funcs  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++

//RegEx in verbindung von classnames handling
//wie $() func, aber man ruft auf $RE("*teilstring"). eralubte params sind
// ^needle: starts with
// $needle: ends with
// *needle: contains
//2 do: evtl. erweiter auch für hasClass("*teilstring") und removeClass("*teilstring")
function $RE(regClass,context){
	var classStr = regClass.substr(1,regClass.length-1);
	context =  context || document;

	if      (regClass.indexOf("^")!=-1) return $("*[@class^='"+classStr+"'],*[@class*=' "+classStr+"']",context);
	else if (regClass.indexOf("$")!=-1) return $("*[@class$='"+classStr+"'],*[@class*='"+classStr+" ']",context);
	else if (regClass.indexOf("*")!=-1) return $("*[@class*='"+classStr+"']",context);

}

//wie $, findet aber selector angewendet auf -scope childs und -scope selbst
function $Self(selector,scope){
	return $(selector,scope).add(scope.filter(selector));
	//return scope.find("*").andSelf().filter(selector); //alternativ, gleiches ergebnis
}


//+++ Div Handling ++++++++++++++++++++++++++++++++++++++++++++++++++++++++

//erzeugt div am ende von body
JQXT.createDiv = function(id,html,opt){
  html = html || "";
  opt = opt || {};
  var div = $("<div />").appendTo("body").attr({id:id}).css({display:"none"}).html(html);
  if(!opt.pos) div = div.css({position:"absolute"});
  return div;
}

//returnt index des siblings, VOR/ÜBER dem die maus ist (wenn maus NACH letztem: index=siblingsLength)
JQXT.mouseOverObj = function(x,y,siblings){
	var mouseBeforeId = 0;
	var siblingsLength = siblings.length;

	//maus NACH letztem?
	var obj = $(siblings).filter(":last");
	if ( (x>obj.left()+obj.width() && y>=obj.top()) || (y>obj.top()+obj.height() && x>=obj.left()) ) mouseBeforeId = siblingsLength;

	for(var i = 0;i<siblingsLength;i++){
		obj = $(siblings[i]);

  	//maus ÜBER diesem
  	if(x>=obj.left() && x<=obj.left()+obj.width() && y>=obj.top() && y <=obj.top()+obj.height()){
  		mouseBeforeId = i;
  		break;
  	}
  	//maus NACH diesem
    else if( (x>obj.left()+obj.width() && y>=obj.top()) || (y>obj.top()+obj.height() && x>obj.left())){
    	mouseBeforeId = i+1;
    }
	}

  return {id:mouseBeforeId,len:siblingsLength};
}


//+++ String ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Object.extend(String.prototype, {

  stripScripts: function(){
    return this.replace(new RegExp('<\s*script[^>]*>([\\S\\s]*?)<\/\s*script[^>]*>', 'img'), '');
  },

  stripTags: function(){
    return this.replace(/<\/?[^>]+>/gi, '');
  },

  truncate: function(length, truncation){
    length = length || 30;
    truncation = truncation === undefined ? '...' : truncation;
    return this.length > length ? this.slice(0, length - truncation.length) + truncation : String(this);
  },

  nl2br: function(){
    return this.replace(/(\r\n)|(\n\r)|\r|\n/g,"<br />");
  },

  br2nl: function(){
    return this.replace(/<br \/>/g,"\n").replace(/<br>/g,"\n");
  },
  
  space2Nbsp: function(){
    return this.repl("  ","&nbsp;&nbsp;");
  },

  nbsp2Space: function(){
    return this.repl("&nbsp;"," ");
  },

  //wie replace, aber es wird string übergeben statt regEx
  //2 do: wenn oldVal = "[wort]" ist, dann gibts fehler, weil [ und ] als regex interpretiert wird
  repl: function(oldVal,newVal){
  	return this.replace(new RegExp(oldVal,"g"), newVal);
  },
  
  //wandlung von eingaben aus textfeld für html darstellung
  inputToHtml: function(){
    return this.stripScripts().stripTags().nl2br().space2Nbsp();
  },

  //wandlung von html für textfeld eingabe
  htmlToInput: function(){
    return this.br2nl().nbsp2Space();
  }
  
});



//+++ jQuery Obj Extension ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
jQuery.fn.extend({

	unwrap: function(expr){
  	return this.each(function(){
    	$(this).parents(expr).eq(0).after(this).remove();
  	});
	},

  //sleep function: waits x millisecs. works as animation (with queue). wenn andere function ausgeführt werden soll benutze den callback
  sleep: function(speed,callback){
  	//make blank animation, animation of rare used param without any changes
  	return this.animate({"line-height":this.css("line-height")},speed,callback);
  },

	//lies teilstirng aus class, die mit prefix beginnt(prefix enthält)
	//beispiel: findet MEINEID per getIdFromClass($(),"needle") aus <div class="text gross needleMEINEID">
	getIdFromClass: function(prefix){
		//2do: wie begrenze ich, dass nur vom ersten returnt wird (falls objekte.length>1 übergeben: da string returnt macht mehr als ein string keinen sinn)
  	var ret = "";
   	if(this.attr("className")){ //error handling
    	$.each(this.attr("className").split(/\s+/), function(){
   	  	var regex = new RegExp(prefix,"g");
       	if(this.indexOf(prefix)!=-1) ret = this.replace(regex, "");
     	});
   	}
   	return ret;
	},


	//hängt obj in body (append) und macht display:none (soz. ausgelagert)
	storeDiv: function(){
	  return this.appendTo("body").css({display:"none"});
	},

  //faded div aus und stored es unsichtbar
  removeOpticalAni: function(speed,callback){
  	var obj = this;
  	this.hide(speed,function(){
  		obj.storeDiv();
  		if(callback) callback.call();
  	});
  	return obj;
  },

  //faded div aus und entfernt es dann komplett
  removeAni: function(speed,callback){
  	var obj = this;
  	this.hide(speed,function(){
  		obj.remove();
  		if(callback) callback.call();
  	});
  	return obj;
  },

  //ersetzt class durch andere class
	switchClass: function(oldClass,newClass){
	  this.filter("."+oldClass).removeClass(oldClass).addClass(newClass);
	  return this;
  },

  //ersetzt einen teil aus src="..." mit neuem string (für img-rollover)
  //val kann string oder regExp sein.
  //2do: wenn val=string -> behandel als string, sonst als regExp
  switchSrc: function(val,newVal){
  	if(typeof val == "string") val = new RegExp(val);
    return this.attr("src", function(){return this.src.replace(val, newVal);} );
	},


  //wie innerHeight, geht auch für display:none
  innerHeightF: function(){
		return(this.height()+parseInt(this.css("padding-top"))+parseInt(this.css("padding-bottom")));
  },
  innerWidthF: function(){
    return(this.width()+parseInt(this.css("padding-left"))+parseInt(this.css("padding-right")));
  },


  //wie stop, aber danach wird NICHTS ausgeführt (schreibt man kommentar in die nächste zeile, gibt es fehler im IE!!!!)????

  stopp: function(){
    return(this.queue([]).stop());
  },

  outerHtml: function(){
    return $( $('<div></div>').html(this.clone())).html();
  } ,

  //wie fadeIn, aber geeignet für fade Toggling (z.b. item faded-out und während fading soll er wieder einfaden)
  toggleFadeIn: function(speed, callback){
  	//methode 1: faded von 0-100
  	return this.stopp().hide().opacity(1).fadeIn(speed,callback); //opacity = ziel-opacity!

  	//methode 2: faded von IST-100
  	//return this.stopp().show().fadeTo(speed,1,callback);
  },
  toggleFadeOut: function(speed, callback){
  	//methode 1: faded von 100-0
  	return this.stopp().show().opacity(1).fadeOut(speed,callback); //opacity = ziel-opacity!

  	//methode 2: faded von IST-0
  	//return this.stopp().show().fadeTo(speed,0,callback);
  },

  
  /*
  2do: besser lösen als:
  
  call als:
  toggleFadeIn(300)
  toggleFadeIn(300,callback)
  toggleFadeIn(300,{start:true,sleep:300},callback)
  
  toggleFadeIn(speed,par1,par2)
    if(typeof par1=="function") var callback = par1;
    else var opt = par1;
    
    var sleep = 0;
    if(opt){
      if(opt.sleep) sleep = opt.sleep;
      if(opt.start) var startFromCurrent = opt.start;
    }
    
    if(startFromCurrent) this.stopp().sleep(sleep).show().fadeTo(speed,1,callback);
    else return this.stopp().hide().opacity(1).sleep(sleep).fadeIn(speed,callback);
  
    //fadeOut analog:
    //if(startFromCurrent) return this.stopp().sleep(sleep).show().fadeTo(speed,0,callback);
    //else return this.stopp().sleep(sleep).fadeOut(speed,callback);
    
  */
  //fadeIn/Out mit sleep davor  
  toggleFadeInS: function(speed, callback){
  	//methode 1: faded von 0-100
  	return this.stopp().hide().opacity(1).sleep(100).fadeIn(speed,callback); //opacity = ziel-opacity!

  	//methode 2: faded von IST-100
  	//return this.stopp().show().fadeTo(speed,1,callback);
  },
  toggleFadeOutS: function(speed, callback){
  	//methode 1: faded von 100-0
  	return this.stopp().sleep(100).fadeOut(speed,callback); //opacity = ziel-opacity!

  	//methode 2: faded von IST-0
  	//return this.stopp().show().fadeTo(speed,0,callback);
  },

  opacity: function(val){
  	if(val || val===0){return this.css({"opacity":val});}
    else return this.css("opacity");
  },

  cloneAsForm: function(obj,opt){
  	opt = opt || {};
    this.cloneDim(obj).css({
      fontSize:obj.css("fontSize"),
      color:obj.css("color"),
      fontWeight:obj.css("font-weight"),
      fontFamily:obj.css("font-family"),
      textAlign:obj.css("text-align"),
      lineHeight:obj.css("line-height"),
      fontStyle:obj.css("font-style"),
      textDecoration:obj.css("text-decoration"),
      height:obj.innerHeightF()+(this.get(0).tagName=="TEXTAREA"?15:0)
    });

  	return this;
  },

  clonePos: function(obj){
    this.css({
      left:obj.offset().left,
      top:obj.offset().top
    });
    return this;
  },

  cloneDim: function(obj){
    this.css({
      width:obj.innerWidth(),
      height:obj.innerHeightF()
    });
    return this;
  },

  left: function(xPos){
    if(xPos==undefined) return this.offset().left;
    else {
    	//2do: call left(xPos,calcRelative=true)
    	//wenn calcRelative(default): 
    	//  xPos ist dann absolute wert. wenn parent relative positioniert ist: lies sein offset und zieh den vom neuen wert ab (top() genauso)
      this.css({left:xPos+"px"});
      return this;
    }
  },

  top: function(yPos){
    if(yPos==undefined) return this.offset().top;
    else {
      this.css({top:yPos+"px"});
      return this;
    }
  },

  moveLeft: function(x){
    return this.css({left:this.left()+x});
  },

  moveTop: function(y){
    return this.css({top:this.top()+y});
  },


  //call like:
  //  setPosTo($(obj), "CENTERX");
  //  setPosTo($(obj), "CENTERX", ["BOTTOMI",-200]);
  //  setPosTo($(obj), ["CENTERX",150], "BOTTOMI");
  //  setPosTo($(obj), ["CENTER",150,200]); //order: xOffset,yOffset
  
  // wenn man called:
  //  setPosTo($(window), "CENTER"); wird es zentriert auf viewport, sichtbar in der mitte egal wo gecrollt ist
  //  setPosTo($(document), "CENTER"); wird es zentriert auf document, in der mitte, also evtl. ausserhalb des viewports

  setPosTo: function(obj, mode1, mode2){
  	isWindowOrDocument = (obj.get(0)==window || obj.get(0)==document);
  	
    var xOffset=0;
    var yOffset=0;

    var xScrollOff = 0;
    var yScrollOff = 0;

    var thisHeight = this.innerHeightF();
    var thisWidth = this.innerWidthF();

		// wegen relativem positionieren: offset des  parents von this berechnen
		var bckpOpac = this.css("opacity");
		var bckpDisplay = this.css("display");
		if(bckpDisplay=="none") this.css({opacity:0,display:"block"}); //show (invisble)
    var thisParentOffsetX = this.left() - parseFloat(this.css("left"));
    var thisParentOffsetY = this.top() - parseFloat(this.css("top"));
    if(bckpDisplay=="none") this.css({opacity:bckpOpac,display:bckpDisplay});

    if(isWindowOrDocument){
	    var objLeft = 0; //2do: kann bei document auch anders sein.
  	  var objTop = 0;
    	var objHeight = obj.height();
    	var objWidth = obj.width();
			
	    xScrollOff = obj.scrollLeft();
  	  yScrollOff = obj.scrollTop();
			
  	} else {
	    var objLeft = obj.left();
  	  var objTop = obj.top();
    	var objHeight = obj.innerHeightF();
    	var objWidth = obj.innerWidthF();
  	}
  	
    for(var i=1;i<=(mode2?2:1);i++){
      param = eval("mode"+i);
      if(typeof param=="object"){
        mode = param[0];
        if(param.length==2){
        	if(mode=="LEFT"||mode=="LEFTI"||mode=="RIGHT"||mode=="RIGHTI"||mode=="CENTERX"||mode=="CENTER"){
        		xOffset = param[1] - thisParentOffsetX + xScrollOff; 
        	} else if(mode=="TOP"||mode=="TOPI"||mode=="BOTTOM"||mode=="BOTTOMI"||mode=="CENTERY"){
        		yOffset = param[1] - thisParentOffsetY + yScrollOff;
        	}
        } else if(param.length>2 && mode=="CENTER"){
       		xOffset = param[1] - thisParentOffsetX + xScrollOff;
       		yOffset = param[2] - thisParentOffsetY + yScrollOff;
        }
      }
      else if(typeof param=="string"){
      	mode = param;
     		xOffset = -thisParentOffsetX + xScrollOff;
     		yOffset = -thisParentOffsetY + yScrollOff;
      }

      mode = mode || "CENTER";
      
      //positionieren
      if(mode=="CENTER"){
      	this.left(objLeft + Math.round((objWidth - thisWidth)/2) + xOffset);
        this.top(objTop + Math.round((objHeight - thisHeight)/2) + yOffset);
      }
      else if(mode=="CENTERX") 	this.left(objLeft + Math.round((objWidth - thisWidth)/2) + xOffset);
      else if(mode=="CENTERY") 	this.top(objTop + Math.round((objHeight - thisHeight)/2) + yOffset);
      else if(mode=="LEFT") 		this.left(objLeft - thisWidth + xOffset); //OUTER
      else if(mode=="LEFTI") 		this.left(objLeft + xOffset); //INNER
      else if(mode=="TOP") 			this.top(objTop - thisHeight + yOffset);
      else if(mode=="TOPI") 		this.top(objTop + yOffset);
      else if(mode=="RIGHT") 		this.left(objLeft + objWidth + xOffset);
      else if(mode=="RIGHTI") 	this.left(objLeft + objWidth - thisWidth + xOffset);
      else if(mode=="BOTTOM") 	this.top(objTop + objHeight + yOffset);
      else if(mode=="BOTTOMI") 	this.top(objTop + objHeight - thisHeight + yOffset);
    }
    
    return this;
  },


  //2do: diese function "skaliert" ein objekt, d.h. alle eigenschaften des objekts und aller childs!
  scale: function(factor,speed){
 		/*
 		//obj:
 		this.animate({
 			width:"-=200",
 			height:"-=100"
 		},speed);

 		//alle childs:
 		divInput.animate({
 			width:"+=400",
 			height:"+=400",
 			fontSize:"30px",
 			lineHeight:"35px"
		},speed);

		//evtl. noch window scrollen (für parallax scroll effekt)
 		$("html,body").animate({ scrollTop: obj.top()-100 }, 120);

 		//evtl. noch position ändern
 		this.animate({
 			left:"-=200",
 			top:"-=100"
 		},speed);

 		*/
  }


});