var Prototype={Version:"1.6.1",Browser:(function(){var B=navigator.userAgent;var A=Object.prototype.toString.call(window.opera)=="[object Opera]";return{IE:!!window.attachEvent&&!A,Opera:A,WebKit:B.indexOf("AppleWebKit/")>-1,Gecko:B.indexOf("Gecko")>-1&&B.indexOf("KHTML")===-1,MobileSafari:/Apple.*Mobile.*Safari/.test(B)}})(),BrowserFeatures:{XPath:!!document.evaluate,SelectorsAPI:!!document.querySelector,ElementExtensions:(function(){var A=window.Element||window.HTMLElement;return !!(A&&A.prototype)})(),SpecificElementExtensions:(function(){if(typeof window.HTMLDivElement!=="undefined"){return true}var C=document.createElement("div");var B=document.createElement("form");var A=false;if(C.__proto__&&(C.__proto__!==B.__proto__)){A=true}C=B=null;return A})()},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 Abstract={};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}};var Class=(function(){function A(){}function B(){var G=null,F=$A(arguments);if(Object.isFunction(F[0])){G=F.shift()}function D(){this.initialize.apply(this,arguments)}Object.extend(D,Class.Methods);D.superclass=G;D.subclasses=[];if(G){A.prototype=G.prototype;D.prototype=new A;G.subclasses.push(D)}for(var E=0;E<F.length;E++){D.addMethods(F[E])}if(!D.prototype.initialize){D.prototype.initialize=Prototype.emptyFunction}D.prototype.constructor=D;return D}function C(J){var F=this.superclass&&this.superclass.prototype;var E=Object.keys(J);if(!Object.keys({toString:true}).length){if(J.toString!=Object.prototype.toString){E.push("toString")}if(J.valueOf!=Object.prototype.valueOf){E.push("valueOf")}}for(var D=0,G=E.length;D<G;D++){var I=E[D],H=J[I];if(F&&Object.isFunction(H)&&H.argumentNames().first()=="$super"){var K=H;H=(function(L){return function(){return F[L].apply(this,arguments)
}})(I).wrap(K);H.valueOf=K.valueOf.bind(K);H.toString=K.toString.bind(K)}this.prototype[I]=H}return this}return{create:B,Methods:{addMethods:C}}})();(function(){var D=Object.prototype.toString;function I(Q,S){for(var R in S){Q[R]=S[R]}return Q}function L(Q){try{if(E(Q)){return"undefined"}if(Q===null){return"null"}return Q.inspect?Q.inspect():String(Q)}catch(R){if(R instanceof RangeError){return"..."}throw R}}function K(Q){var S=typeof Q;switch(S){case"undefined":case"function":case"unknown":return ;case"boolean":return Q.toString()}if(Q===null){return"null"}if(Q.toJSON){return Q.toJSON()}if(H(Q)){return }var R=[];for(var U in Q){var T=K(Q[U]);if(!E(T)){R.push(U.toJSON()+": "+T)}}return"{"+R.join(", ")+"}"}function C(Q){return $H(Q).toQueryString()}function F(Q){return Q&&Q.toHTML?Q.toHTML():String.interpret(Q)}function O(Q){var R=[];for(var S in Q){R.push(S)}return R}function M(Q){var R=[];for(var S in Q){R.push(Q[S])}return R}function J(Q){return I({},Q)}function H(Q){return !!(Q&&Q.nodeType==1)
}function G(Q){return D.call(Q)=="[object Array]"}function P(Q){return Q instanceof Hash}function B(Q){return typeof Q==="function"}function A(Q){return D.call(Q)=="[object String]"}function N(Q){return D.call(Q)=="[object Number]"}function E(Q){return typeof Q==="undefined"}I(Object,{extend:I,inspect:L,toJSON:K,toQueryString:C,toHTML:F,keys:O,values:M,clone:J,isElement:H,isArray:G,isHash:P,isFunction:B,isString:A,isNumber:N,isUndefined:E})})();Object.extend(Function.prototype,(function(){var K=Array.prototype.slice;function D(O,L){var N=O.length,M=L.length;while(M--){O[N+M]=L[M]}return O}function I(M,L){M=K.call(M,0);return D(M,L)}function G(){var L=this.toString().match(/^[\s\(]*function[^(]*\(([^)]*)\)/)[1].replace(/\/\/.*?[\r\n]|\/\*(?:.|[\r\n])*?\*\//g,"").replace(/\s+/g,"").split(",");return L.length==1&&!L[0]?[]:L}function H(N){if(arguments.length<2&&Object.isUndefined(arguments[0])){return this}var L=this,M=K.call(arguments,1);return function(){var O=I(M,arguments);return L.apply(N,O)
}}function F(N){var L=this,M=K.call(arguments,1);return function(P){var O=D([P||window.event],M);return L.apply(N,O)}}function J(){if(!arguments.length){return this}var L=this,M=K.call(arguments,0);return function(){var N=I(M,arguments);return L.apply(this,N)}}function E(N){var L=this,M=K.call(arguments,1);N=N*1000;return window.setTimeout(function(){return L.apply(L,M)},N)}function A(){var L=D([0.01],arguments);return this.delay.apply(this,L)}function C(M){var L=this;return function(){var N=D([L.bind(this)],arguments);return M.apply(this,N)}}function B(){if(this._methodized){return this._methodized}var L=this;return this._methodized=function(){var M=D([this],arguments);return L.apply(null,M)}}return{argumentNames:G,bind:H,bindAsEventListener:F,curry:J,delay:E,defer:A,wrap:C,methodize:B}})());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"'
};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();this.currentlyExecuting=false}catch(A){this.currentlyExecuting=false;throw A}}}});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,(function(){function prepareReplacement(replacement){if(Object.isFunction(replacement)){return replacement}var template=new Template(replacement);
return function(match){return template.evaluate(match)}}function gsub(pattern,replacement){var result="",source=this,match;replacement=prepareReplacement(replacement);if(Object.isString(pattern)){pattern=RegExp.escape(pattern)}if(!(pattern.length||pattern.source)){replacement=replacement("");return replacement+source.split("").join(replacement)+replacement}while(source.length>0){if(match=source.match(pattern)){result+=source.slice(0,match.index);result+=String.interpret(replacement(match));source=source.slice(match.index+match[0].length)}else{result+=source,source=""}}return result}function sub(pattern,replacement,count){replacement=prepareReplacement(replacement);count=Object.isUndefined(count)?1:count;return this.gsub(pattern,function(match){if(--count<0){return match[0]}return replacement(match)})}function scan(pattern,iterator){this.gsub(pattern,iterator);return String(this)}function truncate(length,truncation){length=length||30;truncation=Object.isUndefined(truncation)?"...":truncation;
return this.length>length?this.slice(0,length-truncation.length)+truncation:String(this)}function strip(){return this.replace(/^\s+/,"").replace(/\s+$/,"")}function stripTags(){return this.replace(/<\w+(\s+("[^"]*"|'[^']*'|[^>])+)?>|<\/\w+>/gi,"")}function stripScripts(){return this.replace(new RegExp(Prototype.ScriptFragment,"img"),"")}function extractScripts(){var matchAll=new RegExp(Prototype.ScriptFragment,"img");var matchOne=new RegExp(Prototype.ScriptFragment,"im");return(this.match(matchAll)||[]).map(function(scriptTag){return(scriptTag.match(matchOne)||["",""])[1]})}function evalScripts(){return this.extractScripts().map(function(script){return eval(script)})}function escapeHTML(){return this.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}function unescapeHTML(){return this.stripTags().replace(/&lt;/g,"<").replace(/&gt;/g,">").replace(/&amp;/g,"&")}function toQueryParams(separator){var match=this.strip().match(/([^?#]*)(#.*)?$/);if(!match){return{}}return match[1].split(separator||"&").inject({},function(hash,pair){if((pair=pair.split("="))[0]){var key=decodeURIComponent(pair.shift());
var value=pair.length>1?pair.join("="):pair[0];if(value!=undefined){value=decodeURIComponent(value)}if(key in hash){if(!Object.isArray(hash[key])){hash[key]=[hash[key]]}hash[key].push(value)}else{hash[key]=value}}return hash})}function toArray(){return this.split("")}function succ(){return this.slice(0,this.length-1)+String.fromCharCode(this.charCodeAt(this.length-1)+1)}function times(count){return count<1?"":new Array(count+1).join(this)}function camelize(){var parts=this.split("-"),len=parts.length;if(len==1){return parts[0]}var camelized=this.charAt(0)=="-"?parts[0].charAt(0).toUpperCase()+parts[0].substring(1):parts[0];for(var i=1;i<len;i++){camelized+=parts[i].charAt(0).toUpperCase()+parts[i].substring(1)}return camelized}function capitalize(){return this.charAt(0).toUpperCase()+this.substring(1).toLowerCase()}function underscore(){return this.replace(/::/g,"/").replace(/([A-Z]+)([A-Z][a-z])/g,"$1_$2").replace(/([a-z\d])([A-Z])/g,"$1_$2").replace(/-/g,"_").toLowerCase()}function dasherize(){return this.replace(/_/g,"-")
}function inspect(useDoubleQuotes){var escapedString=this.replace(/[\x00-\x1f\\]/g,function(character){if(character in String.specialChar){return String.specialChar[character]}return"\\u00"+character.charCodeAt().toPaddedString(2,16)});if(useDoubleQuotes){return'"'+escapedString.replace(/"/g,'\\"')+'"'}return"'"+escapedString.replace(/'/g,"\\'")+"'"}function toJSON(){return this.inspect(true)}function unfilterJSON(filter){return this.replace(filter||Prototype.JSONFilter,"$1")}function isJSON(){var str=this;if(str.blank()){return false}str=this.replace(/\\./g,"@").replace(/"[^"\\\n\r]*"/g,"");return(/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/).test(str)}function evalJSON(sanitize){var json=this.unfilterJSON();try{if(!sanitize||json.isJSON()){return eval("("+json+")")}}catch(e){}throw new SyntaxError("Badly formed JSON string: "+this.inspect())}function include(pattern){return this.indexOf(pattern)>-1}function startsWith(pattern){return this.indexOf(pattern)===0}function endsWith(pattern){var d=this.length-pattern.length;
return d>=0&&this.lastIndexOf(pattern)===d}function empty(){return this==""}function blank(){return/^\s*$/.test(this)}function interpolate(object,pattern){return new Template(this,pattern).evaluate(object)}return{gsub:gsub,sub:sub,scan:scan,truncate:truncate,strip:String.prototype.trim?String.prototype.trim:strip,stripTags:stripTags,stripScripts:stripScripts,extractScripts:extractScripts,evalScripts:evalScripts,escapeHTML:escapeHTML,unescapeHTML:unescapeHTML,toQueryParams:toQueryParams,parseQuery:toQueryParams,toArray:toArray,succ:succ,times:times,camelize:camelize,capitalize:capitalize,underscore:underscore,dasherize:dasherize,inspect:inspect,toJSON:toJSON,unfilterJSON:unfilterJSON,isJSON:isJSON,evalJSON:evalJSON,include:include,startsWith:startsWith,endsWith:endsWith,empty:empty,blank:blank,interpolate:interpolate}})());var Template=Class.create({initialize:function(A,B){this.template=A.toString();this.pattern=B||Template.Pattern},evaluate:function(A){if(A&&Object.isFunction(A.toTemplateReplacements)){A=A.toTemplateReplacements()
}return this.template.gsub(this.pattern,function(D){if(A==null){return(D[1]+"")}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].replace(/\\\\]/g,"]"):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)})}});Template.Pattern=/(^|.|\r|\n)(#\{(.*?)\})/;var $break={};var Enumerable=(function(){function C(Y,X){var W=0;try{this._each(function(a){Y.call(X,a,W++)})}catch(Z){if(Z!=$break){throw Z}}return this}function R(Z,Y,X){var W=-Z,a=[],b=this.toArray();if(Z<1){return b}while((W+=Z)<b.length){a.push(b.slice(W,W+Z))}return a.collect(Y,X)}function B(Y,X){Y=Y||Prototype.K;var W=true;this.each(function(a,Z){W=W&&!!Y.call(X,a,Z);if(!W){throw $break}});return W}function I(Y,X){Y=Y||Prototype.K;var W=false;this.each(function(a,Z){if(W=!!Y.call(X,a,Z)){throw $break}});return W}function J(Y,X){Y=Y||Prototype.K;
var W=[];this.each(function(a,Z){W.push(Y.call(X,a,Z))});return W}function T(Y,X){var W;this.each(function(a,Z){if(Y.call(X,a,Z)){W=a;throw $break}});return W}function H(Y,X){var W=[];this.each(function(a,Z){if(Y.call(X,a,Z)){W.push(a)}});return W}function G(Z,Y,X){Y=Y||Prototype.K;var W=[];if(Object.isString(Z)){Z=new RegExp(RegExp.escape(Z))}this.each(function(b,a){if(Z.match(b)){W.push(Y.call(X,b,a))}});return W}function A(W){if(Object.isFunction(this.indexOf)){if(this.indexOf(W)!=-1){return true}}var X=false;this.each(function(Y){if(Y==W){X=true;throw $break}});return X}function Q(X,W){W=Object.isUndefined(W)?null:W;return this.eachSlice(X,function(Y){while(Y.length<X){Y.push(W)}return Y})}function L(W,Y,X){this.each(function(a,Z){W=Y.call(X,W,a,Z)});return W}function V(X){var W=$A(arguments).slice(1);return this.map(function(Y){return Y[X].apply(Y,W)})}function P(Y,X){Y=Y||Prototype.K;var W;this.each(function(a,Z){a=Y.call(X,a,Z);if(W==null||a>=W){W=a}});return W}function N(Y,X){Y=Y||Prototype.K;
var W;this.each(function(a,Z){a=Y.call(X,a,Z);if(W==null||a<W){W=a}});return W}function E(Z,X){Z=Z||Prototype.K;var Y=[],W=[];this.each(function(b,a){(Z.call(X,b,a)?Y:W).push(b)});return[Y,W]}function F(X){var W=[];this.each(function(Y){W.push(Y[X])});return W}function D(Y,X){var W=[];this.each(function(a,Z){if(!Y.call(X,a,Z)){W.push(a)}});return W}function M(X,W){return this.map(function(Z,Y){return{value:Z,criteria:X.call(W,Z,Y)}}).sort(function(d,c){var Z=d.criteria,Y=c.criteria;return Z<Y?-1:Z>Y?1:0}).pluck("value")}function O(){return this.map()}function S(){var X=Prototype.K,W=$A(arguments);if(Object.isFunction(W.last())){X=W.pop()}var Y=[this].concat(W).map($A);return this.map(function(a,Z){return X(Y.pluck(Z))})}function K(){return this.toArray().length}function U(){return"#<Enumerable:"+this.toArray().inspect()+">"}return{each:C,eachSlice:R,all:B,every:B,any:I,some:I,collect:J,map:J,detect:T,findAll:H,select:H,filter:H,grep:G,include:A,member:A,inGroupsOf:Q,inject:L,invoke:V,max:P,min:N,partition:E,pluck:F,reject:D,sortBy:M,toArray:O,entries:O,zip:S,size:K,inspect:U,find:T}
})();function $A(C){if(!C){return[]}if("toArray" in Object(C)){return C.toArray()}var B=C.length||0,A=new Array(B);while(B--){A[B]=C[B]}return A}function $w(A){if(!Object.isString(A)){return[]}A=A.strip();return A?A.split(/\s+/):[]}Array.from=$A;(function(){var S=Array.prototype,M=S.slice,O=S.forEach;function B(W){for(var V=0,X=this.length;V<X;V++){W(this[V])}}if(!O){O=B}function L(){this.length=0;return this}function D(){return this[0]}function G(){return this[this.length-1]}function I(){return this.select(function(V){return V!=null})}function U(){return this.inject([],function(W,V){if(Object.isArray(V)){return W.concat(V.flatten())}W.push(V);return W})}function H(){var V=M.call(arguments,0);return this.select(function(W){return !V.include(W)})}function F(V){return(V!==false?this:this.toArray())._reverse()}function K(V){return this.inject([],function(Y,X,W){if(0==W||(V?Y.last()!=X:!Y.include(X))){Y.push(X)}return Y})}function P(V){return this.uniq().findAll(function(W){return V.detect(function(X){return W===X
})})}function Q(){return M.call(this,0)}function J(){return this.length}function T(){return"["+this.map(Object.inspect).join(", ")+"]"}function R(){var V=[];this.each(function(W){var X=Object.toJSON(W);if(!Object.isUndefined(X)){V.push(X)}});return"["+V.join(", ")+"]"}function A(X,V){V||(V=0);var W=this.length;if(V<0){V=W+V}for(;V<W;V++){if(this[V]===X){return V}}return -1}function N(W,V){V=isNaN(V)?this.length:(V<0?this.length+V:V)+1;var X=this.slice(0,V).reverse().indexOf(W);return(X<0)?X:V-X-1}function C(){var a=M.call(this,0),Y;for(var W=0,X=arguments.length;W<X;W++){Y=arguments[W];if(Object.isArray(Y)&&!("callee" in Y)){for(var V=0,Z=Y.length;V<Z;V++){a.push(Y[V])}}else{a.push(Y)}}return a}Object.extend(S,Enumerable);if(!S._reverse){S._reverse=S.reverse}Object.extend(S,{_each:O,clear:L,first:D,last:G,compact:I,flatten:U,without:H,reverse:F,uniq:K,intersect:P,clone:Q,toArray:Q,size:J,inspect:T,toJSON:R});var E=(function(){return[].concat(arguments)[0][0]!==1})(1,2);if(E){S.concat=C}if(!S.indexOf){S.indexOf=A
}if(!S.lastIndexOf){S.lastIndexOf=N}})();function $H(A){return new Hash(A)}var Hash=Class.create(Enumerable,(function(){function E(Q){this._object=Object.isHash(Q)?Q.toObject():Object.clone(Q)}function F(R){for(var Q in this._object){var S=this._object[Q],T=[Q,S];T.key=Q;T.value=S;R(T)}}function K(Q,R){return this._object[Q]=R}function C(Q){if(this._object[Q]!==Object.prototype[Q]){return this._object[Q]}}function N(Q){var R=this._object[Q];delete this._object[Q];return R}function P(){return Object.clone(this._object)}function O(){return this.pluck("key")}function M(){return this.pluck("value")}function G(R){var Q=this.detect(function(S){return S.value===R});return Q&&Q.key}function I(Q){return this.clone().update(Q)}function D(Q){return new Hash(Q).inject(this,function(R,S){R.set(S.key,S.value);return R})}function B(Q,R){if(Object.isUndefined(R)){return Q}return Q+"="+encodeURIComponent(String.interpret(R))}function A(){return this.inject([],function(S,T){var R=encodeURIComponent(T.key),Q=T.value;
if(Q&&typeof Q=="object"){if(Object.isArray(Q)){return S.concat(Q.map(B.curry(R)))}}else{S.push(B(R,Q))}return S}).join("&")}function L(){return"#<Hash:{"+this.map(function(Q){return Q.map(Object.inspect).join(": ")}).join(", ")+"}>"}function J(){return Object.toJSON(this.toObject())}function H(){return new Hash(this)}return{initialize:E,_each:F,set:K,get:C,unset:N,toObject:P,toTemplateReplacements:P,keys:O,values:M,index:G,merge:I,update:D,toQueryString:A,inspect:L,toJSON:J,clone:H}})());Hash.from=$H;Object.extend(Number.prototype,(function(){function D(){return this.toPaddedString(2,16)}function E(){return this+1}function A(K,J){$R(0,this,true).each(K,J);return this}function B(L,K){var J=this.toString(K||10);return"0".times(L-J.length)+J}function F(){return isFinite(this)?this.toString():"null"}function I(){return Math.abs(this)}function H(){return Math.round(this)}function G(){return Math.ceil(this)}function C(){return Math.floor(this)}return{toColorPart:D,succ:E,times:A,toPaddedString:B,toJSON:F,abs:I,round:H,ceil:G,floor:C}
})());function $R(C,A,B){return new ObjectRange(C,A,B)}var ObjectRange=Class.create(Enumerable,(function(){function B(F,D,E){this.start=F;this.end=D;this.exclusive=E}function C(D){var E=this.start;while(this.include(E)){D(E);E=E.succ()}}function A(D){if(D<this.start){return false}if(this.exclusive){return D<this.end}return D<=this.end}return{initialize:B,_each:C,include:A}})());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&&this.isSameOrigin()&&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}},isSameOrigin:function(){var A=this.url.match(/^\s*https?:\/\/[^\/]*/);return !A||(A[0]=="#{protocol}//#{domain}#{port}".interpolate({protocol:location.protocol,domain:document.domain,port:location.port?":"+location.port:""}))},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||!this.request.isSameOrigin())}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||!this.request.isSameOrigin())}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(C){var B=(function(){var F=document.createElement("form");var E=document.createElement("input");var D=document.documentElement;E.setAttribute("name","test");F.appendChild(E);D.appendChild(F);var G=F.elements?(typeof F.elements.test=="undefined"):null;D.removeChild(F);F=E=null;return G})();var A=C.Element;C.Element=function(F,E){E=E||{};F=F.toLowerCase();var D=Element.cache;if(B&&E.name){F="<"+F+' name="'+E.name+'">';delete E.name;return Element.writeAttribute(document.createElement(F),E)}if(!D[F]){D[F]=Element.extend(document.createElement(F))}return Element.writeAttribute(D[F].cloneNode(false),E)
};Object.extend(C.Element,A||{});if(A){C.Element.prototype=A.prototype}})(this);Element.cache={};Element.idCounter=1;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=$(A);A.style.display="none";return A},show:function(A){A=$(A);A.style.display="";return A},remove:function(A){A=$(A);A.parentNode.removeChild(A);return A},update:(function(){var B=(function(){var E=document.createElement("select"),F=true;E.innerHTML='<option value="test">test</option>';if(E.options&&E.options[0]){F=E.options[0].nodeName.toUpperCase()!=="OPTION"}E=null;return F})();var A=(function(){try{var E=document.createElement("table");if(E&&E.tBodies){E.innerHTML="<tbody><tr><td>test</td></tr></tbody>";var G=typeof E.tBodies[0]=="undefined";E=null;return G}}catch(F){return true}})();var D=(function(){var E=document.createElement("script"),G=false;try{E.appendChild(document.createTextNode(""));G=!E.firstChild||E.firstChild&&E.firstChild.nodeType!==3
}catch(F){G=true}E=null;return G})();function C(F,G){F=$(F);if(G&&G.toElement){G=G.toElement()}if(Object.isElement(G)){return F.update().insert(G)}G=Object.toHTML(G);var E=F.tagName.toUpperCase();if(E==="SCRIPT"&&D){F.text=G;return F}if(B||A){if(E in Element._insertionTranslations.tags){while(F.firstChild){F.removeChild(F.firstChild)}Element._getContentFromAnonymousElement(E,G.stripScripts()).each(function(H){F.appendChild(H)})}else{F.innerHTML=G.stripScripts()}}else{F.innerHTML=G.stripScripts()}G.evalScripts.bind(G).defer();return F}return C})(),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(C,E){C=$(C);if(Object.isString(E)||Object.isNumber(E)||Object.isElement(E)||(E&&(E.toElement||E.toHTML))){E={bottom:E}}var D,F,B,G;for(var A in E){D=E[A];
A=A.toLowerCase();F=Element._insertionTranslations[A];if(D&&D.toElement){D=D.toElement()}if(Object.isElement(D)){F(C,D);continue}D=Object.toHTML(D);B=((A=="before"||A=="after")?C.parentNode:C).tagName.toUpperCase();G=Element._getContentFromAnonymousElement(B,D.stripScripts());if(A=="top"||A=="after"){G.reverse()}G.each(F.curry(C));D.evalScripts.bind(D).defer()}return C},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 Element.recursivelyCollect(A,"parentNode")
},descendants:function(A){return Element.select(A,"*")},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 Element.recursivelyCollect(A,"previousSibling")},nextSiblings:function(A){return Element.recursivelyCollect(A,"nextSibling")},siblings:function(A){A=$(A);return Element.previousSiblings(A).reverse().concat(Element.nextSiblings(A))},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=Element.ancestors(B);return Object.isNumber(D)?C[D]:Selector.findElement(C,D,A)},down:function(B,C,A){B=$(B);if(arguments.length==1){return Element.firstDescendant(B)}return Object.isNumber(C)?Element.descendants(B)[C]:Element.select(B,C)[A||0]},previous:function(B,D,A){B=$(B);
if(arguments.length==1){return $(Selector.handlers.previousElementSibling(B))}var C=Element.previousSiblings(B);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=Element.nextSiblings(C);return Object.isNumber(D)?A[D]:Selector.findElement(A,D,B)},select:function(B){var A=Array.prototype.slice.call(arguments,1);return Selector.findChildElements(B,A)},adjacent:function(B){var A=Array.prototype.slice.call(arguments,1);return Selector.findChildElements(B.parentNode,A).without(B)},identify:function(A){A=$(A);var B=Element.readAttribute(A,"id");if(B){return B}do{B="anonymous_element_"+Element.idCounter++}while($(B));Element.writeAttribute(A,"id",B);return B},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 Element.getDimensions(A).height},getWidth:function(A){return Element.getDimensions(A).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(!Element.hasClassName(A,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 Element[Element.hasClassName(A,B)?"removeClassName":"addClassName"](A,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(B,A){B=$(B),A=$(A);if(B.compareDocumentPosition){return(B.compareDocumentPosition(A)&8)===8}if(A.contains){return A.contains(B)&&A!==B}while(B=B.parentNode){if(B==A){return true}}return false},scrollTo:function(A){A=$(A);var B=Element.cumulativeOffset(A);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||D=="auto"){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=Element.getStyle(C,"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";if(D!="fixed"){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(Prototype.Browser.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.toUpperCase()=="BODY"){break}var C=Element.getStyle(B,"position");if(C!=="static"){break}}}while(B);return Element._returnOffset(D,A)},absolutize:function(B){B=$(B);if(Element.getStyle(B,"position")=="absolute"){return B}var D=Element.positionedOffset(B);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(Element.getStyle(A,"position")=="relative"){return A}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&&(B.tagName.toUpperCase()=="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=Element.viewportOffset(D);B=$(B);var F=[0,0];var C=null;if(Element.getStyle(B,"position")=="absolute"){C=Element.getOffsetParent(B);F=Element.viewportOffset(C)}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}};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){Element.Methods.getOffsetParent=Element.Methods.getOffsetParent.wrap(function(C,B){B=$(B);try{B.offsetParent}catch(E){return $(document.body)}var A=B.getStyle("position");if(A!=="static"){return C(B)}B.setStyle({position:"relative"});
var D=C(B);B.setStyle({position:A});return D});$w("positionedOffset viewportOffset").each(function(A){Element.Methods[A]=Element.Methods[A].wrap(function(E,C){C=$(C);try{C.offsetParent}catch(G){return Element._returnOffset(0,0)}var B=C.getStyle("position");if(B!=="static"){return E(C)}var D=C.getOffsetParent();if(D&&D.getStyle("position")==="fixed"){D.setStyle({zoom:1})}C.setStyle({position:"relative"});var F=E(C);C.setStyle({position:B});return F})});Element.Methods.cumulativeOffset=Element.Methods.cumulativeOffset.wrap(function(B,A){try{A.offsetParent}catch(C){return Element._returnOffset(0,0)}return B(A)});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=(function(){var B="className";var A="for";var C=document.createElement("div");C.setAttribute(B,"x");if(C.className!=="x"){C.setAttribute("class","x");if(C.className==="x"){B="class"}}C=null;C=document.createElement("label");C.setAttribute(A,"x");if(C.htmlFor!=="x"){C.setAttribute("htmlFor","x");if(C.htmlFor==="x"){A="htmlFor"}}C=null;return{read:{names:{"class":B,className:B,"for":A,htmlFor:A},values:{_getAttr:function(D,E){return D.getAttribute(E)},_getAttr2:function(D,E){return D.getAttribute(E,2)},_getAttrNode:function(D,F){var E=D.getAttributeNode(F);return E?E.value:""
},_getEv:(function(){var D=document.createElement("div");D.onclick=Prototype.emptyFunction;var F=D.getAttribute("onclick");var E;if(String(F).indexOf("{")>-1){E=function(G,H){H=G.getAttribute(H);if(!H){return null}H=H.toString();H=H.split("{")[1];H=H.split("}")[0];return H.strip()}}else{if(F===""){E=function(G,H){H=G.getAttribute(H);if(!H){return null}return H.strip()}}}D=null;return E})(),_flag:function(D,E){return $(D).hasAttribute(E)?E:null},style:function(D){return D.style.cssText.toLowerCase()},title:function(D){return D.title}}}}})();Element._attributeTranslations.write={names:Object.extend({cellpadding:"cellPadding",cellspacing:"cellSpacing"},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 frameBorder").each(function(A){Element._attributeTranslations.write.names[A.toLowerCase()]=A;
Element._attributeTranslations.has[A.toLowerCase()]=A});(function(A){Object.extend(A,{href:A._getAttr2,src:A._getAttr2,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);if(Prototype.BrowserFeatures.ElementExtensions){(function(){function A(E){var B=E.getElementsByTagName("*"),D=[];for(var C=0,F;F=B[C];C++){if(F.tagName!=="!"){D.push(F)}}return D}Element.Methods.down=function(C,D,B){C=$(C);if(arguments.length==1){return C.firstDescendant()}return Object.isNumber(D)?A(C)[D]:Element.select(C,D)[B||0]}})()}}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.toUpperCase()=="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("outerHTML" in document.documentElement){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(){var A=Element._insertionTranslations.tags;Object.extend(A,{THEAD:A.TBODY,TFOOT:A.TBODY,TH:A.TD})
})();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);(function(A){if(!Prototype.BrowserFeatures.ElementExtensions&&A.__proto__){window.HTMLElement={};window.HTMLElement.prototype=A.__proto__;Prototype.BrowserFeatures.ElementExtensions=true}A=null})(document.createElement("div"));Element.extend=(function(){function C(G){if(typeof window.Element!="undefined"){var I=window.Element.prototype;if(I){var K="_"+(Math.random()+"").slice(2);var H=document.createElement(G);I[K]="x";var J=(H[K]!=="x");delete I[K];H=null;return J}}return false}function B(H,G){for(var J in G){var I=G[J];if(Object.isFunction(I)&&!(J in H)){H[J]=I.methodize()}}}var D=C("object");if(Prototype.BrowserFeatures.SpecificElementExtensions){if(D){return function(H){if(H&&typeof H._extendedByPrototype=="undefined"){var G=H.tagName;if(G&&(/^(?:object|applet|embed)$/i.test(G))){B(H,Element.Methods);
B(H,Element.Methods.Simulated);B(H,Element.Methods.ByTag[G.toUpperCase()])}}return H}}return Prototype.K}var A={},E=Element.Methods.ByTag;var F=Object.extend(function(I){if(!I||typeof I._extendedByPrototype!="undefined"||I.nodeType!=1||I==window){return I}var G=Object.clone(A),H=I.tagName.toUpperCase();if(E[H]){Object.extend(G,E[H])}B(I,G);I._extendedByPrototype=Prototype.emptyFunction;return I},{refresh:function(){if(!Prototype.BrowserFeatures.ElementExtensions){Object.extend(A,Element.Methods);Object.extend(A,Element.Methods.Simulated)}}});F.refresh();return F})();Element.hasAttribute=function(A,B){if(A.hasAttribute){return A.hasAttribute(B)}return Element.Methods.Simulated.hasAttribute(A,B)};Element.addMethods=function(C){var J=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(M,L,F){F=F||false;for(var O in M){var N=M[O];if(!Object.isFunction(N)){continue}if(!F||!(O in L)){L[O]=N.methodize()}}}function E(N){var F;var M={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(M[N]){F="HTML"+M[N]+"Element"}if(window[F]){return window[F]}F="HTML"+N+"Element";if(window[F]){return window[F]}F="HTML"+N.capitalize()+"Element";
if(window[F]){return window[F]}var L=document.createElement(N);var O=L.__proto__||L.constructor.prototype;L=null;return O}var I=window.HTMLElement?HTMLElement.prototype:Element.prototype;if(J.ElementExtensions){A(Element.Methods,I);A(Element.Methods.Simulated,I,true)}if(J.SpecificElementExtensions){for(var K in Element.Methods.ByTag){var G=E(K);if(Object.isUndefined(G)){continue}A(D[K],G.prototype)}}Object.extend(Element,Element.Methods);delete Element.ByTag;if(Element.extend.refresh){Element.extend.refresh()}Element.cache={}};document.viewport={getDimensions:function(){return{width:this.getWidth(),height:this.getHeight()}},getScrollOffsets:function(){return Element._returnOffset(window.pageXOffset||document.documentElement.scrollLeft||document.body.scrollLeft,window.pageYOffset||document.documentElement.scrollTop||document.body.scrollTop)}};(function(C){var H=Prototype.Browser,F=document,D,E={};function A(){if(H.WebKit&&!F.evaluate){return document}if(H.Opera&&window.parseFloat(window.opera.version())<9.5){return document.body
}return document.documentElement}function G(B){if(!D){D=A()}E[B]="client"+B;C["get"+B]=function(){return D[E[B]]};return C["get"+B]()}C.getWidth=G.curry("Width");C.getHeight=G.curry("Height")})(document.viewport);Element.Storage={UID:1};Element.addMethods({getStorage:function(B){if(!(B=$(B))){return }var A;if(B===window){A=0}else{if(typeof B._prototypeUID==="undefined"){B._prototypeUID=[Element.Storage.UID++]}A=B._prototypeUID[0]}if(!Element.Storage[A]){Element.Storage[A]=$H()}return Element.Storage[A]},store:function(B,A,C){if(!(B=$(B))){return }if(arguments.length===2){Element.getStorage(B).update(A)}else{Element.getStorage(B).set(A,C)}return B},retrieve:function(C,B,A){if(!(C=$(C))){return }var E=Element.getStorage(C),D=E.get(B);if(Object.isUndefined(D)){E.set(B,A);D=A}return D},clone:function(C,A){if(!(C=$(C))){return }var E=C.cloneNode(A);E._prototypeUID=void 0;if(A){var D=Element.select(E,"*"),B=D.length;while(B--){D[B]._prototypeUID=void 0}}return Element.extend(E)}});var Selector=Class.create({initialize:function(A){this.expression=A.strip();
if(this.shouldUseSelectorsAPI()){this.mode="selectorsAPI"}else{if(this.shouldUseXPath()){this.mode="xpath";this.compileXPathMatcher()}else{this.mode="normal";this.compileMatcher()}}},shouldUseXPath:(function(){var A=(function(){var E=false;if(document.evaluate&&window.XPathResult){var D=document.createElement("div");D.innerHTML="<ul><li></li></ul><div><ul><li></li></ul></div>";var C=".//*[local-name()='ul' or local-name()='UL']//*[local-name()='li' or local-name()='LI']";var B=document.evaluate(C,D,null,XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,null);E=(B.snapshotLength!==2);D=null}return E})();return function(){if(!Prototype.BrowserFeatures.XPath){return false}var B=this.expression;if(Prototype.Browser.WebKit&&(B.include("-of-type")||B.include(":empty"))){return false}if((/(\[[\w-]*?:|:checked)/).test(B)){return false}if(A){return false}return true}})(),shouldUseSelectorsAPI:function(){if(!Prototype.BrowserFeatures.SelectorsAPI){return false}if(Selector.CASE_INSENSITIVE_CLASS_NAMES){return false
}if(!Selector._div){Selector._div=new Element("div")}try{Selector._div.querySelector(this.expression)}catch(A){return false}return true},compileMatcher:function(){var e=this.expression,ps=Selector.patterns,h=Selector.handlers,c=Selector.criteria,le,p,m,len=ps.length,name;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=0;i<len;i++){p=ps[i].re;name=ps[i].name;if(m=e.match(p)){this.matcher.push(Object.isFunction(c[name])?c[name](m):new Template(c[name]).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 G=this.expression,H=Selector.patterns,C=Selector.xpath,F,B,A=H.length,D;if(Selector._cache[G]){this.xpath=Selector._cache[G];return }this.matcher=[".//*"];while(G&&F!=G&&(/\S/).test(G)){F=G;
for(var E=0;E<A;E++){D=H[E].name;if(B=G.match(H[E].re)){this.matcher.push(Object.isFunction(C[D])?C[D](B):new Template(C[D]).evaluate(B));G=G.replace(B[0],"");break}}}this.xpath=this.matcher.join("");Selector._cache[this.expression]=this.xpath},findElements:function(A){A=A||document;var C=this.expression,B;switch(this.mode){case"selectorsAPI":if(A!==document){var D=A.id,E=$(A).identify();E=E.replace(/([\.:])/g,"\\$1");C="#"+E+" "+C}B=$A(A.querySelectorAll(C)).map(Element.extend);A.id=D;return B;case"xpath":return document._getElementsByXPath(this.xpath,A);default:return this.matcher(A)}},match:function(I){this.tokens=[];var M=this.expression,A=Selector.patterns,E=Selector.assertions;var B,D,F,L=A.length,C;while(M&&B!==M&&(/\S/).test(M)){B=M;for(var H=0;H<L;H++){D=A[H].re;C=A[H].name;if(F=M.match(D)){if(E[C]){this.tokens.push([C,Object.clone(F)]);M=M.replace(F[0],"")}else{return this.findElements(document).include(I)}}}}var K=true,C,J;for(var H=0,G;G=this.tokens[H];H++){C=G[0],J=G[1];if(!Selector.assertions[C](I,J)){K=false;
break}}return K},toString:function(){return this.expression},inspect:function(){return"#<Selector:"+this.expression.inspect()+">"}});if(Prototype.BrowserFeatures.SelectorsAPI&&document.compatMode==="BackCompat"){Selector.CASE_INSENSITIVE_CLASS_NAMES=(function(){var C=document.createElement("div"),A=document.createElement("span");C.id="prototype_test_id";A.className="Test";C.appendChild(A);var B=(C.querySelector("#prototype_test_id .test")!==null);C=A=null;return B})()}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)]",checked:"[@checked]",disabled:"[(@disabled) and (@type!='hidden')]",enabled:"[not(@disabled) and (@type!='hidden')]",not:function(E){var H=E[6],C=Selector.patterns,I=Selector.xpath,A,J,G=C.length,B;var D=[];while(H&&A!=H&&(/\S/).test(H)){A=H;for(var F=0;F<G;F++){B=C[F].name;if(E=H.match(C[F].re)){J=Object.isFunction(I[B])?I[B](E):new Template(I[B]).evaluate(E);
D.push("("+J.substring(1,J.length-1)+")");H=H.replace(E[0],"");break}}}return"[not("+D.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); c = false;',attr:function(A){A[3]=(A[5]||A[6]);return new Template('n = h.attr(n, r, "#{1}", "#{3}", "#{2}", c); 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:[{name:"laterSibling",re:/^\s*~\s*/},{name:"child",re:/^\s*>\s*/},{name:"adjacent",re:/^\s*\+\s*/},{name:"descendant",re:/^\s/},{name:"tagName",re:/^\s*(\*|[\w\-]+)(\b|$)?/},{name:"id",re:/^#([\w\-\*]+)(\b|$)/},{name:"className",re:/^\.([\w\-\*]+)(\b|$)/},{name:"pseudo",re:/^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not)(\((.*?)\))?(\b|$|(?=\s|[:+~>]))/},{name:"attrPresence",re:/^\[((?:[\w-]+:)?[\w-]+)\]/},{name:"attr",re:/\[((?:[\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 A&&Selector.operators[C[2]](A,C[5]||C[6])}},handlers:{concat:function(B,A){for(var C=0,D;D=A[C];C++){B.push(D)}return B},mark:function(A){var D=Prototype.emptyFunction;for(var B=0,C;C=A[B];B++){C._countedByPrototype=D}return A},unmark:(function(){var A=(function(){var B=document.createElement("div"),E=false,D="_countedByPrototype",C="x";B[D]=C;E=(B.getAttribute(D)===C);B=null;return E})();return A?function(B){for(var C=0,D;D=B[C];C++){D.removeAttribute("_countedByPrototype")}return B}:function(B){for(var C=0,D;D=B[C];C++){D._countedByPrototype=void 0}return B}})(),index:function(A,D,G){A._countedByPrototype=Prototype.emptyFunction;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._countedByPrototype)){F.nodeIndex=C++}}}else{for(var E=0,C=1,B=A.childNodes;
F=B[E];E++){if(F.nodeType==1&&(!G||F._countedByPrototype)){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(typeof (E=B[C])._countedByPrototype=="undefined"){E._countedByPrototype=Prototype.emptyFunction;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(A,I,B,C){var H=$(B),G=Selector.handlers;if(I==document){if(!H){return[]}if(!A){return[H]}}else{if(!I.sourceIndex||I.sourceIndex<1){var A=I.getElementsByTagName("*");for(var E=0,D;D=A[E];E++){if(D.id===B){return[D]}}}}if(A){if(C){if(C=="child"){for(var F=0,D;D=A[F];F++){if(H.parentNode==D){return[H]}}}else{if(C=="descendant"){for(var F=0,D;D=A[F];F++){if(Element.descendantOf(H,D)){return[H]}}}else{if(C=="adjacent"){for(var F=0,D;D=A[F];F++){if(Selector.handlers.previousElementSibling(H)==D){return[H]}}}else{A=G[C](A)}}}}for(var F=0,D;D=A[F];F++){if(D==H){return[H]}}return[]}return(H&&Element.descendantOf(H,I))?[H]:[]},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,G){if(!C){C=B.getElementsByTagName("*")}if(C&&G){C=this[G](C)}var E=[];for(var D=0,F;F=C[D];D++){if(Element.hasAttribute(F,A)){E.push(F)}}return E},attr:function(A,I,H,J,C,B){if(!A){A=I.getElementsByTagName("*")}if(A&&B){A=this[B](A)}var K=Selector.operators[C],F=[];for(var E=0,D;D=A[E];E++){var G=Element.readAttribute(D,H);if(G===null){continue}if(K(G,J)){F.push(D)}}return F},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._countedByPrototype){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){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._countedByPrototype){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&&(!E.type||E.type!=="hidden")){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==A||B&&B.startsWith(A)},"$=":function(B,A){return B==A||B&&B.endsWith(A)},"*=":function(B,A){return B==A||B&&B.include(A)},"~=":function(B,A){return(" "+B+" ").include(" "+A+" ")},"|=":function(B,A){return("-"+(B||"").toUpperCase()+"-").include("-"+(A||"").toUpperCase()+"-")}},split:function(B){var A=[];B.scan(/(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)/,function(C){A.push(C[1].strip())});return A},matchElements:function(F,G){var E=$$(G),D=Selector.handlers;D.mark(E);for(var C=0,B=[],A;A=F[C];C++){if(A._countedByPrototype){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){G=Selector.split(G.join(","));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){Object.extend(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=$(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!="file"&&(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(E){var F=$(E).getElementsByTagName("*"),D,A=[],C=Form.Element.Serializers;for(var B=0;D=F[B];B++){A.push(D)}return A.inject([],function(G,H){if(C[H.tagName.toLowerCase()]){G.push(Element.extend(H))
}return G})},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)$/i.test(D.tagName)})},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)$/i.test(A.type)))){A.select()}}catch(B){}return A},disable:function(A){A=$(A);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(C,F){if(Object.isUndefined(F)){return this[C.type=="select-one"?"selectOne":"selectMany"](C)}else{var B,D,G=!Object.isArray(F);for(var A=0,E=C.length;A<E;A++){B=C.options[A];D=this.optionValue(B);if(G){if(D==F){B.selected=true;return }}else{B.selected=F.include(D)}}}},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)}});(function(){var V={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:{}};var E=document.documentElement;var W="onmouseenter" in E&&"onmouseleave" in E;var O;if(Prototype.Browser.IE){var H={0:1,1:4,2:2};O=function(Y,X){return Y.button===H[X]}}else{if(Prototype.Browser.WebKit){O=function(Y,X){switch(X){case 0:return Y.which==1&&!Y.metaKey;case 1:return Y.which==1&&Y.metaKey;default:return false
}}}else{O=function(Y,X){return Y.which?(Y.which===X+1):(Y.button===X)}}}function R(X){return O(X,0)}function Q(X){return O(X,1)}function K(X){return O(X,2)}function C(Z){Z=V.extend(Z);var Y=Z.target,X=Z.type,a=Z.currentTarget;if(a&&a.tagName){if(X==="load"||X==="error"||(X==="click"&&a.tagName.toLowerCase()==="input"&&a.type==="radio")){Y=a}}if(Y.nodeType==Node.TEXT_NODE){Y=Y.parentNode}return Element.extend(Y)}function M(Y,a){var X=V.element(Y);if(!a){return X}var Z=[X].concat(X.ancestors());return Selector.findElement(Z,a,0)}function P(X){return{x:B(X),y:A(X)}}function B(Z){var Y=document.documentElement,X=document.body||{scrollLeft:0};return Z.pageX||(Z.clientX+(Y.scrollLeft||X.scrollLeft)-(Y.clientLeft||0))}function A(Z){var Y=document.documentElement,X=document.body||{scrollTop:0};return Z.pageY||(Z.clientY+(Y.scrollTop||X.scrollTop)-(Y.clientTop||0))}function N(X){V.extend(X);X.preventDefault();X.stopPropagation();X.stopped=true}V.Methods={isLeftClick:R,isMiddleClick:Q,isRightClick:K,element:C,findElement:M,pointer:P,pointerX:B,pointerY:A,stop:N};
var T=Object.keys(V.Methods).inject({},function(X,Y){X[Y]=V.Methods[Y].methodize();return X});if(Prototype.Browser.IE){function G(Y){var X;switch(Y.type){case"mouseover":X=Y.fromElement;break;case"mouseout":X=Y.toElement;break;default:return null}return Element.extend(X)}Object.extend(T,{stopPropagation:function(){this.cancelBubble=true},preventDefault:function(){this.returnValue=false},inspect:function(){return"[object Event]"}});V.extend=function(Y,X){if(!Y){return false}if(Y._extendedByPrototype){return Y}Y._extendedByPrototype=Prototype.emptyFunction;var Z=V.pointer(Y);Object.extend(Y,{target:Y.srcElement||X,relatedTarget:G(Y),pageX:Z.x,pageY:Z.y});return Object.extend(Y,T)}}else{V.prototype=window.Event.prototype||document.createEvent("HTMLEvents").__proto__;Object.extend(V.prototype,T);V.extend=Prototype.K}function L(b,a,c){var Z=Element.retrieve(b,"prototype_event_registry");if(Object.isUndefined(Z)){D.push(b);Z=Element.retrieve(b,"prototype_event_registry",$H())}var X=Z.get(a);if(Object.isUndefined(X)){X=[];
Z.set(a,X)}if(X.pluck("handler").include(c)){return false}var Y;if(a.include(":")){Y=function(d){if(Object.isUndefined(d.eventName)){return false}if(d.eventName!==a){return false}V.extend(d,b);c.call(b,d)}}else{if(!W&&(a==="mouseenter"||a==="mouseleave")){if(a==="mouseenter"||a==="mouseleave"){Y=function(f){V.extend(f,b);var d=f.relatedTarget;while(d&&d!==b){try{d=d.parentNode}catch(g){d=b}}if(d===b){return }c.call(b,f)}}}else{Y=function(d){V.extend(d,b);c.call(b,d)}}}Y.handler=c;X.push(Y);return Y}function F(){for(var X=0,Y=D.length;X<Y;X++){V.stopObserving(D[X]);D[X]=null}}var D=[];if(Prototype.Browser.IE){window.attachEvent("onunload",F)}if(Prototype.Browser.WebKit){window.addEventListener("unload",Prototype.emptyFunction,false)}var J=Prototype.K;if(!W){J=function(Y){var X={mouseenter:"mouseover",mouseleave:"mouseout"};return Y in X?X[Y]:Y}}function S(a,Z,b){a=$(a);var Y=L(a,Z,b);if(!Y){return a}if(Z.include(":")){if(a.addEventListener){a.addEventListener("dataavailable",Y,false)}else{a.attachEvent("ondataavailable",Y);
a.attachEvent("onfilterchange",Y)}}else{var X=J(Z);if(a.addEventListener){a.addEventListener(X,Y,false)}else{a.attachEvent("on"+X,Y)}}return a}function I(c,a,d){c=$(c);var Z=Element.retrieve(c,"prototype_event_registry");if(Object.isUndefined(Z)){return c}if(a&&!d){var b=Z.get(a);if(Object.isUndefined(b)){return c}b.each(function(e){Element.stopObserving(c,a,e.handler)});return c}else{if(!a){Z.each(function(g){var e=g.key,f=g.value;f.each(function(h){Element.stopObserving(c,e,h.handler)})});return c}}var b=Z.get(a);if(!b){return }var Y=b.find(function(e){return e.handler===d});if(!Y){return c}var X=J(a);if(a.include(":")){if(c.removeEventListener){c.removeEventListener("dataavailable",Y,false)}else{c.detachEvent("ondataavailable",Y);c.detachEvent("onfilterchange",Y)}}else{if(c.removeEventListener){c.removeEventListener(X,Y,false)}else{c.detachEvent("on"+X,Y)}}Z.set(a,b.without(Y));return c}function U(a,Z,Y,X){a=$(a);if(Object.isUndefined(X)){X=true}if(a==document&&document.createEvent&&!a.dispatchEvent){a=document.documentElement
}var b;if(document.createEvent){b=document.createEvent("HTMLEvents");b.initEvent("dataavailable",true,true)}else{b=document.createEventObject();b.eventType=X?"ondataavailable":"onfilterchange"}b.eventName=Z;b.memo=Y||{};if(document.createEvent){a.dispatchEvent(b)}else{a.fireEvent(b.eventType,b)}return V.extend(b)}Object.extend(V,V.Methods);Object.extend(V,{fire:U,observe:S,stopObserving:I});Element.addMethods({fire:U,observe:S,stopObserving:I});Object.extend(document,{fire:U.methodize(),observe:S.methodize(),stopObserving:I.methodize(),loaded:false});if(window.Event){Object.extend(window.Event,V)}else{window.Event=V}})();(function(){var D;function A(){if(document.loaded){return }if(D){window.clearTimeout(D)}document.loaded=true;document.fire("dom:loaded")}function C(){if(document.readyState==="complete"){document.stopObserving("readystatechange",C);A()}}function B(){try{document.documentElement.doScroll("left")}catch(E){D=B.defer();return }A()}if(document.addEventListener){document.addEventListener("DOMContentLoaded",A,false)
}else{document.observe("readystatechange",C);if(window==top){D=B.defer()}}Event.observe(window,"load",A)})();Element.addMethods();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);
/*
// Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
// 
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
// 
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
// For details, see the script.aculo.us web site: http://script.aculo.us/
*/
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}});var EXANIMO=EXANIMO||{};
/**
 *
 * StateManager
 *
 *     Adds state management (back button and deep-linking) to your single-page
 *     web application.
 *
 *     For usage instructions, see
 *     <http://exanimo.com/javascript/using-the-statemanager-in-ajax-apps>.
 *
 *     copyright 2007 matthew john tretter.  available under the MIT License.
 *     (<http://www.opensource.org/licenses/mit-license.php>)

 *     @author     matthew at exanimo dot com
 *     @version    2007.04.30
 *
 */
