var INF=9999999;
var WEB_ROOT		="http://admin.maddancer.com";
var WEB_ROOT_KAPELLE="http://admin.maddancer.com/kapelle";
var WEB_ROOT_STORE	="http://admin.maddancer.com/kapelle/modules/store";
var FC_UPLOAD_PATH	=WEB_ROOT+"/upload/files";
var WEB_ROOT_FRONT	="http://maddancer.com";
	




function clearText(thefield){
	if (thefield.defaultValue==thefield.value)
		thefield.value = ""
} 

function JSONHandler(object){
		
		//var err=$("ErrorCode")
		//if(err)
		//	err.innerHTML="";
		debug = 0;
		try{
			if(object!=""){
				//var JSON=eval('('+object+')');
				var JS=JSON.decode(object);
				
				if(JS==null)
					JS=object;
				
				if(typeof(JS)=="object"){
					for( var i in JS){
						
							//alert("I: "+i+"\n VALUES: "+JSON[i])
							if(arguments.length > 1 && arguments[1]=="inputValue")
								$(i).value=JS[i];
						
							else if(arguments.length>1 && arguments[1]=="process"){
								try{
									//alert("ELEMENT: "+JSON[i]);
									eval('('+JS[i]+')');
								}catch(e){
									
									try{
											eval('(['+JS[i]+'])');
									}catch(e){
										var z=e;
										if (debug) alert("ERROR: "+i+"\n"+JS[i]+"\n"+e);
										alert("Error: Request Failed.  Please try again");
										window.location.reload(true);
									}
								}
								//alert(JSON[i]);
							}
							else{
								if(i!=="undefined" && $(i)!==null){
									
									var el=$(i);
									
									if(el){
										
										var tag=el.get('tag');
										if(tag && tag=="input")
											el.value=JS[i];
										else
											el.innerHTML=JS[i];
									}
									else{
										if (debug) alert("Element i is not defined");
										alert("Error: Request Failed.  Please try again");
										window.location.reload(true);
									}
									
								}
								else{
									if (debug) alert("Element i is not defined");
									alert("Error: Request Failed.  Please try again");
									window.location.reload(true);
								}
							}
					}
				}
			}
		}catch(e){
			if (debug) alert("JSON FAILURE: "+object+"\n"+e);
			alert("Error: Request Failed.  Please try again");
			window.location.reload(true);
		}				
		
}


function kapelleAjax(page){
	
		//alert("Args Length: "+arguments.length);
	
		var argsStart=false;
		var argsFinish=false;
		
		
		if(arguments.length>1)
			argsStart=arguments[1];
			
		if(arguments.length>2)
			argsFinish=arguments[2];	
		
	
		try{
				// Opera 8.0+, Firefox, Safari
				ajaxRequest = new XMLHttpRequest();
			} catch (e){
				// Internet Explorer Browsers
				try{
					ajaxRequest = new ActiveXObject("Msxml2.XMLHTTP");
				} catch (e) {
					try{
						ajaxRequest = new ActiveXObject("Microsoft.XMLHTTP");
					} catch (e){
						// Something went wrong
						alert("Your browser broke!");
						return false;
					}
				}
			}
			
		try{
			var t=ajaxRequest
				// Create a function that will receive data sent from the server
			var type=false;
				
			if(arguments.length>3)
				type=arguments[3];
						
			ajaxRequest.onreadystatechange = function(){
	
				try{
					var t=ajaxRequest.readyState;
				}catch(e){
					
					return false;
					
				}
				
				if(ajaxRequest.readyState == 4){
						//alert("Enter");
						var c=type;
						if(type){
							JSONHandler(ajaxRequest.responseText, type);
						}
						else
							JSONHandler(ajaxRequest.responseText);
							
						if(argsFinish){
													
							argsFinish.each(function(el, indx){
									//alert(el);
									try{
										eval(el);				 
									} catch (e){
										
										alert("ERROR EXECUTING: ".el);
										
									}
													 
							});
							
						}
						//document.getElementById(loc).innerHTML = ajaxRequest.responseText;
					}
				}
				
				if(argsStart){
					argsStart.each(function(el, indx){
									//alert(el);
									eval(el);				 
													 
					});
					
							
					
				}
				
				ajaxRequest.open("GET", page, true);
				ajaxRequest.send(null); 
			} catch (e){}
			
			
		return true;
			
}

