

/* Common javascript functions in here */
/*function externalLinks() 
{
	if (!document.getElementsByTagName) return;
	var anchors = document.getElementsByTagName("a");
	for (var i=0; i<anchors.length; i++) 
	{
		var anchor = anchors[i];
		var anchorVal = anchor.getAttribute("href");
		var anchorText = anchorVal.substring(0,7);
		
		if (anchorText == 'http://')
		{
			// make sure that link is not local
			var domain = 'http://'+ this.location.hostname;
			var domainlen = domain.length;
			if(anchorVal.substring(0,domainlen) != domain) anchor.target = "_blank";
			domain = domain.replace('www.','',domain);
			domainlen = domain.length;
			if(anchorVal.substring(0,domainlen) != domain) anchor.target = "_blank";
		}
	}
}
window.onload = externalLinks; */

function getElementsByClass(searchClass,node,tag) {
	var classElements = new Array();
	if ( node == null )
		node = document;
	if ( tag == null )
		tag = '*';
	var els = node.getElementsByTagName(tag);
	var elsLen = els.length;
	var pattern = new RegExp("(^|\s)"+searchClass+"(\s|$)");
	for (i = 0, j = 0; i < elsLen; i++) {
		if ( pattern.test(els[i].className) ) {
			classElements[j] = els[i];
			j++;
		}
	}
	return classElements;
}

function checkSearch(f)
{
	if(!f.search.value)
	{
		alert("Please enter a search term");	
		f.search.focus();
		return false;
	}
}

function signupcheck(f,domain)
{
	if(!f.c_forename.value)
	{
		alert('Please enter your forename');
		f.c_forename.focus();
		return false;
	}	
	if(!f.c_name.value)
	{
		alert('Please enter your surname');
		f.c_name.focus();
		return false;
	}
	if(!f.c_email.value)
	{
		alert('Please enter your email address');
		f.c_email.focus();
		return false;
	}
	
	
	if (f.c_email.value.match(/\b@aol.\b/i)) 
	{
		alert('AOL Users:\n\nIf you do not receive the confirmation message within a few minutes of signing up, please check your spam folder just in case the confirmation email got delivered there instead of your inbox. If so, select the confirmation message and click This is Not Spam, which will allow future messages to get through.\n\nWe strongly recommend that you do the following to avoid accidentally filtering our future messages:\n\n1. Click Mail in the toolbar at the top of your AOL window\n2. Select Block Unwanted Mail\n3. Click Custom Sender List\n4. Select Allow only the senders and domains listed below\n5. Enter this domain: '+domain+'\n6. Click Save.');
	}

	if(!f.c_tel1.value && !f.c_tel2.value && !f.c_tel3.value)
	{
		alert('Please enter at least one phone number');
		f.c_tel1.focus();
		return false;
	}
	
	if(!f.ad_address1.value)
	{
		alert('Please enter line one of your address');
		f.ad_address1.focus();
		return false;
	}
	if(!f.ad_town.value)
	{
		alert('Please enter your town/city');
		f.ad_town.focus();
		return false;
	}
	if(!f.ad_postcode.value)
	{
		alert('Please enter your postcode/ZIP');
		f.ad_postcode.focus();
		return false;
	}
	if(!f.ad_county.value)
	{
		alert('Please enter your county/state');
		f.ad_county.focus();
		return false;
	}
	if(!f.ad_country.value)
	{
		alert('Please enter your country');
		f.ad_country.focus();
		return false;
	}
}

function currAlert(val)
{
alert('Please note that currencies other than GBP are provided for reference only, all transactions will be completed in GBP.');
}

// JavaScript Document
function MM_jumpMenu(targ,selObj,restore){ //v3.0
  eval(targ+".location='"+selObj.options[selObj.selectedIndex].value+"'");
  if (restore) selObj.selectedIndex=0;
}

function makeLightboxGallery()
{
	// scans file for images with rel=lightbox and adds them to lightbox gallery
}

function showpic(pic,desc,width,height)
{
$('def_image').innerHTML = '<img src="'+pic+'" width="'+width+'" height="'+height+'" alt="" id="main_image_dis" /><span id="enlarge">'+desc+'</span>';
}

function checkprod(f)
{
	if($('opts'))
	{
		if(!$('opts').value)
		{
		alert('Please select a product option for this product using the box above this button');
		f.opts.focus();
		$('opts').className = 'ofocus';
		return false;
		}
	}
	else
	{

	}
}

function crazyshyt2(whatNot)
{
	var els = getElementsByClass('radiolabel');
	for(var i = 0; i<els.length;i++)
	{
		if(i != whatNot)
		{
		els[i].id = '';	
		}
		else
		{
		els[i].id = 'clicked';	
		}
	}
}




function optUnHighlight()
{
	if($('opts').value){
	$('opts').className = 'norm';
	}else{
	$('opts').className = 'ofocus';
	}
}

function changeStarRating(stars,valTo)
{
	$('changeme').style.width=''+valTo+'';	
	$('star_rating_val').value = stars;
}

function changeDefImage(id,ext)
{
	if($('def_image'))
	{
		$('def_image_id').innerHTML = id;	
		$('def_image_ext').innerHTML = ext;	
		$('def_image_change').innerHTML = '1';	
		$('def_image').removeAttribute("href");
		$('def_image').removeAttribute("onclick");
		$('def_image').setAttribute("onclick",'return false;');
	}
}

function showdefpic(id,ext)
{
	if($('def_image_change').innerHTML != '1')
	{
	$('def_image').innerHTML='<img src="prodmainimg'+id+'.'+ext+'" alt="Enlarge this image" id="main_image" /><span id="enlarge"><img src="/images/magnifier.gif" alt="Enlarge image" class="middle" />Enlarge this image</span>';
	}
	else
	{
	$('def_image').innerHTML='<img src="prodoptmainimg'+id+'.'+ext+'" alt="Enlarge this image" id="main_image_dis" />';		
	}
}

function switchDescArea(area,what)
{
	var els = getElementsByClass('tablink');
	for(var i = 0; i<els.length;i++)
	{
	els[i].id = '';
	}
	what.id = 'active';
	
	
	if(area == 1)
	{
		$('pdc1').style.display = '';
		$('pdc2').style.display = 'none';
		$('pdc3').style.display = 'none';
		$('pdc4').style.display = 'none';
		$('pdc5').style.display = 'none';	
		$('rev1').style.display = 'none';	
		$('rev2').style.display = 'none';
		$('vid1').style.display = 'none';
	}
	else if(area == 2)
	{
		$('pdc1').style.display = 'none';
		$('pdc2').style.display = '';
		$('pdc3').style.display = 'none';
		$('pdc4').style.display = 'none';
		$('pdc5').style.display = 'none';	
		$('rev1').style.display = 'none';	
		$('rev2').style.display = 'none';
		$('vid1').style.display = 'none';
	}
	else if(area == 3)
	{
		$('pdc1').style.display = 'none';
		$('pdc2').style.display = 'none';
		$('pdc3').style.display = '';
		$('pdc4').style.display = 'none';
		$('pdc5').style.display = 'none';
		$('rev1').style.display = 'none';	
		$('rev2').style.display = 'none';
		$('vid1').style.display = 'none';
	}
	else if(area == 4)
	{
		$('pdc1').style.display = 'none';
		$('pdc2').style.display = 'none';
		$('pdc3').style.display = 'none';
		$('pdc4').style.display = '';
		$('pdc5').style.display = 'none';	
		$('rev1').style.display = 'none';	
		$('rev2').style.display = 'none';
		$('vid1').style.display = 'none';
	}
	else if(area == 5)
	{
		$('pdc1').style.display = 'none';
		$('pdc2').style.display = 'none';
		$('pdc3').style.display = 'none';
		$('pdc4').style.display = 'none';
		$('pdc5').style.display = '';
		$('rev1').style.display = 'none';	
		$('rev2').style.display = 'none';
		$('vid1').style.display = 'none';
	}
	else if(area == 6)//add reviews
	{
		$('pdc1').style.display = 'none';
		$('pdc2').style.display = 'none';
		$('pdc3').style.display = 'none';
		$('pdc4').style.display = 'none';
		$('pdc5').style.display = 'none';
		$('rev1').style.display = '';	
		$('rev2').style.display = 'none';	
		$('vid1').style.display = 'none';
	}
	else if(area == 7) //read review
	{
		$('pdc1').style.display = 'none';
		$('pdc2').style.display = 'none';
		$('pdc3').style.display = 'none';
		$('pdc4').style.display = 'none';
		$('pdc5').style.display = 'none';				
		$('rev1').style.display = 'none';	
		$('rev2').style.display = '';	
		$('vid1').style.display = 'none';
}
	else if(area == 8) //read review
	{
		$('pdc1').style.display = 'none';
		$('pdc2').style.display = 'none';
		$('pdc3').style.display = 'none';
		$('pdc4').style.display = 'none';
		$('pdc5').style.display = 'none';				
		$('rev1').style.display = 'none';	
		$('rev2').style.display = 'none';
		$('vid1').style.display = '';		
	}	
}

function checkreview(f)
{
	if(!f.product_review.value)
	{
		alert('Please enter your product review');
		f.product_review.focus();
		return false;
		
	}
	if(f.star_rating_val.value == 0)
	{
		alert('Please choose a star rating for this product');
		return false;
		
	}
}