(function(){EXANIMO.managers=EXANIMO.managers||{};
var F=false;var L;var G;var E;var A;var J=false;var C="EXANIMO-managers-StateManager-iFrame";var H;function I(){return EXANIMO.managers.StateManager}var G=navigator.appName.indexOf("Microsoft Internet")!=-1?"IFRAME":navigator.userAgent.indexOf("Safari")!=-1?"LINK":"HASH";function M(){return document.location.href.split("#")[1]||I().defaultStateID}function K(){var P=["object","embed"];for(var Q=0;Q<P.length;Q++){var N=document.getElementsByTagName(P[Q]);for(var O=0;O<N.length;O++){if(N[O].dispatchStateChangeEvents){return N[O]}}}}function D(P,N){P=P||I().defaultStateID;if(!E){H={id:P};var O;if(O=I().onstatechange){H.type="stateChange";O(H)}if(N){if(O=I().onstateset){H.type="stateSet";O(H)}}else{if(O=I().onstaterevisit){H.type="stateRevisit";O(H)}}H=null}else{if(!N){E.dispatchStateChangeEvents(P)}}}var B=(function(){switch(G){case"HASH":return function(O){var N=O==I().defaultStateID?"#":O;document.location.hash=N;A=O};case"IFRAME":return function(O){J=true;var N=document.getElementById(C);
frames[C].document.open();frames[C].document.write('<script>parent.document.location.hash = "'+(O==I().defaultStateID?"#":O)+'"; /* Wait for IE to impose its title before setting ours. */ setTimeout( function(){ parent.EXANIMO.managers.StateManager._updateIFrame("'+O+'"); }, 0);<\/script>');frames[C].document.close()};case"LINK":return function(P){J=true;var O=document.createElement("a");O.setAttribute("href",P==I().defaultStateID?"#":"#"+P);var N=document.createEvent("MouseEvents");N.initEvent("click",true,true);O.dispatchEvent(N);document.location.EXANIMO.managers.StateManager.stateList.push(P)}}})();EXANIMO.managers.StateManager={CHECK_RATE:100,onstatechange:null,onstateset:null,onstaterevisit:null,DEFAULT_STATE:"defaultState",defaultStateID:"defaultState",initialize:function(Q){if(F){return }F=true;E=Q==true?K():Q;switch(G){case"HASH":A=M()==I().defaultStateID?I().defaultStateID:null;var O=function(){var T=M();if(T!=A){A=T;D(T)}};L=setInterval(O,I().CHECK_RATE);break;case"IFRAME":J=true;
var P=document.createElement("iframe");P.setAttribute("src","about:blank");P.setAttribute("name",C);P.setAttribute("id",C);P.style.visibility="hidden";P.style.width="0";P.style.height="0";P.style.position="absolute";P.style.overflow="hidden";document.body.appendChild(P);var S=M();if(S!=I().defaultStateID){setTimeout(function(){D(S)},0)}frames[C].document.open();if(S){frames[C].document.write('<script>parent.document.location.hash = "'+(S==I().defaultStateID?"":S)+'"; parent.EXANIMO.managers.StateManager._updateIFrame("'+S+'");<\/script>')}else{frames[C].document.write('<script>parent.document.location.hash = ""; parent.EXANIMO.managers.StateManager._updateIFrame();<\/script>')}frames[C].document.close();break;case"LINK":document.location.EXANIMO=document.location.EXANIMO||{};document.location.EXANIMO.managers=document.location.EXANIMO.managers||{};document.location.EXANIMO.managers.StateManager=document.location.EXANIMO.managers.StateManager||{};var R=document.location.EXANIMO.managers.StateManager;
window.onunload=function(){R.oldHistoryLength=-1};if(R.deepLink&&R.deepLink!=I().defaultStateID){R.oldHistoryLength=-1;R.deepLink=null}if(typeof R.stateList=="undefined"){R.stateList=[M()||I().defaultStateID];R.deepLink=R.stateList[0];R.offset=history.length-1;while(R.offset){R.stateList.unshift(null);R.offset--}delete R.offset;R.oldHistoryLength=document.location.hash?-1:history.length}var N=function(){var U=document.location.EXANIMO.managers.StateManager;if(J){J=false;U.oldHistoryLength=history.length;return }if(history.length!=U.oldHistoryLength){var T=U.stateList[history.length-1];D(T);U.oldHistoryLength=history.length}};L=setInterval(N,I().CHECK_RATE);break}},setState:function(O,N){if(N){I().setTitle(N)}if(H){return }B(O);D(O,true)},getState:function(){return M()},setTitle:function(N){window.document.title=N||" "},_updateIFrame:function(N){if(!J){D(N)}J=false}}})();var PicLensLite={start:function(B){this.determineBrowserParams();clearTimeout(this.REMOVE_TIMER_ID);clearTimeout(this.AUTO_CLOSE_TIMER_ID);
this.ARGS={};if(typeof B!=="undefined"&&B!==null){this.ARGS=B;if(B.feedUrl){this.THE_FEED_URL=B.feedUrl;if(this.checkForPluginAndLaunchIfPossible(B.feedUrl,B.guid)){return }if(B.loadFeedInFlash){this.showFlashUI("")}else{this.loadViaXHR(B.feedUrl)}}if(typeof B.feedData!=="undefined"){this.showFlashUI(B.feedData)}}else{var A=this.indexFeeds();if(A.length!==0){var C=A[0];this.THE_FEED_URL=C.url;if(this.checkForPluginAndLaunchIfPossible(C.url)){return }this.loadViaXHR(C.url)}}},isRunning:function(){return this.LITE_IS_RUNNING},hasClient:function(){return this.hasCooliris()},addCustomButton:function(C,B,A){this.CUSTOM_BUTTON={targetURL:C,labelText:B,iconImage:A}},setCallbacks:function(A){if(A.onNoPlugins){this.ON_NO_PLUGINS=A.onNoPlugins}if(A.onExit){this.ON_EXIT=A.onExit}},setLiteURLs:function(A){if(!this.LITE_URL){if(A.swf){this.LITE_URL=A.swf}else{if(A.lite){this.LITE_URL=A.lite+"PicLensLite.swf"}}}if(!this.BUTTON_URL){if(A.button){this.BUTTON_URL=A.button}else{if(A.lite){this.BUTTON_URL=A.lite+"NoFlash.jpg"
}}}var B="";if(A.lbox){B=A.lbox}else{if(A.lite){B=A.lite+"../lytebox/"}}if(!this.LBOX_CSS_URL){if(A.lboxcss){this.LBOX_CSS_URL=A.lboxcss}else{if(B!=""){this.LBOX_CSS_URL=B+"lytebox.css"}}}if(!this.LBOX_JS_URL){if(A.lboxjs){this.LBOX_JS_URL=A.lboxjs}else{if(B!=""){this.LBOX_JS_URL=B+"lytebox.js"}}}},ARGS:{},DEBUG_NOCLIENT:false,DEBUG_NOFLASH:false,HPAD:60,VPAD:20,LITE_BG_DIV:null,LITE_FG_DIV:null,LITE_URL:null,BUTTON_URL:null,LBOX_CSS_URL:null,LBOX_JS_URL:null,LBOX_COUNT:0,SHOW_LBOX:false,OS_WIN:false,OS_MAC:false,BROWSER_FFX:false,BROWSER_SAF:false,BROWSER_IE:false,BROWSER_IE6:false,OLD_B_MARGIN:null,OLD_B_OVERFLOW:null,OLD_B_HEIGHT:null,OLD_H_OVERFLOW:null,OLD_H_HEIGHT:null,THE_FEED:"",THE_FEED_URL:"",LITE_IS_RUNNING:false,piclensIsRunning_:false,FLASH_ID_1:"pllflash1",FLASH_ID_2:"pllflash2",FLASH_VER:null,FLASH_URL:"http://www.adobe.com/go/getflashplayer",PL_URL:"http://download.piclens.com/partner/",PLC:null,LEARN_PL_URL:"http://affiliate.piclens.com/partner/",FONT:"font-family: Lucida Grande, Myriad Pro, Verdana, Helvetica, Arial, sans-serif;",KEY_HANDLERS:"",ON_NO_PLUGINS:null,ON_EXIT:null,AUTO_CLOSE_TIMER_ID:0,REMOVE_TIMER_ID:0,RESIZE_TIMER_IE6:null,RESIZE_HANDLER_EXISTS:false,CUSTOM_BUTTON:null,addKeyHandlers:function(){var A=this;
if(typeof document.onkeydown!=="undefined"){this.KEY_HANDLERS=document.onkeydown}document.onkeydown=function(C){var B;if(typeof C==="undefined"||C===null){B=window.event.keyCode}else{B=C.which}var D=A.handleKeyPress(B);if(typeof C!="undefined"&&C!=null){C.returnValue=D}return D}},addMouseHandlers:function(){if(window.addEventListener){window.addEventListener("DOMMouseScroll",this.handleMouseWheel,false)}else{if(document.attachEvent){document.attachEvent("onmousewheel",this.handleMouseWheel)}}window.onmousewheel=document.onmousewheel=this.handleMouseWheel},appendElementsToDocument:function(){if(this.BROWSER_FFX&&this.OS_MAC){this.LITE_BG_DIV.style.display="none"}document.body.appendChild(this.LITE_BG_DIV);document.body.appendChild(this.LITE_FG_DIV)},autoResize:function(){if(!this.isRunning()){clearInterval(this.RESIZE_TIMER_IE6);return }var C=this.getPageSize();var B=this.LITE_BG_DIV;if(B){B.style.height=C.h+"px";B.style.width=C.w+"px"}if(this.LITE_FG_DIV){var A=this.LITE_FG_DIV.style;this.resizeToPaddedBox(A);
this.resizeToFitPaddedBox(A,C);this.resizeFlashToFitPaddedBox()}},checkForPluginAndLaunchIfPossible:function(B,A){if(this.hasCooliris()){if(typeof (A)!="undefined"){this.PLC.launch(B,"uid",A)}else{this.PLC.launch(B,"","")}return true}return false},createBackgroundOverlay:function(){var D=document.createElement("div");this.LITE_BG_DIV=D;D.id="lite_bg_div";var E=D.style;E.position="fixed";E.width=E.height="100%";if(this.BROWSER_IE6){var A=document.body;var C=A.currentStyle;var H=document.documentElement;var G=H.currentStyle;this.OLD_B_MARGIN=C.margin;this.OLD_B_OVERFLOW=C.overflow;this.OLD_B_HEIGHT=C.height;this.OLD_H_OVERFLOW=G.overflow;this.OLD_H_HEIGHT=G.height;this.OLD_SCROLL_Y=H.scrollTop;A.style.margin="0";A.style.overflow="auto";A.style.height="100%";H.style.overflow="auto";H.style.height="100%";E.position="absolute";var F=this.getPageSize();E.height=F.h+"px";E.width=F.w+"px"}E.left=E.right=E.top=E.bottom="0";E.backgroundColor="#000";E.zIndex=1000;E.opacity="0.5";E.filter="alpha(opacity=50)";
var B=this;D.onclick=function(){B.exitPicLensLite()}},createForegroundFlashComponent:function(){var B=document.createElement("div");this.LITE_FG_DIV=B;B.id="lite_fg_div";var A=B.style;A.backgroundColor="#000";A.position="fixed";A.border="2px solid #555";A.zIndex=1001;this.resizeToPaddedBox(A);if(this.BROWSER_IE6){A.position="absolute";this.resizeToFitPaddedBox(A)}},closeFlashUI:function(C){var B=document;B.onkeydown=this.KEY_HANDLERS;window.onmousewheel=B.onmousewheel="";if(window.removeEventListener){window.removeEventListener("DOMMouseScroll",this.handleMouseWheel,false)}if(B.detachEvent){B.detachEvent("onmousewheel",this.handleMouseWheel)}this.LITE_BG_DIV.style.display=this.LITE_FG_DIV.style.display="none";this.REMOVE_TIMER_ID=setTimeout(function(){PicLensLite.removeChildren()},150);if(this.BROWSER_IE6){var A=document.body;var D=document.documentElement;A.style.margin=this.OLD_B_MARGIN;A.style.overflow=this.OLD_B_OVERFLOW;A.style.height=this.OLD_B_HEIGHT;D.style.overflow=this.OLD_H_OVERFLOW;
D.style.height=this.OLD_H_HEIGHT;window.scrollTo(0,this.OLD_SCROLL_Y)}if(this.ON_EXIT!==null){this.ON_EXIT(C)}this.setRunningFlag(false)},determineBrowserParams:function(){var B=BrowserDetect.OS;var A=BrowserDetect.browser;this.OS_MAC=(B=="Mac");this.OS_WIN=(B=="Windows");this.BROWSER_FFX=(A=="Firefox");this.BROWSER_SAF=(A=="Safari");this.BROWSER_IE=(A=="Explorer");this.BROWSER_IE6=(this.BROWSER_IE&&BrowserDetect.version=="6");this.FLASH_VER=swfobjlite.getFlashPlayerVersion()},exitPicLensLite:function(){var A=this.getFlash();if(A!==null&&A.fl_exitPicLensLite){A.fl_exitPicLensLite();this.AUTO_CLOSE_TIMER_ID=setTimeout(function(){if(PicLensLite.isRunning()){PicLensLite.closeFlashUI()}},500)}else{this.closeFlashUI()}},findScriptLocation:function(){var E=document.getElementsByTagName("script");for(var C=0;C!=E.length;++C){var A=E[C];var D=A.getAttribute("type");if(D=="text/javascript"){var F=A.getAttribute("src");if(F===null){continue}var B=F.indexOf("piclens.js");if(B!=-1){this.setLiteURLs({lite:F.substring(0,B)});
return }else{B=F.indexOf("piclens_optimized.js");if(B!=-1){this.setLiteURLs({lite:F.substring(0,B)});return }}}}},getPageSize:function(){var J,F,G,C;var I=document;var E=I.body;var D;if(window.innerHeight&&window.scrollMaxY){J=I.scrollWidth;F=(this.isFrame?parent.innerHeight:self.innerHeight)+(this.isFrame?parent.scrollMaxY:self.scrollMaxY)}else{if(E.scrollHeight>E.offsetHeight){J=E.scrollWidth;F=E.scrollHeight}else{D=I.getElementsByTagName("html").item(0);J=D.offsetWidth;F=D.offsetHeight;J=(J<E.offsetWidth)?E.offsetWidth:J;F=(F<E.offsetHeight)?E.offsetHeight:F}}var A=I.documentElement;if(self.innerHeight){G=(this.isFrame)?parent.innerWidth:self.innerWidth;C=(this.isFrame)?parent.innerHeight:self.innerHeight}else{if(A&&A.clientHeight){G=A.clientWidth;C=A.clientHeight}else{if(E){D=I.getElementsByTagName("html").item(0);G=D.clientWidth;C=D.clientHeight;G=(G==0)?E.clientWidth:G;C=(C==0)?E.clientHeight:C}}}var B=(F<C)?C:F;var H=(J<G)?G:J;return{pw:H,ph:B,w:G,h:C}},getElementsFromXMLFeed:function(){var B;
if(window.ActiveXObject){B=new ActiveXObject("Microsoft.XMLDOM");B.async=false;B.loadXML(PicLensLite.THE_FEED)}else{var C=new DOMParser();B=C.parseFromString(PicLensLite.THE_FEED,"text/xml")}var A=B.getElementsByTagName("*");return A},getBasicSlideShowHTML:function(){if(!this.LBOX_JS_URL||!this.LBOX_CSS_URL){return""}var D=document.getElementsByTagName("head").item(0);var A=document.createElement("script");A.src=this.LBOX_JS_URL;A.type="text/javascript";D.appendChild(A);var G=document.createElement("link");G.rel="stylesheet";G.href=this.LBOX_CSS_URL;G.type="text/css";G.media="screen";D.appendChild(G);var F=this.getElementsFromXMLFeed();var C;var H="";for(C=0;C<F.length;C++){if(F[C].nodeName=="media:content"){var B=F[C].getAttribute("url");if(B.indexOf(".flv")==-1){H+='<a id="lboxImage" href="'+B+'" rel="lytebox[lite]"></a> '}}}var E="<div id='lightbox_images' align='center' style='display: none; padding-top:10px; color:#FFFFFF; font-size:.8em; "+this.FONT+" color:#999999;'>";E+='( Alternatively, <a onclick="javascript:PicLensLite.invokeLytebox();return false;" href="#" style="color:#656588">click here for a basic slideshow</a>. )';
E+=H;E+="</div><br/>";return E},generateAlternativeContent:function(){var J='<div id="altContent" style="text-align:center; margin: 0 0 0 0; padding: 0 0 0 0; background-color: #000; min-width:860px;">';J+='<div align="center" style="width: 100%; padding-top:60px; '+this.FONT+'">';var I=this.FLASH_VER;var G;if(I.major>0){G="update your Flash Player from version "+I.major+"."+I.minor+"."+I.release+" to version 9.0.28 or newer"}else{G="install the most recent Flash Player"}var D="";if(this.THE_FEED!==""){D=this.getBasicSlideShowHTML()}var A=this.PL_URL;var E=this.LEARN_PL_URL;var F=this.ARGS.pid;if(F){A+=F+"/";E+=F+"/"}else{var H="000000000001/";A+=H;E+=H}if(this.SHOW_LBOX){}else{var C="<span style='padding-left:25px; color:#C6C6C6; font-size:";J+="<div style='padding:10px;'>"+C+"1.5em; font-weight: bold; "+this.FONT+"'>You're clicks away from going full screen!</span><br/>"+C+".9em; padding-bottom: 15px; "+this.FONT+"'>You must get the <a href='"+A+"' style='color:#656588'>Cooliris</a> browser plugin, or "+G+".</span></div>";
if(!this.BUTTON_URL){J+='<a href="'+A+'" style="color:#ACD">Get Cooliris Now!</a>'}else{var B='<area shape="rect" coords=';J+='<img src="'+this.BUTTON_URL+'" alt="" border="0" usemap="#Map"><map name="Map" id="Map">'+B+'"0,0,33,33" href="#" onclick="javascript:PicLensLite.closeFlashUI();" />'+B+'"35,35,325,325" href="'+A+'" />'+B+'"593,209,825,301" href="'+this.FLASH_URL+'" />'+B+'"327,148,448,178" href="'+E+'" /></map>'}}J+="</div>";J+=D;J+='<div align="center" style="color:#666666; font-size:11px; '+this.FONT+'">&copy; 2008 Cooliris, Inc. All trademarks are property of their respective holders.<br/><br/><br/></div>';J+="</div>";return J},generateFlashVars:function(){var C="";var B=this.ARGS;if(typeof B.guid!=="undefined"){C+="&startItemGUID="+B.guid}if(B.loadFeedInFlash){C+="&feedURL="+encodeURIComponent(this.THE_FEED_URL)}if(B.paused){C+="&paused="+B.paused}if(B.loop){C+="&loop="+B.loop}if(B.delay){C+="&delay="+B.delay}if(B.pid){C+="&pid="+B.pid}if(typeof B.maxScale!="undefined"){C+="&maxScale="+B.maxScale
}if(typeof B.overlayToolbars!="undefined"){C+="&overlayToolbars="+B.overlayToolbars}var A=this.CUSTOM_BUTTON;if(A!=null){C+="&cButtonURL="+encodeURIComponent(A.targetURL);if(A.labelText!=null){C+="&cButtonLabel="+encodeURIComponent(A.labelText)}if(A.iconImage!=null){C+="&cButtonIcon="+encodeURIComponent(A.iconImage)}}C+="&swfURL="+encodeURIComponent(this.LITE_URL);C=C.substring(1);return C},getFlash:function(){if(this.BROWSER_SAF||this.BROWSER_IE){return document.getElementById(this.FLASH_ID_1)}else{return document.getElementById(this.FLASH_ID_2)}},getWindowSize:function(){var B=document.documentElement;var D=document.body;var A=0,C=0;if(typeof (window.innerWidth)=="number"){A=window.innerWidth;C=window.innerHeight}else{if(B&&(B.clientWidth||B.clientHeight)){A=B.clientWidth;C=B.clientHeight}else{if(D&&(D.clientWidth||D.clientHeight)){A=D.clientWidth;C=D.clientHeight}}}return{w:A,h:C}},handleKeyPress:function(A){if(!this.isRunning()){return true}var B=this.getFlash();if(B!=null&&B.fl_keyPressed){B.fl_keyPressed(A)
}else{if(A==27){this.closeFlashUI();return false}}if(A==9||A==13){return false}return true},handleMouseWheel:function(A){var C=0;if(!A){A=window.event}if(A.wheelDelta){C=A.wheelDelta/120;if(window.opera){C=-C}}else{if(A.detail){var B=A.detail;if(Math.abs(B)<3){C=-B}else{C=-B/3}}}if(C){PicLensLite.sendMouseScrollToFlash(C)}if(A.preventDefault){A.preventDefault()}A.returnValue=false;return false},hasPicLensClient:function(){return this.hasCooliris()},hasCooliris:function(){if(this.DEBUG_NOCLIENT){return false}var E=false;if(this.PLC){E=true}else{if(window.piclens&&window.piclens.launch){this.PLC=window.piclens;E=true}else{var B=null;if(typeof PicLensContext!="undefined"){B=new PicLensContext()}else{try{B=new ActiveXObject("PicLens.Context")}catch(D){if(navigator.mimeTypes["application/x-cooliris"]){B=document.createElement("object");B.style.height="0px";B.style.width="0px";B.type="application/x-cooliris";document.documentElement.appendChild(B)}else{B=null}}}this.PLC=B;if(this.PLC){E=true}}}if(E){if(this.BROWSER_SAF){return true
}var A;try{A=this.PLC.version}catch(D){return false}var C=A.split(".");if(C[0]>1){return true}else{if(C[0]==1){if(C[1]>6){return true}else{if(C[1]==6){if(C[2]>0){return true}else{if(C[2]==0){if(C[3]>=824){return true}}}}}}}return false}else{return false}},invokeLytebox:function(){this.SHOW_LBOX=true;myLytebox.start(document.getElementById("lboxImage"),false,false);this.closeFlashUI()},showLyteboxLink:function(){myLytebox.updateLyteboxItems();myLytebox.doAnimations=false;var A=document.getElementById("lightbox_images");if(A!=null){A.style.display="block";if(this.SHOW_LBOX&&this.getFlash()==null){this.invokeLytebox()}}},startLytebox:function(){if(typeof myLytebox!="undefined"){this.showLyteboxLink()}else{if(typeof initLytebox!="undefined"){initLytebox();this.showLyteboxLink()}else{if(this.LBOX_COUNT>=4){return }setTimeout(function(){PicLensLite.startLytebox()},150);this.LBOX_COUNT++}}},injectFlashPlayer:function(){var A=this.LITE_FG_DIV;var D;var F;D=F="100%";if(this.BROWSER_IE6){D=F="0"}var E=this.generateFlashVars();
var B=this.generateAlternativeContent();if(this.meetsReqs()){var C="<param name=";A.innerHTML='<object id="'+this.FLASH_ID_1+'" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" width="100%" height="100%">'+C+'"movie" value="'+this.LITE_URL+'" />'+C+'"quality" value="high"/> '+C+'"bgcolor" value="#000000"/> '+C+'"allowScriptAccess" value="always"/> '+C+'"FlashVars" value="'+E+'"/> '+C+'"allowFullScreen" value="true"/> '+C+'"wmode" value="window"/> '+C+'"scale" value="noscale"/> <object type="application/x-shockwave-flash" data="'+this.LITE_URL+'" width="'+D+'" height="'+F+'" quality="high" bgcolor="#000000" id="'+this.FLASH_ID_2+'" quality="high" FlashVars="'+E+'" allowFullScreen="true" scale="noscale" wmode="window" allowScriptAccess="always">'+B+"</object></object>"}else{if(this.ON_NO_PLUGINS){this.ON_NO_PLUGINS()}else{A.innerHTML=B;A.style.minWidth="860px";A.style.minHeight="550px"}}if(this.BROWSER_SAF){this.resizeUI()}},indexFeeds:function(){var E=document.getElementsByTagName("link");
var A=[];for(var B=0;B!=E.length;++B){var D=E[B],C=D.getAttribute("type");if(C=="application/rss+xml"||C=="text/xml"){A.push({title:D.getAttribute("title"),url:D.getAttribute("href")})}}return A},loadViaXHR:function(B){var A=this;var D=window.XMLHttpRequest?new XMLHttpRequest():new ActiveXObject("MSXML2.XMLHTTP.3.0");try{D.open("GET",B,true);D.onreadystatechange=function(){if(D.readyState==4){if((D.status==200||D.status==0)){if(D.responseText){A.showFlashUI(D.responseText)}}else{if(console){console.log("PicLens Lite could not load the RSS Feed: "+B)}}}};D.send("")}catch(C){this.ARGS.loadFeedInFlash=true;this.showFlashUI("")}},meetsReqs:function(){if(this.DEBUG_NOFLASH){return false}var B=(this.FLASH_VER.major==0)&&this.BROWSER_IE;var A=swfobjlite.hasFlashPlayerVersion("9.0.28");return A||B},removeChildren:function(){this.REMOVE_TIMER_ID=0;if(this.LITE_BG_DIV!==null){document.body.removeChild(this.LITE_BG_DIV);this.LITE_BG_DIV=null}if(this.LITE_FG_DIV!==null){document.body.removeChild(this.LITE_FG_DIV);
this.LITE_FG_DIV=null}},resizeFlashToFitPaddedBox:function(){var B=this.getFlash();if(B){var C=this.getPageSize();var A=C.w-this.HPAD*2;var D=C.h-this.VPAD*2;B.style.width=A;B.style.height=D;B.width=A;B.height=D}},resizeToFitPaddedBox:function(B,A){if(typeof A=="undefined"){A=this.getPageSize()}B.width=(A.w-this.HPAD*2)+"px";B.height=(A.h-this.VPAD*2)+"px"},resizeToPaddedBox:function(A){A.left=A.right=this.HPAD+"px";A.top=A.bottom=this.VPAD+"px"},resizeUI:function(){if(this.LITE_FG_DIV){var A=this.LITE_FG_DIV.style;this.resizeToPaddedBox(A);this.resizeToFitPaddedBox(A);this.resizeFlashToFitPaddedBox()}},setRunningFlag:function(A){this.LITE_IS_RUNNING=A;this.piclensIsRunning_=A},setResizeHandler:function(){if(!this.RESIZE_HANDLER_EXISTS&&this.BROWSER_SAF){var A=this;window.addEventListener("resize",function(){A.resizeUI()},false);this.RESIZE_HANDLER_EXISTS=true}},setResizeTimer:function(){if(this.BROWSER_IE6){this.RESIZE_TIMER_IE6=setInterval(function(){PicLensLite.autoResize()},1000)}},showFlashUI:function(A){this.THE_FEED=A;
this.findScriptLocation();this.createBackgroundOverlay();this.createForegroundFlashComponent();if(this.BROWSER_IE){this.appendElementsToDocument()}this.injectFlashPlayer();if(!this.BROWSER_IE){this.appendElementsToDocument()}this.addKeyHandlers();this.addMouseHandlers();this.setRunningFlag(true);this.setResizeTimer();this.setResizeHandler();this.startLytebox()},sendMouseScrollToFlash:function(B){if(!this.isRunning()){return }var A=this.getFlash();if(A!=null&&A.fl_mouseMoved){A.fl_mouseMoved(B)}}};var swfobjlite=function(){var UNDEF="undefined",OBJECT="object",SHOCKWAVE_FLASH="Shockwave Flash",SHOCKWAVE_FLASH_AX="ShockwaveFlash.ShockwaveFlash",win=window,doc=document,nav=navigator;var ua=function(){var w3cdom=typeof doc.getElementById!=UNDEF&&typeof doc.getElementsByTagName!=UNDEF&&typeof doc.createElement!=UNDEF&&typeof doc.appendChild!=UNDEF&&typeof doc.replaceChild!=UNDEF&&typeof doc.removeChild!=UNDEF&&typeof doc.cloneNode!=UNDEF,playerVersion=[0,0,0],d=null;if(typeof nav.plugins!=UNDEF&&typeof nav.plugins[SHOCKWAVE_FLASH]==OBJECT){d=nav.plugins[SHOCKWAVE_FLASH].description;
if(d){d=d.replace(/^.*\s+(\S+\s+\S+$)/,"$1");playerVersion[0]=parseInt(d.replace(/^(.*)\..*$/,"$1"),10);playerVersion[1]=parseInt(d.replace(/^.*\.(.*)\s.*$/,"$1"),10);playerVersion[2]=/r/.test(d)?parseInt(d.replace(/^.*r(.*)$/,"$1"),10):0}}else{if(typeof win.ActiveXObject!=UNDEF){var a=null,fp6Crash=false;try{a=new ActiveXObject(SHOCKWAVE_FLASH_AX+".7")}catch(e){try{a=new ActiveXObject(SHOCKWAVE_FLASH_AX+".6");playerVersion=[6,0,21];a.AllowScriptAccess="always"}catch(e){if(playerVersion[0]==6){fp6Crash=true}}if(!fp6Crash){try{a=new ActiveXObject(SHOCKWAVE_FLASH_AX)}catch(e){}}}if(!fp6Crash&&a){try{d=a.GetVariable("$version");if(d){d=d.split(" ")[1].split(",");playerVersion=[parseInt(d[0],10),parseInt(d[1],10),parseInt(d[2],10)]}}catch(e){}}}}var u=nav.userAgent.toLowerCase(),p=nav.platform.toLowerCase(),webkit=/webkit/.test(u)?parseFloat(u.replace(/^.*webkit\/(\d+(\.\d+)?).*$/,"$1")):false,ie=false,windows=p?/win/.test(p):/win/.test(u),mac=p?/mac/.test(p):/mac/.test(u);
/*@cc_on
			ie = true;
			@if (@_win32)
				windows = true;
			@elif (@_mac)
				mac = true;
			@end
		@*/
return{w3cdom:w3cdom,pv:playerVersion,webkit:webkit,ie:ie,win:windows,mac:mac}
}();return{hasFlashPlayerVersion:function(rv){var pv=ua.pv,v=rv.split(".");v[0]=parseInt(v[0],10);v[1]=parseInt(v[1],10);v[2]=parseInt(v[2],10);return(pv[0]>v[0]||(pv[0]==v[0]&&pv[1]>v[1])||(pv[0]==v[0]&&pv[1]==v[1]&&pv[2]>=v[2]))?true:false},getFlashPlayerVersion:function(){return{major:ua.pv[0],minor:ua.pv[1],release:ua.pv[2]}}}}();var BrowserDetect={init:function(){this.browser=this.searchString(this.dataBrowser)||"Unknown Browser";this.version=this.searchVersion(navigator.userAgent)||this.searchVersion(navigator.appVersion)||"Unknown Version";this.OS=this.searchString(this.dataOS)||"Unknown OS"},searchString:function(D){for(var A=0;A<D.length;A++){var B=D[A].string;var C=D[A].prop;this.versionSearchString=D[A].versionSearch||D[A].identity;if(B){if(B.indexOf(D[A].subString)!=-1){return D[A].identity}}else{if(C){return D[A].identity}}}},searchVersion:function(B){var A=B.indexOf(this.versionSearchString);if(A==-1){return }return parseFloat(B.substring(A+this.versionSearchString.length+1))
},dataBrowser:[{string:navigator.userAgent,subString:"OmniWeb",versionSearch:"OmniWeb/",identity:"OmniWeb"},{string:navigator.vendor,subString:"Apple",identity:"Safari"},{prop:window.opera,identity:"Opera"},{string:navigator.vendor,subString:"iCab",identity:"iCab"},{string:navigator.vendor,subString:"KDE",identity:"Konqueror"},{string:navigator.userAgent,subString:"Firefox",identity:"Firefox"},{string:navigator.vendor,subString:"Camino",identity:"Camino"},{string:navigator.userAgent,subString:"Netscape",identity:"Netscape"},{string:navigator.userAgent,subString:"MSIE",identity:"Explorer",versionSearch:"MSIE"},{string:navigator.userAgent,subString:"Gecko",identity:"Mozilla",versionSearch:"rv"},{string:navigator.userAgent,subString:"Mozilla",identity:"Netscape",versionSearch:"Mozilla"}],dataOS:[{string:navigator.platform,subString:"Win",identity:"Windows"},{string:navigator.platform,subString:"Mac",identity:"Mac"},{string:navigator.platform,subString:"Linux",identity:"Linux"}]};BrowserDetect.init();