function kapelleAjaxPost(page, params){
		var ajaxRequest;  // The variable that makes Ajax possible!
				
		try{
			// Opera 8.0+, Firefox, Safari
			ajaxRequest = new XMLHttpRequest();
		} catch (e){
			// Internet Explorer Browsers
			try{
				ajaxRequest = new ActiveXObject("Msxml2.XMLHTTP");
			} catch (e) {
				try{
					ajaxRequest = new ActiveXObject("Microsoft.XMLHTTP");
				} catch (e){
					// Something went wrong
					alert("Your browser broke!");
					return false;
				}
			}
		}
			
		ajaxRequest.open("POST", page, true);

		//Send the proper header information along with the request
		ajaxRequest.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
		ajaxRequest.setRequestHeader("Content-length", params.length);
		ajaxRequest.setRequestHeader("Connection", "close");

		ajaxRequest.onreadystatechange = function() {//Call a function when the state changes.
			if(ajaxRequest.readyState == 4 && ajaxRequest.status == 200) {
				//alert(ajaxRequest.responseText);
				JSONHandler(ajaxRequest.responseText);
			}
		}
		ajaxRequest.send(params);
		
		return true;
			
}


function arraysEqOld(array1, array2) {
   var temp = new Array();
   if ( (!array1[0]) || (!array2[0]) ) { // If either is not an array
      return false;
   }
   if (array1.length != array2.length) {
      return false;
   }
   // Put all the elements from array1 into a "tagged" array
   for (var i=0; i<array1.length; i++) {
      key = (typeof array1[i]) + "~" + array1[i];
   // Use "typeof" so a number 1 isn't equal to a string "1".
      if (temp[key]) { temp[key]++; } else { temp[key] = 1; }
   // temp[key] = # of occurrences of the value (so an element could appear multiple times)
   }
   // Go through array2 - if same tag missing in "tagged" array, not equal
   for (var i=0; i<array2.length; i++) {
      key = (typeof array2[i]) + "~" + array2[i];
      if (temp[key]) {
         if (temp[key] == 0) { return false; } else { temp[key]--; }
      // Subtract to keep track of # of appearances in array2
      } else { // Key didn't appear in array1, arrays are not equal.
         return false;
      }
   }
   // If we get to this point, then every generated key in array1 showed up the exact same
   // number of times in array2, so the arrays are equal.
   return true;
}


function arraysEq(array1, array2) {
	
	
	var a1len=false;
	var a2len=false;
	
	try{
		a1len=array1.length;
		a2len=array2.length;
		
	}catch(e){
		return false;
	}
	
	if(a1len != a2len)
		return false;
		
	var a1Val=array1[0];
	var a2Val=array2[0];
	
	if(a1Val!=a2Val)
		return false;
	else{
		
		if(array1.length==1 & array2.length==1)
			return true;
		else{
			return arraysEq(array1.slice(1), array2.slice(1));
		}
		
		
	} 
	
	return true;
   
}


function getClient(){
	
	//var height = (document.body.height !== undefined) ? document.body.height : document.body.offsetHeight;
	//var width = (document.width !== undefined) ? document.width : document.body.offsetWidth;
	
	
	var x,y; 
	var test1 = document.body.scrollHeight; 
	var test2 = document.body.offsetHeight 
	if (test1 > test2) {
	// all but Explorer Mac 
		 x = document.body.scrollWidth; 
		 y = document.body.scrollHeight; 
	} else  {
		// Explorer Mac; //would also work in Explorer 6 Strict, Mozilla and Safari 
		x = document.body.offsetWidth; 
		y = document.body.offsetHeight; 
	}
	
	
	//alert("Height: "+y);
	
	var r=new Array(x, y);
	
	return r;
	
	
	
}


