/*############################################################################
#
#   Auxiliary Javascript Library
#       http://clouddev.org/auxlib
#
#   Copyright (C) 2010 Theodore Surgent III
# 
#   This software is provided 'as-is', without any express or implied
#   warranty.  In no event will the authors be held liable for any damages
#   arising from the use of this software.
# 
#   Permission is granted to anyone to use this software for any purpose,
#   including commercial applications, and to alter it and redistribute it
#   freely, subject to the following restrictions:
# 
#   1. The origin of this software must not be misrepresented; you must not
#      claim that you wrote the original software. If you use this software
#      in a product, an acknowledgment in the product documentation would be
#      appreciated but is not required.
#   2. Altered source versions must be plainly marked as such, and must not be
#      misrepresented as being the original software.
#   3. This notice may not be removed or altered from any source distribution.
#
##############################################################################*/

//Create a aux object to use like a namespace
//Type "aux.extend(window,aux);" to use this namespace
var aux={};

//An enhanced shorthand to the frequently used document.getElementById()
//Supports unlimited parameters and arrays with recursion
aux.$id=function(IDs1,IDs2,IDsN)
{
    var found=[];
	
	var tmp;
	var arg;
    for(var i=0,l=arguments.length;i<l;++i)
    {
        arg=arguments[i];
		if(typeof(arg)=="object" && arg.length) //Array
		{
		    for(var j=0,len=arg.length;j<len;++j)
			{
			    tmp=aux.$id(arg[j]);
				if(typeof(tmp)=="object" && tmp.length) //Array of Elements
				{
					//Don't return same element twice
					for(var k=0,length=tmp.length;k<length;++k)
					    if(aux.search.linear(found,tmp[k])<0)
							found.push(tmp[k]);
				}
				else if(typeof(tmp)!="null" && aux.search.linear(found,tmp)<0) //Element
				    found.push(tmp);
			}
		}
        else if(typeof(arg)=="string")
        {
            //Get the element
            if(document.getElementById) //W3C
                var elm=document.getElementById(arg);
            else if(document.all) //IE
                var elm=document.all[arg];
            else if(document.layers) //NS
                var elm=document.layers[arg];
                
			//Don't return same element twice
            if(elm && aux.search.linear(found,elm)<0)
			{
			    //A DOM node is not an Object in Internet Explorer
			    if(aux.browser.ie())
				    aux.extend(elm,Object);
				found.push(elm);
			}
        }
    }

    if(found.length==0)
        return null;
    else if(found.length==1)
        return found[0];
    else
        return found;
}

//An enhanced shorthand to document.getElementsByClassName()
//Supports unlimited parameters and arrays with recursion
aux.$class=function(classNames1,classNames2,classNamesN)
{
    var result = [];
	
	var arg;
	var elms;
	if(typeof(document.getElementsByClassName)!="undefined")
	{
		for(var i=0,l=arguments.length;i<l;++i)
		{
			arg=arguments[i];
			if(typeof(arg)=="object" && arg.length) //Array
			{
				for(var j=0,len=arg.length;j<len;++j)
				{
				    elms=aux.$class(arg[j]);
					
					//Don't return same element twice
					for(var k=0,length=elms.length;k<length;++k)
					    if(aux.search.linear(result,elms[k])<0)
				            result.push(elms[k]);
				}
			}
			else
			{
			    elms=document.getElementsByClassName(arg);
				
				//Don't return same element twice
				for(var j=0,len=elms.length;j<len;++j)
				    if(aux.search.linear(result,elms[j])<0)
				        result.push(elms[j]);
			}
		}
	}
	else //Fallback
	{
	    function getNames(args)
		{
			var tmp;
		    var names=[];
			
			for(var i=0,l=args.length;i<l;++i)
			{
			    if(typeof(args[i])=="object" && args[i].length) //Array
				    names=names.concat(getNames(args[i]));
				else
				    names.push(args[i]);
			}
			
			return names;
		}
		var names = getNames(arguments).join('|');
		var nameExp = new RegExp('\\b'+names+'\\b');
		elms = document.getElementsByTagName('*');

		for(var i in elms)
			if(nameExp.test(elms[i].className))
				result.push(elms[i]);
	}
	
    return result;
}

//An enhanced shorthand to document.createElement()
//Supports unlimited parameters and arrays with recursion
aux.create=function(elms1,elms2,elmsN)
{
    var elms=[];
    var arg;
	var tmp;
    for(var i=0,l=arguments.length;i<l;++i)
    {
	    arg=arguments[i];
        if(typeof(arg)=="object" && arg.length) //Array
		{
		    for(var j in arg)
			{
			    tmp=aux.create(arg[j]);
				if(tmp===null)
				    break
				else if(typeof(tmp)!="object")
				    elms.push(tmp);
				else
				    elms=elms.concat(tmp);
			}
		}
		else
		    elms.push(document.createElement(arg));
    }
	
	if(elms.length==0)
	    return null;
	else if(elms.length==1)
	    return elms[0];
	else
	    return elms;
}

//An enhanced shorthand to document.body.appendChild()
//Supports unlimited parameters and arrays with recursion
aux.docApp=function(elms1,elms2,elmsN)
{
    var arg;
    for(var i=0,l=arguments.length;i<l;++i)
    {
	    arg=arguments[i];
        if(typeof(arg)=="object" && arg.length) //Array
		    for(var j in arg)
				aux.docApp(arg[j]);
		else
			//A little more compatible
		    document.getElementsByTagName("body")[0].appendChild(arg);
    }
}

//Extends one object with the properties of another
//Supports unlimited parameters
aux.extend=function(objExtend,obj1,obj2,objN)
{
    for(var i=1,l=arguments.length;i<l;++i)
    {
        var objFrom=arguments[i];
        for(var property in objFrom)
		    objExtend[property]=objFrom[property];
    }

    //Return a reference for convenience.
    return objExtend;
}

//#######################################
//#             aux.types               #
//###########################################################
aux.types={};
aux.types.BitSet=function(bits)
{
    var _bits = typeof(bits)=="undefined" ? 8 : bits;
    var _data = new Array(Math.floor((_bits+7)/8));
    var _this = this;
    
    this.getBitSize=function()
    {
        return _bits;
    }
    this.getByteSize=function()
    {
        return Math.floor((_bits+7)/8);
    }
    this.get=function(pos)
    {
        if(pos>=_bits || pos<0)
            throw("Index out of bounds!");
        
        var idx = Math.floor(pos/8);
        var mask = 0x80 >> pos%8;
        
        if(_data[idx] & mask)
            return true;
        return false;
    }
    this.set=function(pos,val)
    {
        if(typeof(pos)=="undefined")
        {
            var i=0,l=Math.floor(_bits/8)
            for(;i<l;++i)
                _data[i]=0xff;
            _data[i] = (0xff << (8-_bits%8)) & 0xff;
            
            return;
        }
        //Expand to fit
        if(pos >= _bits)
        {
            var oldlen = _this.getByteSize();
            var newlen = Math.floor((pos+8)/8);
            for(var i=oldlen;i<newlen;++i)
                _data.push(0);
            
            _bits=pos+1;
        }
        val = typeof(val)=="undefined" ? true : val;
        
        var idx = Math.floor(pos/8);
        var bits = 0x80 >> pos%8;
        var mask = ~bits;
        
        _data[idx] &= mask;
        if(val==true)
            _data[idx] |= bits;
    }
    this.reset=function(bits)
    {
        _bits=bits;
        _data = new Array(Math.floor((_bits+7)/8));
    }
    this.getBits=function(pos,bits)
    {
        var val=0;
        for(var i=0;i<bits;++i)
        {
            if(_this.get(pos+i))
                val |= 1 << (bits-i-1);
        }
        return val;
    }
    this.setBits=function(pos,bits,val)
    {
        for(var i=0;i<bits;++i)
            _this.set(pos+i,(val & 1<<(bits-i-1)) > 0);
    }
    this.getData=function()
    {
        return _data;
    }
	this.setData=function(data,bits)
	{
	    _data=data;
		_bits=bits;
	}
	this.load=function(dump)
	{
	    var len=dump.length;
	    if(len<2)
		{
			_data=[];
			_bits=0;
		    return;
		}
		
		//1 byte header
		var bitsAtEnd = dump[0];
		if(bitsAtEnd==0)
		    _bits = (len-1)*8;
		else
		    _bits = (len-2)*8 + bitsAtEnd;
		_data = dump.slice(1,len);
	}
	this.save=function()
	{
	    //1 byte header
	    var bitsAtEnd = _bits%8;
		var dump = [bitsAtEnd].concat(_data);
		return dump;
	}
    this.toString=function()
    {
        var str ="";
        var i = 0, j, l;
        var lb = 0x80>>(_bits%8);
        if(_bits%8 == 0)
            l=_this.getByteSize();
        else
            l=_this.getByteSize()-1;
        
        for(; i<l; ++i)
        {
            for(j=0x80; j>0; j>>=1)
            {
                if(_data[i] & j)
                    str+='1';
                else
                    str+='0';
            }
        }
        for(j=0x80; j>lb; j>>=1)
        {
            if(_data[i] & j)
                str+='1';
            else
                str+='0';
        }
        return str;
    }
}
aux.types.Color=function(r,g,b,a)
{
    this.r = typeof(r)!="undefined" ? r : 0; //(int) 0-255
    this.g = typeof(g)!="undefined" ? g : 0; //(int) 0-255
    this.b = typeof(b)!="undefined" ? b : 0; //(int) 0-255
    this.a = typeof(a)!="undefined" ? a : 1; //(float) 0.0f - 1.0f

    this.clone=function()
    {
        return new aux.types.Color(this.r,this.g,this.b,this.a);
    }
    this.toCSSRgb=function()
    {
        return "rgb("+this.r+","+this.g+","+this.b+")";
    }
    this.toCSSRgba=function()
    {
        return "rgba("+this.r+","+this.g+","+this.b+","+this.a+")";
    }
    this.set=function(color)
    {
        this.r=color.r;
        this.g=color.g;
        this.b=color.b;
        this.a=color.a;

        return this;
    }
    this.add=function(color)
    {
        var temp=new aux.types.Color();
        
        temp.r = this.r+color.r;
        temp.g = this.g+color.g;
        temp.b = this.b+color.b;
        temp.a = this.a+color.a;

        return temp;
    }
    this.subtract=function(color)
    {
        var temp=new aux.types.Color();
        
        temp.r = this.r-color.r;
        temp.g = this.g-color.g;
        temp.b = this.b-color.b;
        temp.a = this.a-color.a;

        return temp;
    }
    this.invert=function()
    {
        var temp=new aux.types.Color();
        temp.set(this);
        
        temp.r = 127-(temp.r-128);
        temp.g = 127-(temp.g-128);
        temp.b = 127-(temp.b-128);
        temp.a = 0.5-(temp.a-0.5);
        
        return temp;
    }
    this.mid=function(color)
    {
        var temp=new aux.types.Color();
        
        temp.r = Math.floor( this.r*0.5+color.r*0.5 );
        temp.g = Math.floor( this.g*0.5+color.g*0.5 );
        temp.b = Math.floor( this.b*0.5+color.b*0.5 );
        temp.a = this.a*0.5+color.a*0.5;

        return temp;
    }
    this.blend=function(color,t)
    { 
        t= t<0 ? 0 : t>1 ? 1 : t;
        var temp=new aux.types.Color();

        temp.r=Math.floor( this.r+t*(color.r-this.r) );
        temp.g=Math.floor( this.g+t*(color.g-this.g) );
        temp.b=Math.floor( this.b+t*(color.b-this.b) );
        temp.a=this.a+t*(color.a-this.a);

        return temp;
    }
    this.eq=function(color)
    {
        if(this.r==color.r && this.g==color.g && this.b==color.b && this.a==color.a)
            return true;
        return false;
    }
	this.toString=this.toCSSRgba;
}
aux.types.Point=function(x,y)
{
    this.x = typeof(x) == "undefined" ? 0 : x;
    this.y = typeof(y) == "undefined" ? 0 : y;
    
    this.clone=function()
    {
        return new aux.types.Point(this.x,this.y);
    }
    this.set=function(point)
    {
        this.x=point.x;
        this.y=point.y;
    }
    this.dist=function(point)
    {
        return Math.sqrt( Math.pow(this.x-point.x,2) + Math.pow(this.y-point.y,2) );
    }
	this.dot=function(point)
	{
	    return this.x*point.x + this.y*point.y;
	}
    this.lerp=function(point,s)
    {
        return new aux.types.Point( this.x+s*(point.x-this.x), this.y+s*(point.y-this.y) );
    }
    this.eq=function(point)
    {
        if(this.x==point.x && this.y==point.y)
            return true;
        return false;
    }
	this.toString=function()
	{
	    return '('+this.x+','+this.y+')';
	}
}
aux.types.Rect=function(x,y,width,height)
{
    this.x = typeof(x) == "undefined" ? 0 : x;
    this.y = typeof(y) == "undefined" ? 0 : y;
    this.width = typeof(width)    == "undefined" ? 0 : width;
    this.height = typeof(height) == "undefined" ? 0 : height;
    
    this.clone=function()
    {
        return new aux.types.Rect(this.x,this.y,this.width,this.height);
    }
    this.set=function(rect)
    {
        this.x=rect.x;
        this.y=rect.y;
        this.width=rect.width;
        this.height=rect.height;
    }
    this.eq=function(rect)
    {
        if(this.x==rect.x && this.y==rect.y && this.width==rect.width && this.height==rect.height)
            return true;
        return false;
    }
	this.toString=function()
	{
	    return this.x+','+this.y+' '+this.width+'x'+this.height;
	}
}
aux.types.Pair=function(key,value)
{
    this.key = typeof(key) == "undefined" ? "" : key;
    this.value = typeof(value) == "undefined" ? null : value;
    
    this.clone=function()
    {
        return new aux.types.Pair(this.key,this.value);
    }
    this.set=function(pair)
    {
        this.key=pair.key;
        this.value=pair.value;
    }
    this.eq=function(pair)
    {
        if(this.key==pair.key && this.value==pair.value)
            return true;
        return false;
    }
	this.toString=function()
	{
		return this.key+':'+this.value;
	}
}

//#######################################
//#           aux.colors           #
//###########################################################
aux.colors={};