function _NAMESPACE_( namespace, items ) {
  // Create namespace if it does not exists already
  var ns = namespace.split( '.' ).inject(window, function(parent, child) {
    var o = parent[child] = parent[child] || {}; 
    return o;
  });
  // Copy items to namespace
  if( items ) {
    Object.extend( ns, items );
  }
}


// EOF

function initDownloadBehaviors( params ) {
  initFooterBehavior( params );
  new exposure.home.SupportedOperatorsOverlayBehavior( $$( '.showOperators' )[ 0 ] );
}

function selectCurrentPageLink() {
  var page = location.href.split('/').last();
  var link = $$('a[href=' + page + ']')[0];
  if (link) {
    link.addClassName('selected');
  }
}

function initFooterBehavior( params ) {
  Builder.dump();
  
  var footerB = new exposure.home.FooterBehavior( $$( '.footer' )[ 0 ] );
  if ( params && params.forgotElem ) { // If a dom node
    footerB.attachForgotPasswordListener( params.forgotElem );
  }
  
  new exposure.home.OverlayBehavior( $$( '.screen' )[ 0 ] );
  
  selectCurrentPageLink();
}


// EOF

function initHelpBehaviors( params ) {
  initFooterBehavior( params );
  new exposure.home.SupportedOperatorsOverlayBehavior( $$( '.showOperators' )[ 0 ] );
}


_NAMESPACE_( 'exposure.CONST' );
Object.extend( exposure.CONST, {
  
    PATH: '/',
    SERVICE_NAME: 'Nokia Image Exchange',
    DEFAULT_HASH: '/latest',
    DEFAULT_LOGGED_IN_HASH: '/mylatest',
    SECTION: {
      MY: 'my',
      PUBLIC: 'public',
      MANAGE: 'manage'
    },
    
    REST_PUBLIC_COLLECTION_URL: 'rest/w/public/collection/',
    REST_PRIVATE_COLLECTION_URL: 'rest/w/users/{current_userid}/collection/'

});

Object.extend( exposure.CONST, {

    KEYWORD_TO_COLL_ID_MAP: {
      // PUBLIC
      'latest': {
        needsUser: false,
        urlAnchor: 'latest',
        collectionServiceUrl: exposure.CONST.REST_PUBLIC_COLLECTION_URL + 'latest',
        collectionFeedUrl: '/feed/public-media-rss.xml',
        title: 'Latest',
        section: exposure.CONST.SECTION.PUBLIC
      },          
      'people': {
        needsUser: true,
        urlAnchor: 'people',
        collectionServiceUrl: exposure.CONST.REST_PRIVATE_COLLECTION_URL + 'people',
        collectionFeedUrl: null,
        title: 'People',
        section: exposure.CONST.SECTION.PUBLIC
      },
      'myshared': {
        needsUser: true,
        urlAnchor: 'myshared',
        collectionServiceUrl: exposure.CONST.REST_PRIVATE_COLLECTION_URL + 'myshared',
        collectionFeedUrl: null,
        title: 'My Public',
        section: exposure.CONST.SECTION.PUBLIC
      }, 
      'user': {
        needsUser: true,
        urlAnchor: 'user/{userToken}',
        collectionServiceUrl: exposure.CONST.REST_PUBLIC_COLLECTION_URL + 'users/{userToken}',
        collectionFeedUrl: '/feed/user-{userToken}-media-rss.xml',
        title: 'My Page',
        section: exposure.CONST.SECTION.PUBLIC
      },
      // MY
      'mylatest': {
        needsUser: true,
        urlAnchor: 'mylatest',
        collectionServiceUrl: exposure.CONST.REST_PRIVATE_COLLECTION_URL + 'mylatest',
        collectionFeedUrl: null,
        title: 'Latest',
        section: exposure.CONST.SECTION.MY
      },
      'mytime': {
        needsUser: true,
        urlAnchor: 'mytime',
        collectionServiceUrl: exposure.CONST.REST_PRIVATE_COLLECTION_URL + 'mytime',
        collectionFeedUrl: null,
        title: 'Time',
        section: exposure.CONST.SECTION.MY
      },
      'myfavorites': {
        needsUser: true,
        urlAnchor: 'myfavorites',
        collectionServiceUrl: exposure.CONST.REST_PRIVATE_COLLECTION_URL + 'myfavorites',
        collectionFeedUrl: null,
        title: 'Favorites',
        section: exposure.CONST.SECTION.MY
      },
      // MANAGE
      'manage': {
        needsUser: true,
        urlAnchor: 'manage',
        collectionServiceUrl: 'not_applicable',
        collectionFeedUrl: null,
        title: 'Manage',
        section: exposure.CONST.SECTION.MANAGE
      }

    },
    DEFAULT_THUMB_SIZE: 100,
    MAX_THUMB_SIZE: 160,
    THUMB_SIZE_COOKIE: 'ImExThumbSize'
  
});

if ( location.pathname.indexOf( '/exposure' ) === 0 ) {
  exposure.CONST.PATH = '/exposure/';
}


/* main() */
var main_home = function () {

  (function () {
    new Effect.Fade( $$( '.statusMsg' )[0], { duration: 0.3 } );
  }).delay( 5 );

  Builder.dump();
  var pageState = exposure.common.PageState;
  pageState.initialize();

  // Header
  var headerCtrl = new exposure.home.header.HeaderController();
  var headerView = new exposure.home.header.HeaderView( $$( '.header' )[ 0 ] );
  headerCtrl.registerView( headerView );
  pageState.setController( '*', headerCtrl );
  
  var loginBehavior = new exposure.home.LoginRequiredBehavior( $$( '.screen' )[ 0 ] );
  headerView.addBehavior( loginBehavior );

  // Navigation element  
  var navElem = new exposure.home.NavigationElement( $$( '.navigation' )[ 0 ] );
  var navBehavior = new exposure.home.NavigationBehavior( navElem.getDiv() );

  // Grid view
  var keywordCollCtrl = new exposure.home.grid.FixedCollectionController();
//  var infoBarView = new exposure.home.grid.InfoBarView( $('infoBar') );
//  keywordCollCtrl.registerView( infoBarView );
  var collView = new exposure.home.grid.CollectionView( $('grid') );
  collView.addElement( navElem );
  collView.addBehavior( navBehavior );
  keywordCollCtrl.registerView( collView );  
  pageState.setController( 'mylatest', keywordCollCtrl );
  pageState.setController( 'mytime', keywordCollCtrl );
  pageState.setController( 'latest', keywordCollCtrl );
  pageState.setController( 'myfavorites', keywordCollCtrl );
  pageState.setController( 'myshared', keywordCollCtrl );

  // People menu
  var contactsCtrl = new exposure.home.contacts.ContactsController( 
      exposure.CONST.KEYWORD_TO_COLL_ID_MAP );
  var contactsSideBarView = new exposure.home.contacts.ContactsSideBarView( $('infoBar') );
  contactsCtrl.registerView( contactsSideBarView );
  contactsCtrl.registerView( collView );  
  pageState.setController( 'people', contactsCtrl );


  // Users public images page
  var userPageCtrl = new exposure.home.userpage.UserPageController( 
      exposure.CONST.KEYWORD_TO_COLL_ID_MAP );
  var profileInfoView = new exposure.home.userpage.ProfileInfoView($('profileInfo'));
  userPageCtrl.registerView(profileInfoView);
  userPageCtrl.registerView(collView);
  pageState.setController( 'user', userPageCtrl );


  // Single image view
  var imgCtrl = new exposure.home.photo.PhotoController();
  var imgView = new exposure.home.photo.PhotoView( $('photoDisplay') );
  var img404View = new exposure.home.photo.PhotoNotFoundView( $('photo404Display') );
  var emailSharingView = new exposure.home.email.EmailSharingView( $$('.screen .view')[0] );
  imgView.addElement( navElem );
  imgView.addBehavior( navBehavior );
  imgCtrl.registerView( imgView );
  imgCtrl.registerView( img404View );
  imgCtrl.registerView( emailSharingView );
  pageState.setController( 'photo', imgCtrl );
  
  // Management view
  var manCtrl = new exposure.home.manage.ManageController();
  var manView = new exposure.home.manage.ManageView( $('manageDisplay') );
  manCtrl.registerView( manView );
  pageState.setController( 'manage', manCtrl );
  
  pageState.initStateManager();

};


// EOF


/* main() */
var main_settings = function () {
  
  Builder.dump();

  var div = $( 'settingsDisplay' );
  
  new exposure.settings.DescriptionInputBehavior( div.down( 'textarea[name=description]' ) );
  new exposure.settings.AvatarUploadBehavior( div.down( '.avatarInput' ) );
  new exposure.settings.FormBehavior( div.down( '.settingsForm' ) );
  new exposure.settings.UsernameBehavior( div.down( 'input[name=username]' ) );
  new exposure.home.FooterBehavior( $$( '.footer' )[ 0 ] );
  new exposure.home.OverlayBehavior( $$( '.screen' )[ 0 ] );

  selectCurrentPageLink(); // in footer.js 
};
  
  
// EOF


_NAMESPACE_( 'exposure.CONST', {
  
  LOGIN_REQUIRED: 'account:loginRequired'
  
});



_NAMESPACE_( 'exposure.common' );


/*public*/
exposure.common.Account = {

  getUser: function () {
    return ( username && username != ''? username: null );
  },

  isUserLoggedIn: function () {
    return !!username && username.constructor == String;
  },
  
  updateUsername: function ( newUsername ) {
    username = newUsername;
  },
  
  isUserRequired: function( xhr ) {
    if( xhr.responseText.indexOf( 'loginRequired' ) > -1
     || xhr.responseText.indexOf( 'noUser' ) > -1 
     || xhr.transport.status === 0 ) { // This appears sometimes when the status should be 302
      document.fire( exposure.CONST.LOGIN_REQUIRED );
      return true;
    }
    return false;
  }
  
}


// EOF


_NAMESPACE_( 'exposure.common' );

/**
 * AjaxRequest class.
 */
exposure.common.AjaxRequest = Class.create({
  initialize: function ( method, isJson, action, params, successCallback, errorCallback ) {
    this.method = method;
    this.action = action;
    this.postBody = null;
    if(isJson) {
      this.postBody = params.json;
      this.contentType = 'application/json';      
    } else {
      this.params = ( typeof params == 'object'? Object.toQueryString( params ): params );
      this.params += '&token=' + ajaxToken;      
      this.contentType = 'application/x-www-form-urlencoded';
    }
    this.successCallback = successCallback;
    this.errorCallback = errorCallback;
    // Make a request
    this.send( method );
  },
  
  /**
   * Sends the AJAX request.
   */
  send: function () {
    this.action = exposure.CONST.PATH + this.action;    
    // Use prototype's Ajax class to do the actual request
    
    (new Ajax.Request( this.action, {
      method: this.method,
      parameters: this.params,
      postBody: this.postBody,
      evalJS: false,
      evalJSON: false, 
      onSuccess: this.handleResponse.bind( this ),
      onException: this.handleError.bind( this ), // xhr error
      onFailure: this.handleError.bind( this ),    // HTTP failure status (i.e. non-2xx)
      on302: function ( xhr ) { // 302 Redirect        
        location.href = exposure.CONST.PATH + 'login.jsp';
      },
      contentType: this.contentType
    }));
  },
  
  /**
   * Receives the AJAX response and checks that the response is valid and the
   * user was logged in.
   */
  handleResponse: function  (xhr ) {
    // If login has expired
    if ( exposure.common.Account.isUserRequired( xhr ) ) {
      // Account sent an event some login handler should take care of
      //DEBUG( 'user required' );
      return;
    }
    // Evaluate the JSON response
    var response = null;
    try {
      var json = xhr.responseText;
      response = json.evalJSON( /*sanitize:*/ true );
    } 
    catch ( e ) {
      throw e;
    }
    this.handleJSONResponse( response );
  },
  
  handleJSONResponse: function ( response ) {
    if ( response.status == 'error' ) {
      if (this.errorCallback) {      
        this.errorCallback(response);
      } else {
        var msg = 'Error';
        try {
          msg += ': ' + response.msg;
        } catch (e) {
        }
        //DEBUG(msg);
        // This throw is cathced by prototype and forwarded to onException handler
        throw new Error(msg);
      }
    } else {      
      this.successCallback( response );
    }
  },
  
  /**
   * Called on error. Calls the error callback function reference or if not 
   * provided to the constructor, perform a default action.
   */
  handleError: function ( xhr, exception ) {
    // Try to evaluate json, if the response contains an error code in json
    var response = xhr.transport;
    try {
      var json = response.responseText;
      response = json.evalJSON( /*sanitize:*/ true );
    } 
    catch ( e ) { }
    if ( this.errorCallback ) {
      // TODO: Should this be wrapped in JSON?
      this.errorCallback( response, exception ); 
    } else {
      if ( window.console && console.log ) { 
        console.log( response.status );
        console.log( exception ); 
      }
      throw exception;
    }
  }
});


// Constructors for specific HTTP methods GET and POST  
exposure.common.AjaxGet = (function () {
  var args = ['get', false];
  return Class.create( exposure.common.AjaxRequest,{
    initialize : function ( $super ){
      $super.apply( this, args.concat( $A(arguments).slice( 1 ) ) );
    }
  });
})();
exposure.common.AjaxPost = (function () {
  var args = ['post', false];
  return Class.create( exposure.common.AjaxRequest,{
    initialize : function( $super ){
      $super.apply( this, args.concat( $A(arguments).slice( 1 ) ) ); 
    }
  });
})();
exposure.common.AjaxJsonPost = (function () {
  var args = ['post',true];
  return Class.create( exposure.common.AjaxRequest,{
    initialize : function( $super ){
      $super.apply( this, args.concat( $A(arguments).slice( 1 ) ) ); 
    }
  });
})();


  
// EOF 

_NAMESPACE_( 'exposure.common' );


/**
 * Controller "interface". Classes implementing this interface can register
 * to handle keywords in PageState. They command View instances to handle 
 * UI. 
 */
/*public*/ 
exposure.common.Controller = Class.create({
  initialize: function () {
    var _views = [];
    var _visible = true;

    this.registerView = function ( view ) { 
      _views.push( view ); 
    }
    
    this.notifyViews = function () {
      if ( _visible ) {
        var args = arguments;
        _views.each( function ( view ) {
          if ( view.show ) { view.show.apply( view, args ); }
        });
      }
      else {
        //DEBUG( 'Controller not visible anymore' );
      }
    }
    this.getViews = function () { return _views; }
    this.setVisible = function ( visible ) { _visible = visible; }
  },
  /**
   * Implement in subclass
   */
  show: function ( state ) {
    this.setVisible( true );
  },
  hide: function () { 
    this.setVisible( false );
    this.getViews().invoke( 'hide' );     
  },
  next: function () {},
  previous: function () {},
  goTo: function ( index ) {}
});
  
  
 // EOF
 
_NAMESPACE_( 'exposure.common' );


Object.extend( exposure.common, (function() {
  
  /*** ORIGINAL HEADER ***/
  /*
  Script Name: Javascript Cookie Script
  Author: Public Domain, with some modifications
  Script Source URI: http://techpatterns.com/downloads/javascript_cookies.php
  Version 1.1.0
  Last Update: 24 August 2007
  
  1.1.0 fixes a problem with Get_Cookie that did not correctly handle
  cases where multiple cookies might test as the same, like: site1, site
  
  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
  */
  
  // this fixes an issue with the old method, ambiguous values 
  // with this test document.cookie.indexOf( name + "=" );
  
  // To use, simple do: Get_Cookie('cookie_name'); 
  // replace cookie_name with the real cookie name, '' are required
  function Get_Cookie( check_name ) {
    // first we'll split this cookie up into name/value pairs
    // note: document.cookie only returns name=value, not the other components
    var a_all_cookies = document.cookie.split( ';' );
    var a_temp_cookie = '';
    var cookie_name = '';
    var cookie_value = '';
    var b_cookie_found = false; // set boolean t/f default f
    
    for ( i = 0; i < a_all_cookies.length; i++ )
    {
      // now we'll split apart each name=value pair
      a_temp_cookie = a_all_cookies[i].split( '=' );
      // and trim left/right whitespace while we're at it
      cookie_name = a_temp_cookie[0].replace(/^\s+|\s+$/g, '');
      
      // if the extracted name matches passed check_name
      if ( cookie_name == check_name )
      {
        b_cookie_found = true;
        cookie_value = unescape( a_temp_cookie[1].replace(/^\s+|\s+$/g, '') );
        return cookie_value;
        break;
      }
      a_temp_cookie = null;
      cookie_name = '';
    }
    if ( !b_cookie_found ) 
    {
      return null;
    }
  }
  
  /*
  only the first 2 parameters are required, the cookie name, the cookie
  value. Cookie time is in milliseconds, so the below expires will make the 
  number you pass in the Set_Cookie function call the number of days the cookie
  lasts, if you want it to be hours or minutes, just get rid of 24 and 60.
  
  Generally you don't need to worry about domain, path or secure for most applications
  so unless you need that, leave those parameters blank in the function call.
  */
  function Set_Cookie( name, value, expires, path, domain, secure ) {
    // set time, it's in milliseconds
    var today = new Date();
    today.setTime( today.getTime() );
    // if the expires variable is set, make the correct expires time, the
    // current script below will set it for x number of days, to make it
    // for hours, delete * 24, for minutes, delete * 60 * 24
    if ( expires )
    {
      expires = expires * 1000 * 60 * 60 * 24;
    }
    //alert( 'today ' + today.toGMTString() );// this is for testing purpose only
    var expires_date = new Date( today.getTime() + (expires) );
    //alert('expires ' + expires_date.toGMTString());// this is for testing purposes only
  
    document.cookie = name + "=" +escape( value ) +
      ( ( expires ) ? ";expires=" + expires_date.toGMTString() : "" ) + //expires.toGMTString()
      ( ( path ) ? ";path=" + path : "" ) + 
      ( ( domain ) ? ";domain=" + domain : "" ) +
      ( ( secure ) ? ";secure" : "" );
  }
  
  // this deletes the cookie when called
  function Delete_Cookie( name, path, domain ) {
    if ( Get_Cookie( name ) ) document.cookie = name + "=" +
        ( ( path ) ? ";path=" + path : "") +
        ( ( domain ) ? ";domain=" + domain : "" ) +
        ";expires=Thu, 01-Jan-1970 00:00:01 GMT";
  }

  
  /**
   * Simple wrapper for cookies library.
   * 
   * @author strandel
   * @alias exposure.common.Cookies
   */
  var Cookies = {
		/**
		 * Get a cookie's value with given name.
		 * @param {String} name
		 * @return Cookie's value
		 * @type {String}
		 */
    get: function(name) {
      return Get_Cookie(name);
    },
    
		/**
		 * Creates a new or updates an existing cookie.
		 * 
		 * Only the first 2 parameters are required, the cookie name, the cookie
		 * value. Cookie time is in milliseconds, so the below expires will make the
		 * number you pass in the Set_Cookie function call the number of days the cookie
		 * lasts, if you want it to be hours or minutes, just get rid of 24 and 60.
		 * Generally you don't need to worry about domain, path or secure for most applications
		 * so unless you need that, leave those parameters blank in the function call.
		 * 
		 * @param {String} name
		 * @param {String} value
		 * @param {Number} expires (in days, optional)
		 * @param {Object} path (optional)
		 * @param {Object} domain (optional)
		 * @param {Object} secure (optional)
		 */
    set: function(name, value, expires, path, domain, secure) {
      Set_Cookie(name, value, expires, path, domain, secure);
    },
    
		/**
		 * Deletes the given cookie.
		 * @param {Object} name
		 * @param {Object} path (optional)
		 * @param {Object} domain (optional)
		 */
    remove: function(name, path, domain) {
      Delete_Cookie(name, path, domain);
    }
  }
  
  
  /* Public API */
  return {
    'Cookies': Cookies
  }

})() ); // namespace


// EOF

_NAMESPACE_( 'exposure.common', (function () {


  /**
   * Container for controllers, supports the same methods as a controller.
   */
  /*private*/ 
  var ControllerList = Class.create({
    initialize: function () { this.controllers = []; },
    getControllers: function () { return this.controllers; },
    get: function ( index ) { return this.controllers[ index ] || null; },
    push: function ( controller ) { this.controllers.push( controller ); },
    show: function ( state ) { this.getControllers().invoke( 'show', state ); },
    hide: function () { this.getControllers().invoke( 'hide' ); },
    next: function () { this.getControllers().invoke( 'next' ); },
    previous: function () { this.getControllers().invoke( 'previous' ); },
    goTo: function ( index ) { this.getControllers().invoke( 'goTo', index ); }
  });
  

  /*private*/
  var PageStateImpl = {
    initialize: function () {
      // Private variables (in closure)
      this.controllers = { }; // Handlers for keywords in the URI
      this.settingState = false; // "Semaphore" 
      this.state = null; // Current state
  
      // Browser state manager
  		this.StateManager = EXANIMO.managers.StateManager;
  		this.StateManager.onstatechange = function ( e ) {
        // If setting state (this.setState()), ignore events from StateManager
        if ( !this.settingState ) {
          this.handleHistoryEvent( e.id );
        }
  		}.bind(this);
    },
    
    getControllers: function ( name ) { 
      return this.controllers[ name ]; 
    },

    // Register a controller for a keyword, '*' denotes a global controller
    setController: function ( name, controller ) { 
      if( !this.controllers[ name ] )
        this.controllers[ name ] = new ControllerList();
      this.controllers[ name ].push( controller );
    },

    getState: function () { 
      return this.state; 
    },

    /**
     * Change the current state. Delegate new state to all current and 
     * global controllers.
     */
    setState: function ( state ) { 
      if ( !state instanceof exposure.common.State ) {
        throw new Error( 'Invalid state' );
      }
      // If state has not changed, do nothing
      if ( this.state && this.state.equals( state ) ) {
        //DEBUG( 'setState called, but new state equals to current one' );
        return;
      }
      this.settingState = true; // Reserve semaphore
      var newControllers = this.getControllersForState( state );
      if ( !newControllers ) {
        //DEBUG( 'No controllers for state: ' + state.toURI()  + ', using default uri');
        // My collection for logged in users and public latest for visitors
        var uri = window.username? 
            exposure.CONST.DEFAULT_LOGGED_IN_HASH:
            exposure.CONST.DEFAULT_HASH;
        this.setStateFromUri( uri );
        return;
      }
      try {
        // Within 'try' since there might not yet be any controllers
        // Check if keyword changed
        if ( state.getKeyword() != this.state.getKeyword() ) {
          this.getCurrentControllers().hide();
        }
      } catch ( e ) {}
      this.StateManager.setState( state.toURI() );
      this.state = state; 
      // Notify controllers registered for current keyword
      newControllers.show( state );
      // Notify global controllers 
      if ( this.getControllers( '*' ) ) {
        this.getControllers( '*' ).show( state );
      }
      this.settingState = false; // Release semaphore
    },
    
  	/**
  	 * Initialize the StateManager. Done in a separate method so that all needed
  	 * parts of the system can be initialized before this method is called.
  	 */
    initStateManager: function() {
      this.StateManager.initialize();
      var location = this.StateManager.getState();
      if ( location == 'defaultState' )
        this.handleHistoryEvent(location);
    },
    
    setStateFromUri: function ( uri ) {
      this.handleHistoryEvent( uri );
    },
  	
  	/**
  	 * Handler for changes in the hash of the page. Delegates the changes to 
  	 * setState method. 
  	 */
  	handleHistoryEvent: function ( uri ) {
      var state = this.getStateForUri( uri );
      this.setState( state );
    },
    
    getStateForUri: function ( uri ) {
      var prefix;
      var keyword;
      var params = [];
      if ( uri ) {
        var tokens = uri.split( '/' );
        // first token is empty as uri should begin with '/'
        tokens.shift(); 
        // Loop from end to beginning, uri = /{prefix}/{keyword}/{params}
        for ( var i = tokens.length-1; i>=0; i-- ) {
          var token = tokens[ i ];
          if ( !keyword && this.getControllers( token ) ) {
            // First keyword from the end of the uri decides the controller
            keyword = token;
          }
          else if ( keyword ) {
            if ( !prefix ) { prefix = ''; }
            // Add to beginning of prefix
            prefix = '/' + token + prefix;
          }
          else {
            // Add to beginning of params
            params.splice( 0, 0, token );
          }        
        }
      }
      return new exposure.common.State({
        prefix: prefix,
        keyword: keyword,
        params: params
      });
    },
    
    /**
     * Get the uri of the "prefix state", i.e. the rightmost state included in
     * the url's hash part. 
     */
    getPrefixUrl: function () {
      var prefix = this.state.getPrefix();
      var state = this.getStateForUri( prefix );
      // Construct state without the prefix of the "prefix state"
      var prefixState = new exposure.common.State({
        keyword: state.getKeyword(),
        params: state.getParams()
      });
      return prefixState.toURI();
    },
  
    /**
     * 
     */
    getCurrentState: function() {
      return this.state;
    },
  
    /**
     * Returns the controller for the current keyword.
     */
    getCurrentControllers: function () {
      return this.getControllersForState( this.getState() );
    },
    
    getControllersForState: function ( state ) {
      if ( state === null ) { return null; }
      var controllers = this.getControllers( state.getKeyword() );
      return controllers;
    },
    
    /**
     * Delegates notification to the current controllers.
     */
    next: function () {
      this.getCurrentControllers().next();
    },
    
    /**
     * Delegates notification to the current controllers.
     */
    previous: function () {	  
      this.getCurrentControllers().previous();
    },
    
    /**
     * Returns to upper level using the prefix entry in the current state.
     */
    up: function () {
      var newState = this.state.getPrefix();
      if ( newState ) {
        this.setStateFromUri( newState );
      }
    },
    
    /**
     * Delegates command to move to the index'th item to current controllers. 
     */
    goTo: function ( index ) {
      this.getCurrentControllers().goTo( index );
    }
  
  };
   

  // Public API
  return {
    'PageStateImpl': PageStateImpl
  };
  
  
})() ); // namespace