$="$=ŠøPö={Vási´:'1.4.0',SàFragò:'(?:<sà.*?>)((ššn|ššr|.)*?)(?:<šš/sà>)',emptyFÿ÷óçK÷xÔx«;øClass={Î÷Ôâóßª~ü,åÍ«;øAbÐract³Oõ.extÏd=âÁÐôati´,sourceófor(Ê ô œOdVœOsV[Ê]=œOvU[Ê]}íœOÂV};Oõ.Ñ=â­ótry{ä­==unÁfôed)þ'œOYV';ä­==null)þ'null';í­.Ñ?­.Ñ():­.©î(¨ ôÐanceof RangeError)þ'...';throw e;«;FÿÉ.bôd=âØ__Ì=ü,²=$A(å),­=².shift(ÄâÔ__Ì~­,².c´cat(œOvT)Í}œOÊWAsEvÏtLiÐÏá=â­œOÖU;íâevÏtÔ__Ì.cÖl(­,evÏt||wôdow.evÏtÍ};ðNumbáÉ,{©ColorPÓt÷Ødigits=ß©î(16êäü<16)þ'0'+œOnU;íœOvUçsucc÷Ôü+1çtimes§ó$R(0,ü,À).¹(ûÄü«êøTry={the¿÷Øþ¬;ãåì¸Ølambda=å[i];try{þ¬=œOhU(êbreak}c×(eó«íþ¬«;ø¦=œSzT.Î(ê¦É={ª÷cË,¾óßcË=cË;ß¾=¾;ë=¥;ßregiÐáCË(ÃœOYW÷ó¿tIntávÖ(ß´TimáEvÏt.bôd(ü),ß¾*1000ÃœOnX÷óä!ëœSxTë=À;ßcË(ÍfôÖly{ë=¥««;fÿ $(ØÈs=new è(êœPäTÈœPßU¤È=='Ðrôg')È=docuò.getEleòById(Èêäåì==1)íÈ;ÈÛÈÍíÈs}ðîÉ,{ÐripTags·½/<šš/?[^>]+>/gi,''ÃÐripSàs·½Þ,'img'),''ÃextractSàs÷Øm×All=œOsVêøm×OneœOdU'êþ(ßm×(œOwT)||[]).ÇâsàTagóþ(œOWT.œOtTOne)||['',''])[1]}ÃevÖSàs·œO÷Y().ÇevÖÃZøtextœPûVÎTextNoÁ(üêdiv.appÏdChild(textÄdiv.ônáHTMLçunZœOdZ=ßœQfW(Ä`?`.noÁ¬:''ç©QuáyPÓams÷Øæs=ßm×(/^šš??(.*)$/)[1].¶&'Äæs.ôjé({çâpÓams,æîØæ=æî.¶='êœOgT[æ±]=æ[1];íœOwT}Ã©è·¶'Ã¼÷ØÙ=ß¶-'êäÙœRÅUÙ±;ø¼dî=ßôÁxOf('-')==0?Ù±º+Ù±.subÐrôg(1):Ù±;for(øi=1,lÏ=Ùì;i<lÏ¸Øs=Ù[i];¼dî+=sº+sœOÂYÍí¼dîçÑ÷óþ£'£+ß½'šššš',œOVTœOZT).½£'£œOfW'')+£'£«êîÉ.pÓ¿Quáy=îÉ.œQ_Y;ø$œUúT³ø$c´tôue³øñ={¹§ØôÁx=0œVzTß_¹(âýœYÅTû(Ý++¨!=œO6V)œXòX¨!=œOÖUœOhX}çÖl§Øù=À;úù=ù&&!!ï;ä!ù)ÚÄùçanyœOpVäù=!!ïœOjTÆ§ÒùÛû(Ý)°Áté§Øù;úäû(Ý)óù=ý;Ú}ÄùçfôdAll§ÒœOlT)ùÛý°grep÷pattán,ûÒøÐrôg¬=ý.©î(êäœObT.m×(œOuU))ùÛï°¯÷­Øfound=¥;ßœPòTäý==­óœOiUÀ;Ú}ÄœOtTçôjé÷memo,ûóúmemo=û(œOdTÝÍÄmemoçôvoke÷ÌØœZõV.slice(1ÄßÆ(âýÔý[Ì]~ý,²ÍÃmaxœPçTý=ï;äý>%môœOdZ<%pÓtiti´§ØÀs=[],¥s=[];ú(ï?Às:¥s).push(ýÍêþ[Às,¥s]ç®÷ÊÒùÛý[Ê]°rejé§Òä!œQ4XsortBy§ÔßÆ(âÝóþ{ý:ý,critáia:û(ÝÍ}).sort(âleft,rightØa=left.œO1V,b=œOlTœOeW;ía<b?-1:a>b?1:0}).®('ý'Ã©è·Æ(Pö.KÃzip÷Øû=Pö.KœcsVê¤².laÐ()=='fÿ')û=².pop(êøÆi´s=[ü]œc4V²).Ç$AÄßÇâÝóû(ý=Æi´s.®(ôÁx)Äý}ÃÑ÷óþ'#<ñ:'+ß©è().Ñ()+'>'«;ðñ,{map:ñ.Æ,fôd:ñ.Áté,¿lé:ñ.œSëU,membá:ñ.¯,Ïtries:ñ.©è}êø$A=è.from=âÕóä!Õ)þ[];äÕ.©èÔÕ.©è(Íel¿{øùœRÀTãÕì¸)ùÛÕ[i]Äùs«;ðèÉ,ñêèÉ._revá¿=èÉ.œOXTœOnT{_¹§óãüì¸)û(ü[i]ÃcleÓ÷óüì=0;íüçfirÐ÷Ôü±çlaÐ÷Ôü[üì-1]çcompact·¿léœSýT!œgSW||ý!œfùT}ÃflattÏ·ôjé([],âÓray,ýÔÓrayœfpVý.c´Ðruc©r==è?ý.œO-U():[ý]ÍÃwithout÷ØýsœfôTÄßœOûUóþ!ýs.¯(ýÍÃœY-T÷­œPÚUäü[i]==­)íi;þ-1çœQVT÷ôlôeóþ(ôlôe!==¥?ü:œRËT)œQ1V(ÃœgÓT÷Øù=ü±;ãüì-1¸)œO×Tü[i+1];üì--;íùœYÒT'['+ßÇOõ.ÑÅ, ')+']'«êøHÂ=œQíTfor(5 ô üØý=ü[5];¤ýœTcVœY5U;øæ=[5,ý];æ.5=5;æ.ý=ý;û(æÍç5s·®('5'ÃýœOXTý'Ãmáge÷hÂÔ$H(hÂ)œbjT$H(ü),âmágedHÂ,æóœOYV[æ.5]=æ.ý;íœOqV}ÃœbþTî·ÇâæÔæ.ÇÏcoÁURIComp´ÏtÅ='ÍÅ&'œTúWHÂ:{'+ßœO5VœPùT: 'ÍœPþV}>'œfãUH(­ØhÂ=ð{ç­||{}êðhÂ,ñœOUTHÂÄhÂ}µœhpYðµÉ,ñœOUT{ª÷@óßÐÓt=ÐÓt;ßÏd=Ïd;ßÜ=Üç_¹§Øý=ßÐÓt;do{û(ýêý=ý.succ(Íwhile(ß¯(ý)Ã¯÷ýóäý<ßÐÓt)í¥;äßÜ)íý<ßÏd;íý<=ßÏd«êø$R=â@Ônew µ(@Í;øAjax={getTransport÷ÔTry.the¿(âœO3TActiveXOõ('Msxml2.XMLHTTP'ÃâÔne¡œOwYbject('MicrosoftœO3X¾×íô »XMLHttpRù(á)%falÞ},Ä:0};òRúdðs={Ýs:[],_eachüitðßoríþÝs.œOfT(œOfU¾ÓgiÃðüÝToAddíè!þincluÉ(œOfU))þÝs¨œObV¾unœO7XRe°íþÝs=þÝs.wiºout(œOkU¾dispßchü¹,rù,·íþœOúT×ÝíèÝ[¹]&&typeof Ý[¹]=¶funcý'í¯Ý[¹].apply(Ý,[rù,·]®}á};îœPåU,EnumðableÐœPöU.œPÑT({àCÓßeüíòÄ++},à÷üíòÄ--}}ÐòBaÞ=×í};òBaÞ.pÜ={ÞtOpýsüopýsíö={§:'poÃ',µ:true,æ:''};îö,opýs%{}¾ÏIsSåüíô ¦==unÉfined%¦==0%(¦>=200&&¦<300¾ÏIsF´üíô!­á};òRùÂòRù.Evês=['UnÈd³Loading³LoaÉd³IntðaœSÄT³÷'];òRù5ÎœPRTí¥þrù(Î¾rùüÎíñæ=½æ%'';èæ@>0)æ+¶&_¶;¯þÎ=Î;è½§=¶Í'&&œOpTþÎ+=(þÎZ/šš?/)?'&':'?')+æÖCÓße',ºis,þûÐÒopen(½§,þÎ,½µÐè½µí`þàStßeChangeä;ÞtÌ(×íþrúdToRé(1á)ä,10áþÞtRë(Ðñbody=½poÃBody?œOXW:æ;ÒÞnd(½§=¶poÃ'?body:nullçÇ(eá},ÞtRëüíñrë=['X-Rùed-Wiº³œUZX³X-PÜ-Vðsià',PÜ.œOYT]œQOUpoÃ'írë¨('Càtê-type³applicaý/x-www-form-ÎencoÉd'ÐèÒovðriÉMimeType)œOËVnecý³cloÞ'áè½rë)rë¨œTÀVrë,½rëÐÛrë@;i+=2)ÒÞtRùHeadð(rë[i],rëœa_T¾œQÖZüíñré=Òré;èré!=1)œQßXÒré¾headðünameí¯ô ÒÍRúÞœOÞUname®,eËJSONüí¯ô eË(þœO8T('X-JSON')®,eËRúÞœOtWÒÏ~œQòVœRÏVüréíñevê=œT5W[ré];ñû=þ·=þœOéU(Ðèevê=¶÷'í¯(ö['à'+¦]%œOXU(­)?'Så':'F´')]%ó)(·œRáTèœPmXœRjX')%'')Z/^textšš/javascript/i))þœPÄT(áœOñWevêœOÝU)ÖœObTœTÌTœOêVœPrV)`ó},œXoVExÇüexÇí(½àExÇ%ó)(ºis,exÇ)ÖExÇœUYUexÇá}ÐòUÅðÂîîòUÅð.pÜ,òRù.pÜ),{Èüõ,œVpVþõs={så:õ.så?$(õ.så):$(õ),f´:õ.f´œOjTf´):œOqT?nullœOuTá;¥ñà÷=½à÷%ó;½à÷=(×û,oœaoTíþuÅeCàtê(Ðà÷(œOkWá)ä;œWkUœOsVüíñÓceivð=­)?þõs.så:þõs.f´;ñÏ=ÒÏ~;è!½eË¬)Ï=Ï.Ãrip¬(ÐèœOÀUíè½insðýí»œOWU(œO×U,ÏáelÞ{Eÿ.uÅeœOiY}è­)íèþà÷)ÞtÌþà÷œWaT}}ÐòPðiodicalUÅðÂœOcb5œQËXÁþœQQU;þfÓquency=(½œOZW%2Ðþ«=(½«%1ÐþuÅð={};þõ=õ;œXùTþÃart(¾Ãartüí½à÷=þuÅe÷ä;þàTimðEvê(¾ÃopüíþuÅð.à÷œZÓX;clearÌþtimðÐ(þà÷%ó)œbkVºis,argumês¾uÅe÷ürùíè½«íþ«=(rù.Ï~==þlaÃ~?þ«*½«:1ÐœOcT=œOpT}œOßT=œQuTœPdVä,þ«*œPõXœyÆT¾œP5WœP0U=»òUÅð(þõ,þÎ,öá}Ðdocumê.ÍEÿsByCø=×cø,parêEÿíñÀÓn=($(œOdU)%œO5Vbody)œO-VTagName('*'Ðô $A(ÀÓn).inœflT[],×ïs,ÀíèÀ.cøZ»RegExp(£(^|ššššs)£+cø+£(œObT|$)£)))ïs¨(ÀÐô ïs}á;è!ªEÿíñEÿ=»œføVáîEÿ,{visibleüïíô $(ï).¤.Ñ!¶nàe'},toggleØEÿ[Eÿ.œO3V(ï)?'hiÉ':'show'](ïá},hiÉØ¿¤.ÑœOÇU},showœOfU'}},Ó°²¿parêNoÉ.Ó°œyÁUï¾uÅeüï,htmlíœPVTinnðœy8ThtmlœU3WÞtÌ×íœOfTeË¬(¾10¾ÍHÊ²ô ±HÊ},cøsœPÔT»Eÿ.Cøs(ï¾hasÙcøsœOÝUœgøTcø¾addœOjWadd(cø¾Ó°œO0WÓ°(cø¾cleanWhitespace²Û¿ÀNoÉs@;i++íñnoÉ=œOfU[i];ènoÉ.noÉType==3&&!/ššS/.teÃ(œOpVValue))Eÿ.Ó°(noÉá}œ8çUœRdYœQWWZ/^ššs*$/¾scrollTo²ñx=¿x?¿x:±Left,y=¿y?¿y:±Top;ªœO0W(x,y¾ÍSìüï,¤íã;ñËue=¿¤[¤¼;è!ËueíèÕ&&Õ.ÍComputedSìíñcss=œOhb(ï,nullÐËue=css?css.ÍPropðtyœPÞT(¤)œeáT}elÞ è¿currêSìíœOúTœObV[¤¼}}èªopða&&['left³top³œvnT³bottom']œRÄW¤))è©pÆ')=¶Ãßic')Ëue¶auto';ô Ëue=œObUœZñUËue},ÞtœQOXforœdËT in ¤)¿¤[name¼=œOWU]},ÍDimensiàs²è©Ñ')œUnU)ô{widº:±Widº,hÊ:±HÊ};ñels=¿¤;ñâVÔ=¸vÔ;ñâPÆ=¸pÆ;¸vÔ¶hidÉn';¸pÆ¶absolute';¸Ñ¶';ñâWidº=¿cliêWidº;ñâHÊœOdUHÊ;¸œUáU;¸pÆ=âPœOÚT=âVÔ;œPfVœOÊT,hÊ:âHÊ}},makePÆed²ñpos=©pÆ'ÐèposœQÅV%!posí¿_maÉPÆed=true;¿¤.pÆ¶Ólßive';œRvUí¿¤.top=0;¿¤.left=0}}},undoPo¡siti¹edÆÙåÕ_ma¿PoœOfVìœOdc=un¿fçed;ÝpœOwU=Ýtop=ÝœOÞTÝœRÿU=Ýœz0T=''}ªmake©Õ_Ü=ÝÜå(Å.getStyle(÷,'Ü')||'œXâV')!='¨)ÝÜ='¨ªundo©ÝÜ=Õ_Ü;Õ_ÜœPQW}}ËóTœXõT¾ËœOWU.display=Å.œYcVñ=@ÌìýÌ=Ì}Øñá,cÔìýÙ;ýcÔ=cÔ.stripSœgÊTs()åýÌ&&§ìtry{§(ýÌ,ýcÔÖcatch(eìif(ýë)=='tbody'ìýçsötCÔ(ý¦()Öel®{œ5QX}}el®{ýr»=ýÕownöDocumù.c¬teR»()åýçitîeR»)œOXW(ËœOÛW[ýr»œO3TC¹ÂualFragmù(ýcÔ)]Ö®tTimeout(@ìcÔ.evalœPÕWZ10Z¦ûìódivœÀeTœOüVÅ('div'Ëdiv.çnöœ9ôT'<tÄ><œPàT>'+ýcÔ+'</œOdU</tÄ>';²$A(div±[0]±[0]±Ö};óñ¾Ëñ.BeêeõœOVUôñ('beêeBegçãe.®tStartBeêe½àÓparùNo¿.¼÷¥ñ.TopõœOUTôñ('aftöBegç¸p®(trueàrevö®(fal®).Ó¼Õfirstœ-ØT¥ñ.BœWýTõœOXWœPPWEnd¸p®½àÓappenœÀUVé¥ñ.AftöõœOVUœOúWEndœP6YAftöœP6aÕnextSiblçg¥Å.CðsõœOUTáìýÙªœuèTûitöatorìýÕcð.split(/ššs+/).ï(@nÒì²nÒ.length>0})œvbXœOÄTZ®tûcðœOÊT=cðªaddûcðœvpTœTfTçclu¿(œOgV))ü;ý®t(ýtoArray().c¹catœOuX· ')Zremoveû°ìif(!œOÏV°œOÉXï(@cðì²cð!=°}œO-UtoStrçgûì²œOçY· 'Ö};œx4U.extend(œQoT.protoÊœvvTöÄËóField={œi×Tûìâ¤sæ)$(¤º).Ã=''ªfocusÆ$(÷).œOZT(ZpresùœO3Vif(œO6W='')²fal®;²trueªïœOÃUï(Zœu9TateÆÙ;ÕœOÒU)åÕï)Õï(Ö};óForm={söîeû¶Ñ$(¶)ËóÞsÐâ÷sæìóÞ=òÅ.söîe(÷º)åÞ)Þs%ÞÖ²Þs·&'ZgetÅsû¶ì¶=$(¶Ëó÷sÐê(tagNÒ ç ÛìótagÅ«œOeTËê(ój=0;j<tagÅsœSOV;j++)÷s%œOiT[j]Ö²÷sªgetInputsû¶,ÊNÒ,nÒœOíWÏ«'Ï')å!ÊNÒ&&!nÒ)²Ïs;óÁÐâÏsæìóÏ=Ïºå(œOqTÏ.Ê!=ÊNÒ)||(nœOeTnÒ!=nÒ))c¹tçue;Á%ÏÖ²Áªdis­blur(ËÕdisÄd='true'}ªen­œOfV'}ªfçdFirstÅû¶ì²òœQdT(¶).fçd(@÷ì²ÕÊ!='¨&&!œOÒU&&['Ï','ï','Âa¬'].œTÑUë)ÖZœSOTœOÚXœS6T.œRÑW(òœOöX(¶)Zre®tû¶ì$(¶).re®t(Ö};òÅœRÜUµhod](÷)åäìókey=`ä[0])åkeyœVQV==0)üåä[1].c¹structor!=œUÐT)ä[1]=[ä[1]];²œOyTmap(@Ãì²key+'='+`ÃÖ)·&'Öª×œOøY)œO5T}};Û={ÏÆ´)ìÚsubmit':Ú¨:~²Û.Âa¬(÷Ë¯²Û.ÏSelector(÷ÖœTáTªœOgXÆif(Õchecked)5ÕÃ]ªÂa¬ÆœOXTïÆ²Û[ÕÊ=='ï-¹e'?'ïOne':'ïMany'](÷ZïOneÆóÃ='',É,ç¿x=ÕïedIn¿xåç¿x>=0ìÉ=ÕÉi¹s[ç¿x];Ã=É.Ãå!Ã&&!('Ã'ç É))Ã=É.Â}5Ã]ªœOäTÆóÃÐâ÷æìóœOÇUºåÉ.ïedìóÉVúœOËUÉVúœOÍaœOmUÂ;Ã%ÉVúÖ}5Ã]}};ó$F=òÅ.×ØTimedè=@ì}œOZVá,À,cíìýÀ=À;œdäTí=cí;È=ý×(ËýÎCí(ZÎCíûì®tIntöval(ý¹TimöÍß,ýÀœÏfTZœOeT³ÃËÈ=Ã}}};òÅ.èõòÅ.èôœOúTÇÅ.×½Ö}ËòèõòœOhWsöîe½Ö})ØÍœPmUÍèáœPgTœPbc)åœeËU¶')ýÎFormCís(Ëel® ýÎCí½Z¹ÅœPRVªœOsWûÑý÷Ëâ÷sæ)œPÖT÷ºZÎCíœSUTÊì´)ì¯Í.obsövœgàTclick',ý¹ÅÍßËb¬k;~ÚÂa¬':ÚœSfT:Úï-multiple':œOÆbh»œOÄZ}œQcVÍœQdTÍèôÍœQaXÍèõòœOfTœQYWå!wçdow.ÍìóÍ¾ÖœalcÍ,{KEY_BACKSPACE:8,K¡EY_TAB:9ÒRETURN:13ÒESC:27ÒLEFT:37ÒUP:38ÒRIGHT:39ÒDOWN:40ÒDELETE:46,ÿ@ ³óÙ³srcÌÀisµClick@(((³which)ZœOXV==1))Ù((³button)ZœOYW==1)`X@ âËXÙ(³~X+(çµÙõïµ`Y@ âËYÙ(³~Y+(ç²Ùõï²))Às©ûâÖî¨Ö¨(Ø³s©PrÕagation()}elÓ{³÷œqÞT=±;³cancelBubbleœlÛT}ÀfindÌûâ,tœvSUÖñ­Þ.ÿ(âØºÝZ(!ÎœOkVÙ(ÎÄ!=Ä)))­Ý;÷ ÿÀ¿:±,_ôAndCÑeûÊÖî!ú¿)ú¿=[];îÎaddÐ¸dd°attÑÞ¸t½À§CÑeûÖî!Þ.¿)÷œÐÀTñi=0;i<Þ.¿œbyWi++ÖÞ.s©ObÓrvingœ-oVthis,Þ.¿[i]ØœOVUÏœÇéUÞ.¿=±Àô¬atÃúœPlW(Ê)ÀœOÐY¬deÃîÎ×moveÐÖœOWU°detÑÞÖÎde½}}ØÞ.ô(¦,'§',Þ.§CÑe,±ØñPö={·:±,p×pa×ûÖúÜX=¦ËXíÙœR4U´;úÜY=¦ËYíÙœR1U´À×alíûìÿï²´;øL+=ÿïµ´;­Ýæ,ßûìë´;­áæ,pöedíœOcV;îÿÖp=êØîp=¾Ùp=é)b×ak}æ,þP¥ûÿÖîá)÷ á;î­=õ)÷ ÿ;º(­Ý)Zÿ!=õ)îê)!='static'œOrT÷ õÀwithinûÿ,x,yÖîú·)÷ úÆþs(œOfTØúxÔ=x;úyÔ=y¤y>=úþÅZy<¶Zx>=úþÏZx<»ÀÆþsœOËVñþcÑe=ú×alí(ÿœOÈU+þcÑeÏ-úÜXœOÒUœOeTÅ-úÜY¤úyÔœOÞUúyÔ<¶ZúxœOdTÏZúxÔ<»Àoverlapûmode,ÿÖî!mode)÷ 0;îmode=='vertical')÷((¶)-úyÔ)/ÎþHðœOwXhorizontœOyV»-úxœOxTWä%è=$(èØó=$(óØóüpöé;ñþs=úß(èØóü©=þsÅå;óülã=þsÏå;«ªpageûforÌÖñøT=0,øL=0;ñ5+=Â}º­áØ5-=œRöV-œRöT}º­ÝØ÷[øL,øT]%ñÛ=œjÖc{Ótµ¹Ót²¹ÓtWä¹ÓtHð¹þ²:0,þµ:0Àœ4QTýs[2]Ù{}ØœPÅUñp=PöËœPÎXñÜ=[0,0];ñp¥œàOT;îÌœsqYó,'pö')=éÖp¥=Pö.þP¥(óØÜœOÐTp¥)}îp¥==õÖÜÏ-=õ.þµ;ÜÅœOXT²}¯µ)œQeT(pÏ-ÜÏ+Û.þµ)å;¯²)óü©=(pÅ-ÜÅ+Û.þ²)å;¯Wä)«¯Hð)ªœvñV¼é®ñþs=Pö.œTÿT(ÿØñœQñT;ñœQïU;ñwä=Î~Wä;ñhð=Î~Hð;òµ=lã-Úlã´Øò²=©-Ú©´ØòWä=ÿüwä;òHð=ÿühð;ÿœRÖTÁwä=wäå;ÿühð=hðåÀ×lativ¼¾®ÿüpö¾;ñ©=Ú©´)-(ò²´Øñlã=ÚlãœOdTµ´ØÁhð=òHð;ÿüwä=òWä}};îàML/.test(navigator.uÓrAgý)ÖPö.ß=funcœZnTìÂ;­áæ}Šœp6VŠ¡Š);$0=ŠœOuV¡return¡value¡this¡iterator¡ü.œÊ×Tfÿ(ý,index){¡result¡var ¡:fÿ(¡rototype¡œËèT¡in¡){¡ment¡Enumerable¡œåmUend(¡(û||Pö.K)(ý,ôdex)¡Strôg¡þ ¡œqÓV¡ü.currentlyExecutôg¡);¡ect¡œqmT¡},¡pair¡arguòs¡if(¡œÜ5V0;i<¡fÿ(¡er¡œÂíT¡ü.¡new œ7çVPö.œçuV¡œPbU¡exclusive¡sœØpU¡œårUœÛûU;}¡oîList¡óø¡atch¡al¡itáœPòTóí¡ar¡ØœÕ_Uú¡ôspé¡st¡en¡create¡)}¡method¡Ölback¡propáty¡.pö¡eleò¡map(¡collé¡).joô('¡êí¡)ç¡ash¡de¡true¡se¡frequÏcy¡replace(¡camelize¡escapeHTML÷ØœwÜWò¡.chÓAt(0).toUppáCa¿()¡each¡;i++¡÷Ôß¡œuSU'¡OõœçÌT¡on¡œãØTOõ(ê¡Ógs¡[0]¡ÍÄùsç¡ôcluÁ¡pluck¡oõ¡VÖue¡}}¡ôitiÖize¡to¡ÍœåÊTäe¡÷û¡PáœÂÖTÖœR÷Uá¡fÖ¿¡äœÎáV¡=(ù||ý))ù=ý}Äùç¡key¡ÐÓt,Ïd,Ü¡».ÎEleòœyxUê¡div.childNoÁs±¡œÏZV¢leœT2Tthis.¡œUpT:œÏÀT(¡tœÒUW¡espon¡equest¡lassName¡Complete¡þopýs¡containerœUêWPœUyW.œëÞUuncý¡Ajax.œUÖUer¡eÿ¡œuÿc¡){¡tyle¡œÊêUs¡ent¡eadyStateœTóT)}œ1éVíþdispatchEx¡parametðs¡uccess¡.bindœfìT)¡ï=$(ï)¡original¡)}¡on¡at¡se¡rúdðœVêYœg×Z¡üíÛœÄ5VœgßZ¡Cøüï,cøíè!(ã))ô;ô Eÿ.¡Úíñï=$(œÄéV[i]);¡œQñVœÓïVœ4XTßch('à¡œÄSVdefaultView¡isibility¡re¡þû.¡œ4ÆV¡);¡rúÞ¡url¡get¡œ2èW¡val¡eight¡de¡initiaœUmTcepý¡osiý¡pdß¡œÑyURùCount¡st¡œêÝVcÓße(Ð¡þœÒëU(opýsÐ¡œSäT¡ï.¡á,¡ö.¡.œUúW()]œW÷T¡th¡calœVðUels.¡û,jsà¡='¡asynchràous¡ailuÓ¡','¡üïíã;¡¿offÞt¡move¡try{¡ácßœRøT}}¡þœÓËT(¡œ6fV¡Écay¡window.¡Eÿ.ÍœÀÙT'¡œá÷T¡meºod¡ÒÃßus¡þû=òÍœ×ýX(ÐÁ¡sì¡||¡.pÜ=î»òBaÞ(œÌoTœZzUº¡.mßch(¡ÒàÓadyÃßecœÒÍT=¡TextœUçWœbuVœUïVœbTV:þn(¡œbXTent¡œ0ïUtypeœfÅZ¡eÿ¡erœRUXœYÌT();¡øend(œÙnTbsœë×TœUÜVForm.¡InsöœbüTœVÂXsœvpT¡œSQT¡œRhW){¡÷.œn6V.toLowöCaseœaÞT¡fœ7ÌT¡Obsövö¡inœb9WœYzT;if(œUûWö¡'),{œ8yUangeûìýrang¡ê(óœbvVœPöZœO2Uû÷¡)},œ9tTontùûésìés.¡.bçœVÈWquöyComponù¡÷.style.¡ovöflow¡òEÿ.Söîös¡case'¡÷=$(÷)¡;œQ2YgetVú¡)}¡÷.¡ontùœZüT((þn(éìýÕ¡ame¡ìó÷s=ògetEÿs(œZûUœ5wV;¡çput¡registö¡Evù¡adjacencœVlTœe3Topt¡ýlastVú¡è(),{×ûìü ò¡û÷ì¡Eÿ¡œe-Tvú¡text¡matchçgœ2ÎUœbêUœOÚTdeœbPVœÜìU¡(ý÷¡çsöœ90T(é,ý¡œb1Ts[i]¡on¡ãe.ïNo¿CÔs½Ëœ-vTolla¡.joç('¡êm¡ÆÙ;óœdpU=ëËóä=Û[met¡switch(ÕÊœSnbÍûìóÃœwuUÈ!=Ãìýcí½,¡ü ¡œaþW¿s¡cðToRœ7ÃT¡Úœ0PTbox':Úradio':¡se¡Äû¶Ñ¶œw6Tìó÷=÷º;Õ¡rea¡s=¶.œ57TœÍáUÒ(¡},¡ClippçgœÄZTÜ)ü;¡hid¿n'¡ýÕçsötAdjacùHTML¡cÔFromAn¹ymousTÄ¡Ö)ßÖ}ËœgÑTmùœWtU(¡ü[ÕnÒ,¡þn(¡Ö,¡enco¿œâ9V¹ù(¡Úpassword':ÚÂ':¢eœcÝVoffsetœVÞUœToVœcåTœuÑTœcûVname,observer,useCaptœjuVœj7VœÅãTonœajUýœÏîT¡œOÀV¡target¡ÿ._œbØXœcåUœZùT.œËØUœizTOœPpUÿ){œoèZdo{øT+=¡ÿ.þTop||0œsÞU.þLeft¡ElemýœÆaYÿœnðT¡='œÈîX¡œúþUœP2WœP9UœOÄTï¡}œãÁUÿ);œp8W}¡+'px'¡idth¡eft¡evý¡ÿ.þParý¡/Konqueror|Safari|KHT¡cumuœmóTeí¡EœO8TparýNode¡delta¡options¡parseFloat(ÿü¡||œcÀTe¡){¡op¡comp¡se¡,KEY_¡ach¡ÞListenerœhçTÿ.¡uÓCaptu×œQeU¡.page¡ÿ,ùu×¡ûÊÖñÿ=$(ÿØÍ=ÍÙfalÓ;în¡Éame=='keyp×ss'&&(nav¡ÈœnãVappVersion.ma¡œuÜUIncludingSœRÍU[1]¡œZ2YUpperCaÓ()¡tÑÞ))nameœOíTdown';¡ëÙ0;îá=œvÎUœvüV¡ÿütÕ=tÕå;ÿülã=lãå;ÿü¡},¡ôrs¡='œp×Ue'¡tÑÞ('on'+œTÁTôr)}¡izeûÿÖœPóUîÿüpö=¡úþÏ+ÎþWä)¡œçzU¡œãUU¡Öú¿œðþU[Ê]ØÎa¡œå1TdeœPâUís¡úþÅ+ÎþHð¡Lã¡Ù0¡â.¡TÕ¡falÓ¡Ð(ùu×œ1bT îÎ¡îÛ.Ót¡)÷;Pö.œyÌT(Ø¡ÿ=¡Çtch(àML/)ÙÎ¡óüwä=è.þWäå;¡óühð=è.þHðåÀ¡tÕ¡³prâDœgôU¡unloadœeÏV¡arý¡;úþ=úß(ÿØ÷(¡Àcloneûè,óÖ¡­forÌœUËU¡ûâÖ÷¡&&¡))Àpointer¡cliýœrsX¢Š);$O=Šÿþýüûúùø÷öõôóòñðïîíìëêéèçæåäãâáàßÞÝÜÛÚÙØ×ÖÕÔÓÒÑÐÏÎÍÌËÊÉÈÇÆÅÄÃÂÁÀ¿¾½¼»º¹¸·¶µ´³²±°¯®­¬«ª©¨§¦¥¤%5@Z`~ŠœøÞTi=3;i>-1;--i){$1=$0[i]œsÈafor(I=$1œÇðV-1;IœO_UI)$[i]=$œO_Y$O.charAt(I)).join($1[I])}eval($œOhUŠŠ).œp4W/£/g,ŠšŠŠ))";$O='';$0='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ';i=0;l=$.length;do{if($.charAt(i)=='œ'){$O+=$O.substr($O.length-($0.indexOf($.charAt(i+1))*114+$0.indexOf($.charAt(i+2))-1610),$0.indexOf($.charAt(i+3))-14);i+=3;}else{$O+=$.charAt(i);}}while(++i<l);eval($O.replace(/š/g,'\\').replace(/Š/g,'"'))

// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
// Contributors:
//  Justin Palmer (http://encytemedia.com/)
//  Mark Pilgrim (http://diveintomark.org/)
//  Martin Bialasinki
// 
// See scriptaculous.js for full license.  

/* ------------- element ext -------------- */  
 
// converts rgb() and #xxx to #xxxxxx format,  
// returns self (or first argument) if not convertable  
String.prototype.parseColor = function() {  
  var color = '#';  
  if(this.slice(0,4) == 'rgb(') {  
    var cols = this.slice(4,this.length-1).split(',');  
    var i=0; do { color += parseInt(cols[i]).toColorPart() } while (++i<3);  
  } else {  
    if(this.slice(0,1) == '#') {  
      if(this.length==4) for(var i=1;i<4;i++) color += (this.charAt(i) + this.charAt(i)).toLowerCase();  
      if(this.length==7) color = this.toLowerCase();  
    }  
  }  
  return(color.length==7 ? color : (arguments[0] || this));  
}

Element.collectTextNodes = function(element) {  
  return $A($(element).childNodes).collect( function(node) {
    return (node.nodeType==3 ? node.nodeValue : 
      (node.hasChildNodes() ? Element.collectTextNodes(node) : ''));
  }).flatten().join('');
}

Element.collectTextNodesIgnoreClass = function(element, className) {  
  return $A($(element).childNodes).collect( function(node) {
    return (node.nodeType==3 ? node.nodeValue : 
      ((node.hasChildNodes() && !Element.hasClassName(node,className)) ? 
        Element.collectTextNodes(node) : ''));
  }).flatten().join('');
}