aux.colors.aliceBlue=new aux.types.Color(240,248,255);
aux.colors.antiqueWhite=new aux.types.Color(250,235,215);
aux.colors.aqua=new aux.types.Color(0,255,255);
aux.colors.aquamarine=new aux.types.Color(127,255,212);
aux.colors.azure=new aux.types.Color(240,255,255);
aux.colors.beige=new aux.types.Color(245,245,220);
aux.colors.bisque=new aux.types.Color(255,228,196);
aux.colors.black=new aux.types.Color();
aux.colors.blanchedAlmond=new aux.types.Color(255,235,205);
aux.colors.blue=new aux.types.Color(0,0,255);
aux.colors.blueViolet=new aux.types.Color(138,43,226);
aux.colors.brown=new aux.types.Color(165,42,42);
aux.colors.burlyWood=new aux.types.Color(222,184,135);
aux.colors.cadetBlue=new aux.types.Color(95,158,160);
aux.colors.chartreuse=new aux.types.Color(127,255);
aux.colors.chocolate=new aux.types.Color(210,105,30);
aux.colors.coral=new aux.types.Color(255,127,80);
aux.colors.cornflowerBlue=new aux.types.Color(100,149,237);
aux.colors.cornsilk=new aux.types.Color(255,248,220);
aux.colors.crimson=new aux.types.Color(220,20,60);
aux.colors.cyan=new aux.types.Color(0,255,255);
aux.colors.darkBlue=new aux.types.Color(0,0,139);
aux.colors.darkCyan=new aux.types.Color(0,139,139);
aux.colors.darkGoldenRod=new aux.types.Color(184,134,11);
aux.colors.darkGray=new aux.types.Color(169,169,169);
aux.colors.darkGreen=new aux.types.Color(0,100);
aux.colors.darkKhaki=new aux.types.Color(189,183,107);
aux.colors.darkMagenta=new aux.types.Color(139,0,139);
aux.colors.darkOliveGreen=new aux.types.Color(85,107,47);
aux.colors.darkorange=new aux.types.Color(255,140);
aux.colors.darkOrchid=new aux.types.Color(153,50,204);
aux.colors.darkRed=new aux.types.Color(139);
aux.colors.darkSalmon=new aux.types.Color(233,150,122);
aux.colors.darkSeaGreen=new aux.types.Color(143,188,143);
aux.colors.darkSlateBlue=new aux.types.Color(72,61,139);
aux.colors.darkSlateGray=new aux.types.Color(47,79,79);
aux.colors.darkTurquoise=new aux.types.Color(0,206,209);
aux.colors.darkViolet=new aux.types.Color(148,0,211);
aux.colors.deepPink=new aux.types.Color(255,20,147);
aux.colors.deepSkyBlue=new aux.types.Color(0,191,255);
aux.colors.dimGray=new aux.types.Color(105,105,105);
aux.colors.dodgerBlue=new aux.types.Color(30,144,255);
aux.colors.fireBrick=new aux.types.Color(178,34,34);
aux.colors.floralWhite=new aux.types.Color(255,250,240);
aux.colors.forestGreen=new aux.types.Color(34,139,34);
aux.colors.fuchsia=new aux.types.Color(255,0,255);
aux.colors.gainsboro=new aux.types.Color(220,220,220);
aux.colors.ghostWhite=new aux.types.Color(248,248,255);
aux.colors.gold=new aux.types.Color(255,215);
aux.colors.goldenRod=new aux.types.Color(218,165,32);
aux.colors.gray=new aux.types.Color(128,128,128);
aux.colors.green=new aux.types.Color(0,128);
aux.colors.greenYellow=new aux.types.Color(173,255,47);
aux.colors.honeyDew=new aux.types.Color(240,255,240);
aux.colors.hotPink=new aux.types.Color(255,105,180);
aux.colors.indianRed=new aux.types.Color(205,92,92);
aux.colors.indigo=new aux.types.Color(75,0,130);
aux.colors.ivory=new aux.types.Color(255,255,240);
aux.colors.khaki=new aux.types.Color(240,230,140);
aux.colors.lavender=new aux.types.Color(230,230,250);
aux.colors.lavenderBlush=new aux.types.Color(255,240,245);
aux.colors.lawnGreen=new aux.types.Color(124,252);
aux.colors.lemonChiffon=new aux.types.Color(255,250,205);
aux.colors.lightBlue=new aux.types.Color(173,216,230);
aux.colors.lightCoral=new aux.types.Color(240,128,128);
aux.colors.lightCyan=new aux.types.Color(224,255,255);
aux.colors.lightGoldenRodYellow=new aux.types.Color(250,250,210);
aux.colors.lightGray=new aux.types.Color(211,211,211);
aux.colors.lightGreen=new aux.types.Color(144,238,144);
aux.colors.lightPink=new aux.types.Color(255,182,193);
aux.colors.lightSalmon=new aux.types.Color(255,160,122);
aux.colors.lightSeaGreen=new aux.types.Color(32,178,170);
aux.colors.lightSkyBlue=new aux.types.Color(135,206,250);
aux.colors.lightSlateGray=new aux.types.Color(119,136,153);
aux.colors.lightSteelBlue=new aux.types.Color(176,196,222);
aux.colors.lightYellow=new aux.types.Color(255,255,224);
aux.colors.lime=new aux.types.Color(0,255);
aux.colors.limeGreen=new aux.types.Color(50,205,50);
aux.colors.linen=new aux.types.Color(250,240,230);
aux.colors.magenta=new aux.types.Color(255,0,255);
aux.colors.maroon=new aux.types.Color(128);
aux.colors.mediumAquaMarine=new aux.types.Color(102,205,170);
aux.colors.mediumBlue=new aux.types.Color(0,0,205);
aux.colors.mediumOrchid=new aux.types.Color(186,85,211);
aux.colors.mediumPurple=new aux.types.Color(147,112,216);
aux.colors.mediumSeaGreen=new aux.types.Color(60,179,113);
aux.colors.mediumSlateBlue=new aux.types.Color(123,104,238);
aux.colors.mediumSpringGreen=new aux.types.Color(0,250,154);
aux.colors.mediumTurquoise=new aux.types.Color(72,209,204);
aux.colors.mediumVioletRed=new aux.types.Color(199,21,133);
aux.colors.midnightBlue=new aux.types.Color(25,25,112);
aux.colors.mintCream=new aux.types.Color(245,255,250);
aux.colors.mistyRose=new aux.types.Color(255,228,225);
aux.colors.moccasin=new aux.types.Color(255,228,181);
aux.colors.navajoWhite=new aux.types.Color(255,222,173);
aux.colors.navy=new aux.types.Color(0,0,128);
aux.colors.oldLace=new aux.types.Color(253,245,230);
aux.colors.olive=new aux.types.Color(128,128);
aux.colors.oliveDrab=new aux.types.Color(107,142,35);
aux.colors.orange=new aux.types.Color(255,165);
aux.colors.orangeRed=new aux.types.Color(255,69);
aux.colors.orchid=new aux.types.Color(218,112,214);
aux.colors.paleGoldenRod=new aux.types.Color(238,232,170);
aux.colors.paleGreen=new aux.types.Color(152,251,152);
aux.colors.paleTurquoise=new aux.types.Color(175,238,238);
aux.colors.paleVioletRed=new aux.types.Color(216,112,147);
aux.colors.papayaWhip=new aux.types.Color(255,239,213);
aux.colors.peachPuff=new aux.types.Color(255,218,185);
aux.colors.peru=new aux.types.Color(205,133,63);
aux.colors.pink=new aux.types.Color(255,192,203);
aux.colors.plum=new aux.types.Color(221,160,221);
aux.colors.powderBlue=new aux.types.Color(176,224,230);
aux.colors.purple=new aux.types.Color(128,0,128);
aux.colors.red=new aux.types.Color(255);
aux.colors.rosyBrown=new aux.types.Color(188,143,143);
aux.colors.royalBlue=new aux.types.Color(65,105,225);
aux.colors.saddleBrown=new aux.types.Color(139,69,19);
aux.colors.salmon=new aux.types.Color(250,128,114);
aux.colors.sandyBrown=new aux.types.Color(244,164,96);
aux.colors.seaGreen=new aux.types.Color(46,139,87);
aux.colors.seaShell=new aux.types.Color(255,245,238);
aux.colors.sienna=new aux.types.Color(160,82,45);
aux.colors.silver=new aux.types.Color(192,192,192);
aux.colors.skyBlue=new aux.types.Color(135,206,235);
aux.colors.slateBlue=new aux.types.Color(106,90,205);
aux.colors.slateGray=new aux.types.Color(112,128,144);
aux.colors.snow=new aux.types.Color(255,250,250);
aux.colors.springGreen=new aux.types.Color(0,255,127);
aux.colors.steelBlue=new aux.types.Color(70,130,180);
aux.colors.tan=new aux.types.Color(210,180,140);
aux.colors.teal=new aux.types.Color(0,128,128);
aux.colors.thistle=new aux.types.Color(216,191,216);
aux.colors.tomato=new aux.types.Color(255,99,71);
aux.colors.turquoise=new aux.types.Color(64,224,208);
aux.colors.violet=new aux.types.Color(238,130,238);
aux.colors.wheat=new aux.types.Color(245,222,179);
aux.colors.white=new aux.types.Color(255,255,255);
aux.colors.whiteSmoke=new aux.types.Color(245,245,245);
aux.colors.yellow=new aux.types.Color(255,255);
aux.colors.yellowGreen=new aux.types.Color(154,205,50);

//#######################################
//#          aux.compare            #
//###########################################################
aux.compare={};
aux.compare.numGreater=function(numLHS,numRHS)
{
    if(numLHS > numRHS)
        return 1;
    if(numLHS < numRHS)
        return -1;

    //Same
    return 0;
}
aux.compare.numLess=function(numLHS,numRHS)
{
    if(numLHS < numRHS)
        return 1;
    if(numLHS > numRHS)
        return -1;

    //Same
    return 0;
}
aux.compare.strGreater=function(strLHS,strRHS)
{
    var i=0;
    while(i<strLHS.length && i<strRHS.length)
    {
        if(strLHS.charCodeAt(i) > strRHS.charCodeAt(i))
            return 1;
        else if(strLHS.charCodeAt(i) < strRHS.charCodeAt(i))
            return -1
        ++i;
    }
    
    if(strLHS.length < strRHS.length)
        return 1;
        
    //Same
    return 0;
}
aux.compare.strLess=function(strLHS,strRHS)
{
    var i=0;
    while(i<strLHS.length && i<strRHS.length)
    {
        if(strLHS.charCodeAt(i) < strRHS.charCodeAt(i))
            return 1;
        else if(strLHS.charCodeAt(i) > strRHS.charCodeAt(i))
            return -1
        ++i;
    }
    
    if(strLHS.length > strRHS.length)
        return 1;
        
    //Same
    return 0;
}
aux.compare.strGreaterClosest=function(strLHS,strRHS)
{
    strLHS=strLHS.toUpperCase();
    strRHS=strRHS.toUpperCase();

    var i=0;
    while(i<strLHS.length && i<strRHS.length)
    {
        if(strLHS.charCodeAt(i) > strRHS.charCodeAt(i))
            return 1;
        else if(strLHS.charCodeAt(i) < strRHS.charCodeAt(i))
            return -1
        ++i;
    }
    
    //Same
    return 0;
}
aux.compare.strLessClosest=function(strLHS,strRHS)
{
    strLHS=strLHS.toUpperCase();
    strRHS=strRHS.toUpperCase();

    var i=0;
    while(i<strLHS.length && i<strRHS.length)
    {
        if(strLHS.charCodeAt(i) < strRHS.charCodeAt(i))
            return 1;
        else if(strLHS.charCodeAt(i) > strRHS.charCodeAt(i))
            return -1
        ++i;
    }
    
    //Same
    return 0;
}

//#######################################
//#           aux.ajax              #
//###########################################################
aux.ajax={};
aux.ajax.getXmlHttpRequest=function()
{
    var xmlHttpObj=null;

    if(window.XMLHttpRequest)
    {
            xmlHttpObj=new XMLHttpRequest();
    }
    else if(window.ActiveXObject)
    {
        try
        {
            xmlHttpObj=new ActiveXObject("Msxml2.XMLHTTP");
        }
        catch(e)
        {
            try
            {
                xmlHttpObj=new ActiveXObject("Microsoft.XMLHTTP");
            }
            catch(e)
            {
                return null;
            }
        }
    }
    else
    {
        return null;
    }
    return xmlHttpObj;
};
aux.ajax.makeParam=function(objAsHash)
{
    var params=[];
    for(var key in objAsHash)
        params.push( new aux.types.Pair(key,objAsHash[key]) );
    return params;
};
aux.ajax.request=function(callback,url,param,get,responseIsXML,bustCache)
{
    var Request=function(callback,url,param,get,responseIsXML,bustCache)
    {
        param = typeof(param)=="undefined" ? null : param;
        get = typeof(get)=="undefined" ? true : get;
        responseIsXML = typeof(responseIsXML)=="undefined" ? true : responseIsXML;
        bustCache = typeof(bustCache)=="undefined" ? false : bustCache;

        if(get==true && bustCache==true)
            param.push( new aux.types.Pair("BustCache",new Date().getTime()) );

        var xmlHttpObj=aux.ajax.getXmlHttpRequest();
        if(xmlHttpObj.overrideMimeType)
        {
            if(responseIsXML==true)
                xmlHttpObj.overrideMimeType('text/xml');
            else
                xmlHttpObj.overrideMimeType('text/plain');
        }
        
        var _callback=function()
        {
            if(callback!=null && typeof(xmlHttpObj)!="undefined")
                if(xmlHttpObj.readyState==4 || xmlHttpObj.readyState=="complete")
                    if(xmlHttpObj.status==200)
                        callback(xmlHttpObj);
        }
        xmlHttpObj.onreadystatechange=_callback;

        var paramStr="";
		for(var i=0;i<param.length;++i)
		{
			if(paramStr.length==0)
			{
				if(param[i].value!=null)
					paramStr=param[i].key+"="+encodeURIComponent(param[i].value.toString());
				else
					paramStr=param[i].key;
			}
			else
			{
				if(param[i].value!=null)
					paramStr+="&"+param[i].key+"="+encodeURIComponent(param[i].value.toString());
				else
					paramStr+="&"+param[i].key;
			}
		}

        if(get==true)
        {
            xmlHttpObj.open("GET",url+"?"+paramStr,true);
            xmlHttpObj.send(null);
        }
        else
        {
            xmlHttpObj.open("POST", url, true);
            xmlHttpObj.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
            xmlHttpObj.setRequestHeader("Content-length", paramStr.length);
            xmlHttpObj.setRequestHeader("Connection", "close");
            xmlHttpObj.send(paramStr);
        }
    };
    new Request(callback,url,param,get,responseIsXML,bustCache);
};
aux.ajax.isSupported=function()
{
    return aux.ajax.getXmlHttpRequest()!=null;
};

//#######################################
//#           aux.bezier            #
//###########################################################
aux.bezier=new function()
{
    this.liner=function(p0,p1,t)
    {
        //Clamp t in the range 0-1.
        t= t<0 ? 0: t>1 ? 1:t;

        return (1-t)*p0+t*p1;
    }
    this.quadratic=function(p0,p1,p2,t)
    {
        //Clamp t in the range 0-1.
        t= t<0 ? 0: t>1 ? 1:t;

        var x=1-t;
        return x*x*p0+2*t*x*p1+t*t*p2;
    }
    this.cubic=function(p0,p1,p2,p3,t)
    {
        //Clamp t in the range 0-1.
        t= t<0 ? 0: t>1 ? 1:t;

        var x=1-t;
        return p0*(x*x*x)+3*p1*t*(x*x)+3*p2*t*t*x+p3*t*t*t;
    }
    var quadraticSplineSegment=function(p0,p1,p2,t)
    {
        //Clamp t in the range 0-1
        t= t<0 ? 0: t>1 ? 1:t;

        var p0_5=(p0+p1)/2;
        var p1_5=(p1+p2)/2;
        return aux.bezier.quadratic(p0_5,p1,p1_5,t);
    }
    var cubicSplineSegment=function(p0,p1,p2,p3,t)
    {
        //Clamp t in the range 0-1
        t= t<0 ? 0: t>1 ? 1:t;

        var a=(p0+4*p1+p2)/6;
        var b=(2*p1+p2)/3;
        var c=(2*p2+p1)/3;
        var d=(4*p2+p1+p3)/6;
        return aux.bezier.cubic(a,b,c,d,t);
    }
    this.linerSplien=function(pts,t)
    {
        if(pts.length<2)
        return null;
    
        //Clamp t in the range 0-1.
        t= t<0 ? 0: t>1 ? 1:t;

        var segments=pts.length-1;
        var distPerSegment=1/segments;
        var currentSegment=Math.floor(t/distPerSegment);
    
        //Only for t==1;
        if(currentSegment>=segments)
            currentSegment--;
    
        var newT=(t-currentSegment*distPerSegment)*segments;
        return aux.bezier.liner(pts[currentSegment],pts[currentSegment+1],newT);
    }
    this.quadraticSpline=function(pts,t)
    {
        if(pts.length<3)
        return null;
    
        //Clamp t in the range 0-1
        t= t<0 ? 0: t>1 ? 1:t;

        var segments=pts.length-2;
        var distPerSegment=1/segments;
        var currentSegment=Math.floor(t/distPerSegment);
    
        //Only for t==1;
        if(currentSegment>=segments)
            currentSegment--;
    
        var newT=(t-currentSegment*distPerSegment)*segments;
        return quadraticSplineSegment(pts[currentSegment],pts[currentSegment+1],pts[currentSegment+2],newT);
    }
    this.cubicSpline=function(pts,t)
    {
        if(pts.length<4)
        return null;
    
        //Clamp t in the range 0-1
        t= t<0 ? 0: t>1 ? 1:t;

        var segments=pts.length-3;
        var distPerSegment=1/segments;
        var currentSegment=Math.floor(t/distPerSegment);
    
        //Only for t==1;
        if(currentSegment>=segments)
            currentSegment--;
    
        var newT=(t-currentSegment*distPerSegment)*segments;
        return cubicSplineSegment(pts[currentSegment],pts[currentSegment+1],pts[currentSegment+2],pts[currentSegment+3],newT);
    }
};