/*public*/
exposure.common.PageState = {
  initialize: function () {
    if ( !this.impl ) {
      // this.impl is public, but could be made private with var impl and a getter
      this.impl = exposure.common.PageStateImpl;
      this.reset();
    }
  },
  
  reset: function () {
    this.impl.initialize();
  },

  initStateManager: function () {
    this.impl.initStateManager();  
  },
  
  getPrefixState: function() {
    return this.impl.getStateForUri( this.impl.getPrefixUrl() );
  },

  getCurrentControllers: function () {
    return this.impl.getCurrentControllers();
  },

  next: function () {
    this.impl.next();
  },

  previous: function () {
    this.impl.previous();
  },

  up: function () {
    this.impl.up();
  },

  goTo: function ( index ) {
    this.impl.goTo( index );
  },

  setController: function ( name, controller ) {
    this.impl.setController( name, controller );
  },
  
  getState: function () {
    return this.impl.getState();
  },

  setState: function ( state ) {
    this.impl.setState( state );            
  },
  
  getCurrentState: function () {
    return this.impl.getCurrentState();
  },
  
  setStateFromUri: function( uri ) {
    this.impl.setStateFromUri( uri );
  }, 

  getStateForUri: function( uri ) {
    return this.impl.getStateForUri( uri );
  }, 
  
  getControllersForState: function( state ) {
    var controllersHolder = this.impl.getControllersForState( state ); 
    if(! controllersHolder ) {
      return null;
    }
    return this.impl.getControllersForState( state ).controllers;
  } 

};


// EOF

_NAMESPACE_( 'exposure.common' );


/*public*/
exposure.common.State = Class.create({
  /**
   * State constructor.
   * params = {
   *   prefix: str,
   *   keyword: str, 
   *   params: array
   * }
   */
  initialize: function ( params ) {
    if ( !params instanceof Object ) {
      throw new Error('params is not an object');
    }
    // Private variables (in closure)
    var _prefix = params[ 'prefix' ];
    var _keyword = params[ 'keyword' ];
    var _params = params[ 'params' ] || [];

    // Priviledged functions (access to private variables)
    this.getPrefix = function () { return _prefix; }
    this.getKeyword = function () { return _keyword; }
    this.getParams = function ( index ) { 
      if( !!_params && exposure.common.isInt(index) ) {
        return _params[ index ];
      }
      else {
        return _params;
      }         
    }
    this.setParam = function ( index, value ) { 
      if ( value ) { 
        _params[ index ] = value;
      }
      else {
        // remove one element at index
        _params.splice( index, 1 ); 
      }
    }
  },
   
  /**
   * Turn the State to a string presentation that can be attached to 
   * location.hash.
   */
  toURI: function () {
    var uri = '';
    if ( this.getPrefix() !== undefined ) { uri += this.getPrefix(); }
    if ( this.getKeyword() !== undefined ) { uri += '/' + this.getKeyword(); }
    var pars = this.getParams();
    if ( pars !== undefined && pars.length > 0 ) { 
      uri += '/' + pars.join( '/' ); 
    }
    return uri;
  },
  
  /**
   * Clone this state as a simpler object that can be used to instantiate a 
   * clone of this state. 
   */
  clone: function () {
    var pars = this.getParams();
    return new exposure.common.State({
      'prefix': this.getPrefix(),
      'keyword': this.getKeyword(),
      'params': pars? pars.clone(): pars });
  },
  
  equals: function ( state ) {
    return state &&
           this.getPrefix() == state.getPrefix() && 
           this.getKeyword() == state.getKeyword() && 
           this.paramEquals( state.getParams() );
  },
  
  paramEquals: function ( params ) {
    var len1 = this.getParams().length;
    var len2 = params.length;
    // Check if the param arrays are equal or if one or the other has 0
    // as the last item, denoting page 1, which is default otherwise as well    
    return ( exposure.common.arrayEquals( this.getParams(), params )
      || len1 == len2-1 && exposure.common.toInt( params[ len1 ] ) === 0
      || len2 == len1-1 && exposure.common.toInt( this.getParams( len2 ) ) === 0 );
  },
  
  getConfigMap: function () {
    return exposure.CONST.KEYWORD_TO_COLL_ID_MAP[this.getKeyword()];
  }
  
});


// EOF

/* 
var DEBUG = function () {
  if ( window.console && console.log ) {
    console.log( $A( arguments ).join( ' ' ) );
  }
}
//*/

// TODO Add Util to namespace
_NAMESPACE_( 'exposure.common' );


Object.extend( exposure.common, {

  validateParams: function ( params, itemsToValidate ) {
    if ( !params ) { throw new Error( 'params not provided' ); }
    itemsToValidate.each( function ( item ) {
      if( !params[item] && !exposure.common.isInt( params[item] ) ) { throw new Error( item + ' not provided' ); }
    });   
  },
  
  isInt: function ( x ) {
    return x == +x;
  },
  
  toInt: function ( x ) {
    return +x;
  },
  
  arrayEquals: function ( a, b ) {
    if ( a.length != b.length ) { return false; }
    var i;
    for ( i = 0; i < a.length; i++ ) {
      if ( a[ i ] != b[ i ] ) { return false; }
    }
    return true;
  },
  
  /**
   * Utility function to fade out one div and fade in another.
   */
  fadeDivs: function (params) {
		var options = Object.extend({
			outDuration: 0.1,
			inDuration: 0.2
		}, params || {} );
		if ( !options.outDiv || !options.inDiv ) {
			throw "Missing outDiv or inDiv";
		}
		new Effect.Fade( options.outDiv, {
			duration: options.outDuration, 
			afterFinish: function( effect ) {
				new Effect.Appear( options.inDiv, { duration: options.inDuration } );
			}
		});		
  },
  
  F: function () {},
  
  getSignInTargetUrl: function () {    
    var path = location.pathname;
    if ( path.indexOf( '/exposure' ) === 0 ) {
      path = path.substr( '/exposure'.length );
    }
    var url = encodeURIComponent( path + location.hash );
    url = '?targetUrl=' + url;
    return url;
  },

  isPublic: function ( obj ) {
    return obj && Object.isFunction( obj.isPublic ) && obj.isPublic();
  },
  
  isEmailSyntaxValid: function (email) {
    var regExp = /[\w\.-]+@([a-zA-Z0-9-]+\.)+[a-zA-Z0-9]{2,6}$/;
    return (email != null && regExp.match(email));
  }

});  


// EOF 


_NAMESPACE_( 'exposure' );


/**
 * Behavior class. Registers listeners to react to browser events. Behaviors 
 * can be added to Views.
 * @constructor
 * @param {Element} div
 * @type Object
 */
exposure.Behavior = Class.create({
  
  initialize: function ( div ) {
    if( !div ) {
      throw new Error( 'No div' );
    }
    // Private variables (in closure)
    var _div = $( div );
    this.getDiv = function () { return _div; };
    this.disabled = false;
    if( !this.init ) { throw new Error( 'No init() function' ); }
    this.init();
  },
  
  /**
   * Start listening to DOM event type of event and call handler when captured.
   * 
   * E.g. this.on( 'click', this.onclickHandler );
   * 
   * @param {String} type
   * @param {Function} handler
   * @param {Boolean} doBind
   * @member exposure.Behavior
   */
  on: function ( type, handler, doBind ) {				
    doBind = doBind || true; // defaults to true
    if ( doBind ) {
    	handler = handler.bind( this );
    }
    Event.observe( this.getDiv(), type, function ( e ) {
      if ( this.disabled ) { return; }
      handler( e );
    }.bind( this ) );
  },
  
  /**
   * Start listening to specific type of event and on capture, delegate to 
   * specific handler based on CSS selector.
   * 
   * E.g. this.delegate( 'click', {
   *   '.next': this.nextHandler,
   *   '.previous': this.previousHandler
   * }
   * 
   * @param {String} type
   * @param {Object} rules
   * @param {Boolean} doBind
   * @alias exposure.Behavior.prototype.delegate 
   */
  delegate: function( type, rules, doBind ) {
    doBind = doBind || true; // defaults to true
    if ( doBind ) {
		  for ( var selector in rules ) {
		    rules[ selector ] = rules[ selector ].bindAsEventListener( this );
		  }
    }
		this.getDiv().observe( type, function ( e ) {
		  if ( this.disabled ) { return; }
	    var element = Event.element(e);
		  for ( var selector in rules ) {
  		  if ( element.match( selector ) ) {
  		  	return rules[ selector ].apply( this, $A( arguments ) );
  		  }
		  }
    }.bind( this ) );
	},
	
	enable: function () {
	  this.disabled = false;
	},

	disable: function () {
	  this.disabled = true;
	}
	
});


// EOF


_NAMESPACE_( 'exposure.view' );
  
  
/*public*/
exposure.view.ElementTemplates = {

  /**
   * Cache for already retrieved nodes.
   */
  nodeCache: {},
  
  /**
   * Retrieves node from cache or DOM and returns an extended clone of the node.
   */
  cloneNode: function ( name ) {
    var node = this.nodeCache[ name ];
    if ( !node ) {
      var node =  $$( '#templates .' + name )[ 0 ];
      if ( !node ) { throw new Error( 'Can\'t find template: ' + name ); }
      this.nodeCache[ name ] = node;
    }
    // Make a deep clone and extend template node
    return $( node.cloneNode( true ) );
  }
};
  

// EOF


_NAMESPACE_( 'exposure' );


/**
 * Container for view elements, supports the same methods as a ViewElement.
 * View class.
 * A collection and controller of ViewElement instances.
 * @type {exposure.View} 
 */
exposure.View = Class.create({  

  initialize: function ( div ) {
    // Private variables (in closure)
    var _div = $( div );    
    var _elements = []; // List of controlled ViewElements
    var _behaviors = []; // List of behaviors attached to View
    _div.hide();

    // Priviledged functions (access to private variables)
    this.getDiv = function () { return _div; }
    this.getElements = function () { return _elements; }
    this.addElement = function ( element ) { _elements.push( element ); }
    this.getBehaviors = function () { return _behaviors; }
    this.addBehavior = function ( behavior ) { _behaviors.push( behavior ); }
  },
  /**
   * To be implemented by the subclasses.
   */
  getItems: function ( obj ) {
    this.showItems( obj );
  },
  initElements: function () {},
  initBehaviors: function () {},
  /**
   * Lazy initialization of the views and behaviors to save processing time 
   * when instantiating the View class.
   */
  init: function () { 
    this.init = function () {};
    this.initElements();
    this.initBehaviors();
  },
  /**
   * Delegate the object provided by getItems to all registered elements.
   */
  show: function () {
    this.getDiv().show();        
    this.init();
    this.getBehaviors().invoke( 'enable' );
    var args = $A( arguments );
    try {
      // Clear possible existing contents
      this.getElements().invoke( 'hide' );
      this.getItems.apply( this, args );
    } 
    catch ( e ) {
      throw e;
    }
  },
  
  showItems: function ( obj ) {
    this.getElements().invoke( 'show', obj );
  },
  
  /**
   * Delegate the hide command to all registered elements.
   */
  hide: function ( obj ) {
    this.getElements().invoke( 'hide', obj );
    if ( !obj ) {
      this.getDiv().hide();
      this.getBehaviors().invoke( 'disable' );
    }
  }  
});


// EOF


_NAMESPACE_( 'exposure' );


/**
 * ViewElement class.
 * Abstract UI element class. Controls the contents of one DOM element, can add 
 * and remove DOM elements and modify existing. Gets an object to base the DOM
 * contents on. 
 */
/*public*/
exposure.ViewElement = Class.create({
  
  initialize: function ( div ) {
    this.div = $( div );
    this.getDiv = function () { return this.div; }
  },
  
  show: function ( obj ) {},
  
  hide: function () {
    // Clear the element
    this.getDiv().update();
  },
  
  invoke: function () {}

});


// EOF


_NAMESPACE_( 'exposure.CONST', {
  
  AX_ARCHIVE_ACTION_URL: 'ajax/ArchiveService.action'

});


_NAMESPACE_( 'exposure.data', (function () {

  /*private*/
  var AjaxCreatePackageAction = Class.create({
    initialize: function( params ) {
      var action = exposure.CONST.AX_ARCHIVE_ACTION_URL;
      Object.extend( params, {
        createPackage: null
      });
      new exposure.common.AjaxPost( 
          action, params, params.callback, params.errorCallback );
    }
  });

  /*private*/
  var AjaxGetCountersAction = Class.create({
    initialize: function( params ) {
      var action = exposure.CONST.AX_ARCHIVE_ACTION_URL;
      Object.extend( params, {
        getCounters: null
      });
      new exposure.common.AjaxGet( 
          action, params, params.callback, params.errorCallback );
    }
  });


  /*public*/
  var ArchiveService = {

    getCounters: function( params ) {
      exposure.common.validateParams( params, [ 'callback' ] );
      new AjaxGetCountersAction( params );
    },
    
    createPackage: function ( params ) {
      exposure.common.validateParams( params, [ 'all', 'callback' ] );
      new AjaxCreatePackageAction( params );
    },
    
    uploadFile: function ( params ) {
      exposure.common.validateParams( params, [ 'fileInputElem', 'callback' ] );
      // Not implemented yet, currently done directly in home.manage.UploadBehavior
    }
    
  };

  
  /* Public API */
  return {
    'ArchiveService': ArchiveService
  }  
  

})() ); // namespace


// EOF 


_NAMESPACE_( 'exposure.CONST', {

  FRAGMENT_SIZE: 200
});


_NAMESPACE_( 'exposure.data', (function () {


  /*private*/ 
  function itemListSlicer( itemList, params ) {
    if ( !itemList || itemList.length === 0 ) {
      return [];
    }
    // The requested id range within a fragment
    var begin = params.offset % exposure.CONST.FRAGMENT_SIZE;
    var end = begin + params.count;
    return itemList.slice( begin, end )
  }
  

  /*private*/ 
  function getFragmentOffset( offset ) {
    return Math.floor( offset / exposure.CONST.FRAGMENT_SIZE ) *
        exposure.CONST.FRAGMENT_SIZE;   
  }


  /*private*/ 
  var AjaxItemSetGetter = Class.create({
        
    initialize: function ( collection, params ) {
        this.collection = collection;
        this.params = params;
        this.fragmentOffset = getFragmentOffset( params.offset )

        var axParams = { 
            offset: this.fragmentOffset,
            count: exposure.CONST.FRAGMENT_SIZE };
        var cb = this.getItemsCallback.bind( this );

        new exposure.common.AjaxGet( params.collectionServiceUrl, axParams, cb );
    },
    
    getItemsCallback: function ( response ) {
      var itemList = response.imgList;
      if ( response.title ) { this.collection.setTitle( response.title ); }
      if ( response.description ) { this.collection.setDescription( response.description ); }
      if ( response.count ) { this.collection.setCount( response.count ); }
      // Store itemList
      if ( itemList.length > 0 ) {
        this.collection.addItemList( this.fragmentOffset, itemList );
        var items = this.params.skipItems? 
                        undefined: 
                        itemListSlicer( itemList, this.params );
        this.params.callback( items );
      }
      else {
        this.params.callback( [] );
      }
    }
  });
  
  
  /*private*/
  var AjaxMultiPartItemSetGetter = Class.create({
  
    initialize: function ( collection, params ) {
      this.collection = collection;
      this.params = params;
//DEBUG( 'MultiPartColl params: ' + Object.toJSON( this.params) );
      this.getFirstPart();
    },
    
    getFirstPart: function() {
      var fragmentOffset = getFragmentOffset( this.params.offset );
      this.params1 = {
        offset: this.params.offset,
        count: exposure.CONST.FRAGMENT_SIZE - ( this.params.offset % exposure.CONST.FRAGMENT_SIZE ),
        collectionServiceUrl: this.params.collectionServiceUrl,
        callback: this.firstPartCallback.bind( this )
      };
      var itemList = this.collection.getItemList( fragmentOffset );

      if ( itemList ) {
        this.firstPart = itemListSlicer( itemList, this.params1 );
//DEBUG( '1st part in cache, count: ' + this.firstPart.length )
        this.getSecondPart();
      }
      else {
//DEBUG( '1st part ajax params: ' + Object.toJSON( this.params1 ) );        
        new AjaxItemSetGetter( this.collection, this.params1 );
      }
    },
    
    getSecondPart: function () {
      var fragmentOffset = getFragmentOffset( this.params.offset ) + exposure.CONST.FRAGMENT_SIZE;
      var params = {
        offset: fragmentOffset,
        count: this.params.count - this.params1.count, 
        collectionServiceUrl: this.params1.collectionServiceUrl,
        callback: this.secondPartCallback.bind( this )
      };
      var itemList = this.collection.getItemList( fragmentOffset );
      if ( itemList ) {        
        this.secondPart = itemListSlicer( itemList, params );
//DEBUG( '2nd part in cache, count: ' + this.secondPart.length )
        this.deliverItems();
      }
      else {
//DEBUG( '2nd part ajax params: ' + Object.toJSON( params ) );        
        new AjaxItemSetGetter( this.collection, params );
      }
    },
    
    deliverItems: function () {
      var items = this.firstPart.concat( this.secondPart );
      this.params.callback( items );
    },
    
    firstPartCallback: function( items ) {
      this.firstPart = items;      
//DEBUG( '1st part ajax , count: ' + this.firstPart.length )
      this.getSecondPart();
    },
    
    secondPartCallback: function( items ) {
      this.secondPart = items;      
//DEBUG( '2nd part ajax , count: ' + this.secondPart.length )
      this.deliverItems();
    }
     
  });
  
  
  
  /**
   * Collection class.
   * Data access object to get metadata of and image info for a collection.
   */
  /*public*/ 
  var Collection = Class.create({
  
    initialize: function ( params ) {
      exposure.common.validateParams( params, ['callback', 'collectionServiceUrl' ] );
      // Private variables
      var _title = null;
      var _description = null;
      var _count = null; // Number of images in collection
      var _itemLists = {}; // Ids in the collection, organized in fragments
      var _collectionServiceUrl = params.collectionServiceUrl;
  
      // Priviledged getters and setter to access private vars
      this.getTitle = function() { return _title; }
      this.getDescription = function() { return _description; }
      this.getCount = function () { return _count; }
      this.getCollectionServiceUrl = function () { return _collectionServiceUrl; }
    
      this.setTitle = function( title ) { _title = title; }
      this.setDescription = function( description ) { _description = description; }
      this.setCount = function ( count ) { _count = count; }
      this.getItemList = function ( offset ) { return _itemLists[ offset ]; }
      this.getItemLists = function () { return _itemLists; }
      this.addItemList = function ( offset, itemList ) { 
        _itemLists[ offset ] = itemList.clone(); 
      }
      params.count = exposure.CONST.FRAGMENT_SIZE;
      // Set this collection as the parameter in the callback
      params.callback = params.callback.curry( this );
      params.skipItems = true;
      this.getItems( params );
    },
    
    hasRightsToEdit: function () {
      // TODO: implement this
      return false;
    },
    
    refresh: function ( callback ) {
      var lists = $H( this.getItemLists() );
      lists.each( function ( item ) {
        var params = {
          count: item.value.length,
          offset: item.key,
          callback: callback || exposure.common.F, 
          collectionServiceUrl: this.getCollectionServiceUrl()
        };
        new AjaxItemSetGetter( this, params );
      }.bind( this ) );
    },
    
    /**
     * params = {
     *   count: int,
     *   offset: int,
     *   callback: function
     * }
     */
    getItems: function ( params ) {
      params = Object.extend({
        count: 50,
        offset: 0
      }, params || {} );
      exposure.common.validateParams( params, [ 'callback' ] );
      if ( params.offset < 0 ) { throw new Error( 'negative offset' ); }
      if ( params.count < 0 ) { throw new Error( 'negative count' ); }
      // Zero items requested
      if ( params.count === 0 ) { return params.callback.defer( [] ); }

      var fragmentOffset = getFragmentOffset( params.offset );
      var itemList = this.getItemList( fragmentOffset );
      params.collectionServiceUrl = this.getCollectionServiceUrl();
      // Check if spans two fragments
      if ( params.offset + params.count > 
          fragmentOffset + exposure.CONST.FRAGMENT_SIZE ) {
        new AjaxMultiPartItemSetGetter( this, params );
      }
      // Only uses one fragment, check if already cached
      else if ( itemList ) {
        // Delay call to make it asynchronous, to simulate AJAX response
        params.callback.defer( itemListSlicer( itemList, params ) );
      }
      else {
        new AjaxItemSetGetter( this, params );
      }
    }
  });
  
  // Public API
  return {
    'Collection': Collection
  }
  

})() ); // namespace


// EOF 





_NAMESPACE_( 'exposure.data', (function () {


  /*public*/
  var CollectionService = {
    
    getCollection: function( params ) {
      return new exposure.data.Collection( params );
    }

    
  };

  
  /* Public API */
  return {
    'CollectionService': CollectionService
  }  
  

})() ); // namespace


// EOF 


_NAMESPACE_( 'exposure.CONST', {
  
  REST_CONTACTS_URL: 'rest/w/users/{userid}/contacts',
	REST_CONTACTS_WITH_IMAGES_URL: 'rest/w/users/{userid}/contactswithimages'

});


_NAMESPACE_( 'exposure.data', (function () {

  /*private*/
  var AjaxGetContactsAction = Class.create({
    initialize: function( params, url ) {
      var action = url;
      action = action.sub( '{userid}', userid );
      new exposure.common.AjaxGet( action, {}, params.callback );
    }
  });


  /*public*/
  var ContactsService = {

    getContacts: function( params ) {
      exposure.common.validateParams( params, [ 'callback' ] );
      new AjaxGetContactsAction( params, exposure.CONST.REST_CONTACTS_URL );
    },
		
		getContactsWithImages: function( params ) {
      exposure.common.validateParams( params, [ 'callback' ] );
      new AjaxGetContactsAction( params, exposure.CONST.REST_CONTACTS_WITH_IMAGES_URL );
    }
    
  };

  
  /* Public API */
  return {
    'ContactsService': ContactsService
  }  
  

})() ); // namespace


// EOF 


_NAMESPACE_( 'exposure.CONST', {
  
  DEFAULT_LOGGED_IN_COLLECTION_ID: 'mylatest',
  DEFAULT_VISITOR_COLLECTION_ID: 'latest'

});



_NAMESPACE_( 'exposure.data' );

/**
 * DataHandler class. 
 * Provides access to collection and image data.
 */
/*public*/
exposure.data.DataHandler = {

  /**
   * Lazy constructor, calling can be delayed until it is actually needed.
   */
  init: function () {
    // Make sure init is called only once
    this.init = function () {};
    
    // Private variables
    var _collection = null; // cache the current collection id
    var _image = null; // cache the current image id
    var _contacts = null; // contacts of the current user
    var _operators = null; // operators currently supported by IMEX

    // Priviledged functions (access to private variables)
    this.getCurrentCollection = function () { return _collection; }
    this.setCurrentCollection = function ( collection ) { _collection = collection; }
    this.getCurrentImage = function () { return _image; }
    this.setCurrentImage = function ( image ) { _image = image; }
    this.getContacts = function () { return _contacts; }
    this.setContacts = function ( contacts ) { _contacts = contacts; }
    this.getOperators = function () { return _operators; }
    this.setOperators = function ( operators ) { _operators = operators; }
  },
  
  /**
   * Retrieves a collection from the server.
   * params = {
   *   collectionServiceUrl: url,
   *   callback: function
   * }
   */
  getCollection: function ( params ) {
    //TODO: What if collection not found?
    exposure.common.validateParams( params, [ 'collectionServiceUrl', 'callback' ] );
    this.init(); // Lazy initialization
    //TODO: Store a timestamp when the collection was retrieved and if too much time has passed, get it again
    if ( this.getCurrentCollection() &&
         this.getCurrentCollection().getCollectionServiceUrl() == params.collectionServiceUrl ) {
      params.callback.defer( this.getCurrentCollection() );
    }
    else {
      this.setCurrentCollection( exposure.data.CollectionService.getCollection( params ) );
    }
  },
  
  /**
   * Retrieves an image from the server.
   * params = {
   *   id: int,
   *   callback: function
   * }
   */
  getImage: function ( params ) {
    exposure.common.validateParams( params, [ 'id', 'callback' ] );
    this.init(); 
    if ( this.getCurrentImage() && this.getCurrentImage().getId() == params.id ) {
      params.callback.defer( this.getCurrentImage() );
    }
    else {
      this.setCurrentImage( new exposure.data.Imagex( params ) );
      this.getCurrentImage().loadImageMetaData();
    }
  },
  
  /**
   * Fetches the contacts from server or from cached collection. 
   * Fires an event when the contacs are available.
   */
  fetchContacts: function() {
  this.init();
  if(this.getContacts()) {    
    this.getContactsCallback( this.getContacts() );
    return;
  }
    exposure.data.ContactsService.getContacts({
        callback: this.getContactsCallback.bind( this )
    });    
  },
  
  getContactsCallback: function(contactsJson) {    
    this.setContacts(contactsJson);
    document.fire( 'contacts:contactsReceived', { contacts: contactsJson } );
  },
  
  /**
   * Fetches the operators from server or from cached collection. 
   * Fires an event when the operators are available.
   */
  fetchOperators: function() {
	  this.init();
	  if(this.getOperators()) {    
	    this.getOperatorsCallback( this.getOperators() );
	    return;
	  }
	  exposure.data.OperatorService.getOperators({
	  	callback: this.getOperatorsCallback.bind( this )
	  });    
  },
  
  getOperatorsCallback: function(operatorsJson) {  
    this.setOperators(operatorsJson);
    document.fire( 'operators:operatorsReceived', { operators: operatorsJson } );
  },
  
  /**
   * Get the id of the next image in the collection. Returns the id or
   * null if does not exist.
   * params = {
   *   imgId: int,
   *   collectionServiceUrl: int,
   *   callback: function
   * }
   */
  getNextImageId: function ( params ) {
    exposure.common.validateParams( params, [ 'collectionServiceUrl', 'imgId', 'callback' ] );
    var cb = function ( index ) {
      if ( index == -1 || index == this.getCurrentCollection().getCount()-1) {
        params.callback( null );
        return;
      }
      index += 1; // +1 to get to next
      this.getImageId({ index: index, collectionServiceUrl: params.collectionServiceUrl, callback: params.callback });
    }.bind( this );
    this.getImageIndex({ 
        imgId: params.imgId, 
        collectionServiceUrl: params.collectionServiceUrl, 
        callback: cb
    });
  },
  
  /**
   * Get the id of the previous image in the collection. Returns the id or
   * null if does not exist.
   * params = {
   *   imgId: int,
   *   collectionServiceUrl: int,
   *   callback: function
   * }
   */
  getPreviousImageId: function ( params ) {
    exposure.common.validateParams( params, [ 'collectionServiceUrl', 'imgId', 'callback' ] );
    var cb = function ( index ) {
      if ( index == -1 ) {
        params.callback( null );
        return;
      }
      index -= 1; // -1 to get to previous
      this.getImageId({ index: index, collectionServiceUrl: params.collectionServiceUrl, callback: params.callback });
    }.bind( this );
    this.getImageIndex({ 
        imgId: params.imgId, 
        collectionServiceUrl: params.collectionServiceUrl, 
        callback: cb
    });
  },
  
  /**
   * Get the id if the image at the given index in a given collection.
   * params = {
   *   collectionServiceUrl: int,
   *   index: int,
   *   callback: function
   * }
   */
  getImageId: function ( params ) {
    exposure.common.validateParams( params, [ 'collectionServiceUrl', 'index', 'callback' ] );
    var id = null;
    if ( params.index < 0 ) { 
      params.callback( id ); 
      return;
    }
    var collCb = function ( collection ) {
      if ( params.index == collection.getCount() ) { 
        params.callback( id ); 
        return;
      }
      var fragmentCb = function ( idList ) {
        if ( idList.length > 0 ) {
          id = idList[ 0 ].id;
        }
        params.callback( id );
        return;
      }
      collection.getItems({ offset: params.index, count: 1, callback: fragmentCb });
    };
    this.getCollection({ collectionServiceUrl: params.collectionServiceUrl, callback: collCb });
  },
  
  /**
   * Get the index of the image in given collection.
   * NOTE!! If the collection is not cached, this will retrieve only the first
   *        fragment. If the collection is already cached, this will search
   *        from all already retrieved fragments, but not retrieve additional
   *        fragments.
   * params = {
   *   imgId: int,
   *   collectionServiceUrl: int,
   *   callback: function
   * }
   */
  getImageIndex: function ( params ) {
    exposure.common.validateParams( params, [ 'collectionServiceUrl', 'imgId', 'callback' ] );
    var cb = function ( collection ) {
      var index = -1;
      // item lists are pairs of offset (key) and list (value)
      $H( collection.getItemLists() ).each( function ( item ) {
        var idList = item.value.pluck( 'id' );
        index = idList.indexOf( exposure.common.toInt( params.imgId ) );
        if ( index != -1 ) {
          index += exposure.common.toInt( item.key ); // add offset of fragment
          throw $break; 
        }
      });
      params.callback( index );
      return;
    }
    this.getCollection({ 
        collectionServiceUrl: params.collectionServiceUrl, 
        callback: cb });
  },
  
  /**
   * Save profile settings, such as username. 
   */
  saveProfile: function ( params ) {
    exposure.common.validateParams( params, [ 'callback' ] );
    var cb = function ( callback, response ) {
      if ( response.error === undefined ) {
        if ( params.username ) {
          exposure.common.Account.updateUsername( params.username );
        }
        if ( params.allowCommentNotifications ) {
          allowCommentNotifications = params.allowCommentNotifications;
        }
      }
      callback( response );
    }.curry( params.callback );
    params.callback = cb;
    new exposure.data.ProfileUpdater( params );
  },
  
  /**
   * 
   * @param {Object} params
   */
  checkUsername: function ( params ) {
    exposure.common.validateParams( params, [ 'username', 'callback' ] );
    new exposure.data.UsernameChecker( params );
  },
  
  /**
   * Get image counts for download action.
   */
  getDownloadCounters: function ( params ) {
    exposure.common.validateParams( params, [ 'callback' ] );
    exposure.data.ArchiveService.getCounters( params );
  },
  
  /**
   * Create the downloadable package.
   */
  createDownloadPackage: function ( params ) {
    exposure.common.validateParams( params, [ 'all', 'callback' ] );
    exposure.data.ArchiveService.createPackage( params );
  },
  
  /**
   * Upload a file. 
   */
  uploadFile: function ( params ) {
    exposure.common.validateParams( params, [ 'fileInputElem', 'callback' ] );
    exposure.data.ArchiveService.uploadFile( params );
  },
  
  /**
   * Sends an email invitation to given email address.
   */
  sendInvite: function ( params) {
    exposure.common.validateParams( params, [ 'email', 'message' ] );
    exposure.data.EmailService.sendInvite( params );
  },

  /**
   * Sends feedback to default mail address.
   */
  sendFeedback: function ( params) {
    exposure.common.validateParams( params, [ 'message' ] );
    exposure.data.EmailService.sendFeedback( params );
  },

  /**
   * Sends a report of inapproriate content 
   */
  sendReport: function ( params) {
    exposure.common.validateParams( params, [ 'message', 'url' ] );
    exposure.data.EmailService.sendReport( params );
  },
  
  /**
   * Requests a new password to be sent to user's email
   */
  requestPasswordReset: function ( params) {
    exposure.common.validateParams( params, [ 'username' ] );
    exposure.data.EmailService.requestPasswordReset( params );
  },  

  /**
  * Share or unshare the current image from the given contact.
  */
  toggleCurrentImageSharingTo: function ( contactUserId ) {
    var imageid = this.getCurrentImage().getId();
    var shareOrUnshareFunction = exposure.data.SharingService.shareImage;
    if(this.getCurrentImage().isSharedWith(contactUserId)) {
      shareOrUnshareFunction = exposure.data.SharingService.unshareImage;
    }
    shareOrUnshareFunction({
      'callback': this.sharingToggledCallback.bind( this ), 
      'imageid': imageid, 
      'contactUserIds': new Array(contactUserId)
    });
  },
  
  sharingToggledCallback: function( sharedToContacts ) {
    this.getCurrentImage().setSharingDestinations(sharedToContacts);
    this.getCurrentImage().setIsPublic( false );
    this.fetchContacts();
    document.fire( 'image:publicStatusChanged', { obj: this.getCurrentImage() });
  },  

  shareCurrentImageWithAllContacts: function() {
    exposure.data.SharingService.shareImage({
      'callback': this.sharingToggledCallback.bind( this ), 
      'imageid': this.getCurrentImage().getId(), 
      'contactUserIds': this.concatenateContactUserIds()
    });
  }, 

  concatenateContactUserIds: function( ) {
    return $( this.getContacts() ).collect(function( contact ) {
      return contact.refid;
    }).join('_');
  },  
  
  shareCurrentImageWithNone: function() {
    exposure.data.SharingService.unshareImage({
      'callback': this.sharingToggledCallback.bind( this ), 
      'imageid': this.getCurrentImage().getId(), 
      'contactUserIds': this.concatenateContactUserIds()
    });
  },

  unpublishCurrentImageIfNecessary: function() {
    if ( exposure.common.isPublic(this.getCurrentImage()) ) {
      this.getCurrentImage().updatePublicStatus({ 'isPublic': false });
    }
  }
  
} // DataHandler


// EOF 


_NAMESPACE_('exposure.CONST', {

  AX_FEEDBACK_ACTION_URL: 'ajax/email/Feedback.action',
  AX_INVITE_ACTION_URL: 'ajax/email/InviteFriend.action',
  AX_REPORT_ABUSE_ACTION_URL: 'ajax/email/ReportAbuse.action',
  AX_RESET_PASSWORD_URL: 'ajax/PasswordResetter.action',
  
  REST_SEND_IMAGE_SHARING_MESSAGE_URL: 'rest/w/users/{userToken}/image/{imageId}/sharewithemail'

});