var windowProgress={
	
	timeout: false,
	
	init: function(){
				
		var img=false;
	
		try{
			img=$('progressBar');
			
			if(img){
			
				var src=img.src;
				//alert("B "+img.src);
				if(src.match('barStep1'))
					img.src=img.src.replace('barStep1','barStep2');
				else if(src.match('barStep2'))
					img.src=img.src.replace('barStep2','barStep1');
					
					
				//alert("A "+img.src);	
				this.timeout=setTimeout("windowProgress.init()", 150);
			
			}
			else{
				
				if(this.timeout)
					clearTimeout(this.timeout);
				
			}
						
					
			
			
		} catch(e){
			
			if(this.timeout)
				clearTimeout(this.timeout);
				
			return true;
			
		}
		
		
	}
	
	
}


function getScrollXY(){


	 var scrOfX = 0, scrOfY = 0;
	 
	 if( typeof( window.pageYOffset ) == 'number' ) {
		 //Netscape compliant
		 scrOfY = window.pageYOffset;
		 scrOfX = window.pageXOffset;
	 } else if( document.body && ( document.body.scrollLeft || document.body.scrollTop ) ) {
		 //DOM compliant
		 scrOfY = document.body.scrollTop;
		 scrOfX = document.body.scrollLeft;
	 } else if( document.documentElement && ( document.documentElement.scrollLeft || document.documentElement.scrollTop ) ) {
		 //IE6 standards compliant mode
		 scrOfY = document.documentElement.scrollTop;
		 scrOfX = document.documentElement.scrollLeft;
	 }
		 return [ scrOfX, scrOfY ];

}



function centerElement(el){
	//alert('begin center');
	var winXY = window.getSize();
	var scrollXY = window.getScroll();
	var centerXY={'x':(winXY.x/2+scrollXY.x), 'y':(winXY.y/2+scrollXY.y)};
	
	var divXY=el.getSize();
	
	var posX= centerXY['x']-parseInt(divXY['x']/2);
	var posY= centerXY['y']-parseInt(divXY['y']/2);	
	
		
	$(el).setStyle('left', posX+"px");
	$(el).setStyle('top',  posY+"px");
	
	//alert('end center');
}

function scrollResponse(){
	
	 var div=false;
	 
	 try{
		 div=document.getElementById('msgWindow');
		 centerElement(div);	
		 return true;
		 
	 }	 catch(e){
		 return false;
	 }
	 
		
}

//response is either the text to display in the window, or if response=process we display the proccessing status of the file




function waitResponse(response){
		
		//alert("TEST");
		
		var w=getWindowSize();
		var c=getClient();
		
		var client=new Array( (w[0]>=c[0]?w[0]:c[0]), (w[1]>=c[1]?w[1]:c[1]) );
		var waitDiv=document.createElement("div");
		waitDiv.id='waitDiv';
		waitDiv.style.width=client[0]+'px';
		waitDiv.style.height=client[1]+'px';
		waitDiv.style.display="block";
		
		
		
		document.body.appendChild(waitDiv);
		
		var msgWindow=document.createElement("div", {
											 
											 			'id': "waitInner",
														'styles': {
																	'position': 'absolute',
																	'display': 'block',
																	'width': '300px',
																	'height': '300px'
														}
											 });
		
		/*
		msgWindow.style.display='block';
		//msgWindow.style.border='1px solid black';
		msgWindow.style.position="absolute";
		msgWindow.style.width="500px";
		*/
		
		
		if(response=="process")
			msgWindow.innerHTML=buildProcessResponseWindow(response);
		else
			msgWindow.innerHTML=buildResponseWindow(response);
		
		document.body.appendChild(msgWindow);

	
		var temp=document.body.innerHTML;
		centerElement(msgWindow);	
    	window.addEvent('scroll', scrollResponse);
		
		if(response=="process")
			windowProcessProgress.init();
		else
			windowProgress.init();		
		
}

/*
waitDiv.id='waitDiv';
		waitDiv.style.width=client[0]+'px';
		waitDiv.style.height=client[1]+'px';
		waitDiv.style.display="block";
		
*/


function fade(type, obj){
	
	
	if(type=="in"){
		return new Fx.Tween(obj, 'opacity', {duration: 1000, wait: false}).start(0.8);
	}
	else if(type=="out"){
		return new Fx.Tween(obj, 'opacity', {duration: 1000, wait: false}).start(0);
	}
	else
		return false;
		
	
	
}