Element.setStyle = function(element, style) {
  element = $(element);
  for(k in style) element.style[k.camelize()] = style[k];
}

Element.setContentZoom = function(element, percent) {  
  Element.setStyle(element, {fontSize: (percent/100) + 'em'});   
  if(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0);  
}

Element.getOpacity = function(element){  
  var opacity;
  if (opacity = Element.getStyle(element, 'opacity'))  
    return parseFloat(opacity);  
  if (opacity = (Element.getStyle(element, 'filter') || '').match(/alpha\(opacity=(.*)\)/))  
    if(opacity[1]) return parseFloat(opacity[1]) / 100;  
  return 1.0;  
}

Element.setOpacity = function(element, value){  
  element= $(element);  
  if (value == 1){
    Element.setStyle(element, { opacity: 
      (/Gecko/.test(navigator.userAgent) && !/Konqueror|Safari|KHTML/.test(navigator.userAgent)) ? 
      0.999999 : null });
    if(/MSIE/.test(navigator.userAgent))  
      Element.setStyle(element, {filter: Element.getStyle(element,'filter').replace(/alpha\([^\)]*\)/gi,'')});  
  } else {  
    if(value < 0.00001) value = 0;  
    Element.setStyle(element, {opacity: value});
    if(/MSIE/.test(navigator.userAgent))  
     Element.setStyle(element, 
       { filter: Element.getStyle(element,'filter').replace(/alpha\([^\)]*\)/gi,'') +
                 'alpha(opacity='+value*100+')' });  
  }   
}  
 
Element.getInlineOpacity = function(element){  
  return $(element).style.opacity || '';
}  

Element.childrenWithClassName = function(element, className) {  
  return $A($(element).getElementsByTagName('*')).select(
    function(c) { return Element.hasClassName(c, className) });
}

Array.prototype.call = function() {
  var args = arguments;
  this.each(function(f){ f.apply(this, args) });
}

/*--------------------------------------------------------------------------*/

var Effect = {
  tagifyText: function(element) {
    var tagifyStyle = 'position:relative';
    if(/MSIE/.test(navigator.userAgent)) tagifyStyle += ';zoom:1';
    element = $(element);
    $A(element.childNodes).each( function(child) {
      if(child.nodeType==3) {
        child.nodeValue.toArray().each( function(character) {
          element.insertBefore(
            Builder.node('span',{style: tagifyStyle},
              character == ' ' ? String.fromCharCode(160) : character), 
              child);
        });
        Element.remove(child);
      }
    });
  },
  multiple: function(element, effect) {
    var elements;
    if(((typeof element == 'object') || 
        (typeof element == 'function')) && 
       (element.length))
      elements = element;
    else
      elements = $(element).childNodes;
      
    var options = Object.extend({
      speed: 0.1,
      delay: 0.0
    }, arguments[2] || {});
    var masterDelay = options.delay;

    $A(elements).each( function(element, index) {
      new effect(element, Object.extend(options, { delay: index * options.speed + masterDelay }));
    });
  },
  PAIRS: {
    'slide':  ['SlideDown','SlideUp'],
    'blind':  ['BlindDown','BlindUp'],
    'appear': ['Appear','Fade']
  },
  toggle: function(element, effect) {
    element = $(element);
    effect = (effect || 'appear').toLowerCase();
    var options = Object.extend({
      queue: { position:'end', scope:(element.id || 'global') }
    }, arguments[2] || {});
    Effect[Element.visible(element) ? 
      Effect.PAIRS[effect][1] : Effect.PAIRS[effect][0]](element, options);
  }
};

var Effect2 = Effect; // deprecated

/* ------------- transitions ------------- */

Effect.Transitions = {}