_NAMESPACE_('exposure.data', (function() {

  /*private*/
  var AjaxSendEmailAction = Class.create({
    initialize: function(params) {
      var action = params.action;
      new exposure.common.AjaxPost(action, params, params.callback, params.errorCallback);
    }
  });
  
  
  /*public*/
  var EmailService = {
  
    sendInvite: function(params) {
      exposure.common.validateParams(params, ['email', 'message', 'callback']);
      Object.extend(params, {
        action: exposure.CONST.AX_INVITE_ACTION_URL
      });
      new AjaxSendEmailAction(params);
    },
    
    sendFeedback: function(params) {
      exposure.common.validateParams(params, ['message', 'agreeToPolicy', 'callback']);
      Object.extend(params, {
        action: exposure.CONST.AX_FEEDBACK_ACTION_URL
      });
      new AjaxSendEmailAction(params);
    },
    
    sendReport: function(params) {
      exposure.common.validateParams(params, ['message', 'url', 'callback']);
      Object.extend(params, {
        action: exposure.CONST.AX_REPORT_ABUSE_ACTION_URL
      });
      new AjaxSendEmailAction(params);
    },
    
    requestPasswordReset: function(params) {
      exposure.common.validateParams(params, ['username', 'callback']);
      Object.extend(params, {
        action: exposure.CONST.AX_RESET_PASSWORD_URL
      });
      new AjaxSendEmailAction(params);
    },
    
    sendImageSharingMessage: function(params) {
      exposure.common.validateParams(params, ['sharedImage', 'json', 'callback']);
      var sharedImage = params.sharedImage;
      var action = exposure.CONST.REST_SEND_IMAGE_SHARING_MESSAGE_URL.sub('{imageId}', sharedImage.getId());
      action = action.sub('{userToken}', sharedImage.getOwner().getToken());
      new exposure.common.AjaxJsonPost(action, params, params.callback, params.errorCallback);
    }
    
  };
  
  
  /* Public API */
  return {
    'EmailService': EmailService
  }
  
  
})()); // namespace
// EOF 


_NAMESPACE_( 'exposure.CONST', {

  AX_IMAGE_ACTION_URL: 'ajax/GetImageData.action',
  AX_COMMENT_ACTION_URL: 'ajax/CommentManager.action',
  AX_IMAGE_UPDATE_ACTION_URL: 'ajax/ImageManager.action',
  ORIENTATION: { PORTRAIT: 1, LANDSCAPE: 2, SQUARE: 3 },
  SIZE_TO_FILE_NAME_POSTFIX: { SMALL: '-q.',MEDIUM: '-v.',LARGE: '-l.',BROWSER_THUMB: '-b.',TINY: '-t.',ORIGINAL: '.'}
  
});


_NAMESPACE_( 'exposure.data', (function () {


  /*private*/
  var AjaxImageGetter = Class.create({
    initialize: function ( image, params ) {
      this.image = image;
      this.params = params;
      
      // No image id provided, could be because a deleted image is requested via image.jsp
      if ( !params.id ) {
        this.getImageCallback({ images: [] });
        return;
      }

      var action = exposure.CONST.AX_IMAGE_ACTION_URL;
      var axParams = { 
        imageIds: params.id,
        comments: true
      };
      var cb = this.getImageCallback.bind( this );

      new exposure.common.AjaxGet( action, axParams, cb );
    },
    
    getImageCallback: function ( response ) {
      var img = this.image;      
      var data = response.images[0]; 
      // array is empty and data undefined, if image is missing or deleted.  
      if ( !data ) {
        this.params.callback( null );
        return;
      }

      img.setTitle( data.title );
      var owner = new exposure.data.User(data.owner || null , data.ownerToken || null , data.ownerAvatarUrl || null);
      img.setOwner( owner );
      // img.setOwnerToken( data.ownerToken || null );
      img.setDescription( data.description || null );
      img.setOrientation(data.width, data.height );
      img.setIsPublic( data.isPublic || null );
      img.setIsFavorite( data.favorite || null );
      img.setDate( data.date || null );
      img.setComments( data.comments || [] );
      img.setSharingDestinations( data.sharingDestinations || [] );
      img.setAbsoluteLargeImageUrl( data.absoluteLargeImageUrl || null );
      img.setAbsoluteImagePageUrl( data.absoluteImagePageUrl || null );
      
      this.params.callback( img );
    }
  });
  
  
  /*private*/
  var AjaxCommentAdder = Class.create({
    initialize: function ( image, params ) {
      this.image = image;
      this.params = params;
      var action = exposure.CONST.AX_COMMENT_ACTION_URL;
      Object.extend( params, {
        addComment: null,
        imageId: this.image.getId()
      });
      var cb = this.addCommentCallback.bind( this );
      var errorCb = cb;
      new exposure.common.AjaxPost( action, params, cb, errorCb );
    },
  
    addCommentCallback: function ( response ) {
      if ( response.status === 'ok' ) {
        this.image.setComments( response.comments );
        this.params.callback();
        document.fire( 'image:commentChange', { obj: this.image } );
      }
      else {
        this.params.callback( response.msg );
      }
    }
  });
  
  
  /*private*/
  var AjaxCommentRemover = Class.create({
    initialize: function ( image, params ) {
      this.image = image;
      this.params = params;
      var action = exposure.CONST.AX_COMMENT_ACTION_URL;
      var comment = image.getComments()[ params.commentIndex ];
      Object.extend( params, {
        deleteComment: null,
        imageId: this.image.getId(),
        commentId: comment.id
      });
      var cb = this.removeCommentCallback.bind( this );
      var errorCb = cb;
      new exposure.common.AjaxPost( action, params, cb, errorCb );
    },
  
    removeCommentCallback: function ( response ) {
      var msg;
      if ( response.status === 'error' ) {
        msg = response.msg;
      }
      this.image.setComments( response.comments );
      this.params.callback( msg );
    }
  });
  
  
  /*private*/
  var AjaxImageAction = Class.create({
    initialize: function( image, params, eventName, functionName ) {
      this.image = image;
      this.eventName = eventName;
      this.functionName = functionName;
      var action = exposure.CONST.AX_IMAGE_UPDATE_ACTION_URL;
      Object.extend( params, {
        imageIds: this.image.getId()
      });
      var cb = this.updateStatusCallback.bind( this );
      var errorCb = cb;
      new exposure.common.AjaxPost( action, params, cb, errorCb );
    },
    
    updateStatusCallback: function ( response ) {
      if ( response.status === 'ok' ) {
        if ( this.functionName ) {
          this.image[ this.functionName ]( response.value );
        }
        document.fire( this.eventName, { obj: this.image });
      }
      else {
        //TODO: handle error or just let user try again?
      }
    }
  });
  

  /**
   * Class Imagex.
   * params: {
   *   id: int,
   *   callback: function
   * }
   */
  /*public*/
  var Imagex = Class.create({
    initialize: function ( params ) {
      exposure.common.validateParams( params, [ 'id', 'callback' ] );
      
      var _id = params.id; 
      var _title = null;
      var _description = null;
      var _owner = null;
      var _ownerToken = null;
      var _orientation = null;
      var _isPublic = null;
      var _isFavorite = null; // is the image favorite of the current user
      var _date = null; // the time&date the image was captured or deleted
      var _commentList = null;
      var _sharingDestinations = null;
      var _absoluteLargeImageUrl = null;
      var _absoluteImagePageUrl = null;
      var _initializeParams = params; 
  
      this.getId =          function () { return _id; };
      this.getTitle =       function () { return _title; };
      this.getOwner =       function () { return _owner; };
      this.getOwnerToken =       function () { return _ownerToken; };
      this.getDescription = function () { return _description; };
      this.getOrientation = function () { return _orientation; };
      this.getDate =        function () { return _date; };
      this.isPublic =       function () { return _isPublic; };
      this.isFavorite =     function () { return _isFavorite; };
      this.getComments =    function () { return _commentList; };
      this.getSharingDestinations =    function () { return _sharingDestinations; };
      this.getAbsoluteLargeImageUrl =    function () { return _absoluteLargeImageUrl; };
      this.getAbsoluteImagePageUrl =    function () { return _absoluteImagePageUrl; };

      this.getContacts =    function () { return []; }; // for ListElement compatibility  

      this.getInitializeParams =    function () { return _initializeParams; };
      
      this.setOwner =       function ( owner ) { _owner = owner; };
      this.setDate =        function ( date ) { _date = date; };
  
      this.setOwnerToken =       function ( ownerToken ) { _ownerToken = ownerToken; };
  
      //TODO: These should trigger saving the data
      this.setTitle =       function ( title ) { _title = title; };
      this.setDescription = function ( description ) { _description = description; };
      this.setIsPublic =    function ( isPublic ) { _isPublic = isPublic; };
      this.setIsFavorite =  function ( isFavorite ) { _isFavorite = isFavorite; };
      this.setComments =    function ( commentList ) { _commentList = commentList; };
      this.setSharingDestinations =    function ( sharingDestinations ) { _sharingDestinations = sharingDestinations; };
      this.setAbsoluteLargeImageUrl =    function ( absoluteLargeImageUrl ) { _absoluteLargeImageUrl = absoluteLargeImageUrl; };
      this.setAbsoluteImagePageUrl =    function ( absoluteImagePageUrl ) { _absoluteImagePageUrl = absoluteImagePageUrl; };
      this.setOrientation = function ( w, h ) { 
        var cons = exposure.CONST.ORIENTATION;
        _orientation = h > w? cons.PORTRAIT:
                       h < w? cons.LANDSCAPE: 
                       cons.SQUARE;
      };
            
    },

	loadImageMetaData: function () {
	      new AjaxImageGetter( this, this.getInitializeParams() );
	},
    
    hasRightsToEdit: function () {
      // Check there is owner and it equals to current users
      var screenName = this.getOwner().getScreenName();
      return !! screenName
          && screenName == exposure.common.Account.getUser();
    },
    
    addComment: function ( params ) {
      new AjaxCommentAdder( this, params );
    },
    
    deleteComment: function ( params ) {
      new AjaxCommentRemover( this, params );
    },
    
    updateFavoriteStatus: function ( params ) {
      Object.extend( params, {
        updateFavorite: null
      });
      new AjaxImageAction( this, params, 'image:favoriteStatusChanged', 'setIsFavorite' );
    },
    
    updatePublicStatus: function ( params ) {
      Object.extend( params, {
        updateIsPublic: null
      });
      new AjaxImageAction( this, params, 'image:publicStatusChanged', 'setIsPublic' );      
    },
    
    updateDeleteStatus: function ( params ) {
      Object.extend( params, {
        updateDeleted: null
      });
      new AjaxImageAction( this, params, 'image:deleteStatusChanged' );
    },

    belongsToCurrentUser: function () {
    	return this.getOwner().getScreenName() === username;
    },
    
    isSharedWith: function ( contactUserId ) {
  	  return $( this.getSharingDestinations() ).indexOf( parseInt( contactUserId ) ) >= 0;
    },      
    
    isShared: function ( ) {
      return this.getSharingDestinations().length > 0;
    },      
    
    getAbsoluteImageUrl: function ( size ) {
      var largeSizePostfix = exposure.CONST.SIZE_TO_FILE_NAME_POSTFIX.LARGE;      
      var postfix = exposure.CONST.SIZE_TO_FILE_NAME_POSTFIX[size];
      return this.getAbsoluteLargeImageUrl().sub(largeSizePostfix, postfix);
    }      
    
  });  
  
  /* Public API */
  return {
    'Imagex': Imagex
  };  
  

})() ); // namespace


// EOF 


_NAMESPACE_( 'exposure.CONST', {
  
  REST_ARCHIVE_URL: 'rest/w/users/{userid}/archive'

});


_NAMESPACE_( 'exposure.data', (function () {

  /*private*/
  var AjaxCreatePackageAction = Class.create({
    initialize: function( params ) {
      //TODO: Make separate REST ajax get and post handlers that would do 
      // parameter substitution
      var action = exposure.CONST.REST_ARCHIVE_URL;
      action = action.sub( '{userid}', userid );
      new exposure.common.AjaxPost( action, params, params.callback );
    }
  });

  /*private*/
  var AjaxGetCountersAction = Class.create({
    initialize: function( params ) {
      var action = exposure.CONST.REST_ARCHIVE_URL;
      action = action.sub( '{userid}', userid );
      new exposure.common.AjaxGet( action, {}, params.callback );
    }
  });


  /*public*/
  var ArchiveService = {

    getCounters: function( params ) {
      exposure.common.validateParams( params, [ 'callback' ] );
      new AjaxGetCountersAction( params );
    },
    
    createPackage: function ( params ) {
      exposure.common.validateParams( params, [ 'type' ] );
      new AjaxCreatePackageAction( params );
    }
    
  };

  
  /* Public API */
  return {
    'NewArchiveService': ArchiveService
  }  
  

})() ); // namespace


// EOF 

_NAMESPACE_( 'exposure.data', (function () {

  /*private*/
  var AjaxGetOperatorsAction = Class.create({
    initialize: function( params, url ) {
      new exposure.common.AjaxGet( url, {}, params.callback );
    }
  });

  /*public*/
  var OperatorService = {

    getOperators: function( params ) {
      exposure.common.validateParams( params, [ 'callback' ] );
      new AjaxGetOperatorsAction( params, 'rest/w/public/operators/list' );
    }
    
  };
  
  /* Public API */
  return {
    'OperatorService': OperatorService
  }  
  

})() ); // namespace


// EOF 


_NAMESPACE_( 'exposure.CONST', {
  
  REST_PROFILE_URL: 'rest/w/public/users/{userToken}/profile'

});


_NAMESPACE_( 'exposure.data', (function () {

  /*private*/
  var AjaxGetProfileAction = Class.create({
    initialize: function( params, url ) {
      var action = url;
      action = action.sub( '{userToken}', params.userToken );
      new exposure.common.AjaxGet( action, {}, params.callback );
    }
  });


  /*public*/
  var ProfileService = {

    getProfile: function( params ) {
      exposure.common.validateParams( params, [ 'userToken', 'callback' ] );
      new AjaxGetProfileAction( params, exposure.CONST.REST_PROFILE_URL );
    }
    
  };

  
  /* Public API */
  return {
    'ProfileService': ProfileService
  }  
  

})() ); // namespace


// EOF 


_NAMESPACE_( 'exposure.CONST', {
  
  REST_ACCOUNT_UPDATE_URL: 'rest/w/users/{userid}/account',
  REST_USERNAME_CHECK_URL: 'rest/w/users/{userid}/username/{username}'

});


_NAMESPACE_( 'exposure.data', (function () {

  /*private*/
  var AjaxProfileUpdater = Class.create({
    initialize: function( params ) {
      var action = exposure.CONST.REST_ACCOUNT_UPDATE_URL;
      action = action.sub( '{userid}', userid );
      var cb = params.callback;
      var errorCb = params.callback;
      new exposure.common.AjaxPost( action, params, cb, errorCb );
    }
  });
  
  /*private*/
  var AjaxUsernameChecker = Class.create({
    initialize: function( params ) {
      var action = exposure.CONST.REST_USERNAME_CHECK_URL;
      action = action.sub( '{userid}', userid );
      action = action.sub( '{username}', params.username );
      var cb = params.callback;
      var errorCb = params.callback;
      new exposure.common.AjaxGet( action, {}, cb, errorCb );
    }
  });
  

  /*public*/
  var ProfileUpdater = Class.create({
    initialize: function ( params ) {
      exposure.common.validateParams( params, [ 'callback' ] );
      new AjaxProfileUpdater( params );
    }    
  });
  var UsernameChecker = Class.create({
    initialize: function ( params ) {
      exposure.common.validateParams( params, [ 'callback' ] );
      new AjaxUsernameChecker( params );
    }    
  });  
  
  /* Public API */
  return {
    'ProfileUpdater': ProfileUpdater,
    'UsernameChecker': UsernameChecker
  }  
  

})() ); // namespace


// EOF 


_NAMESPACE_( 'exposure.CONST', {
  
	REST_SHARE_WITH_CONTACTS_URL: 'rest/w/users/{userid}/image/{imageid}/sharewith/{contactUserIds}',
	REST_UNSHARE_WITH_CONTACTS_URL: 'rest/w/users/{userid}/image/{imageid}/unsharewith/{contactUserIds}'

});


_NAMESPACE_( 'exposure.data', (function () {

  /*private*/
	var AjaxShareWithUsersAction = Class.create({
		initialize: function( params, imageid, contactUserIds ) {
			var action = exposure.CONST.REST_SHARE_WITH_CONTACTS_URL;
			action = action.sub( '{userid}', userid );
			action = action.sub( '{imageid}', imageid );
			action = action.sub( '{contactUserIds}', contactUserIds );
			new exposure.common.AjaxPost( action, {}, params.callback );
	    }
	});

	var AjaxUnshareWithUsersAction = Class.create({
		initialize: function( params, imageid, contactUserIds ) {
			var action = exposure.CONST.REST_UNSHARE_WITH_CONTACTS_URL;
			action = action.sub( '{userid}', userid );
			action = action.sub( '{imageid}', imageid );
			action = action.sub( '{contactUserIds}', contactUserIds );
			new exposure.common.AjaxPost( action, {}, params.callback );
	    }
	});


  /*public*/
  var SharingService = {

	shareImage: function( params) {
    	exposure.common.validateParams( params, [ 'callback', 'imageid', 'contactUserIds' ] );
        new AjaxShareWithUsersAction( params, params.imageid, params.contactUserIds );
	},
      
	unshareImage: function( params) {
    	exposure.common.validateParams( params, [ 'callback', 'imageid', 'contactUserIds' ] );
        new AjaxUnshareWithUsersAction( params, params.imageid, params.contactUserIds );
	}
      
  };

  
  /* Public API */
  return {
    'SharingService': SharingService
  }  
  

})() ); // namespace


// EOF 
_NAMESPACE_('exposure.data', (function() {

  /*public*/
  var User = Class.create({
  
    initialize: function(screenName, token, avatarUrl) {
      // Private variables
      var _screenName = screenName;
      var _token = token;
      var _avatarUrl = avatarUrl; // Number of images in collection
      // Priviledged getters and setter to access private vars
      this.getScreenName = function() {
        return _screenName;
      };
      this.setScreenName = function(screenName) {
        _screenName = screenName;
      };
      
      this.getToken = function() {
        return _token;
      };
      this.setToken = function(token) {
        _token = token;
      };
      
      this.getAvatarUrl = function() {
        return _avatarUrl;
      };
      this.setAvatarUrl = function(avatarUrl) {
        _avatarUrl = avatarUrl;
      };
      
    }
  });
  
  return {
    'User': User
  };
  
  
})()); // namespace
// EOF 

_NAMESPACE_( 'exposure.home' );
  
  
/*public*/
exposure.home.InPlaceEditor = Class.create( exposure.ViewElement, {

  show: function ( obj, action, params ) {
    if ( !obj || !action ) {
      throw new Error( 'InPlaceEditor.show(): obj or action missing' );
    }
    // If user has right to edit the object, add in place editor
    if ( obj.hasRightsToEdit() ) {
      params = params || {};
      this.addEditor( action, params );
    }
  },
  
  hide: function ( $super, obj ) {
    // Remove editor if disposing the view
    if ( !obj ) {
      this.removeEditor();
      $super();
    }
  },

  // Implement in subclass  
  getValue: exposure.common.F,  
  setValue: exposure.common.F,  
  isEmptyOrDefault: exposure.common.F,
  getParams: exposure.common.F,

  addEditor: function ( action, params ) {
    // Check the needed methods are implemented in subclass
    if ( this.getValue === exposure.common.F
      || this.setValue === exposure.common.F
      || this.isEmptyOrDefault === exposure.common.F
      || this.getParams == exposure.common.F ) {
      throw new Error( 'InPlaceEditor: not subclassed properly' );
    }
    this.getDiv().addClassName( 'clickable' );
    Object.extend( params, {
      submitOnBlur: true,
      callback: this.getSubmitParams.bind(this),
      onComplete: this.updateSuccess.bind(this),
      onFailure: this.updateFailure.bind(this),
      onEnterEditMode: this.enterEditMode.bind(this)
    });
    this.editor = new Ajax.InPlaceEditor( this.getDiv(), action, params );
    
    // Change <br>'s to normal line feeds and unescape HTML
    this.editor.getText = (function () {
      var text = this.element.innerHTML;
      text = text.gsub( '<br/>', '\n' )
                 .gsub( '<br>', '\n' )
                 .gsub( '<BR/>', '\n' )
                 .gsub( '<BR>', '\n' )
                 .unescapeHTML();
      return text;
    }).bind( this.editor );
    
    // Make escape key work in edit mode
    this.editor._createEditField = this.editor.createEditField;
    this.editor.createEditField = (function () {
      // First call the original in place editor input field creator
      this._createEditField();
      // Then add support for exiting the editor with ESC key
      Event.observe( this._controls.editor, 'keydown', function ( event ) {
        // The same as in Scriptaculous controls.js, except checks for 
        // multirow element if enter pressed (textarea accepts enter)
        if (!this._editing || event.ctrlKey || event.altKey || event.shiftKey) return;
        if (Event.KEY_ESC == event.keyCode)
          this.handleFormCancellation(event);
        else if (Event.KEY_RETURN == event.keyCode && this.options.rows === 1)
          this.handleFormSubmission(event);
      }.bind( this ) );
    }).bind( this.editor );
  },
  
  enterEditMode: function () {
    
  },
  
  removeEditor: function () {
    this.getDiv().removeClassName( 'clickable' );
    if ( this.editor ) {
      this.editor.dispose();
      this.editor = null;
    }
  },
  
  getSubmitParams: function ( form, value ) {
    value = value.strip();
    // Put whitespace in the value so it won't be null in the server, server trims the value 
    if ( this.isEmptyOrDefault( value ) ) {
      value = ' ';
    }
    var params = this.getParams( value );
    // Add ajax token 
    params.set( 'token', window.ajaxToken );
    return params.toQueryString();
  },
  
  updateSuccess: function ( xhr ) {
    if( xhr ) {
      if( exposure.common.Account.isUserRequired( xhr ) ) {
        return;
      }
      var json = xhr.responseText.evalJSON( true );
      if ( json.errors === 0 ) {
        this.setValue( json.value );
      }
      else {
        // TODO notify of error somehow
      }
    }
    this.getDiv().update( this.getValue() );
  },
  
  updateFailure: function () {
    
  }  
});


// EOF

_NAMESPACE_( 'exposure.home' );


/*public*/
exposure.home.AbstractOverlayBehavior = Class.create( exposure.Behavior, {

  init: function () {
    this.screen = $$( '.screen' )[ 0 ];
    this.view = this.screen.down( '.view' );
    this.view.show();
    this.initBehaviour();
  },
  
  // Keep this behavior running at all times
  enable: function () {},
  disable: function () {},
  
  showOverlay: function ( ) {
    // Set top of overlay in case window has been scrolled
    var offsetTop = document.viewport.getScrollOffsets().top;
    this.screen.setStyle({ top: offsetTop + 'px' });
    // Overlay background is not visible in IE without some hack that
    // makes it rerender the background.. Probably would work without 
    // such hack with different HTML or CSS properties, but didn't find any
    if ( Prototype.Browser.IE ) {    
      this.screen.appear({ duration: 0.3, afterFinish: function() {
        var div = $$('.screen .bg')[0];
        div.removeClassName('bg');
        div.addClassName('bg');
      } });
    }
    else {
      this.screen.appear({ duration: 0.3 });
    }
  },
  
  closeOverlay: function ( event ) {
    this.screen.fade({ duration: 0.3 });
  }
  
});


// EOF

_NAMESPACE_( 'exposure.CONST', {
  CRUMB_CLEAR: 'crumb:clear',
  CRUMB_ADD: 'crumb:add',
  CRUMB_HAS_UPPER_LEVEL: 'crumb:has_upper_level',
  CRUMB_UP: 'crumb:up',
  CRUMB_CHECK_STATE: 'crumb:check_state'
});


_NAMESPACE_( 'exposure.home' );


/*public*/
exposure.home.BreadCrumbsBehavior = Class.create( exposure.Behavior, {

  init: function () {
    this.queue = [];
//    this.on( 'click', this.click );
    document.observe( exposure.CONST.CRUMB_CLEAR, this.clear.bind( this) );
    document.observe( exposure.CONST.CRUMB_ADD, this.add.bind( this) );
    document.observe( exposure.CONST.CRUMB_HAS_UPPER_LEVEL, this.hasUpperLevel.bind( this) );
    document.observe( exposure.CONST.CRUMB_UP, this.up.bind( this) );
    document.observe( exposure.CONST.CRUMB_CHECK_STATE, this.checkState.bind( this) );
  },
  
  // Keep this behavior running at all times
  enable: function () {},
  disable: function () {},
  
  click: function ( event ) {
    /*
      Get the clicked a href and retrieve from queue.
      obj.href === href -> activate obj.state
    */
    var elem = Event.element( event );
    var name = unescape( elem.href.split( '#' )[ 1 ] ); // Unescape to remove %20
    var state;
    do {
      state = this.removeState();
    } while ( this.queue.length > 0 && state.name !== name );

    Event.stop( event );
    exposure.common.PageState.setState( state.state );
  },
    
  clear: function( event ) {
    this.queue = [];
    this.getDiv().update();
    this.queuedName = null;
  },
  
  add: function ( event ) {
    this.addState( event.memo.name );
  },
  
  hasUpperLevel: function ( event ) {
    var cb = event.memo || exposure.common.F;
    cb( this.queue.length > 1 );
    Event.stop( event );
  },
  
  up: function ( event ) {
    var state = this.removeState();
    Event.stop( event );
    if ( state ) {
     exposure.common.PageState.setState( state.state );
    }
  },
  
  /**
   * Check if given state is in the queue. If found, remove it and all states
   * after it. Needed if the browser back button is pressed, as there is no 
   * event handler for it.
   */
  checkState: function ( event ) {
    Event.stop( event );
    var state = event.memo;
    if ( !state ) {
      return;
    }
    var removeRemainingStates = false;
    var originalLen = this.queue.length;
    for ( var i = 0; i < originalLen; i++ ) {
      if ( removeRemainingStates ) {
        this.removeState();
      }
      if ( !removeRemainingStates && this.queue[ i ].state.equals( state ) ) {
        removeRemainingStates = true;
        this.removeState();
      }
    }
  },
  
  addState: function ( name ) {
    this.queue.push({ 
      name: name, 
      state: exposure.common.PageState.getState().clone() });

    var span = $( SPAN() );
    if ( this.queue.length > 1 ) { span.insert( ' : ' ); }
    span.insert( A( { href: '#' + name }, name.capitalize() ) );
    this.getDiv().insert( span );
  },
  
  removeState: function () {
    if ( this.queue.length > 1 ) {
      $( this.getDiv().lastChild ).remove();
      return this.queue.pop();
    }
    else {
      return this.queue[0];
    }
  }

});


// EOF

_NAMESPACE_( 'exposure.CONST', {

  OWNER_NO_DESCRIPTION: 'Click to add description',
  NO_DESCRIPTION: 'This image has no description'
  
});



_NAMESPACE_( 'exposure.home' );
  
  
/*public*/
exposure.home.DescriptionElement = Class.create( exposure.home.InPlaceEditor, {

  show: function ( $super, obj ) {    
    if ( obj ) {
      var editorParams = {
        formClassName: 'descriptionEditorForm',
        rows: 3,
        cols: 30,
        okControl: 'link',
        okText: '',
        cancelText: ''
      };
      this.obj = obj;
      this.getDiv().update( this.getValue() );
      $super( obj, 'ajax/ImageManager.action?updateDescription', editorParams );
    }
  },
  
  // Implement InPlaceEditors abstract methods
  isEmptyOrDefault: function ( value ) {
    return ( value == exposure.CONST.NO_DESCRIPTION
          || value == exposure.CONST.OWNER_NO_DESCRIPTION
          || value == '' ); 
  },
  
  getValue: function () {
    var description = this.obj.getDescription();
    if ( !description || description == '' ) {
      description = this.obj.hasRightsToEdit()?
          exposure.CONST.OWNER_NO_DESCRIPTION:
          exposure.CONST.NO_DESCRIPTION;
    }
    return description;    
  },
  
  setValue: function ( value ) {
    this.obj.setDescription( value );
  },
  
  getParams: function ( value ) {
    return $H({
      description: value.escapeHTML(),
      imageIds: this.obj.getId()
    });
  }

});


// EOF

_NAMESPACE_( 'exposure.home' );


/*public*/
exposure.home.FooterBehavior = Class.create( exposure.home.AbstractOverlayBehavior, {

  initBehaviour: function () {
    this.originalOverlayWidth = this.view.getStyle('width');
    this.originalOverlayBgColor = this.view.previous('.bg').getStyle('backgroundColor');
    this.originalOverlayCentererTop = this.view.previous('.overlayCenterer').getStyle('marginTop');
    this.delegate( 'click', {
      '.invite': this.showInviteFriend,
      '.feedback': this.showFeedback,
      '.report': this.showReportAbuse
    });
  },
  
  attachForgotPasswordListener: function ( elem ) {
    elem.observe( 'click', this.showPassword.bind( this ) );
  },
  
  showInviteFriend: function ( event ) {
    this.showContent({ template: 'inviteFriend' });
    // Add username to the message
    var elem = this.view.down( 'textarea' );
    var text = elem.value.gsub( '_user_', '"'+window.username+'"' );
    elem.value = text;
    Event.stop( event );
  },
  
  showFeedback: function ( event ) {
    this.showContent({ template: 'feedbackForm' });
    Event.stop( event );
  },
  
  showReportAbuse: function ( event ) {
    var reportDiv = 
        exposure.view.ElementTemplates.cloneNode( 'reportInappropriateContent' );
    reportDiv.down( 'span' ).update( location.href );
    this.showContent({ div: reportDiv });
    Event.stop( event );
  },
  
  showPassword: function ( event ) {
    this.showContent({ template: 'forgotPassword' });
    Event.stop( event );
  },
  
  // TODO: Remove this method when all popups look the same
  restoreOverlay: function (params) {
    this.view.setStyle({'width': this.originalOverlayWidth});
    this.view.previous('.bg').setStyle({'backgroundColor': this.originalOverlayBgColor});
    this.view.previous('.overlayCenterer').setStyle({'marginTop': this.originalOverlayCentererTop});
    this.view.update('');
    this.showContent(params);  
  },
  
  showContent: function ( params ) {
    // Restore and cleanup div if "new style" overlay shown
    if (this.view.down('div') 
     && this.view.down('div').hasClassName('emailSharingOverlay')) {
      this.closeOverlay();
      this.restoreOverlay.bind(this).delay(0.3, params);
      return;
    }
    var div;
    if ( params.template ) {
      div = exposure.view.ElementTemplates.cloneNode( params.template );
    }
    else {
      div = params.div;
    }
    this.view.update( div );
    // Focus the email element
    Form.Element.activate.delay( 0.5, div.down( '.email' ));

    this.showOverlay();
  }
  
});


// EOF

_NAMESPACE_( 'exposure.home' );
  
  
exposure.home.GridSizeBehavior = Class.create( exposure.Behavior, {
  
  initialize: function ($super, div, cssOriginal, cssNewValue, cssPropertyName) {
    $super(div);
    this.cssOriginal = cssOriginal;
    this.cssNewValue = cssNewValue;
    this.cssPropertyName = cssPropertyName;
  },
  
  init: function () {},
  
  enable: function ( $super ) {
    $super();
    this.updateGridSize( this.cssNewValue, true );
  },
  
  disable: function ( $super ) {
    $super();
    this.updateGridSize( this.cssOriginal, false );
  },

  updateGridSize: function ( newSize, adjustThumbs ) {
    this.getDiv().setStyle(this.cssPropertyName + ':' + newSize);
    this.getDiv().fire( 'ddiv:updateOffsets', { 'adjustThumbs': adjustThumbs } );    
  }
    
});
  
  
// EOF

_NAMESPACE_( 'exposure.home' );
  
  
/*public*/
exposure.home.ListElement = Class.create( exposure.ViewElement, {
  initialize: function ( $super, div, Visualizer, getItemsFuncName ) {
    $super( div );
    this.visualizer = Visualizer;
    if(!getItemsFuncName) {
    	throw new Error( 'ListElement: No getItemsFuncName() function' );
    }
    this.getItemsFuncName = getItemsFuncName;
  },
  
  show: function ( obj ) {
    if ( this.hasGetItemsFunc( obj ) ) {
      var getItemsFunc = obj[this.getItemsFuncName];
      getItemsFunc().each( function ( div, visualizer, item, index ) {
        var elem = visualizer.createElem( item, obj, index );
        if ( elem ) { div.insert( elem ); }
      }.curry( this.getDiv(), this.visualizer ) );
    }    
  },
  
  hasGetItemsFunc: function ( obj ) {
	  return obj && Object.isFunction( obj[this.getItemsFuncName] );
  },
  
  hide: function ( obj ) {
    if ( this.hasGetItemsFunc( obj ) ) {
      // Remove num items from the back
      var i;
      var div = this.getDiv();
      var getItemsFunc = obj[this.getItemsFuncName];
      var items = getItemsFunc();
      for ( i=0; i < items; i++ ) {
        if ( div.hasChildNodes() ) {
          div.removeChild( div.lastChild );
        }
      }
    }
    else {
      // Clear the contents
      this.getDiv().update();
    }
  },
  
  update: function ( obj ) {
    /*
     * Go through items and compare to this.getDiv().childElements()
     * call update() on all children and add the remaining ones.
     */
    var children = this.getDiv().childElements();
    var len = children.size();
    if ( this.hasGetItemsFunc( obj ) ) {
      var getItemsFunc = obj[this.getItemsFuncName];
      var domItem = children[0];
      getItemsFunc().each( function ( div, visualizer, item, i ) {
        // Update current item
        if ( i < len ) {
          var curr = domItem;
          domItem = domItem.next();
          visualizer.update( item, curr );
        }
        // Add new items
        else {
          var elem = visualizer.createElem( item, obj, i );
          if ( elem ) {
            div.insert( elem );
          }          
        }
      }.curry( this.getDiv(), this.visualizer ) );
    }
  },
  
  invoke: function ( name, value ) {
    this.getDiv().childElements().each( function ( item ) {
      this.visualizer[ name ]( item, value );
    }, this );
  }
});


// EOF

_NAMESPACE_( 'exposure.home' );


/*public*/
exposure.home.LoginRequiredBehavior = Class.create( exposure.Behavior, {

  init: function () {
    this.getDiv().hide();
    this.getDiv().down( '.bg' ).setOpacity( 0.85 );
    document.observe( exposure.CONST.LOGIN_REQUIRED, this.showLoginInfo.bind( this) );
  },
  
  // Keep this behavior running at all times
  enable: function () {},
  disable: function () {},
  
  showLoginInfo: function () {
    var div = exposure.view.ElementTemplates.cloneNode( 'loginRequired' );
    var href = div.down( 'a' ).href + exposure.common.getSignInTargetUrl();
    // IE hack
		if ( href.startsWith( 'about:blank' ) ) { // IE6
      href = href.substr( 'about:blank'.length );
    }
    if ( href.startsWith( 'about:' ) ) { // IE7
      href = href.substr( 'about:'.length );
    }
    // END IE hack
    div.down( 'a' ).href = href;        
    this.getDiv().down( '.view' ).update( div );
    this.getDiv().down('.view').show();
    new Effect.Appear( this.getDiv(), { duration: 0.3 } );
  }
  
});


// EOF

_NAMESPACE_( 'exposure.home' );

  
/*public*/
exposure.home.NavigationBehavior = Class.create( exposure.Behavior, {
  init: function () {
    this.delegate( 'click', {
      '.up': this.up,
      '.previous': this.previous,
      '.next': this.next,
      '.goTo': this.goToPage
    });
    Event.observe(window, 'keyup', this.keyup.bind(this));
  },

  keyup: function (event) {
    if (this.disabled) {
      return;
    }
    // Don't prevent default action!!
    var key = event.keyCode;
    if ( key == Event.KEY_LEFT) {
      this.previous();
    }
    else if ( key == Event.KEY_RIGHT) {
      this.next();
    }
  },
  
  up: function () {
    exposure.common.PageState.up();
  },
  
  previous: function () {
    exposure.common.PageState.previous();
  },
  
  next: function () {
    exposure.common.PageState.next();
  },
  
  goToPage: function ( event ) {
    var page = exposure.common.toInt( Event.element( event ).innerHTML );
    exposure.common.PageState.goTo( page-1 ); // Pages indexed from 0
    Event.stop( event );
  }
});