var waitResponse2={
	
	el: false,
	
	show: function(response){
		
		bgFade.show(0.8);
		
		this.el=new Element('div',{
										
										'styles':{
												
													'position': 'fixed',
													'top': -1000,
													'left': -1000,
													'opacity': 0,
													'z-index': 5												
												}
								}).injectInside(document.body);
		
		//this.el.innerHTML=buildResponseWindow(response);
		
		buildResponseWindow(response).inject(this.el);
		
		if(response==''){
			this.el.setStyle('height', 200);
			this.el.setStyle('width', 350);
		}		
			
		centerElement(this.el);
		this.el.fade(1);
			
		
	},
	
	hide: function(){
		
		bgFade.hide();
		this.el.fade(0);
		this.el.destroy();
		
	}
	
	
	
}


var AjaxWaitLoader={
	
	show: function(){	
	
		if ($('ajaxWait')) $('ajaxWait').setStyle('display', 'block');		
	},
	
	hide: function(){
		
		if ($('ajaxWait')) $('ajaxWait').setStyle('display', 'none');
		
	}
	
	
	
}

var bgFade={
	
	currentState: false,
	fadeIn: 0.5,
	fadeOut: 0,
	bg: false,
	
	show: function(){
		
		if(!this.currentState){
			$(document.body).setStyle('overflow', 'hidden');
			this.bg=new Element('div');
			this.bg.setProperty('id', 'bg');
			
			var scroll=window.getScroll();
			var win=window.getSize();
			
			
			this.bg.setStyles({
							'position': 'fixed',
							'background-color': 'Black',
							'top': scroll["y"],
							'left': scroll["x"],
							'width': win["x"],
							'height': win["y"],
							'opacity': 0,
							'z-index': 3	
												   
					   });
			
			this.bg.injectInside(document.body);
			if(arguments.length)
				this.bg.fade(arguments[0]);
			else
				this.bg.fade(this.fadeIn);
			this.currentState=true;
		}
		
		
	},
	
	hide: function(){
		
		if(this.currentState){
			this.bg.fade(this.fadeOut);
			this.bg.destroy();
			this.currentState=false;
			$(document.body).setStyle('overflow', 'auto');
		}		
		
	}
	
			
			
}

/*

function buildResponseWindow(response){
	
	var output="<div class='rc'>";
   		output=output+"<div class='rc_top'><div></div></div>";
		output=output+"<div class='rc_content' id='waitResponseContent'><p><br><br><br>";
			output=output+"<span>";//<img id='progressBar' src='images/waitani.gif' align='middle'>&nbsp;&nbsp;";
			output=output+response+"</span>";
		output=output+"<br><br><br></p></div>";
		output=output+"<div class='rc_bottom'><div></div></div>";
	output=output+"</div>";
	
	return output;
}
*/

function buildWaitResponseWindow(response){
	
	var output="<div class='rc'>";
   		output=output+"<div class='rc_top'><div></div></div>";
		output=output+"<div class='rc_content' id='waitResponseContent'><p><br><br><br>";
			output=output+"<span><img id='progressBar' src='images/waitani.gif' align='middle' style='display: block;'>&nbsp;&nbsp;";
			output=output+response+"</span>";
		output=output+"<br><br><br></p></div>";
		output=output+"<div class='rc_bottom'><div></div></div>";
	output=output+"</div>";
	
	return output;
	
}


function buildResponseWindow(response){
	
	//alert($type(response))
	
	var output="<table border='0'>";
			output=output+"<tr>";
				output=output+"<td style=\"height: 300px;\" id='waitResponseContent' align=\"center\" valign=\"middle\" class=\"StaticText\">"+response+"</td>";
			output=output+"</tr>";
		output=output+"</table>";
					
	var responseWindow=new Element('div',{
								   			'class': 'waitInner',
											'html': output								   
								   
								   })
	
	responseWindow.setProperties({
								 	'align':'center',
									'valign': 'middle'
								 });
	
	//var output="<div class='waitInner' align=\"center\" valign=\"middle\">";
					
	//			output=output+"</div>";
	
	return responseWindow;
	
}