//#######################################
//#           aux.browser           #
//###########################################################
aux.browser=new function()
{   
    this.firefox=function()
    {
        if(navigator.appName=="Netscape" &&
        navigator.userAgent.indexOf("Firefox") >= 0)
            return true;
        else
            return false;
    }
    this.ie=function()
    {
        if(navigator.appName=="Microsoft Internet Explorer" &&
        navigator.userAgent.indexOf("MSIE") >= 0)
            return true;
        else
            return false;
    }
    this.opera=function()
    {
        if(navigator.appName=="Opera" &&
        navigator.userAgent.indexOf("Opera") >= 0)
            return true;
        else
            return false;
    }
    this.safari=function()
    {
        if(navigator.appName=="Netscape" &&
        navigator.userAgent.indexOf("Safari") >= 0 &&
        navigator.userAgent.indexOf("Chrome") < 0)
            return true;
        else
            return false;
    }
    this.chrome=function()
    {
        if(navigator.appName=="Netscape" &&
        navigator.userAgent.indexOf("Chrome") >= 0)
            return true;
        else
            return false;
    }
    this.version=function()
    {
        if(aux.browser.firefox())
        {
             var pattern="Firefox/"; 
             var agent=navigator.userAgent;
             var index=agent.indexOf(pattern)+pattern.length;
             return parseFloat(agent.substring(index,agent.length));
        }
        else if(aux.browser.ie())
        {
             var pattern="compatible; MSIE "; 
             var version=navigator.appVersion;
             var index=version.indexOf(pattern)+pattern.length;
             return parseFloat(version.substring(index,version.length));
        }
        else if(aux.browser.opera())
        {
             return parseFloat(navigator.appVersion);
        }
        else if(aux.browser.safari())
        {
             var pattern="Version/";
             var version=navigator.appVersion;
             var index=version.indexOf(pattern)+pattern.length;
             return parseFloat(version.substring(index,version.length));
        }
        else if(aux.browser.chrome())
        {
             var pattern="Chrome/";
             var version=navigator.appVersion;
             var index=version.indexOf(pattern)+pattern.length;
             return parseFloat(version.substring(index,version.length));
        }
        return null;
    }
    this.mac=function()
    {
        return navigator.platform.indexOf("Mac") >= 0;
    }
    this.linux=function()
    {
        return navigator.platform.indexOf("Linux") >= 0;
    }
    this.windows=function()
    {
        return navigator.platform.indexOf("Win") >= 0;
    }
};

//#######################################
//#          aux.convert            #
//###########################################################
aux.convert={};
aux.convert.baseToDec=function(numIn,itsBase)
{
    numIn=numIn.toUpperCase();
    var baseChars=

['0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'];
    if((itsBase<2)||(itsBase>baseChars.length))
        return "invaled base";
    numIn=numIn.split('');
    newNum=0;
    for(var i=0;i<numIn.length;i++)
    {
        newNum+=aux.search.linear(baseChars,numIn[i])*Math.pow(itsBase,numIn.length-1-i);
    }
    return newNum;
};
aux.convert.decToBase=function(decIn,base,digits)
{
    var baseChars=

['0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'];
    if((base<2)||(base>baseChars.length))
        return "invaled base";
    newBase=[];
    for(var i=0;i<digits;i++)
    {
        newBase[i]=baseChars[Math.floor((decIn%Math.pow(base,digits-i))/(Math.pow(base,digits-1-i)))];
    }
    return newBase.join('');
};
aux.convert.decToHexColor=function(color)
{
    hexString="#";
        hexString+=aux.convert.decToBase(color.r,16,2);
        hexString+=aux.convert.decToBase(color.g,16,2);
        hexString+=aux.convert.decToBase(color.b,16,2);
    return hexString;
};
aux.convert.hexToDecColor=function(hexRgbIn) 
{
    var result='';
    var offset=0;
    if(hexRgbIn.substr(0,1)=='#')
        offset=1;
    else if(hexRgbIn.substr(0,2).toLowerCase()=='0x')
        offset=2;

    var r,g,b;

    if(hexRgbIn.length>=6)
    {
        r=aux.convert.BaseToDec(hexRgbIn.substr(offset+0,2),16);
        g=aux.convert.BaseToDec(hexRgbIn.substr(offset+2,2),16);
        b=aux.convert.BaseToDec(hexRgbIn.substr(offset+4,2),16);
    }
    else
    {
        r=aux.convert.BaseToDec(hexRgbIn.substr(offset+0,1),16)*16;
        g=aux.convert.BaseToDec(hexRgbIn.substr(offset+1,1),16)*16;
        b=aux.convert.BaseToDec(hexRgbIn.substr(offset+2,1),16)*16;
    }

    return new aux.types.Color(r,g,b);
};
aux.convert.degToRad=function(degIn)
{
    return (degIn*Math.PI)/180;
};
aux.convert.radToDeg=function(radIn)
{
    return (radIn*180)/Math.PI;
};
aux.convert.hsvToRgb=function(h,s,v)
{
    h = h<0 ? 0 : h>360 ? 360 : h;
    s = s<0 ? 0 : s>1 ? 1 : s;
    v = v<0 ? 0 : v>1 ? 1 : v;
    var i=h/60;
    var u=Math.floor(i);
    var f=i-u;
    var p=v*(1-s);
    var q=v*(1-f*s);
    var t=v*(1-(1-f)*s);

    var r,g,b;

    switch(u)
    {
        case 0 : r=v,g=p,b=t; break;
        case 1 : r=q,g=v;b=p; break;
        case 2 : r=p,g=v;b=t; break;
        case 3 : r=p,g=q;b=v; break;
        case 4 : r=t,g=p;b=v; break;
        default: r=v,g=p;b=q;
    }

    return new aux.types.Color(Math.floor(r*255),Math.floor(g*255),Math.floor(b*255));
};
aux.convert.rgbToHsv=function(r,g,b)
{
    r = r<0 ? 0 : r>255 ? 1 : r/255;
    g = g<0 ? 0 : g>255 ? 1 : g/255;
    b = b<0 ? 0 : b>255 ? 1 : b/255;
    var h,s,v,min=Math.min(r,g,b),max=Math.max(r,g,b);

    if(max==min)
        h=0;
    else if(max==r&&g>=b)
        h=60*((g-b)/(max-min));
    else if(max==r)
        h=60*((g-b)/(max-min))+360;
    else if(max==g)
        h=60*((b-r)/(max-min))+120;
    else
        h=60*((r-g)/(max-min))+240;

    if(max==0)
        s=0;
    else
        s=1-min/max;

    return {h:h,s:s,v:max};
};
aux.convert.polarToCart=function(radius,angle)
{
    var x=Math.cos(angle)*radius;
    var y=Math.sin(angle)*radius;
    return new aux.types.Point(x,y);
};
aux.convert.cartToPolar=function(x,y)
{
    var angle=Math.atan2(y,x);
    var radius=Math.sqrt( x*x + y*y );
    return { radius:radius, angle:angle };
};

//#######################################
//#            aux.cookie           #
//###########################################################
aux.cookie={};
aux.cookie.clear=function(name)
{
    if(navigator.cookieEnabled)
    {
        aux.cookie.set(name,"",-1);
        return false;
    }
    return true;
};
aux.cookie.clearAll=function()
{
    var cookies=this.getAll();
    for(var i=0;i<cookies.length;i++)
    {
        aux.cookie.clear(cookies[i][0]);
    }
};
aux.cookie.get=function(name)
{
    if(navigator.cookieEnabled&&document.cookie.length)
    {
        var cBegin=document.cookie.indexOf(name+"=");
        if(cBegin>0&&/[ ;]/.test(document.cookie.substr(cBegin-1,1))==false)
            return null;
        if(cBegin!=-1)
        {
            cBegin+=name.length+1;
            var cEnd=document.cookie.indexOf(";",cBegin);
            cEnd= cEnd==-1 ?    document.cookie.length : cEnd;
            return unescape(document.cookie.substring(cBegin,cEnd));
        }
        return null;
    }
    return null;
};
aux.cookie.getAll=function()
{
    var temp=[];
    if(navigator.cookieEnabled&&document.cookie.length)
    {
        temp=document.cookie.split('; ');
        var len=temp.length;
        for(var i=0;i<len;i++)
        {
            temp[i]=temp[i].split('=');
        }
    }
    return temp;
};
aux.cookie.set=function(name,value,daysActive)
{
    if(navigator.cookieEnabled)
    {
        var exp;
        if (daysActive)
        {
            var date=new Date();
            date.setTime(date.getTime()+daysActive*24*60*60*1000);
            exp="; expires="+date.toGMTString();
        }
        else
            exp="";
        var theCookie=name+"="+escape(value)+exp+"; path=/";
        document.cookie=theCookie;
        return false;
    }
    return true;
};

//#######################################
//#              aux.encode             #
//###########################################################
aux.encode={};
aux.encode.strToData=function(str)
{
    var data=[];
	for(var i=0,l=str.length;i<l;++i)
	    data[i] = str.charCodeAt(i);
	return data;
}
aux.encode.hex=function(data)
{
	var str="";
	var nib1,nib2;
	var HEX_TABLE=['0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'];
	
	for(var i=0,l=data.length;i<l;++i)
	{
		nib1 = (data[i]>>4) & 0xF;
		nib2 = data[i] & 0xF;
		str += HEX_TABLE[nib1] + HEX_TABLE[nib2];
	}
	
	return str;
}
aux.encode.base64=function(data)
{
	// Base 64 encoding reference
	// http://tools.ietf.org/html/rfc4648
	
	var str="";
	var q1,q2,q3,q4;
	var BASE64_TABLE=['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',
					  'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f',
					  'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',
					  'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/'];
	
	for(var i=0,l=data.length;i<l;i+=3)
	{
		if(l-i > 2)
		{
			q1 = (data[i]>>2) & 0x3f;
			q2 = ((data[i]&0x3) << 4) | ((data[i+1]>>4) & 0xf);
			q3 = ((data[i+1]&0xf) << 2) | ((data[i+2]>>6) & 0x3);
			q4 = data[i+2] & 0x3f;
			
			str += BASE64_TABLE[q1] + BASE64_TABLE[q2] + BASE64_TABLE[q3] + BASE64_TABLE[q4];
		}
		else if(l-i == 2)
		{
			q1 = (data[i]>>2) & 0x3f;
			q2 = ((data[i]&0x3) << 4) | ((data[i+1]>>4) & 0xf);
			q3 = ((data[i+1]&0xf) << 2);
			
			str += BASE64_TABLE[q1] + BASE64_TABLE[q2] + BASE64_TABLE[q3] + '=';
		}
		else //if(l-i == 1)
		{
			q1 = (data[i]>>2) & 0x3f;
			q2 = ((data[i]&0x3) << 4);
			
			str += BASE64_TABLE[q1] + BASE64_TABLE[q2] + "==";
		}
	}
	
	return str;
}
aux.encode.rle=function(data)
{
	// RLE encoding reference
    // http://www.arturocampos.com/ac_rle.html
	
    var i=0,l=data.length,pos;
	var encoded=[];
    while(i<l)
	{
	    if(i-1==1)
		    encoded.push(data[i++]);
		else
		{
			if(data[i]==data[i+1])
			{
				encoded.push(data[i++]);
				encoded.push(data[i++]);
				pos=i;
				while(data[i]==data[i-1] && i<l && (i-pos)<255) ++i;
				encoded.push(i-pos);
			}
			else
		        encoded.push(data[i++]);
		}
	}
	
	return encoded;
}
aux.encode.lzss=function(data)
{
	/*
	* This implementation uses a 16-4096 byte variable-width sliding window (uses 4-12 bits).
	* 2-17 bytes of data can be referenced in the sliding window (uses 4 bits).
	* A copy entry takes 9 bits (1 flag and 8 bits).
	* An encode (range) entry takes 9-17 bits (1 flag and 8-16 bits).
	* If bytes that follow are the same as the current byte an RLE trick is used (though it doesn’t help a lot).
	* The first byte contains a count of the number of bits in the final byte (handled in aux.types.BitSet).
	*
	* LZSS decoding reference - http://michael.dipperstein.com/lzss/
	************************************************************************************************************/
	
	//Commands
    var COPY_FLAG=0;
    var ENCODE_FLAG=1;
	
	//Encode range
	//Offset bits grow as needed
	var OFFSET_BITS=4;
	var OFFSET_MAX_BITS=12;
	var COUNT_BITS=4;
	
	//Encoder state
	var pos=0;
	var bitPos=0;
    var dataLen=data.length;
    var stream = new aux.types.BitSet(0);
	var offsetBits=OFFSET_BITS;
	var match;
	
	if(dataLen==0)
	    return stream.save();
		
	//Helper method
	stream.write=function(bits,val)
	{
	    stream.setBits(bitPos,bits,val);
		bitPos+=bits;
	}
	
	//Finds if the data at the current pos is in the sliding window.
	function findBestMatch()
	{
	    //Offset is a positive value that represents a negative offset from pos
	    //An offset of 0 means not found
	    var range={offset:0, count:0};

		var maxoffs = 1<<OFFSET_MAX_BITS; //An offset of zero is not used
		var maxcount  = (1<<COUNT_BITS)+1; //2 is the minimum length used
		var winpos  = (pos-maxoffs) >= 0 ? (pos-maxoffs) : 0;
		var count;
		
		//Check for a match in the sliding window
		for(var i=winpos,j,k,count;i<pos;++i)
		{
		    count=0;
			//Compare strings
		    for(j=pos,k=0;(i+k)<pos && j<dataLen && k<maxcount;++j,++k)
			{
			    if(data[j]==data[i+k])
				    ++count;
				else
				    break;
			}
			//If the new match length is longer use the new match
			if(count > range.count)
			{
			    range.offset=pos-i;
				range.count=count;
			}
			//We must not exceed maxcount
			if(count==maxcount)
			    break;
		}
		
		//Check for a RLE match (offset would be 1 and count would be the run length)
		if(pos>0 && data[pos]==data[pos-1])
		{
		    count=1;
			//Check for a run of bytes following pos
			for(var i=pos+1,j;i<dataLen;++i,++j)
			{
			    if(data[i]==data[i-1] && j<maxcount)
				    ++count;
				else
				    break;
			}
			//If the run is longer than the dictionary match use it instead
			if(count > range.count)
			{
			    range.offset=1;
				range.count=count;
			}
		}
		
		return range;
	}
		
	while(pos<dataLen)
	{		
	    //Check for a match in the sliding window
		match=findBestMatch();
		
		//Grow the sliding window as needed until the max size is reached
		if( offsetBits<OFFSET_MAX_BITS && pos>(1<<offsetBits) )
			++offsetBits;
		
		if(match.count>1)
		{
		    //Add an encode (range) entry
			//offset is >= 1 and count is >=2 so we subtract as not to waste bits
		    stream.write(1,ENCODE_FLAG);
		    stream.write(offsetBits,match.offset-1);
		    stream.write(COUNT_BITS,match.count-2);
			
			pos+=match.count;
		}
		else
		{
		    //Add a copy (put) entry
		    stream.write(1,COPY_FLAG);
		    stream.write(8,data[pos]);
			++pos;
		}
	}

	return stream.save();
}