// EOF

_NAMESPACE_( 'exposure.home' );


/*public*/
exposure.home.NavigationElement = Class.create( exposure.ViewElement, {
  initialize: function ( $super, div ) {
    $super( div );
    [ 'up', 'previous', 'next', 'text' ].each( function( item ) {
      this[ item ] = div.down( '.' + item );
    }, this );
    this.hide();
  },    
  
  show: function ( obj ) {
    this.getDiv().setStyle({ visibility: 'visible' });
    this.showOrHideElems( obj );
  },
    
  hide: function ( obj ) {
    this.showOrHideElems( obj );
  },
  
  showOrHideElems: function ( obj ) {
    if ( obj 
      && Object.isFunction( obj.getCurrent ) 
      && Object.isFunction( obj.getTotal ) ) {
      var current = obj.getCurrent();
      var total = obj.getTotal();
      
      //TODO: if current or total are missing, hide navigation altogether?
      this.updateText( current, total );
        
      current > 1? 
          this.previous.setStyle( 'visibility: visible;' ): 
          this.previous.setStyle( 'visibility: hidden;' );
      current < total? 
          this.next.setStyle( 'visibility: visible;' ): 
          this.next.setStyle( 'visibility: hidden;' );
      exposure.common.PageState.getCurrentState().getPrefix()? 
          this.up.setStyle( 'visibility: visible;' ): 
          this.up.setStyle( 'visibility: hidden;' );
     }
     else {
       this.text.update();
       this.next.setStyle( 'visibility: hidden;' );
       this.up.setStyle( 'visibility: hidden;' );
       this.previous.setStyle( 'visibility: hidden;' );
     }
  },
  
  updateText: function ( current, total ) {
    // Clear navigation and only show it if more than one page exist
    this.text.update();
    if ( total > 1 ) {
      // 1 2 3 4 5
      if ( current <= 6 ) {
        for ( var i = 1; i < current; i++ ) {
          this.text.insert( SPAN({ className: 'goTo' }, i ) );
        }        
      }
      // 1 ... 4 5 6    
      else {
        // 1
        this.text.insert( SPAN({ className: 'goTo' }, '1' ) );
        this.text.insert( '... ' );
        // E.g. 4 5 6
        for ( var i = current - 3; i < current; i++ ) {
          this.text.insert( SPAN({ className: 'goTo' }, i ) );
        }        
      }
      // Current
      this.text.insert( SPAN({ className: 'emph' }, current ));
      // 6 7 8 9 10
      if ( total - current <= 5 ) {
        for ( var i = current + 1; i <= total; i++ ) {
          this.text.insert( SPAN({ className: 'goTo' }, i ) );
        }        
      }
      // 6 7 8 ... 12    
      else if ( total - current > 5 ) {
        for ( var i = current + 1; i < current + 4; i++ ) {
          this.text.insert( SPAN({ className: 'goTo' }, i ) );
        }        
        this.text.insert( '... ' );
        this.text.insert( SPAN({ className: 'goTo' }, total ) );
      }
    }
  }
});


// EOF

_NAMESPACE_('exposure.home');


/*public*/
exposure.home.OverlayBehavior = Class.create(exposure.Behavior, {

  init: function () {
    this.getDiv().hide();
    this.getDiv().down('.bg').setOpacity(0.85);
    this.delegate('click', {
      '.close': this.closeOverlay,
      '.sendInvite': this.sendInvite,
      '.sendFeedback': this.sendFeedback,
      '.sendReport': this.sendReport,
      '.sendPassword': this.sendPassword
    });
    Event.observe(window, 'scroll', this.keepOverlayVisibleOnScroll.bind(this));
  },
  
  // Keep this behavior running at all times
  enable: function () {},
  disable: function () {},
  
  closeOverlay: function (event) {
    this.getDiv().fade({duration: 0.3});
    Event.stop(event);
  },
  
  sendInvite: function(event) {
    Event.stop(event);
    this.sending();
    try {
      exposure.data.DataHandler.sendInvite(this.getParams());
    } catch (e) {
      // It's ok not to provide a message, system generates an email anyway
    }
  },
  
  sendFeedback: function (event) {
    Event.stop(event);
    this.sending();
    var params;
    params = this.getParams();
    if (!params.message) {
      this.showError('Please provide a message');
      return;
    }
    params.agreeToPolicy = this.getDiv().down('input.agree').checked;
    exposure.data.DataHandler.sendFeedback(params);
  },
  
  sendReport: function (event) {
    Event.stop(event);
    var params = this.getParams();
    var img = null;
    if (!!exposure.data.DataHandler.getCurrentImage) {
      img = exposure.data.DataHandler.getCurrentImage();
      if (img) {
        params.imgId = img.getId();
      }
    }
    if (!params.message) {
      this.showError('Please provide an explanation of the inappropriate content');
      return;
    }
    params.url = location.href;
    this.sending();
    exposure.data.DataHandler.sendReport(params);
  },
  
  sendPassword: function (event) {
    Event.stop(event);
    this.sending();
    var params = this.getParams();
    if (!params.username) {
      this.showError('Please provide your username');
      return;
    }
    exposure.data.DataHandler.requestPasswordReset(params);
  },
  
  getParams: function () {
    var params = this.getFormData();
    params.callback = (function(response) {
      if (response.status === 'ok') {
        this.clearFormFields();
        this.showResult(response.msg);
      } else {
        this.showError(response.msg);
      }
    }).bind(this);
    params.errorCallback = params.callback;
    return params;
  },
  
  getFormData: function () {
    var data = {};
    var input = this.getDiv().down('input.email');
    if (input) {
      data.email = this.getDiv().down('input.email').getValue();
      data.message = this.getDiv().down('textarea.message').getValue();
    } else {
      data.username = this.getDiv().down('input.username').getValue();
    }
    return data;
  },
  
  clearFormFields: function () {
    var input = this.getDiv().down('input.email');
    if (input) {
      this.getDiv().down('input.email').clear();
      this.getDiv().down('textarea.message').clear();
    } else {
      this.getDiv().down('input.username').clear();
    }
  },
  
  showResult: function (result) {
    this.getDiv().down('.status').update(result);
  },
  
  sending: function () {
    this.showResult('Sending...');
  },
  
  showError: function (result) {
    this.showResult(SPAN({className: 'error'}, result));
  },
  
  keepOverlayVisibleOnScroll: function () {
    var offset = document.viewport.getScrollOffsets().top;
    this.getDiv().setStyle({top: offset + 'px'});
  }
  
});


// EOF

_NAMESPACE_( 'exposure.home' );
  
  
/*public*/
exposure.home.SimpleHTMLElement = Class.create( exposure.ViewElement, {
  
  initialize: function ($super, div, methodName) {
    $super(div);
    this.methodName = methodName;
  },

  show: function ( obj ) {
    if ( obj && Object.isFunction( obj[this.methodName] ) ) {
      var data = obj[this.methodName]() || '&nbsp;';
      this.getDiv().update( data );
    }
  }
  
});


// EOF

_NAMESPACE_('exposure.home');


/*public*/
exposure.home.SupportedOperatorsBehavior = Class.create(exposure.Behavior, {

  init: function() {
    this.delegate('change', {
      '.operatorCountry': this.showOperators
    });
    // Show the operators of the default country
    this.showOperators(null);
  },
  
  // Keep this behavior running at all times
  enable: function() {
  },
  disable: function() {
  },
  
  showOperators: function(event) {
    var countries = exposure.data.DataHandler.getOperators();
    var countrySelect = this.getDiv();
    var operators = countries[countrySelect.getValue()];
    var operatorList = $$('.operators')[0];
    operatorList.update();
    var index = 0;
    for (operator in operators) {
      var elem = $(exposure.view.ElementTemplates.cloneNode('operator'));
      elem.down('.operatorName').update(operators[operator].name);
      elem.down('.operatorStatus').update('(' + operators[operator].status + ')');
      index++;
      if (index % 2 != 0) {
        elem.addClassName('uneven');
      }
      operatorList.insert(elem);
    }
    if (event) {
      Event.stop(event);
    }
  }
  
});


// EOF

_NAMESPACE_('exposure.home');


/*public*/
exposure.home.SupportedOperatorsOverlayBehavior = Class.create(exposure.home.AbstractOverlayBehavior, {

  initBehaviour: function() {
    this.delegate('click', {
      '.showOperators': this.showOperatorSupportPopup,
      '.close': this.closeOverlay
    });
    document.observe('operators:operatorsReceived', this.showOperatorSupport.bind(this));
  },
  
  showOperatorSupportPopup: function(event) {
    // TODO should init be done in a more central manner? 
    exposure.data.DataHandler.init();
    if (exposure.data.DataHandler.getOperators()) {
      this.showOperatorSupport();
    }
    else {
      // operators not yet loaded. Start loading.      
      exposure.data.DataHandler.fetchOperators();
      this.showOperatorSupportLoading();
    }
    Event.stop(event);
  },
  
  showOperatorSupportLoading: function() {
    var div = exposure.view.ElementTemplates.cloneNode('operatorSupportLoading');
    this.view.update(div);
    
    this.showOverlay();
  },
  
  showOperatorSupport: function() {
    var div = exposure.view.ElementTemplates.cloneNode('operatorSupport');
    this.view.update(div);
    
    var countrySelect = div.down('.operatorCountry');
    var countries = exposure.data.DataHandler.getOperators();
    for (country in countries) {
      var option = new Element('option', {
        'class': 'countryOption',
        value: country
      });
      option.update(country);
      countrySelect.insert(option);
    }
    
    new exposure.home.SupportedOperatorsBehavior(countrySelect);
    
    // Focus on country dropdown
    Form.Element.activate.delay(0.5, countrySelect);
    
    this.showOverlay();
  }
  
});


// EOF

_NAMESPACE_( 'exposure.CONST', {

  OWNER_NO_TITLE: 'Click to add title',
  NO_TITLE: 'No title'
  
});



_NAMESPACE_( 'exposure.home' );
  
  
/*public*/
exposure.home.TitleElement = Class.create( exposure.home.InPlaceEditor, {

  show: function ( $super, obj ) {    
    if ( obj ) {
      var editorParams = {
        formClassName: 'titleEditorForm',
        okControl: 'link',
        okText: '',
        cancelText: ''
      };
      this.obj = obj;
      this.getDiv().update( this.getValue() );
      $super( obj, 'ajax/ImageManager.action?updateTitle', editorParams );
    }
  },
  
  // Implement InPlaceEditors abstract methods
  isEmptyOrDefault: function ( value ) {
    return ( value == exposure.CONST.NO_TITLE 
          || value == exposure.CONST.OWNER_NO_TITLE 
          || value == '' ); 
  },
  
  getValue: function () {
    var title = this.obj.getTitle();
    if ( !title || title == '' ) {
      title = this.obj.hasRightsToEdit()?
          exposure.CONST.OWNER_NO_TITLE:
          exposure.CONST.NO_TITLE;
    }
    return title;
  },
  
  setValue: function ( value ) {
    this.obj.setTitle( value );
  },
  
  getParams: function ( value ) {
    return $H({
      title: value.escapeHTML(),
      imageIds: this.obj.getId()
    });
  }
  
});


// EOF

_NAMESPACE_( 'exposure.home.grid' );
  
  
exposure.home.grid.BrowserSizeBehavior = Class.create( exposure.Behavior, {

  initialize: function ( $super, windw, div, callback ) {
    $super( windw );
    this.callback = callback;
    this.ddiv = $( div ); 
    this.callback = callback;
    this.updateOffsets();
    this.resize(); // initial size
    this.ddiv.observe( 'ddiv:updateOffsets', this.updateOffsets.bind( this, true ) );
  },
  
  getStyleValue: function( div, style ) {
    var val = div.getStyle( style );
    val = val.replace( 'px', '' );
    val = exposure.common.toInt( val );
    return val;
  },
  
  init: function () {
    this.on( 'resize', this.resize.bind( this, true ), false );
  },
  
  enable: function ( $super ) {
    $super();
    // In case the browser has been resized while this was disabled
    this.updateOffsets();    
    this.resize(); 
  },
  
  updateOffsets: function ( doResize, event ) {
    this.widthOffset = this.getStyleValue( this.ddiv, 'left' ) + 
        this.getStyleValue( this.ddiv, 'right' );
    this.heightOffset = this.getStyleValue( this.ddiv, 'top' ) + 
        this.getStyleValue( this.ddiv, 'bottom' );
    if ( doResize ) {
      var adjustThumbs = event && event.memo && event.memo.adjustThumbs || false;
      this.resize( adjustThumbs );
    }      
  }, 
  
  resize: function ( adjustThumbs, event ) {
    var d = document.viewport.getDimensions();
    var w = d.width - this.widthOffset;
    var h = d.height - this.heightOffset;
    this.callback( w, h, adjustThumbs );
  }
  
});
  
  
// EOF

_NAMESPACE_( 'exposure.home.grid', (function () {

  
  /*public*/
  var CollectionController = Class.create( exposure.common.Controller, {
    COLL_ID: 0,
    PAGE_ID: 1,
    
    show: function ( $super, state ) {
      $super( state );
      this.state = state.clone();
      this.collectionServiceUrl = this.getCollectionServiceUrl( state );
      this.pageNo = this.getPageNo( state );
      this.showCollection( this.getParams( state ), this.getViewParams( state ) );
    },
    
    getParams: function ( state ) { },
    getViewParams: function ( state ) { },

    getPageNo: function ( state ) {
      return state.getParams( this.PAGE_ID ) || 0;
    },
    
    showCollection: function ( params, viewParams ) {
      params = Object.extend({
        collectionServiceUrl: this.collectionServiceUrl, 
        callback: function ( collection ) {
          if ( !collection ) {
            throw new Error( 'No such collection (collectionServiceUrl:"'+this.collectionServiceUrl+'")' );
          }
          viewParams = Object.extend({
            collection: collection, 
            pageNo: this.pageNo, 
            state: this.state
          }, viewParams || {} );
          this.notifyViews( viewParams );
        }.bind( this )
      }, params || {} );
      exposure.data.DataHandler.getCollection( params ); 
    },
  
    next: function () {      
      var pageNo = exposure.common.toInt( this.pageNo ) + 1;
      this.state.setParam( this.PAGE_ID, pageNo );
      exposure.common.PageState.setState( this.state );
    },  
  
    previous: function () {
      var pageNo = exposure.common.toInt( this.pageNo ) - 1;
      if ( pageNo <= 0 ) { pageNo = undefined; }
      this.state.setParam( this.PAGE_ID, pageNo );
      exposure.common.PageState.setState( this.state );
    },
  
    goTo: function ( index ) {
      //TODO: Check index is valid 
      this.state.setParam( this.PAGE_ID, index );
      exposure.common.PageState.setState( this.state );
    }
  });
  
  
  /*public*/
  var FixedCollectionController = Class.create( CollectionController, {
    PAGE_ID: 0,
    
    getCollectionServiceUrl: function ( state ) {
      var url = state.getConfigMap().collectionServiceUrl;
      return url.sub( '{current_userid}', window.userid );
    }

  });
  

  /* Public API */
  return {
    'CollectionController': CollectionController,
    'FixedCollectionController': FixedCollectionController
  }

})() ); // namespace


// EOF

_NAMESPACE_( 'exposure.home.grid', (function () {

  /*private*/
  var GridData = Class.create({
    initialize: function () {
      [ 'width', 'height', 'thumbSize', 'count', 'totalCount', 'targetCount' ].each(
        this.createGetAndSet.bind( this ) );
    },
    
    createGetAndSet: function ( item ) {
      this[ item ] = null; // Variable
      this[ 'get' + this.capitalize( item ) ] = // Getter
          function () { return this[ item ] };
      this[ 'set' + this.capitalize( item ) ] = // Setter
          function ( value ) { this[ item ] = value };
    },
    
    capitalize: function ( item ) {
      return item.charAt(0).toUpperCase() + item.substring(1);
    },
    
    setInitialPageNo: function ( pageNo ) {
      // Set the original offset
      this.offset = this.getNewNumOfThumbs() * pageNo;
    },
    
    getNewCount: function () {
      //TODO: What if we reach the end of the collection? 
      var offset = this.offset + this.targetCount;
      if ( this.totalCount !== null && offset > this.totalCount ) {
        this.targetCount = this.totalCount - this.offset;
        offset = this.totalCount;
      }
      var count = this.getNewNumOfThumbs() - this.targetCount;
      // Don't go over the collection size
      if ( this.totalCount && offset + count > this.totalCount ) {
        count = this.totalCount - offset;
      }
      return { offset: offset, count: count };
    },
    
    getNewNumOfThumbs: function () {
      var marginTopBottom = 32; // Get these numbers from grid.css
      var marginLeftRight = 20; // #grid .thumb -> margin: 15px 10px;
      var descriptionHeight = 28; // #grid .thumb .name -> height: 14px x 2 = 28px
      marginTopBottom += descriptionHeight;
      var countPerRow = Math.floor( this.width / ( this.thumbSize+marginLeftRight ) );
      var countPerCol = Math.floor( this.height / ( this.thumbSize+marginTopBottom ) );
      return countPerCol*countPerRow || 1; // If num would be 0, return 1 instead
    },
    
    getPageNo: function () {
      var val = Math.floor( this.offset / this.getNewNumOfThumbs() );
      if ( this.offset > 0 && val === 0 ) { val++; }
      return val+1; // There is no such thing as page 0
    },
    
    getPageCount: function () {
      return Math.ceil( this.totalCount / this.targetCount );
    }
  });


  /*public*/  
  var CollectionView = Class.create( exposure.View, {
    init: function ( $super ) {
      this.grid = new GridData();
      this.initialized = false;
      $super();
    },
    
    initElements: function () {
      var div = this.getDiv();
      this.addElement( new exposure.home.ListElement( div.down( '.thumbs' ),
          exposure.home.grid.ThumbnailListComponent , 'getItems' ) );
      this.addElement( new exposure.home.grid.FeedLinkElement( div.down( '.feedLink' )) );          
      this.addElement( new exposure.home.PageChangeObserver() );
    },
    
    initBehaviors: function () {
      var div = this.getDiv();
      this.addBehavior( new exposure.home.grid.GridBehavior( div.down( '.thumbs' ) ) );
      this.addBehavior( new exposure.home.grid.UserNameLinkBehavior( div.down( '.thumbs' ) ) );
      this.addBehavior( new exposure.home.grid.BrowserSizeBehavior( window, 
          div.down( '.thumbs' ), this.onBrowserSizeChange.bind( this ) ) );
      this.addBehavior( new exposure.home.grid.SliderBehavior( 
          div.down( '.slider' ), this.onSliderValueChange.bind(this) ) );
    },
    
    onBrowserSizeChange: function ( w, h, adjustThumbs ) {
      this.grid.setWidth( w );
      this.grid.setHeight( h );
      if ( this.initialized && adjustThumbs ) {
        this.adjustNumOfThumbs(); // Only adjust if the element is showing
      }
    },
    
    onSliderValueChange: function ( value ) {
      this.grid.setThumbSize( value );
      this.getElements().invoke( 'invoke', 'resize', value );
      this.adjustNumOfThumbs();    
    },
    
    getItems: function ( obj ) {
      this.grid.setCount( null );
      this.grid.setTargetCount( null );
      this.grid.setTotalCount( null );
      this.initialized = true;
      this.collection = obj.collection;
      this.grid.setInitialPageNo( obj.pageNo );
      this.adjustNumOfThumbs();
    },
    
    hide: function( $super, obj ) {
      $super( obj );
      if ( !obj ) {
        this.initialized = false;
      }
    },
    
    adjustNumOfThumbs: function () {
      if ( !this.grid ) { return; }
      var newNum = this.grid.getNewCount();
      // Check we're all ready and there is a change in thumb count      
      if ( this.initialized && newNum.count != 0 ) {
        this.grid.setTargetCount( this.grid.getTargetCount() + newNum.count );
        if ( newNum.count > 0 ) {
          this.collection.getItems({ 
            offset: newNum.offset,
            count: newNum.count,
            callback: this.addThumbs.bind( this ) });
        }
        else if ( newNum.count < 0 ) {
          var count = Math.abs( newNum.count );
          this.removeThumbs( count );
        }
      }
    },
    
    addThumbs: function ( items ) {
      if ( $$( '#grid .noItemsInCollection' )[0] ) {
        $$( '#grid .noItemsInCollection' )[0].remove();
      }
      this.grid.setTotalCount( this.collection.getCount() );
      this.grid.setCount( this.grid.getCount() + items.length );
      this.showItems({
          getItems: function () { return items; },
          getCurrent: this.grid.getPageNo.bind( this.grid ),
          getTotal: this.grid.getPageCount.bind( this.grid ),
          getThumbSize: this.grid.getThumbSize.bind( this.grid )
      });
      if ( items.length === 0 ) {
        this.getDiv().down('.thumbs').update(
            exposure.view.ElementTemplates.cloneNode( 'noItemsInCollection' ) );
      }
    },
    
    removeThumbs: function ( count ) {
      // Don't remove the last item
      if ( this.grid.getCount() - count === 0 ) {
        this.grid.setTargetCount( this.grid.getTargetCount() + 1 );
        count--;
      }
      this.grid.setCount( this.grid.getCount() - count );
      this.hide({
        getItems: function () { return count; },
        getCurrent: this.grid.getPageNo.bind( this.grid ),
        getTotal: this.grid.getPageCount.bind( this.grid )
      });
    }
  });

  
  /* Public API */
  return {
    CollectionView: CollectionView
  }

  
})() ); // namespace


// EOF 

_NAMESPACE_('exposure.home.grid');


/*public*/
exposure.home.grid.FeedLinkElement = Class.create(exposure.ViewElement, {

  initialize: function($super, div) {
    $super(div);
    this.getDiv().hide();
  },
  
  show: function(obj) {
    var feedUrl = this.getFeedUrl();
    var icon = null;
    var link = this.getDiv();
    if (feedUrl) {
      if (PicLensLite.hasClient()) {
        link.addClassName('cooliris');
        // CoolIris (aka PicLens) plugin installed, use it to open the link
        this.addOnClickHandler(feedUrl, link);
      }
      link.show();
    } else {
      link.hide();
    }
    link.href = feedUrl;
  },
  
  getFeedUrl: function() {
    var currentState = exposure.common.PageState.getCurrentState();
    var feedUrl = currentState.getConfigMap().collectionFeedUrl;
    if (!feedUrl) {
      return null;
    }
    var token = '';
    var controller = exposure.common.PageState.getCurrentControllers().controllers[0];
    if (controller.getUserToken) {
      token = controller.getUserToken(currentState);
    }
    return feedUrl.sub('{userToken}', token);
  },
  
  hide: function(feedUrl) {
  },
  
  addOnClickHandler: function(feedUrl, link) {
    link.onclick = function() {
      PicLensLite.start({
        feedUrl: feedUrl
      });
      return false;
    }
  }
  
});


// EOF

_NAMESPACE_( 'exposure.home.grid' );
  
  
/**
 * Behavior for the image grid. Handles image clicks with one single event 
 * listener, i.e. no separate listeners for each thumbnail.
 */
exposure.home.grid.GridBehavior = Class.create( exposure.Behavior, {

  init: function () {
    this.on( 'click', this.click );
  },
  
  click: function ( event ) {
    var elem = Event.findElement( event, 'div' );
    if ( elem.hasClassName( 'thumb' ) ) {
      var coll = exposure.data.DataHandler.getCurrentCollection();
      var photoUri = location.hash.split( '#' )[1] + 
         '/photo/' + this.getImageId( elem );
      exposure.common.PageState.setStateFromUri( photoUri );
    }
    Event.stop( event );
  },
  
  getImageId: function( elem ) {
    return elem.id.split( '_' )[ 1 ]
  }
  
});


// EOF

_NAMESPACE_( 'exposure.home.grid' );


/*public*/  
exposure.home.grid.InfoBarView = Class.create( exposure.View, {
  
  initElements: function () {
    var div = this.getDiv();
      this.addElement( 
        new exposure.home.TitleElement( div.down( '.title' ) ));
      this.addElement( 
        new exposure.home.DescriptionElement( div.down( '.description' ) ));
  },
  
  getItems: function ( obj ) {    
    var coll = 
    this.showItems({
      state: obj.state,
      getTitle: function () { return obj.collection.getTitle(); },
      getDescription: function () { return obj.collection.getDescription(); },
      hasRightsToEdit: function () { return obj.collection.hasRightsToEdit(); }
    });
  }
  
});


// EOF 

_NAMESPACE_( 'exposure.home' );
  
  
/*public*/
exposure.home.PageChangeObserver = Class.create( exposure.ViewElement, {

  initialize: function () {},

  show: function ( obj ) {
    var newCurrent = this.current;
    var total;
    if ( obj
      && Object.isFunction( obj.getCurrent )
      && Object.isFunction( obj.getTotal ) ) {
      newCurrent = obj.getCurrent();
      total = obj.getTotal();
      // If the thumbnail size has been changed, 
      // the page count may be less than current page
      if ( total > 0 && newCurrent > total ) {
        newCurrent = total;
      }
      // If the page has been changed due to change in thumbnail size,
      // reload the collection to show correct images
      if ( this.current != newCurrent ) {
        exposure.common.PageState.goTo( newCurrent-1 );
      }
    }
    this.current = newCurrent;
  },
  
  hide: function () { 
    this.current = null;
  }
  
});


// EOF

_NAMESPACE_( 'exposure.home.grid' );


/**
 * @constructor
 * @param {Object} div
 * @param {Function} callback
 * @extends exposure.Behavior
 */
exposure.home.grid.SliderBehavior = Class.create( exposure.Behavior, {
  sliderMin: 0.4,
  sliderMax: 1,

  initialize: function ( $super, div, callback ) {
    this.callback = callback;
    this.timerId = null;
    this.lastChanged = 0;
    var initVal = this.getValue();
    this.slider = new Control.Slider( div.down( '.handle' ), div.down( '.sliderBg' ), {
        onSlide: this.valueChange.bind( this ),
        onChange: this.valueChange.bind( this ),
        range: $R( this.sliderMin, this.sliderMax ),
        sliderValue: initVal });
    this.valueChange( initVal );
    $super( div );
  },
  
  init: function () {
    this.delegate( 'click', {
      '.smaller': this.zoomOut,
      '.bigger': this.zoomIn
    });
  },
  
  getValue: function () {
     return ( exposure.common.Cookies.get( exposure.CONST.THUMB_SIZE_COOKIE )
        || exposure.CONST.DEFAULT_THUMB_SIZE ) / exposure.CONST.MAX_THUMB_SIZE;
  },
  
  store: function ( value ) {
    if ( value > 1 ) { value = 1; }
    value = Math.round( value * exposure.CONST.MAX_THUMB_SIZE );
    var expires = 365; // days
    exposure.common.Cookies.set(exposure.CONST.THUMB_SIZE_COOKIE, value, expires, '/');		
    return value;
  },
  
  valueChange: function ( value ) {
    value = this.store( value );
    this.callback( value );
  },
  
  zoomOut: function () {
    var value = this.getValue() - 0.1;
    if ( value < this.sliderMin ) { value = this.sliderMin; }
    this.slider.setValue( value );
  },
  
  zoomIn: function () {
    var value = this.getValue() + 0.1;
    if ( value > this.sliderMax ) { value = this.sliderMax; }
    this.slider.setValue( value );
  }
  
});


// EOF


_NAMESPACE_( 'exposure.home.grid' );
  
  
/*public*/
/**
 * Creates a thumbnail DOM element.
 * @author strandel
 * @version x
 */
exposure.home.grid.ThumbnailListComponent = {
  
  /**
   * Creates a thumbnail DOM element.
   * @param {Object} item   Thumbnail metadata 
   * @param {Object} params {getThumbSize: function}  
   */
  createElem: function ( item, params ) {
    // Clone template node
    var div = exposure.view.ElementTemplates.cloneNode( 'thumb' );
    var img = div.down( 'img' );
    // Populate cloned node
    div.id = this.getId( item );
    this.fadeIn( div );
    img.src = item.url;
    img.title = item.title;
    img.className = this.getClassName( item );
    div.down( '.name' ).update( item.title );
    // Don't include owner info in private collection
    //TODO: hack that uses current URL to check the collection
    if ( location.href.indexOf( '/my' ) == -1 ) {
      var linkToUserPage = div.down( '.user' );
      linkToUserPage.update( item.owner || 'unknown' );
      // This url is used in UserNameLinkBehavior
      linkToUserPage.href = this.getLinkToUserPage( item.ownerToken );
    }
    this.resize( div, params.getThumbSize() || exposure.CONST.DEFAULT_THUMB_SIZE );
    return div;
  },
  
  /**
   * Creates an id for the DOM element based on image item.
   * @param {Object} item Thumbnail metadata
   * @return {String} id  Id for the DOM element  
   */
  getId: function ( item ) {
    return 'thumb_' + item.id;
  },
  
  /**
   * Returns the CSS class for the thumbnail based on the image size.
   * @param {Object} item Thumbnail metadata
   * @return {String} className  CSS class name 
   */
  getClassName: function ( item ) {    
    return (item.height > item.width ? 'thumbImgPortrait' : 'thumbImgLandscape');
  },
  
  /**
   * Resizes the thumbnail to given size.
   * @param {Element} div  The DOM element
   * @param {Number} value The size to resize to (in pixels)
   */
  resize: function ( div, value ) {
		var cssValue = value + 'px';
    var cssH = value + 28 + 'px';  // 28 pixels is reserved for image titles 
    var img = $( div ).down( 'img' );
    if( img.className == 'thumbImgPortrait' ) {
      img.setStyle({ height: cssValue });
    }
    $( div ).setStyle({ width: cssValue, height: cssH });
  },
  
  getLinkToUserPage: function ( ownerToken ) {
    var currentCollectionUri = location.hash.split( '#' )[1];
    var link = 'home.jsp#' + currentCollectionUri;
    var userPageUri = '/user/' + ownerToken;
    if(currentCollectionUri.indexOf( userPageUri ) < 0) {        
      link += userPageUri;
    } 
    return link;
  },
  
  /**
   * Registers an onload listener to fade the thumbnail in.
   * @param {Object} div DOM element for this thumbnail
   */
  fadeIn: function ( div ) {
    div = $( div );
    div.setStyle({ visibility: 'hidden'});
    div.setOpacity( 0 );
    div.down( 'img' ).setStyle({ display: 'none' });
    div.down( 'img' ).observe( 'load', function ( event ) {
      // On slow connections the initial empty image caused flickering in FF
      if ( div.down( 'img' ).src.indexOf( 'empty.gif' ) === -1 ) {
        div.down( 'img' ).setStyle({ display: 'inline' });
        div.setStyle({ visibility: 'visible' });
        div.appear({ duration: 0.4 });
      }      
    }.bind( div ) );    
  }
  
};
  

// EOF

_NAMESPACE_('exposure.home.grid');


/**
 * Handles user name clicks with one single event
 * listener, i.e. no separate listeners for each thumbnail.
 */
exposure.home.grid.UserNameLinkBehavior = Class.create(exposure.Behavior, {

  init: function() {
    this.on('click', this.click);
  },
  
  click: function(event) {
    var elem = Event.findElement(event, 'a');
    if (elem && elem.hasClassName('user') && elem.href) {
      // This url was set in ThumbnailListComponent
      var userPageUri = elem.href.split('#')[1];
      exposure.common.PageState.setStateFromUri(userPageUri);
    }
    Event.stop(event);
  }
  
});


// EOF

_NAMESPACE_( 'exposure.home.contacts' );
  

/**  
 * Behavior for ContactsSideBar links. On click the state is changed using 
 * PageState and not just by changing the url. 
 */
exposure.home.contacts.ContactBehavior = Class.create( exposure.Behavior, {

  init: function () {
    this.delegate( 'click', {
      'a': this.onClick
    });
  },
  
  onClick: function ( event ) {
    var link = Event.findElement( event, 'a' );
    if ( link ) {
      var hash = link.href.split( '#' )[1];      
      exposure.common.PageState.setStateFromUri( hash );
			exposure.home.contacts.ContactHelper.selectContact( link );
    }
    Event.stop( event );
  }
	
});
  
  
// EOF

_NAMESPACE_( 'exposure.home.contacts' );

exposure.home.contacts.ContactHelper = {
	
  unSelectContacts: function() {
    $$( '#infoBar .contacts li a' ).invoke( 'removeClassName', 'selected' );
  },
  
  selectContact: function ( link ) {
    this.unSelectContacts();
		link.addClassName( 'selected' );
  },

	isSelected: function ( contactId ) {
    if ( !contactId && isNaN( this.getContactId() ) ) {
			// There is no contactId given as parameter and there is no contact
			// id in the location bar hash code, so 'all' is selected
			return true;			
		}
		return this.getContactId() === contactId;
  },
  
  getContactId: function () {
    return exposure.common.toInt( location.hash.split( '/' )[ 2 ] );
  }


};


// EOF


_NAMESPACE_( 'exposure.home.contacts' );
  
  
/*public*/
exposure.home.contacts.ContactListComponent = {
  
	createElem: function ( contact, params, index ) {
    var listItem = exposure.view.ElementTemplates.cloneNode( 'peopleMenuItem' );
    var link = listItem.down( 'a' );
		link.update( contact.contact );
    link.href = '#/people/' + contact.refid;
		if ( exposure.home.contacts.ContactHelper.isSelected( contact.refid ) ) {
			exposure.home.contacts.ContactHelper.selectContact( link );
		}  
    return listItem;
  },

  update: function ( contact, listItem ) {
    //Needed? 
  }
	
};


_NAMESPACE_( 'exposure.home.contacts', (function () {

  /*public*/
  var ContactsController = Class.create( exposure.home.grid.FixedCollectionController, {
    
    NAME_ID: 0,
    PAGE_ID: 1,
    
    show: function ( $super, state ) {
      this.parentShow = $super.curry( state );
      this.state = state.clone();
      this.viewParams = {};
      this.getContacts();
    },

    getCollectionServiceUrl: function($super, state) {
      var serviceUrl = $super( state );
      var contactId = state.getParams( this.NAME_ID );
      if ( contactId ) {
        serviceUrl += '/' + contactId;
      }
      return serviceUrl;
    },
    
    getViewParams: function ( state ) { 
      return this.viewParams; 
    },
    
    getContacts: function () {
      if ( this.contacts ) {
        this.getContactsCallback( this.contacts );
      }
      else {
        exposure.data.ContactsService.getContactsWithImages({
          callback: this.getContactsCallback.bind( this )
        });
      }
    },
    
    getContactsCallback: function ( json ) {
      this.contacts = json;
      this.viewParams.getContacts = function () { return json; };
      this.parentShow();      
    }
            
  });
  

  /* Public API */
  return {
    'ContactsController': ContactsController
  }

})() ); // namespace


// EOF

_NAMESPACE_( 'exposure.home.contacts' );


/*public*/  
exposure.home.contacts.ContactsSideBarView = Class.create( exposure.View, {
  
  initElements: function () {
    var div = this.getDiv();
    this.addElement( new exposure.home.TitleElement( div.down( '.title' ) ));
    this.addElement( new exposure.home.DescriptionElement( div.down( '.description' ) ));
    this.contactsList = new exposure.home.ListElement( 
      div.down( '.contactList' ), exposure.home.contacts.ContactListComponent, 'getContacts');
    this.addElement( this.contactsList );
			
  },
  
  initBehaviors: function () {
    this.addBehavior( new exposure.home.GridSizeBehavior( 
        $$( '#grid .thumbs' )[ 0 ], '30px', '325px', 'right' ));
    this.addBehavior( new exposure.home.contacts.ContactBehavior( 
        this.getDiv().down( '.contacts' ) ));
		this.addBehavior( new exposure.home.contacts.SelectAllElemBehavior( 
		    this.getDiv().down( '.contacts li a' ) ));
  },
  
  getItems: function ( obj ) {    
    this.showItems({
      state: obj.state,
      getTitle: function () { return "People"; },
      getDescription: function () { return "View your friends' public images."; },
      hasRightsToEdit: function () { return false; },
			getContacts: obj.getContacts
    });
  }
  
});