//type=hide || delete
function removeElement(type, id){
	
	//alert("1   "+type+"   "+id);
	
	try{
		
		var el=$(id);
		if(type=='hide'){
			el.style.display="none";
		}
		else if(type=='delete'){
			
			el.innerHTML="";
				
			var par=el.parentNode;
			par.removeChild(el);
			
		}
		
		return true;
			
		
	} catch (e){
		
		
		return false;
		
	}
	
}



function getWindowSize() {
  var myWidth = 0, myHeight = 0;
  if( typeof( window.innerWidth ) == 'number' ) {
    //Non-IE
    myWidth = window.innerWidth;
    myHeight = window.innerHeight;
  } else if( document.documentElement && ( document.documentElement.clientWidth || document.documentElement.clientHeight ) ) {
    //IE 6+ in 'standards compliant mode'
    myWidth = document.documentElement.clientWidth;
    myHeight = document.documentElement.clientHeight;
  } else if( document.body && ( document.body.clientWidth || document.body.clientHeight ) ) {
    //IE 4 compatible
    myWidth = document.body.clientWidth;
    myHeight = document.body.clientHeight;
  }
  
  var v=new Array(myWidth, myHeight );
  return v;
}




function SelectMoveRows(SS1,SS2)
{
    var SelID='';
    var SelText='';
    // Move rows from SS1 to SS2 from bottom to top
    for (i=SS1.options.length - 1; i>=0; i--)
    {
		//alert(i+" "+SS1.options[i].value+"  "+SS1.options[i].value+". ");
		if (SS1.options[i].selected && SS1.options[i].value!=0){
            SelID=SS1.options[i].value;
            SelText=SS1.options[i].text;
            var newRow = new Option(SelText,SelID);
			insertRow(SS2, newRow);
			
            //SS2.options[SS2.length]=newRow;
            SS1.options[i]=null;
			
        }
    }
    //SelectSort(SS2);
	n1=	findSelNone(SS1);
	n2=findSelNone(SS2);
	
	if(SS1.options.length>1 && n1!==false  )
		SS1.options[n1]=null;
	if(SS2.options.length>1 && n2!==false )
		SS2.options[n2]=null;
	if(SS1.options.length==0)
		SS1.options[0]=new Option("None", "0");
	if(SS2.options.length==0)
		SS2.options[0]=new Option("None", "0");	
	
	
	
	
}


function findSelNone(Sel){
	
	for(i=0; i<Sel.options.length; i++){
			if(Sel.options[i].value=="0")
				return i;
		}
		
		return false;
	
}


function insertRow(sList,OP){
	
	
	var index=getInsertionIndex(sList.options, OP.text, 0, sList.options.length-1);
	
	for(var i=index; i<sList.options.length; i++){
		
		var tOpt=sList.options[i];
		sList.options[i]=OP;
		OP=tOpt;
		
	}
	
	sList.options[i]=OP;
		
}

function getInsertionIndex(sList, selText, low, high){	
	
	if(high<low)
		return false;
	
	if(selText < sList[low].text)
		return low;
	else if(selText > sList[high].text)
		return high+1;
		
	  
	var diff=high-low;  
	
	
	var half=low+( diff % 2==0?diff/2:(diff-1)/2);
	
	
	var halfText=sList[half].text
	
	if(selText>halfText){
		if(diff==1)
			return high;
		else
			return getInsertionIndex(sList, selText, half, high);
	}
	else if(selText<halfText){
		if(diff==1)
			return low;
		else
			return getInsertionIndex(sList, selText, low, half);
	}
	else
		return half;
	
	
	
	
	
}


function selectAll(sel){
	
	for(var i=0; i<sel.length; i++){
		
		sel.options[i].selected=true;
		
	}
	
	
}



/*
function SelectSort(SelList)
{
    var ID='';
    var Text='';
    for (x=0; x < SelList.length - 1; x++)
    {
        for (y=x + 1; y < SelList.length; y++)
        {
            if (SelList[x].text > SelList[y].text)
            {
                // Swap rows
                ID=SelList[x].value;
                Text=SelList[x].text;
                SelList[x].value=SelList[y].value;
                SelList[x].text=SelList[y].text;
                SelList[y].value=ID;
                SelList[y].text=Text;
            }
        }
    }
}

*/