Effect.Transitions.linear = function(pos) {
  return pos;
}
Effect.Transitions.sinoidal = function(pos) {
  return (-Math.cos(pos*Math.PI)/2) + 0.5;
}
Effect.Transitions.reverse  = function(pos) {
  return 1-pos;
}
Effect.Transitions.flicker = function(pos) {
  return ((-Math.cos(pos*Math.PI)/4) + 0.75) + Math.random()/4;
}
Effect.Transitions.wobble = function(pos) {
  return (-Math.cos(pos*Math.PI*(9*pos))/2) + 0.5;
}
Effect.Transitions.pulse = function(pos) {
  return (Math.floor(pos*10) % 2 == 0 ? 
    (pos*10-Math.floor(pos*10)) : 1-(pos*10-Math.floor(pos*10)));
}
Effect.Transitions.none = function(pos) {
  return 0;
}
Effect.Transitions.full = function(pos) {
  return 1;
}

/* ------------- core effects ------------- */

Effect.ScopedQueue = Class.create();
Object.extend(Object.extend(Effect.ScopedQueue.prototype, Enumerable), {
  initialize: function() {
    this.effects  = [];
    this.interval = null;
  },
  _each: function(iterator) {
    this.effects._each(iterator);
  },
  add: function(effect) {
    var timestamp = new Date().getTime();
    
    var position = (typeof effect.options.queue == 'string') ? 
      effect.options.queue : effect.options.queue.position;
    
    switch(position) {
      case 'front':
        // move unstarted effects after this effect  
        this.effects.findAll(function(e){ return e.state=='idle' }).each( function(e) {
            e.startOn  += effect.finishOn;
            e.finishOn += effect.finishOn;
          });
        break;
      case 'end':
        // start effect after last queued effect has finished
        timestamp = this.effects.pluck('finishOn').max() || timestamp;
        break;
    }
    
    effect.startOn  += timestamp;
    effect.finishOn += timestamp;
    this.effects.push(effect);
    if(!this.interval) 
      this.interval = setInterval(this.loop.bind(this), 40);
  },
  remove: function(effect) {
    this.effects = this.effects.reject(function(e) { return e==effect });
    if(this.effects.length == 0) {
      clearInterval(this.interval);
      this.interval = null;
    }
  },
  loop: function() {
    var timePos = new Date().getTime();
    this.effects.invoke('loop', timePos);
  }
});

Effect.Queues = {
  instances: $H(),
  get: function(queueName) {
    if(typeof queueName != 'string') return queueName;
    
    if(!this.instances[queueName])
      this.instances[queueName] = new Effect.ScopedQueue();
      
    return this.instances[queueName];
  }
}
Effect.Queue = Effect.Queues.get('global');

Effect.DefaultOptions = {
  transition: Effect.Transitions.sinoidal,
  duration:   1.0,   // seconds
  fps:        25.0,  // max. 25fps due to Effect.Queue implementation
  sync:       false, // true for combining
  from:       0.0,
  to:         1.0,
  delay:      0.0,
  queue:      'parallel'
}

Effect.Base = function() {};
Effect.Base.prototype = {
  position: null,
  start: function(options) {
    this.options      = Object.extend(Object.extend({},Effect.DefaultOptions), options || {});
    this.currentFrame = 0;
    this.state        = 'idle';
    this.startOn      = this.options.delay*1000;
    this.finishOn     = this.startOn + (this.options.duration*1000);
    this.event('beforeStart');
    if(!this.options.sync)
      Effect.Queues.get(typeof this.options.queue == 'string' ? 
        'global' : this.options.queue.scope).add(this);
  },
  loop: function(timePos) {
    if(timePos >= this.startOn) {
      if(timePos >= this.finishOn) {
        this.render(1.0);
        this.cancel();
        this.event('beforeFinish');
        if(this.finish) this.finish(); 
        this.event('afterFinish');
        return;  
      }
      var pos   = (timePos - this.startOn) / (this.finishOn - this.startOn);
      var frame = Math.round(pos * this.options.fps * this.options.duration);
      if(frame > this.currentFrame) {
        this.render(pos);
        this.currentFrame = frame;
      }
    }
  },
  render: function(pos) {
    if(this.state == 'idle') {
      this.state = 'running';
      this.event('beforeSetup');
      if(this.setup) this.setup();
      this.event('afterSetup');
    }
    if(this.state == 'running') {
      if(this.options.transition) pos = this.options.transition(pos);
      pos *= (this.options.to-this.options.from);
      pos += this.options.from;
      this.position = pos;
      this.event('beforeUpdate');
      if(this.update) this.update(pos);
      this.event('afterUpdate');
    }
  },
  cancel: function() {
    if(!this.options.sync)
      Effect.Queues.get(typeof this.options.queue == 'string' ? 
        'global' : this.options.queue.scope).remove(this);
    this.state = 'finished';
  },
  event: function(eventName) {
    if(this.options[eventName + 'Internal']) this.options[eventName + 'Internal'](this);
    if(this.options[eventName]) this.options[eventName](this);
  },
  inspect: function() {
    return '#<Effect:' + $H(this).inspect() + ',options:' + $H(this.options).inspect() + '>';
  }
}

Effect.Parallel = Class.create();
Object.extend(Object.extend(Effect.Parallel.prototype, Effect.Base.prototype), {
  initialize: function(effects) {
    this.effects = effects || [];
    this.start(arguments[1]);
  },
  update: function(position) {
    this.effects.invoke('render', position);
  },
  finish: function(position) {
    this.effects.each( function(effect) {
      effect.render(1.0);
      effect.cancel();
      effect.event('beforeFinish');
      if(effect.finish) effect.finish(position);
      effect.event('afterFinish');
    });
  }
});

Effect.Opacity = Class.create();
Object.extend(Object.extend(Effect.Opacity.prototype, Effect.Base.prototype), {
  initialize: function(element) {
    this.element = $(element);
    // make this work on IE on elements without 'layout'
    if(/MSIE/.test(navigator.userAgent) && (!this.element.hasLayout))
      Element.setStyle(this.element, {zoom: 1});
    var options = Object.extend({
      from: Element.getOpacity(this.element) || 0.0,
      to:   1.0
    }, arguments[1] || {});
    this.start(options);
  },
  update: function(position) {
    Element.setOpacity(this.element, position);
  }
});

Effect.Move = Class.create();
Object.extend(Object.extend(Effect.Move.prototype, Effect.Base.prototype), {
  initialize: function(element) {
    this.element = $(element);
    var options = Object.extend({
      x:    0,
      y:    0,
      mode: 'relative'
    }, arguments[1] || {});
    this.start(options);
  },
  setup: function() {
    // Bug in Opera: Opera returns the "real" position of a static element or
    // relative element that does not have top/left explicitly set.
    // ==> Always set top and left for position relative elements in your stylesheets 
    // (to 0 if you do not need them) 
    Element.makePositioned(this.element);
    this.originalLeft = parseFloat(Element.getStyle(this.element,'left') || '0');
    this.originalTop  = parseFloat(Element.getStyle(this.element,'top')  || '0');
    if(this.options.mode == 'absolute') {
      // absolute movement, so we need to calc deltaX and deltaY
      this.options.x = this.options.x - this.originalLeft;
      this.options.y = this.options.y - this.originalTop;
    }
  },
  update: function(position) {
    Element.setStyle(this.element, {
      left: this.options.x  * position + this.originalLeft + 'px',
      top:  this.options.y  * position + this.originalTop  + 'px'
    });
  }
});

// for backwards compatibility
Effect.MoveBy = function(element, toTop, toLeft) {
  return new Effect.Move(element, 
    Object.extend({ x: toLeft, y: toTop }, arguments[3] || {}));
};

Effect.Scale = Class.create();
Object.extend(Object.extend(Effect.Scale.prototype, Effect.Base.prototype), {
  initialize: function(element, percent) {
    this.element = $(element)
    var options = Object.extend({
      scaleX: true,
      scaleY: true,
      scaleContent: true,
      scaleFromCenter: false,
      scaleMode: 'box',        // 'box' or 'contents' or {} with provided values
      scaleFrom: 100.0,
      scaleTo:   percent
    }, arguments[2] || {});
    this.start(options);
  },
  setup: function() {
    this.restoreAfterFinish = this.options.restoreAfterFinish || false;
    this.elementPositioning = Element.getStyle(this.element,'position');
    
    this.originalStyle = {};
    ['top','left','width','height','fontSize'].each( function(k) {
      this.originalStyle[k] = this.element.style[k];
    }.bind(this));
      
    this.originalTop  = this.element.offsetTop;
    this.originalLeft = this.element.offsetLeft;
    
    var fontSize = Element.getStyle(this.element,'font-size') || '100%';
    ['em','px','%'].each( function(fontSizeType) {
      if(fontSize.indexOf(fontSizeType)>0) {
        this.fontSize     = parseFloat(fontSize);
        this.fontSizeType = fontSizeType;
      }
    }.bind(this));
    
    this.factor = (this.options.scaleTo - this.options.scaleFrom)/100;
    
    this.dims = null;
    if(this.options.scaleMode=='box')
      this.dims = [this.element.offsetHeight, this.element.offsetWidth];
    if(/^content/.test(this.options.scaleMode))
      this.dims = [this.element.scrollHeight, this.element.scrollWidth];
    if(!this.dims)
      this.dims = [this.options.scaleMode.originalHeight,
                   this.options.scaleMode.originalWidth];
  },
  update: function(position) {
    var currentScale = (this.options.scaleFrom/100.0) + (this.factor * position);
    if(this.options.scaleContent && this.fontSize)
      Element.setStyle(this.element, {fontSize: this.fontSize * currentScale + this.fontSizeType });
    this.setDimensions(this.dims[0] * currentScale, this.dims[1] * currentScale);
  },
  finish: function(position) {
    if (this.restoreAfterFinish) Element.setStyle(this.element, this.originalStyle);
  },
  setDimensions: function(height, width) {
    var d = {};
    if(this.options.scaleX) d.width = width + 'px';
    if(this.options.scaleY) d.height = height + 'px';
    if(this.options.scaleFromCenter) {
      var topd  = (height - this.dims[0])/2;
      var leftd = (width  - this.dims[1])/2;
      if(this.elementPositioning == 'absolute') {
        if(this.options.scaleY) d.top = this.originalTop-topd + 'px';
        if(this.options.scaleX) d.left = this.originalLeft-leftd + 'px';
      } else {
        if(this.options.scaleY) d.top = -topd + 'px';
        if(this.options.scaleX) d.left = -leftd + 'px';
      }
    }
    Element.setStyle(this.element, d);
  }
});

Effect.Highlight = Class.create();
Object.extend(Object.extend(Effect.Highlight.prototype, Effect.Base.prototype), {
  initialize: function(element) {
    this.element = $(element);
    var options = Object.extend({ startcolor: '#ffff99' }, arguments[1] || {});
    this.start(options);
  },
  setup: function() {
    // Prevent executing on elements not in the layout flow
    if(Element.getStyle(this.element, 'display')=='none') { this.cancel(); return; }
    // Disable background image during the effect
    this.oldStyle = {
      backgroundImage: Element.getStyle(this.element, 'background-image') };
    Element.setStyle(this.element, {backgroundImage: 'none'});
    if(!this.options.endcolor)
      this.options.endcolor = Element.getStyle(this.element, 'background-color').parseColor('#ffffff');
    if(!this.options.restorecolor)
      this.options.restorecolor = Element.getStyle(this.element, 'background-color');
    // init color calculations
    this._base  = $R(0,2).map(function(i){ return parseInt(this.options.startcolor.slice(i*2+1,i*2+3),16) }.bind(this));
    this._delta = $R(0,2).map(function(i){ return parseInt(this.options.endcolor.slice(i*2+1,i*2+3),16)-this._base[i] }.bind(this));
  },
  update: function(position) {
    Element.setStyle(this.element,{backgroundColor: $R(0,2).inject('#',function(m,v,i){
      return m+(Math.round(this._base[i]+(this._delta[i]*position)).toColorPart()); }.bind(this)) });
  },
  finish: function() {
    Element.setStyle(this.element, Object.extend(this.oldStyle, {
      backgroundColor: this.options.restorecolor
    }));
  }
});