// EOF 

_NAMESPACE_( 'exposure.home.contacts' );
  

/**  
 * Behavior for ContactsSideBar links. On click the state is changed using 
 * PageState and not just by changing the url. 
 */
exposure.home.contacts.SelectAllElemBehavior = Class.create( exposure.Behavior, {

  init: function () { },
  
	enable: function () {
		if ( exposure.home.contacts.ContactHelper.isSelected(/*empty to denote 'all'*/) ) {
			exposure.home.contacts.ContactHelper.selectContact( this.getDiv() );
    }
	}
	
});
  
  
// EOF

_NAMESPACE_( 'exposure.home.userpage' );
  
  
/*public*/
exposure.home.userpage.AvatarElement = Class.create( exposure.ViewElement, {
  
  show: function (profile) {
    this.getDiv().src = profile.getOwner().getAvatarUrl();
  },
  
  hide: function () {
    // Reset the avatar. So that the previous avatar is not displayed 
    // when entering possibly another user's page. 
    this.getDiv().src = 'img/empty.gif';    
  }
  
});


// EOF

_NAMESPACE_('exposure.home.userpage');


/*public*/
exposure.home.userpage.ProfileInfoView = Class.create(exposure.View, {

  initElements: function() {
    var div = this.getDiv();
    this.addElement(new exposure.home.SimpleHTMLElement(div.down('.title'), 'getTitle'));
    this.addElement(new exposure.home.SimpleHTMLElement(div.down('.description'), 'getDescription'));
    this.addElement(new exposure.home.userpage.TitleLineElement($('profileTitleLine')));
    this.addElement(new exposure.home.userpage.AvatarElement(div.down('.avatar')));
  },
  
  initBehaviors: function() {
    this.addBehavior(new exposure.home.GridSizeBehavior($$('#grid .thumbs')[0], '30px', '220px', 'left'));
  },
  
  getItems: function(obj) {
    var profile = obj.getProfile();
    this.showItems({
      state: obj.state,
      getTitle: function() { return profile.name + '\'s Page'; },
      getDescription: function() { return profile.description; },
      getOwner: function() {
        return {
          getAvatarUrl: function() {
            return profile.avatarUrl;
          }
        }
      },
      dummyFunction: function() { return ''; }
    });
  }
  
});


// EOF 

_NAMESPACE_( 'exposure.home.userpage' );
  
  
/*public*/
exposure.home.userpage.TitleLineElement = Class.create( exposure.ViewElement, {
  
  show: function () {
    this.getDiv().setStyle({display: 'block'});
  },
  
  hide: function () {
    this.getDiv().setStyle({display: 'none'});
  }
  
});


// EOF

_NAMESPACE_('exposure.home.userpage', (function() {

  /*public*/
  var UserPageController = Class.create(exposure.home.grid.FixedCollectionController, {
  
    NAME_ID: 0,
    PAGE_ID: 1,
    
     show: function ( $super, state ) {
      this.parentShow = $super.curry( state );
      this.state = state.clone();
      this.viewParams = {};
      this.getProfile();
    },

    getCollectionServiceUrl: function($super, state) {
      var serviceUrl = $super(state);
      return serviceUrl.sub('{userToken}', this.getUserToken(state));
    },
    
    getUserToken: function(state) {
      return state.getParams(this.NAME_ID);
    },
    
    getViewParams: function ( state ) { 
      return this.viewParams; 
    },
    
    getProfile: function () {
      exposure.data.ProfileService.getProfile({
        userToken: this.getUserToken(this.state), 
        callback: this.getProfileCallback.bind( this )
      });
    },
    
    getProfileCallback: function ( json ) {
      this.viewParams.getProfile = function () { return json; };
      this.parentShow();
    }

  });
  
  /* Public API */
  return {
    'UserPageController': UserPageController
  }
  
})()); // namespace
// EOF

_NAMESPACE_( 'exposure.home.header' );


/*public*/ 
exposure.home.header.HeaderController = Class.create( exposure.common.Controller, {

  show: function ( state ) {
    this.notifyViews({ state: state });
  },

  hide: function () {}
  
});
  
  
// EOF
 
_NAMESPACE_( 'exposure.home.header' );


/*public*/  
exposure.home.header.HeaderView = Class.create( exposure.View, {
  
  initElements: function () {
    var div = this.getDiv();
    
    this.addMainSectionElementIfPresent(div.down( '.my' ));
    this.addElement(
        new exposure.home.header.MainSectionElement( div.down( '.public' ) ));
    this.addMainSectionElementIfPresent(div.down( '.manage' ));

    this.addElement( 
        new exposure.home.header.SubsectionElement( div.down( '.subLinks' ) ));
        
  },
  
  addMainSectionElementIfPresent: function (sectionElement) {
    if(sectionElement) {
      this.addElement(
        new exposure.home.header.MainSectionElement( sectionElement, true ));      
    }    
  },
  
  initBehaviors: function () {
    var div = this.getDiv();

    this.addMainSectionBehavior(div.down( '.my' ));
    this.addBehavior(
        new exposure.home.header.MainSectionBehavior( div.down( '.public' ) ));
    this.addMainSectionBehavior(div.down( '.manage' ));

    this.addBehavior( new exposure.home.FooterBehavior( $$( '.footer' )[ 0 ] ));
    this.addBehavior( new exposure.home.OverlayBehavior( $$( '.screen' )[ 0 ] ));
    this.addBehavior( 
        new exposure.home.header.SubsectionBehavior( div.down( '.subLinks' ) ));
  },

  addMainSectionBehavior: function (sectionElement) {
    if(sectionElement) {
      this.addBehavior(
        new exposure.home.header.MainSectionBehavior( sectionElement ));
    }    
  },
  

  
  getItems: function( obj ) {
    this.showItems({ state: obj.state });
  },
  
  hide: function () {}
});


// EOF 

_NAMESPACE_( 'exposure.home.header' );
  
  
/**  
 * Behavior for the icons in the header. Handles clicks and changes from 
 * selected to deselected on custom click event. 
 */
exposure.home.header.MainSectionBehavior = Class.create( exposure.Behavior, {

  init: function () {
    // class name could be e.g. 'my normal'
    this.name = this.getDiv().className.split(' ')[0];
    this.on( 'click', this.onClick );
    document.observe( 
        'header:navigationElementSelected', this.deSelect.bind( this ) );
  },
  
  onClick: function ( event ) {
    if ( this.isEnabled() ) {
      var uri = this.getDiv().href.split( '#' )[1];
      exposure.common.PageState.setStateFromUri( uri );
    }
    Event.stop( event );
  },
  
  deSelect: function ( event ) {
    // Deselect this item if other item was clicked and this item is enabled
    if ( event.memo.name !== this.name && this.isEnabled() ) {
        this.getDiv().removeClassName('selected');
        this.getDiv().addClassName('normal');
    }
  },
  
  isEnabled: function () {
    return !(this.getDiv().hasClassName( 'disabled' ));
  }
});
  
  
// EOF

_NAMESPACE_( 'exposure.home.header' );


/*public*/
exposure.home.header.MainSectionElement = Class.create( exposure.ViewElement, {

  initialize: function ( $super, div, disableIfNoUser ) {
    $super( div );
    this.name = div.className;
    this.triangle = this.getDiv().up().next('.triangle');
    if ( username ) {
      this.changeClassName('normal');
    }
    else if ( disableIfNoUser ) {
      this.changeClassName('disabled'); 
    }    
  },
  
  checkIfSelected: function ( state ) {
    var section = state.getConfigMap();
    // If current state can be used under many section (e.g. photo view)
    // check keyword from prefix
    if ( !section ) {
      var previousState = exposure.common.PageState.getPrefixState();
      section = previousState.getConfigMap();
    }
    if ( section && section.section == this.name ) {
      this.changeClassName('selected');
      this.moveTriangle();
      document.fire( 'header:navigationElementSelected', { name: this.name } );
    }
  },
  
  changeClassName: function (newClassName) {
    var div = this.getDiv();
    div.removeClassName('normal');
    div.removeClassName('disabled');
    div.removeClassName('selected');
    div.addClassName(newClassName);
  },
  
  moveTriangle: function () {
    var div = this.getDiv();
    var left = div.positionedOffset().left + div.getWidth()/2 - this.triangle.getWidth()/2;
    this.triangle.morph({'left': left+'px'}, {duration: 0.5});
  },
  
  show: function ( obj ) {
    this.checkIfSelected( obj.state );
  },
    
  hide: function () {}

});


// EOF

_NAMESPACE_( 'exposure.home.header' );
  

/**  
 * Behavior for InfoBar links. On click the state is changed using PageState
 * and not just by changing the url. 
 */
exposure.home.header.SubsectionBehavior = Class.create( exposure.Behavior, {

  init: function () {
    this.on( 'click', this.onClick );
  },
  
  onClick: function ( event ) {
    var link = Event.findElement( event, 'a' );
    if ( link ) {
      var hash = link.href.split( '#' )[1];      
      exposure.common.PageState.setStateFromUri( hash );
    }    
    Event.stop( event );
  }
});
  
  
// EOF

_NAMESPACE_( 'exposure.home.header' );


/*public*/
exposure.home.header.SubsectionElement = Class.create( exposure.ViewElement, {

  show: function ( obj ) {
    var state = obj.state;    
    var list = this.getItemList( state );
    this.getDiv().update();
    list.each( function ( item ) {
      if ( this.showItem( item ) ) { 
        var elem = this.createLinkElem( state, item );
        this.selectIfCurrent( elem );
        this.getDiv().insert( elem );
      }
    }.bind( this ) );
  },
    
  hide: function () {},
  
  getItemList: function ( state ) {
    var list = exposure.CONST.KEYWORD_TO_COLL_ID_MAP;    
    var section = this.getCurrentSection( state );
    var ret = [];
    if ( section ) {
      $H( list ).each( function ( item ) {
        if ( item.value.section == section ) {
          ret.push( item );
        }
      });
    }
    return ret;
  },
  
  getCurrentSection: function ( state ) {
    var section = state.getConfigMap();
    if ( !section ) {
      var prefixState = exposure.common.PageState.getPrefixState();
      section = prefixState.getConfigMap();
    }
    if ( section ) {
      return section.section;
    }
    
  },
  
  selectIfCurrent: function ( elem ) {
    if ( location.href.indexOf( elem.href ) !== -1 ) {
      elem.className = 'selected';
    }
  },
  
  showItem: function ( item ) {
    return ( window.username || !item.value.needsUser );
  },
  
  createLinkElem: function ( state, item ) {
    var elem = $(A({ href: this.createLinkUrl( item ) }, item.value.title ));
    return elem;
  },

  createLinkUrl: function ( item ) {
    return '#/' + item.value.urlAnchor.sub( '{userToken}', window.userToken );
  }

});


// EOF

_NAMESPACE_( 'exposure.home' );
  
  
exposure.home.AddCommentBehavior = Class.create( exposure.Behavior, {
  
  init: function () {
    this.delegate( 'click', {
      '.send': this.sendComment,
      '.send .greyButton': this.sendComment,
      '.send .greyButton div': this.sendComment
    });
  },
  
  sendComment: function ( event ) {
    this.submitComment();
    Event.stop( event );
  },
  
  submitComment: function () {
    var div = this.getDiv(); 
    var params = {};
    if ( div.down( '.commentAnon' ) ) {
      params.anonAuthor = $F( div.down( '.anonymous' ) );
    }
    params.email = $F( div.down( '.email' ) );
    params.message = $F( div.down( '.message' ) );
    params.callback = this.submitCommentCallback.bind( this );
    exposure.data.DataHandler.getCurrentImage().addComment( params ); 
    this.showMsg( 'Sending...' );
  },
  
  submitCommentCallback: function ( errorMsg ) {
    if ( errorMsg ) {
      // Error
      this.showMsg( SPAN({ className: 'error' }, errorMsg ) );
    }
    else {
      // Success, clean up
      var nameField = this.getDiv().down( 'input.anonymous' );
      if ( nameField ) {
        nameField.clear();
      }
      this.getDiv().down( 'textarea' ).clear();
      this.showMsg();
    }
  },
  
  showMsg: function( msg ) {
    this.getDiv().down( '.msg' ).update( msg );      
  }
  
});
  
  
// EOF

_NAMESPACE_('exposure.home');


/*public*/
exposure.home.AddCommentElement = Class.create(exposure.ViewElement, {

  /**
   * Lazy initialization. Register observer lazily to enable other registrations
   * to happen before this one. 
   */
  init: function() {
    // Make sure init is executed only once
    this.init = function () {};
    document.observe('image:publicStatusChanged', this.publicStatusChangeCb.bind(this));
  },
  
  publicStatusChangeCb: function(event) {
    this.show(event.memo.obj);
  },
  
  show: function(obj) {
    this.init();
    var div = $(DIV());
    if (obj.isPublic() || obj.isShared()) {
      if (obj.isShared()) {
        div.insert(exposure.view.ElementTemplates.cloneNode('imageIsShared'));
      }
      if (!exposure.common.Account.isUserLoggedIn()) {
        var anonDiv = exposure.view.ElementTemplates.cloneNode('commentAnon');
        var href = anonDiv.down('a').href + exposure.common.getSignInTargetUrl();
        href = this.fixIEHrefContents(href);
        anonDiv.down('a').href = href;
        div.insert(anonDiv);
      }
      div.insert(exposure.view.ElementTemplates.cloneNode('commentMessage'));
    } 
    else {
      div.insert(exposure.view.ElementTemplates.cloneNode('noCommentAddition'));
    }
    this.getDiv().update( div.innerHTML );
  },
  
  hide: function() {
  },
  
  fixIEHrefContents: function(href) {
    // Strange IE problem, the url gets initial content from somewhere
    if (href.startsWith('about:blank')) { // IE6
      href = href.substr('about:blank'.length);
    }
    if (href.startsWith('about:')) { // IE7
      href = href.substr('about:'.length);
    }
    return href;
  }
  
});


// EOF

_NAMESPACE_( 'exposure.home' );
  
  
exposure.home.CommentChangeObserver = Class.create( exposure.Behavior, {
  initialize: function ( $super, div, list ) {
    $super( div );
    this.list = list;
  },
  
  init: function () {
    this.on( 'image:commentChange', this.updateList );
    this.on( 'image:publicStatusChanged', this.updateList );
  },
  
  updateList: function ( event ) {
    this.list.update({ getComments: event.memo.obj.getComments });
  }
  
});
  
  
// EOF

_NAMESPACE_( 'exposure.home' );


/**
 * @constructor
 * @param {Object} div
 * @param {Number} index
 * @extends exposure.Behavior
 */
exposure.home.CommentDeleteBehavior = Class.create( exposure.Behavior, {
  
  initialize: function( $super, div, index ) {
    this.index = index;
    $super( div );
  },

  init: function () {
    this.delegate( 'click', {
      '.trashCan': this.showConfirm,
      '.confirm': this.deleteComment,
      '.cancel': this.cancel
    });
  },
  
	/**
	 * @param {Object} event
	 * @alias exposure.home.CommentDeleteBehavior.prototype.showConfirm
	 */
  showConfirm: function ( event ) {
    var elem = Event.element( event );
    new Effect.Morph( elem.up(), { style: 'width: 100px',duration: 0.2 } );
    Event.stop( event );
  },
  
	/**
	 * @param {Object} event
	 * @alias exposure.home.CommentDeleteBehavior.prototype.deleteComment
	 */
  deleteComment: function ( event ) {
    var div = this.getDiv(); 
    var params = {
      commentIndex: this.index,
      callback: this.deleteCommentCallback.bind( this ) };
    exposure.data.DataHandler.getCurrentImage().deleteComment( params );    
    Event.stop( event );
  },
  
  deleteCommentCallback: function ( errorMsg ) {
    if ( errorMsg ) {
//      this.getDiv().down( '.error' ).update( errorMsg );
    }
    else {
      // else was successful, remove comment from UI    
      this.getDiv().stopObserving();
      var div = this.getDiv().up( '.comment' );
      new Effect.Parallel([
        new Effect.Fade( div ),
        new Effect.BlindUp( div )
      ], { duration: 0.2 } );
      div.remove()
    }
  },
  
  cancel: function ( event ) {
    var elem = Event.element( event ).up( '.trash' );
    new Effect.Morph( elem, { style: 'width: 15px', duration: 0.2 } );
    Event.stop( event );
  }

});


// EOF 


_NAMESPACE_( 'exposure.home' );
  
  
/*public*/
exposure.home.CommentListComponent = {
  createElem: function ( item, params, index ) {
    // Don't show deleted comments
    if ( item.deleted ) { return DIV(); }
    // Clone template node 
    var div = exposure.view.ElementTemplates.cloneNode( 'comment');
    // Populate cloned comment node
    var authorElem = div.down('a');
    if ( item.isVisitor ) {
      // Replace 'a' with 'span'
      var span = SPAN([
        authorElem.down('.avatar'),
        authorElem.down('.author').update(item.author + ' (guest)')
      ]);
      div.replaceChild(span, authorElem);
    } else {
      var userPageUri = 'home.jsp#/user/' + item.authorToken;
      authorElem.href = userPageUri;
      authorElem.down('.author').update(item.author);
      authorElem.down('.avatar').src = item.avatarUrl;
      new exposure.home.grid.UserNameLinkBehavior(authorElem);
    }
    div.down( '.date' ).update( item.date );
    var msg = item.message.gsub( '\n', '<br/>' );
    div.down( '.text' ).update( msg );
    
    // Check if delete comment should be shown
    var trash = div.down( '.trash' );
    trash.hide();
    if ( item.author === exposure.common.Account.getUser() ||
        ( params.getOwner && params.getOwner().getScreenName() &&
         params.getOwner().getScreenName() === exposure.common.Account.getUser() ) ) {
      this.behavior = new exposure.home.CommentDeleteBehavior( trash, index );
      div.observe( 'mouseover', function () { trash.show(); });
      div.observe( 'mouseout', function () { trash.hide(); });
    }
    return div;
  },
  
  update: function ( item, div ) {
    if ( item.deleted) {
      div.parentNode.replaceChild(DIV(), div);
    }
  }
};
  

// EOF


_NAMESPACE_( 'exposure.home' );
  
  
/*public*/
exposure.home.ContactListComponent = {
  createElem: function ( contact, params, index ) {
    var listItem = exposure.view.ElementTemplates.cloneNode( 'contactListItem');
    var contactDisplayName = this.cutLongName( contact.contact );
    var contactName = $(listItem).down('.contactName');
    contactName.update( contactDisplayName );
    // Set tool tip
    contactName.writeAttribute({'title': contact.contact}); 
    
    this.toggleSharedHighlight( listItem, contact ) 
	listItem.id = 'contact_' + contact.refid;
    
    return listItem;
  },

  update: function ( contact, listItem ) {
	  this.toggleSharedHighlight( listItem, contact ) 
  },
  
  toggleSharedHighlight: function ( listItem, contact ) {
	    if ( this.isCurrentImageSharedWith( contact ) ) {
	    	listItem.addClassName( 'sharedWith' );
	    } else {
	    	listItem.removeClassName( 'sharedWith' );
	    }
  },

  cutLongName: function ( name ) {
	    if (name.length >= 27  ) {
	    	return name.substring(0, 24) + '...';
	    } 
	    
	    return name
  },
  
  isCurrentImageSharedWith: function ( contact ) {
	  return exposure.data.DataHandler.getCurrentImage().isSharedWith( contact.refid );
  }  
};


_NAMESPACE_( 'exposure.home' );
  
  
exposure.home.ContactsChangeObserver = Class.create( exposure.Behavior, {
  initialize: function ( $super, div, contactsList ) {
    $super( div );
    this.contactsList = contactsList;
  },
  
  init: function () {
    this.on( 'contacts:contactsReceived', this.updateList );
  },
  
  updateList: function ( event ) {
    this.contactsList.update({ getContacts: function() {
    	return event.memo.contacts;
      } 
    });
  }
  
});
  
  
// EOF

_NAMESPACE_( 'exposure.home' );
  
  
exposure.home.DeleteBehavior = Class.create( exposure.Behavior, {
  
  init: function () {
    this.delegate( 'click', {
      '.showConfirm': this.showConfirmation,
      '.confirm': this.doDelete,
      '.cancel': this.cancel
    });
  },
  
  showConfirmation: function ( event ) {
    this.getDiv().update( 
        exposure.view.ElementTemplates.cloneNode( 'deleteConfirm') );
    Event.stop( event );
  },
  
  doDelete: function ( event ) {
    exposure.data.DataHandler.getCurrentImage().
        updateDeleteStatus({ 'isDeleted': true });
    Event.stop( event );
  },
  
  cancel: function ( event ) {
    this.getDiv().update( 
        exposure.view.ElementTemplates.cloneNode( 'deleteButton') );
    Event.stop( event );
  }
  
});


// EOF


_NAMESPACE_( 'exposure.home' );
  
  
/*public*/
exposure.home.DeleteElement = Class.create( exposure.ViewElement, {

  show: function ( obj ) {
    if ( obj && Object.isFunction( obj.getOwner ) ) {
      if ( obj.getOwner().getScreenName() === username ) {
        this.getDiv().show();
      }
      else {
        this.getDiv().hide();
      }
      
      // Make sure the contents are correct, i.e. the confirmation is not shown
      this.getDiv().update( 
        exposure.view.ElementTemplates.cloneNode( 'deleteButton') );
    }
  }

});


// EOF

_NAMESPACE_( 'exposure.home' );
  
/**  
 * @constructor
 */
exposure.home.FavoriteBehavior = Class.create( exposure.Behavior, {
  
  init: function () {
    this.delegate( 'click', {
      '.favoriteAdd': this.addToFavorites,
      '.favoriteAdd a': this.addToFavorites,
      '.favoriteAdd div': this.addToFavorites,
      '.favoriteRemove': this.removeFromFavorites,
      '.favoriteRemove a': this.removeFromFavorites,
      '.favoriteRemove div': this.removeFromFavorites
    });
  },
  
  addToFavorites: function ( event ) {
    this.updateFavoriteStatus( true );
    Event.stop( event );
  },
  
  removeFromFavorites: function ( event ) {
    this.updateFavoriteStatus( false );
    Event.stop( event );
  },
  
  updateFavoriteStatus: function ( isFavorite ) {
    exposure.data.DataHandler.getCurrentImage().
        updateFavoriteStatus({ 'isFavorite': isFavorite });
  }
  
});


// EOF


_NAMESPACE_( 'exposure.home' );
  
  
/*public*/
exposure.home.FavoriteElement = Class.create( exposure.ViewElement, {

  initialize: function ( $super, div ) {
    $super( div );
    document.observe( 'image:favoriteStatusChanged', 
        this.favoriteStatusChangeCb.bind( this ) );
  },

  show: function ( obj ) {    
    if ( obj && Object.isFunction( obj.getOwner ) ) {      
      this.currentObjId = obj.getId();      
      if ( obj.getOwner().getScreenName() === username ) {
        this.getDiv().show();
      }
      else {
        this.getDiv().hide();
      }
      
      this.showFavoriteStatus( obj );        
    }
  },
  
  favoriteStatusChangeCb: function( event ) {
    var obj = event.memo.obj;
    this.showFavoriteStatus( event.memo.obj );    
  },
  
  showFavoriteStatus: function ( obj ) {
    if ( obj 
      && Object.isFunction( obj.getId ) 
      && Object.isFunction( obj.isFavorite ) 
      && obj.getId() === this.currentObjId ) {      
      var templName = ( obj.isFavorite()? 'favoriteRemove': 'favoriteAdd' );
      this.getDiv().update( 
        exposure.view.ElementTemplates.cloneNode( templName ) );
    }
  }

});


// EOF

_NAMESPACE_( 'exposure.home' );
  
  
/*public*/
exposure.home.ImageElement = Class.create( exposure.ViewElement, {
  
  show: function ( obj ) {
    if ( obj && Object.isFunction( obj.getId ) ) {
      var img = this.getDiv().down( 'img' );
      // show a low resolution version first, propably it is in browser cache 
      img.src = obj.getAbsoluteImageUrl('BROWSER_THUMB');
      img.src = obj.getAbsoluteImageUrl(this.getSize());
      this.setDivOrientation( obj.getOrientation() );
    }
  },
  
  hide: function ( obj ) {
    // "Clear out" the image to indicate something is happening 
    // esp. on slow connections
    var img = this.getDiv().down( 'img' );
    img.src = 'img/empty.gif'; 
  },
  
  getSize: function ( size ) {
    if ( !size ) {
      size = this.getDiv().getWidth();
    }
    return (size < 640? 'MEDIUM': 'LARGE'); 
  },
  
  setDivOrientation: function ( orientation ) {
    var div = this.getDiv();
    if ( div && orientation ) {
      if ( orientation == exposure.CONST.ORIENTATION.LANDSCAPE ) {
        div.addClassName( 'landscape' );
        div.removeClassName( 'portrait' );
      }
      else {
        div.addClassName( 'portrait' );
        div.removeClassName( 'landscape' );
      }
    }
  }
  
});


// EOF

_NAMESPACE_( 'exposure.home.photo' );
  
  
exposure.home.photo.ImageElementBehavior = Class.create( exposure.Behavior, {
  
  initialize: function ( $super, div, imgDiv ) {
    $super( div );
    this.img = imgDiv.down();
  },
  
  init: function () {
    this.on( 'resize', this.checkSize );
  },
  
  checkSize: function ( event ) {
    var src = this.img.src;
    var currentSize = this.img.up().hasClassName( 'landscape' ) ? 
        this.img.getWidth(): 
        this.img.getHeight();
    var size = (currentSize < 640? 'MEDIUM': 'LARGE'); 
    var imgObj = exposure.data.DataHandler.getCurrentImage();
    this.img.src = imgObj.getAbsoluteImageUrl(size);
  }
  
});


// EOF

_NAMESPACE_( 'exposure.home' );
  
  
/*public*/
exposure.home.OwnerElement = Class.create( exposure.ViewElement, {

  show: function ( obj ) {
    if ( obj && Object.isFunction( obj.getOwner ) ) {
      var userPageUri = '#/user/' + obj.getOwner().getToken();
      var ownerName = obj.getOwner().getScreenName() || 'unkown';      
      var userPageLink = A({href: userPageUri, className: 'user' }, ownerName);
      new exposure.home.grid.UserNameLinkBehavior( userPageLink );
      this.getDiv().update( userPageLink);
    }
  }
  
});


// EOF

_NAMESPACE_( 'exposure.home.photo' );

  
/*public*/
exposure.home.photo.PhotoController = Class.create( exposure.common.Controller, {
  IMG_ID: 0,
  
  initialize: function ( $super ) {
    $super();
    document.observe( "image:deleteStatusChanged", this.imageDeleteCallback.bind( this ) );
  },
  
  show: function ( $super, state ) {
    $super( state );
    this.state = state.clone();
    this.imgId = this.getImageId();
    this.showImage();
  },
  
  getImageId: function () {
    return this.state.getParams( this.IMG_ID );
  },

  getCollectionServiceUrl: function () {
    var previousState = exposure.common.PageState.getPrefixState();
    if ( !previousState ) {
      return null;
    }
    var previousPageControllers = exposure.common.PageState.getControllersForState(previousState);
    if(! previousPageControllers) {
      return null;
    } 
    // Get collection service urls from previous page controllers. There should be only one.
    var collectionServiceUrls = previousPageControllers.invoke('getCollectionServiceUrl', previousState);
    return collectionServiceUrls[0];
  },
  
  showImage: function () {
    if ( !exposure.common.isInt( this.imgId ) ) {
      throw new Error( 'Image id is not int (id:"'+this.imgId+'")' );
    }
    exposure.data.DataHandler.getImage({ 
      id: this.imgId, 
      callback: 
        function ( image ) {
          if ( !image ) {
            //DEBUG('No such photo (id:"'+this.imgId+'")');
            this.notifyViews( null );
            return;
          }
          if ( !!this.getCollectionServiceUrl() ) { // Check exists
            exposure.data.DataHandler.getImageIndex({ 
              imgId: image.getId(), 
              collectionServiceUrl: this.getCollectionServiceUrl(),
              callback: 
                function ( index ) {
                  //DEBUG( 'index: ' + index );                    
                  // If found, the collection is cached
                  if ( index != -1 ) {
                    this.index = index;
                    var coll = exposure.data.DataHandler.getCurrentCollection();
                    //DEBUG( 'count: ' + coll.getCount() );
                    this.notifyViews( image, index, coll.getCount() );
                  }
                  else {
                    this.notifyViews( image );
                  }
                }.bind( this )
            });
          }
          else {
            this.notifyViews( image );          
          } 
        }.bind( this ) 
    });
  },
  
  imageDeleteCallback: function ( event ) {
    if ( event.memo.obj.getId && event.memo.obj.getId() === this.getImageId() ) {
      // After the current image has been deleted move to next or previous
      // if possible, if not, get the first link (collection) from the header.
      var coll = exposure.data.DataHandler.getCurrentCollection();
      if ( coll ) {
        var index = this.index;
        if ( index === coll.getCount()-1 ) {
          index = this.index - 1;
        }
        var callback = this.goTo.bind( this, index );
        coll.refresh( callback );
      }
      else {
        var href = $$( '.header a' )[0].href;
        location.href = href;
      }
    }
  },
  
  next: function () {      
    this.moveTo( 
        exposure.data.DataHandler.getNextImageId, 
        { imgId: this.imgId } );
  },  

  previous: function () {
    this.moveTo( 
        exposure.data.DataHandler.getPreviousImageId, 
        { imgId: this.imgId } );
  },

  goTo: function ( index ) { 
    this.moveTo( 
        exposure.data.DataHandler.getImageId, 
        { index: index } );
  },
  
  moveTo: function ( method, params ) {
    // Add general parameters collId and callback and call the provided method
    method.call( exposure.data.DataHandler, Object.extend( params, {
      collectionServiceUrl: this.getCollectionServiceUrl(),
      callback:
        function (id) {
          if ( exposure.common.isInt(id) && id != -1 ) {
            this.state.setParam( this.IMG_ID, id );
            exposure.common.PageState.setState( this.state );
          }
          //TODO: How to handle the error condition, do nothing? Is the state ok?
        }.bind( this )
    }) );
  }
});
  
  
// EOF

_NAMESPACE_( 'exposure.home.photo' );


/*public*/  
exposure.home.photo.PhotoNotFoundView = Class.create( exposure.View, {
  
  getItems: function ( image ) {
    if ( image ) {
      // Image found case is handeled by PhotoView.js
      this.hide();
    }
    else {
      this.showItems();
    }
  }
  
});


// EOF 

_NAMESPACE_('exposure.home.photo');


/*public*/
exposure.home.photo.PhotoView = Class.create(exposure.View, {

  initElements: function () {
    /*
     * commentAddition
     * publishing
     * send link as email
     */
    var div = this.getDiv();
    this.list = new exposure.home.ListElement(div.down('.commentList'), exposure.home.CommentListComponent, 'getComments');
    this.addElement(this.list);
    this.addElement(new exposure.home.TitleElement(div.down('.title')));
    this.addElement(new exposure.home.DescriptionElement(div.down('.description')));
    this.addElement(new exposure.home.SimpleHTMLElement(div.down('.date'), 'getDate'));
    this.addElement(new exposure.home.OwnerElement(div.down('.owner')));
    this.addElement(new exposure.home.ImageElement(div.down('.photo')));
    this.addElement(new exposure.home.AddCommentElement(div.down('.addComment')));
    this.addElement(new exposure.home.FavoriteElement(div.down('.favorite')));
    this.addElement(new exposure.home.DeleteElement(div.down('.delete')));
    this.addElement(new exposure.home.photo.SharingTextElement(div.down('.sharing .text')));
    this.addElement(new exposure.home.photo.PublishElement(div.down('.sharing .publish')));
    this.addElement(new exposure.home.photo.SendLinkElement(div.down('.sharing .link')));
    this.addElement(new exposure.home.photo.ShareInOtherServices(div.down('.sharing .shareInOtherServices')));
    this.addElement(new exposure.home.userpage.AvatarElement(div.down('.avatar')));
    
    this.addElement(new exposure.home.photo.ShareWithContactsElement(div.down('.sharing .shareWithContacts')));
    this.addElement(new exposure.home.photo.ShareWithManyElement(div.down('.sharing .shareWithManyLinks')));
    this.contactsList = new exposure.home.ListElement(div.down('.sharing .shareWithContacts .shareWithContactsList'), exposure.home.ContactListComponent, 'getContacts');
    this.addElement(this.contactsList);
    
  },
  
  initBehaviors: function () {
    var div = this.getDiv();
    this.addBehavior(new exposure.home.AddCommentBehavior(div.down('.addComment')));
    this.addBehavior(new exposure.home.CommentChangeObserver(document, this.list));
    this.addBehavior(new exposure.home.FavoriteBehavior(div.down('.favorite')));
    this.addBehavior(new exposure.home.DeleteBehavior(div.down('.delete')));
    this.addBehavior(new exposure.home.photo.PublishBehavior(div.down('.sharing .publish')));
    this.addBehavior(new exposure.home.email.SendEmailLinkBehavior(div.down('.sharing .link')));
    this.addBehavior(new exposure.home.photo.ToggleContactSharingBehavior(div.down('.sharing .shareWithContacts .shareWithContactsList')));
    this.addBehavior(new exposure.home.photo.ShareWithAllBehavior(div.down('.sharing .shareWithContacts .shareWithAll')));
    this.addBehavior(new exposure.home.photo.ShareWithNoneBehavior(div.down('.sharing .shareWithContacts .shareWithNone')));
    this.addBehavior(new exposure.home.ContactsChangeObserver(document, this.contactsList));
    this.addBehavior(new exposure.home.photo.ShareWithNoneOnPublishObserver(document));
    this.addBehavior(new exposure.home.photo.ImageElementBehavior(window, div.down('.photo')));
  },
  
  getItems: function (image, current, count) {
    if (image) {
      this.image = image;
      current = current ? current + 1 : 1; // +1: start navigation from 1 instead of 0
      count = count || 1;
      Object.extend(image, {
        getCurrent: function() { return current; },
        getTotal: function() { return count; }
      });
      this.showItems(image);
    } else {
      this.hide();
    }
  }
});


// EOF 

_NAMESPACE_( 'exposure.home.photo' );
  
  
exposure.home.photo.PublishBehavior = Class.create( exposure.Behavior, {
  
  init: function () {
	this.on( 'click', this.toggleSharing );
  },
  
  toggleSharing: function( event ) {
	  var listItem = Event.findElement( event, 'li' );
	  if ( this.isPublic( listItem )) {
		this.unpublish( event );
	  } else {
		this.publish( event );
	  }
	  Event.stop( event );
  },
  
  isPublic: function ( listItem ) {
    return listItem && listItem.hasClassName( 'contactListItem' )  && listItem.hasClassName( 'sharedWith' );
  },	  
  
  publish: function ( event ) {
    this.updatePublicStatus( true );
  },
	  
  unpublish: function ( event ) {
    this.updatePublicStatus( false );
  },
  
  updatePublicStatus: function ( isPublic ) {
    exposure.data.DataHandler.getCurrentImage().
        updatePublicStatus({ 'isPublic': isPublic });
  }
  
});


// EOF