function getQS(){
	
	var QS="";
	var strHref = window.location.href;
	if ( strHref.indexOf("?") > -1 )
    	QS = strHref.substr(strHref.indexOf("?")+1).toLowerCase();
    
	return QS;
	
	
}


function isCurrency(str) {
	isPrice = /^(\+|-)?\d*(\.\d\d)?$/;  
	return (str==0 || isPrice.test( str ) );
}

function isFloat(str) {
	var Float = /0||(^((\d*(\.\d*)?)|((\d*\.)?\d+))$)/;
	return Float.test( str );
}

function isInteger(str){
	
	var Int=/^\d+$/;
	return Int.test(str);
	
}

function fixToScreen(el) {
	if (!$(el)) return false;
	if(typeof($(el).origTop) == 'undefined') $(el).origTop = $(el).getPosition().y;
	$(el).setStyle('top', $(el).origTop + window.getScroll().y);
	window.addEvent('scroll', function(event) {
		$(el).setStyle('top', $(el).origTop + window.getScroll().y);
	});
	return true;
}

function unfixToScreen(el) {
	if (!$(el)) return false;
	if($(el).origTop) $(el).setStyle('top', $(el).origTop);
	delete($(el).origTop);
	window.removeEvent('scroll', function(event) {
		$(el).setStyle('top', $(el).origTop + window.getScroll().y);
	});
	return true;
}







/** Image swap functions, someday it'd be fun to move this into an object and do some code cleanup **/

function MM_preloadImages() { //v3.0
  var d=document; if(d.images){ if(!d.MM_p) d.MM_p=new Array();
    var i,j=d.MM_p.length,a=MM_preloadImages.arguments; for(i=0; i<a.length; i++)
    if (a[i].indexOf("#")!=0){ d.MM_p[j]=new Image; d.MM_p[j++].src=a[i];}}
}

function MM_swapImgRestore() { //v3.0
  var i,x,a=document.MM_sr; for(i=0;a&&i<a.length&&(x=a[i])&&x.oSrc;i++) x.src=x.oSrc;
}

function MM_findObj(n, d) { //v4.01
  var p,i,x;  if(!d) d=document; if((p=n.indexOf("?"))>0&&parent.frames.length) {
    d=parent.frames[n.substring(p+1)].document; n=n.substring(0,p);}
  if(!(x=d[n])&&d.all) x=d.all[n]; for (i=0;!x&&i<d.forms.length;i++) x=d.forms[i][n];
  for(i=0;!x&&d.layers&&i<d.layers.length;i++) x=MM_findObj(n,d.layers[i].document);
  if(!x && d.getElementById) x=d.getElementById(n); return x;
}

function MM_swapImage() { //v3.0
  var i,j=0,x,a=MM_swapImage.arguments; document.MM_sr=new Array; for(i=0;i<(a.length-2);i+=3)
   if ((x=MM_findObj(a[i]))!=null){document.MM_sr[j++]=x; if(!x.oSrc) x.oSrc=x.src; x.src=a[i+2];}
}

/**
 * getUrlParams
 *
 * @param name optional, the parameter to search for
 * @return mixed the url parameters as a hash, or the requested parameter if name is passed
 */
function getUrlParams(name) {
	var url = unescape(window.location.href);
	url = url.match(/.*\?&?(.*)$/)[1];
	if(!url) return false;
	var pairs = url.split('&');
	var params = {};
	var found = false;
	pairs.each(function(pair) {
		var param = pair.split('=');
		if (name && param[0] == name) {
			found = param[1];
		}
		else params[param[0]] = param[1];
	});

	if (!found && name) return false;
	else if (found && name) return found;
	else return params;
}




function sendtoclipboard(s,el)	{
		if( window.clipboardData )	{
			clipboardData.setData("Text", s);
		} else {
			ffcopy(el);
		}
	}
	
	
 