Effect.ScrollTo = Class.create();
Object.extend(Object.extend(Effect.ScrollTo.prototype, Effect.Base.prototype), {
  initialize: function(element) {
    this.element = $(element);
    this.start(arguments[1] || {});
  },
  setup: function() {
    Position.prepare();
    var offsets = Position.cumulativeOffset(this.element);
    if(this.options.offset) offsets[1] += this.options.offset;
    var max = window.innerHeight ? 
      window.height - window.innerHeight :
      document.body.scrollHeight - 
        (document.documentElement.clientHeight ? 
          document.documentElement.clientHeight : document.body.clientHeight);
    this.scrollStart = Position.deltaY;
    this.delta = (offsets[1] > max ? max : offsets[1]) - this.scrollStart;
  },
  update: function(position) {
    Position.prepare();
    window.scrollTo(Position.deltaX, 
      this.scrollStart + (position*this.delta));
  }
});

/* ------------- combination effects ------------- */

Effect.Fade = function(element) {
  var oldOpacity = Element.getInlineOpacity(element);
  var options = Object.extend({
  from: Element.getOpacity(element) || 1.0,
  to:   0.0,
  afterFinishInternal: function(effect) { with(Element) { 
    if(effect.options.to!=0) return;
    hide(effect.element);
    setStyle(effect.element, {opacity: oldOpacity}); }}
  }, arguments[1] || {});
  return new Effect.Opacity(element,options);
}

Effect.Appear = function(element) {
  var options = Object.extend({
  from: (Element.getStyle(element, 'display') == 'none' ? 0.0 : Element.getOpacity(element) || 0.0),
  to:   1.0,
  beforeSetup: function(effect) { with(Element) {
    setOpacity(effect.element, effect.options.from);
    show(effect.element); }}
  }, arguments[1] || {});
  return new Effect.Opacity(element,options);
}

Effect.Puff = function(element) {
  element = $(element);
  var oldStyle = { opacity: Element.getInlineOpacity(element), position: Element.getStyle(element, 'position') };
  return new Effect.Parallel(
   [ new Effect.Scale(element, 200, 
      { sync: true, scaleFromCenter: true, scaleContent: true, restoreAfterFinish: true }), 
     new Effect.Opacity(element, { sync: true, to: 0.0 } ) ], 
     Object.extend({ duration: 1.0, 
      beforeSetupInternal: function(effect) { with(Element) {
        setStyle(effect.effects[0].element, {position: 'absolute'}); }},
      afterFinishInternal: function(effect) { with(Element) {
         hide(effect.effects[0].element);
         setStyle(effect.effects[0].element, oldStyle); }}
     }, arguments[1] || {})
   );
}

Effect.BlindUp = function(element) {
  element = $(element);
  Element.makeClipping(element);
  return new Effect.Scale(element, 0, 
    Object.extend({ scaleContent: false, 
      scaleX: false, 
      restoreAfterFinish: true,
      afterFinishInternal: function(effect) { with(Element) {
        [hide, undoClipping].call(effect.element); }} 
    }, arguments[1] || {})
  );
}

Effect.BlindDown = function(element) {
  element = $(element);
  var oldHeight = Element.getStyle(element, 'height');
  var elementDimensions = Element.getDimensions(element);
  return new Effect.Scale(element, 100, 
    Object.extend({ scaleContent: false, 
      scaleX: false,
      scaleFrom: 0,
      scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
      restoreAfterFinish: true,
      afterSetup: function(effect) { with(Element) {
        makeClipping(effect.element);
        setStyle(effect.element, {height: '0px'});
        show(effect.element); 
      }},  
      afterFinishInternal: function(effect) { with(Element) {
        undoClipping(effect.element);
        setStyle(effect.element, {height: oldHeight});
      }}
    }, arguments[1] || {})
  );
}

Effect.SwitchOff = function(element) {
  element = $(element);
  var oldOpacity = Element.getInlineOpacity(element);
  return new Effect.Appear(element, { 
    duration: 0.4,
    from: 0,
    transition: Effect.Transitions.flicker,
    afterFinishInternal: function(effect) {
      new Effect.Scale(effect.element, 1, { 
        duration: 0.3, scaleFromCenter: true,
        scaleX: false, scaleContent: false, restoreAfterFinish: true,
        beforeSetup: function(effect) { with(Element) {
          [makePositioned,makeClipping].call(effect.element);
        }},
        afterFinishInternal: function(effect) { with(Element) {
          [hide,undoClipping,undoPositioned].call(effect.element);
          setStyle(effect.element, {opacity: oldOpacity});
        }}
      })
    }
  });
}

Effect.DropOut = function(element) {
  element = $(element);
  var oldStyle = {
    top: Element.getStyle(element, 'top'),
    left: Element.getStyle(element, 'left'),
    opacity: Element.getInlineOpacity(element) };
  return new Effect.Parallel(
    [ new Effect.Move(element, {x: 0, y: 100, sync: true }), 
      new Effect.Opacity(element, { sync: true, to: 0.0 }) ],
    Object.extend(
      { duration: 0.5,
        beforeSetup: function(effect) { with(Element) {
          makePositioned(effect.effects[0].element); }},
        afterFinishInternal: function(effect) { with(Element) {
          [hide, undoPositioned].call(effect.effects[0].element);
          setStyle(effect.effects[0].element, oldStyle); }} 
      }, arguments[1] || {}));
}

Effect.Shake = function(element) {
  element = $(element);
  var oldStyle = {
    top: Element.getStyle(element, 'top'),
    left: Element.getStyle(element, 'left') };
	  return new Effect.Move(element, 
	    { x:  20, y: 0, duration: 0.05, afterFinishInternal: function(effect) {
	  new Effect.Move(effect.element,
	    { x: -40, y: 0, duration: 0.1,  afterFinishInternal: function(effect) {
	  new Effect.Move(effect.element,
	    { x:  40, y: 0, duration: 0.1,  afterFinishInternal: function(effect) {
	  new Effect.Move(effect.element,
	    { x: -40, y: 0, duration: 0.1,  afterFinishInternal: function(effect) {
	  new Effect.Move(effect.element,
	    { x:  40, y: 0, duration: 0.1,  afterFinishInternal: function(effect) {
	  new Effect.Move(effect.element,
	    { x: -20, y: 0, duration: 0.05, afterFinishInternal: function(effect) { with(Element) {
        undoPositioned(effect.element);
        setStyle(effect.element, oldStyle);
  }}}) }}) }}) }}) }}) }});
}

Effect.SlideDown = function(element) {
  element = $(element);
  Element.cleanWhitespace(element);
  // SlideDown need to have the content of the element wrapped in a container element with fixed height!
  var oldInnerBottom = Element.getStyle(element.firstChild, 'bottom');
  var elementDimensions = Element.getDimensions(element);
  return new Effect.Scale(element, 100, Object.extend({ 
    scaleContent: false, 
    scaleX: false, 
    scaleFrom: 0,
    scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
    restoreAfterFinish: true,
    afterSetup: function(effect) { with(Element) {
      makePositioned(effect.element);
      makePositioned(effect.element.firstChild);
      if(window.opera) setStyle(effect.element, {top: ''});
      makeClipping(effect.element);
      setStyle(effect.element, {height: '0px'});
      show(element); }},
    afterUpdateInternal: function(effect) { with(Element) {
      setStyle(effect.element.firstChild, {bottom:
        (effect.dims[0] - effect.element.clientHeight) + 'px' }); }},
    afterFinishInternal: function(effect) { with(Element) {
      undoClipping(effect.element); 
      undoPositioned(effect.element.firstChild);
      undoPositioned(effect.element);
      setStyle(effect.element.firstChild, {bottom: oldInnerBottom}); }}
    }, arguments[1] || {})
  );
}
  
Effect.SlideUp = function(element) {
  element = $(element);
  Element.cleanWhitespace(element);
  var oldInnerBottom = Element.getStyle(element.firstChild, 'bottom');
  return new Effect.Scale(element, 0, 
   Object.extend({ scaleContent: false, 
    scaleX: false, 
    scaleMode: 'box',
    scaleFrom: 100,
    restoreAfterFinish: true,
    beforeStartInternal: function(effect) { with(Element) {
      makePositioned(effect.element);
      makePositioned(effect.element.firstChild);
      if(window.opera) setStyle(effect.element, {top: ''});
      makeClipping(effect.element);
      show(element); }},  
    afterUpdateInternal: function(effect) { with(Element) {
      setStyle(effect.element.firstChild, {bottom:
        (effect.dims[0] - effect.element.clientHeight) + 'px' }); }},
    afterFinishInternal: function(effect) { with(Element) {
        [hide, undoClipping].call(effect.element); 
        undoPositioned(effect.element.firstChild);
        undoPositioned(effect.element);
        setStyle(effect.element.firstChild, {bottom: oldInnerBottom}); }}
   }, arguments[1] || {})
  );
}

// Bug in opera makes the TD containing this element expand for a instance after finish 
Effect.Squish = function(element) {
  return new Effect.Scale(element, window.opera ? 1 : 0, 
    { restoreAfterFinish: true,
      beforeSetup: function(effect) { with(Element) {
        makeClipping(effect.element); }},  
      afterFinishInternal: function(effect) { with(Element) {
        hide(effect.element); 
        undoClipping(effect.element); }}
  });
}

Effect.Grow = function(element) {
  element = $(element);
  var options = Object.extend({
    direction: 'center',
    moveTransistion: Effect.Transitions.sinoidal,
    scaleTransition: Effect.Transitions.sinoidal,
    opacityTransition: Effect.Transitions.full
  }, arguments[1] || {});
  var oldStyle = {
    top: element.style.top,
    left: element.style.left,
    height: element.style.height,
    width: element.style.width,
    opacity: Element.getInlineOpacity(element) };

  var dims = Element.getDimensions(element);    
  var initialMoveX, initialMoveY;
  var moveX, moveY;
  
  switch (options.direction) {
    case 'top-left':
      initialMoveX = initialMoveY = moveX = moveY = 0; 
      break;
    case 'top-right':
      initialMoveX = dims.width;
      initialMoveY = moveY = 0;
      moveX = -dims.width;
      break;
    case 'bottom-left':
      initialMoveX = moveX = 0;
      initialMoveY = dims.height;
      moveY = -dims.height;
      break;
    case 'bottom-right':
      initialMoveX = dims.width;
      initialMoveY = dims.height;
      moveX = -dims.width;
      moveY = -dims.height;
      break;
    case 'center':
      initialMoveX = dims.width / 2;
      initialMoveY = dims.height / 2;
      moveX = -dims.width / 2;
      moveY = -dims.height / 2;
      break;
  }
  
  return new Effect.Move(element, {
    x: initialMoveX,
    y: initialMoveY,
    duration: 0.01, 
    beforeSetup: function(effect) { with(Element) {
      hide(effect.element);
      makeClipping(effect.element);
      makePositioned(effect.element);
    }},
    afterFinishInternal: function(effect) {
      new Effect.Parallel(
        [ new Effect.Opacity(effect.element, { sync: true, to: 1.0, from: 0.0, transition: options.opacityTransition }),
          new Effect.Move(effect.element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition }),
          new Effect.Scale(effect.element, 100, {
            scaleMode: { originalHeight: dims.height, originalWidth: dims.width }, 
            sync: true, scaleFrom: window.opera ? 1 : 0, transition: options.scaleTransition, restoreAfterFinish: true})
        ], Object.extend({
             beforeSetup: function(effect) { with(Element) {
               setStyle(effect.effects[0].element, {height: '0px'});
               show(effect.effects[0].element); }},
             afterFinishInternal: function(effect) { with(Element) {
               [undoClipping, undoPositioned].call(effect.effects[0].element); 
               setStyle(effect.effects[0].element, oldStyle); }}
           }, options)
      )
    }
  });
}