_NAMESPACE_( 'exposure.home.photo' );
  
  
/*public*/
exposure.home.photo.PublishElement = Class.create( exposure.ViewElement, {

  initialize: function ( $super, div ) {
    $super( div );
    document.observe( 'image:publicStatusChanged', 
        this.publicStatusChangeCb.bind( this ) );
  },
  
  show: function ( obj ) {
    if ( obj && Object.isFunction( obj.getOwner ) ) {      
      if ( obj.belongsToCurrentUser() ) {
        this.getDiv().show();
        
        
        var listItem = exposure.view.ElementTemplates.cloneNode( 'contactListItem');
        var contactName = $(listItem).down('.contactName');
        
        if ( exposure.common.isPublic(obj) ) {
            contactName.update( 'Unpublish image' );
            contactName.writeAttribute({'title': 'Make image private'}); 

            listItem.addClassName( 'sharedWith' );
        }
        else {
            contactName.update( 'Publish image' );
            contactName.writeAttribute({'title': 'Share image with everyone'}); 
        	
            listItem.removeClassName( 'sharedWith' );
        }

        this.getDiv().update( listItem );
      }
      else {
        this.getDiv().hide();
      }
    }  
  },
  
  publicStatusChangeCb: function( event ) {
    this.show( event.memo.obj );    
  }  
  
});


// EOF

_NAMESPACE_('exposure.home.photo');


/*public*/
exposure.home.photo.SendLinkElement = Class.create(exposure.ViewElement, {

  initialize: function($super, div) {
    $super(div);
    document.observe('image:publicStatusChanged', this.publicStatusChangeCb.bind(this));
  },
  
  publicStatusChangeCb: function(event) {
    this.show(event.memo.obj);
  },
  
  show: function(image) {
    if (exposure.common.Account.isUserLoggedIn() && exposure.common.isPublic(image)) {
      template = exposure.view.ElementTemplates.cloneNode('sendLinkEnabled');
    } else {
      template = exposure.view.ElementTemplates.cloneNode('sendLinkDisabled');
    }
    this.getDiv().update(template);
  }
});


// EOF

_NAMESPACE_('exposure.home.photo');


/*public*/
exposure.home.photo.ShareInOtherServices = Class.create(exposure.ViewElement, {

  initialize: function($super, div) {
    $super(div);
    document.observe('image:publicStatusChanged', this.publicStatusChangeCb.bind(this));
  },
  
  publicStatusChangeCb: function(event) {
    this.show(event.memo.obj);
  },
  
  show: function(obj) {
    var template;
    if (obj && Object.isFunction(obj.isPublic) && obj.isPublic()) {
      var imagePageUlr = location.hostname + '/photo/' + obj.getId();
      template = exposure.view.ElementTemplates.cloneNode('shareInOtherServicesEnabled');
      var link = template.down('a.fb_share_link');
      link.onclick = function() {
        window.open('http://www.facebook.com/sharer.php?u=' + encodeURIComponent(imagePageUlr) + '&t=' + encodeURIComponent(obj.getTitle()), 'sharer', 'toolbar=0,status=0,width=626,height=436');
        return false;
      };
      
    }
    else {
      template = exposure.view.ElementTemplates.cloneNode('shareInOtherServicesDisabled');
    }
    this.getDiv().update(template);
  }
  
});


// EOF

_NAMESPACE_( 'exposure.home.photo' );
  
  
exposure.home.photo.ShareWithAllBehavior = Class.create( exposure.Behavior, {
  
  init: function () {
	this.on('click', this.updateContacts);
  },
  
  updateContacts: function ( event ) {
	  exposure.data.DataHandler.shareCurrentImageWithAllContacts();
	  Event.stop( event );	  
  }
  
});


// EOF

_NAMESPACE_( 'exposure.home.photo' );
  
  
/*public*/
exposure.home.photo.ShareWithContactsElement = Class.create( exposure.ViewElement, {

  initialize: function ( $super, div ) {
    $super( div );
    this.show();
  },
  
  show: function ( image ) {
	if( image && image.belongsToCurrentUser() ) {		
		this.getDiv().show();
		exposure.data.DataHandler.fetchContacts();	  
	} 
	else {
		this.getDiv().hide();				
	}
  },
  
  hide: function ( ) {
	  this.getDiv().hide();
  }
  
});


// EOF
_NAMESPACE_( 'exposure.home.photo' );
  
  
/*public*/
exposure.home.photo.ShareWithManyElement = Class.create( exposure.ViewElement, {

  initialize: function ( $super, div ) {
    $super( div );
    document.observe( 'contacts:contactsReceived', this.show.bind( this ) );
  },
  
  show: function ( ) {
    if ( this.hasContacts() ) {
    	$(this.getDiv()).show();
    } else {
    	$(this.getDiv()).hide();
    }      
  },

  hide: function ( ) {
	  /* Overriden to prevent ViewElement clearing the content  */
  },

  hasContacts: function() {
	var contacts = exposure.data.DataHandler.getContacts();
    return contacts && contacts.length > 0; 
  }  
  
});


// EOF

_NAMESPACE_('exposure.home.photo');


exposure.home.photo.ShareWithNoneBehavior = Class.create(exposure.Behavior, {

  init: function() {
    this.on('click', this.updateContacts);
  },
  
  updateContacts: function(event) {
    exposure.data.DataHandler.shareCurrentImageWithNone();
    Event.stop(event);
  }
  
});


// EOF

_NAMESPACE_( 'exposure.home.photo' );

exposure.home.photo.ShareWithNoneOnPublishObserver = Class.create( exposure.Behavior, {
  initialize: function ( $super, div ) {
    $super( div );
  },
  
  init: function () {
    this.on( 'image:publicStatusChanged', this.removeSharedWithContacts );
  },
  
  removeSharedWithContacts: function ( event ) {
	//DEBUG('ShareWithNoneOnPublishObserver');
	var image = event.memo.obj;
	if( image.isPublic() ) {
		image.setSharingDestinations( new Array() );
		exposure.data.DataHandler.fetchContacts();		
	}
  }
  
});  
  
// EOF

_NAMESPACE_( 'exposure.home.photo' );
  
  
/*public*/
exposure.home.photo.SharingTextElement = Class.create( exposure.ViewElement, {

  initialize: function ( $super, div ) {
    $super( div );
    document.observe( 'image:publicStatusChanged', 
        this.publicStatusChangeCb.bind( this ) );
  },
  
  publicStatusChangeCb: function( event ) {
    this.show( event.memo.obj );    
  },  

  show: function ( image ) {
    this.getDiv().update( exposure.view.ElementTemplates.cloneNode( this.resolveTemplateName( image ) ) );
  },
  
  resolveTemplateName: function( image ) {
    if ( exposure.common.isPublic( image ) ) {
      return 'imageIsPublic';
    } else {
      if ( image.isShared() ) {
	    return 'imageIsShared';
      } else  {    	  
	    return 'imageIsPrivate';
      }
    }
  }  
  
  
});


// EOF

_NAMESPACE_('exposure.home.photo');


exposure.home.photo.ToggleContactSharingBehavior = Class.create(exposure.Behavior, {

  init: function() {
    this.on('click', this.toggleContactSharing);
  },
  
  toggleContactSharing: function(event) {
    var listItem = Event.findElement(event, 'li');
    if (listItem && listItem.hasClassName('contactListItem')) {
      var contactUserId = this.getContactUserId(listItem);
      exposure.data.DataHandler.toggleCurrentImageSharingTo(contactUserId);
    }
    Event.stop(event);
  },
  
  getContactUserId: function(listItem) {
    return listItem.id.split('_')[1];
  }
});


// EOF

_NAMESPACE_( 'exposure.home.manage' );

  
/*public*/
exposure.home.manage.DownloadBehavior = Class.create( exposure.Behavior, {

  init: function () {
    this.delegate( 'click', {
      '.createZip': this.createZip,
      '.createZip div': this.createZip,
      '.cancel': this.cancel,
      '.cancel div': this.cancel
    });
    document.observe( 'manage:updateCounters', this.updateCounters.bind( this ) );
  },
  
  enable: function( $super ) {
    $super();
    this.updateCounters();
  },
  
  updateCounters: function() {
    exposure.data.DataHandler.getDownloadCounters({ 
        callback: this.updateCountersCallback.bind( this ) });
  },
  
  createZip: function ( event ) {
    Event.stop( event );
    this.cancelled = false;
    var all = this.getDiv().down( 'input', 1 ).checked;    
    exposure.data.DataHandler.createDownloadPackage({
        all: all,
        callback: this.createZipCallback.bind( this ),
        errorCallback: this.createZipErrorCallback.bind( this ) });
    this.showTemplate( 'downloadCreatingPackage' );
  },
  
  cancel: function ( event ) {
    Event.stop( event );
    this.cancelled = true;
    this.updateCounters();
  },
  
  updateCountersCallback: function( response ) {
    var date = response.newDate;
    var newCount = response.newCount;
    var allCount = response.allCount;
    var div = this.getDiv();
    
    if ( allCount === 0 ) {
      this.showTemplate( 'downloadNoImages' );
    }
    else {
      this.showTemplate( 'downloadCreatePackage' );
      // Check if "download new images" should be disabled
      if ( newCount === 0 ) {
        div.down( 'input', 0 ).disable();
        div.down( 'input', 1 ).checked = true;
        div.down( 'input', 0 ).up().addClassName( 'disabled' );
      }
      else {
        div.down( 'input', 0 ).enable();
        div.down( 'input', 0 ).checked = true;
        div.down( 'input', 0 ).up().removeClassName( 'disabled' );
      }
      if ( date ) { 
        date = ' since ' + date.split( ' ' )[ 1 ];
      }
      else {
        date = '';
      }
      div.down( 'span', 0 ).update( '(' + newCount + ' images' + date + ')' );
      div.down( 'span', 1 ).update( '(' + allCount + ' images)' );      
    }    
  },
  
  createZipCallback: function ( response ) {
    if ( this.cancelled ) {
      return;
    }
    this.showTemplate( 'downloadPackageReady' );
    this.getDiv().down( 'a' ).href = 'zip.action?file=' + response.file + 
        '&token=' + ajaxToken;
  },
  
  createZipErrorCallback: function ( response ) {
    //DEBUG( response.msg );
    this.showTemplate( 'downloadCreatingPackageError' );    
  },
  
  showTemplate: function ( templateName ) {
    this.getDiv().update( 
        exposure.view.ElementTemplates.cloneNode( templateName ) );
  }
  
});


// EOF

_NAMESPACE_( 'exposure.home.manage' );


/*public*/ 
exposure.home.manage.ManageController = Class.create( exposure.common.Controller, {

  show: function ( $super, state ) {
    $super();
    this.notifyViews( {} );
  }
 
});
  
  
// EOF
 
_NAMESPACE_( 'exposure.home.manage' );


/*public*/  
exposure.home.manage.ManageView = Class.create( exposure.View, {
  
  initBehaviors: function () {
    var div = this.getDiv();

//    this.addBehavior(
//        new exposure.home.manage.DownloadBehavior( div.down( '.download' ) ));
    this.addBehavior(
        new exposure.home.manage.NewDownloadBehavior( div.down( '.download' ) ));

    this.addBehavior(
        new exposure.home.manage.UploadBehavior( div.down() ));
  }
  
});


// EOF 

_NAMESPACE_( 'exposure.home.manage' );

  
/*public*/
exposure.home.manage.NewDownloadBehavior = Class.create( exposure.Behavior, {

  packageCreationPollTimeout: 15, // seconds

  init: function () {
    this.delegate( 'click', {
      '.createZip': this.createZip,
      '.createZip div': this.createZip,
      '.cancel': this.cancel,
      '.cancel div': this.cancel
    });
    document.observe( 'manage:updateCounters', this.updateCounters.bind( this ) );
  },
  
  enable: function( $super ) {
    $super();
    this.updateCounters();
  },
  
  disable: function ( $super ) {
    $super();
    if ( this.timer ) {
      clearTimeout( this.timer );
    }
  },
  
  updateCounters: function() {
    exposure.data.NewArchiveService.getCounters({ 
        callback: this.getCountersCallback.bind( this ) });
  },
  
  createZip: function ( event ) {
    Event.stop( event );
    this.cancelled = false;
    var type = this.getDiv().down( 'input', 1 ).checked? 'total': 'next';
    exposure.data.NewArchiveService.createPackage({
        type: type,
        callback: this.updateCounters.bind( this ),
        errorCallback: this.createZipErrorCallback.bind( this ) });
  },
  
  cancel: function ( event ) {
    Event.stop( event );
    this.cancelled = true;
    delete this.timer;
    this.updateCounters();
  },
  
  getCountersCallback: function( response ) {
    var state = response.state;
    if ( state === 'NONE' || this.cancelled ) {
      this.showCounters( response );
    }
    else if ( state === 'RUNNING' ) {
      this.showTemplate( 'downloadCreatingPackageNew' );
      this.timer = this.updateCounters.bind( this ).delay( this.packageCreationPollTimeout );
    }
    else if ( state === 'COMPLETE' ) {
      delete this.timer;
      this.showDownloadLinks( response );
    }
  },
  
  showCounters: function ( response ) {     
    var date = '';
    if ( response.lastdate ) {
      date = ' since ' + this.formatDate( new Date( response.lastdate ) );
    }
    var newCount = response.next;
    var allCount = response.total;
    var div = this.getDiv();
    
    if ( allCount === 0 ) {
      this.showTemplate( 'downloadNoImages' );
    }
    else {
      this.showTemplate( 'downloadCreatePackage' );
      // Check if "download new images" should be disabled
      if ( newCount === 0 || newCount === allCount ) {
        div.down( 'input', 0 ).disable();
        div.down( 'input', 1 ).checked = true;
        div.down( 'input', 0 ).up().addClassName( 'disabled' );
      }
      else {
        div.down( 'input', 0 ).enable();
        div.down( 'input', 0 ).checked = true;
        div.down( 'input', 0 ).up().removeClassName( 'disabled' );
      }
      div.down( 'span', 0 ).update( '(' + newCount + ' images' + date + ')' );
      div.down( 'span', 1 ).update( '(' + allCount + ' images)' );      
    }    
  },
  
  showDownloadLinks: function ( response ) {
    if ( this.cancelled ) {
      return;
    }
    this.showTemplate( 'downloadPackageReadyNew' );
    response.parts.each( function ( item ) {
      this.div.insert( DIV( 
          A( { href: item }, this.that.getLinkTextFromZipUrl( item ) ) ));
    }, { div: this.getDiv().down('.links'), that: this } );
  },
  
  getLinkTextFromZipUrl: function ( url ) {
    var re = /.*(\d+)_of_(\d+)\..+/;
    var link = url.replace( re, 'Download package $1 of $2' );
    // If no replacement was done, link is the same as the original, i.e. 1 package only
    if ( link === url ) {
      link = 'Download package';
    }
    return link;
  },
  
  createZipErrorCallback: function ( response ) {
    //DEBUG( response.msg );
    this.showTemplate( 'downloadCreatingPackageError' );    
  },
  
  /*
   * Turn Javascript date object to "dd.mm.yyyy".
   */
  formatDate: function ( date ) {
    // Add zero to beggining of number if below 10
    function az( num ) {
      return (num < 10 ? '0' : '') + num; 
    }
    var ret = az( date.getDate() ) + '.' + az( date.getMonth()+1 ) + '.' + 
        date.getFullYear();
    return ret;
  },
    
  showTemplate: function ( templateName ) {
    this.getDiv().update(
        exposure.view.ElementTemplates.cloneNode( templateName ) );
  }
  
});


// EOF

_NAMESPACE_( 'exposure.home.manage' );

  
/*public*/
exposure.home.manage.UploadBehavior = Class.create( exposure.Behavior, {

  init: function () {
    this.queueDiv = this.getDiv().down( '#uploadQueue' );
    // Global callback
    window.uploadCallback = this.onUploadFinish.bind( this );
  },
  
  enable: function () {
    this.getDiv().down( '#uploadElem' ).update( this.createUploadElem() );
  },
  
  disable: function () {
    this.getDiv().down( '#uploadElem' ).update();
  },

  createUploadElem: function() {
    var fileElem = $(INPUT( { type: 'file', name: 'imgFile' } ));
    fileElem.observe( 'change', this.onFileElemChange.bindAsEventListener( this ) );
    return fileElem;
  },
  
  onFileElemChange: function ( event ) {
    var elem = Event.element( event );
    elem.stopObserving( 'change' );
    elem.parentNode.replaceChild( this.createUploadElem(), elem );
    this.addToQueue( elem );
  },
  
  
  addToQueue: function ( fileElem ) {
    fileElem.hide();
    var div = $(DIV([
      SPAN( { className: 'uploadQueueElem_status' }, [
        DIV( { className: 'load' } ),
        DIV( { className: 'wait clickable' } )
      ]),
      SPAN( { className: 'uploadQueueElem_wait' }, fileElem.value ),
      fileElem
    ]));
    div.hide();
    div.down( '.load' ).hide();
    this.queueDiv.insert( div );
    new Effect.Parallel([
        new Effect.BlindDown( div ),
        new Effect.Appear( div, { from: 0.5 } )
      ], { duration: 0.3 } );
    var timerId = this.startUploading.bind( this, div).delay( 3 );
    div.down( '.wait' ).id = timerId;      
    div.down( '.wait' ).observe( 'click', this.trashFromQueue.bindAsEventListener( this ) );
  },

  startUploading: function ( div ) {
    // Not uploading, upload given div
    if ( !this.uploading && div ) {
      this.uploading = true;
      var waitImg = div.down( '.wait' );
      waitImg.stopObserving( 'click' );
      waitImg.removeClassName( 'clickable' );
      waitImg.hide();
      div.down( '.load' ).show()
      div.down( 'span.uploadQueueElem_wait' ).className = 'uploadQueueElem_load';
      
      var form = $('uploadForm');
      // Remove old input elements
      var oldInput = form.down( 'input' );
      while ( oldInput ) {
        if ( oldInput.name == 'imgFile' ) {
          oldInput.remove();
        }
        oldInput = oldInput.next();
      }
      var input = div.down( 'input' );
      input.remove(); // Remove new input element from div
      form.insert( input );
      (function () {
        form.submit();
      }).delay( 1 );
    }
    // Not uploading, upload the first from queue
    else if ( !this.uploading ) {
      var queuedElems = $$( 'span.uploadQueueElem_wait' );
      if ( queuedElems.length ) {
        this.startUploading( queuedElems[0].up() );
      }
    }
  },
  
  onUploadFinish: function ( params ) {
    try {
      // Remove the first element
      var div = this.queueDiv.down( '.uploadQueueElem_load' ).up();
      var statusDiv = div.down();
      statusDiv.next().className = 'uploadQueueElem_done';
      statusDiv.setStyle( { visibility: 'hidden' } );
      div.remove.bind( div ).delay( 1 );
      
      if ( params.success ) {
        // Fire an event to notify of the need to update the counters
        this.showMsg();
        document.fire( 'manage:updateCounters' );
      }
      else {
        if ( params.msg == 'noUser' ) {
          //TODO: redirect to login page?
          //DEBUG( params.msg );
          return;
        }
        else if ( params.msg == 'noFile' ) {
          //TODO: handle this properly
          //DEBUG( params.msg ); 
        }
        else {
          this.showMsg( params.msg );
        }
  		}
    } 
    finally {
      this.uploading = false;
      this.startUploading();      
    }
  },
  
  showMsg: function ( msg ) {
    if ( !msg ) { msg = ''; }
    this.getDiv().down( '.error' ).update( msg );
  },
  
  trashFromQueue: function ( event ) {
    var trashElem = Event.element( event );
    window.clearTimeout( trashElem.id );
    trashElem.stopObserving( 'click' );
    trashElem.up( 'div' ).remove();
  }
      
});


// EOF

_NAMESPACE_( 'exposure.home.email' );


/*public*/  
exposure.home.email.EmailSharingView = Class.create( exposure.View, {
  
  initBehaviors: function () {
    var div = this.getDiv();
    this.addBehavior(new exposure.home.email.SendEmailBehavior(div));
    this.addBehavior(new exposure.home.email.ToFieldManager(div));
  }
  
});


// EOF 
_NAMESPACE_('exposure.home.email');

/*public*/
exposure.home.email.MessageTextareaBehavior = Class.create(exposure.Behavior, {

  init: function() {
    this.on('focus', this.onfocus);
  },
  
  onfocus: function(event) {
    var div = this.getDiv();
    if(div.hasClassName('waitingForFirstInput')) {
      div.update();
      div.removeClassName('waitingForFirstInput');
    }
  }

});


// EOF
_NAMESPACE_('exposure.home.email');


/*public*/
exposure.home.email.SendEmailBehavior = Class.create(exposure.Behavior, {

  init: function() {
    this.delegate('click', {
      '.send': this.sendEmail,
      '.send div': this.sendEmail
    });
  },
  
  sendEmail: function(event) {
    Event.stop(event);
    if (this.isOkToSendEmail()) {
      exposure.data.EmailService.sendImageSharingMessage({
        sharedImage: exposure.data.DataHandler.getCurrentImage(),
        json: Object.toJSON(this.getEmailObject()),
        callback: this.callback.bind(this),
        errorCallback: this.errorCallback.bind(this)
      });
      this.setStatus('Sending...');
    }
  },
  
  getEmailObject: function() {
    return {
      recipients: this.getRecipients(),
      usersMessage: this.getMessage()
    };
  },
  
  callback: function(response) {
    //DEBUG('SendEmailBehavior ' + response);
    this.setStatus('Your message has been sent!');
    var popup = $$('.screen')[0]; 
    Element.fade.delay(3, popup, { duration: 0.3 });
  },
  
  errorCallback: function(response) {
    //DEBUG(response);
    if(response.error && response.error == 'tooManyMessages') {
      this.setErrorStatus('Email not sent. Sorry! You have sent too many messages today.');
    } else {      
      this.setErrorStatus('error');
    }
  },
  
  getRecipients: function () {
    var recipients = $$('.toField input[name=recipient]').pluck('value');
    var inputValue = $$('.toField input[name=toFieldInput]')[0].getValue();
    if (inputValue) {
      recipients.push(inputValue);
    } 
    return recipients;
  },
  
  getMessage: function () {
    var textarea = $$('.emailSharingOverlay textarea')[0];
    if (textarea.hasClassName('waitingForFirstInput')) {
      return "";
    }
    return textarea.value;
  },
  
  isOkToSendEmail: function () {
    var input = $$('.toField input[name=toFieldInput]')[0];
    var inputValue = input.getValue();
    if (!inputValue.blank() && !exposure.common.isEmailSyntaxValid(inputValue)) {
      new Effect.Highlight(input, {startcolor: '#E40E62'});
      return false;
    }
    return true;
  },
  
  setStatus: function (msg) {
    var statusDiv = this.getDiv().down('.status');
    statusDiv.update(msg);
  },
  
  setErrorStatus: function (msg) {
    var statusDiv = this.getDiv().down('.status');
    statusDiv.update(msg);
    statusDiv.addClassName('error');
  }
  
});

// EOF 

_NAMESPACE_('exposure.home.email');


/*public*/
exposure.home.email.SendEmailLinkBehavior = Class.create(exposure.home.AbstractOverlayBehavior, {

  initBehaviour: function() {
    this.on('click', this.openEmailSharingOverlay);
  },
  
  
  openEmailSharingOverlay: function(event) {
    if (event) {
      Event.stop(event);
    }
    if(this.getDiv().down('.sendLinkDisabled')) {
      return;
    }
    
    this.setupOverlay();
    var div = exposure.view.ElementTemplates.cloneNode('emailSharingOverlay');
    this.stopFormFromBeingSubmittedOnEnter(div);
    new exposure.home.email.ToFieldInputBehavior(div.down('.emailAddressInput'));
    new exposure.home.email.MessageTextareaBehavior(div.down('textarea'));
    
    var image = exposure.data.DataHandler.getCurrentImage();
    var previewImage = div.down('.previewImage')
    previewImage.src = image.getAbsoluteImageUrl('SMALL');
    if(image.getTitle()) {
      previewImage.alt = image.getTitle();    
    }
    div.down('.imageTitle').update(image.getTitle());
    div.down('.imagePageLink a').href = image.getAbsoluteImagePageUrl();    
    
    this.view.update(div);
    this.showOverlay();
  },
  
  // TODO: Remove this method when all the popups look the same
  setupOverlay: function() {
    this.view.parentNode.addClassName('emailSharingScreen');
    this.view.setStyle({
      'width': '41em'
    });
    var backgroundDiv = this.view.previous('.bg');
    backgroundDiv.setStyle({
      'backgroundColor': '#555555',
      opacity: 0.65
    });
    this.view.previous('.overlayCenterer').setStyle({
      'marginTop': '-140px'
    });
  },
  
  stopFormFromBeingSubmittedOnEnter: function(div) {
    div.down('form').observe('submit', function(event) {
      event.preventDefault();
    });
  }
  
});


// EOF

_NAMESPACE_( 'exposure.home.email' );
  
  
/*public*/
exposure.home.email.ToFieldElement = Class.create({

  initialize: function (recipient) {
    if (!recipient) {
      throw new Error('Recipient should not be empty');
    }
    this.createElem(recipient);
    this.addClickListener();
  },
  
  createElem: function (recipient) {
    this.elem = exposure.view.ElementTemplates.cloneNode('toFieldElement');
    this.elem.down('span').update(recipient);
    this.elem.down('input').value = recipient;
    return this.elem;
  },
  
  addClickListener: function() {
    this.clickListener = this.removeFromDOM.bind(this);
    this.elem.down('a').observe('click', this.clickListener);
  },
  
  getElem: function() {
    return this.elem;
  },
  
  removeFromDOM: function (event) {
    Event.stop(event);
    this.elem.down('a').stopObserving(this.clickListener);
    this.elem.remove(); 
  }

});


// EOF
_NAMESPACE_('exposure.home.email');

/*public*/
exposure.home.email.ToFieldInputBehavior = Class.create(exposure.Behavior, {

  init: function() {
    this.on('keyup', this.onkeyup);
    this.on('focus', this.onfocus);
  },
  
  onfocus: function(event) {
    if (this.getDiv().hasClassName('waitingForFirstInput')) {
      //DEBUG('EmailAddressInputBehavior clear on focus ');
      this.getDiv().value = '';
      this.getDiv().removeClassName('waitingForFirstInput');
    }
  },
  
  onkeyup: function(event) {
    var key = event.keyCode;
    if (key && key == Event.KEY_RETURN) {
      this.addRecipients();
    }
    var input = this.getDiv();
    if (input.value.indexOf(',') >= 0) {
      this.addRecipients();
    }
    input.size = Math.max(2, input.value.length + 2);
  },
  
  addRecipients: function() {
    var inputValue = this.getDiv().value;
    var recipients = inputValue.split(',');
    recipients = recipients.invoke('strip'); // trim each value
    recipients.each(function(recipient, index) {
      //DEBUG('recipient ' + recipient);
      if (recipient.blank()) {
        // Remove empty values
        delete recipients[index];
      } else if (exposure.common.isEmailSyntaxValid(recipient)) {
        document.fire('tofield:addElement', {
          recipient: recipient
        });
        // Remove valid address from input value
        delete recipients[index];
      }
    });
    var inputValue = recipients.compact().join(', ');
    this.getDiv().value = inputValue;
    if (!inputValue.blank()) {
      new Effect.Highlight(this.getDiv(), {startcolor: '#E40E62'});
    }
  }
  
});


// EOF
_NAMESPACE_('exposure.home.email');


/*public*/
exposure.home.email.ToFieldManager = Class.create(exposure.Behavior, {

  init: function () {
    document.observe('tofield:addElement', this.addRecipient.bind(this));
    this.delegate('click', {
      '.toField': this.focusToField
    });
  },
  
  addRecipient: function (event) {
    if (this.disabled) { return; }
    Event.stop(event);
    var recipient = event.memo.recipient;
    if (this.hasDuplicate(recipient)) {
      this.highlightDuplicate(recipient);
    } else {
      this.addRecipientToDOM(recipient);
    }
  },
  
  addRecipientToDOM: function(recipient) {
    var toFieldInput = this.getDiv().down('.toField input.emailAddressInput');
    if (!recipient || !toFieldInput) {
      throw new Error('Missing recipient or toFieldInputElement: ' + recipient  + ' ' + toFieldInput);
    }
    var recipientElem = new exposure.home.email.ToFieldElement(recipient).getElem();
    toFieldInput.insert({'before': recipientElem});
  },
  
  getDOMElementForRecipient: function (recipient) {
    var elem = $$('.toFieldElement input[value='+recipient+']')[0];
    return elem;
  },
  
  hasDuplicate: function (recipient) {
    return !!this.getDOMElementForRecipient(recipient);
  },
  
  highlightDuplicate: function (recipient) {
    var elem = this.getDOMElementForRecipient(recipient);
    if (elem) {
      elem = elem.up('.toFieldElement');
      elem.highlight({startcolor: '#E40E62'});
    }
  },
  
  focusToField: function (event) {
    this.getDiv().down('input.emailAddressInput').focus();
  }

});

// EOF 

/**
 * login.js
 * 
 * (c) Nokia Research Center, 2008
 * Toni Strandell
 */

function main_login() {

  Builder.dump();

  new LoginFormBehavior( $$( '.login form' )[0] );
  var forgotPaswordElem = $$( '.forgotPasswordLink a' )[0];
  initFooterBehavior( {forgotElem: forgotPaswordElem} );
}



/*
 * Put focus to the username field on load. 
 * Make the submit button submit the form.
 */
var LoginFormBehavior = Class.create( exposure.Behavior, {
  
  init: function () {
    this.getDiv().down( 'input' ).activate();
    this.getDiv().down( '.yellowButton' ).observe( 'click', this.onSubmit.bind( this ) );
  },
  
  onSubmit: function ( event ) {
    this.getDiv().submit();
    Event.stop( event );    
  }
    
});


// EOF

_NAMESPACE_( 'exposure.tour' );
  
  
exposure.tour.TourHighlightBehavior = Class.create( exposure.Behavior, {
  
  init: function () {
    this.hideTextElem();
    this.on( 'click', this.showHighlight );
  },
  
  showHighlight: function ( event ) {    
    var elem = Event.element( event );
    this.hideHighlights();
    this.showTextElem();
    Event.stop( event );
  },
  
  hideHighlights: function () {
    $$( '.highlights div.text' ).invoke( 'hide' );
  },
  
  hideTextElem: function () {
    this.getTextElem().hide();
  },
  
  showTextElem: function () {
    this.getTextElem().appear({ duration: 0.3 });
  },
  
  getTextElem: function () {
    var href = this.getDiv().href;
    var elemId = href.substring( href.lastIndexOf( '/' ) + 1 );    
    return $( elemId );
  }
  
});


// EOF 


_NAMESPACE_('exposure.settings');


/*public*/
exposure.settings.AvatarUploadBehavior = Class.create(exposure.Behavior, {

  init: function() {
    this.form = $$('.avatarUploadForm')[0];
    this.iframe = $$('iframe')[0];
    var fileInput = this.getDiv().down('.avatarFileInput');
    this.statusDiv = $$('.avatarInput .error')[0];
    this.addFileSelectedObserver(fileInput);
  },
  
  addFileSelectedObserver: function(input) {
    $(input).observe('change', this.uploadFile.bind(this));
  },
  
  createNewUploadElem: function() {
    var input = INPUT({
      type: 'file',
      className: 'avatarFileInput',
      name: 'avatarFile'
    });
    this.addFileSelectedObserver(input);
    return input;
  },
  
  uploadFile: function(event) {
    Event.stop(event);
    if (!this.checkHasValidFileExtension()) {
      this.statusDiv.update('Please use a .jpg or .jpeg file');
      return;
    }
    var elem = Event.element(event);
    elem.parentNode.replaceChild(this.createNewUploadElem(), elem);
    elem.stopObserving('change');
    this.submitForm(elem);
  },
  
  checkHasValidFileExtension: function() {
    var fileName = $$('.avatarInput input[type=file]')[0].getValue();
    var extension = fileName.substring(fileName.lastIndexOf('.') + 1);
    return extension.toLowerCase() === 'jpg' || extension === 'jpeg';
  },
  
  submitForm: function(fileElem) {
    var currentInput = this.form.down('input');
    if (currentInput) {
      currentInput.remove();
    }
    //DEBUG('submitForm ' + this.form);
    this.statusDiv.update('Uploading new avatar image...');
    this.iframe.observe('load', this.avatarUploadedCallback.bind(this));
    this.form.insert(fileElem);
    this.form.submit();
  },
  
  avatarUploadedCallback: function(event) {
    this.iframe.stopObserving();
    var value = this.iframe.contentWindow.document.body.firstChild.innerHTML;
    try {
      var json = value.evalJSON(true);
    } catch (e) {
      this.checkWebServerError();
      return;
    }
    if (json.status) {
      this.statusDiv.update('');
      $$('#settingsDisplay .avatar img')[0].src = json.largeAvatarUrl;
    } else if (json.error) {
      if (json.error === 'Processing error') {
        this.statusDiv.update('Please use .jpg or .jpeg file');
      } else {
        this.statusDiv.update('Error: ' + json.error);
      }
    }
  },
  
  checkWebServerError: function() {
    var title = this.iframe.contentWindow.document.getElementsByTagName('title')[0];
    var statusText = 'There was an error uploading the profile image.<br/>' +
    'Please refresh the page and try again.';
    if (title.innerHTML.indexOf('413') === 0) {
      statusText = 'The profile image can not be bigger than 1 MB';
    }
    this.statusDiv.update(statusText);
  }
  
});


// EOF


_NAMESPACE_( 'exposure.settings' );

  
/*public*/
exposure.settings.DescriptionInputBehavior = Class.create( exposure.Behavior, {

  LIMIT: 512,

  init: function () {
    this.on('keyup', this.checkLength);
  },
  
  checkLength: function (event) {
    var value = this.getDiv().getValue();
    if (value.length > this.LIMIT) {
      this.getDiv().setValue(value.substring(0,this.LIMIT));
    }
  }  
  
});


// EOF


_NAMESPACE_( 'exposure.settings' );

  
/*public*/
exposure.settings.FormBehavior = Class.create( exposure.Behavior, {
  
  messages: {
    usernameTaken: 'The username is already reserved, please choose another one.',
    usernameSyntax: 'The username should contain only letters and numbers<br/>and be at least 3 characters long.'
  },  

  init: function () {
    this.on( 'submit', this.onSubmit );
    $$( '.submit a.save' )[0].observe( 'click', this.onSubmit.bind( this ) );
  },
  
  onSubmit: function ( event ) {
    Event.stop( event );    
    var data = this.getDiv().serialize( true );
    // Add checkbox value as boolean
    data.allowCommentNotifications = this.getDiv().down( '.check input' ).checked;
    data.allowEmailSending = this.getDiv().down( '.check input', 1 ).checked;
    Object.extend( data, { callback: this.onSubmitCallback.bind( this ) } );
    exposure.data.DataHandler.saveProfile( data )
  },
  
  onSubmitCallback: function ( response ) {
    this.showMsg( response.error );
  },
  
  showMsg: function ( error ) {
    var message = '&nbsp;';
    if ( error === undefined ) {
      // Take "back to images" link and add parameter to notify of saved profile
      var url = this.getDiv().down( '.back' ).href;
      url = url.sub( '.jsp', '.jsp?s=1' )
      location.href = url;
    }
    else  {
      var message = this.messages[ error ];      
    }
    this.getDiv().down( '.error' ).update( message );
  }
  
});


// EOF


_NAMESPACE_( 'exposure.settings' );

  
/*public*/
exposure.settings.UsernameBehavior = Class.create( exposure.Behavior, {
  
  messages: exposure.settings.FormBehavior.prototype.messages,  

  init: function () {
    this.on( 'change', this.onChange );    
  },
  
  onChange: function ( event ) {
    Event.stop( event );
    var username = $F( this.getDiv() );
    if ( username.length === 0 ) {
      this.showMsg({ error: 'usernameSyntax' });
    }
    else {
      var data = {
        username: username,
        callback: this.onChangeCallback.bind( this )
      };
      this.showMsg(); // Clear the message
      exposure.data.DataHandler.checkUsername( data )
    }
  },
  
  onChangeCallback: function ( response ) {
    this.showMsg( response );
  },
  
  showMsg: function ( response ) {
    var message = '&nbsp;';
    if ( response && response.error !== undefined ) {
      var message = this.messages[ response.error ];      
    }
    this.getDiv().up( 'form').down( '.error' ).update( message );
  }
  
});


// EOF