//#######################################
//#              aux.decode             #
//###########################################################
aux.decode={}
aux.decode.dataToStr=function(data)
{
    var str="";
	for(var i=0,l=data.length;i<l;++i)
	    str+=String.fromCharCode(data[i]);
	return str;
}
aux.decode.hex=function(str)
{
	var bytes=[];
	var nib1,nib2;
	var HEX_TABLE=['0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'];
	
	str=str.toUpperCase();
	str=str.replace(/^0X|^#|\s/gm,'');
	for(var i=0,j=0,l=str.length;i<l;i+=2,++j)
	{
		nib1 = aux.search.linear(HEX_TABLE,str.charAt(i));
		nib2 = aux.search.linear(HEX_TABLE,str.charAt(i+1));
		bytes[j] = nib1<<4 | nib2;
	}
	
	return bytes;
}
aux.decode.base64=function(str)
{
	// Base 64 reference
	// http://tools.ietf.org/html/rfc4648
	
	var bytes=[];
	var q1,q2,q3,q4;
	var BASE64_TABLE=['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',
					  'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f',
					  'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',
					  'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/'];
			
	str=str.replace(/\s/gm,'');
	for(var i=0,l=str.length;i<l;i+=4)
	{
		q1 = aux.search.linear(BASE64_TABLE,str.charAt(i));
		q2 = aux.search.linear(BASE64_TABLE,str.charAt(i+1));
		q3 = aux.search.linear(BASE64_TABLE,str.charAt(i+2));
		q4 = aux.search.linear(BASE64_TABLE,str.charAt(i+3));
			
		if(str.substr(i+2,2)=="==")
			bytes.push( (q1<<2) | (q2>>4) );
		else if(str.charAt(i+3)=='=')
		{
			bytes.push( (q1<<2) | (q2>>4) );
			bytes.push( ((q2&0xf)<<4) | ((q3&0x3c)>>2) );
		}
		else
		{
			bytes.push( (q1<<2) | (q2>>4) );
			bytes.push( ((q2&0xf)<<4) | ((q3&0x3c)>>2) );
			bytes.push( ((q3&0x3)<<6) | q4 );
		}
	}
	
	return bytes;
}
aux.decode.rle=function(data)
{
	// RLE decoding reference
    // http://www.arturocampos.com/ac_rle.html
	
    var i=0,l=data.length,j,count;
	var decoded=[];
	while(i<l)
	{
	    if(i-l==1)
		    decoded.push(data[i++]);
		else
		{
		    decoded.push(data[i++]);
			if(data[i]==data[i-1])
			{
		        decoded.push(data[i++]);
				count=data[i++];
				for(j=0;j<count;++j)
		            decoded.push(data[i-2]);
			}
		}
	}
	
	return decoded;
}
aux.decode.lzss=function(data)
{	
	/*
	* This implementation uses a 16-4096 byte variable-width sliding window (uses 4-12 bits).
	* 2-17 bytes of data can be referenced in the sliding window (uses 4 bits).
	* A copy entry takes 9 bits (1 flag and 8 bits).
	* An encode (range) entry takes 9-17 bits (1 flag and 8-16 bits).
	* If bytes that follow are the same as the current byte an RLE trick is used (though it doesn’t help a lot).
	* The first byte contains a count of the number of bits in the final byte (handled in aux.types.BitSet).
	*
	* LZSS decoding reference - http://michael.dipperstein.com/lzss/
	************************************************************************************************************/
	
	//Commands
    var COPY_FLAG=0;
    var ENCODE_FLAG=1;
	
	//Encode range
	//Offset bits grows as needed
	var OFFSET_BITS=4;
	var OFFSET_MAX_BITS=12;
	var COUNT_BITS=4;
	
	//Decoder state
    var dataLen=data.length;
    var stream = new aux.types.BitSet(0);
	stream.load(data);
	var pos=0;
	var bitPos=0;
	var bitLen=stream.getBitSize();
	var decoded=[];
	var command;
	var offsetBits=OFFSET_BITS;
	var offset;
	var count;

	//Helper method
	stream.read=function(bits)
	{
	    var val=stream.getBits(bitPos,bits);
		bitPos+=bits;
		return val;
	}
	
	if(bitLen==0)
	    return decoded;
		
	while(bitPos<bitLen)
	{
		//Grow the sliding window as needed until the max size is reached
		if( offsetBits<OFFSET_MAX_BITS && pos>(1<<offsetBits) )
			++offsetBits;
			
	    command=stream.read(1);
		if(command==ENCODE_FLAG)
		{
		    //Read an encode (range) entry
			//offset is >= 1 and count is >=2 so we add as bits were not wasted
		    offset=stream.read(offsetBits)+1;
			count=stream.read(COUNT_BITS)+2;

			for(var i=pos-offset,j=0;j<count;++i,++j)
			{
			    if(i<pos)
				    decoded.push(decoded[i]);
				else //RLE trick
				    decoded.push(decoded[pos-1]);
			}
			
			pos+=count;
		}
		else
		    decoded[pos++]=stream.read(8); //Read a copy (get) entry
	}
	
	return decoded;
}

//#######################################
//#             aux.element             #
//###########################################################
aux.element=new function()
{
    this.getWidth=function(elm)
    {
        return parseInt(elm.style.width);
    }
    this.getHeight=function(elm)
    {
        return parseInt(elm.style.height);
    }
    this.getX=function(elm)
    {
        return parseInt(elm.style.left);
    }
    this.getY=function(elm)
    {
        return parseInt(elm.style.top);
    }
    this.moveTo=function(elm,left,top)
    {
        elm.style.left=left+"px";
        elm.style.top=top+"px";
    }
    this.moveBy=function(elm,left,top)
    {
        elm.style.left=(parseInt(elm.style.left)+left)+"px";
        elm.style.top=(parseInt(elm.style.top)+top)+"px";
    }
    this.sizeTo=function(elm,width,height)
    {
        elm.style.width=width+"px";
        elm.style.height=height+"px";
    }
    this.sizeBy=function(elm,width,height)
    {
        elm.style.width = (parseInt(elm.style.width)+width)+"px";
        elm.style.height = (parseInt(elm.style.height)+height)+"px";
    }
    this.rotate=function(elm,centerX,centerY,angle)
    {
        var x=parseInt(elm.style.left)-centerX;
        var y=parseInt(elm.style.top)-centerY;
        elm.style.left=(( x*Math.cos(angle) - y*Math.sin(angle) ) + centerX)+"px";
        elm.style.top=(( x*Math.sin(angle) + y*Math.cos(angle) ) + centerY)+"px";
    }
    this.show=function(elm)
    {
        elm.style.visibility="visible";
    }
    this.hide=function(elm)
    {
        elm.style.visibility="hidden";
    }
    this.opacity=function(elm,opacityIn)
    {
        if(typeof(elm.style.opacity)!="undefined")
        {
            elm.style.opacity=opacityIn;
        }
        else if(typeof(elm.style.filter)!="undefined")
        {
            elm.style.filter="alpha(opacity: "+Math.floor(opacityIn*100)+")";
        }
        else if(typeof(elm.style.mozOpacity)!="undefined")
        {
            elm.style.mozOpacity=opacityIn;
        }
        else if(typeof(elm.style.KhtmlOpacity)!="undefined")
        {
            elm.style.KhtmlOpacity=opacityIn;
        }
    }
    this.clip=function(elm,x1,y1,x2,y2)
    {
        elm.style.clip="rect("+y1+"px "+x2+"px "+y2+"px "+x1+"px)";
    }
    this.remove=function(elm)
    {
        var oldElm=elm.parentNode.removeChild(elm);
        return oldElm;
    }
    this.zIndex=function(elm,zIndex)
    {
        elm.style.zIndex=zIndex;
    }
};

//#######################################
//#             aux.evt             #
//###########################################################
aux.evt={};
aux.evt.Handler=function()
{
    var callbacks=[];

    this.callback=function(evt)
    {
        if(typeof(evt)=="undefined" && typeof(window.event)!="undefined")
            evt=window.event;
        var returnValue=true;
        for(var i=0;i<callbacks.length;i++)
        {
            if(callbacks[i](evt)==false)
                returnValue=false;
        }
        return returnValue;
    }
    this.add=function(callback)
    {
        callbacks.push(callback);
    }
    this.remove=function(callback)
    {
        var temp=[];
        for(var i=0,j=0;i<callbacks.length;i++)
        {
            if(callbacks[i]!=callback)
            {
                temp[j]=callbacks[i];
                j++;
            }
        }
        callbacks=temp;
    }
};
aux.evt.add=function(elm,type,callback)
{
    if(typeof(elm.addEventListener)!="undefined")
        elm.addEventListener(type,callback,false);
    else if(typeof(elm.attachEvent)!="undefined")
        elm.attachEvent("on"+type,callback);
}
aux.evt.remove=function(elm,type,callback)
{
    if(typeof(elm.removeEventListener)!="undefined")
        elm.removeEventListener(type,callback,false);
    else if(typeof(elm.detachEvent)!="undefined")
        elm.detachEvent("on"+type,callback);
}

aux.evt.blur=new aux.evt.Handler();
aux.evt.change=new aux.evt.Handler();
aux.evt.click=new aux.evt.Handler();
aux.evt.dblclick=new aux.evt.Handler();
aux.evt.contextmenu=new aux.evt.Handler();
aux.evt.error=new aux.evt.Handler();
aux.evt.focus=new aux.evt.Handler();
aux.evt.keydown=new aux.evt.Handler();
aux.evt.keypress=new aux.evt.Handler();
aux.evt.keyup=new aux.evt.Handler();
aux.evt.load=new aux.evt.Handler();
aux.evt.mousedown=new aux.evt.Handler();
aux.evt.mousemove=new aux.evt.Handler();
aux.evt.mouseout=new aux.evt.Handler();
aux.evt.mouseover=new aux.evt.Handler();
aux.evt.mouseup=new aux.evt.Handler();
aux.evt.mousewheel=new aux.evt.Handler();
aux.evt.resize=new aux.evt.Handler();
aux.evt.scroll=new aux.evt.Handler();
aux.evt.unload=new aux.evt.Handler();

//Setup globle events.
aux.evt.add(window,"blur",aux.evt.blur.callback);
if(typeof(window.deactivate)!="undefined")
    aux.evt.add(window,"deactivate",aux.evt.change.callback);
else
    aux.evt.add(window,"change",aux.evt.change.callback);
aux.evt.add(document,"click",aux.evt.click.callback);
aux.evt.add(document,"dblclick",aux.evt.dblclick.callback);
aux.evt.add(document,"contextmenu",aux.evt.contextmenu.callback);
aux.evt.add(window,"error",aux.evt.error.callback);
aux.evt.add(window,"focus",aux.evt.focus.callback);
aux.evt.add(document,"keydown",aux.evt.keydown.callback);
aux.evt.add(document,"keypress",aux.evt.keypress.callback);
aux.evt.add(document,"keyup",aux.evt.keyup.callback);
aux.evt.add(window,"load",aux.evt.load.callback);
aux.evt.add(document,"mousedown",aux.evt.mousedown.callback);
aux.evt.add(document,"mousemove",aux.evt.mousemove.callback);
aux.evt.add(document,"mouseout",aux.evt.mouseout.callback);
aux.evt.add(document,"mouseover",aux.evt.mouseover.callback);
aux.evt.add(document,"mouseup",aux.evt.mouseup.callback);
if(typeof(window.addEventListener)!="undefined")
    window.addEventListener("DOMMouseScroll",aux.evt.mousewheel.callback,false);
else
{
    aux.evt.add(window,"mousewheel",aux.evt.mousewheel.callback);
    aux.evt.add(document,"mousewheel",aux.evt.mousewheel.callback);
}
aux.evt.add(window,"resize",aux.evt.resize.callback);
aux.evt.add(window,"scroll",aux.evt.scroll.callback);
aux.evt.add(window,"unload",aux.evt.unload.callback);

//#######################################
//#          aux.eyeCandy           #
//###########################################################
aux.eyeCandy=new function()
{
    var _callbacks=[];
    var _detail=50; //0%-100%, default is 50%

    if(navigator.cookieEnabled)
        if(aux.cookie.get("\"aux.eyeCandy\" Detail")!=null)
            _detail=parseInt(aux.cookie.get("\"aux.eyeCandy\" Detail"));

    function updateAll()
    {
        for(var i=0;i<_callbacks.length;i++)
            _callbacks[i](_detail);
    }

    this.setDetail=function(detail)
    {
        if(_detail!=detail && typeof(detail)=="number")
        {
            if(detail<0)
                _detail=0;
            else if(detail>100)
                _detail=100;
            else
                _detail=Math.floor(detail);

            if(navigator.cookieEnabled)
                aux.cookie.set("\"aux.eyeCandy\" Detail",_detail.toString(),30)
            updateAll();
        }
    }
    this.getDetail=function()
    {
        return _detail;
    }
    this.add=function(callback)
    {
        _callbacks.push(callback);
        callback(_detail);
        return callback;
    }
    this.remove=function(callback)
    {
        var temp=[];
        for(var i=0,j=0;i<_callbacks.length;i++)
        {
            if(callback!=_callbacks[i])
            {
                temp[j]=_callbacks[i];
                j++;
            }
        }
        _callbacks=temp;
    }
};

//#######################################
//#            aux.get              #
//###########################################################
aux.get=new function()
{
    //Returns a bool representing if a given x and y position is on a 
	//checkerd pattern defined by checkerWidth and checkerHeight.
    this.checker=function(x,y,checkerWidth,checkerHeight)
	{
		if(x%(checkerWidth*2) < checkerWidth)
			if(y%(checkerHeight*2) < checkerHeight)
				return true;
			else
				return false;
		else
			if(y%(checkerHeight*2) < checkerHeight)
				return false;
			else
				return true;
	}
	this.pointInPoly=function(pt,poly)
    {
        //Edge points that represent one edge of the polygon
        var ep1, ep2;
        
        //Number of times a ray crosses the polygons edges from the left to the test point
        var crossings=0;
        
        //Test each edge for a crossing
        for(var i=0, l=poly.length; i<l; ++i)
        {
            //Points representing the current polygon edge to test
            ep1 = poly[i];
            ep2 = poly[(i+1) % l];
            
            //One endpoint must be above the test point and the other must be below for a crossing to occur
            if( (ep1.y >= pt.y && ep2.y < pt.y) ||
            (ep1.y < pt.y && ep2.y >= pt.y) )
            {
                if(ep1.x < pt.x && ep2.x < pt.x) //Both points left is a crossing
                    ++crossings;
                else if(ep1.x < pt.x || ep2.x < pt.x) //One point left requires more work
                { 
                    var x1 = ep1.x;
                    var y1 = ep1.y;
                    var x2 = ep2.x;
                    var y2 = ep2.y;
                    var y = pt.y;
                    
                    //Point on the edge with the same y value as the test point
                    var x = (y-y1) * ((x2-x1)/(y2-y1)) + x1;
                    
                    //If the edge is to the left of the test point
                    if(x < pt.x)
                        ++crossings;
                }
            }
        }
        
        //Odd number of edge crossings means the point is in the polygon
        return crossings & 1;
    }
    this.docWidth=function()
    {
        var body=(document.compatMode && document.compatMode != "BackCompat")? document.documentElement : document.body;
        return body.scrollWidth;
    }
    this.docHeight=function()
    {
        var body=(document.compatMode && document.compatMode != "BackCompat")? document.documentElement : document.body;
        return body.scrollHeight;
    }
    this.docWinWidth=function()
    {
        if(window.innerWidth)
            return window.innerWidth;
        else
        {
            var body=(document.compatMode && document.compatMode != "BackCompat")? document.documentElement : document.body;
            return body.clientWidth;
        }
    }
    this.docWinHeight=function()
    {
        if(window.innerHeight)
            return window.innerHeight;
        else
        {
            var body=(document.compatMode && document.compatMode != "BackCompat")? document.documentElement : document.body;
            return body.clientHeight;
        }
    }
    //Returns position relative to screen
    this.elmX=function(elm)
    {
        var offsetX=elm.offsetLeft;

        while (elm && elm.offsetParent)
        {
            elm = elm.offsetParent;
            offsetX += (elm.offsetLeft-elm.scrollLeft);
        }
        return offsetX;
    }
    //Returns position relative to screen
    this.elmY=function(elm)
    {
        var offsetY=elm.offsetTop;

        while (elm && elm.offsetParent)
        {
            elm = elm.offsetParent;
            offsetY += (elm.offsetTop-elm.scrollTop);
        }
        return offsetY;
    }
    this.mouseWheelDelta=function(evt)
    {
        delta=0;
    
        if (evt.wheelDelta)
        {
            delta = evt.wheelDelta;
            if (window.opera)
                delta = -delta;
        }
        else if (evt.detail)
        { 
            delta = -evt.detail;
        }
    
        return delta > 0 ? 1 : delta < 0 ? -1 : 0;
    }

    //Keep track of the mouse so an event object isn't required
    var mp_x=0;
    var mp_y=0;
    aux.evt.mousemove.add(function(evtIn)
    {
        if (typeof(evtIn.pageX)!="undefined")
        {
            mp_x = evtIn.pageX;
        }
        else if (typeof(evtIn.clientX)!="undefined")
        {
            var body=(document.compatMode && document.compatMode != "BackCompat")? document.documentElement : document.body;
            mp_x = evtIn.clientX + body.scrollLeft;
        }
    });
    aux.evt.mousemove.add(function(evtIn)
    {
        if (typeof(evtIn.pageY)!="undefined")
        {
            mp_y = evtIn.pageY;
        }
        else if (typeof(evtIn.clientY)!="undefined")
        {
            var body=(document.compatMode && document.compatMode != "BackCompat")? document.documentElement : document.body;
            mp_y = event.clientY + body.scrollTop;
        }
        return mp_y;
    });

    this.mouseX=function()
    {
        return mp_x;
    }
    this.mouseY=function()
    {
        return mp_y;
    }
    this.scrollX=function()
    {
        if(typeof(window.pageXOffset)!="undefined")
        {
            return window.pageXOffset;
        }
        else
        {
            var body=(document.compatMode && document.compatMode != "BackCompat")? document.documentElement : document.body;
            return body.scrollLeft;
        }
    }
    this.scrollY=function()
    {
        if(typeof(window.pageYOffset)!="undefined")
        {
            return window.pageYOffset;
        }
        else
        {
            var body=(document.compatMode && document.compatMode != "BackCompat")? document.documentElement : document.body;
            return body.scrollTop;
        }
    }
    this.selection=function()
    {
        if(window.getSelection)
            return window.getSelection().getRangeAt(0);
        else if(document.getSelection)
            return document.getSelection().getRangeAt(0);
        else if(document.selection)
            return document.selection.createRange();
        else
            return null;
    }
    this.srcElement=function(evt)
    {
        return typeof(evt.srcElement)!="undefined" ? evt.srcElement : evt.target;
    }
};

//#######################################
//#            aux.json             #
//###########################################################
aux.json=new function()
{
    this.getObj=function(jsonText)
    {
        //Make sure text is a object and not a function or variable!
        for(var i=0;i<jsonText.length;i++)
        {
            var t=jsonText.substr(i,1);
            if(!(t==' '||t=='\t'||t=='\r'||t=='\n'))
            {
                if(jsonText.substr(i,1)!='{')
                    return null;
                else
                    break;
            }
        }
        for(var i=jsonText.length-1;i>=0;i--)
        {
            var t=jsonText.substr(i,1);
            if(!(t==' '||t=='\t'||t=='\r'||t=='\n'))
            {
                if(jsonText.substr(i,1)!='}')
                    return null;
                else
                    break;
            }
        }
        if(jsonText.indexOf("new function"))
            return null;
    
        return eval("("+jsonText+")");
    }
    this.getObjFields=function(obj)
    {
        var fields=[];
        for(var i in obj)
            fields.push(i);
        return fields;
    }
    this.dhtmlObjTree=function(obj,recrusion)
    {
        recrusion=typeof(recrusion)=="undefined" ? -1:recrusion;
        var tree=document.createElement("ul");
    
        for(var i in obj)
        {
            var li=document.createElement("li");
    
            if(typeof(obj[i])=="object"&&recrusion!=0)
            {
                var span=document.createElement("span");
                span.innerHTML=i;
                span.style.fontWeight=900;
                span.style.color="#00cc00";
                li.appendChild(span);
                span.style.cursor="pointer";
                span.onclick=function()
                {
                    this.nextSibling.style.display = this.nextSibling.style.display=="none" ? "block":"none" ;
                }
                var ul=aux.json.dhtmlObjTree(obj[i],recrusion-1);
                ul.style.display="none";
                li.appendChild(ul);
                tree.appendChild(li);
            }
            else
            {
                var span=document.createElement("span");
                span.innerHTML=i;
                span.style.fontWeight=900;
                if(typeof(obj[i])=="object")
                    span.style.color="#00cc00";
                else if(typeof(obj[i])=="function")
                    span.style.color="#0000ff";
                else
                    span.style.color="#ff0000";
    
                li.appendChild(span);
                span.style.cursor="pointer";
                span.onclick=function()
                {
                    this.parentNode.nextSibling.style.display = this.parentNode.nextSibling.style.display=="none" ? "block":"none" 

;
                }
                var li2=document.createElement("li");
                var li3=document.createElement("li");
    
                li2.innerHTML="TYPE: "+typeof(obj[i]);
    
                if(typeof(obj[i])=="function")
                {
                    li3.innerHTML="VALUE: "+obj[i].toString().substr(0,obj[i].toString().indexOf('{')).replace(/\n/gm,'<br>').replace(/ /gm,'&nbsp;');
                    var button=document.createElement("input");
                    button.type="button";
                    button.value="View source.";
                    button.style.display="inline";
                    button.onclick=function()
                    {
                        this.nextSibling.style.display = this.nextSibling.style.display=="none" ? "block":"none" ;
                    };
                    var textarea=document.createElement("textarea");
                    textarea.value=obj[i].toString();
                    textarea.setAttribute("spellCheck","false");
                    textarea.setAttribute("wrap","off");
                    textarea.style.width="600px";
                    textarea.style.height="200px";
                    textarea.style.display="none";
                    li3.appendChild(button);
                    li3.appendChild(textarea);
                }
                else
                    li3.innerHTML="VALUE: "+obj[i].toString().replace(/\n/gm,'<br>').replace(/ /gm,'&nbsp;');
    
                var ol=document.createElement("ol");
                ol.style.display="none";
                ol.appendChild(li2);
                ol.appendChild(li3);
                tree.appendChild(li);
                tree.appendChild(ol);
            }
        }
    
        return tree;
    }
};

//#######################################
//#            aux.make             #
//###########################################################
aux.make={};
aux.make=new function()
{
    this.arrayCopy=function(arrayTo,arrayFrom)
    {
        for(var i=0;i<arrayFrom.length;i++)
        {
            arrayTo[i]=arrayFrom[i];
        }
    }
    this.floatingElement=function(elmName)
    {
        var elm = document.createElement(elmName);
        elm.style.position="absolute";
        elm.style.left=0;
        elm.style.top=0;
        aux.evt.load.add(function(){document.body.appendChild(elm)});
        return elm;
    }
};



//#######################################
//#           aux.query             #
//###########################################################
aux.query=new function()
{
    this.addToUrl=function(url,name,value)
    {
        if(url.indexOf('?')>-1)
        {
            if(url.indexOf('?')==url.length)
                url+=name+'='+encodeURIComponent(value);
            else
                url+='&'+name+'='+encodeURIComponent(value);
        }
        else
        {
            url+='?'+name+'='+encodeURIComponent(value);
        }
        return url;
    }
    this.extarctFromUrl=function(url,name)
    {
        if(url.indexOf('?')>-1)
            url=url.substring(url.indexOf('?')+1,url.length);
        var begin=url.indexOf(name+'=');
        if(begin!=-1)
        {
            begin+=name.length+1;
            var end=url.indexOf("&",begin);
            end= end==-1 ? url.length : end;
            return decodeURIComponent(url.substring(begin,end));
        }
        else
            return null;
    }
    this.parseUrl=function(url)
    {
        if(!(((url.indexOf('http://')>-1)||(url.indexOf('ftp://')>-1)||(url.indexOf('file:///')>-1))&&url.indexOf('?')==-1))
        {
            var qStart= url.indexOf('?') > -1 ? url.indexOf('?')+1 : 0;
            var queries=url.substr(qStart,url.length);
            queries=queries.split('&');
            if(queries[0]=="")
                queries=[];
            for(var i=0;i<queries.length;i++)
                queries[i]=queries[i].split('=');
            for(var i=0;i<queries.length;i++)
            {
                if(typeof(queries[i][1])=="undefined")
                    queries[i][1]="";
                else
                    queries[i][1]=decodeURIComponent(queries[i][1]);
            }
        }
        else
            var queries=[];
        return queries;
    }
};

//#######################################
//#             aux.search              #
//###########################################################
aux.search={};

aux.search.linear=function(arr,elm)
{
    for(var i=0;i<arr.length;i++)
        if(arr[i]==elm)
            return i;

    return -1;
};

//Use a compare of type greater if the list is from low to high 
aux.search.binary=function(arr,elm,cmp)
{
    var low=0;
    var high=arr.length-1;
    var result;
    
    //Check out of range.
    if(cmp(elm,arr[low])==-1) //Less
        return -1;
    else if(cmp(elm,arr[high])==1) //Greater
        return -1;
    
    while(low<=high)
    {
        var mid=low+Math.floor((high-low)/2);
        
        if(cmp(arr[mid],elm)==1) //Greater
            high=mid-1;
        else if(cmp(arr[mid],elm)==-1) //Less
            low=mid+1;
        else if(cmp(arr[mid],elm)==0) //Equal
        {
            //Find first equal occurrence.
            while(mid>=0 && cmp(arr[mid],elm)==0)
            {
                result=mid;
                --mid;
            }
            return result;
        }
    }
    return -1;
}

//#######################################
//#            aux.sort             #
//###########################################################
aux.sort={}

//Best O(n) | Average O(n^2) | Worst O(n^2)
aux.sort.bubble=function(arr,cmp)
{
	var lastSwap=arr.length-1;
	var exchangeMade=true;
	var temp;
	while(lastSwap>0 && exchangeMade)
	{
		exchangeMade=false;
		for(var i=0,l=lastSwap;i<l;++i)
		{
			if(cmp(arr[i],arr[i+1])==-1) //If not in order
			{
				temp=arr[i];
				arr[i]=arr[i+1];
				arr[i+1]=temp;
				lastSwap=i;
				exchangeMade=true;
			}
		}
	}
	
	return arr;
}

//Best O(n log n) | Average O(n log n) | Worst O(n log n)
aux.sort.merge=function(arr,cmp)
{
	if(arr.length<=1)
		return arr;
	//Provides a speed up by reducing recursion.
	else if(arr.length==2)
	{
		if(cmp(arr[0],arr[1])==1) //If in order
			return arr;
		else
			return [arr[1],arr[0]];
	}
	
	//Divide array into halves
	var mid=Math.floor(arr.length/2);
	var left=arr.slice(0,mid);
	var right=arr.slice(mid,arr.length);
	
	//Sort divided halves
	left=aux.sort.merge(left,cmp);
	right=aux.sort.merge(right,cmp);
	
	//Merge halves
	var result=[];
	var l=0, r=0, ll=left.length, rl=right.length;
	while(l<ll && r<rl)
	{
		if(cmp(left[l],right[r])==1) //If in order
		{
			result.push(left[l]);
			++l;
		}
		else
		{
			result.push(right[r]);
		    ++r;
		}
	}
	for(;l<ll;++l)
		result.push(left[l]);
	for(;r<rl;++r)
		result.push(right[r]);
	
	return result;
}

//Best O(n log n) | Average O(n log n) | Worst O(n^2)
aux.sort.quick=function(arr,cmp)
{
    var lo, hi;
    if(arguments.length==4)
    {
        lo=arguments[2];
        hi=arguments[3];
    }
    else
    {
        lo=0;
        hi=arr.length-1;
    }
    
    if(lo >= hi)
        return;
    
    var pvt = arr[lo];
    var l = lo+1;
    var r = hi;
    
    while(l<=r)
    {
        while(l<=r && cmp(arr[l],pvt)<=0)
            ++l;
        while(l<=r && cmp(arr[r],pvt)>=0)
            --r;
        
        if(l<=r)
        {
            var tmp=arr[l];
            arr[l]=arr[r];
            arr[r]=tmp;
            ++l;
            --r;
        }
    }

    arr[lo] = arr[r];
    arr[r] = pvt;
    aux.sort.quick(arr,cmp,lo,r-1);
    aux.sort.quick(arr,cmp,r+1,hi);
}

//#######################################
//#            aux.lsystem              #
//###########################################################
aux.lsystem={};

// Lindenmayer system
// http://en.wikipedia.org/wiki/L-system
aux.lsystem.LSystem=function()
{
    var _st=""; //Starting pattern
    var _pr=[]; //Production Rules
	
	this.getStart=function()
	{
	    return _st;
	}
	this.setStart=function(pattern)
	{
	    _st=pattern;
	}
	this.addProdRule=function(pred,succ)
	{
	    _pr.push({pred:pred,succ:succ});
	}
	this.removeProdRule=function(pred)
	{
	    for(var i=0,l=_pr.length; i<l; ++i)
		    if(pred == _pr[i].pred)
			{
			    _pr.splice(i,1);
				return;
			}
	}
	this.clearProdRules=function()
	{
	    _pr=[];
	}
	this.getGeneration=function(n)
	{
	    var s = _st; //Growing pattern
		
		//For each generation
	    for(var gen=0; gen<n; ++gen)
		{
		    //For each position in the pattern string
		    for(var i=0; i<s.length; ++i)
			{
			    //For each production rule
				var bm=null; //Best match
			    for(var j=0,lj=_pr.length; j<lj; ++j)
				{
				    var pr=_pr[j];
					
					//Bail out early if not enough characters left
					if((s.length-i)<pr.pred.length)
					    continue;
						
					//Chech for predecessor substring match 
					var match=true;
					for(var k=0,lk=pr.pred.length; k<lk; ++k)
					    if(s[i+k] != pr.pred[k])
						{
						    match=false;
							break;
						}
						
					//If a match and the longest match, then use this rule
					if(match && (bm==null || pr.pred.length > bm.pred.length))
					    bm = pr;
				}
				
				//Replace pred substring match with succ and skip succ.length characters
				if(bm!=null)
				{
				    s = s.substring(0,i) + bm.succ + s.substring(i+bm.pred.length,s.length);
					i+=bm.succ.length-1; //Subtract 1 from i because it is incremented in the loop
				}
			}
		}
		
		return s;
	}
};

//Actions for TurtleGfx
aux.lsystem.ActMove=function(delta)
{
    //Private id
    this.$="move";
    
	this.delta=delta;
}
aux.lsystem.ActSkip=function(delta)
{
    //Private id
    this.$="skip";
    
	this.delta=delta;
}
aux.lsystem.ActTurn=function(theta)
{
    //Private id
    this.$="turn";
	
    this.theta=theta;
}
aux.lsystem.ActScale=function(scale)
{
    //Private id
    this.$="scale";
	
	this.scale=scale;
}
aux.lsystem.ActPush=function()
{
    //Private id
    this.$="push";
}
aux.lsystem.ActPop=function()
{
    //Private id
    this.$="pop";
}

//TurtleGfx is designed to work with LSystem
aux.lsystem.TurtleGfx=function(ctx)
{	
    var _sym=[];
	//Symbol action table for ASCII printable characters
	for(var i=0;i<95;++i)
	    _sym[i]=null;
		
	//State stack
	var _state=[];
	
	//Start position and direction
	this.pos={x:0,y:0};
	this.dir=0;
	
	this.defSymbol=function(ch,action)
	{
	    if(ch.length!=1)
		    throw("'ch' must be a string of length 1");
			
	    var cc = ch.charCodeAt(0);
		if(cc<32 || cc>126)
		    throw("'ch' must be an ASCII printable character");
			
		_sym[cc-32] = action;
	}
	this.getSymbol=function(ch)
	{
	    if(ch.length!=1)
		    throw("'ch' must be a string of length 1");
			
	    var cc = ch.charCodeAt(0);
		if(cc<32 || cc>126)
		    throw("'ch' must be an ASCII printable character");
			
		return _sym[cc-32];
	}
	
	this.draw=function(symbols)
	{
	   var pos={x:this.pos.x,y:this.pos.y};
	   var dir=this.dir;
	   var scale=1;
	   
	   for(var i=0,l=symbols.length; i<l; ++i)
	   {
	       var sym = symbols.substr(i,1);
		   var cmd = this.getSymbol(sym);
		   
		   if(cmd==null)
		       continue;
		   
		   switch(cmd.$)
		   {
		       case "move":
			       ctx.beginPath();
				   ctx.moveTo(pos.x,pos.y);
				   pos.x += (scale*cmd.delta)*Math.cos(dir);
				   pos.y += (scale*cmd.delta)*Math.sin(dir);
				   ctx.lineTo(pos.x,pos.y);
				   ctx.stroke();
			       break;
		       case "skip":
				   pos.x += (scale*cmd.delta)*Math.cos(dir);
				   pos.y += (scale*cmd.delta)*Math.sin(dir);
			       break;
		       case "turn":
			       dir += cmd.theta;
			       break;
		       case "scale":
			       scale *= cmd.scale;
			       break;
		       case "push":
			       {
			           var elm = { pos:{x:0, y:0}, dir:0, scale:1 };
				       elm.pos.x = pos.x;
					   elm.pos.y = pos.y;
					   elm.dir = dir;
					   elm.scale = scale;
					   _state.push(elm);
				   }
			       break;
		       case "pop":
			       if(_state.length>0)
				   {
				       var elm = _state.pop();
					   pos.x = elm.pos.x;
					   pos.y = elm.pos.y;
					   dir = elm.dir;
					   scale = elm.scale;
				   }
			       break;
		       default: break; //Ignore undefined actions
		   };
	   }
	}
	this.getContext=function()
	{
	    return ctx;
	}
};

//#######################################
//#             aux.synth               #
//###########################################################
aux.synth={};

aux.synth.BITS_SAMPLE_8 = 8;
aux.synth.BITS_SAMPLE_16 = 16;
aux.synth.RATE_FAST = 8000;
aux.synth.RATE_GOOD = 22050;
aux.synth.RATE_BEST = 44100;

aux.synth.sinFunc=function(freq,t)
{
	return Math.sin(Math.PI*freq*t);
}
aux.synth.squareFunc=function(freq,t)
{
	var cycleWidth=1/freq;
	if(Math.floor(t/cycleWidth) & 1) //If odd
		return -1;
	return 1;
}
aux.synth.triangleFunc=function(freq,t)
{
	var cycleWidth=(1/freq)*2;
	return (Math.abs((t%cycleWidth)*freq-1)-0.5)*2;
}
aux.synth.sawtoothFunc=function(freq,t)
{
	var cycleWidth=(1/freq)*2;
	return (t%cycleWidth)*freq-1;
}
aux.synth.noiseFunc=function(freq,t)
{
	return Math.random()*2-1;
}

// Note frequency reference
// http://www.intmath.com/Trigonometric-graphs/music.php
aux.synth.getNoteFreq=function(noteStr,octive)
{	
	//Default value
	if(typeof(octive)=="undefined")
		octive=4;
	
	//Frequency of 'A4' - used as a base frequency for calculation
	var A4=440;
	//Note to offset helper table
	var NOTES=['A','A#','B','C','C#','D','D#','E','F','F#','G','G#'];
	
	//Get note offset relative to 'A' in the range 0-1
	var noteOffset = aux.search.linear(NOTES,noteStr.toUpperCase()) / 12;
	
	//Get selected note frequency
	freq = A4 * Math.pow(2,noteOffset);
	
	//Get selected octive frequency
	freq *= Math.pow(2,octive-4);
	
	return freq;
}

aux.synth.Synth=function(length,sampleRate,bitsPerSample,stereo)
{			
	var _length = typeof(length)=="undefined" ? 1 : length<0 ? 0 : length;
	var _sampleRate = typeof(sampleRate)=="undefined" ? 8000 : sampleRate;
	var _bitsPerSample = typeof(bitsPerSample)=="undefined" ? 8 : bitsPerSample;
	var _stereo = typeof(stereo)=="undefined" ? false : stereo;
	var _vibratoRate = 8;
	var _vibratoWarp = 0;
	var _data=[];
	var _dataUri="";
	var _funcs=[];
	
	function intToWord(num)
	{
		//Little Endian
		var bytes=[];
		bytes.push(num & 0xff);
		bytes.push((num>>8) & 0xff);
		return bytes;
	}
	function intToDword(num)
	{
		//Little Endian
		var bytes=[];
		bytes.push(num & 0xff);
		bytes.push((num>>8) & 0xff);
		bytes.push((num>>16) & 0xff);
		bytes.push((num>>24) & 0xff);
		return bytes;
	}
	function strToDword(str)
	{
		var code;
		var bytes=[];
		for(var i=0;i<4;++i)
		{
			code=str.charCodeAt(i);
			if(code<256)
				bytes.push(code);
		}
		return bytes;
	}
	function intToSword(num)
	{
		var bytes=[];

		//Little Endian
		bytes[0] = num & 0xff;
		bytes[1] = (num>>8) & 0xff;
		
		return bytes;
	}
	function swordToInt(bytes)
	{
		var num;
		
		//Little Endian
		if(bytes[1] & 0x80)
			num = 0xffff0000 | (bytes[1]<<8) | bytes[0];
		else
			num = (bytes[1]<<8) | bytes[0];
		
		return num;
	}	
	function waveToDataUri(wave)
	{
		return "data:audio/wav;base64,"+encodeURIComponent(aux.encode.base64(wave));
	}
	
	// Wave file format references
	// http://ccrma.stanford.edu/courses/422/projects/WaveFormat/
	// http://www.sonicspot.com/guide/wavefiles.html
	function makeWave()
	{	
		var numChannels = _stereo ? 2 : 1;
		var byteRate = _sampleRate*numChannels*(_bitsPerSample/8);
		var blockAlign = numChannels*(_bitsPerSample/8);
		
		//########## Begin wave header ##########//
		var header=[];
		//ChunkID = "RIFF"
		header=header.concat(strToDword("RIFF"));
		//ChunkSize = 36 + _data.length
		header=header.concat(intToDword(36 + _data.length));
		//Format = "WAVE"
		header=header.concat(strToDword("WAVE"));
		//Subchunk1ID = "fmt "
		header=header.concat(strToDword("fmt "));
		//Subchunk1Size = 16
		header=header.concat(intToDword(16));
		//AudioFormat = 1
		header=header.concat(intToWord(1));
		//NumChannels
		header=header.concat(intToWord(numChannels));
		//SampleRate
		header=header.concat(intToDword(_sampleRate));
		//ByteRate
		header=header.concat(intToDword(byteRate));
		//BlockAlign
		header=header.concat(intToWord(blockAlign));
		//BitsPerSample
		header=header.concat(intToWord(_bitsPerSample));
		//Subchunk2ID
		header=header.concat(strToDword("data"));
		//Subchunk2Size
		header=header.concat(intToDword(_data.length));
		//########## End wave header ##########//
		
		var waveData = header.concat(_data);
		return waveData;
	}
	
	function checkFormat()
	{
		if(!(_bitsPerSample==8 || _bitsPerSample==16))
			throw("Bit rate not supported!");
		if(!(_sampleRate==8000 || _sampleRate==22050 || _sampleRate==44100))
			throw("Sample rate not supported!");
	}
	
	function createSoundData()
	{
		checkFormat(_bitsPerSample,_sampleRate);
		
		var numChannels = _stereo ? 2 : 1;
		var blockSize = numChannels*(_bitsPerSample/8);
		var blocks = _length * _sampleRate;
		var byteLength = blocks * blockSize;
		
		_data=[];
		
		if(_bitsPerSample == 8)
			for(var i=0;i<byteLength;++i)
				_data[i]=127;
		else// if(_bitsPerSample == 16)
			for(var i=0;i<byteLength;++i)
				_data[i]=0;
	}
		
	function renderFunc(osc,freq,vol,pan)
	{
		checkFormat(_bitsPerSample,_sampleRate);
		
		var sampleData;
		var sampleAdd;
		var time;
		var vibratoTimeOffset;
		if(_bitsPerSample==8)
		{
			if(_stereo == false)
			{
				for(var i=0,l=_data.length;i<l;++i)
				{
					sampleData = _data[i];
					time = i/_sampleRate;
					vibratoTimeOffset =  Math.sin(Math.PI*(i/_sampleRate*2*_vibratoRate))*_vibratoWarp/1000;
					sampleAdd = osc(freq,time + vibratoTimeOffset) * vol;
					sampleData += Math.floor(sampleAdd*128);
					if(sampleData < 0) sampleData=0;
					else if(sampleData > 255) sampleData=255;
					_data[i] = sampleData;
				}
			}
			else
			{
				for(var i=0,l=_data.length;i<l;i+=2)
				{
					var volLeft=1;
					var volRight=1;
					if(pan < 0)
						volRight = 1+pan;
					else// if(pan > 0)
						volLeft = 1-pan;
					
					time = i/2/_sampleRate;
					vibratoTimeOffset =  Math.sin(Math.PI*(i/2/_sampleRate*2*_vibratoRate))*_vibratoWarp/1000;
					
					sampleData = _data[i];
					sampleAdd = osc(freq,time + vibratoTimeOffset) * vol * volLeft;
					sampleData += Math.floor(sampleAdd*128);
					if(sampleData < 0) sampleData=0;
					else if(sampleData > 255) sampleData=255;
					_data[i] = sampleData;
					
					sampleData = _data[i+1];
					sampleAdd = osc(freq,time + vibratoTimeOffset) * vol * volRight;
					sampleData += Math.floor(sampleAdd*128);
					if(sampleData < 0) sampleData=0;
					else if(sampleData > 255) sampleData=255;
					_data[i+1] = sampleData;
				}
			}
		}
		else// if(_bitsPerSample==16)
		{
			var tmp;
			if(_stereo == false)
			{
				for(var i=0,l=_data.length;i<l;i+=2)
				{
					sampleData = swordToInt([_data[i],_data[i+1]]);
					time = i/2/_sampleRate;
					vibratoTimeOffset =  Math.sin(Math.PI*(i/_sampleRate*2*_vibratoRate))*_vibratoWarp/1000;
					sampleAdd = osc(freq,time + vibratoTimeOffset) * vol;
					sampleData += Math.floor(sampleAdd*32768);
					if(sampleData < -32768) sampleData=-32768;
					else if(sampleData > 32767) sampleData=32767;
					tmp = intToSword(sampleData);
					_data[i] = tmp[0];
					_data[i+1] = tmp[1];
				}
			}
			else
			{
				for(var i=0,l=_data.length;i<l;i+=4)
				{
					var volLeft=1;
					var volRight=1;
					if(pan < 0)
						volRight = 1+pan;
					else// if(pan > 0)
						volLeft = 1-pan;
					
					time = i/4/_sampleRate;
					vibratoTimeOffset =  Math.sin(Math.PI*(i/4/_sampleRate*2*_vibratoRate))*_vibratoWarp/1000;
					
					sampleData = swordToInt([_data[i],_data[i+1]]);
					sampleAdd = osc(freq,time + vibratoTimeOffset) * vol * volLeft;
					sampleData += Math.floor(sampleAdd*32768);
					if(sampleData < -32768) sampleData=-32768;
					else if(sampleData > 32767) sampleData=32767;
					tmp = intToSword(sampleData);
					_data[i] = tmp[0];
					_data[i+1] = tmp[1];
					
					sampleData = swordToInt([_data[i+2],_data[i+3]]);
					sampleAdd = osc(freq,time + vibratoTimeOffset) * vol * volRight;
					sampleData += Math.floor(sampleAdd*32768);
					if(sampleData < -32768) sampleData=-32768;
					else if(sampleData > 32767) sampleData=32767;
					tmp = intToSword(sampleData);
					_data[i+2] = tmp[0];
					_data[i+3] = tmp[1];
				}
			}
		}
	}
	
	this.setLength=function(length)
	{
		if(length>=0)
			_length=length;
	}
	this.getLength=function()
	{
		return _length;
	}
	this.setVibratoRate=function(hz)
	{
		if(hz<0) hz=0;
		_vibratoRate=hz;
	}
	this.getVibratoRate=function()
	{
		return _vibratoRate;
	}
	this.setVibratoWarp=function(millisecondWarp)
	{
		_vibratoWarp=millisecondWarp;
	}
	this.getVibratoWarp=function()
	{
		return _vibratoWarp;
	}
	this.addFunc=function(func,id,freq,vol,pan)
	{
		if(freq<1) freq=1;
		if(vol<0) vol=0;
		if(pan<-1) pan=-1;
		else if(pan>1) pan=1;
		
		_funcs.push({func:func,id:id,freq:freq,vol:vol,pan:pan});
	}
	this.removeFunc=function(id)
	{
		for(var i=0,l=_funcs.length;i<l;++i)
		{
			if(_funcs[i].id==id)
			{
				_funcs=_funcs.splice(i,1);
				break;
			}
		}
	}
	this.setFreq=function(id,freq)
	{
		if(freq<1) freq=1;
		
		for(var i=0,l=_funcs.length;i<l;++i)
		{
			if(_funcs[i].id==id)
			{
				_funcs[i].freq=freq;
				break;
			}
		}
	}
	this.getFreq=function(id)
	{
		for(var i=0,l=_funcs.length;i<l;++i)
			if(_funcs[i].id==id)
				return _funcs[i].freq;
		return null;
	}
	this.setVol=function(id,vol)
	{
		if(vol<0) vol=0;
		
		for(var i=0,l=_funcs.length;i<l;++i)
		{
			if(_funcs[i].id==id)
			{
				_funcs[i].vol=vol;
				break;
			}
		}
	}
	this.getVol=function(id)
	{
		for(var i=0,l=_funcs.length;i<l;++i)
			if(_funcs[i].id==id)
				return _funcs[i].vol;
		return null;
	}
	this.setPan=function(id,pan)
	{
		if(pan<-1) pan=-1;
		else if(pan>1) pan=1;
		
		for(var i=0,l=_funcs.length;i<l;++i)
		{
			if(_funcs[i].id==id)
			{
				_funcs[i].pan=pan;
				break;
			}
		}
	}
	this.getPan=function(id)
	{
		for(var i=0,l=_funcs.length;i<l;++i)
			if(_funcs[i].id==id)
				return _funcs[i].pan;
		return null;
	}
	this.render=function()
	{
		createSoundData();
		
		var func;
		for(var i=0,l=_funcs.length;i<l;++i)
		{
			func=_funcs[i];
			renderFunc(func.func,func.freq,func.vol,func.pan);
		}
		
		_dataUri = waveToDataUri(makeWave());
	}
	this.getRawData=function()
	{
		return _data;
	}
	this.getDataUri=function()
	{
		return _dataUri;
	}
}

//#######################################
//#           aux.textArea              #
//###########################################################
aux.textArea={};
aux.textArea.getSelected=function(textArea)
{
    return aux.textArea.getValue(textArea).substring(aux.textArea.getSelectionStart(textArea),aux.textArea.getSelectionEnd(textArea));
};
aux.textArea.getSelectionEnd=function(textArea)
{
    if(typeof(textArea.selectionEnd)!="undefined")
    {
        return textArea.selectionEnd;
    }
    else if (document.selection && document.selection.createRange)
    {
        var range = document.selection.createRange().duplicate();
        if (range.parentElement()==textArea)
        {
            //Find the selection end offset relative to the document.
            var selectionEndOffset=Math.abs(range.moveEnd("character",-1000000));

            //Find the textArea document offset.
            range.moveToElementText(textArea);
            range.collapse(true);
            var textAreaDocumentOffset=Math.abs(range.moveStart("character",-1000000));

            //Subtract the selection end document offset from the textArea
            //document offset to get the selection end!
            return (selectionEndOffset-textAreaDocumentOffset);
        }
        else return -1;
    }
    else return -1;
};
aux.textArea.getSelectionStart=function(textArea)
{
    if(typeof(textArea.selectionStart)!="undefined")
    {
        return textArea.selectionStart;
    }
    else if (document.selection && document.selection.createRange)
    {
        var range = document.selection.createRange().duplicate();
        if(range.parentElement()==textArea)
        {
            //Find the selection start offset relative to the document.
            var selectionStartOffset=Math.abs(range.moveStart("character",-1000000));

            //Find the textArea document offset.
            range.moveToElementText(textArea);
            range.collapse(true);
            var textAreaDocumentOffset=Math.abs(range.moveStart("character",-1000000));

            //Subtract the selection start document offset from the textArea
            //document offset to get the selection start!
            return (selectionStartOffset-textAreaDocumentOffset);
        }
        else return -1;
    }
    else return -1;
};
aux.textArea.getValue=function(textArea)
{
    //Make sure lines end with ‘\n’
    if(textArea.value.indexOf("\r\n")>-1)
        return textArea.value.replace(/\r\n/gm,'\n');
    else if(textArea.value.indexOf("\r")>-1)
        return textArea.value.replace(/\r/gm,'\n');
    else
        return textArea.value;
};
aux.textArea.hasSelected=function(textArea)
{
    if(aux.textArea.getSelectionStart(textArea)!=aux.textArea.getSelectionEnd(textArea))
        return true;
    return false;
};
aux.textArea.insertAtBegin=function(textArea,toInsert,focus)
{
    focus= typeof(focus)=="undefined" ? true : focus;
    var scrollTop=textArea.scrollTop;
    var ss=aux.textArea.getSelectionStart(textArea)+toInsert.length;
    var se=aux.textArea.getSelectionEnd(textArea)+toInsert.length;
    textArea.value=toInsert+textArea.value;
    aux.textArea.selectRange(textArea,ss,se,focus);
    textArea.scrollTop=scrollTop;
};
aux.textArea.insertAtEnd=function(textArea,toInsert,focus)
{
    focus= typeof(focus)=="undefined" ? true : focus;
    var scrollTop=textArea.scrollTop;
    var ss=aux.textArea.getSelectionStart(textArea);
    var se=aux.textArea.getSelectionEnd(textArea);
    textArea.value+=toInsert;
    aux.textArea.selectRange(textArea,ss,se,focus);
    textArea.scrollTop=scrollTop;
};
aux.textArea.insertAtSelectionEnd=function(textArea,toInsert,focus)
{
    focus= typeof(focus)=="undefined" ? true : focus;
    var scrollTop=textArea.scrollTop;
    var ss=aux.textArea.getSelectionStart(textArea);
    var se=aux.textArea.getSelectionEnd(textArea);
    var first=aux.textArea.getValue(textArea).substring(0,aux.textArea.getSelectionEnd(textArea));
    var last=aux.textArea.getValue(textArea).substring(aux.textArea.getSelectionEnd(textArea),aux.textArea.getValue(textArea).length);
    var total=first+toInsert+last;
    textArea.value=total;
    aux.textArea.selectRange(textArea,ss,se,focus);
    textArea.scrollTop=scrollTop;
};
aux.textArea.insertAtSelectionStart=function(textArea,toInsert,focus)
{
    focus= typeof(focus)=="undefined" ? true : focus;
    var scrollTop=textArea.scrollTop;
    var ss=aux.textArea.getSelectionStart(textArea)+toInsert.length;
    var se=aux.textArea.getSelectionEnd(textArea)+toInsert.length;
    var first=aux.textArea.getValue(textArea).substring(0,aux.textArea.getSelectionStart(textArea));
    var last=aux.textArea.getValue(textArea).substring(aux.textArea.getSelectionStart(textArea),aux.textArea.getValue(textArea).length);
    var total=first+toInsert+last;
    textArea.value=total;
    aux.textArea.selectRange(textArea,ss,se,focus);
    textArea.scrollTop=scrollTop;
};
aux.textArea.replaceSelected=function(textArea,toInsert,focus)
{
    focus= typeof(focus)=="undefined" ? true : focus;
    var scrollTop=textArea.scrollTop;
    var ss=aux.textArea.getSelectionStart(textArea);
    var se=ss+toInsert.length;
    var first=aux.textArea.getValue(textArea).substring(0,aux.textArea.getSelectionStart(textArea));
    var last=aux.textArea.getValue(textArea).substring(aux.textArea.getSelectionEnd(textArea),aux.textArea.getValue(textArea).length);
    var total=first+toInsert+last;
    textArea.value=total;
    aux.textArea.selectRange(textArea,ss,se,focus);
    textArea.scrollTop=scrollTop;
};
aux.textArea.selectAll=function(textArea,focus)
{
    focus= typeof(focus)=="undefined" ? true : focus;
    if(focus==true)
        textArea.focus();
    textArea.select();
};
aux.textArea.selectRange=function(textArea,start,end,focus)
{
    focus= typeof(focus)=="undefined" ? true : focus;
    if(typeof(textArea.setSelectionRange)!="undefined")
    {
        if(focus==true)
            textArea.focus();
        textArea.setSelectionRange(start,end);
    }
    else if(typeof(textArea.createTextRange)!="undefined")
    {
        if(focus==true)
            textArea.focus();
        var range=textArea.createTextRange();
        range.collapse(true);
        range.moveEnd('character',end);
        range.moveStart('character',start);
        range.select();
    }
};
aux.textArea.setCaretPos=function(textArea,pos,focus)
{
    focus= typeof(focus)=="undefined" ? true : focus;
    aux.textArea.selectRange(textArea,pos,pos,focus);
};

//#######################################
//#             aux.ui              #
//###########################################################

//To-Do Add 'aux.ui.ProgressBar' and 'aux.ui.Button'
aux.ui={};

aux.ui.AutoScroll=function(elm)
{
    elm.style.overflow="hidden";
    var _interval=null;
    var _speed=10;
    
    function scrollBy(left,top)
    {
        elm.scrollLeft+=left;
        elm.scrollTop+=top;
    }
    
    function scroll()
    {
        var ex=aux.get.elmX(elm);
        var ey=aux.get.elmY(elm);
        var ew=elm.clientWidth;
        var eh=elm.clientHeight;
        var mx=aux.get.mouseX();
        var my=aux.get.mouseY();
        
        if(mx<ex || my<ey || mx>ex+ew || my>ey+eh)
            return;
            
        var rx = mx-(ex+ew/2);
        var ry = my-(ey+eh/2);
        scrollBy(rx/ew * _speed,ry/eh * _speed);
    }
    
    this.init=function()
    {
        _interval=setInterval(scroll,25);
    }
    this.uninit=function()
    {
        clearInterval(interval);
        _interval=null;
    }
    
    this.setSpeed=function(speed)
    {
        _speed=speed;
    }
    this.getSpeed=function()
    {
        return _speed;
    }
}

aux.ui.ColorPicker=function(parentElement)
{
    //########## Private implimentation ##########//

    var color=new aux.types.Color();
    var onchange=new aux.evt.Handler;
    var colorTable=document.createElement("table");
    var huePalletTable=document.createElement("table");
    var satBrightPalletTable=document.createElement("table");
    
    //Setup the color table
    colorTable.border=0;
    colorTable.cellPadding=0;
    colorTable.cellSpacing=0;
    colorTable.insertRow(0);
    colorTable.insertRow(1);
    colorTable.rows[0].insertCell(0);
    colorTable.rows[0].insertCell(1);
    colorTable.rows[1].insertCell(0);
    colorTable.style.width="140px";
    colorTable.style.height="140px";
    colorTable.style.cursor="crosshair";
    colorTable.rows[0].cells[0].style.width="20px";
    colorTable.rows[0].cells[0].style.height="120px";
    colorTable.rows[0].cells[1].style.width="120px";
    colorTable.rows[0].cells[1].style.height="120px";
    colorTable.rows[1].cells[0].colSpan="2";
    colorTable.rows[1].cells[0].style.width="140px";
    colorTable.rows[1].cells[0].style.height="20px";
    colorTable.rows[1].cells[0].style.backgroundColor="#000";
    colorTable.rows[1].cells[0].style.color="#fff";
    colorTable.rows[1].cells[0].style.fontSize="10pt";
    colorTable.rows[1].cells[0].style.textAlign="center";
    colorTable.rows[1].cells[0].innerHTML="R: 0 G: 0 B: 0";

    //Setup the hue pallet table
    huePalletTable.border=0;
    huePalletTable.cellPadding=0;
    huePalletTable.cellSpacing=0;
    for(var i=0;i<120;i++)
    {
        huePalletTable.insertRow(0);
        huePalletTable.rows[0].insertCell(0);
        huePalletTable.rows[0].cells[0].style.width="20px";
        huePalletTable.rows[0].cells[0].style.height="1px";
        huePalletTable.rows[0].cells[0].onmousedown=mDownHue;
        huePalletTable.rows[0].cells[0].onmouseover=mOverHue;
    }
    for(var i=0;i<20;i++)
    {
        huePalletTable.rows[i].cells[0].style.backgroundColor="rgb("+255+","+Math.floor(255/19*i)+","+0+")";
    }
    for(var i=0;i<20;i++)
    {
        huePalletTable.rows[i+20].cells[0].style.backgroundColor="rgb("+Math.floor(255-(255/19*i))+","+255+","+0+")";
    }
    for(var i=0;i<20;i++)
    {
        huePalletTable.rows[i+40].cells[0].style.backgroundColor="rgb("+0+","+255+","+Math.floor(255/19*i)+")";
    }
    for(var i=0;i<20;i++)
    {
        huePalletTable.rows[i+60].cells[0].style.backgroundColor="rgb("+0+","+Math.floor(255-(255/19*i))+","+255+")";
    }
    for(var i=0;i<20;i++)
    {
        huePalletTable.rows[i+80].cells[0].style.backgroundColor="rgb("+Math.floor(255/19*i)+","+0+","+255+")";
    }
    for(var i=0;i<20;i++)
    {
        huePalletTable.rows[i+100].cells[0].style.backgroundColor="rgb("+255+","+0+","+Math.floor(255-(255/19*i))+")";
    }
    colorTable.rows[0].cells[0].appendChild(huePalletTable);

    //Setup the sat/bright pallet table
    satBrightPalletTable.border=0;
    satBrightPalletTable.cellPadding=0;
    satBrightPalletTable.cellSpacing=0;
    for(var i=0;i<30;i++)
    {
        satBrightPalletTable.insertRow(0);
        for(var j=0;j<30;j++)
        {
            satBrightPalletTable.rows[0].insertCell(0);
            satBrightPalletTable.rows[0].cells[0].onmousedown=mDownSatBright;
            satBrightPalletTable.rows[0].cells[0].onmouseover=mOverSatBright;
            satBrightPalletTable.rows[0].cells[0].style.width="4px";
            satBrightPalletTable.rows[0].cells[0].style.height="4px";
        }
    }

    function genorateSatBrightColors(color)
    {
        for(var i=0;i<30;i++)
        {
            for(var j=0;j<30;j++)
            {
                var tempCol;
                tempCol=color.blend(new aux.types.Color(127,127,127),i/29);
                if(j<15)
                    tempCol.set(tempCol.blend(new aux.types.Color(255,255,255),(14-j)/14));
                else
                    tempCol.set(tempCol.blend(new aux.types.Color(0,0,0),(j-14)/14));
                satBrightPalletTable.rows[i].cells[j].style.backgroundColor=tempCol.toCSSRgb();
            }
        }
    }
    genorateSatBrightColors(new aux.types.Color(255,0,0));
    colorTable.rows[0].cells[1].appendChild(satBrightPalletTable);
    parentElement.appendChild(colorTable);

    //Event handlers.
    function mUp(evt)
    {
        updateSatBright=false;
        updateCol=false;
    }
    aux.evt.mouseup.add(mUp);

    function extractColor(evt)
    {
        evt= typeof(event)!="undefined" ? event : evt;
        var tempCol=aux.get.srcElement(evt).style.backgroundColor;
        if(tempCol.indexOf('rgb')>-1)
        {
            tempCol=tempCol.substring(4,tempCol.length-1);
            tempCol=tempCol.split(',');
            for(var i=0;i<3;i++)
                tempCol[i]=parseInt(tempCol[i]);
            tempCol=new aux.types.Color(tempCol[0],tempCol[1],tempCol[2]);
        }
        else
            tempCol=aux.convert.hexToDecColor(tempCol);
        return tempCol;
    }

    var updateSatBright=false;
    function mDownHue(evt)
    {
        evt= typeof(event)!="undefined" ? event : evt;
        genorateSatBrightColors(extractColor(evt));
        updateSatBright=true;
        return false;
    }

    function mOverHue(evt)
    {
        evt= typeof(event)!="undefined" ? event : evt;
        if(updateSatBright==true)
            genorateSatBrightColors(extractColor(evt));
    }

    var updateCol=false;
    function mDownSatBright(evt)
    {
        evt= typeof(event)!="undefined" ? event : evt;
        color=extractColor(evt);
        
        var colInv=color.invert();
        colorTable.rows[1].cells[0].style.backgroundColor="rgb("+color.r+", "+color.g+", "+color.b+")";
        colorTable.rows[1].cells[0].style.color="rgb("+colInv.r+", "+colInv.g+", "+colInv.b+")";
        colorTable.rows[1].cells[0].innerHTML="R: "+color.r+" G: "+color.g+" B: "+color.b;
        
        onchange.callback(color);
        updateCol=true;
        return false;
    }

    function mOverSatBright(evt)
    {
        evt= typeof(event)!="undefined" ? event : evt;
        if(updateCol==true)
        {
            var color=extractColor(evt);
            
            var colInv=color.invert();
            colorTable.rows[1].cells[0].style.backgroundColor="rgb("+color.r+", "+color.g+", "+color.b+")";
            colorTable.rows[1].cells[0].style.color="rgb("+colInv.r+", "+colInv.g+", "+colInv.b+")";
            colorTable.rows[1].cells[0].innerHTML="R: "+color.r+" G: "+color.g+" B: "+color.b;
            
            onchange.callback(color);
        }
    }

    //########## Public usage ##########//
    this.getColor=function()
    {
        return color;
    }
    this.onchange=onchange;
};

aux.ui.HSlider=function(handleElement)
{
    var val=0;
    var min=0;
    var max=100;
    var mDown=false;
    var handle=handleElement;
    var track=handle.parentNode;
    var hSize=handle.clientWidth;
    var tSize=track.clientWidth;
    var tOffs=aux.get.elmX(track);
    var mOffs=0;
    var onchange=new aux.evt.Handler;

    handle.style.cursor="pointer";
    handle.style.margin="auto auto auto 0px";
    handle.style.position="relative";
    track.style.textAlign="left";
    track.style.verticalAlign="middle";

    function resize()
    {
        hSize=handle.clientWidth;
        tSize=track.clientWidth;
        tOffs=aux.get.elmX(track);
            
        handle.style.visibility="visible";
        if(max-min == 0)
            handle.style.left="0px";
        else
            handle.style.left=parseInt((tSize-hSize)*(val/(max-min)))+"px";
    }
    aux.evt.load.add(resize);
    aux.evt.resize.add(resize);

    handle.onmousedown=function()
    {
        mDown=true
        var mouseX=aux.get.mouseX();
        var handleX=aux.get.elmX(handle);
        mOffs = mouseX-handleX;
        return false;
    }; 

    aux.evt.mouseup.add(function()
    {
        mDown=false;
    });

    aux.evt.mousemove.add(function()
    {
        if(mDown==true)
        {
            var mouseX=aux.get.mouseX();
            
            if(mouseX-mOffs < tOffs)
            {
                val=min;
                handle.style.left="0px";
                onchange.callback(val);
                return false;
            }
            else if(mouseX-mOffs+hSize > tOffs+tSize)
            {
                val=max;
                handle.style.left=(tSize-hSize)+"px";
                onchange.callback(val);
                return false;
            }
                
            if(max-min == 0)
                val=min;
            else
                val=min+(((mouseX-mOffs)-tOffs)/(tSize-hSize))*(max-min);
            handle.style.left=parseInt((mouseX-mOffs)-tOffs)+"px";
            
            onchange.callback(val);
            return false;
        }
        return true;
    });

    this.getMin=function()
    {
        return min;
    }
    this.setMin=function(minimum)
    {
        min=minimum;
        if(min > max)
            max=min;
            
        this.setValue(val);
    }
    this.getMax=function()
    {
        return max;
    }
    this.setMax=function(maximum)
    {
        max=maximum;
        if(max < min)
            min=max;
        
        this.setValue(val);
    }
    this.getValue=function()
    {
        return val;
    }
    this.setValue=function(value)
    {
        if(value < min)
            value=min;
        if(value > max)
            value=max;
            
        val=value;
        if(max-min == 0)
            handle.style.left="0px";
        else
            handle.style.left=parseInt((tSize-hSize)*(val/(max-min)))+"px";
        onchange.callback(val);
    }
    this.onchange=onchange;
    this.resize=resize;
}

aux.ui.VSlider=function(handleElement)
{
    var val=0;
    var min=0;
    var max=100;
    var mDown=false;
    var handle=handleElement;
    var track=handle.parentNode;
    var hSize=handle.clientHeight;
    var tSize=track.clientHeight;
    var tOffs=aux.get.elmY(track);
    var mOffs=0;
    var onchange=new aux.evt.Handler;

    handle.style.cursor="pointer";
    handle.style.margin="0px auto auto auto";
    handle.style.position="relative";
    track.style.textAlign="center";
    track.style.verticalAlign="top";

    function resize()
    {
        hSize=handle.clientHeight;
        tSize=track.clientHeight;
        tOffs=aux.get.elmY(track);
        
        handle.style.visibility="visible";
        if(max-min == 0)
            handle.style.top="0px";
        else
            handle.style.top=parseInt((tSize-hSize)*(val/(max-min)))+"px";
    }
    aux.evt.load.add(resize);
    aux.evt.resize.add(resize);

    handle.onmousedown=function()
    {
        mDown=true
        var mouseY=aux.get.mouseY();
        var handleY=aux.get.elmY(handle);
        mOffs = mouseY-handleY;
        return false;
    }; 

    aux.evt.mouseup.add(function()
    {
        mDown=false;
    });

    aux.evt.mousemove.add(function()
    {
        if(mDown==true)
        {
            var mouseY=aux.get.mouseY();
            
            if(mouseY-mOffs < tOffs)
            {
                val=min;
                handle.style.top="0px";
                onchange.callback(val);
                return false;
            }
            else if(mouseY-mOffs+hSize > tOffs+tSize)
            {
                val=max;
                handle.style.top=(tSize-hSize)+"px";
                onchange.callback(val);
                return false;
            }

            if(max-min == 0)
                val=min;
            else
                val=min+(((mouseY-mOffs)-tOffs)/(tSize-hSize))*(max-min);
            handle.style.top=parseInt((mouseY-mOffs)-tOffs)+"px";

            onchange.callback(val);
            return false;
        }
        return true;
    });

    this.getMin=function()
    {
        return min;
    }
    this.setMin=function(minimum)
    {
        min=minimum;
        if(min > max)
            max=min;
            
        this.setValue(val);
    }
    this.getMax=function()
    {
        return max;
    }
    this.setMax=function(maximum)
    {
        max=maximum;
        if(max < min)
            min=max;
        
        this.setValue(val);
    }
    this.getValue=function()
    {
        return val;
    }
    this.setValue=function(value)
    {
        if(value < min)
            value=min;
        if(value > max)
            value=max;
            
        val=value;
        if(max-min == 0)
            handle.style.top="0px";
        else
            handle.style.top=parseInt((tSize-hSize)*(val/(max-min)))+"px";
        onchange.callback(val);
    }
    this.onchange=onchange;
    this.resize=resize;
}

aux.ui.Tooltip=function()
{
    //########## Private implementation ##########//
    var _div=aux.make.floatingElement("div");
    var _offset=new aux.types.Point(16,16);
    var _timeoutId=null;
    var _delay=2000;
    var _hidden=true;
    var _width="auto";
    var _height="auto";
    var _padding="4px";
    var _color="#000";
    var _bgColor="#fff";
    var _opacity=100;
    var _showBorder=true;
    var _borderColor="#000";
    
    function moveIt(evt)
    {
        if(_hidden==false)
        {
            aux.element.moveTo(_div,aux.get.mouseX()+_offset.x,aux.get.mouseY()+_offset.y);
        }
    }
    aux.evt.mousemove.add(moveIt);
    function disp()
    {
        _div.style.display="block";
    }

    //########## Public usage ##########//
    this.setDelay=function(delay)
    {
        _delay=delay;
    }
    this.getDelay=function()
    {
        return _delay;
    }
    this.setOffset=function(x,y)
    {
        _offset.x=x;
        _offset.y=y;
    }
    this.getOffset=function()
    {
        return _offset.clone();
    }
    this.setWidth=function(width)
    {
        _width=width;
    }
    this.getWidth=function()
    {
        return _width;
    }
    this.setHeight=function(height)
    {
        _height=height;
    }
    this.getHeight=function()
    {
        return _height;
    }
    this.setPadding=function(padding)
    {
        _padding=padding;
    }
    this.getPadding=function()
    {
        return _padding;
    }
    this.setColor=function(color)
    {
        if(typeof(color)=="string")
            _color=color;
        else
            _color=color.toCSSRgb();
    }
    this.getColor=function()
    {
        return color;
    }
    this.setBGColor=function(color)
    {
        if(typeof(color)=="string")
            _bgColor=color;
        else
            _bgColor=color.toCSSRgb();
    }
    this.getBGColor=function()
    {
        return _bgColor;
    }
    this.setOpacity=function(opacity)
    {
        _opacity=opacity;
    }
    this.getOpacity=function()
    {
        return _opacity;
    }
    this.setShowBorder=function(show)
    {
        _showBorder= show == true ? true : false;
    }
    this.getShowBorder=function()
    {
        return _showBorder;
    }
    this.setBorderColor=function(color)
    {
        if(typeof(color)=="string")
            _borderColor=color;
        else
            _borderColor=color.toCSSRgb();
    }
    this.getBorderColor=function()
    {
        return _borderColor;
    }
    this.show=function(html)
    {
        _div.innerHTML=html;
        
        _div.style.width=_width;
        _div.style.height=_height;
        _div.style.padding=_padding;
        _div.style.color=_color;
        _div.style.backgroundColor=_bgColor;
        aux.element.opacity(_div,_opacity);
        _div.style.borderWidth="2px";
        _div.style.borderStyle= _showBorder==true ? "solid" : "hidden";
        _div.style.borderColor=_borderColor;
        
        _timeoutId=setTimeout(disp,_delay);
        _div.style.display="none";
        _hidden=false;
    }
    this.hide=function()
    {
        _div.style.display="none";
        clearTimeout(_timeoutId);
        _hidden=true;
    }
};

aux.ui.Sound=function(src,useAudio)
{
    //########## Implementation ##########//
    useAudio = typeof(useAudio)=="undefined" ? false : useAudio; //Default value
    var _embed;
	var _audio;
	if(useAudio)
	{
		_audio=new Audio(src);
		document.body.appendChild(_audio);
	}
	else
	{
		_embed=document.createElement("embed");
		_embed.src=src;
		_embed.console="smallconsole";
		_embed.setAttribute("enablejavascript","true");
		_embed.setAttribute("autostart","false");
		_embed.loop="false";
		_embed.style.position="absolute";
		_embed.style.width=0;
		_embed.style.height=0;
		document.body.appendChild(_embed);
	}

    //########## Usage ##########//
    this.loop=function(enable)
    {
        enable=typeof(enable)=="undefined" ? true : enable;
		
		if(useAudio)
			_audio.loop=enable;
		else
		{
			if(enable)
				_embed.loop="true";
			else
				_embed.loop="false";
		}
    }
    this.mute=function()
    {
		if(useAudio)
			_audio.muted=true;
		else
		{
			if(typeof(_embed.SetMute)!="undefined")
				_embed.SetMute(1);
			else if(typeof(_embed.Mute)!="undefined")
				_embed.Mute=true;
		}
    }
    this.getLength=function()
    {
		if(useAudio)
			return _audio.duration;
		else
		{
			if(typeof(_embed.GetLength)!="undefined")
				return _embed.GetLength()*1000;
			else if(typeof(_embed.GetDuration)!="undefined" && typeof(_embed.GetTimeScale)!="undefined")
			    return _embed.GetDuration()/_embed.GetTimeScale();
			else if(typeof(_embed.Duration)!="undefined")
				return _embed.Duration;
		}
		
		return null;
    }
    this.getVolume=function()
    {
		if(useAudio)
			return _audio.volume;
		else
		{
			if(typeof(_embed.GetVolume)!="undefined")
			    return _embed.GetVolume();
			else if(typeof(_embed.Volume)!="undefined")
				return _embed.Volume;
		}
		
		return null;
    }
    this.pause=function()
    {
		if(useAudio)
			_audio.pause();
		else
		{
			if(typeof(_embed.Pause)!="undefined")
				_embed.Pause();
			else if(typeof(_embed.DoPause)!="undefined")
				_embed.DoPause();
			else if(typeof(_embed.SetRate)!="undefined")
				_embed.SetRate(0);
		}
    }
    this.play=function()
    {
		if(useAudio)
			_audio.play();
		else
		{
			if(typeof(_embed.Play)!="undefined")
				_embed.Play();
			else if(typeof(_embed.DoPlay)!="undefined")
				_embed.DoPlay();
		}
    }
    this.playAsync=function()
    {
		if(useAudio)
		{
			var sound=new Audio(src);
			sound.autoplay=true;
			document.body.appendChild(sound);
			setTimeout(
				function()
				{
					document.body.removeChild(sound);
				}
			,this.getLength()*1000*10); // "*10" is added to account for possable loading time before the sound is played
		}
		else
		{
			var sound=document.createElement("embed");
			sound.src=_embed.src;
			sound.console="smallconsole";
			sound.setAttribute("autostart","true");
			sound.loop="false";
			sound.style.position="absolute";
			sound.style.width=0;
			sound.style.height=0;
			document.body.appendChild(sound);
			setTimeout(
				function()
				{
					document.body.removeChild(sound);
				}
			,this.getLength()*1000*10); // "*10" is added to account for possable loading time before the sound is played
		}
    }
    this.setVolume=function(volume)
    {
		if(useAudio)
			_audio.volume=volume;
		else
		{
			if(typeof(_embed.SetVolume)!="undefined")
				_embed.SetVolume(volume);
			else if(typeof(_embed.Volume)!="undefined")
				_embed.Volume=volume;
		}
    }
    this.stop=function()
    {
		if(useAudio)
		{
			if(!_audio.paused)
			    _audio.pause();
			_audio.currentTime=0;
		}
		else
		{
			if(typeof(_embed.Stop)!="undefined")
				_embed.Stop();
			else if(typeof(_embed.DoStop)!="undefined")
				_embed.DoStop();
		}
    }
    this.unMute=function()
    {
		if(useAudio)
			_audio.muted=false;
		else
		{
			if(typeof(_embed.SetMute)!="undefined")
				_embed.SetMute(0);
			else if(typeof(_embed.Mute)!="undefined")
				_embed.Mute=false;
		}
    }
    this.unPause=function()
    {
		if(useAudio)
			_audio.play();
		else
		{
			if(typeof(_embed.Play)!="undefined")
				_embed.Play();
			else if(typeof(_embed.DoPlay)!="undefined")
				_embed.DoPlay();
			else if(typeof(_embed.SetRate)!="undefined")
				_embed.SetRate(1);
		}
    }
};

