var Prototype={Version:"1.6.0.1",Browser:{IE:!!(window.attachEvent&&!window.opera),Opera:!!window.opera,WebKit:navigator.userAgent.indexOf("AppleWebKit/")>-1,Gecko:navigator.userAgent.indexOf("Gecko")>-1&&navigator.userAgent.indexOf("KHTML")==-1,MobileSafari:!!navigator.userAgent.match(/Apple.*Mobile.*Safari/)},BrowserFeatures:{XPath:!!document.evaluate,ElementExtensions:!!window.HTMLElement,SpecificElementExtensions:document.createElement("div").__proto__&&document.createElement("div").__proto__!==document.createElement("form").__proto__},ScriptFragment:"<script[^>]*>([\\S\\s]*?)<\/script>",JSONFilter:/^\/\*-secure-([\s\S]*)\*\/\s*$/,emptyFunction:function(){},K:function(A){return A}};if(Prototype.Browser.MobileSafari){Prototype.BrowserFeatures.SpecificElementExtensions=false}var Class={create:function(){var E=null,D=$A(arguments);if(Object.isFunction(D[0])){E=D.shift()}function A(){this.initialize.apply(this,arguments)}Object.extend(A,Class.Methods);A.superclass=E;A.subclasses=[];if(E){var B=function(){};B.prototype=E.prototype;A.prototype=new B;E.subclasses.push(A)}for(var C=0;C<D.length;C++){A.addMethods(D[C])}if(!A.prototype.initialize){A.prototype.initialize=Prototype.emptyFunction}A.prototype.constructor=A;return A}};Class.Methods={addMethods:function(G){var C=this.superclass&&this.superclass.prototype;var B=Object.keys(G);if(!Object.keys({toString:true}).length){B.push("toString","valueOf")}for(var A=0,D=B.length;A<D;A++){var F=B[A],E=G[F];if(C&&Object.isFunction(E)&&E.argumentNames().first()=="$super"){var H=E,E=Object.extend((function(I){return function(){return C[I].apply(this,arguments)}})(F).wrap(H),{valueOf:function(){return H},toString:function(){return H.toString()}})}this.prototype[F]=E}return this}};var Abstract={};Object.extend=function(A,C){for(var B in C){A[B]=C[B]}return A};Object.extend(Object,{inspect:function(A){try{if(Object.isUndefined(A)){return"undefined"}if(A===null){return"null"}return A.inspect?A.inspect():A.toString()}catch(B){if(B instanceof RangeError){return"..."}throw B}},toJSON:function(A){var C=typeof A;switch(C){case"undefined":case"function":case"unknown":return ;case"boolean":return A.toString()}if(A===null){return"null"}if(A.toJSON){return A.toJSON()}if(Object.isElement(A)){return }var B=[];for(var E in A){var D=Object.toJSON(A[E]);if(!Object.isUndefined(D)){B.push(E.toJSON()+": "+D)}}return"{"+B.join(", ")+"}"},toQueryString:function(A){return $H(A).toQueryString()},toHTML:function(A){return A&&A.toHTML?A.toHTML():String.interpret(A)},keys:function(A){var B=[];for(var C in A){B.push(C)}return B},values:function(B){var A=[];for(var C in B){A.push(B[C])}return A},clone:function(A){return Object.extend({},A)},isElement:function(A){return A&&A.nodeType==1},isArray:function(A){return A&&A.constructor===Array},isHash:function(A){return A instanceof Hash},isFunction:function(A){return typeof A=="function"},isString:function(A){return typeof A=="string"},isNumber:function(A){return typeof A=="number"},isUndefined:function(A){return typeof A=="undefined"}});Object.extend(Function.prototype,{argumentNames:function(){var A=this.toString().match(/^[\s\(]*function[^(]*\((.*?)\)/)[1].split(",").invoke("strip");return A.length==1&&!A[0]?[]:A},bind:function(){if(arguments.length<2&&Object.isUndefined(arguments[0])){return this}var A=this,C=$A(arguments),B=C.shift();return function(){return A.apply(B,C.concat($A(arguments)))}},bindAsEventListener:function(){var A=this,C=$A(arguments),B=C.shift();return function(D){return A.apply(B,[D||window.event].concat(C))}},curry:function(){if(!arguments.length){return this}var A=this,B=$A(arguments);return function(){return A.apply(this,B.concat($A(arguments)))}},delay:function(){var A=this,B=$A(arguments),C=B.shift()*1000;return window.setTimeout(function(){return A.apply(A,B)},C)},wrap:function(B){var A=this;return function(){return B.apply(this,[A.bind(this)].concat($A(arguments)))}},methodize:function(){if(this._methodized){return this._methodized}var A=this;return this._methodized=function(){return A.apply(null,[this].concat($A(arguments)))}}});Function.prototype.defer=Function.prototype.delay.curry(0.01);Date.prototype.toJSON=function(){return'"'+this.getUTCFullYear()+"-"+(this.getUTCMonth()+1).toPaddedString(2)+"-"+this.getUTCDate().toPaddedString(2)+"T"+this.getUTCHours().toPaddedString(2)+":"+this.getUTCMinutes().toPaddedString(2)+":"+this.getUTCSeconds().toPaddedString(2)+'Z"'};var Try={these:function(){var C;for(var B=0,D=arguments.length;B<D;B++){var A=arguments[B];try{C=A();break}catch(E){}}return C}};RegExp.prototype.match=RegExp.prototype.test;RegExp.escape=function(A){return String(A).replace(/([.*+?^=!:${}()|[\]\/\\])/g,"\\$1")};var PeriodicalExecuter=Class.create({initialize:function(B,A){this.callback=B;this.frequency=A;this.currentlyExecuting=false;this.registerCallback()},registerCallback:function(){this.timer=setInterval(this.onTimerEvent.bind(this),this.frequency*1000)},execute:function(){this.callback(this)},stop:function(){if(!this.timer){return }clearInterval(this.timer);this.timer=null},onTimerEvent:function(){if(!this.currentlyExecuting){try{this.currentlyExecuting=true;this.execute()}finally{this.currentlyExecuting=false}}}});Object.extend(String,{interpret:function(A){return A==null?"":String(A)},specialChar:{"\b":"\\b","\t":"\\t","\n":"\\n","\f":"\\f","\r":"\\r","\\":"\\\\"}});Object.extend(String.prototype,{gsub:function(E,C){var A="",D=this,B;C=arguments.callee.prepareReplacement(C);while(D.length>0){if(B=D.match(E)){A+=D.slice(0,B.index);A+=String.interpret(C(B));D=D.slice(B.index+B[0].length)}else{A+=D,D=""}}return A},sub:function(C,A,B){A=this.gsub.prepareReplacement(A);B=Object.isUndefined(B)?1:B;return this.gsub(C,function(D){if(--B<0){return D[0]}return A(D)})},scan:function(B,A){this.gsub(B,A);return String(this)},truncate:function(B,A){B=B||30;A=Object.isUndefined(A)?"...":A;return this.length>B?this.slice(0,B-A.length)+A:String(this)},strip:function(){return this.replace(/^\s+/,"").replace(/\s+$/,"")},stripTags:function(){return this.replace(/<\/?[^>]+>/gi,"")},stripScripts:function(){return this.replace(new RegExp(Prototype.ScriptFragment,"img"),"")},extractScripts:function(){var B=new RegExp(Prototype.ScriptFragment,"img");var A=new RegExp(Prototype.ScriptFragment,"im");return(this.match(B)||[]).map(function(C){return(C.match(A)||["",""])[1]})},evalScripts:function(){return this.extractScripts().map(function(script){return eval(script)})},escapeHTML:function(){var A=arguments.callee;A.text.data=this;return A.div.innerHTML},unescapeHTML:function(){var A=new Element("div");A.innerHTML=this.stripTags();return A.childNodes[0]?(A.childNodes.length>1?$A(A.childNodes).inject("",function(B,C){return B+C.nodeValue}):A.childNodes[0].nodeValue):""},toQueryParams:function(B){var A=this.strip().match(/([^?#]*)(#.*)?$/);if(!A){return{}}return A[1].split(B||"&").inject({},function(E,F){if((F=F.split("="))[0]){var C=decodeURIComponent(F.shift());var D=F.length>1?F.join("="):F[0];if(D!=undefined){D=decodeURIComponent(D)}if(C in E){if(!Object.isArray(E[C])){E[C]=[E[C]]}E[C].push(D)}else{E[C]=D}}return E})},toArray:function(){return this.split("")},succ:function(){return this.slice(0,this.length-1)+String.fromCharCode(this.charCodeAt(this.length-1)+1)},times:function(A){return A<1?"":new Array(A+1).join(this)},camelize:function(){var D=this.split("-"),A=D.length;if(A==1){return D[0]}var C=this.charAt(0)=="-"?D[0].charAt(0).toUpperCase()+D[0].substring(1):D[0];for(var B=1;B<A;B++){C+=D[B].charAt(0).toUpperCase()+D[B].substring(1)}return C},capitalize:function(){return this.charAt(0).toUpperCase()+this.substring(1).toLowerCase()},underscore:function(){return this.gsub(/::/,"/").gsub(/([A-Z]+)([A-Z][a-z])/,"#{1}_#{2}").gsub(/([a-z\d])([A-Z])/,"#{1}_#{2}").gsub(/-/,"_").toLowerCase()},dasherize:function(){return this.gsub(/_/,"-")},inspect:function(B){var A=this.gsub(/[\x00-\x1f\\]/,function(C){var D=String.specialChar[C[0]];return D?D:"\\u00"+C[0].charCodeAt().toPaddedString(2,16)});if(B){return'"'+A.replace(/"/g,'\\"')+'"'}return"'"+A.replace(/'/g,"\\'")+"'"},toJSON:function(){return this.inspect(true)},unfilterJSON:function(A){return this.sub(A||Prototype.JSONFilter,"#{1}")},isJSON:function(){var A=this;if(A.blank()){return false}A=this.replace(/\\./g,"@").replace(/"[^"\\\n\r]*"/g,"");return(/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/).test(A)},evalJSON:function(sanitize){var json=this.unfilterJSON();try{if(!sanitize||json.isJSON()){return eval("("+json+")")}}catch(e){}throw new SyntaxError("Badly formed JSON string: "+this.inspect())},include:function(A){return this.indexOf(A)>-1},startsWith:function(A){return this.indexOf(A)===0},endsWith:function(A){var B=this.length-A.length;return B>=0&&this.lastIndexOf(A)===B},empty:function(){return this==""},blank:function(){return/^\s*$/.test(this)},interpolate:function(A,B){return new Template(this,B).evaluate(A)}});if(Prototype.Browser.WebKit||Prototype.Browser.IE){Object.extend(String.prototype,{escapeHTML:function(){return this.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;")},unescapeHTML:function(){return this.replace(/&amp;/g,"&").replace(/&lt;/g,"<").replace(/&gt;/g,">")}})}String.prototype.gsub.prepareReplacement=function(B){if(Object.isFunction(B)){return B}var A=new Template(B);return function(C){return A.evaluate(C)}};String.prototype.parseQuery=String.prototype.toQueryParams;Object.extend(String.prototype.escapeHTML,{div:document.createElement("div"),text:document.createTextNode("")});with(String.prototype.escapeHTML){div.appendChild(text)}var Template=Class.create({initialize:function(A,B){this.template=A.toString();this.pattern=B||Template.Pattern},evaluate:function(A){if(Object.isFunction(A.toTemplateReplacements)){A=A.toTemplateReplacements()}return this.template.gsub(this.pattern,function(D){if(A==null){return""}var F=D[1]||"";if(F=="\\"){return D[2]}var B=A,G=D[3];var E=/^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/;D=E.exec(G);if(D==null){return F}while(D!=null){var C=D[1].startsWith("[")?D[2].gsub("\\\\]","]"):D[1];B=B[C];if(null==B||""==D[3]){break}G=G.substring("["==D[3]?D[1].length:D[0].length);D=E.exec(G)}return F+String.interpret(B)}.bind(this))}});Template.Pattern=/(^|.|\r|\n)(#\{(.*?)\})/;var $break={};var Enumerable={each:function(C,B){var A=0;C=C.bind(B);try{this._each(function(E){C(E,A++)})}catch(D){if(D!=$break){throw D}}return this},eachSlice:function(D,C,B){C=C?C.bind(B):Prototype.K;var A=-D,E=[],F=this.toArray();while((A+=D)<F.length){E.push(F.slice(A,A+D))}return E.collect(C,B)},all:function(C,B){C=C?C.bind(B):Prototype.K;var A=true;this.each(function(E,D){A=A&&!!C(E,D);if(!A){throw $break}});return A},any:function(C,B){C=C?C.bind(B):Prototype.K;var A=false;this.each(function(E,D){if(A=!!C(E,D)){throw $break}});return A},collect:function(C,B){C=C?C.bind(B):Prototype.K;var A=[];this.each(function(E,D){A.push(C(E,D))});return A},detect:function(C,B){C=C.bind(B);var A;this.each(function(E,D){if(C(E,D)){A=E;throw $break}});return A},findAll:function(C,B){C=C.bind(B);var A=[];this.each(function(E,D){if(C(E,D)){A.push(E)}});return A},grep:function(D,C,B){C=C?C.bind(B):Prototype.K;var A=[];if(Object.isString(D)){D=new RegExp(D)}this.each(function(F,E){if(D.match(F)){A.push(C(F,E))}});return A},include:function(A){if(Object.isFunction(this.indexOf)){if(this.indexOf(A)!=-1){return true}}var B=false;this.each(function(C){if(C==A){B=true;throw $break}});return B},inGroupsOf:function(B,A){A=Object.isUndefined(A)?null:A;return this.eachSlice(B,function(C){while(C.length<B){C.push(A)}return C})},inject:function(A,C,B){C=C.bind(B);this.each(function(E,D){A=C(A,E,D)});return A},invoke:function(B){var A=$A(arguments).slice(1);return this.map(function(C){return C[B].apply(C,A)})},max:function(C,B){C=C?C.bind(B):Prototype.K;var A;this.each(function(E,D){E=C(E,D);if(A==null||E>=A){A=E}});return A},min:function(C,B){C=C?C.bind(B):Prototype.K;var A;this.each(function(E,D){E=C(E,D);if(A==null||E<A){A=E}});return A},partition:function(D,B){D=D?D.bind(B):Prototype.K;var C=[],A=[];this.each(function(F,E){(D(F,E)?C:A).push(F)});return[C,A]},pluck:function(B){var A=[];this.each(function(C){A.push(C[B])});return A},reject:function(C,B){C=C.bind(B);var A=[];this.each(function(E,D){if(!C(E,D)){A.push(E)}});return A},sortBy:function(B,A){B=B.bind(A);return this.map(function(D,C){return{value:D,criteria:B(D,C)}}).sort(function(F,E){var D=F.criteria,C=E.criteria;return D<C?-1:D>C?1:0}).pluck("value")},toArray:function(){return this.map()},zip:function(){var B=Prototype.K,A=$A(arguments);if(Object.isFunction(A.last())){B=A.pop()}var C=[this].concat(A).map($A);return this.map(function(E,D){return B(C.pluck(D))})},size:function(){return this.toArray().length},inspect:function(){return"#<Enumerable:"+this.toArray().inspect()+">"}};Object.extend(Enumerable,{map:Enumerable.collect,find:Enumerable.detect,select:Enumerable.findAll,filter:Enumerable.findAll,member:Enumerable.include,entries:Enumerable.toArray,every:Enumerable.all,some:Enumerable.any});function $A(C){if(!C){return[]}if(C.toArray){return C.toArray()}var B=C.length||0,A=new Array(B);while(B--){A[B]=C[B]}return A}if(Prototype.Browser.WebKit){function $A(C){if(!C){return[]}if(!(Object.isFunction(C)&&C=="[object NodeList]")&&C.toArray){return C.toArray()}var B=C.length||0,A=new Array(B);while(B--){A[B]=C[B]}return A}}Array.from=$A;Object.extend(Array.prototype,Enumerable);if(!Array.prototype._reverse){Array.prototype._reverse=Array.prototype.reverse}Object.extend(Array.prototype,{_each:function(B){for(var A=0,C=this.length;A<C;A++){B(this[A])}},clear:function(){this.length=0;return this},first:function(){return this[0]},last:function(){return this[this.length-1]},compact:function(){return this.select(function(A){return A!=null})},flatten:function(){return this.inject([],function(B,A){return B.concat(Object.isArray(A)?A.flatten():[A])})},without:function(){var A=$A(arguments);return this.select(function(B){return !A.include(B)})},reverse:function(A){return(A!==false?this:this.toArray())._reverse()},reduce:function(){return this.length>1?this:this[0]},uniq:function(A){return this.inject([],function(D,C,B){if(0==B||(A?D.last()!=C:!D.include(C))){D.push(C)}return D})},intersect:function(A){return this.uniq().findAll(function(B){return A.detect(function(C){return B===C})})},clone:function(){return[].concat(this)},size:function(){return this.length},inspect:function(){return"["+this.map(Object.inspect).join(", ")+"]"},toJSON:function(){var A=[];this.each(function(B){var C=Object.toJSON(B);if(!Object.isUndefined(C)){A.push(C)}});return"["+A.join(", ")+"]"}});if(Object.isFunction(Array.prototype.forEach)){Array.prototype._each=Array.prototype.forEach}if(!Array.prototype.indexOf){Array.prototype.indexOf=function(C,A){A||(A=0);var B=this.length;if(A<0){A=B+A}for(;A<B;A++){if(this[A]===C){return A}}return -1}}if(!Array.prototype.lastIndexOf){Array.prototype.lastIndexOf=function(B,A){A=isNaN(A)?this.length:(A<0?this.length+A:A)+1;var C=this.slice(0,A).reverse().indexOf(B);return(C<0)?C:A-C-1}}Array.prototype.toArray=Array.prototype.clone;function $w(A){if(!Object.isString(A)){return[]}A=A.strip();return A?A.split(/\s+/):[]}if(Prototype.Browser.Opera){Array.prototype.concat=function(){var E=[];for(var B=0,C=this.length;B<C;B++){E.push(this[B])}for(var B=0,C=arguments.length;B<C;B++){if(Object.isArray(arguments[B])){for(var A=0,D=arguments[B].length;A<D;A++){E.push(arguments[B][A])}}else{E.push(arguments[B])}}return E}}Object.extend(Number.prototype,{toColorPart:function(){return this.toPaddedString(2,16)},succ:function(){return this+1},times:function(A){$R(0,this,true).each(A);return this},toPaddedString:function(C,B){var A=this.toString(B||10);return"0".times(C-A.length)+A},toJSON:function(){return isFinite(this)?this.toString():"null"}});$w("abs round ceil floor").each(function(A){Number.prototype[A]=Math[A].methodize()});function $H(A){return new Hash(A)}var Hash=Class.create(Enumerable,(function(){function A(B,C){if(Object.isUndefined(C)){return B}return B+"="+encodeURIComponent(String.interpret(C))}return{initialize:function(B){this._object=Object.isHash(B)?B.toObject():Object.clone(B)},_each:function(C){for(var B in this._object){var D=this._object[B],E=[B,D];E.key=B;E.value=D;C(E)}},set:function(B,C){return this._object[B]=C},get:function(B){return this._object[B]},unset:function(B){var C=this._object[B];delete this._object[B];return C},toObject:function(){return Object.clone(this._object)},keys:function(){return this.pluck("key")},values:function(){return this.pluck("value")},index:function(C){var B=this.detect(function(D){return D.value===C});return B&&B.key},merge:function(B){return this.clone().update(B)},update:function(B){return new Hash(B).inject(this,function(C,D){C.set(D.key,D.value);return C})},toQueryString:function(){return this.map(function(D){var C=encodeURIComponent(D.key),B=D.value;if(B&&typeof B=="object"){if(Object.isArray(B)){return B.map(A.curry(C)).join("&")}}return A(C,B)}).join("&")},inspect:function(){return"#<Hash:{"+this.map(function(B){return B.map(Object.inspect).join(": ")}).join(", ")+"}>"},toJSON:function(){return Object.toJSON(this.toObject())},clone:function(){return new Hash(this)}}})());Hash.prototype.toTemplateReplacements=Hash.prototype.toObject;Hash.from=$H;var ObjectRange=Class.create(Enumerable,{initialize:function(C,A,B){this.start=C;this.end=A;this.exclusive=B},_each:function(A){var B=this.start;while(this.include(B)){A(B);B=B.succ()}},include:function(A){if(A<this.start){return false}if(this.exclusive){return A<this.end}return A<=this.end}});var $R=function(C,A,B){return new ObjectRange(C,A,B)};var Ajax={getTransport:function(){return Try.these(function(){return new XMLHttpRequest()},function(){return new ActiveXObject("Msxml2.XMLHTTP")},function(){return new ActiveXObject("Microsoft.XMLHTTP")})||false},activeRequestCount:0};Ajax.Responders={responders:[],_each:function(A){this.responders._each(A)},register:function(A){if(!this.include(A)){this.responders.push(A)}},unregister:function(A){this.responders=this.responders.without(A)},dispatch:function(D,B,C,A){this.each(function(E){if(Object.isFunction(E[D])){try{E[D].apply(E,[B,C,A])}catch(F){}}})}};Object.extend(Ajax.Responders,Enumerable);Ajax.Responders.register({onCreate:function(){Ajax.activeRequestCount++},onComplete:function(){Ajax.activeRequestCount--}});Ajax.Base=Class.create({initialize:function(A){this.options={method:"post",asynchronous:true,contentType:"application/x-www-form-urlencoded",encoding:"UTF-8",parameters:"",evalJSON:true,evalJS:true};Object.extend(this.options,A||{});this.options.method=this.options.method.toLowerCase();if(Object.isString(this.options.parameters)){this.options.parameters=this.options.parameters.toQueryParams()}else{if(Object.isHash(this.options.parameters)){this.options.parameters=this.options.parameters.toObject()}}}});Ajax.Request=Class.create(Ajax.Base,{_complete:false,initialize:function($super,B,A){$super(A);this.transport=Ajax.getTransport();this.request(B)},request:function(B){this.url=B;this.method=this.options.method;var D=Object.clone(this.options.parameters);if(!["get","post"].include(this.method)){D._method=this.method;this.method="post"}this.parameters=D;if(D=Object.toQueryString(D)){if(this.method=="get"){this.url+=(this.url.include("?")?"&":"?")+D}else{if(/Konqueror|Safari|KHTML/.test(navigator.userAgent)){D+="&_="}}}try{var A=new Ajax.Response(this);if(this.options.onCreate){this.options.onCreate(A)}Ajax.Responders.dispatch("onCreate",this,A);this.transport.open(this.method.toUpperCase(),this.url,this.options.asynchronous);if(this.options.asynchronous){this.respondToReadyState.bind(this).defer(1)}this.transport.onreadystatechange=this.onStateChange.bind(this);this.setRequestHeaders();this.body=this.method=="post"?(this.options.postBody||D):null;this.transport.send(this.body);if(!this.options.asynchronous&&this.transport.overrideMimeType){this.onStateChange()}}catch(C){this.dispatchException(C)}},onStateChange:function(){var A=this.transport.readyState;if(A>1&&!((A==4)&&this._complete)){this.respondToReadyState(this.transport.readyState)}},setRequestHeaders:function(){var E={"X-Requested-With":"XMLHttpRequest","X-Prototype-Version":Prototype.Version,Accept:"text/javascript, text/html, application/xml, text/xml, */*"};if(this.method=="post"){E["Content-type"]=this.options.contentType+(this.options.encoding?"; charset="+this.options.encoding:"");if(this.transport.overrideMimeType&&(navigator.userAgent.match(/Gecko\/(\d{4})/)||[0,2005])[1]<2005){E.Connection="close"}}if(typeof this.options.requestHeaders=="object"){var C=this.options.requestHeaders;if(Object.isFunction(C.push)){for(var B=0,D=C.length;B<D;B+=2){E[C[B]]=C[B+1]}}else{$H(C).each(function(F){E[F.key]=F.value})}}for(var A in E){this.transport.setRequestHeader(A,E[A])}},success:function(){var A=this.getStatus();return !A||(A>=200&&A<300)},getStatus:function(){try{return this.transport.status||0}catch(A){return 0}},respondToReadyState:function(A){var C=Ajax.Request.Events[A],B=new Ajax.Response(this);if(C=="Complete"){try{this._complete=true;(this.options["on"+B.status]||this.options["on"+(this.success()?"Success":"Failure")]||Prototype.emptyFunction)(B,B.headerJSON)}catch(D){this.dispatchException(D)}var E=B.getHeader("Content-type");if(this.options.evalJS=="force"||(this.options.evalJS&&E&&E.match(/^\s*(text|application)\/(x-)?(java|ecma)script(;.*)?\s*$/i))){this.evalResponse()}}try{(this.options["on"+C]||Prototype.emptyFunction)(B,B.headerJSON);Ajax.Responders.dispatch("on"+C,this,B,B.headerJSON)}catch(D){this.dispatchException(D)}if(C=="Complete"){this.transport.onreadystatechange=Prototype.emptyFunction}},getHeader:function(A){try{return this.transport.getResponseHeader(A)||null}catch(B){return null}},evalResponse:function(){try{return eval((this.transport.responseText||"").unfilterJSON())}catch(e){this.dispatchException(e)}},dispatchException:function(A){(this.options.onException||Prototype.emptyFunction)(this,A);Ajax.Responders.dispatch("onException",this,A)}});Ajax.Request.Events=["Uninitialized","Loading","Loaded","Interactive","Complete"];Ajax.Response=Class.create({initialize:function(C){this.request=C;var D=this.transport=C.transport,A=this.readyState=D.readyState;if((A>2&&!Prototype.Browser.IE)||A==4){this.status=this.getStatus();this.statusText=this.getStatusText();this.responseText=String.interpret(D.responseText);this.headerJSON=this._getHeaderJSON()}if(A==4){var B=D.responseXML;this.responseXML=Object.isUndefined(B)?null:B;this.responseJSON=this._getResponseJSON()}},status:0,statusText:"",getStatus:Ajax.Request.prototype.getStatus,getStatusText:function(){try{return this.transport.statusText||""}catch(A){return""}},getHeader:Ajax.Request.prototype.getHeader,getAllHeaders:function(){try{return this.getAllResponseHeaders()}catch(A){return null}},getResponseHeader:function(A){return this.transport.getResponseHeader(A)},getAllResponseHeaders:function(){return this.transport.getAllResponseHeaders()},_getHeaderJSON:function(){var A=this.getHeader("X-JSON");if(!A){return null}A=decodeURIComponent(escape(A));try{return A.evalJSON(this.request.options.sanitizeJSON)}catch(B){this.request.dispatchException(B)}},_getResponseJSON:function(){var A=this.request.options;if(!A.evalJSON||(A.evalJSON!="force"&&!(this.getHeader("Content-type")||"").include("application/json"))||this.responseText.blank()){return null}try{return this.responseText.evalJSON(A.sanitizeJSON)}catch(B){this.request.dispatchException(B)}}});Ajax.Updater=Class.create(Ajax.Request,{initialize:function($super,A,C,B){this.container={success:(A.success||A),failure:(A.failure||(A.success?null:A))};B=Object.clone(B);var D=B.onComplete;B.onComplete=(function(E,F){this.updateContent(E.responseText);if(Object.isFunction(D)){D(E,F)}}).bind(this);$super(C,B)},updateContent:function(D){var C=this.container[this.success()?"success":"failure"],A=this.options;if(!A.evalScripts){D=D.stripScripts()}if(C=$(C)){if(A.insertion){if(Object.isString(A.insertion)){var B={};B[A.insertion]=D;C.insert(B)}else{A.insertion(C,D)}}else{C.update(D)}}}});Ajax.PeriodicalUpdater=Class.create(Ajax.Base,{initialize:function($super,A,C,B){$super(B);this.onComplete=this.options.onComplete;this.frequency=(this.options.frequency||2);this.decay=(this.options.decay||1);this.updater={};this.container=A;this.url=C;this.start()},start:function(){this.options.onComplete=this.updateComplete.bind(this);this.onTimerEvent()},stop:function(){this.updater.options.onComplete=undefined;clearTimeout(this.timer);(this.onComplete||Prototype.emptyFunction).apply(this,arguments)},updateComplete:function(A){if(this.options.decay){this.decay=(A.responseText==this.lastText?this.decay*this.options.decay:1);this.lastText=A.responseText}this.timer=this.onTimerEvent.bind(this).delay(this.decay*this.frequency)},onTimerEvent:function(){this.updater=new Ajax.Updater(this.container,this.url,this.options)}});function $(B){if(arguments.length>1){for(var A=0,D=[],C=arguments.length;A<C;A++){D.push($(arguments[A]))}return D}if(Object.isString(B)){B=document.getElementById(B)}return Element.extend(B)}if(Prototype.BrowserFeatures.XPath){document._getElementsByXPath=function(F,A){var C=[];var E=document.evaluate(F,$(A)||document,null,XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,null);for(var B=0,D=E.snapshotLength;B<D;B++){C.push(Element.extend(E.snapshotItem(B)))}return C}}if(!window.Node){var Node={}}if(!Node.ELEMENT_NODE){Object.extend(Node,{ELEMENT_NODE:1,ATTRIBUTE_NODE:2,TEXT_NODE:3,CDATA_SECTION_NODE:4,ENTITY_REFERENCE_NODE:5,ENTITY_NODE:6,PROCESSING_INSTRUCTION_NODE:7,COMMENT_NODE:8,DOCUMENT_NODE:9,DOCUMENT_TYPE_NODE:10,DOCUMENT_FRAGMENT_NODE:11,NOTATION_NODE:12})}(function(){var A=this.Element;this.Element=function(D,C){C=C||{};D=D.toLowerCase();var B=Element.cache;if(Prototype.Browser.IE&&C.name){D="<"+D+' name="'+C.name+'">';delete C.name;return Element.writeAttribute(document.createElement(D),C)}if(!B[D]){B[D]=Element.extend(document.createElement(D))}return Element.writeAttribute(B[D].cloneNode(false),C)};Object.extend(this.Element,A||{})}).call(window);Element.cache={};Element.Methods={visible:function(A){return $(A).style.display!="none"},toggle:function(A){A=$(A);Element[Element.visible(A)?"hide":"show"](A);return A},hide:function(A){$(A).style.display="none";return A},show:function(A){$(A).style.display="";return A},remove:function(A){A=$(A);A.parentNode.removeChild(A);return A},update:function(A,B){A=$(A);if(B&&B.toElement){B=B.toElement()}if(Object.isElement(B)){return A.update().insert(B)}B=Object.toHTML(B);A.innerHTML=B.stripScripts();B.evalScripts.bind(B).defer();return A},replace:function(B,C){B=$(B);if(C&&C.toElement){C=C.toElement()}else{if(!Object.isElement(C)){C=Object.toHTML(C);var A=B.ownerDocument.createRange();A.selectNode(B);C.evalScripts.bind(C).defer();C=A.createContextualFragment(C.stripScripts())}}B.parentNode.replaceChild(C,B);return B},insert:function(B,D){B=$(B);if(Object.isString(D)||Object.isNumber(D)||Object.isElement(D)||(D&&(D.toElement||D.toHTML))){D={bottom:D}}var C,E,A,F;for(position in D){C=D[position];position=position.toLowerCase();E=Element._insertionTranslations[position];if(C&&C.toElement){C=C.toElement()}if(Object.isElement(C)){E(B,C);continue}C=Object.toHTML(C);A=((position=="before"||position=="after")?B.parentNode:B).tagName.toUpperCase();F=Element._getContentFromAnonymousElement(A,C.stripScripts());if(position=="top"||position=="after"){F.reverse()}F.each(E.curry(B));C.evalScripts.bind(C).defer()}return B},wrap:function(B,C,A){B=$(B);if(Object.isElement(C)){$(C).writeAttribute(A||{})}else{if(Object.isString(C)){C=new Element(C,A)}else{C=new Element("div",C)}}if(B.parentNode){B.parentNode.replaceChild(C,B)}C.appendChild(B);return C},inspect:function(B){B=$(B);var A="<"+B.tagName.toLowerCase();$H({id:"id",className:"class"}).each(function(F){var E=F.first(),C=F.last();var D=(B[E]||"").toString();if(D){A+=" "+C+"="+D.inspect(true)}});return A+">"},recursivelyCollect:function(A,C){A=$(A);var B=[];while(A=A[C]){if(A.nodeType==1){B.push(Element.extend(A))}}return B},ancestors:function(A){return $(A).recursivelyCollect("parentNode")},descendants:function(A){return $(A).getElementsBySelector("*")},firstDescendant:function(A){A=$(A).firstChild;while(A&&A.nodeType!=1){A=A.nextSibling}return $(A)},immediateDescendants:function(A){if(!(A=$(A).firstChild)){return[]}while(A&&A.nodeType!=1){A=A.nextSibling}if(A){return[A].concat($(A).nextSiblings())}return[]},previousSiblings:function(A){return $(A).recursivelyCollect("previousSibling")},nextSiblings:function(A){return $(A).recursivelyCollect("nextSibling")},siblings:function(A){A=$(A);return A.previousSiblings().reverse().concat(A.nextSiblings())},match:function(B,A){if(Object.isString(A)){A=new Selector(A)}return A.match($(B))},up:function(B,D,A){B=$(B);if(arguments.length==1){return $(B.parentNode)}var C=B.ancestors();return Object.isNumber(D)?C[D]:Selector.findElement(C,D,A)},down:function(B,C,A){B=$(B);if(arguments.length==1){return B.firstDescendant()}return Object.isNumber(C)?B.descendants()[C]:B.select(C)[A||0]},previous:function(B,D,A){B=$(B);if(arguments.length==1){return $(Selector.handlers.previousElementSibling(B))}var C=B.previousSiblings();return Object.isNumber(D)?C[D]:Selector.findElement(C,D,A)},next:function(C,D,B){C=$(C);if(arguments.length==1){return $(Selector.handlers.nextElementSibling(C))}var A=C.nextSiblings();return Object.isNumber(D)?A[D]:Selector.findElement(A,D,B)},select:function(){var A=$A(arguments),B=$(A.shift());return Selector.findChildElements(B,A)},adjacent:function(){var A=$A(arguments),B=$(A.shift());return Selector.findChildElements(B.parentNode,A).without(B)},identify:function(B){B=$(B);var C=B.readAttribute("id"),A=arguments.callee;if(C){return C}do{C="anonymous_element_"+A.counter++}while($(C));B.writeAttribute("id",C);return C},readAttribute:function(C,A){C=$(C);if(Prototype.Browser.IE){var B=Element._attributeTranslations.read;if(B.values[A]){return B.values[A](C,A)}if(B.names[A]){A=B.names[A]}if(A.include(":")){return(!C.attributes||!C.attributes[A])?null:C.attributes[A].value}}return C.getAttribute(A)},writeAttribute:function(E,C,F){E=$(E);var B={},D=Element._attributeTranslations.write;if(typeof C=="object"){B=C}else{B[C]=Object.isUndefined(F)?true:F}for(var A in B){C=D.names[A]||A;F=B[A];if(D.values[A]){C=D.values[A](E,F)}if(F===false||F===null){E.removeAttribute(C)}else{if(F===true){E.setAttribute(C,C)}else{E.setAttribute(C,F)}}}return E},getHeight:function(A){return $(A).getDimensions().height},getWidth:function(A){return $(A).getDimensions().width},classNames:function(A){return new Element.ClassNames(A)},hasClassName:function(A,B){if(!(A=$(A))){return }var C=A.className;return(C.length>0&&(C==B||new RegExp("(^|\\s)"+B+"(\\s|$)").test(C)))},addClassName:function(A,B){if(!(A=$(A))){return }if(!A.hasClassName(B)){A.className+=(A.className?" ":"")+B}return A},removeClassName:function(A,B){if(!(A=$(A))){return }A.className=A.className.replace(new RegExp("(^|\\s+)"+B+"(\\s+|$)")," ").strip();return A},toggleClassName:function(A,B){if(!(A=$(A))){return }return A[A.hasClassName(B)?"removeClassName":"addClassName"](B)},cleanWhitespace:function(B){B=$(B);var C=B.firstChild;while(C){var A=C.nextSibling;if(C.nodeType==3&&!/\S/.test(C.nodeValue)){B.removeChild(C)}C=A}return B},empty:function(A){return $(A).innerHTML.blank()},descendantOf:function(D,C){D=$(D),C=$(C);var F=C;if(D.compareDocumentPosition){return(D.compareDocumentPosition(C)&8)===8}if(D.sourceIndex&&!Prototype.Browser.Opera){var E=D.sourceIndex,B=C.sourceIndex,A=C.nextSibling;if(!A){do{C=C.parentNode}while(!(A=C.nextSibling)&&C.parentNode)}if(A){return(E>B&&E<A.sourceIndex)}}while(D=D.parentNode){if(D==F){return true}}return false},scrollTo:function(A){A=$(A);var B=A.cumulativeOffset();window.scrollTo(B[0],B[1]);return A},getStyle:function(B,C){B=$(B);C=C=="float"?"cssFloat":C.camelize();var D=B.style[C];if(!D){var A=document.defaultView.getComputedStyle(B,null);D=A?A[C]:null}if(C=="opacity"){return D?parseFloat(D):1}return D=="auto"?null:D},getOpacity:function(A){return $(A).getStyle("opacity")},setStyle:function(B,C){B=$(B);var E=B.style,A;if(Object.isString(C)){B.style.cssText+=";"+C;return C.include("opacity")?B.setOpacity(C.match(/opacity:\s*(\d?\.?\d*)/)[1]):B}for(var D in C){if(D=="opacity"){B.setOpacity(C[D])}else{E[(D=="float"||D=="cssFloat")?(Object.isUndefined(E.styleFloat)?"cssFloat":"styleFloat"):D]=C[D]}}return B},setOpacity:function(A,B){A=$(A);A.style.opacity=(B==1||B==="")?"":(B<0.00001)?0:B;return A},getDimensions:function(C){C=$(C);var G=$(C).getStyle("display");if(G!="none"&&G!=null){return{width:C.offsetWidth,height:C.offsetHeight}}var B=C.style;var F=B.visibility;var D=B.position;var A=B.display;B.visibility="hidden";B.position="absolute";B.display="block";var H=C.clientWidth;var E=C.clientHeight;B.display=A;B.position=D;B.visibility=F;return{width:H,height:E}},makePositioned:function(A){A=$(A);var B=Element.getStyle(A,"position");if(B=="static"||!B){A._madePositioned=true;A.style.position="relative";if(window.opera){A.style.top=0;A.style.left=0}}return A},undoPositioned:function(A){A=$(A);if(A._madePositioned){A._madePositioned=undefined;A.style.position=A.style.top=A.style.left=A.style.bottom=A.style.right=""}return A},makeClipping:function(A){A=$(A);if(A._overflow){return A}A._overflow=Element.getStyle(A,"overflow")||"auto";if(A._overflow!=="hidden"){A.style.overflow="hidden"}return A},undoClipping:function(A){A=$(A);if(!A._overflow){return A}A.style.overflow=A._overflow=="auto"?"":A._overflow;A._overflow=null;return A},cumulativeOffset:function(B){var A=0,C=0;do{A+=B.offsetTop||0;C+=B.offsetLeft||0;B=B.offsetParent}while(B);return Element._returnOffset(C,A)},positionedOffset:function(B){var A=0,D=0;do{A+=B.offsetTop||0;D+=B.offsetLeft||0;B=B.offsetParent;if(B){if(B.tagName=="BODY"){break}var C=Element.getStyle(B,"position");if(C=="relative"||C=="absolute"){break}}}while(B);return Element._returnOffset(D,A)},absolutize:function(B){B=$(B);if(B.getStyle("position")=="absolute"){return }var D=B.positionedOffset();var F=D[1];var E=D[0];var C=B.clientWidth;var A=B.clientHeight;B._originalLeft=E-parseFloat(B.style.left||0);B._originalTop=F-parseFloat(B.style.top||0);B._originalWidth=B.style.width;B._originalHeight=B.style.height;B.style.position="absolute";B.style.top=F+"px";B.style.left=E+"px";B.style.width=C+"px";B.style.height=A+"px";return B},relativize:function(A){A=$(A);if(A.getStyle("position")=="relative"){return }A.style.position="relative";var C=parseFloat(A.style.top||0)-(A._originalTop||0);var B=parseFloat(A.style.left||0)-(A._originalLeft||0);A.style.top=C+"px";A.style.left=B+"px";A.style.height=A._originalHeight;A.style.width=A._originalWidth;return A},cumulativeScrollOffset:function(B){var A=0,C=0;do{A+=B.scrollTop||0;C+=B.scrollLeft||0;B=B.parentNode}while(B);return Element._returnOffset(C,A)},getOffsetParent:function(A){if(A.offsetParent){return $(A.offsetParent)}if(A==document.body){return $(A)}while((A=A.parentNode)&&A!=document.body){if(Element.getStyle(A,"position")!="static"){return $(A)}}return $(document.body)},viewportOffset:function(D){var A=0,C=0;var B=D;do{A+=B.offsetTop||0;C+=B.offsetLeft||0;if(B.offsetParent==document.body&&Element.getStyle(B,"position")=="absolute"){break}}while(B=B.offsetParent);B=D;do{if(!Prototype.Browser.Opera||B.tagName=="BODY"){A-=B.scrollTop||0;C-=B.scrollLeft||0}}while(B=B.parentNode);return Element._returnOffset(C,A)},clonePosition:function(B,D){var A=Object.extend({setLeft:true,setTop:true,setWidth:true,setHeight:true,offsetTop:0,offsetLeft:0},arguments[2]||{});D=$(D);var E=D.viewportOffset();B=$(B);var F=[0,0];var C=null;if(Element.getStyle(B,"position")=="absolute"){C=B.getOffsetParent();F=C.viewportOffset()}if(C==document.body){F[0]-=document.body.offsetLeft;F[1]-=document.body.offsetTop}if(A.setLeft){B.style.left=(E[0]-F[0]+A.offsetLeft)+"px"}if(A.setTop){B.style.top=(E[1]-F[1]+A.offsetTop)+"px"}if(A.setWidth){B.style.width=D.offsetWidth+"px"}if(A.setHeight){B.style.height=D.offsetHeight+"px"}return B}};Element.Methods.identify.counter=1;Object.extend(Element.Methods,{getElementsBySelector:Element.Methods.select,childElements:Element.Methods.immediateDescendants});Element._attributeTranslations={write:{names:{className:"class",htmlFor:"for"},values:{}}};if(Prototype.Browser.Opera){Element.Methods.getStyle=Element.Methods.getStyle.wrap(function(D,B,C){switch(C){case"left":case"top":case"right":case"bottom":if(D(B,"position")==="static"){return null}case"height":case"width":if(!Element.visible(B)){return null}var E=parseInt(D(B,C),10);if(E!==B["offset"+C.capitalize()]){return E+"px"}var A;if(C==="height"){A=["border-top-width","padding-top","padding-bottom","border-bottom-width"]}else{A=["border-left-width","padding-left","padding-right","border-right-width"]}return A.inject(E,function(F,G){var H=D(B,G);return H===null?F:F-parseInt(H,10)})+"px";default:return D(B,C)}});Element.Methods.readAttribute=Element.Methods.readAttribute.wrap(function(C,A,B){if(B==="title"){return A.title}return C(A,B)})}else{if(Prototype.Browser.IE){$w("positionedOffset getOffsetParent viewportOffset").each(function(A){Element.Methods[A]=Element.Methods[A].wrap(function(D,C){C=$(C);var B=C.getStyle("position");if(B!="static"){return D(C)}C.setStyle({position:"relative"});var E=D(C);C.setStyle({position:B});return E})});Element.Methods.getStyle=function(A,B){A=$(A);B=(B=="float"||B=="cssFloat")?"styleFloat":B.camelize();var C=A.style[B];if(!C&&A.currentStyle){C=A.currentStyle[B]}if(B=="opacity"){if(C=(A.getStyle("filter")||"").match(/alpha\(opacity=(.*)\)/)){if(C[1]){return parseFloat(C[1])/100}}return 1}if(C=="auto"){if((B=="width"||B=="height")&&(A.getStyle("display")!="none")){return A["offset"+B.capitalize()]+"px"}return null}return C};Element.Methods.setOpacity=function(B,E){function F(G){return G.replace(/alpha\([^\)]*\)/gi,"")}B=$(B);var A=B.currentStyle;if((A&&!A.hasLayout)||(!A&&B.style.zoom=="normal")){B.style.zoom=1}var D=B.getStyle("filter"),C=B.style;if(E==1||E===""){(D=F(D))?C.filter=D:C.removeAttribute("filter");return B}else{if(E<0.00001){E=0}}C.filter=F(D)+"alpha(opacity="+(E*100)+")";return B};Element._attributeTranslations={read:{names:{"class":"className","for":"htmlFor"},values:{_getAttr:function(A,B){return A.getAttribute(B,2)},_getAttrNode:function(A,C){var B=A.getAttributeNode(C);return B?B.value:""},_getEv:function(A,B){B=A.getAttribute(B);return B?B.toString().slice(23,-2):null},_flag:function(A,B){return $(A).hasAttribute(B)?B:null},style:function(A){return A.style.cssText.toLowerCase()},title:function(A){return A.title}}}};Element._attributeTranslations.write={names:Object.clone(Element._attributeTranslations.read.names),values:{checked:function(A,B){A.checked=!!B},style:function(A,B){A.style.cssText=B?B:""}}};Element._attributeTranslations.has={};$w("colSpan rowSpan vAlign dateTime accessKey tabIndex encType maxLength readOnly longDesc").each(function(A){Element._attributeTranslations.write.names[A.toLowerCase()]=A;Element._attributeTranslations.has[A.toLowerCase()]=A});(function(A){Object.extend(A,{href:A._getAttr,src:A._getAttr,type:A._getAttr,action:A._getAttrNode,disabled:A._flag,checked:A._flag,readonly:A._flag,multiple:A._flag,onload:A._getEv,onunload:A._getEv,onclick:A._getEv,ondblclick:A._getEv,onmousedown:A._getEv,onmouseup:A._getEv,onmouseover:A._getEv,onmousemove:A._getEv,onmouseout:A._getEv,onfocus:A._getEv,onblur:A._getEv,onkeypress:A._getEv,onkeydown:A._getEv,onkeyup:A._getEv,onsubmit:A._getEv,onreset:A._getEv,onselect:A._getEv,onchange:A._getEv})})(Element._attributeTranslations.read.values)}else{if(Prototype.Browser.Gecko&&/rv:1\.8\.0/.test(navigator.userAgent)){Element.Methods.setOpacity=function(A,B){A=$(A);A.style.opacity=(B==1)?0.999999:(B==="")?"":(B<0.00001)?0:B;return A}}else{if(Prototype.Browser.WebKit){Element.Methods.setOpacity=function(A,B){A=$(A);A.style.opacity=(B==1||B==="")?"":(B<0.00001)?0:B;if(B==1){if(A.tagName=="IMG"&&A.width){A.width++;A.width--}else{try{var D=document.createTextNode(" ");A.appendChild(D);A.removeChild(D)}catch(C){}}}return A};Element.Methods.cumulativeOffset=function(B){var A=0,C=0;do{A+=B.offsetTop||0;C+=B.offsetLeft||0;if(B.offsetParent==document.body){if(Element.getStyle(B,"position")=="absolute"){break}}B=B.offsetParent}while(B);return Element._returnOffset(C,A)}}}}}if(Prototype.Browser.IE||Prototype.Browser.Opera){Element.Methods.update=function(B,C){B=$(B);if(C&&C.toElement){C=C.toElement()}if(Object.isElement(C)){return B.update().insert(C)}C=Object.toHTML(C);var A=B.tagName.toUpperCase();if(A in Element._insertionTranslations.tags){$A(B.childNodes).each(function(D){B.removeChild(D)});Element._getContentFromAnonymousElement(A,C.stripScripts()).each(function(D){B.appendChild(D)})}else{B.innerHTML=C.stripScripts()}C.evalScripts.bind(C).defer();return B}}if(document.createElement("div").outerHTML){Element.Methods.replace=function(C,E){C=$(C);if(E&&E.toElement){E=E.toElement()}if(Object.isElement(E)){C.parentNode.replaceChild(E,C);return C}E=Object.toHTML(E);var D=C.parentNode,B=D.tagName.toUpperCase();if(Element._insertionTranslations.tags[B]){var F=C.next();var A=Element._getContentFromAnonymousElement(B,E.stripScripts());D.removeChild(C);if(F){A.each(function(G){D.insertBefore(G,F)})}else{A.each(function(G){D.appendChild(G)})}}else{C.outerHTML=E.stripScripts()}E.evalScripts.bind(E).defer();return C}}Element._returnOffset=function(B,C){var A=[B,C];A.left=B;A.top=C;return A};Element._getContentFromAnonymousElement=function(C,B){var D=new Element("div"),A=Element._insertionTranslations.tags[C];if(A){D.innerHTML=A[0]+B+A[1];A[2].times(function(){D=D.firstChild})}else{D.innerHTML=B}return $A(D.childNodes)};Element._insertionTranslations={before:function(A,B){A.parentNode.insertBefore(B,A)},top:function(A,B){A.insertBefore(B,A.firstChild)},bottom:function(A,B){A.appendChild(B)},after:function(A,B){A.parentNode.insertBefore(B,A.nextSibling)},tags:{TABLE:["<table>","</table>",1],TBODY:["<table><tbody>","</tbody></table>",2],TR:["<table><tbody><tr>","</tr></tbody></table>",3],TD:["<table><tbody><tr><td>","</td></tr></tbody></table>",4],SELECT:["<select>","</select>",1]}};(function(){Object.extend(this.tags,{THEAD:this.tags.TBODY,TFOOT:this.tags.TBODY,TH:this.tags.TD})}).call(Element._insertionTranslations);Element.Methods.Simulated={hasAttribute:function(A,C){C=Element._attributeTranslations.has[C]||C;var B=$(A).getAttributeNode(C);return B&&B.specified}};Element.Methods.ByTag={};Object.extend(Element,Element.Methods);if(!Prototype.BrowserFeatures.ElementExtensions&&document.createElement("div").__proto__){window.HTMLElement={};window.HTMLElement.prototype=document.createElement("div").__proto__;Prototype.BrowserFeatures.ElementExtensions=true}Element.extend=(function(){if(Prototype.BrowserFeatures.SpecificElementExtensions){return Prototype.K}var A={},B=Element.Methods.ByTag;var C=Object.extend(function(F){if(!F||F._extendedByPrototype||F.nodeType!=1||F==window){return F}var D=Object.clone(A),E=F.tagName,H,G;if(B[E]){Object.extend(D,B[E])}for(H in D){G=D[H];if(Object.isFunction(G)&&!(H in F)){F[H]=G.methodize()}}F._extendedByPrototype=Prototype.emptyFunction;return F},{refresh:function(){if(!Prototype.BrowserFeatures.ElementExtensions){Object.extend(A,Element.Methods);Object.extend(A,Element.Methods.Simulated)}}});C.refresh();return C})();Element.hasAttribute=function(A,B){if(A.hasAttribute){return A.hasAttribute(B)}return Element.Methods.Simulated.hasAttribute(A,B)};Element.addMethods=function(C){var I=Prototype.BrowserFeatures,D=Element.Methods.ByTag;if(!C){Object.extend(Form,Form.Methods);Object.extend(Form.Element,Form.Element.Methods);Object.extend(Element.Methods.ByTag,{FORM:Object.clone(Form.Methods),INPUT:Object.clone(Form.Element.Methods),SELECT:Object.clone(Form.Element.Methods),TEXTAREA:Object.clone(Form.Element.Methods)})}if(arguments.length==2){var B=C;C=arguments[1]}if(!B){Object.extend(Element.Methods,C||{})}else{if(Object.isArray(B)){B.each(H)}else{H(B)}}function H(F){F=F.toUpperCase();if(!Element.Methods.ByTag[F]){Element.Methods.ByTag[F]={}}Object.extend(Element.Methods.ByTag[F],C)}function A(L,K,F){F=F||false;for(var N in L){var M=L[N];if(!Object.isFunction(M)){continue}if(!F||!(N in K)){K[N]=M.methodize()}}}function E(L){var F;var K={OPTGROUP:"OptGroup",TEXTAREA:"TextArea",P:"Paragraph",FIELDSET:"FieldSet",UL:"UList",OL:"OList",DL:"DList",DIR:"Directory",H1:"Heading",H2:"Heading",H3:"Heading",H4:"Heading",H5:"Heading",H6:"Heading",Q:"Quote",INS:"Mod",DEL:"Mod",A:"Anchor",IMG:"Image",CAPTION:"TableCaption",COL:"TableCol",COLGROUP:"TableCol",THEAD:"TableSection",TFOOT:"TableSection",TBODY:"TableSection",TR:"TableRow",TH:"TableCell",TD:"TableCell",FRAMESET:"FrameSet",IFRAME:"IFrame"};if(K[L]){F="HTML"+K[L]+"Element"}if(window[F]){return window[F]}F="HTML"+L+"Element";if(window[F]){return window[F]}F="HTML"+L.capitalize()+"Element";if(window[F]){return window[F]}window[F]={};window[F].prototype=document.createElement(L).__proto__;return window[F]}if(I.ElementExtensions){A(Element.Methods,HTMLElement.prototype);A(Element.Methods.Simulated,HTMLElement.prototype,true)}if(I.SpecificElementExtensions){for(var J in Element.Methods.ByTag){var G=E(J);if(Object.isUndefined(G)){continue}A(D[J],G.prototype)}}Object.extend(Element,Element.Methods);delete Element.ByTag;if(Element.extend.refresh){Element.extend.refresh()}Element.cache={}};document.viewport={getDimensions:function(){var A={};var C=Prototype.Browser;$w("width height").each(function(E){var B=E.capitalize();A[E]=(C.WebKit&&!document.evaluate)?self["inner"+B]:(C.Opera)?document.body["client"+B]:document.documentElement["client"+B]});return A},getWidth:function(){return this.getDimensions().width},getHeight:function(){return this.getDimensions().height},getScrollOffsets:function(){return Element._returnOffset(window.pageXOffset||document.documentElement.scrollLeft||document.body.scrollLeft,window.pageYOffset||document.documentElement.scrollTop||document.body.scrollTop)}};var Selector=Class.create({initialize:function(A){this.expression=A.strip();this.compileMatcher()},shouldUseXPath:function(){if(!Prototype.BrowserFeatures.XPath){return false}var A=this.expression;if(Prototype.Browser.WebKit&&(A.include("-of-type")||A.include(":empty"))){return false}if((/(\[[\w-]*?:|:checked)/).test(this.expression)){return false}return true},compileMatcher:function(){if(this.shouldUseXPath()){return this.compileXPathMatcher()}var e=this.expression,ps=Selector.patterns,h=Selector.handlers,c=Selector.criteria,le,p,m;if(Selector._cache[e]){this.matcher=Selector._cache[e];return }this.matcher=["this.matcher = function(root) {","var r = root, h = Selector.handlers, c = false, n;"];while(e&&le!=e&&(/\S/).test(e)){le=e;for(var i in ps){p=ps[i];if(m=e.match(p)){this.matcher.push(Object.isFunction(c[i])?c[i](m):new Template(c[i]).evaluate(m));e=e.replace(m[0],"");break}}}this.matcher.push("return h.unique(n);\n}");eval(this.matcher.join("\n"));Selector._cache[this.expression]=this.matcher},compileXPathMatcher:function(){var E=this.expression,F=Selector.patterns,B=Selector.xpath,D,A;if(Selector._cache[E]){this.xpath=Selector._cache[E];return }this.matcher=[".//*"];while(E&&D!=E&&(/\S/).test(E)){D=E;for(var C in F){if(A=E.match(F[C])){this.matcher.push(Object.isFunction(B[C])?B[C](A):new Template(B[C]).evaluate(A));E=E.replace(A[0],"");break}}}this.xpath=this.matcher.join("");Selector._cache[this.expression]=this.xpath},findElements:function(A){A=A||document;if(this.xpath){return document._getElementsByXPath(this.xpath,A)}return this.matcher(A)},match:function(H){this.tokens=[];var L=this.expression,A=Selector.patterns,E=Selector.assertions;var B,D,F;while(L&&B!==L&&(/\S/).test(L)){B=L;for(var I in A){D=A[I];if(F=L.match(D)){if(E[I]){this.tokens.push([I,Object.clone(F)]);L=L.replace(F[0],"")}else{return this.findElements(document).include(H)}}}}var K=true,C,J;for(var I=0,G;G=this.tokens[I];I++){C=G[0],J=G[1];if(!Selector.assertions[C](H,J)){K=false;break}}return K},toString:function(){return this.expression},inspect:function(){return"#<Selector:"+this.expression.inspect()+">"}});Object.extend(Selector,{_cache:{},xpath:{descendant:"//*",child:"/*",adjacent:"/following-sibling::*[1]",laterSibling:"/following-sibling::*",tagName:function(A){if(A[1]=="*"){return""}return"[local-name()='"+A[1].toLowerCase()+"' or local-name()='"+A[1].toUpperCase()+"']"},className:"[contains(concat(' ', @class, ' '), ' #{1} ')]",id:"[@id='#{1}']",attrPresence:function(A){A[1]=A[1].toLowerCase();return new Template("[@#{1}]").evaluate(A)},attr:function(A){A[1]=A[1].toLowerCase();A[3]=A[5]||A[6];return new Template(Selector.xpath.operators[A[2]]).evaluate(A)},pseudo:function(A){var B=Selector.xpath.pseudos[A[1]];if(!B){return""}if(Object.isFunction(B)){return B(A)}return new Template(Selector.xpath.pseudos[A[1]]).evaluate(A)},operators:{"=":"[@#{1}='#{3}']","!=":"[@#{1}!='#{3}']","^=":"[starts-with(@#{1}, '#{3}')]","$=":"[substring(@#{1}, (string-length(@#{1}) - string-length('#{3}') + 1))='#{3}']","*=":"[contains(@#{1}, '#{3}')]","~=":"[contains(concat(' ', @#{1}, ' '), ' #{3} ')]","|=":"[contains(concat('-', @#{1}, '-'), '-#{3}-')]"},pseudos:{"first-child":"[not(preceding-sibling::*)]","last-child":"[not(following-sibling::*)]","only-child":"[not(preceding-sibling::* or following-sibling::*)]",empty:"[count(*) = 0 and (count(text()) = 0 or translate(text(), ' \t\r\n', '') = '')]",checked:"[@checked]",disabled:"[@disabled]",enabled:"[not(@disabled)]",not:function(B){var H=B[6],G=Selector.patterns,A=Selector.xpath,E,C;var F=[];while(H&&E!=H&&(/\S/).test(H)){E=H;for(var D in G){if(B=H.match(G[D])){C=Object.isFunction(A[D])?A[D](B):new Template(A[D]).evaluate(B);F.push("("+C.substring(1,C.length-1)+")");H=H.replace(B[0],"");break}}}return"[not("+F.join(" and ")+")]"},"nth-child":function(A){return Selector.xpath.pseudos.nth("(count(./preceding-sibling::*) + 1) ",A)},"nth-last-child":function(A){return Selector.xpath.pseudos.nth("(count(./following-sibling::*) + 1) ",A)},"nth-of-type":function(A){return Selector.xpath.pseudos.nth("position() ",A)},"nth-last-of-type":function(A){return Selector.xpath.pseudos.nth("(last() + 1 - position()) ",A)},"first-of-type":function(A){A[6]="1";return Selector.xpath.pseudos["nth-of-type"](A)},"last-of-type":function(A){A[6]="1";return Selector.xpath.pseudos["nth-last-of-type"](A)},"only-of-type":function(A){var B=Selector.xpath.pseudos;return B["first-of-type"](A)+B["last-of-type"](A)},nth:function(E,C){var F,G=C[6],B;if(G=="even"){G="2n+0"}if(G=="odd"){G="2n+1"}if(F=G.match(/^(\d+)$/)){return"["+E+"= "+F[1]+"]"}if(F=G.match(/^(-?\d*)?n(([+-])(\d+))?/)){if(F[1]=="-"){F[1]=-1}var D=F[1]?Number(F[1]):1;var A=F[2]?Number(F[2]):0;B="[((#{fragment} - #{b}) mod #{a} = 0) and ((#{fragment} - #{b}) div #{a} >= 0)]";return new Template(B).evaluate({fragment:E,a:D,b:A})}}}},criteria:{tagName:'n = h.tagName(n, r, "#{1}", c);   c = false;',className:'n = h.className(n, r, "#{1}", c); c = false;',id:'n = h.id(n, r, "#{1}", c);        c = false;',attrPresence:'n = h.attrPresence(n, r, "#{1}"); c = false;',attr:function(A){A[3]=(A[5]||A[6]);return new Template('n = h.attr(n, r, "#{1}", "#{3}", "#{2}"); c = false;').evaluate(A)},pseudo:function(A){if(A[6]){A[6]=A[6].replace(/"/g,'\\"')}return new Template('n = h.pseudo(n, "#{1}", "#{6}", r, c); c = false;').evaluate(A)},descendant:'c = "descendant";',child:'c = "child";',adjacent:'c = "adjacent";',laterSibling:'c = "laterSibling";'},patterns:{laterSibling:/^\s*~\s*/,child:/^\s*>\s*/,adjacent:/^\s*\+\s*/,descendant:/^\s/,tagName:/^\s*(\*|[\w\-]+)(\b|$)?/,id:/^#([\w\-\*]+)(\b|$)/,className:/^\.([\w\-\*]+)(\b|$)/,pseudo:/^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not)(\((.*?)\))?(\b|$|(?=\s|[:+~>]))/,attrPresence:/^\[([\w]+)\]/,attr:/\[((?:[\w-]*:)?[\w-]+)\s*(?:([!^$*~|]?=)\s*((['"])([^\4]*?)\4|([^'"][^\]]*?)))?\]/},assertions:{tagName:function(A,B){return B[1].toUpperCase()==A.tagName.toUpperCase()},className:function(A,B){return Element.hasClassName(A,B[1])},id:function(A,B){return A.id===B[1]},attrPresence:function(A,B){return Element.hasAttribute(A,B[1])},attr:function(B,C){var A=Element.readAttribute(B,C[1]);return Selector.operators[C[2]](A,C[3])}},handlers:{concat:function(B,A){for(var C=0,D;D=A[C];C++){B.push(D)}return B},mark:function(A){for(var B=0,C;C=A[B];B++){C._counted=true}return A},unmark:function(A){for(var B=0,C;C=A[B];B++){C._counted=undefined}return A},index:function(A,D,G){A._counted=true;if(D){for(var B=A.childNodes,E=B.length-1,C=1;E>=0;E--){var F=B[E];if(F.nodeType==1&&(!G||F._counted)){F.nodeIndex=C++}}}else{for(var E=0,C=1,B=A.childNodes;F=B[E];E++){if(F.nodeType==1&&(!G||F._counted)){F.nodeIndex=C++}}}},unique:function(B){if(B.length==0){return B}var D=[],E;for(var C=0,A=B.length;C<A;C++){if(!(E=B[C])._counted){E._counted=true;D.push(Element.extend(E))}}return Selector.handlers.unmark(D)},descendant:function(A){var D=Selector.handlers;for(var C=0,B=[],E;E=A[C];C++){D.concat(B,E.getElementsByTagName("*"))}return B},child:function(A){var E=Selector.handlers;for(var D=0,C=[],F;F=A[D];D++){for(var B=0,G;G=F.childNodes[B];B++){if(G.nodeType==1&&G.tagName!="!"){C.push(G)}}}return C},adjacent:function(A){for(var C=0,B=[],E;E=A[C];C++){var D=this.nextElementSibling(E);if(D){B.push(D)}}return B},laterSibling:function(A){var D=Selector.handlers;for(var C=0,B=[],E;E=A[C];C++){D.concat(B,Element.nextSiblings(E))}return B},nextElementSibling:function(A){while(A=A.nextSibling){if(A.nodeType==1){return A}}return null},previousElementSibling:function(A){while(A=A.previousSibling){if(A.nodeType==1){return A}}return null},tagName:function(A,H,C,B){var I=C.toUpperCase();var E=[],G=Selector.handlers;if(A){if(B){if(B=="descendant"){for(var F=0,D;D=A[F];F++){G.concat(E,D.getElementsByTagName(C))}return E}else{A=this[B](A)}if(C=="*"){return A}}for(var F=0,D;D=A[F];F++){if(D.tagName.toUpperCase()===I){E.push(D)}}return E}else{return H.getElementsByTagName(C)}},id:function(B,A,H,F){var G=$(H),D=Selector.handlers;if(!G){return[]}if(!B&&A==document){return[G]}if(B){if(F){if(F=="child"){for(var C=0,E;E=B[C];C++){if(G.parentNode==E){return[G]}}}else{if(F=="descendant"){for(var C=0,E;E=B[C];C++){if(Element.descendantOf(G,E)){return[G]}}}else{if(F=="adjacent"){for(var C=0,E;E=B[C];C++){if(Selector.handlers.previousElementSibling(G)==E){return[G]}}}else{B=D[F](B)}}}}for(var C=0,E;E=B[C];C++){if(E==G){return[G]}}return[]}return(G&&Element.descendantOf(G,A))?[G]:[]},className:function(B,A,C,D){if(B&&D){B=this[D](B)}return Selector.handlers.byClassName(B,A,C)},byClassName:function(C,B,F){if(!C){C=Selector.handlers.descendant([B])}var H=" "+F+" ";for(var E=0,D=[],G,A;G=C[E];E++){A=G.className;if(A.length==0){continue}if(A==F||(" "+A+" ").include(H)){D.push(G)}}return D},attrPresence:function(C,B,A){if(!C){C=B.getElementsByTagName("*")}var E=[];for(var D=0,F;F=C[D];D++){if(Element.hasAttribute(F,A)){E.push(F)}}return E},attr:function(A,H,G,I,B){if(!A){A=H.getElementsByTagName("*")}var J=Selector.operators[B],D=[];for(var E=0,C;C=A[E];E++){var F=Element.readAttribute(C,G);if(F===null){continue}if(J(F,I)){D.push(C)}}return D},pseudo:function(B,C,E,A,D){if(B&&D){B=this[D](B)}if(!B){B=A.getElementsByTagName("*")}return Selector.pseudos[C](B,E,A)}},pseudos:{"first-child":function(B,F,A){for(var D=0,C=[],E;E=B[D];D++){if(Selector.handlers.previousElementSibling(E)){continue}C.push(E)}return C},"last-child":function(B,F,A){for(var D=0,C=[],E;E=B[D];D++){if(Selector.handlers.nextElementSibling(E)){continue}C.push(E)}return C},"only-child":function(B,G,A){var E=Selector.handlers;for(var D=0,C=[],F;F=B[D];D++){if(!E.previousElementSibling(F)&&!E.nextElementSibling(F)){C.push(F)}}return C},"nth-child":function(B,C,A){return Selector.pseudos.nth(B,C,A)},"nth-last-child":function(B,C,A){return Selector.pseudos.nth(B,C,A,true)},"nth-of-type":function(B,C,A){return Selector.pseudos.nth(B,C,A,false,true)},"nth-last-of-type":function(B,C,A){return Selector.pseudos.nth(B,C,A,true,true)},"first-of-type":function(B,C,A){return Selector.pseudos.nth(B,"1",A,false,true)},"last-of-type":function(B,C,A){return Selector.pseudos.nth(B,"1",A,true,true)},"only-of-type":function(B,D,A){var C=Selector.pseudos;return C["last-of-type"](C["first-of-type"](B,D,A),D,A)},getIndices:function(B,A,C){if(B==0){return A>0?[A]:[]}return $R(1,C).inject([],function(D,E){if(0==(E-A)%B&&(E-A)/B>=0){D.push(E)}return D})},nth:function(A,L,N,K,C){if(A.length==0){return[]}if(L=="even"){L="2n+0"}if(L=="odd"){L="2n+1"}var J=Selector.handlers,I=[],B=[],E;J.mark(A);for(var H=0,D;D=A[H];H++){if(!D.parentNode._counted){J.index(D.parentNode,K,C);B.push(D.parentNode)}}if(L.match(/^\d+$/)){L=Number(L);for(var H=0,D;D=A[H];H++){if(D.nodeIndex==L){I.push(D)}}}else{if(E=L.match(/^(-?\d*)?n(([+-])(\d+))?/)){if(E[1]=="-"){E[1]=-1}var O=E[1]?Number(E[1]):1;var M=E[2]?Number(E[2]):0;var P=Selector.pseudos.getIndices(O,M,A.length);for(var H=0,D,F=P.length;D=A[H];H++){for(var G=0;G<F;G++){if(D.nodeIndex==P[G]){I.push(D)}}}}}J.unmark(A);J.unmark(B);return I},empty:function(B,F,A){for(var D=0,C=[],E;E=B[D];D++){if(E.tagName=="!"||(E.firstChild&&!E.innerHTML.match(/^\s*$/))){continue}C.push(E)}return C},not:function(A,D,I){var G=Selector.handlers,J,C;var H=new Selector(D).findElements(I);G.mark(H);for(var F=0,E=[],B;B=A[F];F++){if(!B._counted){E.push(B)}}G.unmark(H);return E},enabled:function(B,F,A){for(var D=0,C=[],E;E=B[D];D++){if(!E.disabled){C.push(E)}}return C},disabled:function(B,F,A){for(var D=0,C=[],E;E=B[D];D++){if(E.disabled){C.push(E)}}return C},checked:function(B,F,A){for(var D=0,C=[],E;E=B[D];D++){if(E.checked){C.push(E)}}return C}},operators:{"=":function(B,A){return B==A},"!=":function(B,A){return B!=A},"^=":function(B,A){return B.startsWith(A)},"$=":function(B,A){return B.endsWith(A)},"*=":function(B,A){return B.include(A)},"~=":function(B,A){return(" "+B+" ").include(" "+A+" ")},"|=":function(B,A){return("-"+B.toUpperCase()+"-").include("-"+A.toUpperCase()+"-")}},matchElements:function(F,G){var E=new Selector(G).findElements(),D=Selector.handlers;D.mark(E);for(var C=0,B=[],A;A=F[C];C++){if(A._counted){B.push(A)}}D.unmark(E);return B},findElement:function(B,C,A){if(Object.isNumber(C)){A=C;C=false}return Selector.matchElements(B,C||"*")[A||0]},findChildElements:function(E,G){var H=G.join(",");G=[];H.scan(/(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)/,function(I){G.push(I[1].strip())});var D=[],F=Selector.handlers;for(var C=0,B=G.length,A;C<B;C++){A=new Selector(G[C].strip());F.concat(D,A.findElements(E))}return(B>1)?F.unique(D):D}});if(Prototype.Browser.IE){Selector.handlers.concat=function(B,A){for(var C=0,D;D=A[C];C++){if(D.tagName!=="!"){B.push(D)}}return B}}function $$(){return Selector.findChildElements(document,$A(arguments))}var Form={reset:function(A){$(A).reset();return A},serializeElements:function(G,B){if(typeof B!="object"){B={hash:!!B}}else{if(Object.isUndefined(B.hash)){B.hash=true}}var C,F,A=false,E=B.submit;var D=G.inject({},function(H,I){if(!I.disabled&&I.name){C=I.name;F=$(I).getValue();if(F!=null&&(I.type!="submit"||(!A&&E!==false&&(!E||C==E)&&(A=true)))){if(C in H){if(!Object.isArray(H[C])){H[C]=[H[C]]}H[C].push(F)}else{H[C]=F}}}return H});return B.hash?D:Object.toQueryString(D)}};Form.Methods={serialize:function(B,A){return Form.serializeElements(Form.getElements(B),A)},getElements:function(A){return $A($(A).getElementsByTagName("*")).inject([],function(B,C){if(Form.Element.Serializers[C.tagName.toLowerCase()]){B.push(Element.extend(C))}return B})},getInputs:function(G,C,D){G=$(G);var A=G.getElementsByTagName("input");if(!C&&!D){return $A(A).map(Element.extend)}for(var E=0,H=[],F=A.length;E<F;E++){var B=A[E];if((C&&B.type!=C)||(D&&B.name!=D)){continue}H.push(Element.extend(B))}return H},disable:function(A){A=$(A);Form.getElements(A).invoke("disable");return A},enable:function(A){A=$(A);Form.getElements(A).invoke("enable");return A},findFirstElement:function(B){var C=$(B).getElements().findAll(function(D){return"hidden"!=D.type&&!D.disabled});var A=C.findAll(function(D){return D.hasAttribute("tabIndex")&&D.tabIndex>=0}).sortBy(function(D){return D.tabIndex}).first();return A?A:C.find(function(D){return["input","select","textarea"].include(D.tagName.toLowerCase())})},focusFirstElement:function(A){A=$(A);A.findFirstElement().activate();return A},request:function(B,A){B=$(B),A=Object.clone(A||{});var D=A.parameters,C=B.readAttribute("action")||"";if(C.blank()){C=window.location.href}A.parameters=B.serialize(true);if(D){if(Object.isString(D)){D=D.toQueryParams()}Object.extend(A.parameters,D)}if(B.hasAttribute("method")&&!A.method){A.method=B.method}return new Ajax.Request(C,A)}};Form.Element={focus:function(A){$(A).focus();return A},select:function(A){$(A).select();return A}};Form.Element.Methods={serialize:function(A){A=$(A);if(!A.disabled&&A.name){var B=A.getValue();if(B!=undefined){var C={};C[A.name]=B;return Object.toQueryString(C)}}return""},getValue:function(A){A=$(A);var B=A.tagName.toLowerCase();return Form.Element.Serializers[B](A)},setValue:function(A,B){A=$(A);var C=A.tagName.toLowerCase();Form.Element.Serializers[C](A,B);return A},clear:function(A){$(A).value="";return A},present:function(A){return $(A).value!=""},activate:function(A){A=$(A);try{A.focus();if(A.select&&(A.tagName.toLowerCase()!="input"||!["button","reset","submit"].include(A.type))){A.select()}}catch(B){}return A},disable:function(A){A=$(A);A.blur();A.disabled=true;return A},enable:function(A){A=$(A);A.disabled=false;return A}};var Field=Form.Element;var $F=Form.Element.Methods.getValue;Form.Element.Serializers={input:function(A,B){switch(A.type.toLowerCase()){case"checkbox":case"radio":return Form.Element.Serializers.inputSelector(A,B);default:return Form.Element.Serializers.textarea(A,B)}},inputSelector:function(A,B){if(Object.isUndefined(B)){return A.checked?A.value:null}else{A.checked=!!B}},textarea:function(A,B){if(Object.isUndefined(B)){return A.value}else{A.value=B}},select:function(D,A){if(Object.isUndefined(A)){return this[D.type=="select-one"?"selectOne":"selectMany"](D)}else{var C,F,G=!Object.isArray(A);for(var B=0,E=D.length;B<E;B++){C=D.options[B];F=this.optionValue(C);if(G){if(F==A){C.selected=true;return }}else{C.selected=A.include(F)}}}},selectOne:function(B){var A=B.selectedIndex;return A>=0?this.optionValue(B.options[A]):null},selectMany:function(D){var A,E=D.length;if(!E){return null}for(var C=0,A=[];C<E;C++){var B=D.options[C];if(B.selected){A.push(this.optionValue(B))}}return A},optionValue:function(A){return Element.extend(A).hasAttribute("value")?A.value:A.text}};Abstract.TimedObserver=Class.create(PeriodicalExecuter,{initialize:function($super,A,B,C){$super(C,B);this.element=$(A);this.lastValue=this.getValue()},execute:function(){var A=this.getValue();if(Object.isString(this.lastValue)&&Object.isString(A)?this.lastValue!=A:String(this.lastValue)!=String(A)){this.callback(this.element,A);this.lastValue=A}}});Form.Element.Observer=Class.create(Abstract.TimedObserver,{getValue:function(){return Form.Element.getValue(this.element)}});Form.Observer=Class.create(Abstract.TimedObserver,{getValue:function(){return Form.serialize(this.element)}});Abstract.EventObserver=Class.create({initialize:function(A,B){this.element=$(A);this.callback=B;this.lastValue=this.getValue();if(this.element.tagName.toLowerCase()=="form"){this.registerFormCallbacks()}else{this.registerCallback(this.element)}},onElementEvent:function(){var A=this.getValue();if(this.lastValue!=A){this.callback(this.element,A);this.lastValue=A}},registerFormCallbacks:function(){Form.getElements(this.element).each(this.registerCallback,this)},registerCallback:function(A){if(A.type){switch(A.type.toLowerCase()){case"checkbox":case"radio":Event.observe(A,"click",this.onElementEvent.bind(this));break;default:Event.observe(A,"change",this.onElementEvent.bind(this));break}}}});Form.Element.EventObserver=Class.create(Abstract.EventObserver,{getValue:function(){return Form.Element.getValue(this.element)}});Form.EventObserver=Class.create(Abstract.EventObserver,{getValue:function(){return Form.serialize(this.element)}});if(!window.Event){var Event={}}Object.extend(Event,{KEY_BACKSPACE:8,KEY_TAB:9,KEY_RETURN:13,KEY_ESC:27,KEY_LEFT:37,KEY_UP:38,KEY_RIGHT:39,KEY_DOWN:40,KEY_DELETE:46,KEY_HOME:36,KEY_END:35,KEY_PAGEUP:33,KEY_PAGEDOWN:34,KEY_INSERT:45,cache:{},relatedTarget:function(B){var A;switch(B.type){case"mouseover":A=B.fromElement;break;case"mouseout":A=B.toElement;break;default:return null}return Element.extend(A)}});Event.Methods=(function(){var A;if(Prototype.Browser.IE){var B={0:1,1:4,2:2};A=function(D,C){return D.button==B[C]}}else{if(Prototype.Browser.WebKit){A=function(D,C){switch(C){case 0:return D.which==1&&!D.metaKey;case 1:return D.which==1&&D.metaKey;default:return false}}}else{A=function(D,C){return D.which?(D.which===C+1):(D.button===C)}}}return{isLeftClick:function(C){return A(C,0)},isMiddleClick:function(C){return A(C,1)},isRightClick:function(C){return A(C,2)},element:function(D){var C=Event.extend(D).target;return Element.extend(C.nodeType==Node.TEXT_NODE?C.parentNode:C)},findElement:function(D,F){var C=Event.element(D);if(!F){return C}var E=[C].concat(C.ancestors());return Selector.findElement(E,F,0)},pointer:function(C){return{x:C.pageX||(C.clientX+(document.documentElement.scrollLeft||document.body.scrollLeft)),y:C.pageY||(C.clientY+(document.documentElement.scrollTop||document.body.scrollTop))}},pointerX:function(C){return Event.pointer(C).x},pointerY:function(C){return Event.pointer(C).y},stop:function(C){Event.extend(C);C.preventDefault();C.stopPropagation();C.stopped=true}}})();Event.extend=(function(){var A=Object.keys(Event.Methods).inject({},function(B,C){B[C]=Event.Methods[C].methodize();return B});if(Prototype.Browser.IE){Object.extend(A,{stopPropagation:function(){this.cancelBubble=true},preventDefault:function(){this.returnValue=false},inspect:function(){return"[object Event]"}});return function(B){if(!B){return false}if(B._extendedByPrototype){return B}B._extendedByPrototype=Prototype.emptyFunction;var C=Event.pointer(B);Object.extend(B,{target:B.srcElement,relatedTarget:Event.relatedTarget(B),pageX:C.x,pageY:C.y});return Object.extend(B,A)}}else{Event.prototype=Event.prototype||document.createEvent("HTMLEvents").__proto__;Object.extend(Event.prototype,A);return Prototype.K}})();Object.extend(Event,(function(){var B=Event.cache;function C(J){if(J._eventID){return J._eventID}arguments.callee.id=arguments.callee.id||1;return J._eventID=++arguments.callee.id}function G(J){if(J&&J.include(":")){return"dataavailable"}return J}function A(J){return B[J]=B[J]||{}}function F(L,J){var K=A(L);return K[J]=K[J]||[]}function H(K,J,L){var O=C(K);var N=F(O,J);if(N.pluck("handler").include(L)){return false}var M=function(P){if(!Event||!Event.extend||(P.eventName&&P.eventName!=J)){return false}Event.extend(P);L.call(K,P)};M.handler=L;N.push(M);return M}function I(M,J,K){var L=F(M,J);return L.find(function(N){return N.handler==K})}function D(M,J,K){var L=A(M);if(!L[J]){return false}L[J]=L[J].without(I(M,J,K))}function E(){for(var K in B){for(var J in B[K]){B[K][J]=null}}}if(window.attachEvent){window.attachEvent("onunload",E)}return{observe:function(L,J,M){L=$(L);var K=G(J);var N=H(L,J,M);if(!N){return L}if(L.addEventListener){L.addEventListener(K,N,false)}else{L.attachEvent("on"+K,N)}return L},stopObserving:function(L,J,M){L=$(L);var O=C(L),K=G(J);if(!M&&J){F(O,J).each(function(P){L.stopObserving(J,P.handler)});return L}else{if(!J){Object.keys(A(O)).each(function(P){L.stopObserving(P)});return L}}var N=I(O,J,M);if(!N){return L}if(L.removeEventListener){L.removeEventListener(K,N,false)}else{L.detachEvent("on"+K,N)}D(O,J,M);return L},fire:function(L,K,J){L=$(L);if(L==document&&document.createEvent&&!L.dispatchEvent){L=document.documentElement}var M;if(document.createEvent){M=document.createEvent("HTMLEvents");M.initEvent("dataavailable",true,true)}else{M=document.createEventObject();M.eventType="ondataavailable"}M.eventName=K;M.memo=J||{};if(document.createEvent){L.dispatchEvent(M)}else{L.fireEvent(M.eventType,M)}return Event.extend(M)}}})());Object.extend(Event,Event.Methods);Element.addMethods({fire:Event.fire,observe:Event.observe,stopObserving:Event.stopObserving});Object.extend(document,{fire:Element.Methods.fire.methodize(),observe:Element.Methods.observe.methodize(),stopObserving:Element.Methods.stopObserving.methodize(),loaded:false});(function(){var B;function A(){if(document.loaded){return }if(B){window.clearInterval(B)}document.fire("dom:loaded");document.loaded=true}if(document.addEventListener){if(Prototype.Browser.WebKit){B=window.setInterval(function(){if(/loaded|complete/.test(document.readyState)){A()}},0);Event.observe(window,"load",A)}else{document.addEventListener("DOMContentLoaded",A,false)}}else{document.write("<script id=__onDOMContentLoaded defer src=//:><\/script>");$("__onDOMContentLoaded").onreadystatechange=function(){if(this.readyState=="complete"){this.onreadystatechange=null;A()}}}})();Hash.toQueryString=Object.toQueryString;var Toggle={display:Element.toggle};Element.Methods.childOf=Element.Methods.descendantOf;var Insertion={Before:function(A,B){return Element.insert(A,{before:B})},Top:function(A,B){return Element.insert(A,{top:B})},Bottom:function(A,B){return Element.insert(A,{bottom:B})},After:function(A,B){return Element.insert(A,{after:B})}};var $continue=new Error('"throw $continue" is deprecated, use "return" instead');var Position={includeScrollOffsets:false,prepare:function(){this.deltaX=window.pageXOffset||document.documentElement.scrollLeft||document.body.scrollLeft||0;this.deltaY=window.pageYOffset||document.documentElement.scrollTop||document.body.scrollTop||0},within:function(B,A,C){if(this.includeScrollOffsets){return this.withinIncludingScrolloffsets(B,A,C)}this.xcomp=A;this.ycomp=C;this.offset=Element.cumulativeOffset(B);return(C>=this.offset[1]&&C<this.offset[1]+B.offsetHeight&&A>=this.offset[0]&&A<this.offset[0]+B.offsetWidth)},withinIncludingScrolloffsets:function(B,A,D){var C=Element.cumulativeScrollOffset(B);this.xcomp=A+C[0]-this.deltaX;this.ycomp=D+C[1]-this.deltaY;this.offset=Element.cumulativeOffset(B);return(this.ycomp>=this.offset[1]&&this.ycomp<this.offset[1]+B.offsetHeight&&this.xcomp>=this.offset[0]&&this.xcomp<this.offset[0]+B.offsetWidth)},overlap:function(B,A){if(!B){return 0}if(B=="vertical"){return((this.offset[1]+A.offsetHeight)-this.ycomp)/A.offsetHeight}if(B=="horizontal"){return((this.offset[0]+A.offsetWidth)-this.xcomp)/A.offsetWidth}},cumulativeOffset:Element.Methods.cumulativeOffset,positionedOffset:Element.Methods.positionedOffset,absolutize:function(A){Position.prepare();return Element.absolutize(A)},relativize:function(A){Position.prepare();return Element.relativize(A)},realOffset:Element.Methods.cumulativeScrollOffset,offsetParent:Element.Methods.getOffsetParent,page:Element.Methods.viewportOffset,clone:function(B,C,A){A=A||{};return Element.clonePosition(C,B,A)}};if(!document.getElementsByClassName){document.getElementsByClassName=function(B){function A(C){return C.blank()?null:"[contains(concat(' ', @class, ' '), ' "+C+" ')]"}B.getElementsByClassName=Prototype.BrowserFeatures.XPath?function(C,E){E=E.toString().strip();var D=/\s/.test(E)?$w(E).map(A).join(""):A(E);return D?document._getElementsByXPath(".//*"+D,C):[]}:function(E,F){F=F.toString().strip();var G=[],H=(/\s/.test(F)?$w(F):null);if(!H&&!F){return G}var C=$(E).getElementsByTagName("*");F=" "+F+" ";for(var D=0,J,I;J=C[D];D++){if(J.className&&(I=" "+J.className+" ")&&(I.include(F)||(H&&H.all(function(K){return !K.toString().blank()&&I.include(" "+K+" ")})))){G.push(Element.extend(J))}}return G};return function(D,C){return $(C||document.body).getElementsByClassName(D)}}(Element.Methods)}Element.ClassNames=Class.create();Element.ClassNames.prototype={initialize:function(A){this.element=$(A)},_each:function(A){this.element.className.split(/\s+/).select(function(B){return B.length>0})._each(A)},set:function(A){this.element.className=A},add:function(A){if(this.include(A)){return }this.set($A(this).concat(A).join(" "))},remove:function(A){if(!this.include(A)){return }this.set($A(this).without(A).join(" "))},toString:function(){return $A(this).join(" ")}};Object.extend(Element.ClassNames.prototype,Enumerable);Element.addMethods();var Scriptaculous={Version:"1.8.1",require:function(A){document.write('<script type="text/javascript" src="'+A+'"><\/script>')},REQUIRED_PROTOTYPE:"1.6.0",load:function(){function A(B){var C=B.split(".");return parseInt(C[0])*100000+parseInt(C[1])*1000+parseInt(C[2])}if((typeof Prototype=="undefined")||(typeof Element=="undefined")||(typeof Element.Methods=="undefined")||(A(Prototype.Version)<A(Scriptaculous.REQUIRED_PROTOTYPE))){throw ("script.aculo.us requires the Prototype JavaScript framework >= "+Scriptaculous.REQUIRED_PROTOTYPE)}}};Scriptaculous.load();var Builder={NODEMAP:{AREA:"map",CAPTION:"table",COL:"table",COLGROUP:"table",LEGEND:"fieldset",OPTGROUP:"select",OPTION:"select",PARAM:"object",TBODY:"table",TD:"table",TFOOT:"table",TH:"table",THEAD:"table",TR:"table"},node:function(A){A=A.toUpperCase();var F=this.NODEMAP[A]||"div";var B=document.createElement(F);try{B.innerHTML="<"+A+"></"+A+">"}catch(E){}var D=B.firstChild||null;if(D&&(D.tagName.toUpperCase()!=A)){D=D.getElementsByTagName(A)[0]}if(!D){D=document.createElement(A)}if(!D){return }if(arguments[1]){if(this._isStringOrNumber(arguments[1])||(arguments[1] instanceof Array)||arguments[1].tagName){this._children(D,arguments[1])}else{var C=this._attributes(arguments[1]);if(C.length){try{B.innerHTML="<"+A+" "+C+"></"+A+">"}catch(E){}D=B.firstChild||null;if(!D){D=document.createElement(A);for(attr in arguments[1]){D[attr=="class"?"className":attr]=arguments[1][attr]}}if(D.tagName.toUpperCase()!=A){D=B.getElementsByTagName(A)[0]}}}}if(arguments[2]){this._children(D,arguments[2])}return D},_text:function(A){return document.createTextNode(A)},ATTR_MAP:{className:"class",htmlFor:"for"},_attributes:function(A){var B=[];for(attribute in A){B.push((attribute in this.ATTR_MAP?this.ATTR_MAP[attribute]:attribute)+'="'+A[attribute].toString().escapeHTML().gsub(/"/,"&quot;")+'"')}return B.join(" ")},_children:function(B,A){if(A.tagName){B.appendChild(A);return }if(typeof A=="object"){A.flatten().each(function(C){if(typeof C=="object"){B.appendChild(C)}else{if(Builder._isStringOrNumber(C)){B.appendChild(Builder._text(C))}}})}else{if(Builder._isStringOrNumber(A)){B.appendChild(Builder._text(A))}}},_isStringOrNumber:function(A){return(typeof A=="string"||typeof A=="number")},build:function(B){var A=this.node("div");$(A).update(B.strip());return A.down()},dump:function(B){if(typeof B!="object"&&typeof B!="function"){B=window}var A=("A ABBR ACRONYM ADDRESS APPLET AREA B BASE BASEFONT BDO BIG BLOCKQUOTE BODY BR BUTTON CAPTION CENTER CITE CODE COL COLGROUP DD DEL DFN DIR DIV DL DT EM FIELDSET FONT FORM FRAME FRAMESET H1 H2 H3 H4 H5 H6 HEAD HR HTML I IFRAME IMG INPUT INS ISINDEX KBD LABEL LEGEND LI LINK MAP MENU META NOFRAMES NOSCRIPT OBJECT OL OPTGROUP OPTION P PARAM PRE Q S SAMP SCRIPT SELECT SMALL SPAN STRIKE STRONG STYLE SUB SUP TABLE TBODY TD TEXTAREA TFOOT TH THEAD TITLE TR TT U UL VAR").split(/\s+/);A.each(function(C){B[C]=function(){return Builder.node.apply(Builder,[C].concat($A(arguments)))}})}};String.prototype.parseColor=function(){var A="#";if(this.slice(0,4)=="rgb("){var C=this.slice(4,this.length-1).split(",");var B=0;do{A+=parseInt(C[B]).toColorPart()}while(++B<3)}else{if(this.slice(0,1)=="#"){if(this.length==4){for(var B=1;B<4;B++){A+=(this.charAt(B)+this.charAt(B)).toLowerCase()}}if(this.length==7){A=this.toLowerCase()}}}return(A.length==7?A:(arguments[0]||this))};Element.collectTextNodes=function(A){return $A($(A).childNodes).collect(function(B){return(B.nodeType==3?B.nodeValue:(B.hasChildNodes()?Element.collectTextNodes(B):""))}).flatten().join("")};Element.collectTextNodesIgnoreClass=function(A,B){return $A($(A).childNodes).collect(function(C){return(C.nodeType==3?C.nodeValue:((C.hasChildNodes()&&!Element.hasClassName(C,B))?Element.collectTextNodesIgnoreClass(C,B):""))}).flatten().join("")};Element.setContentZoom=function(A,B){A=$(A);A.setStyle({fontSize:(B/100)+"em"});if(Prototype.Browser.WebKit){window.scrollBy(0,0)}return A};Element.getInlineOpacity=function(A){return $(A).style.opacity||""};Element.forceRerendering=function(A){try{A=$(A);var C=document.createTextNode(" ");A.appendChild(C);A.removeChild(C)}catch(B){}};var Effect={_elementDoesNotExistError:{name:"ElementDoesNotExistError",message:"The specified DOM element does not exist, but is required for this effect to operate"},Transitions:{linear:Prototype.K,sinoidal:function(A){return(-Math.cos(A*Math.PI)/2)+0.5},reverse:function(A){return 1-A},flicker:function(A){var A=((-Math.cos(A*Math.PI)/4)+0.75)+Math.random()/4;return A>1?1:A},wobble:function(A){return(-Math.cos(A*Math.PI*(9*A))/2)+0.5},pulse:function(B,A){A=A||5;return(((B%(1/A))*A).round()==0?((B*A*2)-(B*A*2).floor()):1-((B*A*2)-(B*A*2).floor()))},spring:function(A){return 1-(Math.cos(A*4.5*Math.PI)*Math.exp(-A*6))},none:function(A){return 0},full:function(A){return 1}},DefaultOptions:{duration:1,fps:100,sync:false,from:0,to:1,delay:0,queue:"parallel"},tagifyText:function(A){var B="position:relative";if(Prototype.Browser.IE){B+=";zoom:1"}A=$(A);$A(A.childNodes).each(function(C){if(C.nodeType==3){C.nodeValue.toArray().each(function(D){A.insertBefore(new Element("span",{style:B}).update(D==" "?String.fromCharCode(160):D),C)});Element.remove(C)}})},multiple:function(B,C){var E;if(((typeof B=="object")||Object.isFunction(B))&&(B.length)){E=B}else{E=$(B).childNodes}var A=Object.extend({speed:0.1,delay:0},arguments[2]||{});var D=A.delay;$A(E).each(function(G,F){new C(G,Object.extend(A,{delay:F*A.speed+D}))})},PAIRS:{slide:["SlideDown","SlideUp"],blind:["BlindDown","BlindUp"],appear:["Appear","Fade"]},toggle:function(B,C){B=$(B);C=(C||"appear").toLowerCase();var A=Object.extend({queue:{position:"end",scope:(B.id||"global"),limit:1}},arguments[2]||{});Effect[B.visible()?Effect.PAIRS[C][1]:Effect.PAIRS[C][0]](B,A)}};Effect.DefaultOptions.transition=Effect.Transitions.sinoidal;Effect.ScopedQueue=Class.create(Enumerable,{initialize:function(){this.effects=[];this.interval=null},_each:function(A){this.effects._each(A)},add:function(B){var C=new Date().getTime();var A=Object.isString(B.options.queue)?B.options.queue:B.options.queue.position;switch(A){case"front":this.effects.findAll(function(D){return D.state=="idle"}).each(function(D){D.startOn+=B.finishOn;D.finishOn+=B.finishOn});break;case"with-last":C=this.effects.pluck("startOn").max()||C;break;case"end":C=this.effects.pluck("finishOn").max()||C;break}B.startOn+=C;B.finishOn+=C;if(!B.options.queue.limit||(this.effects.length<B.options.queue.limit)){this.effects.push(B)}if(!this.interval){this.interval=setInterval(this.loop.bind(this),15)}},remove:function(A){this.effects=this.effects.reject(function(B){return B==A});if(this.effects.length==0){clearInterval(this.interval);this.interval=null}},loop:function(){var C=new Date().getTime();for(var B=0,A=this.effects.length;B<A;B++){this.effects[B]&&this.effects[B].loop(C)}}});Effect.Queues={instances:$H(),get:function(A){if(!Object.isString(A)){return A}return this.instances.get(A)||this.instances.set(A,new Effect.ScopedQueue())}};Effect.Queue=Effect.Queues.get("global");Effect.Base=Class.create({position:null,start:function(options){function codeForEvent(options,eventName){return((options[eventName+"Internal"]?"this.options."+eventName+"Internal(this);":"")+(options[eventName]?"this.options."+eventName+"(this);":""))}if(options&&options.transition===false){options.transition=Effect.Transitions.linear}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.fromToDelta=this.options.to-this.options.from;this.totalTime=this.finishOn-this.startOn;this.totalFrames=this.options.fps*this.options.duration;eval('this.render = function(pos){ if (this.state=="idle"){this.state="running";'+codeForEvent(this.options,"beforeSetup")+(this.setup?"this.setup();":"")+codeForEvent(this.options,"afterSetup")+'};if (this.state=="running"){pos=this.options.transition(pos)*'+this.fromToDelta+"+"+this.options.from+";this.position=pos;"+codeForEvent(this.options,"beforeUpdate")+(this.update?"this.update(pos);":"")+codeForEvent(this.options,"afterUpdate")+"}}");this.event("beforeStart");if(!this.options.sync){Effect.Queues.get(Object.isString(this.options.queue)?"global":this.options.queue.scope).add(this)}},loop:function(C){if(C>=this.startOn){if(C>=this.finishOn){this.render(1);this.cancel();this.event("beforeFinish");if(this.finish){this.finish()}this.event("afterFinish");return }var B=(C-this.startOn)/this.totalTime,A=(B*this.totalFrames).round();if(A>this.currentFrame){this.render(B);this.currentFrame=A}}},cancel:function(){if(!this.options.sync){Effect.Queues.get(Object.isString(this.options.queue)?"global":this.options.queue.scope).remove(this)}this.state="finished"},event:function(A){if(this.options[A+"Internal"]){this.options[A+"Internal"](this)}if(this.options[A]){this.options[A](this)}},inspect:function(){var A=$H();for(property in this){if(!Object.isFunction(this[property])){A.set(property,this[property])}}return"#<Effect:"+A.inspect()+",options:"+$H(this.options).inspect()+">"}});Effect.Parallel=Class.create(Effect.Base,{initialize:function(A){this.effects=A||[];this.start(arguments[1])},update:function(A){this.effects.invoke("render",A)},finish:function(A){this.effects.each(function(B){B.render(1);B.cancel();B.event("beforeFinish");if(B.finish){B.finish(A)}B.event("afterFinish")})}});Effect.Tween=Class.create(Effect.Base,{initialize:function(C,F,E){C=Object.isString(C)?$(C):C;var B=$A(arguments),D=B.last(),A=B.length==5?B[3]:null;this.method=Object.isFunction(D)?D.bind(C):Object.isFunction(C[D])?C[D].bind(C):function(G){C[D]=G};this.start(Object.extend({from:F,to:E},A||{}))},update:function(A){this.method(A)}});Effect.Event=Class.create(Effect.Base,{initialize:function(){this.start(Object.extend({duration:0},arguments[0]||{}))},update:Prototype.emptyFunction});Effect.Opacity=Class.create(Effect.Base,{initialize:function(B){this.element=$(B);if(!this.element){throw (Effect._elementDoesNotExistError)}if(Prototype.Browser.IE&&(!this.element.currentStyle.hasLayout)){this.element.setStyle({zoom:1})}var A=Object.extend({from:this.element.getOpacity()||0,to:1},arguments[1]||{});this.start(A)},update:function(A){this.element.setOpacity(A)}});Effect.Move=Class.create(Effect.Base,{initialize:function(B){this.element=$(B);if(!this.element){throw (Effect._elementDoesNotExistError)}var A=Object.extend({x:0,y:0,mode:"relative"},arguments[1]||{});this.start(A)},setup:function(){this.element.makePositioned();this.originalLeft=parseFloat(this.element.getStyle("left")||"0");this.originalTop=parseFloat(this.element.getStyle("top")||"0");if(this.options.mode=="absolute"){this.options.x=this.options.x-this.originalLeft;this.options.y=this.options.y-this.originalTop}},update:function(A){this.element.setStyle({left:(this.options.x*A+this.originalLeft).round()+"px",top:(this.options.y*A+this.originalTop).round()+"px"})}});Effect.MoveBy=function(B,A,C){return new Effect.Move(B,Object.extend({x:C,y:A},arguments[3]||{}))};Effect.Scale=Class.create(Effect.Base,{initialize:function(B,C){this.element=$(B);if(!this.element){throw (Effect._elementDoesNotExistError)}var A=Object.extend({scaleX:true,scaleY:true,scaleContent:true,scaleFromCenter:false,scaleMode:"box",scaleFrom:100,scaleTo:C},arguments[2]||{});this.start(A)},setup:function(){this.restoreAfterFinish=this.options.restoreAfterFinish||false;this.elementPositioning=this.element.getStyle("position");this.originalStyle={};["top","left","width","height","fontSize"].each(function(B){this.originalStyle[B]=this.element.style[B]}.bind(this));this.originalTop=this.element.offsetTop;this.originalLeft=this.element.offsetLeft;var A=this.element.getStyle("font-size")||"100%";["em","px","%","pt"].each(function(B){if(A.indexOf(B)>0){this.fontSize=parseFloat(A);this.fontSizeType=B}}.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(A){var B=(this.options.scaleFrom/100)+(this.factor*A);if(this.options.scaleContent&&this.fontSize){this.element.setStyle({fontSize:this.fontSize*B+this.fontSizeType})}this.setDimensions(this.dims[0]*B,this.dims[1]*B)},finish:function(A){if(this.restoreAfterFinish){this.element.setStyle(this.originalStyle)}},setDimensions:function(A,D){var E={};if(this.options.scaleX){E.width=D.round()+"px"}if(this.options.scaleY){E.height=A.round()+"px"}if(this.options.scaleFromCenter){var C=(A-this.dims[0])/2;var B=(D-this.dims[1])/2;if(this.elementPositioning=="absolute"){if(this.options.scaleY){E.top=this.originalTop-C+"px"}if(this.options.scaleX){E.left=this.originalLeft-B+"px"}}else{if(this.options.scaleY){E.top=-C+"px"}if(this.options.scaleX){E.left=-B+"px"}}}this.element.setStyle(E)}});Effect.Highlight=Class.create(Effect.Base,{initialize:function(B){this.element=$(B);if(!this.element){throw (Effect._elementDoesNotExistError)}var A=Object.extend({startcolor:"#ffff99"},arguments[1]||{});this.start(A)},setup:function(){if(this.element.getStyle("display")=="none"){this.cancel();return }this.oldStyle={};if(!this.options.keepBackgroundImage){this.oldStyle.backgroundImage=this.element.getStyle("background-image");this.element.setStyle({backgroundImage:"none"})}if(!this.options.endcolor){this.options.endcolor=this.element.getStyle("background-color").parseColor("#ffffff")}if(!this.options.restorecolor){this.options.restorecolor=this.element.getStyle("background-color")}this._base=$R(0,2).map(function(A){return parseInt(this.options.startcolor.slice(A*2+1,A*2+3),16)}.bind(this));this._delta=$R(0,2).map(function(A){return parseInt(this.options.endcolor.slice(A*2+1,A*2+3),16)-this._base[A]}.bind(this))},update:function(A){this.element.setStyle({backgroundColor:$R(0,2).inject("#",function(B,C,D){return B+((this._base[D]+(this._delta[D]*A)).round().toColorPart())}.bind(this))})},finish:function(){this.element.setStyle(Object.extend(this.oldStyle,{backgroundColor:this.options.restorecolor}))}});Effect.ScrollTo=function(D){var C=arguments[1]||{},B=document.viewport.getScrollOffsets(),E=$(D).cumulativeOffset(),A=(window.height||document.body.scrollHeight)-document.viewport.getHeight();if(C.offset){E[1]+=C.offset}return new Effect.Tween(null,B.top,E[1]>A?A:E[1],C,function(F){scrollTo(B.left,F.round())})};Effect.Fade=function(C){C=$(C);var A=C.getInlineOpacity();var B=Object.extend({from:C.getOpacity()||1,to:0,afterFinishInternal:function(D){if(D.options.to!=0){return }D.element.hide().setStyle({opacity:A})}},arguments[1]||{});return new Effect.Opacity(C,B)};Effect.Appear=function(B){B=$(B);var A=Object.extend({from:(B.getStyle("display")=="none"?0:B.getOpacity()||0),to:1,afterFinishInternal:function(C){C.element.forceRerendering()},beforeSetup:function(C){C.element.setOpacity(C.options.from).show()}},arguments[1]||{});return new Effect.Opacity(B,A)};Effect.Puff=function(B){B=$(B);var A={opacity:B.getInlineOpacity(),position:B.getStyle("position"),top:B.style.top,left:B.style.left,width:B.style.width,height:B.style.height};return new Effect.Parallel([new Effect.Scale(B,200,{sync:true,scaleFromCenter:true,scaleContent:true,restoreAfterFinish:true}),new Effect.Opacity(B,{sync:true,to:0})],Object.extend({duration:1,beforeSetupInternal:function(C){Position.absolutize(C.effects[0].element)},afterFinishInternal:function(C){C.effects[0].element.hide().setStyle(A)}},arguments[1]||{}))};Effect.BlindUp=function(A){A=$(A);A.makeClipping();return new Effect.Scale(A,0,Object.extend({scaleContent:false,scaleX:false,restoreAfterFinish:true,afterFinishInternal:function(B){B.element.hide().undoClipping()}},arguments[1]||{}))};Effect.BlindDown=function(B){B=$(B);var A=B.getDimensions();return new Effect.Scale(B,100,Object.extend({scaleContent:false,scaleX:false,scaleFrom:0,scaleMode:{originalHeight:A.height,originalWidth:A.width},restoreAfterFinish:true,afterSetup:function(C){C.element.makeClipping().setStyle({height:"0px"}).show()},afterFinishInternal:function(C){C.element.undoClipping()}},arguments[1]||{}))};Effect.SwitchOff=function(B){B=$(B);var A=B.getInlineOpacity();return new Effect.Appear(B,Object.extend({duration:0.4,from:0,transition:Effect.Transitions.flicker,afterFinishInternal:function(C){new Effect.Scale(C.element,1,{duration:0.3,scaleFromCenter:true,scaleX:false,scaleContent:false,restoreAfterFinish:true,beforeSetup:function(D){D.element.makePositioned().makeClipping()},afterFinishInternal:function(D){D.element.hide().undoClipping().undoPositioned().setStyle({opacity:A})}})}},arguments[1]||{}))};Effect.DropOut=function(B){B=$(B);var A={top:B.getStyle("top"),left:B.getStyle("left"),opacity:B.getInlineOpacity()};return new Effect.Parallel([new Effect.Move(B,{x:0,y:100,sync:true}),new Effect.Opacity(B,{sync:true,to:0})],Object.extend({duration:0.5,beforeSetup:function(C){C.effects[0].element.makePositioned()},afterFinishInternal:function(C){C.effects[0].element.hide().undoPositioned().setStyle(A)}},arguments[1]||{}))};Effect.Shake=function(D){D=$(D);var B=Object.extend({distance:20,duration:0.5},arguments[1]||{});var E=parseFloat(B.distance);var C=parseFloat(B.duration)/10;var A={top:D.getStyle("top"),left:D.getStyle("left")};return new Effect.Move(D,{x:E,y:0,duration:C,afterFinishInternal:function(F){new Effect.Move(F.element,{x:-E*2,y:0,duration:C*2,afterFinishInternal:function(G){new Effect.Move(G.element,{x:E*2,y:0,duration:C*2,afterFinishInternal:function(H){new Effect.Move(H.element,{x:-E*2,y:0,duration:C*2,afterFinishInternal:function(I){new Effect.Move(I.element,{x:E*2,y:0,duration:C*2,afterFinishInternal:function(J){new Effect.Move(J.element,{x:-E,y:0,duration:C,afterFinishInternal:function(K){K.element.undoPositioned().setStyle(A)}})}})}})}})}})}})};Effect.SlideDown=function(C){C=$(C).cleanWhitespace();var A=C.down().getStyle("bottom");var B=C.getDimensions();return new Effect.Scale(C,100,Object.extend({scaleContent:false,scaleX:false,scaleFrom:window.opera?0:1,scaleMode:{originalHeight:B.height,originalWidth:B.width},restoreAfterFinish:true,afterSetup:function(D){D.element.makePositioned();D.element.down().makePositioned();if(window.opera){D.element.setStyle({top:""})}D.element.makeClipping().setStyle({height:"0px"}).show()},afterUpdateInternal:function(D){D.element.down().setStyle({bottom:(D.dims[0]-D.element.clientHeight)+"px"})},afterFinishInternal:function(D){D.element.undoClipping().undoPositioned();D.element.down().undoPositioned().setStyle({bottom:A})}},arguments[1]||{}))};Effect.SlideUp=function(C){C=$(C).cleanWhitespace();var A=C.down().getStyle("bottom");var B=C.getDimensions();return new Effect.Scale(C,window.opera?0:1,Object.extend({scaleContent:false,scaleX:false,scaleMode:"box",scaleFrom:100,scaleMode:{originalHeight:B.height,originalWidth:B.width},restoreAfterFinish:true,afterSetup:function(D){D.element.makePositioned();D.element.down().makePositioned();if(window.opera){D.element.setStyle({top:""})}D.element.makeClipping().show()},afterUpdateInternal:function(D){D.element.down().setStyle({bottom:(D.dims[0]-D.element.clientHeight)+"px"})},afterFinishInternal:function(D){D.element.hide().undoClipping().undoPositioned();D.element.down().undoPositioned().setStyle({bottom:A})}},arguments[1]||{}))};Effect.Squish=function(A){return new Effect.Scale(A,window.opera?1:0,{restoreAfterFinish:true,beforeSetup:function(B){B.element.makeClipping()},afterFinishInternal:function(B){B.element.hide().undoClipping()}})};Effect.Grow=function(C){C=$(C);var B=Object.extend({direction:"center",moveTransition:Effect.Transitions.sinoidal,scaleTransition:Effect.Transitions.sinoidal,opacityTransition:Effect.Transitions.full},arguments[1]||{});var A={top:C.style.top,left:C.style.left,height:C.style.height,width:C.style.width,opacity:C.getInlineOpacity()};var G=C.getDimensions();var H,F;var E,D;switch(B.direction){case"top-left":H=F=E=D=0;break;case"top-right":H=G.width;F=D=0;E=-G.width;break;case"bottom-left":H=E=0;F=G.height;D=-G.height;break;case"bottom-right":H=G.width;F=G.height;E=-G.width;D=-G.height;break;case"center":H=G.width/2;F=G.height/2;E=-G.width/2;D=-G.height/2;break}return new Effect.Move(C,{x:H,y:F,duration:0.01,beforeSetup:function(I){I.element.hide().makeClipping().makePositioned()},afterFinishInternal:function(I){new Effect.Parallel([new Effect.Opacity(I.element,{sync:true,to:1,from:0,transition:B.opacityTransition}),new Effect.Move(I.element,{x:E,y:D,sync:true,transition:B.moveTransition}),new Effect.Scale(I.element,100,{scaleMode:{originalHeight:G.height,originalWidth:G.width},sync:true,scaleFrom:window.opera?1:0,transition:B.scaleTransition,restoreAfterFinish:true})],Object.extend({beforeSetup:function(J){J.effects[0].element.setStyle({height:"0px"}).show()},afterFinishInternal:function(J){J.effects[0].element.undoClipping().undoPositioned().setStyle(A)}},B))}})};Effect.Shrink=function(C){C=$(C);var B=Object.extend({direction:"center",moveTransition:Effect.Transitions.sinoidal,scaleTransition:Effect.Transitions.sinoidal,opacityTransition:Effect.Transitions.none},arguments[1]||{});var A={top:C.style.top,left:C.style.left,height:C.style.height,width:C.style.width,opacity:C.getInlineOpacity()};var F=C.getDimensions();var E,D;switch(B.direction){case"top-left":E=D=0;break;case"top-right":E=F.width;D=0;break;case"bottom-left":E=0;D=F.height;break;case"bottom-right":E=F.width;D=F.height;break;case"center":E=F.width/2;D=F.height/2;break}return new Effect.Parallel([new Effect.Opacity(C,{sync:true,to:0,from:1,transition:B.opacityTransition}),new Effect.Scale(C,window.opera?1:0,{sync:true,transition:B.scaleTransition,restoreAfterFinish:true}),new Effect.Move(C,{x:E,y:D,sync:true,transition:B.moveTransition})],Object.extend({beforeStartInternal:function(G){G.effects[0].element.makePositioned().makeClipping()},afterFinishInternal:function(G){G.effects[0].element.hide().undoClipping().undoPositioned().setStyle(A)}},B))};Effect.Pulsate=function(C){C=$(C);var B=arguments[1]||{};var A=C.getInlineOpacity();var E=B.transition||Effect.Transitions.sinoidal;var D=function(F){return E(1-Effect.Transitions.pulse(F,B.pulses))};D.bind(E);return new Effect.Opacity(C,Object.extend(Object.extend({duration:2,from:0,afterFinishInternal:function(F){F.element.setStyle({opacity:A})}},B),{transition:D}))};Effect.Fold=function(B){B=$(B);var A={top:B.style.top,left:B.style.left,width:B.style.width,height:B.style.height};B.makeClipping();return new Effect.Scale(B,5,Object.extend({scaleContent:false,scaleX:false,afterFinishInternal:function(C){new Effect.Scale(B,1,{scaleContent:false,scaleY:false,afterFinishInternal:function(D){D.element.hide().undoClipping().setStyle(A)}})}},arguments[1]||{}))};Effect.Morph=Class.create(Effect.Base,{initialize:function(C){this.element=$(C);if(!this.element){throw (Effect._elementDoesNotExistError)}var A=Object.extend({style:{}},arguments[1]||{});if(!Object.isString(A.style)){this.style=$H(A.style)}else{if(A.style.include(":")){this.style=A.style.parseStyle()}else{this.element.addClassName(A.style);this.style=$H(this.element.getStyles());this.element.removeClassName(A.style);var B=this.element.getStyles();this.style=this.style.reject(function(D){return D.value==B[D.key]});A.afterFinishInternal=function(D){D.element.addClassName(D.options.style);D.transforms.each(function(E){D.element.style[E.style]=""})}}}this.start(A)},setup:function(){function A(B){if(!B||["rgba(0, 0, 0, 0)","transparent"].include(B)){B="#ffffff"}B=B.parseColor();return $R(0,2).map(function(C){return parseInt(B.slice(C*2+1,C*2+3),16)})}this.transforms=this.style.map(function(G){var F=G[0],E=G[1],D=null;if(E.parseColor("#zzzzzz")!="#zzzzzz"){E=E.parseColor();D="color"}else{if(F=="opacity"){E=parseFloat(E);if(Prototype.Browser.IE&&(!this.element.currentStyle.hasLayout)){this.element.setStyle({zoom:1})}}else{if(Element.CSS_LENGTH.test(E)){var C=E.match(/^([\+\-]?[0-9\.]+)(.*)$/);E=parseFloat(C[1]);D=(C.length==3)?C[2]:null}}}var B=this.element.getStyle(F);return{style:F.camelize(),originalValue:D=="color"?A(B):parseFloat(B||0),targetValue:D=="color"?A(E):E,unit:D}}.bind(this)).reject(function(B){return((B.originalValue==B.targetValue)||(B.unit!="color"&&(isNaN(B.originalValue)||isNaN(B.targetValue))))})},update:function(A){var D={},B,C=this.transforms.length;while(C--){D[(B=this.transforms[C]).style]=B.unit=="color"?"#"+(Math.round(B.originalValue[0]+(B.targetValue[0]-B.originalValue[0])*A)).toColorPart()+(Math.round(B.originalValue[1]+(B.targetValue[1]-B.originalValue[1])*A)).toColorPart()+(Math.round(B.originalValue[2]+(B.targetValue[2]-B.originalValue[2])*A)).toColorPart():(B.originalValue+(B.targetValue-B.originalValue)*A).toFixed(3)+(B.unit===null?"":B.unit)}this.element.setStyle(D,true)}});Effect.Transform=Class.create({initialize:function(A){this.tracks=[];this.options=arguments[1]||{};this.addTracks(A)},addTracks:function(A){A.each(function(B){B=$H(B);var C=B.values().first();this.tracks.push($H({ids:B.keys().first(),effect:Effect.Morph,options:{style:C}}))}.bind(this));return this},play:function(){return new Effect.Parallel(this.tracks.map(function(A){var D=A.get("ids"),C=A.get("effect"),B=A.get("options");var E=[$(D)||$$(D)].flatten();return E.map(function(F){return new C(F,Object.extend({sync:true},B))})}).flatten(),this.options)}});Element.CSS_PROPERTIES=$w("backgroundColor backgroundPosition borderBottomColor borderBottomStyle borderBottomWidth borderLeftColor borderLeftStyle borderLeftWidth borderRightColor borderRightStyle borderRightWidth borderSpacing borderTopColor borderTopStyle borderTopWidth bottom clip color fontSize fontWeight height left letterSpacing lineHeight marginBottom marginLeft marginRight marginTop markerOffset maxHeight maxWidth minHeight minWidth opacity outlineColor outlineOffset outlineWidth paddingBottom paddingLeft paddingRight paddingTop right textIndent top width wordSpacing zIndex");Element.CSS_LENGTH=/^(([\+\-]?[0-9\.]+)(em|ex|px|in|cm|mm|pt|pc|\%))|0$/;String.__parseStyleElement=document.createElement("div");String.prototype.parseStyle=function(){var B,A=$H();if(Prototype.Browser.WebKit){B=new Element("div",{style:this}).style}else{String.__parseStyleElement.innerHTML='<div style="'+this+'"></div>';B=String.__parseStyleElement.childNodes[0].style}Element.CSS_PROPERTIES.each(function(C){if(B[C]){A.set(C,B[C])}});if(Prototype.Browser.IE&&this.include("opacity")){A.set("opacity",this.match(/opacity:\s*((?:0|1)?(?:\.\d*)?)/)[1])}return A};if(document.defaultView&&document.defaultView.getComputedStyle){Element.getStyles=function(B){var A=document.defaultView.getComputedStyle($(B),null);return Element.CSS_PROPERTIES.inject({},function(C,D){C[D]=A[D];return C})}}else{Element.getStyles=function(B){B=$(B);var A=B.currentStyle,C;C=Element.CSS_PROPERTIES.inject({},function(D,E){D[E]=A[E];return D});if(!C.opacity){C.opacity=B.getOpacity()}return C}}Effect.Methods={morph:function(A,B){A=$(A);new Effect.Morph(A,Object.extend({style:B},arguments[2]||{}));return A},visualEffect:function(C,E,B){C=$(C);var D=E.dasherize().camelize(),A=D.charAt(0).toUpperCase()+D.substring(1);new Effect[A](C,B);return C},highlight:function(B,A){B=$(B);new Effect.Highlight(B,A);return B}};$w("fade appear grow shrink fold blindUp blindDown slideUp slideDown pulsate shake puff squish switchOff dropOut").each(function(A){Effect.Methods[A]=function(C,B){C=$(C);Effect[A.charAt(0).toUpperCase()+A.substring(1)](C,B);return C}});$w("getInlineOpacity forceRerendering setContentZoom collectTextNodes collectTextNodesIgnoreClass getStyles").each(function(A){Effect.Methods[A]=Element[A]});Element.addMethods(Effect.Methods);if(Object.isUndefined(Effect)){throw ("dragdrop.js requires including script.aculo.us' effects.js library")}var Droppables={drops:[],remove:function(A){this.drops=this.drops.reject(function(B){return B.element==$(A)})},add:function(B){B=$(B);var A=Object.extend({greedy:true,hoverclass:null,tree:false},arguments[1]||{});if(A.containment){A._containers=[];var C=A.containment;if(Object.isArray(C)){C.each(function(D){A._containers.push($(D))})}else{A._containers.push($(C))}}if(A.accept){A.accept=[A.accept].flatten()}Element.makePositioned(B);A.element=B;this.drops.push(A)},findDeepestChild:function(A){deepest=A[0];for(i=1;i<A.length;++i){if(Element.isParent(A[i].element,deepest.element)){deepest=A[i]}}return deepest},isContained:function(B,A){var C;if(A.tree){C=B.treeNode}else{C=B.parentNode}return A._containers.detect(function(D){return C==D})},isAffected:function(A,C,B){return((B.element!=C)&&((!B._containers)||this.isContained(C,B))&&((!B.accept)||(Element.classNames(C).detect(function(D){return B.accept.include(D)})))&&Position.within(B.element,A[0],A[1]))},deactivate:function(A){if(A.hoverclass){Element.removeClassName(A.element,A.hoverclass)}this.last_active=null},activate:function(A){if(A.hoverclass){Element.addClassName(A.element,A.hoverclass)}this.last_active=A},show:function(A,C){if(!this.drops.length){return }var B,D=[];this.drops.each(function(E){if(Droppables.isAffected(A,C,E)){D.push(E)}});if(D.length>0){B=Droppables.findDeepestChild(D)}if(this.last_active&&this.last_active!=B){this.deactivate(this.last_active)}if(B){Position.within(B.element,A[0],A[1]);if(B.onHover){B.onHover(C,B.element,Position.overlap(B.overlap,B.element))}if(B!=this.last_active){Droppables.activate(B)}}},fire:function(B,A){if(!this.last_active){return }Position.prepare();if(this.isAffected([Event.pointerX(B),Event.pointerY(B)],A,this.last_active)){if(this.last_active.onDrop){this.last_active.onDrop(A,this.last_active.element,B);return true}}},reset:function(){if(this.last_active){this.deactivate(this.last_active)}}};var Draggables={drags:[],observers:[],register:function(A){if(this.drags.length==0){this.eventMouseUp=this.endDrag.bindAsEventListener(this);this.eventMouseMove=this.updateDrag.bindAsEventListener(this);this.eventKeypress=this.keyPress.bindAsEventListener(this);Event.observe(document,"mouseup",this.eventMouseUp);Event.observe(document,"mousemove",this.eventMouseMove);Event.observe(document,"keypress",this.eventKeypress)}this.drags.push(A)},unregister:function(A){this.drags=this.drags.reject(function(B){return B==A});if(this.drags.length==0){Event.stopObserving(document,"mouseup",this.eventMouseUp);Event.stopObserving(document,"mousemove",this.eventMouseMove);Event.stopObserving(document,"keypress",this.eventKeypress)}},activate:function(A){if(A.options.delay){this._timeout=setTimeout(function(){Draggables._timeout=null;window.focus();Draggables.activeDraggable=A}.bind(this),A.options.delay)}else{window.focus();this.activeDraggable=A}},deactivate:function(){this.activeDraggable=null},updateDrag:function(A){if(!this.activeDraggable){return }var B=[Event.pointerX(A),Event.pointerY(A)];if(this._lastPointer&&(this._lastPointer.inspect()==B.inspect())){return }this._lastPointer=B;this.activeDraggable.updateDrag(A,B)},endDrag:function(A){if(this._timeout){clearTimeout(this._timeout);this._timeout=null}if(!this.activeDraggable){return }this._lastPointer=null;this.activeDraggable.endDrag(A);this.activeDraggable=null},keyPress:function(A){if(this.activeDraggable){this.activeDraggable.keyPress(A)}},addObserver:function(A){this.observers.push(A);this._cacheObserverCallbacks()},removeObserver:function(A){this.observers=this.observers.reject(function(B){return B.element==A});this._cacheObserverCallbacks()},notify:function(B,A,C){if(this[B+"Count"]>0){this.observers.each(function(D){if(D[B]){D[B](B,A,C)}})}if(A.options[B]){A.options[B](A,C)}},_cacheObserverCallbacks:function(){["onStart","onEnd","onDrag"].each(function(A){Draggables[A+"Count"]=Draggables.observers.select(function(B){return B[A]}).length})}};var Draggable=Class.create({initialize:function(B){var C={handle:false,reverteffect:function(F,E,D){var G=Math.sqrt(Math.abs(E^2)+Math.abs(D^2))*0.02;new Effect.Move(F,{x:-D,y:-E,duration:G,queue:{scope:"_draggable",position:"end"}})},endeffect:function(E){var D=Object.isNumber(E._opacity)?E._opacity:1;new Effect.Opacity(E,{duration:0.2,from:0.7,to:D,queue:{scope:"_draggable",position:"end"},afterFinish:function(){Draggable._dragging[E]=false}})},zindex:1000,revert:false,quiet:false,scroll:false,scrollSensitivity:20,scrollSpeed:15,snap:false,delay:0};if(!arguments[1]||Object.isUndefined(arguments[1].endeffect)){Object.extend(C,{starteffect:function(D){D._opacity=Element.getOpacity(D);Draggable._dragging[D]=true;new Effect.Opacity(D,{duration:0.2,from:D._opacity,to:0.7})}})}var A=Object.extend(C,arguments[1]||{});this.element=$(B);if(A.handle&&Object.isString(A.handle)){this.handle=this.element.down("."+A.handle,0)}if(!this.handle){this.handle=$(A.handle)}if(!this.handle){this.handle=this.element}if(A.scroll&&!A.scroll.scrollTo&&!A.scroll.outerHTML){A.scroll=$(A.scroll);this._isScrollChild=Element.childOf(this.element,A.scroll)}Element.makePositioned(this.element);this.options=A;this.dragging=false;this.eventMouseDown=this.initDrag.bindAsEventListener(this);Event.observe(this.handle,"mousedown",this.eventMouseDown);Draggables.register(this)},destroy:function(){Event.stopObserving(this.handle,"mousedown",this.eventMouseDown);Draggables.unregister(this)},currentDelta:function(){return([parseInt(Element.getStyle(this.element,"left")||"0"),parseInt(Element.getStyle(this.element,"top")||"0")])},initDrag:function(A){if(!Object.isUndefined(Draggable._dragging[this.element])&&Draggable._dragging[this.element]){return }if(Event.isLeftClick(A)){var C=Event.element(A);if((tag_name=C.tagName.toUpperCase())&&(tag_name=="INPUT"||tag_name=="SELECT"||tag_name=="OPTION"||tag_name=="BUTTON"||tag_name=="TEXTAREA")){return }var B=[Event.pointerX(A),Event.pointerY(A)];var D=Position.cumulativeOffset(this.element);this.offset=[0,1].map(function(E){return(B[E]-D[E])});Draggables.activate(this);Event.stop(A)}},startDrag:function(B){this.dragging=true;if(!this.delta){this.delta=this.currentDelta()}if(this.options.zindex){this.originalZ=parseInt(Element.getStyle(this.element,"z-index")||0);this.element.style.zIndex=this.options.zindex}if(this.options.ghosting){this._clone=this.element.cloneNode(true);this.element._originallyAbsolute=(this.element.getStyle("position")=="absolute");if(!this.element._originallyAbsolute){Position.absolutize(this.element)}this.element.parentNode.insertBefore(this._clone,this.element)}if(this.options.scroll){if(this.options.scroll==window){var A=this._getWindowScroll(this.options.scroll);this.originalScrollLeft=A.left;this.originalScrollTop=A.top}else{this.originalScrollLeft=this.options.scroll.scrollLeft;this.originalScrollTop=this.options.scroll.scrollTop}}Draggables.notify("onStart",this,B);if(this.options.starteffect){this.options.starteffect(this.element)}},updateDrag:function(event,pointer){if(!this.dragging){this.startDrag(event)}if(!this.options.quiet){Position.prepare();Droppables.show(pointer,this.element)}Draggables.notify("onDrag",this,event);this.draw(pointer);if(this.options.change){this.options.change(this)}if(this.options.scroll){this.stopScrolling();var p;if(this.options.scroll==window){with(this._getWindowScroll(this.options.scroll)){p=[left,top,left+width,top+height]}}else{p=Position.page(this.options.scroll);p[0]+=this.options.scroll.scrollLeft+Position.deltaX;p[1]+=this.options.scroll.scrollTop+Position.deltaY;p.push(p[0]+this.options.scroll.offsetWidth);p.push(p[1]+this.options.scroll.offsetHeight)}var speed=[0,0];if(pointer[0]<(p[0]+this.options.scrollSensitivity)){speed[0]=pointer[0]-(p[0]+this.options.scrollSensitivity)}if(pointer[1]<(p[1]+this.options.scrollSensitivity)){speed[1]=pointer[1]-(p[1]+this.options.scrollSensitivity)}if(pointer[0]>(p[2]-this.options.scrollSensitivity)){speed[0]=pointer[0]-(p[2]-this.options.scrollSensitivity)}if(pointer[1]>(p[3]-this.options.scrollSensitivity)){speed[1]=pointer[1]-(p[3]-this.options.scrollSensitivity)}this.startScrolling(speed)}if(Prototype.Browser.WebKit){window.scrollBy(0,0)}Event.stop(event)},finishDrag:function(B,E){this.dragging=false;if(this.options.quiet){Position.prepare();var D=[Event.pointerX(B),Event.pointerY(B)];Droppables.show(D,this.element)}if(this.options.ghosting){if(!this.element._originallyAbsolute){Position.relativize(this.element)}delete this.element._originallyAbsolute;Element.remove(this._clone);this._clone=null}var F=false;if(E){F=Droppables.fire(B,this.element);if(!F){F=false}}if(F&&this.options.onDropped){this.options.onDropped(this.element)}Draggables.notify("onEnd",this,B);var A=this.options.revert;if(A&&Object.isFunction(A)){A=A(this.element)}var C=this.currentDelta();if(A&&this.options.reverteffect){if(F==0||A!="failure"){this.options.reverteffect(this.element,C[1]-this.delta[1],C[0]-this.delta[0])}}else{this.delta=C}if(this.options.zindex){this.element.style.zIndex=this.originalZ}if(this.options.endeffect){this.options.endeffect(this.element)}Draggables.deactivate(this);Droppables.reset()},keyPress:function(A){if(A.keyCode!=Event.KEY_ESC){return }this.finishDrag(A,false);Event.stop(A)},endDrag:function(A){if(!this.dragging){return }this.stopScrolling();this.finishDrag(A,true);Event.stop(A)},draw:function(A){var F=Position.cumulativeOffset(this.element);if(this.options.ghosting){var C=Position.realOffset(this.element);F[0]+=C[0]-Position.deltaX;F[1]+=C[1]-Position.deltaY}var E=this.currentDelta();F[0]-=E[0];F[1]-=E[1];if(this.options.scroll&&(this.options.scroll!=window&&this._isScrollChild)){F[0]-=this.options.scroll.scrollLeft-this.originalScrollLeft;F[1]-=this.options.scroll.scrollTop-this.originalScrollTop}var D=[0,1].map(function(G){return(A[G]-F[G]-this.offset[G])}.bind(this));if(this.options.snap){if(Object.isFunction(this.options.snap)){D=this.options.snap(D[0],D[1],this)}else{if(Object.isArray(this.options.snap)){D=D.map(function(G,H){return(G/this.options.snap[H]).round()*this.options.snap[H]}.bind(this))}else{D=D.map(function(G){return(G/this.options.snap).round()*this.options.snap}.bind(this))}}}var B=this.element.style;if((!this.options.constraint)||(this.options.constraint=="horizontal")){B.left=D[0]+"px"}if((!this.options.constraint)||(this.options.constraint=="vertical")){B.top=D[1]+"px"}if(B.visibility=="hidden"){B.visibility=""}},stopScrolling:function(){if(this.scrollInterval){clearInterval(this.scrollInterval);this.scrollInterval=null;Draggables._lastScrollPointer=null}},startScrolling:function(A){if(!(A[0]||A[1])){return }this.scrollSpeed=[A[0]*this.options.scrollSpeed,A[1]*this.options.scrollSpeed];this.lastScrolled=new Date();this.scrollInterval=setInterval(this.scroll.bind(this),10)},scroll:function(){var current=new Date();var delta=current-this.lastScrolled;this.lastScrolled=current;if(this.options.scroll==window){with(this._getWindowScroll(this.options.scroll)){if(this.scrollSpeed[0]||this.scrollSpeed[1]){var d=delta/1000;this.options.scroll.scrollTo(left+d*this.scrollSpeed[0],top+d*this.scrollSpeed[1])}}}else{this.options.scroll.scrollLeft+=this.scrollSpeed[0]*delta/1000;this.options.scroll.scrollTop+=this.scrollSpeed[1]*delta/1000}Position.prepare();Droppables.show(Draggables._lastPointer,this.element);Draggables.notify("onDrag",this);if(this._isScrollChild){Draggables._lastScrollPointer=Draggables._lastScrollPointer||$A(Draggables._lastPointer);Draggables._lastScrollPointer[0]+=this.scrollSpeed[0]*delta/1000;Draggables._lastScrollPointer[1]+=this.scrollSpeed[1]*delta/1000;if(Draggables._lastScrollPointer[0]<0){Draggables._lastScrollPointer[0]=0}if(Draggables._lastScrollPointer[1]<0){Draggables._lastScrollPointer[1]=0}this.draw(Draggables._lastScrollPointer)}if(this.options.change){this.options.change(this)}},_getWindowScroll:function(w){var T,L,W,H;with(w.document){if(w.document.documentElement&&documentElement.scrollTop){T=documentElement.scrollTop;L=documentElement.scrollLeft}else{if(w.document.body){T=body.scrollTop;L=body.scrollLeft}}if(w.innerWidth){W=w.innerWidth;H=w.innerHeight}else{if(w.document.documentElement&&documentElement.clientWidth){W=documentElement.clientWidth;H=documentElement.clientHeight}else{W=body.offsetWidth;H=body.offsetHeight}}}return{top:T,left:L,width:W,height:H}}});Draggable._dragging={};var SortableObserver=Class.create({initialize:function(B,A){this.element=$(B);this.observer=A;this.lastValue=Sortable.serialize(this.element)},onStart:function(){this.lastValue=Sortable.serialize(this.element)},onEnd:function(){Sortable.unmark();if(this.lastValue!=Sortable.serialize(this.element)){this.observer(this.element)}}});var Sortable={SERIALIZE_RULE:/^[^_\-](?:[A-Za-z0-9\-\_]*)[_](.*)$/,sortables:{},_findRootElement:function(A){while(A.tagName.toUpperCase()!="BODY"){if(A.id&&Sortable.sortables[A.id]){return A}A=A.parentNode}},options:function(A){A=Sortable._findRootElement($(A));if(!A){return }return Sortable.sortables[A.id]},destroy:function(A){var B=Sortable.options(A);if(B){Draggables.removeObserver(B.element);B.droppables.each(function(C){Droppables.remove(C)});B.draggables.invoke("destroy");delete Sortable.sortables[B.element.id]}},create:function(C){C=$(C);var B=Object.extend({element:C,tag:"li",dropOnEmpty:false,tree:false,treeTag:"ul",overlap:"vertical",constraint:"vertical",containment:C,handle:false,only:false,delay:0,hoverclass:null,ghosting:false,quiet:false,scroll:false,scrollSensitivity:20,scrollSpeed:15,format:this.SERIALIZE_RULE,elements:false,handles:false,onChange:Prototype.emptyFunction,onUpdate:Prototype.emptyFunction},arguments[1]||{});this.destroy(C);var A={revert:true,quiet:B.quiet,scroll:B.scroll,scrollSpeed:B.scrollSpeed,scrollSensitivity:B.scrollSensitivity,delay:B.delay,ghosting:B.ghosting,constraint:B.constraint,handle:B.handle};if(B.starteffect){A.starteffect=B.starteffect}if(B.reverteffect){A.reverteffect=B.reverteffect}else{if(B.ghosting){A.reverteffect=function(F){F.style.top=0;F.style.left=0}}}if(B.endeffect){A.endeffect=B.endeffect}if(B.zindex){A.zindex=B.zindex}var D={overlap:B.overlap,containment:B.containment,tree:B.tree,hoverclass:B.hoverclass,onHover:Sortable.onHover};var E={onHover:Sortable.onEmptyHover,overlap:B.overlap,containment:B.containment,hoverclass:B.hoverclass};Element.cleanWhitespace(C);B.draggables=[];B.droppables=[];if(B.dropOnEmpty||B.tree){Droppables.add(C,E);B.droppables.push(C)}(B.elements||this.findElements(C,B)||[]).each(function(H,F){var G=B.handles?$(B.handles[F]):(B.handle?$(H).select("."+B.handle)[0]:H);B.draggables.push(new Draggable(H,Object.extend(A,{handle:G})));Droppables.add(H,D);if(B.tree){H.treeNode=C}B.droppables.push(H)});if(B.tree){(Sortable.findTreeElements(C,B)||[]).each(function(F){Droppables.add(F,E);F.treeNode=C;B.droppables.push(F)})}this.sortables[C.id]=B;Draggables.addObserver(new SortableObserver(C,B.onUpdate))},findElements:function(B,A){return Element.findChildren(B,A.only,A.tree?true:false,A.tag)},findTreeElements:function(B,A){return Element.findChildren(B,A.only,A.tree?true:false,A.treeTag)},onHover:function(E,D,A){if(Element.isParent(D,E)){return }if(A>0.33&&A<0.66&&Sortable.options(D).tree){return }else{if(A>0.5){Sortable.mark(D,"before");if(D.previousSibling!=E){var B=E.parentNode;E.style.visibility="hidden";D.parentNode.insertBefore(E,D);if(D.parentNode!=B){Sortable.options(B).onChange(E)}Sortable.options(D.parentNode).onChange(E)}}else{Sortable.mark(D,"after");var C=D.nextSibling||null;if(C!=E){var B=E.parentNode;E.style.visibility="hidden";D.parentNode.insertBefore(E,C);if(D.parentNode!=B){Sortable.options(B).onChange(E)}Sortable.options(D.parentNode).onChange(E)}}}},onEmptyHover:function(E,G,H){var I=E.parentNode;var A=Sortable.options(G);if(!Element.isParent(G,E)){var F;var C=Sortable.findElements(G,{tag:A.tag,only:A.only});var B=null;if(C){var D=Element.offsetSize(G,A.overlap)*(1-H);for(F=0;F<C.length;F+=1){if(D-Element.offsetSize(C[F],A.overlap)>=0){D-=Element.offsetSize(C[F],A.overlap)}else{if(D-(Element.offsetSize(C[F],A.overlap)/2)>=0){B=F+1<C.length?C[F+1]:null;break}else{B=C[F];break}}}}G.insertBefore(E,B);Sortable.options(I).onChange(E);A.onChange(E)}},unmark:function(){if(Sortable._marker){Sortable._marker.hide()}},mark:function(B,A){var D=Sortable.options(B.parentNode);if(D&&!D.ghosting){return }if(!Sortable._marker){Sortable._marker=($("dropmarker")||Element.extend(document.createElement("DIV"))).hide().addClassName("dropmarker").setStyle({position:"absolute"});document.getElementsByTagName("body").item(0).appendChild(Sortable._marker)}var C=Position.cumulativeOffset(B);Sortable._marker.setStyle({left:C[0]+"px",top:C[1]+"px"});if(A=="after"){if(D.overlap=="horizontal"){Sortable._marker.setStyle({left:(C[0]+B.clientWidth)+"px"})}else{Sortable._marker.setStyle({top:(C[1]+B.clientHeight)+"px"})}}Sortable._marker.show()},_tree:function(E,B,F){var D=Sortable.findElements(E,B)||[];for(var C=0;C<D.length;++C){var A=D[C].id.match(B.format);if(!A){continue}var G={id:encodeURIComponent(A?A[1]:null),element:E,parent:F,children:[],position:F.children.length,container:$(D[C]).down(B.treeTag)};if(G.container){this._tree(G.container,B,G)}F.children.push(G)}return F},tree:function(D){D=$(D);var C=this.options(D);var B=Object.extend({tag:C.tag,treeTag:C.treeTag,only:C.only,name:D.id,format:C.format},arguments[1]||{});var A={id:null,parent:null,children:[],container:D,position:0};return Sortable._tree(D,B,A)},_constructIndex:function(B){var A="";do{if(B.id){A="["+B.position+"]"+A}}while((B=B.parent)!=null);return A},sequence:function(B){B=$(B);var A=Object.extend(this.options(B),arguments[1]||{});return $(this.findElements(B,A)||[]).map(function(C){return C.id.match(A.format)?C.id.match(A.format)[1]:""})},setSequence:function(B,C){B=$(B);var A=Object.extend(this.options(B),arguments[2]||{});var D={};this.findElements(B,A).each(function(E){if(E.id.match(A.format)){D[E.id.match(A.format)[1]]=[E,E.parentNode]}E.parentNode.removeChild(E)});C.each(function(E){var F=D[E];if(F){F[1].appendChild(F[0]);delete D[E]}})},serialize:function(C){C=$(C);var B=Object.extend(Sortable.options(C),arguments[1]||{});var A=encodeURIComponent((arguments[1]&&arguments[1].name)?arguments[1].name:C.id);if(B.tree){return Sortable.tree(C,arguments[1]).children.map(function(D){return[A+Sortable._constructIndex(D)+"[id]="+encodeURIComponent(D.id)].concat(D.children.map(arguments.callee))}).flatten().join("&")}else{return Sortable.sequence(C,arguments[1]).map(function(D){return A+"[]="+encodeURIComponent(D)}).join("&")}}};Element.isParent=function(B,A){if(!B.parentNode||B==A){return false}if(B.parentNode==A){return true}return Element.isParent(B.parentNode,A)};Element.findChildren=function(D,B,A,C){if(!D.hasChildNodes()){return null}C=C.toUpperCase();if(B){B=[B].flatten()}var E=[];$A(D.childNodes).each(function(G){if(G.tagName&&G.tagName.toUpperCase()==C&&(!B||(Element.classNames(G).detect(function(H){return B.include(H)})))){E.push(G)}if(A){var F=Element.findChildren(G,B,A,C);if(F){E.push(F)}}});return(E.length>0?E.flatten():[])};Element.offsetSize=function(A,B){return A["offset"+((B=="vertical"||B=="height")?"Height":"Width")]};if(typeof Effect=="undefined"){throw ("controls.js requires including script.aculo.us' effects.js library")}var Autocompleter={};Autocompleter.Base=Class.create({baseInitialize:function(B,C,A){B=$(B);this.element=B;this.update=$(C);this.hasFocus=false;this.changed=false;this.active=false;this.index=0;this.entryCount=0;this.oldElementValue=this.element.value;if(this.setOptions){this.setOptions(A)}else{this.options=A||{}}this.options.paramName=this.options.paramName||this.element.name;this.options.tokens=this.options.tokens||[];this.options.frequency=this.options.frequency||0.4;this.options.minChars=this.options.minChars||1;this.options.onShow=this.options.onShow||function(D,E){if(!E.style.position||E.style.position=="absolute"){E.style.position="absolute";Position.clone(D,E,{setHeight:false,offsetTop:D.offsetHeight})}Effect.Appear(E,{duration:0.15})};this.options.onHide=this.options.onHide||function(D,E){new Effect.Fade(E,{duration:0.15})};if(typeof (this.options.tokens)=="string"){this.options.tokens=new Array(this.options.tokens)}if(!this.options.tokens.include("\n")){this.options.tokens.push("\n")}this.observer=null;this.element.setAttribute("autocomplete","off");Element.hide(this.update);Event.observe(this.element,"blur",this.onBlur.bindAsEventListener(this));Event.observe(this.element,"keydown",this.onKeyPress.bindAsEventListener(this))},show:function(){if(Element.getStyle(this.update,"display")=="none"){this.options.onShow(this.element,this.update)}if(!this.iefix&&(Prototype.Browser.IE)&&(Element.getStyle(this.update,"position")=="absolute")){new Insertion.After(this.update,'<iframe id="'+this.update.id+'_iefix" style="display:none;position:absolute;filter:progid:DXImageTransform.Microsoft.Alpha(opacity=0);" src="javascript:false;" frameborder="0" scrolling="no"></iframe>');this.iefix=$(this.update.id+"_iefix")}if(this.iefix){setTimeout(this.fixIEOverlapping.bind(this),50)}},fixIEOverlapping:function(){Position.clone(this.update,this.iefix,{setTop:(!this.update.style.height)});this.iefix.style.zIndex=1;this.update.style.zIndex=2;Element.show(this.iefix)},hide:function(){this.stopIndicator();if(Element.getStyle(this.update,"display")!="none"){this.options.onHide(this.element,this.update)}if(this.iefix){Element.hide(this.iefix)}},startIndicator:function(){if(this.options.indicator){Element.show(this.options.indicator)}},stopIndicator:function(){if(this.options.indicator){Element.hide(this.options.indicator)}},onKeyPress:function(A){if(this.active){switch(A.keyCode){case Event.KEY_TAB:case Event.KEY_RETURN:this.selectEntry();Event.stop(A);case Event.KEY_ESC:this.hide();this.active=false;Event.stop(A);return ;case Event.KEY_LEFT:case Event.KEY_RIGHT:return ;case Event.KEY_UP:this.markPrevious();this.render();Event.stop(A);return ;case Event.KEY_DOWN:this.markNext();this.render();Event.stop(A);return }}else{if(A.keyCode==Event.KEY_TAB||A.keyCode==Event.KEY_RETURN||(Prototype.Browser.WebKit>0&&A.keyCode==0)){return }}this.changed=true;this.hasFocus=true;if(this.observer){clearTimeout(this.observer)}this.observer=setTimeout(this.onObserverEvent.bind(this),this.options.frequency*1000)},activate:function(){this.changed=false;this.hasFocus=true;this.getUpdatedChoices()},onHover:function(B){var A=Event.findElement(B,"LI");if(this.index!=A.autocompleteIndex){this.index=A.autocompleteIndex;this.render()}Event.stop(B)},onClick:function(B){var A=Event.findElement(B,"LI");this.index=A.autocompleteIndex;this.selectEntry();this.hide()},onBlur:function(A){setTimeout(this.hide.bind(this),250);this.hasFocus=false;this.active=false},render:function(){if(this.entryCount>0){for(var A=0;A<this.entryCount;A++){this.index==A?Element.addClassName(this.getEntry(A),"selected"):Element.removeClassName(this.getEntry(A),"selected")}if(this.hasFocus){this.show();this.active=true}}else{this.active=false;this.hide()}},markPrevious:function(){if(this.index>0){this.index--}else{this.index=this.entryCount-1}this.getEntry(this.index).scrollIntoView(true)},markNext:function(){if(this.index<this.entryCount-1){this.index++}else{this.index=0}this.getEntry(this.index).scrollIntoView(false)},getEntry:function(A){return this.update.firstChild.childNodes[A]},getCurrentEntry:function(){return this.getEntry(this.index)},selectEntry:function(){this.active=false;this.updateElement(this.getCurrentEntry())},updateElement:function(F){if(this.options.updateElement){this.options.updateElement(F);return }var D="";if(this.options.select){var A=$(F).select("."+this.options.select)||[];if(A.length>0){D=Element.collectTextNodes(A[0],this.options.select)}}else{D=Element.collectTextNodesIgnoreClass(F,"informal")}var C=this.getTokenBounds();if(C[0]!=-1){var E=this.element.value.substr(0,C[0]);var B=this.element.value.substr(C[0]).match(/^\s+/);if(B){E+=B[0]}this.element.value=E+D+this.element.value.substr(C[1])}else{this.element.value=D}this.oldElementValue=this.element.value;this.element.focus();if(this.options.afterUpdateElement){this.options.afterUpdateElement(this.element,F)}},updateChoices:function(C){if(!this.changed&&this.hasFocus){this.update.innerHTML=C;Element.cleanWhitespace(this.update);Element.cleanWhitespace(this.update.down());if(this.update.firstChild&&this.update.down().childNodes){this.entryCount=this.update.down().childNodes.length;for(var A=0;A<this.entryCount;A++){var B=this.getEntry(A);B.autocompleteIndex=A;this.addObservers(B)}}else{this.entryCount=0}this.stopIndicator();this.index=0;if(this.entryCount==1&&this.options.autoSelect){this.selectEntry();this.hide()}else{this.render()}}},addObservers:function(A){Event.observe(A,"mouseover",this.onHover.bindAsEventListener(this));Event.observe(A,"click",this.onClick.bindAsEventListener(this))},onObserverEvent:function(){this.changed=false;this.tokenBounds=null;if(this.getToken().length>=this.options.minChars){this.getUpdatedChoices()}else{this.active=false;this.hide()}this.oldElementValue=this.element.value},getToken:function(){var A=this.getTokenBounds();return this.element.value.substring(A[0],A[1]).strip()},getTokenBounds:function(){if(null!=this.tokenBounds){return this.tokenBounds}var E=this.element.value;if(E.strip().empty()){return[-1,0]}var F=arguments.callee.getFirstDifferencePos(E,this.oldElementValue);var H=(F==this.oldElementValue.length?1:0);var D=-1,C=E.length;var G;for(var B=0,A=this.options.tokens.length;B<A;++B){G=E.lastIndexOf(this.options.tokens[B],F+H-1);if(G>D){D=G}G=E.indexOf(this.options.tokens[B],F+H);if(-1!=G&&G<C){C=G}}return(this.tokenBounds=[D+1,C])}});Autocompleter.Base.prototype.getTokenBounds.getFirstDifferencePos=function(C,A){var D=Math.min(C.length,A.length);for(var B=0;B<D;++B){if(C[B]!=A[B]){return B}}return D};Ajax.Autocompleter=Class.create(Autocompleter.Base,{initialize:function(C,D,B,A){this.baseInitialize(C,D,A);this.options.asynchronous=true;this.options.onComplete=this.onComplete.bind(this);this.options.defaultParams=this.options.parameters||null;this.url=B},getUpdatedChoices:function(){this.startIndicator();var A=encodeURIComponent(this.options.paramName)+"="+encodeURIComponent(this.getToken());this.options.parameters=this.options.callback?this.options.callback(this.element,A):A;if(this.options.defaultParams){this.options.parameters+="&"+this.options.defaultParams}new Ajax.Request(this.url,this.options)},onComplete:function(A){this.updateChoices(A.responseText)}});Autocompleter.Local=Class.create(Autocompleter.Base,{initialize:function(B,D,C,A){this.baseInitialize(B,D,A);this.options.array=C},getUpdatedChoices:function(){this.updateChoices(this.options.selector(this))},setOptions:function(A){this.options=Object.extend({choices:10,partialSearch:true,partialChars:2,ignoreCase:true,fullSearch:false,selector:function(B){var D=[];var C=[];var H=B.getToken();var G=0;for(var E=0;E<B.options.array.length&&D.length<B.options.choices;E++){var F=B.options.array[E];var I=B.options.ignoreCase?F.toLowerCase().indexOf(H.toLowerCase()):F.indexOf(H);while(I!=-1){if(I==0&&F.length!=H.length){D.push("<li><strong>"+F.substr(0,H.length)+"</strong>"+F.substr(H.length)+"</li>");break}else{if(H.length>=B.options.partialChars&&B.options.partialSearch&&I!=-1){if(B.options.fullSearch||/\s/.test(F.substr(I-1,1))){C.push("<li>"+F.substr(0,I)+"<strong>"+F.substr(I,H.length)+"</strong>"+F.substr(I+H.length)+"</li>");break}}}I=B.options.ignoreCase?F.toLowerCase().indexOf(H.toLowerCase(),I+1):F.indexOf(H,I+1)}}if(C.length){D=D.concat(C.slice(0,B.options.choices-D.length))}return"<ul>"+D.join("")+"</ul>"}},A||{})}});Field.scrollFreeActivate=function(A){setTimeout(function(){Field.activate(A)},1)};Ajax.InPlaceEditor=Class.create({initialize:function(C,B,A){this.url=B;this.element=C=$(C);this.prepareOptions();this._controls={};arguments.callee.dealWithDeprecatedOptions(A);Object.extend(this.options,A||{});if(!this.options.formId&&this.element.id){this.options.formId=this.element.id+"-inplaceeditor";if($(this.options.formId)){this.options.formId=""}}if(this.options.externalControl){this.options.externalControl=$(this.options.externalControl)}if(!this.options.externalControl){this.options.externalControlOnly=false}this._originalBackground=this.element.getStyle("background-color")||"transparent";this.element.title=this.options.clickToEditText;this._boundCancelHandler=this.handleFormCancellation.bind(this);this._boundComplete=(this.options.onComplete||Prototype.emptyFunction).bind(this);this._boundFailureHandler=this.handleAJAXFailure.bind(this);this._boundSubmitHandler=this.handleFormSubmission.bind(this);this._boundWrapperHandler=this.wrapUp.bind(this);this.registerListeners()},checkForEscapeOrReturn:function(A){if(!this._editing||A.ctrlKey||A.altKey||A.shiftKey){return }if(Event.KEY_ESC==A.keyCode){this.handleFormCancellation(A)}else{if(Event.KEY_RETURN==A.keyCode){this.handleFormSubmission(A)}}},createControl:function(G,C,B){var E=this.options[G+"Control"];var F=this.options[G+"Text"];if("button"==E){var A=document.createElement("input");A.type="submit";A.value=F;A.className="editor_"+G+"_button";if("cancel"==G){A.onclick=this._boundCancelHandler}this._form.appendChild(A);this._controls[G]=A}else{if("link"==E){var D=document.createElement("a");D.href="#";D.appendChild(document.createTextNode(F));D.onclick="cancel"==G?this._boundCancelHandler:this._boundSubmitHandler;D.className="editor_"+G+"_link";if(B){D.className+=" "+B}this._form.appendChild(D);this._controls[G]=D}}},createEditField:function(){var C=(this.options.loadTextURL?this.options.loadingText:this.getText());var B;if(1>=this.options.rows&&!/\r|\n/.test(this.getText())){B=document.createElement("input");B.type="text";var A=this.options.size||this.options.cols||0;if(0<A){B.size=A}}else{B=document.createElement("textarea");B.rows=(1>=this.options.rows?this.options.autoRows:this.options.rows);B.cols=this.options.cols||40}B.name=this.options.paramName;B.value=C;B.className="editor_field";if(this.options.submitOnBlur){B.onblur=this._boundSubmitHandler}this._controls.editor=B;if(this.options.loadTextURL){this.loadExternalText()}this._form.appendChild(this._controls.editor)},createForm:function(){var B=this;function A(D,E){var C=B.options["text"+D+"Controls"];if(!C||E===false){return }B._form.appendChild(document.createTextNode(C))}this._form=$(document.createElement("form"));this._form.id=this.options.formId;this._form.addClassName(this.options.formClassName);this._form.onsubmit=this._boundSubmitHandler;this.createEditField();if("textarea"==this._controls.editor.tagName.toLowerCase()){this._form.appendChild(document.createElement("br"))}if(this.options.onFormCustomization){this.options.onFormCustomization(this,this._form)}A("Before",this.options.okControl||this.options.cancelControl);this.createControl("ok",this._boundSubmitHandler);A("Between",this.options.okControl&&this.options.cancelControl);this.createControl("cancel",this._boundCancelHandler,"editor_cancel");A("After",this.options.okControl||this.options.cancelControl)},destroy:function(){if(this._oldInnerHTML){this.element.innerHTML=this._oldInnerHTML}this.leaveEditMode();this.unregisterListeners()},enterEditMode:function(A){if(this._saving||this._editing){return }this._editing=true;this.triggerCallback("onEnterEditMode");if(this.options.externalControl){this.options.externalControl.hide()}this.element.hide();this.createForm();this.element.parentNode.insertBefore(this._form,this.element);if(!this.options.loadTextURL){this.postProcessEditField()}if(A){Event.stop(A)}},enterHover:function(A){if(this.options.hoverClassName){this.element.addClassName(this.options.hoverClassName)}if(this._saving){return }this.triggerCallback("onEnterHover")},getText:function(){return this.element.innerHTML},handleAJAXFailure:function(A){this.triggerCallback("onFailure",A);if(this._oldInnerHTML){this.element.innerHTML=this._oldInnerHTML;this._oldInnerHTML=null}},handleFormCancellation:function(A){this.wrapUp();if(A){Event.stop(A)}},handleFormSubmission:function(D){var B=this._form;var C=$F(this._controls.editor);this.prepareSubmission();var E=this.options.callback(B,C)||"";if(Object.isString(E)){E=E.toQueryParams()}E.editorId=this.element.id;if(this.options.htmlResponse){var A=Object.extend({evalScripts:true},this.options.ajaxOptions);Object.extend(A,{parameters:E,onComplete:this._boundWrapperHandler,onFailure:this._boundFailureHandler});new Ajax.Updater({success:this.element},this.url,A)}else{var A=Object.extend({method:"get"},this.options.ajaxOptions);Object.extend(A,{parameters:E,onComplete:this._boundWrapperHandler,onFailure:this._boundFailureHandler});new Ajax.Request(this.url,A)}if(D){Event.stop(D)}},leaveEditMode:function(){this.element.removeClassName(this.options.savingClassName);this.removeForm();this.leaveHover();this.element.style.backgroundColor=this._originalBackground;this.element.show();if(this.options.externalControl){this.options.externalControl.show()}this._saving=false;this._editing=false;this._oldInnerHTML=null;this.triggerCallback("onLeaveEditMode")},leaveHover:function(A){if(this.options.hoverClassName){this.element.removeClassName(this.options.hoverClassName)}if(this._saving){return }this.triggerCallback("onLeaveHover")},loadExternalText:function(){this._form.addClassName(this.options.loadingClassName);this._controls.editor.disabled=true;var A=Object.extend({method:"get"},this.options.ajaxOptions);Object.extend(A,{parameters:"editorId="+encodeURIComponent(this.element.id),onComplete:Prototype.emptyFunction,onSuccess:function(C){this._form.removeClassName(this.options.loadingClassName);var B=C.responseText;if(this.options.stripLoadedTextTags){B=B.stripTags()}this._controls.editor.value=B;this._controls.editor.disabled=false;this.postProcessEditField()}.bind(this),onFailure:this._boundFailureHandler});new Ajax.Request(this.options.loadTextURL,A)},postProcessEditField:function(){var A=this.options.fieldPostCreation;if(A){$(this._controls.editor)["focus"==A?"focus":"activate"]()}},prepareOptions:function(){this.options=Object.clone(Ajax.InPlaceEditor.DefaultOptions);Object.extend(this.options,Ajax.InPlaceEditor.DefaultCallbacks);[this._extraDefaultOptions].flatten().compact().each(function(A){Object.extend(this.options,A)}.bind(this))},prepareSubmission:function(){this._saving=true;this.removeForm();this.leaveHover();this.showSaving()},registerListeners:function(){this._listeners={};var A;$H(Ajax.InPlaceEditor.Listeners).each(function(B){A=this[B.value].bind(this);this._listeners[B.key]=A;if(!this.options.externalControlOnly){this.element.observe(B.key,A)}if(this.options.externalControl){this.options.externalControl.observe(B.key,A)}}.bind(this))},removeForm:function(){if(!this._form){return }this._form.remove();this._form=null;this._controls={}},showSaving:function(){this._oldInnerHTML=this.element.innerHTML;this.element.innerHTML=this.options.savingText;this.element.addClassName(this.options.savingClassName);this.element.style.backgroundColor=this._originalBackground;this.element.show()},triggerCallback:function(B,A){if("function"==typeof this.options[B]){this.options[B](this,A)}},unregisterListeners:function(){$H(this._listeners).each(function(A){if(!this.options.externalControlOnly){this.element.stopObserving(A.key,A.value)}if(this.options.externalControl){this.options.externalControl.stopObserving(A.key,A.value)}}.bind(this))},wrapUp:function(A){this.leaveEditMode();this._boundComplete(A,this.element)}});Object.extend(Ajax.InPlaceEditor.prototype,{dispose:Ajax.InPlaceEditor.prototype.destroy});Ajax.InPlaceCollectionEditor=Class.create(Ajax.InPlaceEditor,{initialize:function($super,C,B,A){this._extraDefaultOptions=Ajax.InPlaceCollectionEditor.DefaultOptions;$super(C,B,A)},createEditField:function(){var A=document.createElement("select");A.name=this.options.paramName;A.size=1;this._controls.editor=A;this._collection=this.options.collection||[];if(this.options.loadCollectionURL){this.loadCollection()}else{this.checkForExternalText()}this._form.appendChild(this._controls.editor)},loadCollection:function(){this._form.addClassName(this.options.loadingClassName);this.showLoadingText(this.options.loadingCollectionText);var options=Object.extend({method:"get"},this.options.ajaxOptions);Object.extend(options,{parameters:"editorId="+encodeURIComponent(this.element.id),onComplete:Prototype.emptyFunction,onSuccess:function(transport){var js=transport.responseText.strip();if(!/^\[.*\]$/.test(js)){throw"Server returned an invalid collection representation."}this._collection=eval(js);this.checkForExternalText()}.bind(this),onFailure:this.onFailure});new Ajax.Request(this.options.loadCollectionURL,options)},showLoadingText:function(B){this._controls.editor.disabled=true;var A=this._controls.editor.firstChild;if(!A){A=document.createElement("option");A.value="";this._controls.editor.appendChild(A);A.selected=true}A.update((B||"").stripScripts().stripTags())},checkForExternalText:function(){this._text=this.getText();if(this.options.loadTextURL){this.loadExternalText()}else{this.buildOptionList()}},loadExternalText:function(){this.showLoadingText(this.options.loadingText);var A=Object.extend({method:"get"},this.options.ajaxOptions);Object.extend(A,{parameters:"editorId="+encodeURIComponent(this.element.id),onComplete:Prototype.emptyFunction,onSuccess:function(B){this._text=B.responseText.strip();this.buildOptionList()}.bind(this),onFailure:this.onFailure});new Ajax.Request(this.options.loadTextURL,A)},buildOptionList:function(){this._form.removeClassName(this.options.loadingClassName);this._collection=this._collection.map(function(D){return 2===D.length?D:[D,D].flatten()});var B=("value" in this.options)?this.options.value:this._text;var A=this._collection.any(function(D){return D[0]==B}.bind(this));this._controls.editor.update("");var C;this._collection.each(function(E,D){C=document.createElement("option");C.value=E[0];C.selected=A?E[0]==B:0==D;C.appendChild(document.createTextNode(E[1]));this._controls.editor.appendChild(C)}.bind(this));this._controls.editor.disabled=false;Field.scrollFreeActivate(this._controls.editor)}});Ajax.InPlaceEditor.prototype.initialize.dealWithDeprecatedOptions=function(A){if(!A){return }function B(C,D){if(C in A||D===undefined){return }A[C]=D}B("cancelControl",(A.cancelLink?"link":(A.cancelButton?"button":A.cancelLink==A.cancelButton==false?false:undefined)));B("okControl",(A.okLink?"link":(A.okButton?"button":A.okLink==A.okButton==false?false:undefined)));B("highlightColor",A.highlightcolor);B("highlightEndColor",A.highlightendcolor)};Object.extend(Ajax.InPlaceEditor,{DefaultOptions:{ajaxOptions:{},autoRows:3,cancelControl:"link",cancelText:"cancel",clickToEditText:"Click to edit",externalControl:null,externalControlOnly:false,fieldPostCreation:"activate",formClassName:"inplaceeditor-form",formId:null,highlightColor:"#ffff99",highlightEndColor:"#ffffff",hoverClassName:"",htmlResponse:true,loadingClassName:"inplaceeditor-loading",loadingText:"Loading...",okControl:"button",okText:"ok",paramName:"value",rows:1,savingClassName:"inplaceeditor-saving",savingText:"Saving...",size:0,stripLoadedTextTags:false,submitOnBlur:false,textAfterControls:"",textBeforeControls:"",textBetweenControls:""},DefaultCallbacks:{callback:function(A){return Form.serialize(A)},onComplete:function(B,A){new Effect.Highlight(A,{startcolor:this.options.highlightColor,keepBackgroundImage:true})},onEnterEditMode:null,onEnterHover:function(A){A.element.style.backgroundColor=A.options.highlightColor;if(A._effect){A._effect.cancel()}},onFailure:function(B,A){alert("Error communication with the server: "+B.responseText.stripTags())},onFormCustomization:null,onLeaveEditMode:null,onLeaveHover:function(A){A._effect=new Effect.Highlight(A.element,{startcolor:A.options.highlightColor,endcolor:A.options.highlightEndColor,restorecolor:A._originalBackground,keepBackgroundImage:true})}},Listeners:{click:"enterEditMode",keydown:"checkForEscapeOrReturn",mouseover:"enterHover",mouseout:"leaveHover"}});Ajax.InPlaceCollectionEditor.DefaultOptions={loadingCollectionText:"Loading options..."};Form.Element.DelayedObserver=Class.create({initialize:function(B,A,C){this.delay=A||0.5;this.element=$(B);this.callback=C;this.timer=null;this.lastValue=$F(this.element);Event.observe(this.element,"keyup",this.delayedListener.bindAsEventListener(this))},delayedListener:function(A){if(this.lastValue==$F(this.element)){return }if(this.timer){clearTimeout(this.timer)}this.timer=setTimeout(this.onTimerEvent.bind(this),this.delay*1000);this.lastValue=$F(this.element)},onTimerEvent:function(){this.timer=null;this.callback(this.element,$F(this.element))}});if(!Control){var Control={}}Control.Slider=Class.create({initialize:function(D,A,B){var C=this;if(Object.isArray(D)){this.handles=D.collect(function(E){return $(E)})}else{this.handles=[$(D)]}this.track=$(A);this.options=B||{};this.axis=this.options.axis||"horizontal";this.increment=this.options.increment||1;this.step=parseInt(this.options.step||"1");this.range=this.options.range||$R(0,1);this.value=0;this.values=this.handles.map(function(){return 0});this.spans=this.options.spans?this.options.spans.map(function(E){return $(E)}):false;this.options.startSpan=$(this.options.startSpan||null);this.options.endSpan=$(this.options.endSpan||null);this.restricted=this.options.restricted||false;this.maximum=this.options.maximum||this.range.end;this.minimum=this.options.minimum||this.range.start;this.alignX=parseInt(this.options.alignX||"0");this.alignY=parseInt(this.options.alignY||"0");this.trackLength=this.maximumOffset()-this.minimumOffset();this.handleLength=this.isVertical()?(this.handles[0].offsetHeight!=0?this.handles[0].offsetHeight:this.handles[0].style.height.replace(/px$/,"")):(this.handles[0].offsetWidth!=0?this.handles[0].offsetWidth:this.handles[0].style.width.replace(/px$/,""));this.active=false;this.dragging=false;this.disabled=false;if(this.options.disabled){this.setDisabled()}this.allowedValues=this.options.values?this.options.values.sortBy(Prototype.K):false;if(this.allowedValues){this.minimum=this.allowedValues.min();this.maximum=this.allowedValues.max()}this.eventMouseDown=this.startDrag.bindAsEventListener(this);this.eventMouseUp=this.endDrag.bindAsEventListener(this);this.eventMouseMove=this.update.bindAsEventListener(this);this.handles.each(function(F,E){E=C.handles.length-1-E;C.setValue(parseFloat((Object.isArray(C.options.sliderValue)?C.options.sliderValue[E]:C.options.sliderValue)||C.range.start),E);F.makePositioned().observe("mousedown",C.eventMouseDown)});this.track.observe("mousedown",this.eventMouseDown);document.observe("mouseup",this.eventMouseUp);document.observe("mousemove",this.eventMouseMove);this.initialized=true},dispose:function(){var A=this;Event.stopObserving(this.track,"mousedown",this.eventMouseDown);Event.stopObserving(document,"mouseup",this.eventMouseUp);Event.stopObserving(document,"mousemove",this.eventMouseMove);this.handles.each(function(B){Event.stopObserving(B,"mousedown",A.eventMouseDown)})},setDisabled:function(){this.disabled=true},setEnabled:function(){this.disabled=false},getNearestValue:function(A){if(this.allowedValues){if(A>=this.allowedValues.max()){return(this.allowedValues.max())}if(A<=this.allowedValues.min()){return(this.allowedValues.min())}var C=Math.abs(this.allowedValues[0]-A);var B=this.allowedValues[0];this.allowedValues.each(function(D){var E=Math.abs(D-A);if(E<=C){B=D;C=E}});return B}if(A>this.range.end){return this.range.end}if(A<this.range.start){return this.range.start}return A},setValue:function(B,A){if(!this.active){this.activeHandleIdx=A||0;this.activeHandle=this.handles[this.activeHandleIdx];this.updateStyles()}A=A||this.activeHandleIdx||0;if(this.initialized&&this.restricted){if((A>0)&&(B<this.values[A-1])){B=this.values[A-1]}if((A<(this.handles.length-1))&&(B>this.values[A+1])){B=this.values[A+1]}}B=this.getNearestValue(B);this.values[A]=B;this.value=this.values[0];this.handles[A].style[this.isVertical()?"top":"left"]=this.translateToPx(B);this.drawSpans();if(!this.dragging||!this.event){this.updateFinished()}},setValueBy:function(B,A){this.setValue(this.values[A||this.activeHandleIdx||0]+B,A||this.activeHandleIdx||0)},translateToPx:function(A){return Math.round(((this.trackLength-this.handleLength)/(this.range.end-this.range.start))*(A-this.range.start))+"px"},translateToValue:function(A){return((A/(this.trackLength-this.handleLength)*(this.range.end-this.range.start))+this.range.start)},getRange:function(B){var A=this.values.sortBy(Prototype.K);B=B||0;return $R(A[B],A[B+1])},minimumOffset:function(){return(this.isVertical()?this.alignY:this.alignX)},maximumOffset:function(){return(this.isVertical()?(this.track.offsetHeight!=0?this.track.offsetHeight:this.track.style.height.replace(/px$/,""))-this.alignY:(this.track.offsetWidth!=0?this.track.offsetWidth:this.track.style.width.replace(/px$/,""))-this.alignX)},isVertical:function(){return(this.axis=="vertical")},drawSpans:function(){var A=this;if(this.spans){$R(0,this.spans.length-1).each(function(B){A.setSpan(A.spans[B],A.getRange(B))})}if(this.options.startSpan){this.setSpan(this.options.startSpan,$R(0,this.values.length>1?this.getRange(0).min():this.value))}if(this.options.endSpan){this.setSpan(this.options.endSpan,$R(this.values.length>1?this.getRange(this.spans.length-1).max():this.value,this.maximum))}},setSpan:function(B,A){if(this.isVertical()){B.style.top=this.translateToPx(A.start);B.style.height=this.translateToPx(A.end-A.start+this.range.start)}else{B.style.left=this.translateToPx(A.start);B.style.width=this.translateToPx(A.end-A.start+this.range.start)}},updateStyles:function(){this.handles.each(function(A){Element.removeClassName(A,"selected")});Element.addClassName(this.activeHandle,"selected")},startDrag:function(C){if(Event.isLeftClick(C)){if(!this.disabled){this.active=true;var D=Event.element(C);var E=[Event.pointerX(C),Event.pointerY(C)];var A=D;if(A==this.track){var B=Position.cumulativeOffset(this.track);this.event=C;this.setValue(this.translateToValue((this.isVertical()?E[1]-B[1]:E[0]-B[0])-(this.handleLength/2)));var B=Position.cumulativeOffset(this.activeHandle);this.offsetX=(E[0]-B[0]);this.offsetY=(E[1]-B[1])}else{while((this.handles.indexOf(D)==-1)&&D.parentNode){D=D.parentNode}if(this.handles.indexOf(D)!=-1){this.activeHandle=D;this.activeHandleIdx=this.handles.indexOf(this.activeHandle);this.updateStyles();var B=Position.cumulativeOffset(this.activeHandle);this.offsetX=(E[0]-B[0]);this.offsetY=(E[1]-B[1])}}}Event.stop(C)}},update:function(A){if(this.active){if(!this.dragging){this.dragging=true}this.draw(A);if(Prototype.Browser.WebKit){window.scrollBy(0,0)}Event.stop(A)}},draw:function(B){var C=[Event.pointerX(B),Event.pointerY(B)];var A=Position.cumulativeOffset(this.track);C[0]-=this.offsetX+A[0];C[1]-=this.offsetY+A[1];this.event=B;this.setValue(this.translateToValue(this.isVertical()?C[1]:C[0]));if(this.initialized&&this.options.onSlide){this.options.onSlide(this.values.length>1?this.values:this.value,this)}},endDrag:function(A){if(this.active&&this.dragging){this.finishDrag(A,true);Event.stop(A)}this.active=false;this.dragging=false},finishDrag:function(A,B){this.active=false;this.dragging=false;this.updateFinished()},updateFinished:function(){if(this.initialized&&this.options.onChange){this.options.onChange(this.values.length>1?this.values:this.value,this)}this.event=null}});Sound={tracks:{},_enabled:true,template:new Template('<embed style="height:0" id="sound_#{track}_#{id}" src="#{url}" loop="false" autostart="true" hidden="true"/>'),enable:function(){Sound._enabled=true},disable:function(){Sound._enabled=false},play:function(B){if(!Sound._enabled){return }var A=Object.extend({track:"global",url:B,replace:false},arguments[1]||{});if(A.replace&&this.tracks[A.track]){$R(0,this.tracks[A.track].id).each(function(D){var C=$("sound_"+A.track+"_"+D);C.Stop&&C.Stop();C.remove()});this.tracks[A.track]=null}if(!this.tracks[A.track]){this.tracks[A.track]={id:0}}else{this.tracks[A.track].id++}A.id=this.tracks[A.track].id;$$("body")[0].insert(Prototype.Browser.IE?new Element("bgsound",{id:"sound_"+A.track+"_"+A.id,src:A.url,loop:1,autostart:true}):Sound.template.evaluate(A))}};if(Prototype.Browser.Gecko&&navigator.userAgent.indexOf("Win")>0){if(navigator.plugins&&$A(navigator.plugins).detect(function(A){return A.name.indexOf("QuickTime")!=-1})){Sound.template=new Template('<object id="sound_#{track}_#{id}" width="0" height="0" type="audio/mpeg" data="#{url}"/>')}else{Sound.play=function(){}}};

//==== Namespaces
var IO = {};
  IO.Callbacks = {};
  
var UI = {};
  UI.Trails = {};
  UI.InfoWindow = {};
  UI.Icons = {};
  UI.Markers = {};
  UI.tags_multi_selects = [];

  
var Init = {};
	Init.location_loaded = false;

var Services = {
	Elevation: {},
	Callbacks: {},
	Geocoding: { Callbacks: {}}
	};

var Util = {};

var Map = {};
    Map.Events = {};

var Search = {};
    Search.Callbacks = {};
    Search.UI = {};
	Search.UI.InfoWindow = {};
    Search.Event_Handlers = {};
	Search.UI.Multiselect_tags = [];
	
	

//==== UI Look and feel (the CSS needs to be match these as well)
var MAP_BOTTOM_MARGIN = 5;
var FOOTER_HEIGHT = 22;


//The distance past the edge of the viewable map in which to
//search, expressed in percentage of the largest dimension.
Search.SEARCH_BOUNDS_EXPANSION = 0.2;

//A number that determines at which zoom level trails paths are displayed
//in search mode.  Higher = zoomed closer
Search.FULL_TRAIL_MIN_ZOOM = 8;

//Number of seconds between when the user stops panning/zooming
//and when the search is preformed
Search.SEARCH_DELAY = 2;
//How long to wait after a change in the search filter to perform a search
Search.SEARCH_FILTER_DELAY = 1;
//Delay between observations of the search filter UI
Search.FILTER_CHECK_FREQUENCY = 0.5;

//The number of trails to retrieve the full path of per search request
Map.TRAIL_PATHS_PER_REQUEST = 2;
//How many seconds between each full path trail request
Map.TRAIL_PATHS_BACKGROUND_FREQUENCY = 1;

//Maximum numer of overlays on the map at any one time (lines count for 10)
Map.MAX_VISIBLE_OVERLAY_COUNT = 200;

//How long busy or error messages stay visible
UI.MSG_DELAY = 5;

//How many seconds between autosaves
IO.AUTOSAVE_FREQUENCY = 60;
	

var LINE_COLOR = "#ff5030";
var SEARCH_LINE_COLOR = "#ff0000";
var SELECTED_LINE_COLOR = "#ff0000";
var LINE_WIDTH = 3;
var SEARCH_LINE_WIDTH = 2;


// ========== Unobtrusive Extensions ==========
Event.observers = function(elements, event_name, handler){
	if(typeof(elements) == 'string') elements = $$(elements);
	elements.each(function(ele){
		Event.observe(ele, event_name, handler);
	});		
}


// ========== CustomEvents ===========
var CustomEvent = function(default_context)
{
	this._observers = [];
	this._default_context = default_context;
}

Object.extend(CustomEvent.prototype, {
	observe:function(observer, context)
	{
		var observer_info = 
		{
			func: observer,
			context: context || this._default_context,
			stop: this._stop_observing_this,
			event: this
		};
		this._observers.push(observer_info);
		return observer_info;
	},
	
	_stop_observing_this: function(){
		this.event.stop_observing(this);
	},
	
	stop_observing:function(observer_info)
	{
		this._observers = this._observers.without(observer_info)
		observer_info.func = null;
	},
	
	clear_observers:function(){
		this._observers.length = 0;
	},
	
	fire:function(event_info)
	{
		var frozen_list = this._observers;
		for (var i = 0, l = frozen_list.length; i < l; ++i) {
			var observer_info = frozen_list[i];
			if(observer_info && observer_info.func)
				observer_info.func.apply(observer_info.context, [event_info, observer_info]);
		}
	}
});

var DefineEvents = function(){
	arguments = $A(arguments);
	var event_host = arguments.shift();
	
	if(!event_host._event_list)
		event_host._event_list = [];
		
	var events = Object.isArray(arguments[0]) ? arguments[0] : arguments;
	for (var i = events.length - 1; i >= 0; --i) {
		var event = new CustomEvent(event_host);
		event_host[events[i]] = event;
		event_host._event_list.push(event);
	}
}


// ==============  Units Converter ==============
function Units_Converter(abbreviate, useMetric)
{
    this.abbreviate = abbreviate;
    this.useMetric = useMetric;
}

Units_Converter.prototype.Std=
[
    {s:"inch", p:"inches", a:"in", v:0.0254},
    {s:"foot", p:"feet", a:"ft", v:0.3048},
    {s:"yard", p:"yards", a:"yd", v:0.9144},
    {s:"mile", p:"miles", a:"mi", v:1609.344}
];
Units_Converter.prototype.Metric=
[
    {s:"centimeter", p:"centimeters", a:"cm", v:0.01},
    {s:"meter", p:"meters", a:"m", v:1},
    {s:"kilometer", p:"kilometers", a:"km", v:1000}
];

Units_Converter.prototype.length = function(L, unit_index)
{
	if(isNaN(L) || !isFinite(L) || typeof L != 'number')
		return null;
	
    var system = this.useMetric ? this.Metric : this.Std;
    var newL = Math.abs(L);
    unit_index = unit_index || -1;
    var suffix = '';
	var unit = null;
    
    for(var i=system.length-1; unit = system[i]; --i)
    {
        if(unit_index >= 0 && i!= unit_index)
            continue;
            
        newL = this.round3(L / unit.v);
        if(newL >= 1 || i==0 || i == unit_index)
        {
            suffix = this.abbreviate ? unit.a + '.' : (newL == 1 ? unit.s : unit.p);
            break;
        }
    }
    
    if(L<0)
        newL = -newL;
    
    return newL + " " + suffix;
}

Units_Converter.prototype.len = function(){
	var temp = this.abbreviate;
	this.abbreviate = true;
	var result = this.length.apply(this, arguments);
	this.abbreviate = temp;
	return result;
}

//Rounds numbers >100 to ints, >10 to one decimal
//, and all else to two decimals
Units_Converter.prototype.round3 = function(x)
{
    if(x >= 100 || x <= -100)
        return Math.round(x);
    else if(x >= 10 || x <= -10)
        return Math.round(x*10)/10;
    else
        return Math.round(x*100)/100;
}   


//=============== Math Functions ===============

//Rounds degrees (lat/lng) to six decimal places, which is accurate to within 4 inches
function RoundDeg(deg)
{
    return Math.round(deg*1000000)/1000000;
}


function Round(num, divisor)
{
	if(divisor == null)
		divisor = 1;
	
	if(num != null && num != NaN)
	    return Math.round(num*divisor)/divisor;
	else
		return null;
}

function Round1(num)
{
    return Math.round(num * 10) / 10;
}

//=============== Cookies ===============
function createCookie(name,value,days) {
	if (days) {
		var date = new Date();
		date.setTime(date.getTime()+(days*24*60*60*1000));
		var expires = "; expires="+date.toGMTString();
	}
	else var expires = "";
	document.cookie = name+"="+value+expires+"; path=/";
}

function readCookie(name) {
	var nameEQ = name + "=";
	var ca = document.cookie.split(';');
	for(var i=0;i < ca.length;i++) {
		var c = ca[i].strip();
		if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
	}
	return null;
}

function eraseCookie(name) {
	createCookie(name,"",-1);
}

function getDomain()
{
	return window.location.protocol + "//" + window.location.host;
}

//=============== String Functions ================
function sq(str)
{
    return "'" + str + "'";
}

function XMLencode(str)
{
    if(typeof(str) == "undefined" || str == null || str == '')
        return '';

    str = str.toString();
        
    str = str.replace(/&/g, "&amp;");
    str = str.replace(/</g, "&lt;");
    str = str.replace(/>/g, "&gt;");
    str = str.replace(/'/g, "&apos;");
    str = str.replace(/"/g, "&quot;");
    
    return str;
}

//Creates a random case-sensitive string of 16 characters and numbers suitable for use as a UUID
var guidChars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz";
var guidArray = new Array(16); 
function GUID()
{ 
    for (var i=0;i<16;i++)
        guidArray[i] = guidChars.charAt(Math.floor(Math.random() * guidChars.length)); 
    return guidArray.join(''); 
}

//============== Date functions =============
Date.prototype.toTimeString = function()
{
	return this.format("g:i:s A");
}

Date.prototype.toDateTimeString = function()
{
	return this.format("T g:i:s A");
}

Date.prototype.format = function(format) {
	var returnStr = '';
	var replace = Date.replaceChars;
	for (var i = 0; i < format.length; i++) {
		var curChar = format.charAt(i);
		if (replace[curChar])
			returnStr += replace[curChar].call(this);
		else
			returnStr += curChar;
	}
	return returnStr;
};

Date.replaceChars = {
	shortMonths: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
	longMonths: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
	shortDays: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
	longDays: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
	
	// Day
	T: function() {
		var t = new Date();
		if(this.getDate() == t.getDate() && this.getMonth() == t.getMonth() && this.getFullYear() == t.getFullYear())
			return "Today";
		else 
			return this.format("n/d/y")
	},
	d: function() { return (this.getDate() < 10 ? '0' : '') + this.getDate(); },
	D: function() { return Date.replace.shortDays[this.getDay()]; },
	j: function() { return this.getDate(); },
	l: function() { return Date.replace.longDays[this.getDay()]; },
	N: function() { return this.getDay() + 1; },
	S: function() { return (this.getDate() % 10 == 1 && this.getDate() != 11 ? 'st' : (this.getDate() % 10 == 2 && this.getDate() != 12 ? 'nd' : (this.getDate() % 10 == 13 && this.getDate() != 1 ? 'rd' : 'th'))); },
	w: function() { return this.getDay(); },
	// Month
	F: function() { return Date.replace.longMonths[this.getMonth()]; },
	m: function() { return (this.getMonth() < 11 ? '0' : '') + (this.getMonth() + 1); },
	M: function() { return Date.replace.shortMonths[this.getMonth()]; },
	n: function() { return this.getMonth() + 1; },
	// Year
	Y: function() { return this.getFullYear(); },
	y: function() { return ('' + this.getFullYear()).substr(2); },
	// Time
	a: function() { return this.getHours() < 12 ? 'am' : 'pm'; },
	A: function() { return this.getHours() < 12 ? 'AM' : 'PM'; },
	g: function() { return this.getHours() == 0 ? 12 : (this.getHours() > 12 ? this.getHours() - 12 : this.getHours()); },
	G: function() { return this.getHours(); },
	h: function() { return (this.getHours() < 10 || (12 < this.getHours() < 22) ? '0' : '') + (this.getHours() < 10 ? this.getHours() + 1 : this.getHours() - 12); },
	H: function() { return (this.getHours() < 10 ? '0' : '') + this.getHours(); },
	i: function() { return (this.getMinutes() < 10 ? '0' : '') + this.getMinutes(); },
	s: function() { return (this.getSeconds() < 10 ? '0' : '') + this.getSeconds(); },
	// Timezone
	O: function() { return (this.getTimezoneOffset() < 0 ? '-' : '+') + (this.getTimezoneOffset() / 60 < 10 ? '0' : '') + (this.getTimezoneOffset() / 60) + '00'; },
	Z: function() { return this.getTimezoneOffset() * 60; },
	// Full Date/Time
	r: function() { return this.toString(); },
	U: function() { return this.getTime() / 1000; }
}


//================ Map Functions ===============
Map.ClearMap = function()
{
    map.clearOverlays();
}

Map.hide_overlay = function(poly)
{//if (is a marker or the line supports hiding)
	if (poly.getIcon || poly.supportsHide()) 
		poly.hide();
	else 
	{
		map.removeOverlay(poly);
		poly.added = false;
	}
}

Map.show_overlay = function(poly)
{
	if ((poly.getIcon || poly.supportsHide()) && poly.added) 
		poly.show();
	else 
	{
		map.addOverlay(poly);
		poly.added = true;
	}
}

//================ UI Functions ===============
//Get the height of the browser window content area
UI.getWindowHeight = function()
{
    if (window.self && self.innerHeight)
        {return self.innerHeight;}

    if (document.documentElement && document.documentElement.clientHeight)
        {return document.documentElement.clientHeight;}

    return 0;
}

UI.limit_TextField = function(limitField, limitNum) {
    if (limitField.value.length > limitNum) {
        limitField.value = limitField.value.substring(0, limitNum);
    } 
}

if(Include_map)
{
	GLatLngBounds.prototype.getDiagonalDistance = function()
	{
		if (this.isEmpty()) 
			return 0;
		
		var span = this.toSpan();
		var lat = span.lat();
		var lng = span.lng();
		
		return Math.sqrt(lat * lat + lng * lng)
	}
	
	GLatLngBounds.prototype.getMinZoom = function()
	{
		var dist = this.getDiagonalDistance();
		return Math.floor(Search.FULL_TRAIL_MIN_ZOOM - Math.log(dist) / Math.LN2);
	}

	GLatLngBounds.prototype.expand = function(percent)
	{
		var ne = this.getNorthEast(), sw = this.getSouthWest();
		var span = this.toSpan();
		var dist = Math.max(span.lng(), span.lat()) * (percent/2);
		var lng = [sw.lng() - dist,  				ne.lng() + dist];
		var lat = [Math.max(sw.lat() - dist, -90), 	Math.min(ne.lat() + dist, 90)];		
		
		if((span.lng() + dist * 2) > 360){
			lng_left = -180;
			lng_right = 180;
		} 
		
		var bound = new GLatLngBounds(
			new GLatLng(lat[0], lng[0], true),
			new GLatLng(lat[1], lng[1], true));

		return bound;
	}
	
	GLatLngBounds.prototype.toString = function()
	{
		var ne = this.getNorthEast();
		var sw = this.getSouthWest();
		return "{ne:{" + ne.lat() + "," + ne.lng() + "}, sw:{" + sw.lat() + "," + sw.lng() + "}}";
	}
	
}

//--------- Misc Utility Functions ----------
function dhtmlLoadScript(url)
{
   var e = document.createElement("script");
   e.src = url;
   e.type="text/javascript";
   document.getElementsByTagName("head")[0].appendChild(e);
}


//==== Global Objects
var Common;
//The map object from the Google Maps API
var map;
//The trail that is loaded and being edited or viewed
var currentTrail = null;
//The Toolbox Control
var Toolbox;
//The TrailFacts Control
var TrailFacts;
//Array of initialzation methods
var Initializers = [];
//Array of location loading methods
var Loc_Loaders = [];

//Units converter
var Units = new Units_Converter(true, false);

//Array of currently selected points
var SelectedPoints = [];
//Array of points to delete at the next database update
IO._points_to_delete = [];
//Array of Tracks to delete at the next database update
IO._tracks_to_delete = [];
//Array of objects that were serialized for saving
IO._serialized_objects = [];


// ======= Initialization =======
Init.initialize = function()
{
    Common = new Object();
    
	Common.busy_icons = ["msg_ico_busy",
	"msg_ico_positive",
	"msg_ico_error"];
	
    var commonElements = 
    ["footer",
    "footer_top",
    "noncontent"];
	

    if(Include_map)
    {
        commonElements = commonElements.concat(
        ["map_canvas",
		"map_toolbar",
        
		"panels_container",
        "panelhidearrow",
        "panelshowarrow",
        "leftpanel",
        "paneltoggle",
		"paneltabs",
		"save_controls",
		
		"infowindow_basic",
		"segment_info_window",
		
		"autosave_label",
		"autosave_check",
		"button_save",
		
		'hidden_container'
		]);
		
		var find_nodes_with_prefix = function(prefix, array){
			var container = {};
			prefix += "_";
			array.each(function(word){
				container[word] = $(prefix + word);
			});
			return container;
		}
		
 		Common.info_window = find_nodes_with_prefix('infow', $w("title distance loop description zoomtoview viewdetails edit delete alt_change\
			alt_gain alt_loss uphill_dist downhill_dist zoomtoview zoomout add_my_trail"));
		
		var trail_info_names = $w("units_link length alt_change alt_gain alt_loss uphill_dist downhill_dist point_count");
 		Common.segment_info = find_nodes_with_prefix('segment_info', trail_info_names);
 		Common.trail_facts = find_nodes_with_prefix('info', trail_info_names);		
    }

    commonElements.each(function(id) {
        Common[id] = $(id);
    });

	Common.busy_icons = Common.busy_icons.map(function(str){return $(str);});
    
    if(Include_map)
    {
        Common['map'] = Common.map_canvas;
    
        Init.SetupTabs();
    
        //This is needed to fix an IE bug, apparently the window height is
		//reported as X and after resizing the map element, it's reported as X + 20
		UI.AppResized();    
		UI.AppResized();
		
        Init.InitializeMap();
		

		if(!readonlyMode)    
        	Init.SetupAddressButton();
		
        UI.TogglePanel(true);

		Init.InitializeMapLocation();

		Init.Save_Loc_To_Cookie();
    }

	//Round all corners
	$$(".roundy").each(function(e){Rico.Corner.round(e);});	
	$$(".roundy-small").each(function(e){Rico.Corner.round(e, {compact: true});});	

	//Call each method in the Initializers array
	Initializers.each(function(init){init();});

	Init.LoadOrCreateTrail();

    UI.AppResized.curry(true).delay(1);
}

Init.InitializeMapLocation = function()
{
	Loc_Loaders.push(Init.Load_Loc_From_Cookie);

	//Added last = Lowest priority 
	if(Search.search_mode){
		Loc_Loaders.push(function(){
      return Map.MoveToIPLocation(true);
		});
	}

  Loc_Loaders.push(function(){
    map.setCenter(new GLatLng(25.799891, -18.984375), 2, G_PHYSICAL_MAP)
  });
	
	Loc_Loaders.each(function(init){
		if(init())
		{
			Init.location_loaded = true;
			throw $break;
		}
	});	
}

//Setup the event handling for the tabs
Init.SetupTabs = function()
{
	UI.Tabs.list = $H();
	var selected_tab = null;
	
	$$('#paneltabs .tab').each(function(element)
	{
		var tab_name = element.id.substring(4); // get rid of the "tab_"
		UI.Tabs.list.set(tab_name, element);
		
		if(element.hasClassName("tab_on"))
			selected_tab = tab_name;
		
		Event.observe(element, "click", function(e){
			UI.Tabs.select(tab_name);
		});			
	});
	
	//TODO: clean this up (load history on tab click)
	var history_tab = $$('#paneltabs td.tab_history');
	if(history_tab && history_tab.length == 1){
		history_tab = history_tab[0];
		var handler = function(event)
		{
			history_tab.stopObserving('click', handler);
			UI.Tabs.RefreshHistoryTab();
		}
		history_tab.observe("click", handler); 		
	}
	
	if(selected_tab)
    	UI.Tabs.select(selected_tab);
}

//Setup event handling for fields that need to respond to key events
Init.SetupAddressButton = function()
{
	Event.observe($("gotoaddress"), "click", Services.Geocoding.GeoCodeAddress);
	Event.observe($("trail_addressline1"), "keydown", Services.Geocoding.address_KeyDown);
}

Init.Initialize_Tag_Controls = function()
{
	var tags = currentTrail && currentTrail.tags != null;
	$('trail_type_control')[ tags ? 'hide' : 'show']();
	$('trail_tags_controls')[tags ? 'show' : 'hide']();
}

//Setup the map and add the custom controls
Init.InitializeMap = function()
{
    if (!GBrowserIsCompatible())
    {
         //Fail Terribly and inform user gracefully:-(
    }
    
    map = new GMap2(Common.map);
    Common.Geocoder = new GClientGeocoder();
    map.setCenter(new GLatLng(0,0), 2);
	
	map.enableDoubleClickZoom();
    
    map.addMapType(G_PHYSICAL_MAP);
    map.addMapType(Map.CreateMyTopoTileLayer());
    map.addControl(new GLargeMapControl());
    map.addControl(new GScaleControl());
    map.addControl(new GHierarchicalMapTypeControl());
	new GKeyboardHandler(map);
	
	if ($("trail_info")) 
	{
		TrailFacts = new TrailFactsControl();
		map.addControl(TrailFacts);
	}
	
    if(IsEditMode)
        Init.InitializeMapForEditing();
    
	if(Common.map_toolbar)
		Toolbox = new ToolBoxControl();
	
	var marker_map = [6,0, 13,0, 19,6, 19,12, 10,33, 0,12, 0,6];
	
    var icon_array = [
		{name:"Edit_Delete", img:"delete.png", size:[11,11], anchor:[5,5]},

		{name:"Edit_Ghost", img:"ghost.png", size:[13,13], anchor:[6,6]},
		{name:"Edit_Insert", img:"insert.png", size:[13,13], anchor:[6,7]},		
		{name:"Edit", img:"edit.png", size:[13,13], anchor:[6,6]},
		
		{name:"Filtered", img:"filtered.png", transparent:"filtered_trans.png", size:[15,15], anchor:[6,6],
			imageMap:[7,0,  14,7,  7,14, 0,7]},
		{name:"Start", img:"start.png", size:[20,34], anchor:[10,34], transparent:"marker_trans.png", imageMap: marker_map, info_window:[19,0]},
		{name:"End", img:"end.png", size:[20,34], anchor:[10,34], transparent:"marker_trans.png", imageMap: marker_map,  info_window:[19,0]},
		
		{name:"MeasureStart", img:"measure_start.png", size:[19,19], anchor:[9,9],
			transparent:"measure_transparent.png",
			imageMap: [9,0,  18,9,  9,18, 0,9],
			info_window:[18,0]},
		{name:"MeasureEnd", img:"measure_end.png", size:[19,19], anchor:[9,9],
			transparent:"measure_transparent.png",
			imageMap: [9,0,  18,9,  9,18, 0,9],
			info_window:[18,0]}
	];
	

	icon_array.each(function(opts)
	{
		var ico = new GIcon();
	    ico.image = "/images/markers/" + opts.img;
	    ico.iconSize = new GSize(opts.size[0],opts.size[1]);
		ico.iconAnchor = new GPoint(opts.anchor[0],opts.anchor[1]);
		ico.imageMap = opts.imageMap;

		if(opts.transparent)
			ico.transparent = "/images/markers/" + opts.transparent;
		if(opts.info_window)
			ico.infoWindowAnchor = new GPoint(opts.info_window[0],opts.info_window[1]);
			
		UI.Icons[opts.name] = ico;
	})

    UI.Markers.Point = {icon: UI.Icons.Unselected, draggable: true, dragCrossMove: false, bouncy: false};
	UI.Markers.Start = {icon: UI.Icons.Start, draggable: false, dragCrossMove: false, bouncy: false};
	UI.Markers.End = {icon: UI.Icons.End, draggable: false, dragCrossMove: false, bouncy: false};
	UI.Markers.Filtered = {icon: UI.Icons.Filtered, draggable: false, dragCrossMove: false, bouncy: false};	
	
    GEvent.addListener(map, 'moveend', Init.Save_Loc_To_Cookie);
    GEvent.addListener(map, 'zoomend', Init.Save_Loc_To_Cookie);    
    GEvent.addListener(map, 'maptypechanged', Init.Save_Loc_To_Cookie);        
}


Init.LoadOrCreateTrail = function()
{
	//==== Create a New Trail so users can immediately begin editing
	
	if (IsEditMode && window.location.hash && window.location.pathname.indexOf("/trails/") >= 0) 
	{
		IO.LoadTrailForEditing(window.location.hash.slice(1));
	}
	else if (IsEditMode && TrailIDToLoad == null) 
	{
		Init.CreateNewTrail();
	}
}

Init.CreateNewTrail = function(){
	if (IsEditMode) {
		if(currentTrail){
			if (!window.location.pathname.match(/trails\/edit[\/]?$/)) 
			{
				window.location.pathname = 'trails/edit';
				return;
			}
				
			UI.Trail.removeEditableUIForTrail(currentTrail);
			//currentTrail.release_data();
			UI.Map_Edit_Marker_Mover.set_active_trail(null);
		}
		
		if(window.location.hash.length > 1)
			window.location.hash = '#';
		
		currentTrail = Trail.CreateNew();
		currentTrail.CreateTrack();
		currentTrail['private'] = 0;
		
		UI.Privacy_Control.write(currentTrail);
		UI.Privacy_Control.set_readonly(false);
				
		UI.Map_Edit_Marker_Mover.set_active_trail(currentTrail);
		UI.Map_Edit_Marker_Mover.enable_editing();
		
		UI.Trail.createEditableUIForTrail(currentTrail);
    UI.TrackList.setTrail(currentTrail);
		
		Init.Initialize_Tag_Controls();
		
		UI.PushTrailDataToUIFields(currentTrail);
		UI.Show_Wizard_Page('wizard_dialog_1');
	}
}

Init.Load_Loc_From_Cookie = function()
{
    try
    {
		return Init.Load_Loc_From_String(readCookie("loc"));
    }
    catch(e) {
	}
	return false;	
}


Init.Load_Loc_From_String = function(loc_str)
{
    try
    {
		var bounds = null;

        if(loc_str)
        {
            loc_str = loc_str.split(/[|_]/);

			if (!loc_str.any(function(x){return isNaN(parseFloat(x));})) 
				bounds = new GLatLngBounds(new GLatLng(loc_str[0], loc_str[1]), new GLatLng(loc_str[2], loc_str[3]));
        }
		
		if (bounds) 
		{
			map.setCenter(bounds.getCenter(), map.getBoundsZoomLevel(bounds), map.getMapTypes()[loc_str[4]]);
			return true;
		}
    }
    catch(e) {
	}
	return false;
}

Init.Save_Loc_To_Cookie = function()
{
    createCookie("loc", Init.Get_Loc_String(true), 100);
}

Init.Get_Loc_String = function(shrink_bounds)
{
    var bounds = map.getBounds();
	if(shrink_bounds){
		bounds = bounds.expand(-0.05);
	}
	var ne = bounds.getNorthEast();
	var sw = bounds.getSouthWest();
	
    return [RoundDeg(sw.lat()), RoundDeg(sw.lng()), RoundDeg(ne.lat()), RoundDeg(ne.lng()), map.getMapTypes().indexOf(map.getCurrentMapType())].join("_");
}


Init.InitializeMapForEditing = function()
{
	if(IsEditMode) 
	{
		Toolbox = new ToolBoxControl();
	}
}

IO.Callbacks.Feedback_Sent = function()
{
	UI.show_Busy_Message("Submitting Feedback...");
	UI.hide_Message.delay(UI.MSG_DELAY);	
}

IO.Callbacks.Feedback_Complete = function()
{
	UI.show_Positive_Message("Feedback Submitted");
	UI.hide_Message.delay(UI.MSG_DELAY);
}


IO.savingInProcess = false
//================== Saving and Loading ================
// This method could be split up more.
IO.SaveTrail = function(from_autosave, skip_wizard)
{
    if(IO.savingInProcess || (currentTrail && currentTrail.readonly))
        return;
    
    IO.savingInProcess = true;
	
	if(!from_autosave)
		UI.show_Busy_Message("Preparing to Save...");

    UI.PullTrailDataFromUIFields(currentTrail);
    
	if(!from_autosave && currentTrail.custom_route == null && currentTrail['private'] == '0' && skip_wizard != true)
	{
		UI.hide_Message();
		UI.Show_Wizard_Page('wizard_dialog_3');
		var save_trail = IO.SaveTrail.curry(false, true);
		UI.WizardCloseHandlers.push(save_trail);
		UI.WizardCancelHandlers.push(save_trail);
		IO.savingInProcess = false;
		return;
	}
	
    var XML = currentTrail.SerializeToXML();
    var DeleteXML = IO.SerializeDeleteLists();

    if (XML == null && DeleteXML == null) 
	{
		if (!from_autosave) 
		{
			if (currentTrail.stored) 
				UI.show_Positive_Message("No Changes");
			else
				UI.show_Positive_Message("Nothing to save yet");
				
			UI.hide_Message.delay(UI.MSG_DELAY);
		}
		IO.savingInProcess = false;
		return;
	}
        
    if(XML == null) XML = '';
	
    if (DeleteXML == null) 
	{
		DeleteXML = '';
		IO._sent_deletes = null;
	} else 
	{
		//Record the deletes that were sent with this save
		IO._sent_deletes = 
		{
			tracks: IO._tracks_to_delete,
			points: IO._points_to_delete
		};
		
		IO._points_to_delete = [];
		IO._tracks_to_delete = [];
	}
        
    var request = new Ajax.Request(getDomain() + "/trails/save", 
    {
        method:'post',
        onSuccess: function(response)
		{
			IO.Callbacks.SuccessfulSave(response);
			Common.autosave_label.update("saved at: " + (new Date().toTimeString()));
			new Effect.Highlight(Common.autosave_label, {duration:2});
			$('share_this_trail_link').show();
		},
        onFailure: IO.Callbacks.FailedSave,
        onComplete: IO.Callbacks.SaveComplete,
        parameters: {xml:"<doc>" + XML + DeleteXML + "</doc>"}
    });
	
	UI.show_Busy_Message("Saving...");	
    
    request.trail = currentTrail;
}

IO.DownloadCurrentTrail = function(format){
	var message = UI.Beforeunload_Event_Handler();
	
	if(message != null && !confirm(message + '  Do you want to download anyway?'))
		return;
		
	var url = '/trails/export?id=' + currentTrail.id + '&format=' + format;
	if(VersionToLoad)
		url += '&version=' + VersionToLoad;

	// If there were unsaved changes open in a new window so we don't get asked again.
	if(message)
		window.open(url, 'download',  'width=200, height=150');
	else
		window.location.href = url;
}

IO.LoadSingleTrail = function(id, readonly, version)
{			
	readonly |= false;
	params = {id: id}
	
	UI.show_Busy_Message("Loading trail...");
	
    var url = getDomain();
	
	if(version){
		url += "/trails/get_historical";
		params.version = version;
		readonly = true;
	}else{
		url += "/trails/get";
    }
	
    var request = new Ajax.Request(url, 
    {
        method:'get',
        onSuccess: IO.Callbacks.Handle_LoadTrail_Response,
        onFailure: IO.Callbacks.Handle_Failed_Load,
        parameters: params
    });
	
	request.readonly = readonly;
}

IO.LoadTrailForEditing = function(id)
{
	IO.LoadSingleTrail(id);
	Common.autosave_check.checked = false;
	IO.Autosave.Enable();			
}



IO.LoadMyTrails = function()
{
	UI.show_Busy_Message("Loading My Trails...");
	    
    var url = getDomain() + "/trails/mytrails";
        
    var request = new Ajax.Request(url, 
    {
        method:'get',
        onSuccess: IO.Callbacks.Handle_LoadMyTrails_Response,
        onFailure: IO.Callbacks.Handle_Failed_Load,
        parameters: {id: id}
    });
}

IO.AddToMyTrails = function(trail){
	new Ajax.Request('/mytrails/add/' + trail.id,
		{asynchronous:true, evalScripts:true,
		 onSuccess:function(request){
		 	alert('Trail successfully added to My Trails.');}
		});
	return false;	
}



//=========  Autosaver  =========
IO.Autosave = new (function() 
{
	var executer = null;
	var enabled = false;
	
	function create_executor()
	{
		if(executer)
			executer.stop();

		executer = new PeriodicalExecuter(callback, IO.AUTOSAVE_FREQUENCY);
	}
	
	this.Stop = function()
	{
		if(executer)
			executer.stop();
			
		executer = null;
	};
	
	this.Disable = function(){enabled = false;};
	this.Enable = function()
	{
		if(!executer)
			create_executor();
			
		enabled = true;
	};	
	
	var callback = (function()
	{
		if (enabled && 
			currentTrail != null && !currentTrail.readonly &&
			Common.autosave_check && Common.autosave_check.checked) 
		{
			IO.SaveTrail(true);
		}
	}).bind(this);
	
	return this;
})();

//Adds a Trail,Track, or Point to the list of objects to be saved
//This enables us to roll back the dirty flag in-case the save doesn't work
IO.AddToSaveList = function(obj)
{
	IO._serialized_objects.push(obj);
	obj.setDirty(false);
}

//Called when a save fails, re-sets all the dirty flags to true
IO.RollBackLastSave = function()
{
	IO._serialized_objects.each(function(obj)
	{
		obj.setDirty(true);
	});
	
	IO._serialized_objects.length = 0;
	
	if (IO._sent_deletes) 
	{
		IO._points_to_delete = IO._points_to_delete.concat(IO._sent_deletes.points);
		IO._tracks_to_delete = IO._tracks_to_delete.concat(IO._sent_deletes.tracks);
	}
	
	IO._sent_deletes = null;
}

//============   Save Callbacks
IO.Callbacks.SuccessfulSave = function(response)
{
	UI.show_Busy_Message("Cleaning Up...");
	
	IO._serialized_objects.each(function(obj)
	{
		obj.dirty = false;
		obj.stored = true;
	});
	IO._serialized_objects.length = 0;
	IO._sent_deletes = null;	
		
	IO.setDirty(false);

	var path = window.location.pathname;
	if(path.endsWith("trails/edit") || path.endsWith("trails/edit/"))
		window.location.hash = response.request.trail.id;
		
	response.request.trail._isCompletelyLoaded = true;
    
	UI.show_Positive_Message("Done");	
}

IO.Callbacks.FailedSave = function(response)
{
	if(response.responseText && response.responseText.length < 100)
		UI.show_Error_Message(response.responseText);
	else
	    UI.show_Error_Message("There was a problem saving, we'll try again.");
		
	IO.RollBackLastSave();
}

IO.Callbacks.SaveComplete = function()
{
    IO.savingInProcess = false;
	UI.hide_Message.delay(UI.MSG_DELAY);
}
//========================


IO.Callbacks.Handle_LoadTrail_Response = function(response)
{
	UI.show_Busy_Message("Processing...");	
	
    //try
    //{
        currentTrail = Trail.ReadFromXML(response.responseXML.getElementsByTagName("t")[0]);
    
		if (response.request.readonly) 
		{
			Map.Overlay_Manager.Add_Trail(currentTrail);
			Map.Overlay_Manager.Force_Trail_Visibility(currentTrail, true);
			if (TrailIDToLoad == currentTrail.id && !IsEditMode) {
				UI.UpdateTrailFacts(currentTrail.get_primary_track());
				UI.Map_Edit_Marker_Mover.set_active_trail(currentTrail);
				UI.Map_Edit_Marker_Mover.enable();
				UI.TrackList.setTrail(currentTrail);
			}
		} else 
		{
			if (IsEditMode) 
			{
				if (currentTrail.created_by_user_id != User_ID && currentTrail['private'] > 0) 
				{
					alert("This trail is a private trail from another user, it can't be saved.");
					Common.button_save.hide();
					Common.autosave_check.checked = false;
					Common.autosave_check.disable();
					IO.Autosave.Disable();
					IO.Autosave.Stop();
				}
				
				UI.TrackList.setTrail(currentTrail);
				
				UI.Map_Edit_Marker_Mover.set_active_trail(currentTrail);
				UI.Map_Edit_Marker_Mover.enable_editing();
				
				UI.Trail.createEditableUIForTrail(currentTrail);
				
				Init.Initialize_Tag_Controls();
			}			
		}
		
		var add_to_link = $("add_to_my_trails");
		if (add_to_link) 
			add_to_link[currentTrail.created_by_user_id != User_ID ? "show" : "hide"]();
		
		$('share_this_trail_link').show();
		
        UI.PushTrailDataToUIFields(currentTrail);
    
		currentTrail.Recalculate_Extents();
        Map.RecenterView(currentTrail);
		
		/*
    }
    catch(e)
    {
        GLog.write(e.name + ": " + e.message);
        GLog.write(e.stack);        
    }
	*/
	
	UI.show_Positive_Message("Loaded");	
	UI.hide_Message.delay(UI.MSG_DELAY);
}

IO.Callbacks.Handle_LoadMyTrails_Response = function(xhr)
{
	UI.show_Busy_Message("Processing...");	
	
    try
    {
        MyTrails = Search.ReadResults(response.responseXML);
    }
    catch(e)
    {
        GLog.write(e.name + ": " + e.message);
        GLog.write(e.stack);        
    }
	
	UI.show_Positive_Message("Loaded");	
	UI.hide_Message.delay(UI.MSG_DELAY);
}

IO.Callbacks.Handle_Failed_Load = function(xhr)
{
    UI.show_Error_Message("There was a problem loading trails, we'll try again.");	
	UI.hide_Message.delay(UI.MSG_DELAY);	
}


IO.SerializeDeleteLists = function()
{
    var XML = [];

    if(IO._tracks_to_delete.length == 0 && IO._points_to_delete.length == 0)
        return null;
        
    XML.push("<delete>");
    
    IO._tracks_to_delete.each(
        function(id) {
            XML.push("<track>" + id + "</track>")});

    IO._points_to_delete.each(
        function(id) {
            XML.push("<p>" + id + "</p>")});

    XML.push("</delete>")
    
    return XML.join('');
}

//This should be called when ANYTHING gets dirty,
//or when EVERYTHING is clean
IO.setDirty = function(dirty)
{
	if(dirty == IO.anythingDirty)
		return;	
	
	if(dirty)	
	{
		IO.Autosave.Enable();
		IO.anythingDirty = true;	
	}
	else
	{
		IO.anythingDirty = false;	
	}	
}

Map = Object.extend(new function()
{
	//================== Map Event Handling ===================
	this.RecenterView = function(trail)
	{
		if (trail && trail.bounds) 
			Map.CenterViewOnBounds(trail.bounds, 0.05);
	}
	
	this.CenterViewOnBounds = function(bounds, expand_by_percent)
	{
		if(expand_by_percent)
			bounds = bounds.expand(expand_by_percent);
		
		if(bounds)
		{
			var zoom = map.getBoundsZoomLevel(bounds);
			map.setCenter(bounds.getCenter(), zoom);
		}
	}

  this.MoveToIPLocation = function(set_map_type){
    if(google.loader.ClientLocation){
      var loc = google.loader.ClientLocation;
      map.setCenter(new GLatLng(loc.latitude, loc.longitude), 10, set_map_type ? G_PHYSICAL_MAP : undefined);
      return true;
    }
    return false;
  }

  this.CreateMyTopoTileLayer = function(){
    //From http://www.mytopo.com/google/mytopo.js
    //Which is referenced at http://www.mytopo.com/maps/index.cfm
    GetMyTopoTile=function(a,b,c) {
      var lURL=this.myBaseURL;
      lURL+=b;
      lURL+="/"+a.x;
      lURL+="/"+a.y+".jpg";
      return lURL;
    }

    var mytopoCopy = new GCopyright(1,new GLatLngBounds(new GLatLng(-90, -180), new GLatLng(90, 180)),0,'&#169; MyTopo <a href="http://www.mytopo.com/google/index.cfm" target="_blank"><b>(Topo Maps Terms of Use)</b></a>')

    var mytopoc = new GCopyrightCollection('');
    mytopoc.addCopyright(mytopoCopy);

    var tileMytopo = new GTileLayer(mytopoc,9,15);
    tileMytopo.myLayers='topoG';
    tileMytopo.myFormat='image/jpg';
    tileMytopo.myBaseURL='http://maps.mytopo.com/wikiwalki/tilecache.py/1.0.0/topoG/';

    tileMytopo.getTileUrl=GetMyTopoTile;

    var mytopoLayer=[tileMytopo];

    return new GMapType(mytopoLayer, G_SATELLITE_MAP.getProjection(), "MyTopo", G_PHYSICAL_MAP);
  };



	//============== Overlay Creators ================
	this.Overlays = new function()
	{
		function get_z_index(marker){
			return marker.z_index;
		}
		

		this.newMarker = function(latlng)
		{
			var mark = new GMarker(latlng, UI.Markers.Point);
			return mark;
		}
		
		this.newLine = function(points, color)
		{
			//Line-color will eventually be changeable via user preference or trail/track type
			return new GPolyline(points, color, LINE_WIDTH, 1, {clickable:false});
		}
		
		this.newLineW = function(points, color, width)
		{
			//Line-color will eventually be changeable via user preference or trail/track type
			return new GPolyline(points, color, width, .3);
		}
		
		this.createMarker = function(opts, latlng)
		{
			opts.zIndexProcess = get_z_index;
			var mark = new GMarker(latlng || new GLatLng(0, 0), opts);
			mark.z_index = opts.z_index;
			map.addOverlay(mark);
			if(!latlng) mark.hide();
			return mark;
		}
	}();
}(), Map);


//================== Elevation ================
//We created a local proxy so that we can execute requests from a different domain (gisdata.usgs.net)
Services.Elevation = new (function(){
	var queue = [];
	var point_hash = new Hash();
	var waiting_for_response = false;
	var requester = null;
	this.enabled = true;
	
	function request_next_point()
	{
		if(waiting_for_response) return;
		waiting_for_response = true;
		
		var point = queue.pop();
		start_or_stop_requester();
			
		if(readonlyMode || UseElevation < 1)
			return;

	    //var url = "http://gisdata.usgs.net/XMLWebServices/TNM_Elevation_Service.asmx/getElevation";
	    //var url = "http://dsb.homedns.org:1600/mytesting/services/elevation.php?";
	    var url = getDomain() + "/services/elevation.php";
				
	    var request = new Ajax.Request(url, 
	    {
	        method:'get',
	        onSuccess: function(response){
			    point.setAltitude(parseFloat(response.responseText));
				track.notifyPointsElevationChanged(point);
			},
			onComplete: function(){
				waiting_for_response = false;
				point_hash.unset(point.id);
			},
	        parameters:
	            {service:UseElevation, lat:point.latlng.lat(), lng:point.latlng.lng()}
	    });
	
	    request.point = point;
	}

	this.GetForPoint = function(point){
		if(!this.enabled) return;
		if(point_hash.get(point.id)) return;
			
		queue.push(point);
		point_hash.set(point.id, point);
		start_or_stop_requester();
	}
	
	function start_or_stop_requester(){
		if(queue.length > 0 && !requester){
			requester = new PeriodicalExecuter(request_next_point, 0.5);
		}else if(queue.length == 0 && requester){
			requester.stop();
			requester = null;
		}
	}
})();


//==================  Geocoding  ===================
Services.Geocoding.address_KeyDown = function(event)
{
    if(event.keyCode == 13)
        Services.Geocoding.GeoCodeAddress();
}

//Geocode the address currently in the addressline1 text box
Services.Geocoding.GeoCodeAddress = function()
{
    address = $F("trail_addressline1");
    
    if(address.length > 1)
        Common.Geocoder.getLocations(address, Services.Geocoding.Callbacks.GeoCoderCallback);
}

//This is called when the Geocode server responds
Services.Geocoding.Callbacks.GeoCoderCallback = function(response)
{
    var err_msg;
    if(!response || response.Status.code != 200)
    {
        if(!response)
            err_msg = "Couldn't connect to Google.";
        else
        {
            code = response.Status.code;
            if(code == 400)
                err_msg = "The Address is not properly formatted";
            else if(code == 500)
                err_msg = "Google's server is having a problem";
            else if(code == 602)
                err_msg = "Address not found";
            else
                err_msg = "Can't find the address right now";                
        }
    }
    else
    {
        //Extract the coordinates from the response
        place = response.Placemark[0];
        point = new GLatLng(place.Point.coordinates[1],place.Point.coordinates[0]);
        
        //Cheap hack to get an estimate of how close we are supposed to zoom.
        accuracy = place.AddressDetails.Accuracy;
        map.setCenter(point, Math.floor(accuracy * 2 + 2));
    }
    
    // Display the error if there was one
    $("addr_err_msg").update(err_msg);    
}

Services.geoLocateUser = function(opts){
	if(Services.geoLocating)
		return;
	Services.geoLocating = true;
	dhtmlLoadScript('http://j.maxmind.com/app/geoip.js');
	var start_time = (new Date()).getTime();

	window.geoip_longitude = false;
	new PeriodicalExecuter(function(pe){
		if(window.geoip_longitude){
			pe.stop();
			if(opts.onSuccess) opts.onSuccess();
			
		}else if (((new Date).getTime() - start_time) > 3000){
			pe.stop();
			if(opts.onFailure) opts.onFailure();
			
		}else return;
		
		Services.geoLocating = false;
		if(opts.onComplete) opts.onComplete();
		
	}, 0.25); // check every 1/4 sec
	return true;
};



// ======== UI =========
//This function is called every time the browser is resized
UI.AppResized = function (force_resize)
{
    if(!Common || !Common.map)
        return;
		
    //Calculate the vertical distance between the top of the map and the top of the content area of the browser
    var mapOffsetTop = Common.map.cumulativeOffset().top;
    var panelOffsetTop = Common.leftpanel.cumulativeOffset().top;
	
    //Calculate the height of the map area
    var window_height = UI.getWindowHeight();
	if (force_resize || window_height != UI._last_window_height) 
	{
		UI._last_window_height = window_height;
		
		var map_height = window_height - mapOffsetTop - (MAP_BOTTOM_MARGIN + FOOTER_HEIGHT);
		
		if (map_height >= 0) 
		{
			//Set the height of the map
			var height_style = {height: map_height + "px"};
			
			Common.map.setStyle(height_style);
			Common.paneltoggle.setStyle(height_style);
			
			//The height of the entire left panel
			Common.leftpanel.setStyle({height: (3 + window_height - panelOffsetTop - FOOTER_HEIGHT) + "px"});
			
			//Set all of these to the same value
			var top = {top: (map_height / 2) + "px"};
			Common.panelhidearrow.setStyle(top)
			Common.panelshowarrow.setStyle(top);
			
			//Set the height of the tab panels but leave room for the tabs themselves
			var panels_top = Common.panels_container.cumulativeOffset().top;
			Common.panels_container.setStyle({height: (3 + window_height - panels_top - FOOTER_HEIGHT) + "px"});
			//The 3 above is a mystery
		}
	}
	   
    //Tell the Map it has been resized
    if (map)
	{
		map.checkResize();
	
		//Custom map resized event
		GEvent.trigger(map,"resized");
	}
}

Initializers.push(function(){
	$$(".menu_container").each(function(element){
  		new UI.Menu(element.id + "_btn", element.id);
	});

  var locate_me_link = $('locate_me');
  if(locate_me_link)
    locate_me_link.observe('click', function(){Map.MoveToIPLocation(false); return false;});
});

//Show or hide the panel on the left side of the page
UI.TogglePanel = function(force_show)
{
    if(!Common)
        return;
        
	var toolbar = Common.map_toolbar;
    var panels = Common.panels_container;
    var mapdiv = Common.map;
    var toggle = Common.paneltoggle;
    var hidearrow = Common.panelhidearrow;
    var showarrow = Common.panelshowarrow;

    if(force_show || panels.style.display == "none")
    {
        Common.leftpanel.show();
        panels.show();
		
        if(toolbar) toolbar.setStyle({marginLeft: "20em"});        
        mapdiv.setStyle({left: "20em", marginRight: "20.5em"});
        toggle.setStyle({left: "20em"});
        Common.footer_top.setStyle({marginLeft: "20em"});
        
        hidearrow.show();
        showarrow.hide();
    }
    else
    {
        Common.leftpanel.hide();
        panels.hide();
        
        if(toolbar) toolbar.setStyle({marginLeft: 0});		
        mapdiv.setStyle({left: 0, marginRight: "0.5em"});
        toggle.setStyle({left: 0});
        Common.footer_top.setStyle({marginLeft: 0});
                     
        hidearrow.hide();
        showarrow.show();
    }

    map.checkResize(); 
}

UI.Tabs = {
	RefreshHistoryTab: function(e)
	{
		var id = TrailIDToLoad || (currentTrail.stored && currentTrail.id);
		//if(!id) return;
		new Ajax.Updater('history_tab_content', '/edit_histories/list_for_tab', 
		{
			asynchronous: true,
			evalScripts: false,
			method: "get",
			parameters: {trail_id: id}
		});
	}
};

//This is called when a user clicks on one of the tabs
UI.Tabs.select = function(selected){
	UI.Tabs.list.each(function(pair){
        $("panel_" + pair.key).className = (pair.key == selected ? "panel": "panel_hidden");
        pair.value.className = (pair.key == selected ? "tab_on": "tab_off");
	});
}


UI.closeLightbox = function()
{
    if(!Object.isUndefined(UI.Generic_Lightbox))
        UI.Generic_Lightbox.deactivate();
}

UI.show_Busy_Message = function(message)
{
	$(Common.busy_icons).invoke("hide");
	$('msg_ico_busy').show();
	
    $('busy_message_txt').update(message); 
    $('busy_message').show();
}

UI.show_Error_Message = function(message)
{
	$(Common.busy_icons).invoke("hide");
	$('msg_ico_error').show();

    $('busy_message_txt').update(message); 
    $('busy_message').show();
}

UI.show_Positive_Message = function(message)
{
	$(Common.busy_icons).invoke("hide");
	$('msg_ico_positive').show();
	
    $('busy_message_txt').update(message); 
    $('busy_message').show();
}

UI.hide_Message = function()
{
    $('busy_message').hide();
}

UI.Exit_Edit_Mode = function()
{
	if(!IsEditMode)
		return;

	var url = window.location.href;
	
	if(!currentTrail.stored)
		window.location.pathname = '/search';
	else{
		try {window.location.href = url.replace(/\/edit\/#?/, '/view/');} 
		catch (e) {}//when the user cancels navigation away from the page, an exception is thrown
	}
}

UI.Beforeunload_Event_Handler = function()
{
	if(!IsEditMode || currentTrail == null || currentTrail.readonly)
		return;
	
    if(IO.savingInProcess)
        return "We're in the middle of saving a trail...";
	
    UI.PullTrailDataFromUIFields(currentTrail);
	 
    if (currentTrail.Has_Unsaved_User_Changes()) 
		return "There are changes that haven't been saved.";
}

UI.Rating_Field_Clicked = function(field, index)
{
	var img = window[field + "_img"];
	
	$R(1,10).each(function(i){
		var rating_img = $(field + "_" + i);
		if(!rating_img) throw $break;
		rating_img.src = (i <= index) ? img.full : img.empty;
	});
	
	var element = $(field)
	if(element)
		element.value = (index == null || typeof(index) == 'undefined') ? '' : index;
};

UI.TrailType_Click = function(type){
	var tags = '';
	switch(type){
		case 'road': tags = 	'va ma pv rb ba ru wr'; break;
		case 'trail': tags = 	'nv nm di pa ba mb hi ru wa dt'; break;
		case 'paved': tags = 	'nv nm pv pa ba rb ru dt wa'; break;
	}
	tags = $w(tags);
	
	UI.tags_multi_selects.each(function (ms){
		tags.each(function(tag){ms.addItemById(tag);});
	});
	
	if ($('trail_tags_controls').visible() == false){
		new Effect.BlindUp('trail_type_control');
		new Effect.BlindDown('trail_tags_controls');
	}
}


//================= UI Form Fields -> JS objects ===================
//Fields that will be copied straight from user entered text boxes,
//i.e. those that need no additional processing
UI.Trail_UIFields_To_Update = [
    "title", 
    "description",
    "approach_description",
    "addressline1",
	"difficulty" 	
    ];

//Fields that will be translated directly from the JS object to XML attributes
//upon Save
IO.Trail_Fields_Attributes = [
    "min_lat",
    "max_lat",
    "min_lng",
    "max_lng",
    "start_lat",
    "start_lng",
    "end_lat",
    "end_lng",
	"tags",
	"difficulty",
	"private",
	"custom_route",
	"primary_track_id"
    ];
	
IO.Trail_Fields = new Hash();

IO.Trail_Fields_Attributes.each(function(field){
	var info = IO.Trail_Fields.get(field) || {};
	info.attr = true;
	IO.Trail_Fields.set(field, info);
});

UI.Trail_UIFields_To_Update.each(function(field){
	var info = IO.Trail_Fields.get(field) || {};
	info.txt = true;
	IO.Trail_Fields.set(field, info);	
});

    
UI.PullTrailDataFromUIFields = function(T)
{
	if(T.readonly)
		return;
		
    UI.Trail_UIFields_To_Update.each(function(field)
    {
		var val = $F('trail_'+ field);

		if(val == "" || (val.blank && val.blank()))
			val = null;

        T.set(field, val, true);
    });
	
	if(!UI.Privacy_Control.get_readonly())
		T.set('private', UI.Privacy_Control.read(), true);

	T.set('custom_route', UI.custom_route_button.getSelected(), true);
	
	//=== Tags ===
	var tags = [];
	UI.tags_multi_selects.each(function(multi)
	{
		tags.push(multi.getSelectedIds());
	});
	
	tags = tags.flatten();

	if(tags && tags.length > 0)
		T.set("tags", "|" + tags.join("|") + "|", true);
	else
		T.set("tags", null, true);
}


//================= JS objects -> UI Form Fields ===================
//This only happens once when the trail is first loaded

UI.PushTrailDataToUIFields = function(T)
{
	//Perhaps we should add a form field watcher that would set a
	//dirty flag whenever the forms content has changed
	if(!T.readonly)
		IO.Autosave.Enable();
		
    UI.Trail_UIFields_To_Update.each(function(field)
    {
        value = T[field];
        if(value == null)
            value = "";
		
		var element = $('trail_'+ field);
		
		if (element) 
		{
			if (element.getValue) 
				element.value = value;
			else 
				element.update(value);
		}
    });

	UI.Privacy_Control.set_readonly(T.readonly || (T.stored && User_ID != T.created_by_user_id) || !IsEditMode);

	UI.Privacy_Control.write(T);

	UI.Rating_Field_Clicked("trail_difficulty", T["difficulty"]);
	
	UI.custom_route_button.setSelected(T['custom_route']);

	if(T.updated_at)
	    $("trail_updated_at").update(T.updated_at.toDateTimeString());
	
	if (T.tags) 
	{
		var tag_ids = T.tags.split('|');
		UI.tags_multi_selects.each(function(multi)
		{
			multi.setSelectedIds(tag_ids);
		});
	}

}


UI.UpdateTrailFacts = function(track, dont_recalc)
{
	UI._updateTrailFacts(Common.trail_facts, track.stats);
}


UI._updateTrailFacts = function(info, stats)
{
	if(!stats)
		return;
		
	if(info.units_link)
	    info.units_link.update(Units.useMetric ? "(standard)" : "(metric)");

	info.length.update(Units.length(stats.distance));
    info.alt_change.update(Units.length(stats.alt_change, 1));
    info.alt_gain.update(Units.length(stats.alt_gain, 1));
    info.alt_loss.update(Units.length(stats.alt_loss, 1)); 
    info.uphill_dist.update(Units.length(stats.uphill_dist)); 
    info.downhill_dist.update(Units.length(stats.downhill_dist)); 
    info.point_count.update(stats.point_count); 
}

UI.send_Share_Trail_Email = function()
{
	$("share_form").request({parameters: {id: currentTrail.id}});
	UI.closeWizard();
}



UI.Show_Wizard_Page = function(div){
	UI.Generic_Lightbox.showLightboxWithElement($(div));
}

UI.clearWizardHandlers = function(){
	UI.WizardCancelHandlers = [];
	UI.WizardCloseHandlers = [];
}

UI.cancelWizard = function(){
	UI.closeLightbox();
	UI.WizardCancelHandlers.each(function(handler){handler();});	
	UI.clearWizardHandlers();
}

UI.closeWizard = function(){
	UI.closeLightbox();
	UI.WizardCloseHandlers.each(function(handler){handler();});
	UI.clearWizardHandlers();	
}

UI.clearWizardHandlers();

//TODO Change the name of this class!
UI.Map_Edit_Marker_Mover = new (function(){
	var me = this,
		active_trail = null,
		track_observers = [],
		enabled = false,
		editing_enabled = false,
		events_registered = false,
		max_edit_distance = 100*100, //distances are squared so we don't have to do square roots
		min_line_length_for_ghost = 20*20,
		mode = 'pan',
		allowed_modes = $w('delete edit move pan measure select add_track').inject({}, function(hash, value){hash[value] = true; return hash;}),		
		dragging = false,
		insertion_end = null,
		Markers = {terminals:new Hash()},
		next_z_index = 0,
		last_map_bounds = null,
		info_window_open = false,
		trail_segment = null,
		trail_segment_display = null;
		
	DefineEvents(this, 'mode_changed');

	if(!Include_map)
		return;
		
	Initializers.push(function(){
		trail_segment = new TrailSegment();
		trail_segment_display = new UI.TrailSegmentInterface();
		trail_segment_display.setTrailSegment(trail_segment);
		
		var marker = Map.Overlays.createMarker;
		Markers.Insert = marker({icon: UI.Icons.Edit_Insert, z_index: 0});

		var mark = Markers.Delete = marker({icon: UI.Icons.Edit_Delete, draggable: true, z_index:1});
		GEvent.bind(mark, "click", this, marker_delete_click);
		add_listener(mark, 'dragstart', marker_drag_start);
		add_listener(mark, 'dragend', marker_drag_end);

		var ghost = Markers.Ghost = marker({icon: UI.Icons.Edit_Ghost, draggable: true, z_index:2});
		add_listener(ghost, "dragstart", marker_drag_start);
		add_listener(ghost, "dragend", marker_drag_end);

		Markers.Edit = marker({icon: UI.Icons.Edit, draggable: true, z_index:3});
		GEvent.bind(Markers.Edit, "dragstart", this, function(){marker_drag_start(Markers.Edit);});
		GEvent.bind(Markers.Edit, "dragend", this, function(){marker_drag_end(Markers.Edit);});

		Markers.MeasureStart = marker({icon: UI.Icons.MeasureStart, draggable: false, clickable: false, z_index:6});
		Markers.MeasureEnd = marker({icon: UI.Icons.MeasureEnd, draggable: false, clickable: false, z_index:7});		

		Markers.moveable_markers = $w('Ghost Edit Delete MeasureStart MeasureEnd').map(function(marker_name){return Markers[marker_name]});
		me.set_mode('pan');
	});
	
	function make_start_and_end_markers(track){
		var markers = Markers.terminals.get(track.id) ||
						{start: create_terminal_marker(UI.Icons.Start),
						end: create_terminal_marker(UI.Icons.End)};
		Markers.terminals.set(track.id, markers);
		return markers;
	}
	
	function release_start_and_end_markers(track){
		var markers = Markers.terminals.get(track.id);
		
		if (markers) {
			release_terminal_marker(markers.start);
			release_terminal_marker(markers.end);
			
			Markers.terminals.unset(track.id);
		}
	}

	function register_event_handlers(){
		if(events_registered)
			return;
		events_registered = true;
		
		GEvent.bind(map, "mousemove", this, mouse_move_handler);
		GEvent.bind(map, "click", this, map_click_handler);
		GEvent.bind(map, 'infowindowopen', this, function(){
			info_window_open = true;
		});
		GEvent.bind(map, 'infowindowclose', this, function(){
			(function(){
				info_window_open = false
			}).delay(0.5);
		});		
	}
	
	
	this.set_active_trail = function(in_trail){
		if(active_trail == in_trail)
			return;
		
		if (active_trail) {
			track_observers.each(function(to) {
				to.observers.invoke('stop');
			});
			track_observers = [];
			
			active_trail.tracks.each(release_start_and_end_markers);
		}

		active_trail = in_trail;
		trail_segment.setTrack(active_trail.get_primary_track());
		
		if(in_trail){
			in_trail.tracks.each(function(track){
				observe_track_events(track);
				make_start_and_end_markers(track);
				move_endpoint_markers(track);
			});
			
			insertion_end = Markers.terminals.get(in_trail.tracks.first().id).end;
		} else {
			track_observers = [];
		}
		
		for (var marker in Markers)
			if (Markers[marker].hide) Markers[marker].hide();
	}
	
	function observe_track_events(track){
		if(track_observers.any(function(to){to.track == track;}))
			return;
			
		track_observers.push({track:track, observers:[
			track.pathChanged.observe(pathChanged_handler),
			track.pointsRemoved.observe(pointsRemoved_handler),
			track.destroyed.observe(trackDestroyed_handler)]
		});		
	}
	
	this.set_mode = function(in_mode){
		check_mode(in_mode);
			 
		if(mode_is('measure','select') && mode != in_mode)
			trail_segment.reset();
			
		mode = in_mode;

		if(!mode_is('edit') || !(active_trail && active_trail.hasPoints()))
			Markers.Insert.hide();
		else{
			var endpoints = active_trail.tracks.map(function(t){return [t.points.first(), t.points.last()];}).flatten();
			var point_is_still_end = endpoints.include(Markers.Insert.point);
			var insertion_point = point_is_still_end ? Markers.Insert.point : active_trail.tracks.first().points.last();
			move_marker_to_point(Markers.Insert, insertion_point);
		}

		[Markers.Delete, Markers.Edit, Markers.Ghost, Markers.MeasureEnd, Markers.MeasureStart].invoke('hide');
					
		Markers.all_terminals().invoke(mode_is('edit', 'move', 'delete') ? 'enableDragging' : 'disableDragging');
		
		this.mode_changed.fire({new_mode: mode});
	}
	
	Markers.all_terminals = function(){
		var terminals = [];
		Markers.terminals.each(function(pair){
			terminals.push(pair.value.start);
			terminals.push(pair.value.end);
		});
		return terminals;		
	}
	
	this.disable_editing = function(){
		Markers.Ghost.hide();
		Markers.Edit.hide();
		Markers.Insert.hide();
		editing_enabled = false;
	}
	
	this.enable_editing = function(){
		editing_enabled = true;
		this.enable();
	}

	this.disable = function(){
		enabled = false;
		this.disable_editing();
	}	
		
	this.enable = function(){
		enabled = true;
		register_event_handlers();		
	}
	
	this.hasSelectedSegment = function(){
		return trail_segment.hasStartEnd();
	}
	
	this.getSelectedSegment = function(){
		return trail_segment;
	}

	//=======  Track Event handlers =======
	function pathChanged_handler(changed_points){
		var track = changed_points.start.track;
		if(track) move_endpoint_markers(track);
	}
	function pointsAdded_handler(info){
		
	}
	function pointsRemoved_handler(removed_points){
		if (mode_is('select')){
			hide_other_markers();
		}
	}
	
	function trackDestroyed_handler(track){
		release_start_and_end_markers(track);

		var to = track_observers.find(function(to){return to.track == track;});
		if(to) to.observers.invoke('stop');
		track_observers = track_observers.without(to);
	}

	//=======  Marker Event Handlers =======	
	function marker_drag_start(marker){
		dragging = true;	
	}

	function marker_drag_end(marker){
		dragging = false;
		
		if(!editing_enabled || !mode_is('edit', 'delete', 'move'))
			return;		
		
		if(marker == Markers.Ghost)
		{
			var track = marker.first_point.track;
            var new_point = track.addPointAt(marker.getLatLng(), track.points.indexOf(marker.first_point)+1);
			marker.first_point = null;
			
		} else if (marker && marker.point)
		{
			var track = marker.point.track;			
			var markers = Markers.terminals.get(marker.point.track.id);
			
		    //Tell the point object it has been moved
		    marker.point.setLatLng(marker.getPoint());
			marker.point._pix_clean = false;
			
		    marker.point.track.notifyPointsMoved(marker.point);
		
		    Services.Elevation.GetForPoint(marker.point);

			if(marker == markers.start || marker == markers.end)
			{
				move_endpoint_markers(track);
				
				if(mode_is('edit','move') && insertion_end == marker)
					move_marker_to_point(Markers.Insert, marker.point);

				if(mode_is('delete'))
					move_marker_to_point(Markers.Delete, marker.point);
			}
			else if (marker == Markers.Delete) 
			{
				if(!marker.point.prev)
					move_marker_to_point(markers.start, marker.point);
				if(!marker.point.next)
					move_marker_to_point(markers.end, marker.point);
			} else 
				marker.point = null;
		}
	}
	
	function marker_delete_click(){
		var delete_point = Markers.Delete.point;

		if (delete_point && mode_is('delete') && editing_enabled && !dragging) 
		{
			if(delete_point.track == active_trail.get_primary_track() && delete_point.track.points.length <= 1){
				alert("You can't delete the last point of the primary track, though you can move it.");
				return;
			}
			
			if(delete_point.next)
				delete_point.next._pix_clean = false;
			
			delete_point.track.removePoint(delete_point);
			Markers.Delete.hide();
		}

		Markers.Delete.point = null;
	}
	
	function click_terminal_point(marker){
		if (mode_is('edit')) 
		{
			move_marker_to_point(Markers.Insert, marker.point);
			insertion_end = marker;
		}
	}
	
	function trail_segment_mode_click(){
		if(!mode_is('measure','select') || info_window_open) return;
		
		var ts = trail_segment;
		ts.setTrack(Markers.MeasureStart.point.track);
		
		if (!ts.start_point) {
			if (Markers.MeasureStart.point)
				ts.setStart(Markers.MeasureStart.point);
		} else if (!ts.end_point && ts.start_point != Markers.MeasureEnd.point) 
			ts.setEnd(Markers.MeasureEnd.point);
		else {
			ts.setEnd(null);
			ts.setStart(Markers.MeasureStart.point);
		}
		return false;
	}
		
		
	//=======  Map Event Handlers  ========
		
	function mouse_move_handler(latlng)
	{	
		if(!enabled || dragging || !active_trail || !active_trail.hasPoints() || mode_is('pan') || info_window_open)
			return;
		
		var mouse = map.fromLatLngToDivPixel(latlng);
		
		//if the map bounds have changed, everything msut be recalculated
		var bounds = map.getBounds();
		var recalculate = !(last_map_bounds && last_map_bounds.equals(bounds));
		var do_ghosts = mode_is('edit');

		if (mode_is('measure', 'select')) {
			var start = trail_segment.start_point;
			var end = trail_segment.end_point;
			var start_placed = start && !end;
			
			var closest = find_closest_mid_or_point(mouse, do_ghosts, recalculate, start_placed ? {only_track: start.track}: null);
			var point = closest.p;
			
			if (start_placed) 
				show_marker_at_point(Markers.MeasureEnd, point, closest.dist);
			else show_marker_at_point(Markers.MeasureStart, point, closest.dist);
			
		} else {
			var closest = find_closest_mid_or_point(mouse, do_ghosts, recalculate);
			var point = closest.p;
		
			if (editing_enabled && mode_is('edit', 'move')) {
				if (closest.mid) 
					show_ghost_edit_marker(point.prev, closest.dist);
				else if (point.next == null || point.prev == null) 
					hide_other_markers(null);
				else show_marker_at_point(Markers.Edit, point, closest.dist);
			} else if (editing_enabled && mode_is('delete')) {
				show_marker_at_point(Markers.Delete, point, closest.dist);
			}
		}
		
		last_map_bounds = bounds;
	}


	function find_closest_mid_or_point(mouse, check_mids, force_recalc, opts){

		var closest_dist = 100000000,
		    closest_is_mid = false,
			closest = null;
				
		active_trail.tracks.each(function(track){
			if(opts && opts.only_track && opts.only_track != track)
				return;
				
			var points = track.points;
			
			//recalculate everything that needs it
			recalculate_dirty_distances(track, force_recalc);
		
			//Find the point closest to the mouse
			
			for(var i=0, len=points.length; i < len; ++i) {
				var point = points[i];
				point._pix_clean = true;
				
				var pos = point._div_pix;
				var dist = distance_from(pos, mouse);
				//give a default value greater than dist_1 incase we aren't checking mid_points
				var dist_mid = dist + 100;
	
				if(check_mids && point.prev && point._pix_dist > min_line_length_for_ghost)
					dist_mid = distance_from(mouse, point._mid_point);
	
				dist = Math.min(dist, dist_mid);
				
				if(dist < closest_dist)
				{
					closest_dist = dist;
					closest = point;
					closest_is_mid = dist == dist_mid;
				}
			}
		});
		
		return {p:    closest,
		        dist: closest_dist,
                mid:  closest_is_mid};
	}


	function recalculate_dirty_distances(track, force_recalc){
		var points = track.points;
		
		//recalculate everything that needs it
		for(var i = 0, len = points.length; i < len; ++i) 
		{
			var point = points[i];
			
			if (force_recalc || !point._pix_clean) 
				point._div_pix = map.fromLatLngToDivPixel(point.latlng);
			
			if (point.prev && (force_recalc || !point._pix_clean || !point.prev._pix_clean)){
				point._mid_point = mid_point(point._div_pix, point.prev._div_pix);
				point._pix_dist = distance_from(point._div_pix, point.prev._div_pix);
			}			
		}
	}


	function map_click_handler(overlay, latlng){
		if(mode_is('edit'))
		{
			if(overlay)	return;
			var new_point = null;
			var track = (Markers.Insert.point && Markers.Insert.point.track) || active_trail.tracks.first();
			var markers = Markers.terminals.get(track.id);
			
			if(markers && insertion_end == markers.start)
				new_point = track.addPointAt(latlng, 0);
			else if(!markers || insertion_end == markers.end || track.points.length < 2)
				new_point = track.addPointAt(latlng, track.points.length);
			
			if(new_point && insertion_end)
				move_terminal_marker(insertion_end, new_point);
			
			if(track.points.length == 1)
				move_marker_to_point(markers.start, track.points[0]);				
	    }
		else if(mode_is('measure','select') && !info_window_open && !overlay)
			trail_segment_mode_click();
			
		else if(mode_is('add_track')){
			if(overlay)	return;
			var markers;
			var track = active_trail.tracks.find(function(track){return track.points.length == 0;});

			if (!track) {
				track = active_trail.CreateTrack();
				UI.Trail.createEditableUIForTrack(track);
				observe_track_events(track);
			}
			
			markers = make_start_and_end_markers(track);
			insertion_end = markers.end;
			
			var new_point = track.addPointAt(latlng, 0);
			move_marker_to_point(markers.start, new_point);
			move_marker_to_point(Markers.Insert, new_point);
			
			me.set_mode('edit');
		}
	}	


//============ Utility Functions =============	
	//get the dist^2 between two GPoints
	function distance_from(p1, p2){
		var dx = (p1.x - p2.x);
		var dy = (p1.y - p2.y);
		return dx*dx + dy*dy;
	}
	
	function mid_point(p1, p2){
		return new GPoint((p1.x+p2.x)/2, (p1.y+p2.y)/2)
	}

	function add_listener(object, event, func){
		return GEvent.bind(object, event, this, function(){func(object);});
	}
	
	function get_next_z_index(){
		return next_z_index++;
	}
	


//===========  Marker Showing/Hiding/moving/creating  =============	
	//Display the ghost edit marker between lower_p and lower_p.next
	//lower_p is a trackpoint Point object
	//dist is the pixel distance between the mouse and the mid_point
	function show_ghost_edit_marker(lower_p, dist){
		var mid = mid_point(lower_p._div_pix, lower_p.next._div_pix);
	
		Markers.Ghost.first_point = lower_p;
		
		hide_other_markers(Markers.Ghost);
		show_marker(Markers.Ghost, map.fromDivPixelToLatLng(mid), dist);
	}
	
	function show_marker_at_point(marker, point, dist){
		marker.point = point;
		hide_other_markers(marker);
		show_marker(marker, point.latlng, dist);
	}	
	
	function hide_other_markers(except) {
		Markers.moveable_markers.each(function(marker){
			if(except != marker) marker.hide();
		});
	}
	
	function show_marker(marker, latlng, dist){
		if(dist > max_edit_distance) 
		{
			marker.hide();
		}
		else
		{
			marker.setLatLng(latlng);		
			marker.show();
		}
	}
	
	function move_marker_to_point(marker, point){
		marker.setLatLng(point.latlng);
		marker.point = point;
		marker.show();
	}
	
	//Move either the start or end marker
	function move_terminal_marker(marker, new_point)
	{
		move_marker_to_point(marker, new_point);
		if(mode_is('edit') && insertion_end == marker)
			move_marker_to_point(Markers.Insert, new_point);
	}
	
	function create_terminal_marker(icon){
		var marker = Map.Overlays.createMarker({icon: icon, draggable: true, z_index:get_next_z_index()});
		marker._event_handlers = [
			add_listener(marker, "dragstart", marker_drag_start),
			add_listener(marker, "dragend", marker_drag_end),
			add_listener(marker, 'click', click_terminal_point)
		];
		return marker;
	}
	
	function release_terminal_marker(marker){
		marker._event_handlers.each(GEvent.removeListener);
		map.removeOverlay(marker);
	}
	
	function move_endpoint_markers(track) {
		var markers = Markers.terminals.get(track.id);
		var points = track.points;
		
		if (markers && points.length > 0) {
			move_terminal_marker(markers.start, points.first());
			if (points.length > 1)
				move_terminal_marker(markers.end, points.last());
			else
				markers.end.hide();
		}
	}
	
	//Mode functionality
	function check_mode(mode_to_check){
		if(!allowed_modes[mode_to_check])
			throw("Tool Mode: " + mode_to_check + " is not a recognized mode.");
	}
	
	function mode_is(){
		for(var i=0; i<arguments.length; i++){ 
			check_mode(arguments[i]);
			if(mode == arguments[i]) return true;
		}
		return false;
	}
})();

//=================  Trail Class  ====================

function Trail()
{
	this._isCompletelyLoaded = false;
	this._detail_level = 0;
	DefineEvents(this, 'trackAdded', 'trackRemoved');
}

Trail.Detail_Levels = 
{
	bounds: 1,
	meta: 2,
	full: 3
};

//This could be broken up
Trail.prototype.SerializeToXML = function()
{
	if(this.stored && !this.isCompletelyLoaded())
		throw "Cannot save trail from search results.";
		
    var XML = [];
    var anythingdirty = false;
    XML.push("<t id='" + this.id + "'");
 
    this.Recalculate_Extents();
	this.set('primary_track_id', this.get_primary_track().id);
	
	if(!this.hasTrack() || !this.hasPoints())
		return null;
        
    //If the record hasn't been stored, or has changed
    if(!this.stored || this.dirty)
    {
        anythingdirty = true;
		IO.AddToSaveList(this);
		this.setDirty(false);
				        
        if(!this.stored)
            XML.push("new='1'");

		//Attributes
        IO.Trail_Fields.each(function(pair)
        {
			if(!pair.value.attr) return;
			
            var value = this[pair.key];
            
            if(value == null)
                value = "''";
            else
                value = sq(XMLencode(value.toString()));
               
            XML.push(pair.key + '=' + value);
        },this);        


        XML.push("distance=" + sq(Round(this.getDistance())));
		XML.push(">");	 // end of first trail tag

		//Elements
        IO.Trail_Fields.each(function(pair)
        {	
			if(pair.value.attr) return;		
			var field = pair.key;
            var value = this[field];

            if(value == null || value == "")
                XML.push('<' + field + '/>');
            else
				XML.push('<' + field + '><![CDATA[' + value + ']]></'+ field + '>');
        }, this);  			
    }
    else
	    XML.push(">");


    var TrackXML = [];
    for(var i=0; i<this.tracks.length; i++)
    {
        var xml = this.tracks[i].SerializeToXML();

        if(xml == null)
			continue;

        anythingdirty = true;
        TrackXML.push(xml);
    }

	if (TrackXML.length > 0) 
	{
		XML.push("<tracks>");
		XML.push(TrackXML.join(" "));
		XML.push("</tracks>")
	}
	
    XML.push("</t>");

    if(anythingdirty)
        return XML.join(" ");
    else
        return null;
}

Trail.prototype.Has_Unsaved_User_Changes = function(){
	return IO._points_to_delete.length > 0 || this.user_changes || 
		this.tracks.any(function(track){
			return track.user_changes || track.points.any(function(point){
				return point.dirty})});
}

Trail.prototype.Recalculate_Extents = function()
{
	if(this.readonly)
		return;
		
	if (this.hasPoints()) 
	{
		var maxlng, minlng;
		var maxlat = maxlng = -180;
		var minlat = minlng = 180;
		
		for (var i = 0; i < this.tracks.length; i++) 
		{
			var points = this.tracks[i].points;
			var len = points.length;
			
			for (var x = 0; x < len; ++x) 
			{
				//Calculate the boundaries of this track
				var coord = points[x].latlng;
				
				if (coord.lat() > maxlat) 
					maxlat = coord.lat();
				if (coord.lat() < minlat) 
					minlat = coord.lat();
				
				if (coord.lng() > maxlng) 
					maxlng = coord.lng();
				if (coord.lng() < minlng) 
					minlng = coord.lng();
			}
		}
		
		this.set("max_lat", RoundDeg(maxlat));
		this.set("min_lat", RoundDeg(minlat));
		this.set("max_lng", RoundDeg(maxlng));
		this.set("min_lng", RoundDeg(minlng));
		
		var p = this.getstartloc();
		this.set("start_lat", RoundDeg(p.lat()));
		this.set("start_lng", RoundDeg(p.lng()));
		
		p = this.getendloc();
		this.set("end_lat", RoundDeg(p.lat()));
		this.set("end_lng", RoundDeg(p.lng()));
	}
	else if (this.start_lat) 
	{
		this.start_loc = this.getstartloc();
		this.end_loc = this.getendloc();
	}
	
	if (this.min_lat) 
		this.bounds = new GLatLngBounds(new GLatLng(this.min_lat, this.min_lng),
		new GLatLng(this.max_lat, this.max_lng));

}

//Creates a track object owned by this trail and adds it to the tracks collection
//Returns: the created Track object
Trail.prototype.CreateTrack = function()
{
    var track = Track.CreateNew(this);
	this.AddTrack(track);
	
    return track;
}

Trail.prototype.AddTrack = function(track){
	if(this.tracks.include(track))
		return;
		
    this.tracks.push(track);
	
	if (IsEditMode && !this.readonly) {
		track.pointsRemoved.observe(function(points) {
			if (track.points.length == 0) (function() {
				track.trail.RemoveTrack(track);
			}).delay(0);
		});
	}
	
	this.trackAdded.fire(track);	
}

Trail.prototype.RemoveTrack = function(track)
{
	if(!this.tracks.include(track))
		return;
	
    if (track.stored) {
		IO._tracks_to_delete.push(track.id);
		track.points.each(function(p){
			if(p.stored)
				IO._points_to_delete.push(p.id);
		})
	}	
	
	track.destroy();
	
	this.tracks = this.tracks.without(track);
	this.trackRemoved.fire(track);

    return track;
}

Trail.prototype.get_primary_track = function(){
	return this.primary_track || this.tracks.first();
}

Trail.prototype.getDistance = function(){
	if(this.hasTrack())
		return this.get_primary_track().getDistance();
	else
		return this.distance;
}

Trail.prototype.getstartloc = function()
{
    if(this.hasPoints())
		return this.start_loc = this.get_primary_track().getstartloc();
	else if(this.start_loc)
		return this.start_loc;
	else
		return this.start_loc = new GLatLng(this.start_lat, this.start_lng);
}

Trail.prototype.getendloc = function()
{
    if(this.hasPoints())
		return this.end_loc = this.get_primary_track().getendloc()
	else if(this.end_loc)
		return this.end_loc;
	else
		return this.end_loc = new GLatLng(this.end_lat, this.end_lng);
}

Trail.prototype.hasMetaInfo = function(){
	return (this._detail_level >= Trail.Detail_Levels.meta || this._isCompletelyLoaded);
}

Trail.prototype.getDetailLevel = function(){
	return this._detail_level;
}

Trail.prototype.hasTrack = function(){
	return this.tracks && this.tracks.length >= 0;
}

Trail.prototype.hasPoints = function(){
	return this.hasTrack() && this.get_primary_track().points.length > 0;
}

Trail.prototype.isCompletelyLoaded = function()
{
	return this._isCompletelyLoaded;
}

Trail.prototype.UpdateFromXML = function(trailnode)
{
	if(this.id && this.id != trailnode.getAttribute("id")) 
		throw "Stored ID doesn't match ID in update XML"
	
	this.id = trailnode.getAttribute("id");
	this.filtered = '0';
	
	//Convert Attributes into JS properties 
	var attrs = trailnode.attributes;
	var len = attrs.length;
	for (i = 0; i < len; i++) {
		var attr = attrs.item(i);
		this[attr.name] = attr.value;
	}
	
	this.filtered = this.filtered == '1';
	
	//Detail level can only go up
	var new_detail_level = Trail.Detail_Levels[this.detail];
	if(new_detail_level > this._detail_level)
		this._detail_level = new_detail_level;
	
	if(this.start_lat)
		this.start_loc = new GLatLng(parseFloat(this.start_lat), parseFloat(this.start_lng));

	if(this.end_lat)
		this.end_loc = new GLatLng(parseFloat(this.end_lat), parseFloat(this.end_lng));

	if(this.min_lat)
		this.bounds = new GLatLngBounds(new GLatLng(parseFloat(this.min_lat), parseFloat(this.min_lng)),
									new GLatLng(parseFloat(this.max_lat), parseFloat(this.max_lng)));
	if(this.updated_at) {
		this.updated_at = new Date(this.updated_at * 1000);
		this.created_at = new Date(this.created_at * 1000);
	}
	
	//Convert Elements into JS properties
	var nodes = trailnode.childNodes;
	len = nodes.length;

	for (i = 0; i < len; i++) {
		var node = nodes[i];
		//Tracks
		if (node.nodeName == 'tracks') {

			var tracknodes = node.childNodes;
			var len = tracknodes.length;
			
			this.tracks = new Array();
			
			for (var t = 0; t < len; t++) {
				if (tracknodes[t].nodeType == 1) 
				{
					var track = Track.ReadFromXML(tracknodes[t], this.readonly, this);
					this.AddTrack(track);
				}
			}

			var primary_id = this.primary_track_id;
			this.primary_track = this.tracks.find(function(track){return track.id == primary_id;}) || this.tracks.first();
		}
		//meta-data in elements like <description>
		else {
			var value = node.textContent || node.text;
			if (value) {
				this[node.nodeName] = new String(value).strip();
			}
		}
	}
	
	this._isCompletelyLoaded = this._isCompletelyLoaded || (this.detail == 'full');
}

Trail.prototype.removeTrailSegment = function(ts){
	if (this.tracks.include(ts.track)) 
		ts.track.removeSegment(ts);
}


//===== Static Methods
Trail.ReadFromXML = function(trailnode, readonly)
{
    T = new Trail();
    T.dirty = false;
    T.stored = true;
	T.readonly = readonly;
 	
	T.UpdateFromXML(trailnode);
	
	if(T.created_by_user_id != User_ID && T['private'] > 0)
		T.readonly = true;
    
    return T
}

Trail.CreateNew = function()
{
    var T = new Trail();
    T.id = GUID(); 
    T.tracks = [];
    T.dirty = true;
    T.stored = false;
	T.readonly = false;
	T._isCompletelyLoaded = true;
    return T;
}




//=================  Track Class  ====================

function Track(id, readonly)
{
    this.id = id;
	this.readonly = readonly;
    this.points = [];
    this.length = 0;
    this.duration = 0;
	DefineEvents(this, $w('pathChanged statsChanged pointsRemoved pointsAdded pointsMoved destroyed'));
    this.pathChanged.observe(this.RecalculateStats);	
}

//==== Static Methods ====
Track.CreateNew = function(trail)
{
    var T = new Track(GUID(), false);
    T.trail_id = trail.id;
	T.trail = trail;
    T.dirty = true;
    T.stored = false;
    return T;
}

Track.ReadFromXML = function(trackNode, readonly, trail)
{
    var T = new Track(trackNode.getAttribute("id"), readonly);

	if(trail)
		T.trail = trail;
		
	Track.NUMERIC_COLUMNS.each(function(col){
		var val = parseInt(trackNode.getAttribute(col));
		if(val == NaN)
			val = null;
	    T[col] = val;
	})
	
    T.dirty = false;
		    
    var pointMap = new Hash();
    var pointNodes = trackNode.getElementsByTagName("p");
    var len = pointNodes.length;

        
    for(var i=0; i<len; i++)
    {
        p = Point.ReadFromXML(pointNodes[i]);
        p.track = T;
        pointMap.set(p.id, p);
    }


    //Follow the linked list and put them in order    
    var points = new Array(len);
    T.points = points;
    var currentId = trackNode.getAttribute("start_point_id");
    var prevPoint = null;
    for(var i=0; i<len; i++)
    {
        var currentPoint = pointMap.get(currentId);

        //Error handling to deal with missing points
        if(currentPoint == null)
        {
            prevPoint.setNext(null)
            points.length = i;
            break;
        }

        pointMap.unset(currentId);

        currentId = currentPoint.next_id;
        points[i] = currentPoint;
        
        currentPoint.setPrev(prevPoint, true);
        prevPoint = currentPoint;

        //Error handling to deal with disconnected points
        if(currentId == null)
        {
			//The solution is not to just delete all the left overs
			//What if one next_id is accidentally set to null
			/*
            //leftover points
            pointMap.values().each(function (p)
            {
                IO._points_to_delete.push(p.id);
            });
            points.length = i+1;
            len = points.length;
            */

			//trim the Array to exclude leftover (disconnected) points
            points.length = i + 1;
            break;
        }
    }

	T.RecalculateStats();
    
    T.stored = true;
    
    return T;   
}

//==== Instance Methods ====

Track.NUMERIC_COLUMNS = ["distance", "altitude_loss", "altitude_gain", "altitude_change", "uphill_dist", "downhill_dist"];

Track.prototype.SerializeToXML = function()
{
    var ps = this.points;
    var len = ps.length-1;
    var out = new Array();
    var OnePointIsDirty = false;
    out.push("<track ");
    out.push("id=" + sq(this.id));
	
	if ((!this.stored || this.dirty) && ps.length > 0) 
	{
		out.push("start_point_id=" + sq(ps[0].id));
		
		Track.NUMERIC_COLUMNS.each(function(col)
		{
			out.push(col + "=" + sq(Round(this[col], 1)));
		}, this)
	}
	
    if(!this.stored)
        out.push("new='1'");
		
    out.push(">");
    
    //Step through the points and serialize them to XML if they are dirty
    for(var i=0; i<=len; i++)
    {
        var point = ps[i];
        
        //If next_id isn't set to what it should be, set it and mark the point dirty
        if(i < len && point.next_id != ps[i+1].id)
        {
            point.next_id = ps[i+1].id;
            point.dirty = true;
        }
        
        //If it's not dirty, don't bother
        if(!point.dirty)
            continue;
        
		IO.AddToSaveList(point);
		
        OnePointIsDirty = true;

        out.push("<p id='" + point.id + "'");
        out.push("lat='" + RoundDeg(point.latlng.lat()) + "'");
        out.push("lng='" + RoundDeg(point.latlng.lng()) + "'");
        if (point.alt_source > 0) 
		{
			out.push("alt_s='" + point.alt_source + "'");
			//Altitude is irrelevant if it does have a source
			if (point.altitude) 
				out.push("alt='" + point.altitude + "'");
		}
        if(point.next_id)
            out.push("next_id='" + point.next_id + "'");
        if(!point.stored)
            out.push("new='1'");
            
        out.push("/>");
    }
    
    out.push("</track>");

    //If no points need to be updated, then the track doesn't either.    
    if (OnePointIsDirty || this.dirty) 
	{
		IO.AddToSaveList(this);

		return out.join(' ');
	} else 
		return null;
}



Track.prototype.RecalculateStats = function()
{
	var r = this.stats = Track.calculateStatsForSegment(this.points[0], this.points.last(), false);
	if(!r) return;
	
	this.bounds = new GLatLngBounds(new GLatLng(r.min_lat, r.min_lng),
		new GLatLng(r.max_lat, r.max_lng));
	
    this.set('distance', r.distance);
	if(this.trail) this.trail.set('distance', r.distance);

    this.set('altitude_loss', r.alt_loss);
    this.set('altitude_gain', r.alt_gain);
    this.set('altitude_change', r.alt_change);
    this.set('uphill_dist', r.uphill_dist);
    this.set('downhill_dist', r.downhill_dist);
	
	this.statsChanged.fire(this);
	return r;
}



Track.calculateStatsForSegment = function(start_point, end_point, reverse)
{
	if(!start_point || !end_point || start_point == end_point)
		return;

	if (reverse) {
		var temp = end_point;
		end_point = start_point
		start_point = temp;
	}
	
    var alt_gain = 0,
		alt_loss = 0,
		uphill_dist = 0,
		last_alt  = -10000,
		first_alt = -10000,
		last_alt_dist = 0,
		dist = 0,
		count = 1,
		prev = start_point,
		r = {};
		r.max_lat = r.min_lat = prev.latlng.lat();
		r.max_lng = r.min_lng = prev.latlng.lng();		

	do {
        var p = prev.next;

        prev.calculateDistance();
        dist += prev.elevCorrectedDist;
		last_alt_dist += prev.elevCorrectedDist;

		if (p.altitude || p.altitude == 0) 
		{
			var alt_change = p.altitude - prev.altitude;
			
			//if either of the altitudes are undefined, don't bother
			if (alt_change || alt_change == 0) 
			{
				//Total up the gain and loss
				if (alt_change > 0) 
				{
					alt_gain += alt_change;
					uphill_dist += last_alt_dist;
				} else 
				{
					alt_loss += alt_change;
				}
			}
			
			last_alt = p.altitude;
			last_alt_dist = 0;
			
			if(first_alt == -10000)
				first_alt = p.altitude;
		}

				
		//Calculate the boundaries of segment
		var coord = p.latlng;
		
		if (coord.lat() > r.max_lat) 
			r.max_lat = coord.lat();
		if (coord.lat() < r.min_lat) 
			r.min_lat = coord.lat();
		
		if (coord.lng() > r.max_lng) 
			r.max_lng = coord.lng();
		if (coord.lng() < r.min_lng) 
			r.min_lng = coord.lng();
		
		count++;
		prev = p;
    } while(p != end_point && p)

	var ag = Round(alt_gain),
		al = Round(-alt_loss),
		ac = Round(last_alt - first_alt),
		dd = Round(dist - uphill_dist),
		ud = Round(uphill_dist);

	if (reverse) {
		results = {
			alt_loss: ag,
			alt_gain: al,
			alt_change: -ac,
			uphill_dist: dd,
			downhill_dist: ud
		};
	} else {
		results = {
			alt_loss: al,
			alt_gain: ag,
			alt_change: ac,
			uphill_dist: ud,
			downhill_dist: dd
		};
	}
	
	//r = min/mix_lat/lng
	Object.extend(results, r);
	results.point_count = count;
    results.distance = Round(dist);
	return results;
};


Track.prototype.notifyPointsMoved = function(start, end){
	this.pointsMoved.fire(new TrailSegment.PointsEnumerable(start, end || start));
	this.pathChanged.fire(new TrailSegment.PointsEnumerable(start, end || start));	
}

Track.prototype.notifyPointsElevationChanged = function(start, end){
	this.pathChanged.fire(new TrailSegment.PointsEnumerable(start, end || start));
}

Track.prototype.destroy = function(){
	this.destroyed.fire(this);
	this._event_list.invoke('clear_observers');
}

Track.prototype.addPointAt = function(latlng, index)
{
	//Limit index to [0, points.length]
	index = Math.max(index, 0);
	index = Math.min(index, this.points.length);
	
    newPoint = Point.CreateNew(latlng, this);
	var prev = index > 0 ? this.points[index-1] : null;
	var next = index < this.points.length ? this.points[index] : null;
	
    //insert the point into the doubly linked list
    newPoint.setNext(next);    
    newPoint.setPrev(prev);

    Services.Elevation.GetForPoint(newPoint);
    
    this.points.splice(index, 0, newPoint);
	this.setDirty(true);

	var region = new TrailSegment.PointsEnumerable(newPoint, newPoint);
	this.pointsAdded.fire(region);
	this.pathChanged.fire(region);
	
    return newPoint;
}

Track.prototype.removePoint = function(point)
{
    index = this.points.indexOf(point);

    if(index < 0)
        return;
    
    if(point.stored)
        IO._points_to_delete.push(point.id);
    
    //Take this point out of the doubly linked list
    if(point.prev)
        point.prev.setNext(point.next);
    else if(point.next)
        point.next.setPrev(point.prev);
		
    //Remove it from the array
    this.points.splice(index, 1);
	this.setDirty(true);
	this._check_for_empty_track();
    
	var region = new TrailSegment.PointsEnumerable(point, point);
	this.pointsRemoved.fire(region);
	this.pathChanged.fire(region);		
}

Track.prototype._check_for_empty_track = function(){
	if (this.points.length == 0){
		this.length = 0;
		return true;
	}
	return false;
}
    


Track.prototype.removeSegment = function(ts){
	if(ts.track != this) return;
	
	ts.points.each(function(p){
	    if(p.stored) IO._points_to_delete.push(p.id);
	});
	
	var start_p = ts.is_reverse ? ts.end_point : ts.start_point;
	var end_p = ts.is_reverse ? ts.start_point : ts.end_point;	

    //Take this segment out of the doubly linked list
    if(start_p.prev)
        start_p.prev.setNext(end_p.next);
    if(end_p.next)
        end_p.next.setPrev(start_p.prev);

    //Remove the points from the array	
	var start_index = this.points.indexOf(start_p);
	var end_index = this.points.indexOf(end_p);	
	
    this.points.splice(start_index, end_index - start_index + 1);
	this.setDirty(true);

    if(this.points.length == 0)
        this.length = 0;

	var region = new TrailSegment.PointsEnumerable(start_p, end_p);
	this.pointsRemoved.fire(region);
	this.pathChanged.fire(region);
}

//Uh... what was I planning on doing?
Track.prototype.getDistance = function(){
	if(this.distance)
		return this.distance;
	else
		return this.distance;
}

Track.prototype.getstartloc = function()
{
    return this.points.length > 0 ? this.points[0].latlng : null;
}

Track.prototype.getendloc = function()
{
    return this.points.length > 0 ? this.points[this.points.length-1].latlng : null;
}

Track.prototype.point_count = function()
{
    return this.points.length;
}

//=============  Point Class  ===============
function Point(latlng)
{
    this.latlng = latlng;
    this.line = null;
    this.elevCorrectedDist = 0;
    this.next_id = null;
    this.recalcDistance = false;
}

Point.CreateNew = function(latlng, track)
{
    var p = new Point(latlng);
    p.id = GUID();
    p.setDirty(true);  
    p.stored = false; 
    p.track = track;
    p.recalcDistance = true;	
    return p;
}

Point.ReadFromXML = function(pointNode)
{
    var latlng = new GLatLng(parseFloat(pointNode.getAttribute("lat")),parseFloat(pointNode.getAttribute("lng")));
    var p = new Point(latlng);
    p.altitude = parseFloat(pointNode.getAttribute("alt"));
	p.alt_source = pointNode.getAttribute("alt_s");
    p.id = pointNode.getAttribute("id");
    p.next_id = pointNode.getAttribute("next_id");

    p.setDirty(false);
    p.stored = true;
	p.recalcDistance = true; 

    return p;
}


Point.prototype.setNext = function(p, not_dirty)
{
	if(this.next != p && not_dirty != true)
		this.setDirty(true);

    this.next = p;
    if(p) p.prev = this;
}

Point.prototype.setPrev = function(p, not_dirty)
{
    this.prev = p;
    if(p){
		if(p.next != this && not_dirty != true)
			p.setDirty(true);
		p.next = this;
	}	
}

Point.prototype.setLatLng = function(LatLng)
{
    this.setDirty(true);
	//if you move the point, the altitude is no longer valid
	this.altitude = null;
	this.alt_source = null;
    this.latlng = LatLng;
    this.recalcDistance = true;    
}

Point.prototype.setAltitude = function(alt)
{
	if (this.altitude != alt || this.alt_source == null) 
	{
		//Sanity check
		if(alt < -2000)
			alt = null;

		this.setDirty(true);
		this.altitude = alt;
		this.alt_source = UseElevation;
		this.recalcDistance = true;
	}
}

Point.prototype.calculateDistance = function()
{
	if (this.next) 
	{
		if(!this.recalcDistance && !this.next.recalcDistance)
			return;
			
		var lateral_dist = this.latlng.distanceFrom(this.next.latlng);
		
		if(this.altitude && this.next.altitude) 
		{
			var alt_change = this.next.altitude - this.altitude;
			this.elevCorrectedDist = Math.sqrt(lateral_dist * lateral_dist + alt_change * alt_change);
		}
		else
			this.elevCorrectedDist = lateral_dist;
			
		this.recalcDistance = false;
	}
	else
		this.elevCorrectedDist = 0;
		
}

//============== Common Methods ===============

IO._setDirty = function(dirty)
{
	this.dirty = dirty;

	if(!dirty && this.user_changes)
		this.user_changes = false;

	// IO.dirty should be an OR of all the dirty's, so we can't
	// set it to false unless we know everything is false
	if(dirty)
		IO.setDirty(dirty);
}
Trail.prototype.setDirty = IO._setDirty;
Track.prototype.setDirty = IO._setDirty;
Point.prototype.setDirty = IO._setDirty;


//change an existing value and mark a dirty flag if the new value is different
Util._CopyNewValue = function(field, newvalue, from_user)
{
    if(newvalue != null && newvalue != 0 && (Object.isUndefined(newvalue) || newvalue.blank && newvalue.blank()))
        newvalue = null;
		
    var oldvalue = this[field];
    if(oldvalue != newvalue && (!Object.isUndefined(oldvalue) || newvalue != null))
    {
        this[field] = newvalue;
        this.setDirty(true);
		if(from_user)
			this.user_changes = true;
    }
}

Trail.prototype.set = Util._CopyNewValue;
Track.prototype.set = Util._CopyNewValue;

Map.Overlay_Manager = new (function()
{
	var me = this;
	var visible_overlays = 0;

	var mapbounds = null;
	var mapzoom = null;
	
	//Hash of 
	var overlays = new Hash();
	
	//Queue of trails ready to be requested in full from the server
	var queue = new Hash();
	
	var _MarkerManager = null;
	var ajax_request = null;
	var trail_loader = null;
	var default_options = null;
	var limit_warning_visible = false;
	var _hit_max_overlays = false;

	if(!Include_map)
		return this;
	
	this._show_paths = false;
	
	Initializers.push(function()
	{
		var event_handler = this.Update_Overlays.bind(this);
		GEvent.addListener(map, 'moveend', event_handler);
		GEvent.addListener(map, 'zoomend', event_handler);
		GEvent.addListener(map, 'resized', event_handler);
		
		default_options = {path:true, color:SEARCH_LINE_COLOR, opacity:1, width:LINE_WIDTH, start:UI.Markers.Start, end:UI.Markers.End, filtered:UI.Markers.Filtered};		
		
	}.bind(this));

	this.Activate_Loading = function()
	{
		if(trail_loader)
			return;
			
		trail_loader = new PeriodicalExecuter(this.Load_Next_Trail.bind(this), Map.TRAIL_PATHS_BACKGROUND_FREQUENCY);
	}
	
	this.Stop_Loading = function()
	{
		if(!trail_loader)
			return;

		trail_loader.stop();
		trail_loader = null;
	}
	
	this.Reset_Loading = function()
	{
		this.Stop_Loading();
		this.Activate_Loading();
		this.Load_Next_Trail();
	}

	this.Update_Overlays = function()
	{
		this.Update_Specific_Overlays(overlays);
	}
	
	//This adds and removes line overlays for trails based on the
	//current zoom and position
	this.Update_Specific_Overlays = function(overlays_to_update)
	{
		mapzoom = map.getZoom();
		mapbounds = map.getBounds().expand(Search.SEARCH_BOUNDS_EXPANSION);
		_hit_max_overlays = false;

		//We still have the problem that Not all matching
		//results are shown if there are too many filtered
		//trailheads
		overlays_to_update.each(function(pair)
		{
			var over = pair.value ? pair.value : pair;
			update_one_overlay(over, true);
		});

		overlays_to_update.each(function(pair)
		{
			var over = pair.value ? pair.value : pair;
			if(!over.trail.filtered)
				update_one_overlay(over, false);
		});

		overlays_to_update.each(function(pair)
		{
			var over = pair.value ? pair.value : pair;
			if(over.trail.filtered)
				update_one_overlay(over, false);
		});
		
		update_limit_warning(_hit_max_overlays);
	};
	
	function update_one_overlay(over, only_hide)
	{
		var trail = over.trail;
		
		if (!mapbounds.containsLatLng(trail.start_loc) || at_Max_Overlays()) {
			hide_overlay(over.filtered_marker);
			hide_overlay(over.start_marker);
		} else if(!only_hide) {
			if (!trail.filtered || over.force_visibility) {
				show_overlay(over.start_marker);
				hide_overlay(over.filtered_marker);
			} else {
				show_overlay(over.filtered_marker);
				hide_overlay(over.start_marker);					
			}
		}
				
		if(!over.overlays)
			return;
			
		if (over.visible && (at_Max_Overlays() || over.force_visibility == false || !over.bounds.intersects(mapbounds) || (over.force_visibility != true && (!me._show_paths || trail.filtered || mapzoom < over.minZoom)))) 
		{
			hide_overlay(over.overlays);
			hide_overlay(over.end_marker);
			over.visible = false;
		} else if (!only_hide && !over.visible && over.bounds.intersects(mapbounds) && (over.force_visibility == true || (over.force_visibility != false && mapzoom >= over.minZoom && !trail.filtered && me._show_paths))) 
		{
			if (show_overlay(over.overlays)) 
			{
				show_overlay(over.end_marker);
				over.visible = true;
			}
		}
	}
	
	function at_Max_Overlays(){
		var at_max =  visible_overlays >= (Map.MAX_VISIBLE_OVERLAY_COUNT * (me._faster_computer ? 4 : 1));
		_hit_max_overlays |= at_max;
		return at_max;
	}	
	
	this.Force_Trail_Visibility = function(trail, visibility){
		var id = trail.id ? trail.id : trail;
		var over = overlays.get(id);
		
		if(over && over.force_visibility != visibility){
			over.force_visibility = visibility;
			
			if (visibility) {
				if(trail.hasPoints())
					this.Create_Path_For_Trail(trail);
				else
					this.Load_Trail_Immediately(trail);
			}

			this.Update_One_Trail(trail);			
		}
	};
	
	this.Hide_All_Forced_Trails = function(){
		overlays.each(function(pair){
			pair.value.force_visibility = null;
		});
		this.Update_Overlays();
	};
	
	this.Toggle_Trail_Visibility = function(trail){
		var id = trail.id ? trail.id : trail;
		var over = overlays.get(id);

		this.Force_Trail_Visibility(trail, over.force_visibility == true ? null : true);
		return over.force_visibility;
	};	
	
	
	this.Load_Next_Trail = function()
	{
		//Don't make a second request until the first one is done, or we can actually display more
		if(ajax_request || at_Max_Overlays() || !this._show_paths)
			return;
			
		var mapbounds = map.getBounds().expand(Search.SEARCH_BOUNDS_EXPANSION);
		var zoom = map.getZoom();
		var remove_ids = [];
		var valid_trails = [];
		
		queue.each(function(pair)
		{
			var t_info = pair.value;
			if (!t_info.bounds.intersects(mapbounds) || zoom < t_info.minZoom || t_info.trail.filtered)
			{
				remove_ids.push(pair.key);
				return false;
			}
			
			valid_trails.push(pair.key);

			if(valid_trails.length >= Map.TRAIL_PATHS_PER_REQUEST)
				throw $break;
		});
		
		if(valid_trails.length > 0)
			request_trail_paths(valid_trails);
			
		remove_ids.each(function(id){queue.unset(id);});
		
		//if the queue is empty, stop loading
		if(!queue.any())
			this.Stop_Loading();
	}

	//Add a trail to the overlay manager with the specified displayoptions (null = default)
	//passing force_update = true will re-evaluate whether the trail should be displayed,
	//instead of waiting untill the next refresh.
	this.Add_Trail = function(trail, displayoptions, force_update)
	{
		if (queue.get(trail.id))
			return;
		
		var t_info = overlays.get(trail.id)
		if (t_info)
		{
			if(t_info.options.path && !trail.hasPoints())
				queue.set(trail.id, t_info)

			return;
		}

		var opts = displayoptions ? default_options.clone().extend(displayoptions) : default_options;
		
		t_info = 
		{
			trail: trail,
			bounds: trail.bounds,
			visible: false,
			added: false,
			minZoom: trail.bounds.getMinZoom(),
			options: opts
		};
		
		if(opts.start)
		{
			t_info.start_marker = new GMarker(trail.start_loc, opts.start);
			t_info.start_marker.trail = trail;
		}
		
		if(opts.end)
		{
			t_info.end_marker = new GMarker(trail.end_loc, opts.end);
			t_info.end_marker.trail = trail;
		}
		
		if(opts.filtered)
		{
			t_info.filtered_marker = new GMarker(trail.start_loc, opts.filtered);
			t_info.filtered_marker.trail = trail;
		}		
		
		overlays.set(trail.id, t_info);
		
		if(opts.path) 
		{
			if (trail.hasPoints()) 
			{
				create_path_overlay(t_info);
			} else 
			{
				queue.set(trail.id, t_info);
				this.Activate_Loading();
			}
		}
		
		if(force_update)
			this.Update_Specific_Overlays([t_info]);
	}
	
	this.Load_Trail_Immediately = function(trail){
		if (trail.hasPoints()) 
			return;

		request_trail_paths([trail.id]);
	}
	
	this.Create_Path_For_Trail = function(trail){
		var t_info = overlays.get(trail.id);
		create_path_overlay(t_info);
	}

	this.Update_One_Trail = function(trail){
		var over = overlays.get(trail.id);
		this.Update_Specific_Overlays([over]);
	}

	//=== Private Section
	function create_path_overlay(t_info)
	{
		var trail = t_info.trail;
		
		if (t_info.overlays) 
			return;
			
		var opts = t_info.options;
		
		if (trail.hasPoints()) 
		{
			t_info.overlays = [];
			trail.tracks.each(function(track){
				var path = track.points.pluck('latlng');
				t_info.overlays.push(new GPolyline(path, opts.color, opts.width, opts.opacity, {clickable:false}));
				trail.overlays = t_info.overlays;
			});
		}
	}
	
	function show_overlay(over)
	{
		if (over.each) {
			return over.all(show_overlay);
		}
		
		if(!over.isHidden() && over.added)
			return true;
			
		if (at_Max_Overlays()) 
			return false;
		
		//10 for lines, 1 for markers
		visible_overlays += over.getLength ? 10 : 1

		Map.show_overlay(over);
		return true;
	}

	function hide_overlay(over)
	{
		if (over.each) {
			return over.each(hide_overlay);
		}
		
		if(over.isHidden() || !over.added)
			return;
			
		//10 for lines, 1 for markers
		visible_overlays -= over.getLength ? 10 : 1;
		if(visible_overlays < 0) visible_overlays = 0;
		
		Map.hide_overlay(over);
	}
	
	function update_limit_warning(override)
	{
		if(!UI.Max_Overlay_Warning) return;
		
		if(override == true || at_Max_Overlays())
		{
			if (!limit_warning_visible) 
			{
				UI.Max_Overlay_Warning.show();
				limit_warning_visible = true;
			}
		}
		else if(override == false || limit_warning_visible)
		{
			UI.Max_Overlay_Warning.hide();
			limit_warning_visible = false;			
		}
	}
	

	function request_trail_paths(trail_ids)
	{
	    ajax_request = new Ajax.Request('/trails/get/', { 
	        method: 'get',
			parameters: {id: trail_ids.join(',')},
	        onSuccess: success_callback,
			onComplete: finished_callback
	        });
			
		trail_ids.each(function(id){queue.unset(id);});
	}
	
	var success_callback = function(response)
	{
		var results = Search.ReadResults(response.responseXML);

		if (results) 
		{
			var new_trails = results.all_results.map(function(trail)
			{
				var t_info = overlays.get(trail.id);
				create_path_overlay(t_info);
				return t_info
			});
			this.Update_Specific_Overlays(new_trails);
		}
		
		ajax_request = null;
		
	}.bind(this);
	
	
	var finished_callback = function()
	{
		ajax_request = null;
		
	}.bind(this);
	
	return this;
})();


if (Include_map) 
{
	// ============= Toolbox Control ================
	function ToolBoxControl()
	{
	    var btn_prefix = "toolbtn_";  
		var button_info = $H({
			han:		{mode:'pan',		cursor:'hand'},
			mov:		{mode:'move',		cursor:'hand'},
			add:		{mode:'edit',		cusror:'cross'},
			del:		{mode:'delete',		cusror:'hand'},
			sel:		{mode:'measure',	cusror:'pointer'},
			mes:		{mode:'select',		cusror:'pointer'},
			add_track:	{mode:'add_track',	cusror:'cross'},	
			rec:	{},
			joi:	{},
			spl:	{},
			rem:	{}
		});
		
		this.action_buttons = [];
		this.mode_to_button_map = $H();
		this.button_info = $H();
		
		button_info.each(function(pair){
			var code = pair.key;
			var element_id = btn_prefix + code;
			var btn = $(element_id);
			if (!btn) 
				return;
			
			//Create button constants like BTN_ADD = toolbtn_add;
			this["BTN_" + code.toUpperCase()] = btn;

			GEvent.addDomListener(btn, "click", this.button_click_handler.bindAsEventListener(this));
			
			if(pair.value.mode)
				this.mode_to_button_map.set(pair.value.mode, btn);
			else
				this.action_buttons.push(btn);
			
			this.button_info.set(element_id, pair.value);
		}, this);
		
		this.toolmode_buttons = this.mode_to_button_map.values();
		 
		this.setSelected(this.BTN_HAN);
		
		UI.Map_Edit_Marker_Mover.mode_changed.observe(this.mode_changed_handler, this);
	}

	ToolBoxControl.prototype.mode_changed_handler = function(info){
		var btn = this.mode_to_button_map.get(info.new_mode);
		this.setSelected(btn);
	}

	ToolBoxControl.prototype.setSelected = function(selected)
	{
		this.toolmode_buttons.each(function(btn)
		{
			btn.removeClassName('selected');
		});
		sel_element = $(selected);
		sel_element.addClassName("selected");
		this.Selected = sel_element;
	}
		
	ToolBoxControl.prototype.button_click_handler = function(event)
	{
		var btn = event.target;
		
		if (this.action_buttons.include(btn)) 
		{
			switch (btn)
			{
				case this.BTN_REC:
					if (currentTrail) {
						currentTrail.Recalculate_Extents();
						Map.RecenterView(currentTrail);
					}
					break;
				case this.BTN_REM:
					if (UI.Map_Edit_Marker_Mover.hasSelectedSegment() &&
						confirm('Are you sure you want to delete the selected segment of the Trail?')) 
					{
						var ts = UI.Map_Edit_Marker_Mover.getSelectedSegment();
						
						if(ts.track == currentTrail.get_primary_track() && ts.isEntireTrack()){
							alert("You can't delete the last point of the primary track, though you can move it.");
							break;
						}
							
						currentTrail.removeTrailSegment(ts);
						ts.reset();
						map.closeInfoWindow();
					}
					break;
			}
		} else if (this.toolmode_buttons.include(btn)) 
		{
			if(this.Selected == btn)
				return true;
				
			var mc = Common.map;
			
			var set_cursor_class = function(which_cursor){
				$w('hand cross pointer').each(function(cursor){
					mc[(cursor == which_cursor ? 'add' : 'remove') + 'ClassName']('cur_' + cursor);
				});
			}
			
			var info = this.button_info.get(btn.id);
			UI.Map_Edit_Marker_Mover.set_mode(info.mode);
			set_cursor_class(info.cursor);
			
			var select_tools = $('select_tools')
			if (select_tools) {
				if(btn == this.BTN_SEL)
					select_tools.show();
				else
					select_tools.hide();
			}
		}
		return true;
	}
}


//=============== Base Map Control ==============
function BaseMapControl(div_id)
{
    this.control_div_id = div_id;
}

if(Include_map)
{
    var base = new GControl(false, false);
	BaseMapControl.prototype = base;

    base.initialize = function(map)
    {
        var container = $(this.control_div_id);
        container.remove();
        map.getContainer().appendChild(container);

        //The dragging bounds don't seem to work yet.
        //this.dragger = new GDraggableObject(container, $("map"));

        return container;
    }
    
    base.getDefaultPosition = function()
    {
        return this._position;
    }
	
	base.hide = function() {$(this.control_div_id).hide();};
	base.show = function() {$(this.control_div_id).show();};	
	base.set_Position = function(anchor, offset){
		this._position = new GControlPosition(anchor, offset);
	}
	base.set_Position(G_ANCHOR_BOTTOM_RIGHT, new GSize(200, 15));
}


//=============== Trail Facts Control ==============
function TrailFactsControl()
{
}

if (Include_map) 
{
	TrailFactsControl.prototype = new GControl(false, false);
	TrailFactsControl.prototype.initialize = function(map)
	{
		var container = $("trail_info");
		container.remove();
		map.getContainer().appendChild(container);
		Event.observe($("info_units_link"), "click", ToggleUnits);
		
		//The dragging bounds don't seem to work yet.
		//this.dragger = new GDraggableObject(container, $("map"));
		return container;
	}
	
	TrailFactsControl.prototype.getDefaultPosition = function()
	{
		return new GControlPosition(G_ANCHOR_BOTTOM_RIGHT, new GSize(15, 15));
	}
	
	function ToggleUnits()
	{
		Units.useMetric = !Units.useMetric;
		UI.UpdateTrailFacts(currentTrail.get_primary_track(), true);
	}
}

var Accounts = {}


function Submit_Resend_Confirmation_Form()
{
    var form = $("confirm");
    form.submit();
}


//========= Modify Profile
function Submit_Profile()
{
    var errors = []
    
    var result = Validate_Password(true);
    if(result) errors.push(result);

    result = Validate_Email(true);
    if(result) errors.push(result);
    
    if(errors.length > 0 )
    {
        alert(errors[0]);
        return false;
    }
    
    var pass1 = $F("post_password");

    if(pass1 == '' || pass1 == null)
        $("post_cryptpassword").value = '';
    else
        $("post_cryptpassword").value = SHA1(window.SALT + pass1);    

    $("post_password").value = "";
    $("post_password_confirm").value = "";
    
    return true;
}


//========= Signin
function Sign_In()
{
    var pass1 = $F("post_password");

    $("post_cryptpassword").value = SHA1(window.challenge + SHA1(window.SALT + pass1));
    $("post_cryptpassword_old").value = SHA1(window.challenge + SHA1(window.SALT + SHA1(pass1)));	
    $("post_password").value = "";
    
    var form = $("signin_form");
    UI.show_Busy_Message("Signing In");
    form.request({evalScripts: false, onSuccess: Sign_in_Response, onFailure: Sign_in_Failed});
    form.disable();

    return false;
}

function Sign_in_Response(xhr)
{
    $("signin_form").enable();
    
    if(xhr.responseText == "OK")
    {
        UI.closeLightbox();
                
        if(window.redirect)
        {
            window.location.href = window.redirect;
        }
        else
            new Ajax.Updater(Common.noncontent,"/account/top");
	
		UI.show_Positive_Message("Signed In");
		UI.hide_Message.delay(UI.MSG_DELAY);			
    }
    else
    {
        $('signin_errors').update(xhr.responseText);
		UI.hide_Message();		
    }
}

function Sign_in_Failed(xhr)
{
    UI.show_Error_Message("Something is wrong with our servers, please try again later");
	UI.hide_Message.delay(UI.MSG_DELAY);

    lightbox.prototype.deactivate();
}

//========= Signup

function Submit_Signup_Form()
{
    var errors = Validate_Fields();
    
    if(errors)
    {
        alert(errors[0]);
        return false;
    }
    
    var login = $F("post_login");
    var pass1 = $F("post_password");
    var pass2 = $F("post_password_confirm");
    var email = $F("post_email");

    $("post_cryptpassword").value = SHA1(window.SALT + pass1);;
    $("post_password").value = "";
    $("post_password_confirm").value = "";
    
    var form = $("signup");
    form.submit();
}

function Validate_Fields()
{
    var errors = [];    
    var result;

    result = Validate_Login_ID();
    if(result) errors.push(result);

    result = Validate_Password();
    if(result) errors.push(result);

    result = Validate_Email();
    if(result) errors.push(result);

    if(errors.length > 0)
        return errors;
    else
        return null;
}


function Validate_Login_ID()
{
    if(!$("post_login"))
        return null;
    
    var login = $F("post_login");

    if(login == "")
        return "Please enter a username";
    
    //Minimum length of ID
    if(login.length < 2)
        return "Username must be at least 2 letters.";
        
    if(!login.match(/^[a-zA-Z0-9]+$/))
        return "Username must be only letters and numbers.";
}

function Validate_Password(allow_blank)
{
    var pass1 = $F("post_password");
    var pass2 = $F("post_password_confirm");

    if(allow_blank && pass1 == '' && pass2 == '')
        return;

    if(pass1 != pass2)
        return "Password and Confirm password must be the same.";
        
    if(pass1 == "")
        return "Please enter a password";
    
    if(pass1.length < 4)
        return "Password must be at least 4 characters";
}

function Validate_Email(allow_blank)
{
    var email = $F("post_email");

    if(allow_blank && email == '')
        return;
        
    if(email == "")
        return "Please enter an email address";
        
    if(!email.match(/^.+@.+\..{2,4}$/))
        return email + " is not a valid email address.";
}


/**
*
*  Secure Hash Algorithm (SHA1)
*  http://www.webtoolkit.info/
*
**/

function SHA1 (msg) {

	function rotate_left(n,s) {
		var t4 = ( n<<s ) | (n>>>(32-s));
		return t4;
	};

	function lsb_hex(val) {
		var str="";
		var i;
		var vh;
		var vl;

		for( i=0; i<=6; i+=2 ) {
			vh = (val>>>(i*4+4))&0x0f;
			vl = (val>>>(i*4))&0x0f;
			str += vh.toString(16) + vl.toString(16);
		}
		return str;
	};

	function cvt_hex(val) {
		var str="";
		var i;
		var v;

		for( i=7; i>=0; i-- ) {
			v = (val>>>(i*4))&0x0f;
			str += v.toString(16);
		}
		return str;
	};


	function Utf8Encode(string) {
		string = string.replace(/\r\n/g,"\n");
		var utftext = "";

		for (var n = 0; n < string.length; n++) {

			var c = string.charCodeAt(n);

			if (c < 128) {
				utftext += String.fromCharCode(c);
			}
			else if((c > 127) && (c < 2048)) {
				utftext += String.fromCharCode((c >> 6) | 192);
				utftext += String.fromCharCode((c & 63) | 128);
			}
			else {
				utftext += String.fromCharCode((c >> 12) | 224);
				utftext += String.fromCharCode(((c >> 6) & 63) | 128);
				utftext += String.fromCharCode((c & 63) | 128);
			}

		}

		return utftext;
	};

	var blockstart;
	var i, j;
	var W = new Array(80);
	var H0 = 0x67452301;
	var H1 = 0xEFCDAB89;
	var H2 = 0x98BADCFE;
	var H3 = 0x10325476;
	var H4 = 0xC3D2E1F0;
	var A, B, C, D, E;
	var temp;

	msg = Utf8Encode(msg);

	var msg_len = msg.length;

	var word_array = new Array();
	for( i=0; i<msg_len-3; i+=4 ) {
		j = msg.charCodeAt(i)<<24 | msg.charCodeAt(i+1)<<16 |
		msg.charCodeAt(i+2)<<8 | msg.charCodeAt(i+3);
		word_array.push( j );
	}

	switch( msg_len % 4 ) {
		case 0:
			i = 0x080000000;
		break;
		case 1:
			i = msg.charCodeAt(msg_len-1)<<24 | 0x0800000;
		break;

		case 2:
			i = msg.charCodeAt(msg_len-2)<<24 | msg.charCodeAt(msg_len-1)<<16 | 0x08000;
		break;

		case 3:
			i = msg.charCodeAt(msg_len-3)<<24 | msg.charCodeAt(msg_len-2)<<16 | msg.charCodeAt(msg_len-1)<<8	| 0x80;
		break;
	}

	word_array.push( i );

	while( (word_array.length % 16) != 14 ) word_array.push( 0 );

	word_array.push( msg_len>>>29 );
	word_array.push( (msg_len<<3)&0x0ffffffff );


	for ( blockstart=0; blockstart<word_array.length; blockstart+=16 ) {

		for( i=0; i<16; i++ ) W[i] = word_array[blockstart+i];
		for( i=16; i<=79; i++ ) W[i] = rotate_left(W[i-3] ^ W[i-8] ^ W[i-14] ^ W[i-16], 1);

		A = H0;
		B = H1;
		C = H2;
		D = H3;
		E = H4;

		for( i= 0; i<=19; i++ ) {
			temp = (rotate_left(A,5) + ((B&C) | (~B&D)) + E + W[i] + 0x5A827999) & 0x0ffffffff;
			E = D;
			D = C;
			C = rotate_left(B,30);
			B = A;
			A = temp;
		}

		for( i=20; i<=39; i++ ) {
			temp = (rotate_left(A,5) + (B ^ C ^ D) + E + W[i] + 0x6ED9EBA1) & 0x0ffffffff;
			E = D;
			D = C;
			C = rotate_left(B,30);
			B = A;
			A = temp;
		}

		for( i=40; i<=59; i++ ) {
			temp = (rotate_left(A,5) + ((B&C) | (B&D) | (C&D)) + E + W[i] + 0x8F1BBCDC) & 0x0ffffffff;
			E = D;
			D = C;
			C = rotate_left(B,30);
			B = A;
			A = temp;
		}

		for( i=60; i<=79; i++ ) {
			temp = (rotate_left(A,5) + (B ^ C ^ D) + E + W[i] + 0xCA62C1D6) & 0x0ffffffff;
			E = D;
			D = C;
			C = rotate_left(B,30);
			B = A;
			A = temp;
		}

		H0 = (H0 + A) & 0x0ffffffff;
		H1 = (H1 + B) & 0x0ffffffff;
		H2 = (H2 + C) & 0x0ffffffff;
		H3 = (H3 + D) & 0x0ffffffff;
		H4 = (H4 + E) & 0x0ffffffff;

	}

	var temp = cvt_hex(H0) + cvt_hex(H1) + cvt_hex(H2) + cvt_hex(H3) + cvt_hex(H4);

	return temp.toLowerCase();

}

/*
Created By: Chris Campbell
Website: http://particletree.com
Date: 2/1/2006

Inspired by the lightbox implementation found at http://www.huddletogether.com/projects/lightbox/
*/

/*-------------------------------GLOBAL VARIABLES------------------------------------*/

var detect = navigator.userAgent.toLowerCase();
var OS,browser,version,total,thestring;

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

//Browser detect script origionally created by Peter Paul Koch at http://www.quirksmode.org/

function getBrowserInfo() {
	if (checkIt('konqueror')) {
		browser = "Konqueror";
		OS = "Linux";
	}
	else if (checkIt('safari')) browser 	= "Safari"
	else if (checkIt('omniweb')) browser 	= "OmniWeb"
	else if (checkIt('opera')) browser 		= "Opera"
	else if (checkIt('webtv')) browser 		= "WebTV";
	else if (checkIt('icab')) browser 		= "iCab"
	else if (checkIt('msie')) browser 		= "Internet Explorer"
	else if (!checkIt('compatible')) {
		browser = "Netscape Navigator"
		version = detect.charAt(8);
	}
	else browser = "An unknown browser";

	if (!version) version = detect.charAt(place + thestring.length);

	if (!OS) {
		if (checkIt('linux')) OS 		= "Linux";
		else if (checkIt('x11')) OS 	= "Unix";
		else if (checkIt('mac')) OS 	= "Mac"
		else if (checkIt('win')) OS 	= "Windows"
		else OS 								= "an unknown operating system";
	}
}

function checkIt(string) {
	place = detect.indexOf(string) + 1;
	thestring = string;
	return place;
}

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

Initializers.push(getBrowserInfo);
Initializers.push(initialize_Lightbox);

var lightbox = Class.create();

lightbox.prototype = {

	yPos : 0,
	xPos : 0,

	initialize: function(ctrl) {
		if (ctrl != null) 
		{
			this.content = ctrl.href;
			Event.observe(ctrl, 'click', function(){
        if(lightbox.content_div) this.deactivate();
        this.activate();
      }.bindAsEventListener(this), false);

			ctrl.onclick = function()
			{
				return false;
			};
		}
	},
	
	// Turn everything on - mainly the IE fixes
	activate: function(){
		if (browser == 'Internet Explorer'){
			this.getScroll();
			this.prepareIE('100%', 'hidden');
			this.setScroll(0,0);
			this.hideSelects('hidden');
		}
		this.displayLightbox("block");
	},
	
	// Ie requires height to 100% and overflow hidden or else you can scroll down past the lightbox
	prepareIE: function(height, overflow){
		bod = document.getElementsByTagName('body')[0];
		bod.style.height = height;
		bod.style.overflow = overflow;
  
		htm = document.getElementsByTagName('html')[0];
		htm.style.height = height;
		htm.style.overflow = overflow; 
	},
	
	// In IE, select elements hover on top of the lightbox
	hideSelects: function(visibility){
		selects = document.getElementsByTagName('select');
		for(i = 0; i < selects.length; i++) {
			selects[i].style.visibility = visibility;
		}
	},
	
	// Taken from lightbox implementation found at http://www.huddletogether.com/projects/lightbox/
	getScroll: function(){
		if (self.pageYOffset) {
			this.yPos = self.pageYOffset;
		} else if (document.documentElement && document.documentElement.scrollTop){
			this.yPos = document.documentElement.scrollTop; 
		} else if (document.body) {
			this.yPos = document.body.scrollTop;
		}
	},
	
	setScroll: function(x, y){
		window.scrollTo(x, y); 
	},
	
	displayLightbox: function(display){
		if ($('overlay')) 
		{
			$('overlay').style.display = display;
			$('lightbox').style.display = display;
			if (display != 'none' && this.content) 
				this.loadInfo();
		}
	},
	
	// Begin Ajax request based off of the href of the clicked linked
	loadInfo: function() {
		var myAjax = new Ajax.Request(
        this.content,
        {method: 'get', parameters: "", onComplete: this.processInfo.bindAsEventListener(this)}
		);
		
	},
	
	// Display Ajax response
	processInfo: function(response){
		info = "<div id='lbContent'>" + response.responseText + "</div>";
		new Insertion.Before($('lbLoadMessage'), info)
		$('lightbox').className = "done";	
		this.actions();			
	},
	
	// Search through new links within the lightbox, and attach click event
	actions: function(){
		lbActions = document.getElementsByClassName('lbAction');

		for(i = 0; i < lbActions.length; i++) {
			Event.observe(lbActions[i], 'click', this[lbActions[i].rel].bindAsEventListener(this), false);
			lbActions[i].onclick = function(){return false;};
		}

	},
	
	// Example of creating your own functionality once lightbox is initiated
	insert: function(e){
	   link = Event.element(e).parentNode;
	   Element.remove($('lbContent'));
	 
	   var myAjax = new Ajax.Request(
			  link.href,
			  {method: 'post', parameters: "", onComplete: this.processInfo.bindAsEventListener(this)}
	   );
	},
	
	showLightboxWithElement:function(content){
		if(lightbox.content_div == content) return;
		if(lightbox.content_div) this.deactivate();
		
		lightbox.content_original_parent = $(content.parentNode);
		lightbox.content_div = content;
		$('lightbox').insert(content);
		$('lightbox').className = "done";
		this.displayLightbox('block');
	},
	
	// Example of creating your own functionality once lightbox is initiated
	deactivate: function(){
		var content = $('lbContent');
		if(content)
			Element.remove($('lbContent'));

		if(lightbox.content_original_parent)
			lightbox.content_original_parent.insert(lightbox.content_div);

		lightbox.content_div = null;
		
		//This has been disabled, it was causing problems in IE
		if (false && browser == "Internet Explorer"){
			this.setScroll(0,this.yPos);
			this.prepareIE("auto", "auto");
			this.hideSelects("visible");
		}
		
		this.displayLightbox("none");
	}
}

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

// Onload, make all links that need to trigger a lightbox active
function initialize_Lightbox(){
	addLightboxMarkup();
	lbox = document.getElementsByClassName('lbOn');
	for(i = 0; i < lbox.length; i++) {
		valid = new lightbox(lbox[i]);
	}
}

	UI.Generic_Lightbox = new lightbox();

// Add in markup necessary to make this work. Basically two divs:
// Overlay holds the shadow
// Lightbox is the centered square that the content is put into.
function addLightboxMarkup() {
	bod 				= document.getElementsByTagName('body')[0];
	overlay 			= document.createElement('div');
	overlay.id		= 'overlay';
	lb					= document.createElement('div');
	lb.id				= 'lightbox';
	lb.className 	= 'lb_loading';
	lb.innerHTML	= '<div id="lbLoadMessage"> </div>';
	bod.appendChild(overlay);
	bod.appendChild(lb);
}

UI.MultiSelect_filter = function(element, options)
{
	var allowed_states = [0,1,2]; 
	
	if(options && options.exclude_states)
		allowed_states = allowed_states.without(options.exclude_states);
		
	var items = new Hash();
	var main_div = element;
	var tag_item_class = "tag_item_filter";
	var filter_changed_events = [];
	
	var tag_items = $A(main_div.getElementsByClassName(tag_item_class));
	
	if (tag_items){
		tag_items.each(function(item){
			Event.observe(item, 'click', Item_Click_Handler.bindAsEventListener(this));
			item.tag_id = item.id.slice(-2);
			item.state = 0;
			items.set(item.tag_id, item)
		});
	}
	
	function Item_Click_Handler(event)
	{
		var item = event.element();
		
		advance_state_of_item(item);
		
		filter_changed_events.each(function (func){func();});
		
		return false;
	}
	
	this.add_filter_changed_observer = function(func){
		filter_changed_events.push(func);
	}
	
	function advance_state_of_item(item)
	{
		var state_index = (allowed_states.indexOf(item.state) + 1) % allowed_states.length;
		if (state_index == -1)
			return;
			
		set_item_state(item, allowed_states[state_index]);
	}
	
	function set_item_state(item, state)
	{
		item.removeClassName("inc");
		item.removeClassName("exc");
		item.state = state;
		
		switch(state)
		{
			case 1:	item.addClassName("inc"); break;
			case 2:	item.addClassName("exc"); break;
		}		
	}

	this.set_items_states = function(ids, state)  
	{
		ids.each(function(id){
			var item = items.get(id);
			if(item)
				set_item_state(item, state);
		});
	}
	
	this.getSelectedIds = function(CSS_class)
	{
		 return items.map(
		 	function (pair){
				return pair[1].hasClassName(CSS_class) ? pair.key : null;
			}).compact();
	};

	this.clearSelections = function()
	{
		 return items.values().each(function (item){
			item.removeClassName("inc");
			item.removeClassName("exc");
			item.state = 0;		 	
		 });
	};
};


UI.MultiSelect = function(element, list, new_options)
{
	var me = this;
	var options = {};
	var parent = element;
	var menu_container = new_options.readonly? null : (new_options.menu || element.down('.menu'));
	var items_container = $(element.identify() + "_items");
	var items = list;
	var selected_items = new Hash();
	var unselected_items = new Hash();
	var hide_menu_timer;

	function populateMenu()
	{
		items.each(function(item)
		{
			if (!options.readonly) 
			{
				item.menu_element = options.createMenuElement(item);
				menu_container.insert(item.menu_element);
				Event.observe(item.menu_element, "click", clickEventHandler.bindAsEventListener(item));
			}
			unselected_items.set(item.id, item);
		});

		if (!options.readonly) 
		{
			Event.observe(menu_container, 'mouseout', me.hideMenu);
			Event.observe(menu_container, 'mouseover',me.cancelHideMenu);
		}
	}
	
	function clickEventHandler(event)
	{
		if (!options.readonly){
			me.addItem(this);
			menu_container.hide();
		}
	}
	
	function _hideMenu()
	{
		menu_container.hide();
	}
	
	options.createMenuElement = function(item)
	{
		var container = $(document.createElement("div"));
		container.insert(item.name);
		container.addClassName("menu_item");
		container.addClassName("btn_y");		
		return container;
	}	

	
	options.createDisplayElement = function(item)
	{
		var container = $(document.createElement("div"));
		container.insert("<span class='nowrap'>" + item.name + "</span>");

		if(!this.readonly)
		{
			container.addClassName("tag_item");
			var img = $(document.createElement("img"));
			
			img.setAttribute("src", "/images/buttons/greyX.png");
			img.addClassName("greyX");
	
			container.insert(img);
			item.remove_button = img;
		}
		else
			container.addClassName("tag_item_readonly");

		return container;
	}	


	Object.extend(options, new_options);

	this.showMenu = function(){
		if (unselected_items.size() > 0) 
		{
			//menu_container.
			new Effect.BlindDown(menu_container, 
			{
				duration: 0.05 * (unselected_items.size() + 1)
			});
		}
	}
	
	this.hideMenu = function(event){
		if(hide_menu_timer)	clearTimeout(hide_menu_timer);
		hide_menu_timer = setTimeout(_hideMenu, 250);
	}
	
	this.cancelHideMenu = function(event){
		if(hide_menu_timer)
			clearTimeout(hide_menu_timer);
		hide_menu_timer = null;
	}	
	
	
	this.getSelectedIds = function()  
	{
		return selected_items.values().pluck("id");
	}

	this.setSelectedIds = function(ids)  
	{
		this.clear();
		var me = this;
		
		ids.each(this.addItemById.bind(this));
	}


	this.addSelectedItem = function()  
	{
		if(options.readonly)
			return;
			
		var selected_element = select.options[select.selectedIndex];
		var item = items.find(function(item){
			return item.opt_element.value == selected_element.value;
		});

		this.addItem(item);
	}

	this.addItemById = function(id)
	{
		if(!id || id == '')
			return;
			
		var item = unselected_items.get(id);
		
		if(item)
			this.addItem(item);
	}
	
	this.clear = function()
	{
		selected_items.values().each(this.removeItem.bind(this));
	}
	
	this.addItem = function(item)  
	{
		if(selected_items.get(item.id))
			return;

		if(!item.display_element) 
			item.display_element = options.createDisplayElement(item);

		items_container.insert(item.display_element);
		
		if (!options.readonly) 
		{
			item.remove_button.item = item;
			Event.observe(item.remove_button, "click", this.remove_Click_Handler);
			item.menu_element.remove();
			new Effect.Highlight(item.display_element);
		}
		
		selected_items.set(item.id, item);
		unselected_items.unset(item.id);
	}

	
	this.remove_Click_Handler = (function(event)
	{
		this.removeItem(event.element().item);
	}).bindAsEventListener(this);
	
	
	this.removeItem = function(item)  
	{
		if (!options.readonly) 
		{
			Event.stopObserving(item.display_element, "click", this.remove_Click_Handler);
			menu_container.insert(item.menu_element);
		}	
		
		item.display_element.remove();
		selected_items.unset(item.id);
		unselected_items.set(item.id, item);
	}
	
	this.getElement = function(){return parent;};

	populateMenu();	

	//if(options.add_button && !options.readonly)
		//Event.observe($(options.add_button), "click", this.addSelectedItem.bindAsEventListener(this));
}

/*----------------------------------------------------------------------------\
|                                Slider 1.02                                  |
|-----------------------------------------------------------------------------|
|                         Created by Erik Arvidsson                           |
|                  (http://webfx.eae.net/contact.html#erik)                   |
|                      For WebFX (http://webfx.eae.net/)                      |
|-----------------------------------------------------------------------------|
|                Copyright (c) 2002, 2003, 2006 Erik Arvidsson                |
|-----------------------------------------------------------------------------|
| Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| use this file except in compliance with the License.  You may obtain a copy |
| of the License at http://www.apache.org/licenses/LICENSE-2.0                |
\----------------------------------------------------------------------------*/

Slider.isSupported = typeof document.createElement != "undefined" &&
	typeof document.documentElement != "undefined" &&
	typeof document.documentElement.offsetWidth == "number";


function Slider(oElement, oInput, sOrientation) {
	if (!oElement) return;
	this._orientation = sOrientation || "horizontal";
	this._range = new Slider_Range();
	this._range.setExtent(0);
	this._blockIncrement = 10;
	this._unitIncrement = 1;
	this._value_is_set = false;
	this._timer = new Slider_Timer(100);


	if (Slider.isSupported && oElement) {

		this.document = oElement.ownerDocument || oElement.document;

		this.element = oElement;
		this.element.slider = this;
		this.element.unselectable = "on";

		// add class name tag to class name
		this.element.className = this._orientation + " " + this.classNameTag + " " + this.element.className;

		// create line
		this.line = this.document.createElement("DIV");
		this.line.className = "line";
		this.line.unselectable = "on";
		this.line.appendChild(this.document.createElement("DIV"));
		this.element.appendChild(this.line);

		// create handle
		this.handle = this.document.createElement("DIV");
		this.handle.className = "handle";
		this.handle.unselectable = "on";
		this.handle.appendChild(this.document.createElement("DIV"));
		this.handle.firstChild.appendChild(
			this.document.createTextNode(String.fromCharCode(160)));
		this.element.appendChild(this.handle);
	}

	this.input = oInput;

	// events
	var oThis = this;
	this._range.onchange = function () {
		oThis.recalculate();
		if (typeof oThis.onchange == "function")
			oThis.onchange();
	};

	if (Slider.isSupported && oElement) {
	    var eh = Slider.eventHandlers;
		this.element.onfocus		= eh.onfocus;
		this.element.onblur			= eh.onblur;
		this.element.onmousedown	= eh.onmousedown;
		this.element.onmouseover	= eh.onmouseover;
		this.element.onmouseout		= eh.onmouseout;
		this.element.onkeydown		= eh.onkeydown;
		this.element.onkeypress		= eh.onkeypress;
		this.element.onmousewheel	= eh.onmousewheel;
		this.handle.onselectstart	=
		this.element.onselectstart	= function () { return false; };

		this._timer.ontimer = function () {
			oThis.ontimer();
		};

		// extra recalculate for ie
		window.setTimeout(function() {
			oThis.recalculate();
		}, 1);
	}
	else {
		this.input.onchange = function (e) {
			oThis.setValue(oThis.input.value);
		};
	}
}

Slider.eventHandlers = {

	// helpers to make events a bit easier
	getEvent:	function (e, el) {
		if (!e) {
			if (el)
				e = el.document.parentWindow.event;
			else
				e = window.event;
		}
		if (!e.srcElement) {
			var el = e.target;
			while (el != null && el.nodeType != 1)
				el = el.parentNode;
			e.srcElement = el;
		}
		if (typeof e.offsetX == "undefined") {
			e.offsetX = e.layerX;
			e.offsetY = e.layerY;
		}

		return e;
	},

	getDocument:	function (e) {
		if (e.target)
			return e.target.ownerDocument;
		return e.srcElement.document;
	},

	getSlider:	function (e) {
		var el = e.target || e.srcElement;
		while (el != null && el.slider == null)	{
			el = el.parentNode;
		}
		if (el)
			return el.slider;
		return null;
	},

	getLine:	function (e) {
		var el = e.target || e.srcElement;
		while (el != null && el.className != "line")	{
			el = el.parentNode;
		}
		return el;
	},

	getHandle:	function (e) {
		var el = e.target || e.srcElement;
		var re = /handle/;
		while (el != null && !re.test(el.className))	{
			el = el.parentNode;
		}
		return el;
	},
	// end helpers

	onfocus:	function (e) {
		var s = this.slider;
		s._focused = true;
		s.handle.className = "handle hover";
	},

	onblur:	function (e) {
		var s = this.slider
		s._focused = false;
		s.handle.className = "handle";
	},

	onmouseover:	function (e) {
		e = Slider.eventHandlers.getEvent(e, this);
		var s = this.slider;
		if (e.srcElement == s.handle)
			s.handle.className = "handle hover";
	},

	onmouseout:	function (e) {
		e = Slider.eventHandlers.getEvent(e, this);
		var s = this.slider;
		if (e.srcElement == s.handle && !s._focused)
			s.handle.className = "handle";
	},

	onmousedown:	function (e) {
		e = Slider.eventHandlers.getEvent(e, this);
		var s = this.slider;
		if (s.element.focus)
			s.element.focus();

		Slider._currentInstance = s;
		var doc = s.document;

		if (doc.addEventListener) {
			doc.addEventListener("mousemove", Slider.eventHandlers.onmousemove, true);
			doc.addEventListener("mouseup", Slider.eventHandlers.onmouseup, true);
		}
		else if (doc.attachEvent) {
			doc.attachEvent("onmousemove", Slider.eventHandlers.onmousemove);
			doc.attachEvent("onmouseup", Slider.eventHandlers.onmouseup);
			doc.attachEvent("onlosecapture", Slider.eventHandlers.onmouseup);
			s.element.setCapture();
		}

		if (Slider.eventHandlers.getHandle(e)) {	// start drag
			Slider._sliderDragData = {
				screenX:	e.screenX,
				screenY:	e.screenY,
				dx:			e.screenX - s.handle.offsetLeft,
				dy:			e.screenY - s.handle.offsetTop,
				startValue:	s.getValue(),
				slider:		s
			};
		}
		else {
			var lineEl = Slider.eventHandlers.getLine(e);
			s._mouseX = e.offsetX + (lineEl ? s.line.offsetLeft : 0);
			s._mouseY = e.offsetY + (lineEl ? s.line.offsetTop : 0);
			s._increasing = null;
			s.ontimer();
		}
	},

	onmousemove:	function (e) {
		e = Slider.eventHandlers.getEvent(e, this);

		if (Slider._sliderDragData) {	// drag
			var s = Slider._sliderDragData.slider;

			var boundSize = s.getMaximum() - s.getMinimum();
			var size, pos, reset;

			if (s._orientation == "horizontal") {
				size = s.element.offsetWidth - s.handle.offsetWidth;
				pos = e.screenX - Slider._sliderDragData.dx;
				reset = Math.abs(e.screenY - Slider._sliderDragData.screenY) > 100;
			}
			else {
				size = s.element.offsetHeight - s.handle.offsetHeight;
				pos = s.element.offsetHeight - s.handle.offsetHeight -
					(e.screenY - Slider._sliderDragData.dy);
				reset = Math.abs(e.screenX - Slider._sliderDragData.screenX) > 100;
			}
			s.setValue(reset ? Slider._sliderDragData.startValue :
						s.getMinimum() + boundSize * pos / size);
			return false;
		}
		else {
			var s = Slider._currentInstance;
			if (s != null) {
				var lineEl = Slider.eventHandlers.getLine(e);
				s._mouseX = e.offsetX + (lineEl ? s.line.offsetLeft : 0);
				s._mouseY = e.offsetY + (lineEl ? s.line.offsetTop : 0);
			}
		}

	},

	onmouseup:	function (e) {
		e = Slider.eventHandlers.getEvent(e, this);
		var s = Slider._currentInstance;
		var doc = s.document;
		if (doc.removeEventListener) {
			doc.removeEventListener("mousemove", Slider.eventHandlers.onmousemove, true);
			doc.removeEventListener("mouseup", Slider.eventHandlers.onmouseup, true);
		}
		else if (doc.detachEvent) {
			doc.detachEvent("onmousemove", Slider.eventHandlers.onmousemove);
			doc.detachEvent("onmouseup", Slider.eventHandlers.onmouseup);
			doc.detachEvent("onlosecapture", Slider.eventHandlers.onmouseup);
			s.element.releaseCapture();
		}

		if (Slider._sliderDragData) {	// end drag
			Slider._sliderDragData = null;
		}
		else {
			s._timer.stop();
			s._increasing = null;
		}
		Slider._currentInstance = null;
	},

	onkeydown:	function (e) {
		e = Slider.eventHandlers.getEvent(e, this);
		//var s = Slider.eventHandlers.getSlider(e);
		var s = this.slider;
		var kc = e.keyCode;
		switch (kc) {
			case 33:	// page up
				s.setValue(s.getValue() + s.getBlockIncrement());
				break;
			case 34:	// page down
				s.setValue(s.getValue() - s.getBlockIncrement());
				break;
			case 35:	// end
				s.setValue(s.getOrientation() == "horizontal" ?
					s.getMaximum() :
					s.getMinimum());
				break;
			case 36:	// home
				s.setValue(s.getOrientation() == "horizontal" ?
					s.getMinimum() :
					s.getMaximum());
				break;
			case 38:	// up
			case 39:	// right
				s.setValue(s.getValue() + s.getUnitIncrement());
				break;

			case 37:	// left
			case 40:	// down
				s.setValue(s.getValue() - s.getUnitIncrement());
				break;
		}

		if (kc >= 33 && kc <= 40) {
			return false;
		}
	},

	onkeypress:	function (e) {
		e = Slider.eventHandlers.getEvent(e, this);
		var kc = e.keyCode;
		if (kc >= 33 && kc <= 40) {
			return false;
		}
	},

	onmousewheel:	function (e) {
		e = Slider.eventHandlers.getEvent(e, this);
		var s = this.slider;
		if (s._focused) {
			s.setValue(s.getValue() + e.wheelDelta / 120 * s.getUnitIncrement());
			// windows inverts this on horizontal sliders. That does not
			// make sense to me
			return false;
		}
	}
};



Slider.prototype.classNameTag = "slider-control",

Slider.prototype.setValue = function (v) {
	this._value_is_set = (v != null);
	if (v == null) return this._range.onchange();
	
	this._range.setValue(v);
	this.input.value = this.getValue();
};

Slider.prototype.getValue = function () {
	return this._range.getValue();
};

Slider.prototype.isValueSet = function () {
	return this._value_is_set;
};

Slider.prototype.setMinimum = function (v) {
	this._range.setMinimum(v);
	this.input.value = this.getValue();
};

Slider.prototype.getMinimum = function () {
	return this._range.getMinimum();
};

Slider.prototype.setMaximum = function (v) {
	this._range.setMaximum(v);
	this.input.value = this.getValue();
};

Slider.prototype.getMaximum = function () {
	return this._range.getMaximum();
};

Slider.prototype.setUnitIncrement = function (v) {
	this._unitIncrement = v;
};

Slider.prototype.getUnitIncrement = function () {
	return this._unitIncrement;
};

Slider.prototype.setBlockIncrement = function (v) {
	this._blockIncrement = v;
};

Slider.prototype.getBlockIncrement = function () {
	return this._blockIncrement;
};

Slider.prototype.getOrientation = function () {
	return this._orientation;
};

Slider.prototype.setOrientation = function (sOrientation) {
	if (sOrientation != this._orientation) {
		if (Slider.isSupported && this.element) {
			// add class name tag to class name
			this.element.className = this.element.className.replace(this._orientation,
									sOrientation);
		}
		this._orientation = sOrientation;
		this.recalculate();

	}
};

Slider.prototype.recalculate = function() {
	if (!Slider.isSupported || !this.element) return;

	var w = this.element.offsetWidth;
	var h = this.element.offsetHeight;
	var hw = this.handle.offsetWidth;
	var hh = this.handle.offsetHeight;
	var lw = this.line.offsetWidth;
	var lh = this.line.offsetHeight;

	// this assumes a border-box layout

	if (this._orientation == "horizontal") {
		this.handle.style.left = (w - hw) * (this.getValue() - this.getMinimum()) /
			(this.getMaximum() - this.getMinimum()) + "px";
		this.handle.style.top = (h - hh) / 2 + "px";

		this.line.style.top = (h - lh) / 2 + "px";
		this.line.style.left = hw / 2 + "px";
		//this.line.style.right = hw / 2 + "px";
		this.line.style.width = Math.max(0, w - hw - 2)+ "px";
		this.line.firstChild.style.width = Math.max(0, w - hw - 4)+ "px";
	}
	else {
		this.handle.style.left = (w - hw) / 2 + "px";
		this.handle.style.top = h - hh - (h - hh) * (this.getValue() - this.getMinimum()) /
			(this.getMaximum() - this.getMinimum()) + "px";

		this.line.style.left = (w - lw) / 2 + "px";
		this.line.style.top = hh / 2 + "px";
		this.line.style.height = Math.max(0, h - hh - 2) + "px";	//hard coded border width
		//this.line.style.bottom = hh / 2 + "px";
		this.line.firstChild.style.height = Math.max(0, h - hh - 4) + "px";	//hard coded border width
	}
};

Slider.prototype.ontimer = function () {
	var hw = this.handle.offsetWidth;
	var hh = this.handle.offsetHeight;
	var hl = this.handle.offsetLeft;
	var ht = this.handle.offsetTop;

	if (this._orientation == "horizontal") {
		if (this._mouseX > hl + hw &&
			(this._increasing == null || this._increasing)) {
			this.setValue(this.getValue() + this.getBlockIncrement());
			this._increasing = true;
		}
		else if (this._mouseX < hl &&
			(this._increasing == null || !this._increasing)) {
			this.setValue(this.getValue() - this.getBlockIncrement());
			this._increasing = false;
		}
	}
	else {
		if (this._mouseY > ht + hh &&
			(this._increasing == null || !this._increasing)) {
			this.setValue(this.getValue() - this.getBlockIncrement());
			this._increasing = false;
		}
		else if (this._mouseY < ht &&
			(this._increasing == null || this._increasing)) {
			this.setValue(this.getValue() + this.getBlockIncrement());
			this._increasing = true;
		}
	}

	this._timer.start();
};

// ---  Timer Class

function Slider_Timer(nPauseTime) {
	this._pauseTime = typeof nPauseTime == "undefined" ? 1000 : nPauseTime;
	this._timer = null;
	this._isStarted = false;
}

var s_t = Slider_Timer.prototype;
s_t.start = function () {
	if (this.isStarted())
		this.stop();
	var me = this;
	this._timer = window.setTimeout(function () {
		if (typeof me.ontimer == "function")
			me.ontimer();
	}, this._pauseTime);
	this._isStarted = false;
};

s_t.stop = function () {
	if (this._timer != null)
		window.clearTimeout(this._timer);
	this._isStarted = false;
};

s_t.isStarted = function () {
	return this._isStarted;
};

s_t.getPauseTime = function () {
	return this._pauseTime;
};

s_t.setPauseTime = function (in_t) {
	this._pauseTime = in_t;
};


// ---- Slider Range Class

function Slider_Range() {
	this._value = 0;
	this._minimum = 0;
	this._maximum = 100;
	this._extent = 0;

	this._isChanging = false;
}

var s_r = Slider_Range.prototype;

s_r.setValue = function (value) {
	value = Math.round(parseFloat(value));
	if (isNaN(value)) return;
	if (this._value != value) {
		if (value + this._extent > this._maximum)
			this._value = this._maximum - this._extent;
		else if (value < this._minimum)
			this._value = this._minimum;
		else
			this._value = value;
		if (!this._isChanging && typeof this.onchange == "function")
			 this.onchange();
	}
};

s_r.getValue = function () {
	return this._value;
};

s_r.setExtent = function (extent) {
	if (this._extent != extent) {
		if (extent < 0)
			this._extent = 0;
		else if (this._value + extent > this._maximum)
			this._extent = this._maximum - this._value;
		else
			this._extent = extent;
		if (!this._isChanging && typeof this.onchange == "function")
			this.onchange();
	}
};

s_r.getExtent = function () {
	return this._extent;
};

s_r.setMinimum = function (minimum) {
	if (this._minimum != minimum) {
		var oldIsChanging = this._isChanging;
		this._isChanging = true;

		this._minimum = minimum;

		if (minimum > this._value)
			this.setValue(minimum);
		if (minimum > this._maximum) {
			this._extent = 0;
			this.setMaximum(minimum);
			this.setValue(minimum)
		}
		if (minimum + this._extent > this._maximum)
			this._extent = this._maximum - this._minimum;

		this._isChanging = oldIsChanging;
		if (!this._isChanging && typeof this.onchange == "function")
			this.onchange();
	}
};

s_r.getMinimum = function () {
	return this._minimum;
};

s_r.setMaximum = function (maximum) {
	if (this._maximum != maximum) {
		var oldIsChanging = this._isChanging;
		this._isChanging = true;

		this._maximum = maximum;

		if (maximum < this._value)
			this.setValue(maximum - this._extent);
		if (maximum < this._minimum) {
			this._extent = 0;
			this.setMinimum(maximum);
			this.setValue(this._maximum);
		}
		if (maximum < this._minimum + this._extent)
			this._extent = this._maximum - this._minimum;
		if (maximum < this._value + this._extent)
			this._extent = this._maximum - this._value;

		this._isChanging = oldIsChanging;
		if (!this._isChanging && typeof this.onchange == "function")
			this.onchange();
	}
};

s_r.getMaximum = function () {
	return this._maximum;
};


UI.Radio_Button_Group = function(opts){
	var buttons = [];
	var imgs = [];
	var me = this;
	var values = {};
	var element_ids = {};
	var selected_value = null;
	if(opts == null) opts = {};

	this.add_button = function(el, value){
		el = $(el);
		buttons.push(el);
		imgs.push(el.down('img'));
		
		values[el.id] = value;
		element_ids[value] = el.id;
		
		if(!opts.readonly)
			Event.observe(el, 'click', button_click);		
	}
	
	this.setSelected = function(value){
		var found = element_ids[value];
		select_element($(found));
	}
	
	this.getSelected = function(){
		return selected_value;
	}

	this.setReadonly = function(readonly){
		opts.readonly = readonly;
	}
	
	function button_click(){
		if(!opts.readonly)
			select_element(this);
	}
	
	function select_element(el) {
		var el_id = (el == null || Object.isUndefined(el)) ? null : el['id'];
		selected_value = values[el_id];
		
		for(var i=0; i<buttons.length; i++){
			var btn = buttons[i];
			if(btn.id == el_id){
				if(!opts.readonly) btn.addClassName('back_y');
				imgs[i].src = '/images/buttons/radio-full.png';
				selected = i;
			}else{
				if(!opts.readonly) btn.removeClassName('back_y');
				imgs[i].src = '/images/buttons/radio-empty.png';
			}
		}
	}
}



/**
  *
  *  Copyright 2005 Sabre Airline Solutions
  *
  *  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
  *  file except in compliance with the License. You may obtain a copy of the License at
  *
  *         http://www.apache.org/licenses/LICENSE-2.0
  *
  *  Unless required by applicable law or agreed to in writing, software distributed under the
  *  License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
  *  either express or implied. See the License for the specific language governing permissions
  *  and limitations under the License.
  **/


//-------------------- rico.js
var Rico = {
  Version: '1.1.0',
  prototypeVersion: parseFloat(Prototype.Version.split(".")[0] + "." + Prototype.Version.split(".")[1])
}

//-------------------- ricoColor.js
Rico.Color = Class.create();

Rico.Color.prototype = {

   initialize: function(red, green, blue) {
      this.rgb = { r: red, g : green, b : blue };
   },

   blend: function(other) {
      this.rgb.r = Math.floor((this.rgb.r + other.rgb.r)/2);
      this.rgb.g = Math.floor((this.rgb.g + other.rgb.g)/2);
      this.rgb.b = Math.floor((this.rgb.b + other.rgb.b)/2);
   },

   asRGB: function() {
      return "rgb(" + this.rgb.r + "," + this.rgb.g + "," + this.rgb.b + ")";
   },

   asHex: function() {
      return "#" + this.rgb.r.toColorPart() + this.rgb.g.toColorPart() + this.rgb.b.toColorPart();
   },

   asHSB: function() {
      return Rico.Color.RGBtoHSB(this.rgb.r, this.rgb.g, this.rgb.b);
   },

   toString: function() {
      return this.asHex();
   }

};

Rico.Color.createFromHex = function(hexCode) {
  if(hexCode.length==4) {
    var shortHexCode = hexCode;
    var hexCode = '#';
    for(var i=1;i<4;i++) hexCode += (shortHexCode.charAt(i) + shortHexCode.charAt(i));
  }
   if ( hexCode.indexOf('#') == 0 )
   hexCode = hexCode.substring(1);
   var red   = hexCode.substring(0,2);
   var green = hexCode.substring(2,4);
   var blue  = hexCode.substring(4,6);
   return new Rico.Color( parseInt(red,16), parseInt(green,16), parseInt(blue,16) );
}

/**
 * Factory method for creating a color from the background of
 * an HTML element.
 */
Rico.Color.createColorFromBackground = function(elem) {

   //var actualColor = RicoUtil.getElementsComputedStyle($(elem), "backgroundColor", "background-color"); // Changed to prototype style
	var actualColor = $(elem).getStyle('backgroundColor');

   if ( actualColor == "transparent" && elem.parentNode )
      return Rico.Color.createColorFromBackground(elem.parentNode);

   if ( actualColor == null )
      return new Rico.Color(255,255,255);

   if ( actualColor.indexOf("rgb(") == 0 ) {
      var colors = actualColor.substring(4, actualColor.length - 1 );
      var colorArray = colors.split(",");
      return new Rico.Color( parseInt( colorArray[0] ),
                            parseInt( colorArray[1] ),
                            parseInt( colorArray[2] )  );

   }
   else if ( actualColor.indexOf("#") == 0 ) {
      return Rico.Color.createFromHex(actualColor);
   }
   else
      return new Rico.Color(255,255,255);
}

/* next two functions changed to mootools color.js functions */
Rico.Color.HSBtoRGB = function(hue, saturation, brightness) {
			
			var br = Math.round(brightness / 100 * 255);
			if (this[1] == 0){
				return [br, br, br];
			} else {
				var hue = this[0] % 360;
				var f = hue % 60;
				var p = Math.round((brightness * (100 - saturation)) / 10000 * 255);
				var q = Math.round((brightness * (6000 - saturation * f)) / 600000 * 255);
				var t = Math.round((brightness * (6000 - saturation * (60 - f))) / 600000 * 255);
				switch(Math.floor(hue / 60)){
					case 0: return { r : br, g : t, b : p };
					case 1: return { r : q, g : br, b : p };
					case 2: return { r : p, g : br, b : t };
					case 3: return { r : p, g : q, b : br };
					case 4: return { r : t, g : p, b : br };
					case 5: return { r : br, g : p, b : q };
				}
			}
			return false;
		}

Rico.Color.RGBtoHSB = function(red, green, blue) {
		var hue, saturation, brightness;
		var max = Math.max(red, green, blue), min = Math.min(red, green, blue);
		var delta = max - min;
		brightness = max / 255;
		saturation = (max != 0) ? delta / max : 0;
		if (saturation == 0){
			hue = 0;
		} else {
			var rr = (max - red) / delta;
			var gr = (max - green) / delta;
			var br = (max - blue) / delta;
			if (red == max) hue = br - gr;
			else if (green == max) hue = 2 + rr - br;
			else hue = 4 + gr - rr;
			hue /= 6;
			if (hue < 0) hue++;
		}
		return { h : Math.round(hue * 360), s : Math.round(saturation * 100), b : Math.round(brightness * 100)};
}


//-------------------- ricoCorner.js
Rico.Corner = {

   round: function(e, options) {
      var e = $(e);
      this._setOptions(options);

      var color = this.options.color;
      if ( this.options.color == "fromElement" )
         color = this._background(e);

      var bgColor = this.options.bgColor;
      if ( this.options.bgColor == "fromParent" )
         bgColor = this._background(e.offsetParent);

      this._roundCornersImpl(e, color, bgColor);
   },

   _roundCornersImpl: function(e, color, bgColor) {
      if(this.options.border)
         this._renderBorder(e,bgColor);
      if(this._isTopRounded())
         this._roundTopCorners(e,color,bgColor);
      if(this._isBottomRounded())
         this._roundBottomCorners(e,color,bgColor);
   },

   _renderBorder: function(el,bgColor) {
      var borderValue = "1px solid " + this._borderColor(bgColor);
      var borderL = "border-left: "  + borderValue;
      var borderR = "border-right: " + borderValue;
      var style   = "style='" + borderL + ";" + borderR +  "'";
      el.innerHTML = "<div " + style + ">" + el.innerHTML + "</div>"
   },

   _roundTopCorners: function(el, color, bgColor) {
      var corner = this._createCorner(bgColor);
      for(var i=0 ; i < this.options.numSlices ; i++ )
         corner.appendChild(this._createCornerSlice(color,bgColor,i,"top"));
      el.style.paddingTop = 0;
      el.insertBefore(corner,el.firstChild);
   },

   _roundBottomCorners: function(el, color, bgColor) {
      var corner = this._createCorner(bgColor);
      for(var i=(this.options.numSlices-1) ; i >= 0 ; i-- )
         corner.appendChild(this._createCornerSlice(color,bgColor,i,"bottom"));
      el.style.paddingBottom = 0;
      el.appendChild(corner);
   },

   _createCorner: function(bgColor) {
      var corner = document.createElement("div");
      corner.style.backgroundColor = (this._isTransparent() ? "transparent" : bgColor);
      return corner;
   },

   _createCornerSlice: function(color,bgColor, n, position) {
      var slice = document.createElement("span");

      var inStyle = slice.style;
      inStyle.backgroundColor = color;
      inStyle.display  = "block";
      inStyle.height   = "1px";
      inStyle.overflow = "hidden";
      inStyle.fontSize = "1px";

      var borderColor = this._borderColor(color,bgColor);
      if ( this.options.border && n == 0 ) {
         inStyle.borderTopStyle    = "solid";
         inStyle.borderTopWidth    = "1px";
         inStyle.borderLeftWidth   = "0px";
         inStyle.borderRightWidth  = "0px";
         inStyle.borderBottomWidth = "0px";
         inStyle.height            = "0px"; // assumes css compliant box model
         inStyle.borderColor       = borderColor;
      }
      else if(borderColor) {
         inStyle.borderColor = borderColor;
         inStyle.borderStyle = "solid";
         inStyle.borderWidth = "0px 1px";
      }

      if ( !this.options.compact && (n == (this.options.numSlices-1)) )
         inStyle.height = "2px";

      this._setMargin(slice, n, position);
      this._setBorder(slice, n, position);
      return slice;
   },

   _setOptions: function(options) {
      this.options = {
         corners : "all",
         color   : "fromElement",
         bgColor : "fromParent",
         blend   : true,
         border  : false,
         compact : false
      }
      Object.extend(this.options, options || {});

      this.options.numSlices = this.options.compact ? 2 : 4;
      if ( this._isTransparent() )
         this.options.blend = false;
   },

   _whichSideTop: function() {
      if ( this._hasString(this.options.corners, "all", "top") )
         return "";

      if ( this.options.corners.indexOf("tl") >= 0 && this.options.corners.indexOf("tr") >= 0 )
         return "";

      if (this.options.corners.indexOf("tl") >= 0)
         return "left";
      else if (this.options.corners.indexOf("tr") >= 0)
          return "right";
      return "";
   },

   _whichSideBottom: function() {
      if ( this._hasString(this.options.corners, "all", "bottom") )
         return "";

      if ( this.options.corners.indexOf("bl")>=0 && this.options.corners.indexOf("br")>=0 )
         return "";

      if(this.options.corners.indexOf("bl") >=0)
         return "left";
      else if(this.options.corners.indexOf("br")>=0)
         return "right";
      return "";
   },

   _borderColor : function(color,bgColor) {
      if ( color == "transparent" )
         return bgColor;
      else if ( this.options.border )
         return this.options.border;
      else if ( this.options.blend )
         return this._blend( bgColor, color );
      else
         return "";
   },


   _setMargin: function(el, n, corners) {
      var marginSize = this._marginSize(n);
      var whichSide = corners == "top" ? this._whichSideTop() : this._whichSideBottom();

      if ( whichSide == "left" ) {
         el.style.marginLeft = marginSize + "px"; el.style.marginRight = "0px";
      }
      else if ( whichSide == "right" ) {
         el.style.marginRight = marginSize + "px"; el.style.marginLeft  = "0px";
      }
      else {
         el.style.marginLeft = marginSize + "px"; el.style.marginRight = marginSize + "px";
      }
   },

   _setBorder: function(el,n,corners) {
      var borderSize = this._borderSize(n);
      var whichSide = corners == "top" ? this._whichSideTop() : this._whichSideBottom();
      if ( whichSide == "left" ) {
         el.style.borderLeftWidth = borderSize + "px"; el.style.borderRightWidth = "0px";
      }
      else if ( whichSide == "right" ) {
         el.style.borderRightWidth = borderSize + "px"; el.style.borderLeftWidth  = "0px";
      }
      else {
         el.style.borderLeftWidth = borderSize + "px"; el.style.borderRightWidth = borderSize + "px";
      }
      if (this.options.border != false)
        el.style.borderLeftWidth = borderSize + "px"; el.style.borderRightWidth = borderSize + "px";
   },

   _marginSize: function(n) {
      if ( this._isTransparent() )
         return 0;

      var marginSizes          = [ 5, 3, 2, 1 ];
      var blendedMarginSizes   = [ 3, 2, 1, 0 ];
      var compactMarginSizes   = [ 2, 1 ];
      var smBlendedMarginSizes = [ 1, 0 ];

      if ( this.options.compact && this.options.blend )
         return smBlendedMarginSizes[n];
      else if ( this.options.compact )
         return compactMarginSizes[n];
      else if ( this.options.blend )
         return blendedMarginSizes[n];
      else
         return marginSizes[n];
   },

   _borderSize: function(n) {
      var transparentBorderSizes = [ 5, 3, 2, 1 ];
      var blendedBorderSizes     = [ 2, 1, 1, 1 ];
      var compactBorderSizes     = [ 1, 0 ];
      var actualBorderSizes      = [ 0, 2, 0, 0 ];

      if ( this.options.compact && (this.options.blend || this._isTransparent()) )
         return 1;
      else if ( this.options.compact )
         return compactBorderSizes[n];
      else if ( this.options.blend )
         return blendedBorderSizes[n];
      else if ( this.options.border )
         return actualBorderSizes[n];
      else if ( this._isTransparent() )
         return transparentBorderSizes[n];
      return 0;
   },

   _hasString: function(str) { for(var i=1 ; i<arguments.length ; i++) if (str.indexOf(arguments[i]) >= 0) return true; return false; },
   _blend: function(c1, c2) { var cc1 = Rico.Color.createFromHex(c1); cc1.blend(Rico.Color.createFromHex(c2)); return cc1; },
   _background: function(el) { try { return Rico.Color.createColorFromBackground(el).asHex(); } catch(err) { return "#ffffff"; } },
   _isTransparent: function() { return this.options.color == "transparent"; },
   _isTopRounded: function() { return this._hasString(this.options.corners, "all", "top", "tl", "tr"); },
   _isBottomRounded: function() { return this._hasString(this.options.corners, "all", "bottom", "bl", "br"); },
   _hasSingleTextChild: function(el) { return el.childNodes.length == 1 && el.childNodes[0].nodeType == 3; }
}



function TrailSegment(){
	this.track = 
	this.start_point =
	this.end_point = null;
	
	DefineEvents(this, 'selection_changed');
}

TrailSegment.PointsEnumerable = function(start, end, is_reverse){
	this.track = start.track;
	this.start = start;
	this.end = end;
	this.is_reverse = is_reverse;
};
Object.extend(TrailSegment.PointsEnumerable.prototype, Enumerable);
Object.extend(TrailSegment.PointsEnumerable.prototype, {
	_each:function(iter){
		var p=this.start;
		if(!p) return;
		
		if(this.is_reverse){
			do {
				iter(p);
			} while ((p = p.prev) && p != null && p.next != this.end)
		}else{
			do {
				iter(p);
			} while (p != this.end && (p = p.next) && p != null)
		}
	}
});

Object.extend(TrailSegment.prototype,
{
	setTrack: function(in_track){
		var old_track = this.track
		this.track = in_track;
		if (old_track != in_track) {
			this.points = new TrailSegment.PointsEnumerable(in_track);
			this.reset();
		}
	},
	
	setStart: function(in_start){
		this.start_point = this.points.start = in_start;
		this._start_or_end_changed();
	},
	
	setEnd: function(in_end){
		this.end_point = this.points.end = in_end;
		this._start_or_end_changed();
	},
	
	reset:function(){
		this.start_point = this.points.start =
		this.points.end = this.end_point = null;
		this._start_or_end_changed();		
	},
	
	hasStartEnd: function(){
		return !!(this.start_point && this.end_point);
	},
	
	recalculateStats: function(){
		return this.stats = Track.calculateStatsForSegment(this.start_point, this.end_point, this.is_reverse);
	},
	
	getLatLngArray: function(){
		return this.points.pluck('latlng');
	},
	
	isEntireTrack: function(){
		var p = this.track.points;
		if(this.is_reverse)
			return p.first() == this.end_point && p.last() == this.start_point;
		else
			return p.first() == this.start_point && p.last() == this.end_point
	},

	_start_or_end_changed:function(){
		this.is_reverse = this.points.is_reverse = this._isReverse();
		if(this.hasStartEnd())	this.recalculateStats();
		 
		this.selection_changed.fire({stats:this.stats});
	},
	
	_isReverse:function(){
		if(!this.hasStartEnd()) return null;
		
		var p=this.start_point;
		do {
			if(p.next == this.end_point) return false;
		} while ((p = p.next) && p != null)
		return true;
	}
});

UI.TrailSegmentInterface = function(in_opts){
	var segment;
	var segment_observer;
	var last_start;
	var last_end;
	var last_path;
	var opts;

	var overlays = {placed: false};
	this.default_opts = {
		start:{icon:UI.Icons.MeasureStart, z_index:21},
		end:{icon:UI.Icons.MeasureEnd, z_index:20},
		line:{
			color: '#7777ff',
			weight: 5,
			opacity: 1
		}
	};
	
	if(opts) opts = Object.extend(this.default_opts.clone(), in_opts)
	else 	 opts = this.default_opts;
	
			
	this.setTrailSegment = function(ts){
		segment = ts;

		if(segment_observer) segment_observer.stop();
		segment_observer = segment.selection_changed.observe(segment_changed, this);

		this.updateOverlays();
	}
	
	function segment_changed(){
		this.updateOverlays();
		if(segment.hasStartEnd())
			this.showTrailFacts();
		else
			map.closeInfoWindow();
	}
	
	this.updateOverlays = function(){
		var start = segment.start_point;
		var end = segment.end_point;
		
		var start_changed = start && start.latlng != last_start;
		var end_changed = end && end.latlng != last_end;
		
		if (start && !overlays.start) {
			overlays.start = Map.Overlays.createMarker(opts.start, start.latlng);
		} else if(start_changed){
			overlays.start.setLatLng(start.latlng);
			overlays.start.show();
		} else if(!start && overlays.start)
			overlays.start.hide();
						
		if (end && !overlays.end) {
			overlays.end = Map.Overlays.createMarker(opts.end, end.latlng);
			GEvent.bind(overlays.end, 'click', this, this.showTrailFacts);
		} else if(end_changed){
			overlays.end.setLatLng(end.latlng);
			overlays.end.show();
		} else if(!end && overlays.end)
			overlays.end.hide();
		
		if(overlays.path) map.removeOverlay(overlays.path);
		if(start && end && (start_changed || end_changed)){
			overlays.path = new GPolyline(segment.getLatLngArray(), opts.line.color, opts.line.weight, opts.line.opacity)
			map.addOverlay(overlays.path);
		}

		last_start = start && start.latlng;
		last_end = end && end.latlng;		
	}
	
	this.showTrailFacts = function(){
		if (!segment.stats) return;
		UI._updateTrailFacts(Common.segment_info, segment.stats);
		
		overlays.end.openInfoWindow(Common.segment_info_window, {
			maxWidth: 170
		});
	}
	
	this.destroyOverlays = function(){
		if(!this.overlays.placed) return;
		
		var overs = this.overlays;
		$w('start end path').each(function(over){
			map.removeOverlay(overs[over]);
		});
	}
}

UI.TrackList = (function(){
	var trail,
		tracks = new Hash(),
		trail_observers = [],
		row_container;
		
// ===== Public Interface  =====	
	var public_interface = {
		setTrail: function(in_trail){
			if(in_trail == trail) return;
			stop_observing_trail_events();
			trail = in_trail;
			
			tracks.each(function(track_info) {
				remove_track_from_list(track_info.track);
			});
			trail.tracks.each(add_track_to_list);
			observe_trail_events();
			mark_primary_track_as_selected();
		}
	};
		

// ===== Setup =====	

	function add_track_to_list(track){
		var info = {track:track};
		tracks.set(track.id, info);
		
		observe_track_events(info);
		create_track_row(info);
		update_track_row(track);
	}
	
	function remove_track_from_list(track){
		var info = tracks.get(track.id);
		if(!info) return;
		
		stop_observing_track_events(info);
		destroy_track_row(info);
	}
	
	function observe_track_events(track_info){
		track_info.observers = [
			track_info.track.statsChanged.observe(trackStatsChanged_handler),
			track_info.track.destroyed.observe(trackDestroyed_handler)
		];
	}

	function stop_observing_track_events(track_info){
		if(track_info.observers)
			track_info.observers.invoke('stop');
		track_info.observers = null;
	}

	function observe_trail_events(){
		trail_observers = [
			trail.trackAdded.observe(trackAdded_handler),
			trail.trackRemoved.observe(trackRemoved_handler)
		];
	}
	
	function stop_observing_trail_events(){
		trail_observers.invoke('stop');
		trail_observers = [];
	}
	


// ===== Dhtml Creation / Deletion =====
	
	function create_track_row(track_info){
		if(!row_container) row_container = $('tracks_table_rows');
		
		var row = new Element('tr');

		track_info.primary_td = new Element('td', {'class':'primary'});
		track_info.primary_td.insert(new Element('div', {'class':'radio'}));
		row.insert(track_info.primary_td);		

		track_info.name_cell = new Element('td');
		row.insert(track_info.name_cell);
		
		track_info.dist_cell = new Element('td', {'class':'distance'});
		row.insert(track_info.dist_cell);

		track_info.links_td = new Element('td');
		var zoom = new Element('span', {'class':'link'});
		zoom.update('view');
		track_info.links_td.insert(zoom);
		track_info.links_td.insert(' ');

		row.insert(track_info.links_td);	

		row_container.insert(row);
		track_info.row = row;
		
		zoom.observe('click', function(){
			Map.RecenterView(track_info.track);
		});

		if (IsEditMode && !trail.readonly) {
			var del = new Element('span', {'class': 'link'});
			del.update('delete');
			track_info.delete_link = del;
			track_info.links_td.insert(del);

			track_info.primary_td.observe('click', function(){
				set_primary_track(track_info.track);
			});
		
			track_info.delete_link.observe('click', function() {
				if (trail.get_primary_track() != track_info.track &&
					confirm('Are you sure you want to delete this Track?'))
					trail.RemoveTrack(track_info.track);
			});

			row.observe('mouseover', function(){
				if(!row.hasClassName('primary'))
					row.addClassName('hovered');
			});
			
			row.observe('mouseout', function(){
				row.removeClassName('hovered');
			});
		}		
	}
	
	function destroy_track_row(track_info){
		track_info.row.remove();
		track_info.row = null;
	}
	
	function update_track_row(track){
		var info = tracks.get(track.id);
		info.dist_cell.update(Units.len(track.distance));
		info.name_cell.update(track.name || '(none)');
	}
	
	function mark_primary_track_as_selected(){
		var primary_track = trail.get_primary_track();
		tracks.each(function(pair){
			var info = pair.value;
			var is_primary = info.track == primary_track;
			if(info.delete_link)
				info.delete_link[is_primary ? 'hide' : 'show']();
			info.row[(is_primary ? 'add' : 'remove') + "ClassName"]('primary');
		});
	}

	function set_primary_track(in_track){
		if(!IsEditMode || trail.readonly ||
			in_track == trail.get_primary_track()) return;
		
		trail.set('primary_track', in_track);
		trail.set('primary_track_id', in_track.id);
		mark_primary_track_as_selected();
	}	
	
// ===== Event Handlers  ===== 

	function trackRemoved_handler(track){
		remove_track_from_list(track);
	}
	
	function trackAdded_handler(track){
		add_track_to_list(track);
	}
	
	function trackStatsChanged_handler(track){
		update_track_row(track);
	}
	
	function trackDestroyed_handler(track){
		
	}	

	return public_interface;
})();


var ClientSettings = (function(){
  var settings = null;

  var NAME_VALUE_SEPARATOR = "/";
  var PAIR_SEPARATOR = "//";
  var CLIENT_SETTINGS_COOKIE_NAME = "__wwset";
  var CLIENT_SETTINGS_BY_NAME = $H({
    first_visit :           {id : 'fv',  default_value : true,   type : 'boolean'},
    map_type :              {id : 'mt',  default_value : 1,      type : 'number'},
    show_trail_paths :      {id : 'stp', default_value : true,   type : 'boolean'},
    show_more_overlays :    {id : 'smt', default_value : false,  type : 'boolean'}
  });

  var CLIENT_SETTINGS_BY_ID = CLIENT_SETTINGS_BY_NAME.inject(new Hash(), function(hash, pair){
    var setting_info = pair[1];
    setting_info.name = pair[0];
    hash.set(setting_info.id, setting_info);
    return hash;
  });

  return {
  set_setting: function(name, value){
    get_settings().set(name, value);
    write_settings_cookie();
  },

  get_setting: function(name){
    var value = get_settings().get(name);
    return value == null ? CLIENT_SETTINGS_BY_NAME.get(name).default_value : value;
  }
  };

  function get_settings(){
    if(settings) return settings;
    var cookie_value = readCookie(CLIENT_SETTINGS_COOKIE_NAME);

    if(cookie_value){
      settings = unescape(cookie_value).split(PAIR_SEPARATOR);

      settings = settings.inject(new Hash(), function(hash, pair){
        pair = pair.split(NAME_VALUE_SEPARATOR, 2);
        var id = pair[0], value = pair[1];

        var setting_info = CLIENT_SETTINGS_BY_ID.get(id);

        if(setting_info != null){
          switch(setting_info.type){
            case 'boolean': value = (value == 'f' ? false : (value == 't' ? true : null)); break;
            case 'number':  value = parseInt(value);  break;
          }

          hash.set(setting_info.name, value);
        }
        return hash;
      });
    }
    if(settings == null) settings = new Hash();
    return settings;
  }

  function write_settings_cookie(){
    var keys_values = [];
    settings.each(function(pair){
      var setting_info = CLIENT_SETTINGS_BY_NAME.get(pair.key)

      var value = pair.value;
      if(setting_info.type == 'boolean')
          value = value ? 't' : (value === false ? 'f' : null);

      keys_values.push(setting_info.id + NAME_VALUE_SEPARATOR + value);
    });
    createCookie(CLIENT_SETTINGS_COOKIE_NAME, escape(keys_values.join(PAIR_SEPARATOR)), 180);
  }

})();


Initializers.push(function(){
	Event.observers('#share_this .wizard_close_btn', 'click', UI.cancelWizard);
});

var Sharing = {
update_width: function(){
	var width = $('share_width');
	if($('share_full_width').checked){
		width.disable();
		width.value = '100%';
	}else{
		width.enable();
		width.value = '500';
	}
	Sharing.update_embedded();
},
Show:function(type, opts){
	if(!opts) opts = {};
	
	if(type =='search'){
		Sharing.Send_Email = Search.UI.send_Share_Search_Email;
		$('share_link').value = getDomain() + "/search?" + Search.Get_Search_Query_String();

	} else if(type =='trail') {
		Sharing.Send_Email = UI.send_Share_Trail_Email;
		if(!opts.trail_id && currentTrail && currentTrail.id)
			opts.trail_id = currentTrail.id;

		Sharing.trail_id = opts.trail_id;
		$('share_link').value = getDomain() + "/trails/view/" + opts.trail_id; 
		Sharing.update_html();			
	}

	$$('.share_trail').invoke(type == 'search' ? 'hide' : 'show');
	$$('.share_search').invoke(type == 'search' ? 'show' : 'hide');		
	
	$('share_form').setAttribute('action', '/'+ type + '/share');
	
	UI.Show_Wizard_Page('share_this');
},
update_html: function(){
	var opts = {};				
	
	var width = parseInt($F('share_width'));
	if(width == 0) width = 500
	if($F('share_full_width') == '1')
		opts.width_style =  '';
	else
		opts.width_style = 'width:' + Math.max(Math.min(width, 1000),200)  + 'px;';
	
	opts.height = parseInt($F('share_height')) - 40;
	opts.trail_id = Sharing.trail_id;
	opts.domain = getDomain();

	(function(){
		$('share_html').value = Sharing._blog_html.evaluate(opts);
		Sharing.select_text('share_html');
	}).delay(0.1);
},
update_gadget: function(){
	var gadget = new Template("#{domain}/widget/google_gadget_trail/#{trail_id}");	
	var opts = {};				

	opts.trail_id = Sharing.trail_id;
	opts.domain = getDomain();

	(function(){
		$('share_html').value = gadget.evaluate(opts);
		Sharing.select_text('share_html');
	}).delay(0.1);
},

update_embedded: function(){
	var is_gadget = Sharing.html_is_gadget = $('share_gadget').checked;
	var gadget = is_gadget ? 'show' : 'hide';
	var notgadget = is_gadget ? 'hide' : 'show';

	$('share_html_tagimg')[notgadget]();
	$('share_html_opts')[notgadget]();
	$('share_html_linkimg')[gadget](); 
	$('share_html_gadget_msg')[gadget]();
	
	if(is_gadget)	Sharing.update_gadget();
	else 		Sharing.update_html();
},

select_text:function(el){
	try{$(el).select();}catch(e){}
}};

UI.Trail = (function(){
	var observers = new Hash();
	var trails = new Hash();

	//Track event handlers
	function statsChanged_handler(points){
		UI.UpdateTrailFacts(this); 		
	}
	
	function pointsAdded_handler(points){
		updateLineForPoint(points.start.prev);
		points.each(updateLineForPoint);
	}
	
	function pointsRemoved_handler(points){
		points.each(releaseOverlaysForPoint);
		updateLineForPoint(points.start.prev);
	}
	
	function pointsMoved_handler(points){
		points.each(updateLineForPoint);
		updateLineForPoint(points.start.prev);
	}
	
	//Each point's line starts from it's location and goes to the next point
	function updateLineForPoint(point)
	{
		if(!point) return;
		
	    if(point.line)
	        map.removeOverlay(point.line);
	
		point.line = null;
		
	    if(point.next)
	    {
	        point.line = Map.Overlays.newLine([point.latlng, point.next.latlng], LINE_COLOR);                    
	        map.addOverlay(point.line);
	    }
	    else
	    {
	        point.line = null;
	        point.calculateDistance();
	    }
	}
	
	
	function releaseOverlaysForPoint(point)
	{
	    if(point.line)
	        map.removeOverlay(point.line);
	             
	    if(point.marker)
	    {
	        point.marker.point = null;
	        map.removeOverlay(point.marker);    
	    }
		
		point.line = point.marker = null;
	}		

	//Public interface	
	var public_interface = {
		createEditableUIForTrail:function(trail){
			if(trails.include(trail)) return;
			
			trails.set(trail.id, trail);
			
			trail.tracks.each(this.createEditableUIForTrack);

			//To update the Trail Facts
			UI.UpdateTrailFacts(trail.tracks.first());
		},
		
		createEditableUIForTrack:function(track){
			if(!observers.get(track.id)){
				observers.set(track.id, [
					track.statsChanged.observe(statsChanged_handler, track),
					track.pointsAdded.observe(pointsAdded_handler, track),
					track.pointsRemoved.observe(pointsRemoved_handler, track),
					track.pointsMoved.observe(pointsMoved_handler, track),
					track.destroyed.observe(public_interface.removeEditableUIForTrack, public_interface)
				]);
			}
			track.points.each(updateLineForPoint);
		},
		
		
		removeEditableUIForTrail:function(trail){
			if(!trails.get(trail.id)) return;
			
			trail.tracks.each(this.removeEditableUIForTrack);
			
			trails.unset(trail.id);
		},
		
		removeEditableUIForTrack:function(track){
			var to = observers.get(track.id);
			if(to) to.invoke('stop');
			
			track.points.each(function(point){
				if(point.line) map.removeOverlay(point.line);
			});
		}		
	};
	return public_interface;
})();


UI.Menu = function(btn_id, menu_id){
	var hide_timer = null;
	var menu;
	var btn;

	function clear_timer_and_replace(replacement){
		if(hide_timer)
			clearTimeout(hide_timer);
		hide_timer = replacement;
	}
	
	function hide_now(){
		menu.hide();
	}
	
	function show(){
		if (me.enabled){
			new Effect.BlindDown(menu_id, 
			{
				duration: 0.5
			});
		}
	}
	
	function hide(){
		clear_timer_and_replace(
			setTimeout(hide_now, 250));
	}
	
	function cancel_hide(){
		clear_timer_and_replace(null);
	}	

	(function(){
		btn = $(btn_id);
		btn.observe('click', show);
		btn.observe('mouseout', hide);
		btn.observe('mouseover', cancel_hide);
		menu = $(menu_id);
		menu.observe('mouseover', cancel_hide);	
		menu.observe('mouseout', hide);	
	}).delay(1);
	
	var me = {
		enabled:true
	};
	return me;	
}