function ffcopy(inElement) {
	 
	if (inElement.createTextRange) {
		var range = inElement.createTextRange();
		if (range && BodyLoaded==1)
			  range.execCommand('Copy');
	} 
	else {
		var flashcopier = 'flashcopier';
		if(!$(flashcopier)) {
			var divholder = new Element('div');
		  	divholder.id = flashcopier;
		  	document.body.appendChild(divholder);
		}
		$(flashcopier).set('html', '<embed src="'+WEB_ROOT_KAPELLE+'/common/flash/_clipboard.swf" FlashVars="clipboard='+encodeURIComponent(inElement.value)+'" width="0" height="0" type="application/x-shockwave-flash"></embed>');
		
	}
	
}

function urldecode( str ) {
    // Decodes URL-encoded string  
    // 
    // version: 904.317
    // discuss at: http://phpjs.org/functions/urldecode
    // +   original by: Philip Peterson
    // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
    // +      input by: AJ
    // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
    // +   improved by: Brett Zamir (http://brettz9.blogspot.com)
    // +      input by: travc
    // +      input by: Brett Zamir (http://brettz9.blogspot.com)
    // +   bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
    // %          note 1: info on what encoding functions to use from: http://xkr.us/articles/javascript/encode-compare/
    // *     example 1: urldecode('Kevin+van+Zonneveld%21');
    // *     returns 1: 'Kevin van Zonneveld!'
    // *     example 2: urldecode('http%3A%2F%2Fkevin.vanzonneveld.net%2F');
    // *     returns 2: 'http://kevin.vanzonneveld.net/'
    // *     example 3: urldecode('http%3A%2F%2Fwww.google.nl%2Fsearch%3Fq%3Dphp.js%26ie%3Dutf-8%26oe%3Dutf-8%26aq%3Dt%26rls%3Dcom.ubuntu%3Aen-US%3Aunofficial%26client%3Dfirefox-a');
    // *     returns 3: 'http://www.google.nl/search?q=php.js&ie=utf-8&oe=utf-8&aq=t&rls=com.ubuntu:en-US:unofficial&client=firefox-a'
    
    var histogram = {};
    var ret = str.toString();
    
    var replacer = function(search, replace, str) {
        var tmp_arr = [];
        tmp_arr = str.split(search);
        return tmp_arr.join(replace);
    };
    
    // The histogram is identical to the one in urlencode.
    histogram["'"]   = '%27';
    histogram['(']   = '%28';
    histogram[')']   = '%29';
    histogram['*']   = '%2A';
    histogram['~']   = '%7E';
    histogram['!']   = '%21';
    histogram['%20'] = '+';
    histogram['\u20AC'] = '%80';
    histogram['\u0081'] = '%81';
    histogram['\u201A'] = '%82';
    histogram['\u0192'] = '%83';
    histogram['\u201E'] = '%84';
    histogram['\u2026'] = '%85';
    histogram['\u2020'] = '%86';
    histogram['\u2021'] = '%87';
    histogram['\u02C6'] = '%88';
    histogram['\u2030'] = '%89';
    histogram['\u0160'] = '%8A';
    histogram['\u2039'] = '%8B';
    histogram['\u0152'] = '%8C';
    histogram['\u008D'] = '%8D';
    histogram['\u017D'] = '%8E';
    histogram['\u008F'] = '%8F';
    histogram['\u0090'] = '%90';
    histogram['\u2018'] = '%91';
    histogram['\u2019'] = '%92';
    histogram['\u201C'] = '%93';
    histogram['\u201D'] = '%94';
    histogram['\u2022'] = '%95';
    histogram['\u2013'] = '%96';
    histogram['\u2014'] = '%97';
    histogram['\u02DC'] = '%98';
    histogram['\u2122'] = '%99';
    histogram['\u0161'] = '%9A';
    histogram['\u203A'] = '%9B';
    histogram['\u0153'] = '%9C';
    histogram['\u009D'] = '%9D';
    histogram['\u017E'] = '%9E';
    histogram['\u0178'] = '%9F';

    for (replace in histogram) {
        search = histogram[replace]; // Switch order when decoding
        ret = replacer(search, replace, ret) // Custom replace. No regexing   
    }
    
    // End with decodeURIComponent, which most resembles PHP's encoding functions
    ret = decodeURIComponent(ret);

    return ret;
}