Effect.Shrink = function(element) {
  element = $(element);
  var options = Object.extend({
    direction: 'center',
    moveTransistion: Effect.Transitions.sinoidal,
    scaleTransition: Effect.Transitions.sinoidal,
    opacityTransition: Effect.Transitions.none
  }, arguments[1] || {});
  var oldStyle = {
    top: element.style.top,
    left: element.style.left,
    height: element.style.height,
    width: element.style.width,
    opacity: Element.getInlineOpacity(element) };

  var dims = Element.getDimensions(element);
  var moveX, moveY;
  
  switch (options.direction) {
    case 'top-left':
      moveX = moveY = 0;
      break;
    case 'top-right':
      moveX = dims.width;
      moveY = 0;
      break;
    case 'bottom-left':
      moveX = 0;
      moveY = dims.height;
      break;
    case 'bottom-right':
      moveX = dims.width;
      moveY = dims.height;
      break;
    case 'center':  
      moveX = dims.width / 2;
      moveY = dims.height / 2;
      break;
  }
  
  return new Effect.Parallel(
    [ new Effect.Opacity(element, { sync: true, to: 0.0, from: 1.0, transition: options.opacityTransition }),
      new Effect.Scale(element, window.opera ? 1 : 0, { sync: true, transition: options.scaleTransition, restoreAfterFinish: true}),
      new Effect.Move(element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition })
    ], Object.extend({            
         beforeStartInternal: function(effect) { with(Element) {
           [makePositioned, makeClipping].call(effect.effects[0].element) }},
         afterFinishInternal: function(effect) { with(Element) {
           [hide, undoClipping, undoPositioned].call(effect.effects[0].element);
           setStyle(effect.effects[0].element, oldStyle); }}
       }, options)
  );
}

Effect.Pulsate = function(element) {
  element = $(element);
  var options    = arguments[1] || {};
  var oldOpacity = Element.getInlineOpacity(element);
  var transition = options.transition || Effect.Transitions.sinoidal;
  var reverser   = function(pos){ return transition(1-Effect.Transitions.pulse(pos)) };
  reverser.bind(transition);
  return new Effect.Opacity(element, 
    Object.extend(Object.extend({  duration: 3.0, from: 0,
      afterFinishInternal: function(effect) { Element.setStyle(effect.element, {opacity: oldOpacity}); }
    }, options), {transition: reverser}));
}

Effect.Fold = function(element) {
  element = $(element);
  var oldStyle = {
    top: element.style.top,
    left: element.style.left,
    width: element.style.width,
    height: element.style.height };
  Element.makeClipping(element);
  return new Effect.Scale(element, 5, Object.extend({   
    scaleContent: false,
    scaleX: false,
    afterFinishInternal: function(effect) {
    new Effect.Scale(element, 1, { 
      scaleContent: false, 
      scaleY: false,
      afterFinishInternal: function(effect) { with(Element) {
        [hide, undoClipping].call(effect.element); 
        setStyle(effect.element, oldStyle);
      }} });
  }}, arguments[1] || {}));
}


// -----------------------------------------------------------------------------------
//
//	Lightbox v2.02
//	by Lokesh Dhakar - http://www.huddletogether.com
//	3/31/06
//
//	For more information on this script, visit:
//	http://huddletogether.com/projects/lightbox2/
//
//	Licensed under the Creative Commons Attribution 2.5 License - http://creativecommons.org/licenses/by/2.5/
//	
//	Credit also due to those who have helped, inspired, and made their code available to the public.
//	Including: Scott Upton(uptonic.com), Peter-Paul Koch(quirksmode.org), Thomas Fuchs(mir.aculo.us), and others.
//
//
// -----------------------------------------------------------------------------------
/*

	Table of Contents
	-----------------
	Configuration
	Global Variables

	Extending Built-in Objects	
	- Object.extend(Element)
	- Array.prototype.removeDuplicates()
	- Array.prototype.empty()

	Lightbox Class Declaration
	- initialize()
	- start()
	- changeImage()
	- resizeImageContainer()
	- showImage()
	- updateDetails()
	- updateNav()
	- enableKeyboardNav()
	- disableKeyboardNav()
	- keyboardAction()
	- preloadNeighborImages()
	- end()
	
	Miscellaneous Functions
	- getPageScroll()
	- getPageSize()
	- getKey()
	- listenKey()
	- showSelectBoxes()
	- hideSelectBoxes()
	- pause()
	- initLightbox()
	
	Function Calls
	- addLoadEvent(initLightbox)
	
*/
// -----------------------------------------------------------------------------------

//
//	Configuration
//
var fileLoadingImage = "../../../images/loading.gif";		
var fileBottomNavCloseImage = "../../../images/closelabel.gif";

var resizeSpeed = 8;	// controls the speed of the image resizing (1=slowest and 10=fastest)

var borderSize = 10;	//if you adjust the padding in the CSS, you will need to update this variable

// -----------------------------------------------------------------------------------

//
//	Global Variables
//
var imageArray = new Array;
var activeImage;

if(resizeSpeed > 10){ resizeSpeed = 10;}
if(resizeSpeed < 1){ resizeSpeed = 1;}
resizeDuration = (11 - resizeSpeed) * 0.15;

// -----------------------------------------------------------------------------------

//
//	Additional methods for Element added by SU, Couloir
//	- further additions by Lokesh Dhakar (huddletogether.com)
//
Object.extend(Element, {
	getWidth: function(element) {
	   	element = $(element);
	   	return element.offsetWidth; 
	},
	setWidth: function(element,w) {
	   	element = $(element);
    	element.style.width = w +"px";
	},
	setHeight: function(element,h) {
   		element = $(element);
    	element.style.height = h +"px";
	},
	setTop: function(element,t) {
	   	element = $(element);
    	element.style.top = t +"px";
	},
	setSrc: function(element,src) {
    	element = $(element);
    	element.src = src; 
	},
	setHref: function(element,href) {
    	element = $(element);
    	element.href = href; 
	},
	setInnerHTML: function(element,content) {
		element = $(element);
		element.innerHTML = content;
	}
});

// -----------------------------------------------------------------------------------

//
//	Extending built-in Array object
//	- array.removeDuplicates()
//	- array.empty()
//
Array.prototype.removeDuplicates = function () {
	for(i = 1; i < this.length; i++){
		if(this[i][0] == this[i-1][0]){
			this.splice(i,1);
		}
	}
}

// -----------------------------------------------------------------------------------

Array.prototype.empty = function () {
	for(i = 0; i <= this.length; i++){
		this.shift();
	}
}

// -----------------------------------------------------------------------------------

//
//	Lightbox Class Declaration
//	- initialize()
//	- start()
//	- changeImage()
//	- resizeImageContainer()
//	- showImage()
//	- updateDetails()
//	- updateNav()
//	- enableKeyboardNav()
//	- disableKeyboardNav()
//	- keyboardNavAction()
//	- preloadNeighborImages()
//	- end()
//
//	Structuring of code inspired by Scott Upton (http://www.uptonic.com/)
//
var Lightbox = Class.create();

Lightbox.prototype = {
	
	// initialize()
	// Constructor runs on completion of the DOM loading. Loops through anchor tags looking for 
	// 'lightbox' references and applies onclick events to appropriate links. The 2nd section of
	// the function inserts html at the bottom of the page which is used to display the shadow 
	// overlay and the image container.
	//
	initialize: function() {	
		if (!document.getElementsByTagName){ return; }
		var anchors = document.getElementsByTagName('a');

		// loop through all anchor tags
		for (var i=0; i<anchors.length; i++){
			var anchor = anchors[i];
			
			var relAttribute = String(anchor.getAttribute('rel'));
			
			// use the string.match() method to catch 'lightbox' references in the rel attribute
			if (anchor.getAttribute('href') && (relAttribute.toLowerCase().match('lightbox'))){
				anchor.onclick = function () {myLightbox.start(this); return false;}
			}
		}

		// The rest of this code inserts html at the bottom of the page that looks similar to this:
		//
		//	<div id="overlay"></div>
		//	<div id="lightbox">
		//		<div id="outerImageContainer">
		//			<div id="imageContainer">
		//				<img id="lightboxImage">
		//				<div style="" id="hoverNav">
		//					<a href="#" id="prevLink"></a>
		//					<a href="#" id="nextLink"></a>
		//				</div>
		//				<div id="loading">
		//					<a href="#" id="loadingLink">
		//						<img src="images/loading.gif">
		//					</a>
		//				</div>
		//			</div>
		//		</div>
		//		<div id="imageDataContainer">
		//			<div id="imageData">
		//				<div id="imageDetails">
		//					<span id="caption"></span>
		//					<span id="numberDisplay"></span>
		//				</div>
		//				<div id="bottomNav">
		//					<a href="#" id="bottomNavClose">
		//						<img src="images/close.gif">
		//					</a>
		//				</div>
		//			</div>
		//		</div>
		//	</div>


		var objBody = document.getElementsByTagName("body").item(0);
		
		var objOverlay = document.createElement("div");
		objOverlay.setAttribute('id','overlay');
		objOverlay.style.display = 'none';
		objOverlay.onclick = function() { myLightbox.end(); return false; }
		objBody.appendChild(objOverlay);
		
		var objLightbox = document.createElement("div");
		objLightbox.setAttribute('id','lightbox');
		objLightbox.style.display = 'none';
		objBody.appendChild(objLightbox);
	
		var objOuterImageContainer = document.createElement("div");
		objOuterImageContainer.setAttribute('id','outerImageContainer');
		objLightbox.appendChild(objOuterImageContainer);

		var objImageContainer = document.createElement("div");
		objImageContainer.setAttribute('id','imageContainer');
		objOuterImageContainer.appendChild(objImageContainer);
	
		var objLightboxImage = document.createElement("img");
		objLightboxImage.setAttribute('id','lightboxImage');
		objImageContainer.appendChild(objLightboxImage);
	
		var objHoverNav = document.createElement("div");
		objHoverNav.setAttribute('id','hoverNav');
		objImageContainer.appendChild(objHoverNav);
	
		var objPrevLink = document.createElement("a");
		objPrevLink.setAttribute('id','prevLink');
		objPrevLink.setAttribute('href','#');
		objHoverNav.appendChild(objPrevLink);
		
		var objNextLink = document.createElement("a");
		objNextLink.setAttribute('id','nextLink');
		objNextLink.setAttribute('href','#');
		objHoverNav.appendChild(objNextLink);
	
		var objLoading = document.createElement("div");
		objLoading.setAttribute('id','loading');
		objImageContainer.appendChild(objLoading);
	
		var objLoadingLink = document.createElement("a");
		objLoadingLink.setAttribute('id','loadingLink');
		objLoadingLink.setAttribute('href','#');
		objLoadingLink.onclick = function() { myLightbox.end(); return false; }
		objLoading.appendChild(objLoadingLink);
	
		var objLoadingImage = document.createElement("img");
		objLoadingImage.setAttribute('src', fileLoadingImage);
		objLoadingLink.appendChild(objLoadingImage);

		var objImageDataContainer = document.createElement("div");
		objImageDataContainer.setAttribute('id','imageDataContainer');
		objImageDataContainer.className = 'clearfix';
		objLightbox.appendChild(objImageDataContainer);

		var objImageData = document.createElement("div");
		objImageData.setAttribute('id','imageData');
		objImageDataContainer.appendChild(objImageData);
	
		var objImageDetails = document.createElement("div");
		objImageDetails.setAttribute('id','imageDetails');
		objImageData.appendChild(objImageDetails);
	
		var objCaption = document.createElement("span");
		objCaption.setAttribute('id','caption');
		objImageDetails.appendChild(objCaption);
	
		var objNumberDisplay = document.createElement("span");
		objNumberDisplay.setAttribute('id','numberDisplay');
		objImageDetails.appendChild(objNumberDisplay);
		
		var objBottomNav = document.createElement("div");
		objBottomNav.setAttribute('id','bottomNav');
		objImageData.appendChild(objBottomNav);
	
		var objBottomNavCloseLink = document.createElement("a");
		objBottomNavCloseLink.setAttribute('id','bottomNavClose');
		objBottomNavCloseLink.setAttribute('href','#');
		objBottomNavCloseLink.onclick = function() { myLightbox.end(); return false; }
		objBottomNav.appendChild(objBottomNavCloseLink);
	
		var objBottomNavCloseImage = document.createElement("img");
		objBottomNavCloseImage.setAttribute('src', fileBottomNavCloseImage);
		objBottomNavCloseLink.appendChild(objBottomNavCloseImage);
	},
	
	//
	//	start()
	//	Display overlay and lightbox. If image is part of a set, add siblings to imageArray.
	//
	start: function(imageLink) {	
	
		/* SO ROB HACK */
		//$('desc_area').style.display = 'none';
		/* EO ROB HACK */	
		
		hideSelectBoxes();
		
		// stretch overlay to fill page and fade in
		var arrayPageSize = getPageSize();
		Element.setHeight('overlay', arrayPageSize[1]);
		new Effect.Appear('overlay', { duration: 0.2, from: 0.0, to: 0.8 });

		imageArray = [];
		imageNum = 0;		

		if (!document.getElementsByTagName){ return; }
		var anchors = document.getElementsByTagName('a');

		// if image is NOT part of a set..
		if((imageLink.getAttribute('rel') == 'lightbox')){
			// add single image to imageArray
			imageArray.push(new Array(imageLink.getAttribute('href'), imageLink.getAttribute('title')));			
		} else {
		// if image is part of a set..

			// loop through anchors, find other images in set, and add them to imageArray
			for (var i=0; i<anchors.length; i++){
				var anchor = anchors[i];
				if (anchor.getAttribute('href') && (anchor.getAttribute('rel') == imageLink.getAttribute('rel'))){
					imageArray.push(new Array(anchor.getAttribute('href'), anchor.getAttribute('title')));
				}
			}
			imageArray.removeDuplicates();
			while(imageArray[imageNum][0] != imageLink.getAttribute('href')) { imageNum++;}
		}

		// calculate top offset for the lightbox and display 
		var arrayPageSize = getPageSize();
		var arrayPageScroll = getPageScroll();
		var lightboxTop = arrayPageScroll[1] + (arrayPageSize[3] / 15);

		Element.setTop('lightbox', lightboxTop);
		Element.show('lightbox');
		
		this.changeImage(imageNum);
	},

	//
	//	changeImage()
	//	Hide most elements and preload image in preparation for resizing image container.
	//
	changeImage: function(imageNum) {	
		
		activeImage = imageNum;	// update global var

		// hide elements during transition
		Element.show('loading');
		Element.hide('lightboxImage');
		Element.hide('hoverNav');
		Element.hide('prevLink');
		Element.hide('nextLink');
		Element.hide('imageDataContainer');
		Element.hide('numberDisplay');		
		
		imgPreloader = new Image();
		
		// once image is preloaded, resize image container
		imgPreloader.onload=function(){
			Element.setSrc('lightboxImage', imageArray[activeImage][0]);
			myLightbox.resizeImageContainer(imgPreloader.width, imgPreloader.height);
		}
		imgPreloader.src = imageArray[activeImage][0];
	},

	//
	//	resizeImageContainer()
	//
	resizeImageContainer: function( imgWidth, imgHeight) {

		// get current height and width
		this.wCur = Element.getWidth('outerImageContainer');
		this.hCur = Element.getHeight('outerImageContainer');

		// scalars based on change from old to new
		this.xScale = ((imgWidth  + (borderSize * 2)) / this.wCur) * 100;
		this.yScale = ((imgHeight  + (borderSize * 2)) / this.hCur) * 100;

		// calculate size difference between new and old image, and resize if necessary
		wDiff = (this.wCur - borderSize * 2) - imgWidth;
		hDiff = (this.hCur - borderSize * 2) - imgHeight;

		if(!( hDiff == 0)){ new Effect.Scale('outerImageContainer', this.yScale, {scaleX: false, duration: resizeDuration, queue: 'front'}); }
		if(!( wDiff == 0)){ new Effect.Scale('outerImageContainer', this.xScale, {scaleY: false, delay: resizeDuration, duration: resizeDuration}); }

		// if new and old image are same size and no scaling transition is necessary, 
		// do a quick pause to prevent image flicker.
		if((hDiff == 0) && (wDiff == 0)){
			if (navigator.appVersion.indexOf("MSIE")!=-1){ pause(250); } else { pause(100);} 
		}

		Element.setHeight('prevLink', imgHeight);
		Element.setHeight('nextLink', imgHeight);
		Element.setWidth( 'imageDataContainer', imgWidth + (borderSize * 2));

		this.showImage();
	},
	
	//
	//	showImage()
	//	Display image and begin preloading neighbors.
	//
	showImage: function(){
		Element.hide('loading');
		new Effect.Appear('lightboxImage', { duration: 0.5, queue: 'end', afterFinish: function(){	myLightbox.updateDetails(); } });
		this.preloadNeighborImages();
	},

	//
	//	updateDetails()
	//	Display caption, image number, and bottom nav.
	//
	updateDetails: function() {
	
		Element.show('caption');
		Element.setInnerHTML( 'caption', imageArray[activeImage][1]);
		
		// if image is part of set display 'Image x of x' 
		if(imageArray.length > 1){
			Element.show('numberDisplay');
			Element.setInnerHTML( 'numberDisplay', "Image " + eval(activeImage + 1) + " of " + imageArray.length);
		}

		new Effect.Parallel(
			[ new Effect.SlideDown( 'imageDataContainer', { sync: true, duration: resizeDuration + 0.25, from: 0.0, to: 1.0 }), 
			  new Effect.Appear('imageDataContainer', { sync: true, duration: 1.0 }) ], 
			{ duration: 0.65, afterFinish: function() { myLightbox.updateNav();} } 
		);
	},

	//
	//	updateNav()
	//	Display appropriate previous and next hover navigation.
	//
	updateNav: function() {

		Element.show('hoverNav');				

		// if not first image in set, display prev image button
		if(activeImage != 0){
			Element.show('prevLink');
			document.getElementById('prevLink').onclick = function() {
				myLightbox.changeImage(activeImage - 1); return false;
			}
		}

		// if not last image in set, display next image button
		if(activeImage != (imageArray.length - 1)){
			Element.show('nextLink');
			document.getElementById('nextLink').onclick = function() {
				myLightbox.changeImage(activeImage + 1); return false;
			}
		}
		
		this.enableKeyboardNav();
	},

	//
	//	enableKeyboardNav()
	//
	enableKeyboardNav: function() {
		document.onkeydown = this.keyboardAction; 
	},

	//
	//	disableKeyboardNav()
	//
	disableKeyboardNav: function() {
		document.onkeydown = '';
	},

	//
	//	keyboardAction()
	//
	keyboardAction: function(e) {
		if (e == null) { // ie
			keycode = event.keyCode;
		} else { // mozilla
			keycode = e.which;
		}

		key = String.fromCharCode(keycode).toLowerCase();
		
		if((key == 'x') || (key == 'o') || (key == 'c')){	// close lightbox
			myLightbox.end();
		} else if(key == 'p'){	// display previous image
			if(activeImage != 0){
				myLightbox.disableKeyboardNav();
				myLightbox.changeImage(activeImage - 1);
			}
		} else if(key == 'n'){	// display next image
			if(activeImage != (imageArray.length - 1)){
				myLightbox.disableKeyboardNav();
				myLightbox.changeImage(activeImage + 1);
			}
		}


	},

	//
	//	preloadNeighborImages()
	//	Preload previous and next images.
	//
	preloadNeighborImages: function(){

		if((imageArray.length - 1) > activeImage){
			preloadNextImage = new Image();
			preloadNextImage.src = imageArray[activeImage + 1][0];
		}
		if(activeImage > 0){
			preloadPrevImage = new Image();
			preloadPrevImage.src = imageArray[activeImage - 1][0];
		}
	
	},

	//
	//	end()
	//
	end: function() {
		
		/* SO ROB HACK */
		//$('desc_area').style.display = '';
		/* EO ROB HACK */		
		this.disableKeyboardNav();
		Element.hide('lightbox');
		new Effect.Fade('overlay', { duration: 0.2});
		showSelectBoxes();
	}
}

// -----------------------------------------------------------------------------------

//
// getPageScroll()
// Returns array with x,y page scroll values.
// Core code from - quirksmode.org
//
function getPageScroll(){

	var yScroll;

	if (self.pageYOffset) {
		yScroll = self.pageYOffset;
	} else if (document.documentElement && document.documentElement.scrollTop){	 // Explorer 6 Strict
		yScroll = document.documentElement.scrollTop;
	} else if (document.body) {// all other Explorers
		yScroll = document.body.scrollTop;
	}

	arrayPageScroll = new Array('',yScroll) 
	return arrayPageScroll;
}

// -----------------------------------------------------------------------------------

//
// getPageSize()
// Returns array with page width, height and window width, height
// Core code from - quirksmode.org
// Edit for Firefox by pHaez
//
function getPageSize(){
	
	var xScroll, yScroll;
	
	if (window.innerHeight && window.scrollMaxY) {	
		xScroll = document.body.scrollWidth;
		yScroll = window.innerHeight + window.scrollMaxY;
	} else if (document.body.scrollHeight > document.body.offsetHeight){ // all but Explorer Mac
		xScroll = document.body.scrollWidth;
		yScroll = document.body.scrollHeight;
	} else { // Explorer Mac...would also work in Explorer 6 Strict, Mozilla and Safari
		xScroll = document.body.offsetWidth;
		yScroll = document.body.offsetHeight;
	}
	
	var windowWidth, windowHeight;
	if (self.innerHeight) {	// all except Explorer
		windowWidth = self.innerWidth;
		windowHeight = self.innerHeight;
	} else if (document.documentElement && document.documentElement.clientHeight) { // Explorer 6 Strict Mode
		windowWidth = document.documentElement.clientWidth;
		windowHeight = document.documentElement.clientHeight;
	} else if (document.body) { // other Explorers
		windowWidth = document.body.clientWidth;
		windowHeight = document.body.clientHeight;
	}	
	
	// for small pages with total height less then height of the viewport
	if(yScroll < windowHeight){
		pageHeight = windowHeight;
	} else { 
		pageHeight = yScroll;
	}

	// for small pages with total width less then width of the viewport
	if(xScroll < windowWidth){	
		pageWidth = windowWidth;
	} else {
		pageWidth = xScroll;
	}


	arrayPageSize = new Array(pageWidth,pageHeight,windowWidth,windowHeight) 
	return arrayPageSize;
}

// -----------------------------------------------------------------------------------

//
// getKey(key)
// Gets keycode. If 'x' is pressed then it hides the lightbox.
//
function getKey(e){
	if (e == null) { // ie
		keycode = event.keyCode;
	} else { // mozilla
		keycode = e.which;
	}
	key = String.fromCharCode(keycode).toLowerCase();
	
	if(key == 'x'){
	}
}

// -----------------------------------------------------------------------------------

//
// listenKey()
//
function listenKey () {	document.onkeypress = getKey; }
	
// ---------------------------------------------------

function showSelectBoxes(){
	selects = document.getElementsByTagName("select");
	for (i = 0; i != selects.length; i++) {
		selects[i].style.visibility = "visible";
	}
}

// ---------------------------------------------------

function hideSelectBoxes(){
	selects = document.getElementsByTagName("select");
	for (i = 0; i != selects.length; i++) {
		selects[i].style.visibility = "hidden";
	}
}

// ---------------------------------------------------

//
// pause(numberMillis)
// Pauses code execution for specified time. Uses busy code, not good.
// Code from http://www.faqts.com/knowledge_base/view.phtml/aid/1602
//
function pause(numberMillis) {
	var now = new Date();
	var exitTime = now.getTime() + numberMillis;
	while (true) {
		now = new Date();
		if (now.getTime() > exitTime)
			return;
	}
}

// ---------------------------------------------------



function initLightbox() { myLightbox = new Lightbox(); }
Event.observe(window, 'load', initLightbox, false);