eval(function(p,a,c,k,e,r){e=function(c){return(c<a?'':e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--)r[e(c)]=k[c]||e(c);k=[function(e){return r[e]}];e=function(){return'\\w+'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p}('E 14={8s:\'1.6.cy\',1J:{3s:!!(1k.6b&&!1k.7g),4e:!!1k.7g,5p:4n.4F.2T(\'fI/\')>-1,6v:4n.4F.2T(\'6v\')>-1&&4n.4F.2T(\'ap\')==-1,ae:!!4n.4F.1l(/dY.*dq.*9t/)},2J:{62:!!V.2p,5g:!!1k.4I,6M:V.3j(\'2t\').4z!==V.3j(\'83\').4z},6x:\'<5d[^>]*>([\\\\S\\\\s]*?)<\\/5d>\',am:/^\\/\\*-eM-([\\s\\S]*)\\*\\/\\s*$/,3i:q(){},K:q(x){o x}};C(14.1J.ae)14.2J.6M=1f;E 1y={2d:q(a,b){C(1b.L==1&&!I.1M(a))b=a,a=19;E c=q(){C(!1y.5f)B.2w.2V(B,1b)};c.ga=a;c.5u=[];C(I.1M(a)){1y.5f=1d;c.O=1c a();a.5u.W(c);5n 1y.5f}C(b)1y.M(c,b);c.O.84=c;o c},M:q(a,b){N(E c 1t b)1y.7W(a,b,c);o a},7W:q(a,b,c){E d=a.O,7P=d[c],28=b[c];C(7P&&I.1M(28)&&28.aA().2F()=="$f8"){E e=28,28=7P.6L(e);I.M(28,{eL:q(){o e},24:q(){o e.24()}})}d[c]=28;C(a.5u&&a.5u.L>0){N(E i=0,49;49=a.5u[i];i++){1y.5f=1d;I.M(49.O,1c a());49.O.84=49;5n 1y.5f;1y.7W(49,a.O,c)}}},ee:q(a,b){o I.M(a,b)}};E 2W={};I.M=q(a,b){N(E c 1t b){a[c]=b[c]}o a};I.M(I,{1T:q(a){1N{C(a===1q)o\'1q\';C(a===19)o\'19\';o a.1T?a.1T():a.24()}1R(e){C(e 6T dn)o\'...\';3u e;}},26:q(a){E b=3g a;4Y(b){2c\'1q\':2c\'q\':2c\'d6\':o;2c\'d2\':o a.24()}C(a===19)o\'19\';C(a.26)o a.26();C(I.2C(a))o;E c=[];N(E d 1t a){E e=I.26(a[d]);C(e!==1q)c.W(d.26()+\': \'+e)}o\'{\'+c.1Y(\', \')+\'}\'},2G:q(a){o a&&a.2G?a.2G():1F.4N(a)},5N:q(a){E b=[];N(E c 1t a)b.W(c);o b},2R:q(a){E b=[];N(E c 1t a)b.W(a[c]);o b},2l:q(a){o I.M({},a)},2C:q(a){o a&&a.2u==1},3d:q(a){o a&&a.84===1U},1M:q(a){o 3g a=="q"},2q:q(a){o 3g a=="6B"},6j:q(a){o 3g a=="bV"},8R:q(a){o 3g a=="1q"}});I.M(7Q.O,{aA:q(){E a=B.24().1l(/^[\\s\\(]*q\\s*\\((.*?)\\)/)[1].3V(",").6A("2K");o a.L==1&&!a[0]?[]:a},1i:q(){C(1b.L<2&&1b[0]===1q)o B;E a=B,2a=$A(1b),2I=2a.4E();o q(){o a.2V(2I,2a.1z($A(1b)))}},g5:q(){E b=B,2a=$A(1b),2I=2a.4E();o q(a){o b.2V(2I,[a||1k.g4].1z(2a))}},8o:q(){C(!1b.L)o B;E a=B,2a=$A(1b);o q(){o a.2V(B,2a.1z($A(1b)))}},8k:q(){E a=B,2a=$A(1b),be=2a.4E()*8h;o 1k.fZ(q(){o a.2V(a,2a)},be)},6L:q(a){E b=B;o q(){o a.2V(B,[b.1i(B)].1z($A(1b)))}},3C:q(){C(B.8e)o B.8e;E a=B;o B.8e=q(){o a.2V(19,[B].1z($A(1b)))}}});7Q.O.2L=7Q.O.8k.8o(0.fQ);fL.O.26=q(){o\'"\'+B.fJ()+\'-\'+(B.fH()+1).3E(2)+\'-\'+B.fC().3E(2)+\'T\'+B.fv().3E(2)+\':\'+B.ft().3E(2)+\':\'+B.fr().3E(2)+\'"\'};E aP={aL:q(){E a;N(E i=0,L=1b.L;i<L;i++){E b=1b[i];1N{a=b();1E}1R(e){}}o a}};3c.O.1l=3c.O.2o;3c.fl=q(a){o 1F(a).1w(/([.*+?^=!:${}()|[\\]\\/\\\\])/g,\'\\\\$1\')};E fe=1y.2d({2w:q(a,b){B.4t=a;B.3S=b;B.6l=1f;B.3Z()},3Z:q(){B.4p=7X(B.3M.1i(B),B.3S*8h)},7D:q(){C(!B.4p)o;ad(B.4p);B.4p=19},3M:q(){C(!B.6l){1N{B.6l=1d;B.4t(B)}eE{B.6l=1f}}}});I.M(1F,{4N:q(a){o a==19?\'\':1F(a)},a8:{\'\\b\':\'\\\\b\',\'\\t\':\'\\\\t\',\'\\n\':\'\\\\n\',\'\\f\':\'\\\\f\',\'\\r\':\'\\\\r\',\'\\\\\':\'\\\\\\\\\'}});I.M(1F.O,{2i:q(a,b){E c=\'\',3D=B,1l;b=1b.3G.7y(b);1v(3D.L>0){C(1l=3D.1l(a)){c+=3D.3m(0,1l.4M);c+=1F.4N(b(1l));3D=3D.3m(1l.4M+1l[0].L)}15{c+=3D,3D=\'\'}}o c},9Q:q(b,c,d){c=B.2i.7y(c);d=d===1q?1:d;o B.2i(b,q(a){C(--d<0)o a[0];o c(a)})},9O:q(a,b){B.2i(a,b);o 1F(B)},dT:q(a,b){a=a||30;b=b===1q?\'...\':b;o B.L>a?B.3m(0,a-b.L)+b:1F(B)},2K:q(){o B.1w(/^\\s+/,\'\').1w(/\\s+$/,\'\')},9L:q(){o B.1w(/<\\/?[^>]+>/9J,\'\')},2A:q(){o B.1w(1c 3c(14.6x,\'9G\'),\'\')},9E:q(){E b=1c 3c(14.6x,\'9G\');E c=1c 3c(14.6x,\'dm\');o(B.1l(b)||[]).2k(q(a){o(a.1l(c)||[\'\',\'\'])[1]})},3p:q(){o B.9E().2k(q(a){o 5X(a)})},5V:q(){E a=1b.3G;a.2S.dg=B;o a.2t.3J},9s:q(){E c=1c G(\'2t\');c.3J=B.9L();o c.2Z[0]?(c.2Z.L>1?$A(c.2Z).2N(\'\',q(a,b){o a+b.79}):c.2Z[0].79):\'\'},5W:q(e){E f=B.2K().1l(/([^?#]*)(#.*)?$/);C(!f)o{};o f[1].3V(e||\'&\').2N({},q(a,b){C((b=b.3V(\'=\'))[0]){E c=9l(b.4E());E d=b.L>1?b.1Y(\'=\'):b[0];C(d!=1q)d=9l(d);C(c 1t a){C(!I.3d(a[c]))a[c]=[a[c]];a[c].W(d)}15 a[c]=d}o a})},2r:q(){o B.3V(\'\')},73:q(){o B.3m(0,B.L-1)+1F.cX(B.9h(B.L-1)+1)},5P:q(a){E b=\'\';N(E i=0;i<a;i++)b+=B;o b},71:q(){E a=B.3V(\'-\'),7r=a.L;C(7r==1)o a[0];E b=B.5K(0)==\'-\'?a[0].5K(0).25()+a[0].4a(1):a[0];N(E i=1;i<7r;i++)b+=a[i].5K(0).25()+a[i].4a(1);o b},5G:q(){o B.5K(0).25()+B.4a(1).1I()},cH:q(){o B.2i(/::/,\'/\').2i(/([A-Z]+)([A-Z][a-z])/,\'#{1}54#{2}\').2i(/([a-z\\d])([A-Z])/,\'#{1}54#{2}\').2i(/-/,\'54\').1I()},cB:q(){o B.2i(/54/,\'-\')},1T:q(c){E d=B.2i(/[\\cx-\\ct\\\\]/,q(a){E b=1F.a8[a[0]];o b?b:\'\\\\cq\'+a[0].9h().3E(2,16)});C(c)o\'"\'+d.1w(/"/g,\'\\\\"\')+\'"\';o"\'"+d.1w(/\'/g,\'\\\\\\\'\')+"\'"},26:q(){o B.1T(1d)},6S:q(a){o B.9Q(a||14.am,\'#{1}\')},ac:q(){E a=B.1w(/\\\\./g,\'@\').1w(/"[^"\\\\\\n\\r]*"/g,\'\');o(/^[,:{}\\[\\]0-9.\\-+c9-u \\n\\r\\t]*$/).2o(a)},4r:q(a){E b=B.6S();1N{C(!a||b.ac())o 5X(\'(\'+b+\')\')}1R(e){}3u 1c c2(\'bY bU 8T 6B: \'+B.1T());},1p:q(a){o B.2T(a)>-1},7L:q(a){o B.2T(a)===0},8P:q(a){E d=B.L-a.L;o d>=0&&B.6O(a)===d},5k:q(){o B==\'\'},5i:q(){o/^\\s*$/.2o(B)},bI:q(a,b){o 1c 2e(B,b).2p(a)}});C(14.1J.5p||14.1J.3s)I.M(1F.O,{5V:q(){o B.1w(/&/g,\'&b1;\').1w(/</g,\'&b2;\').1w(/>/g,\'&8F;\')},9s:q(){o B.1w(/&b1;/g,\'&\').1w(/&b2;/g,\'<\').1w(/&8F;/g,\'>\')}});1F.O.2i.7y=q(b){C(I.1M(b))o b;E c=1c 2e(b);o q(a){o c.2p(a)}};1F.O.bu=1F.O.5W;I.M(1F.O.5V,{2t:V.3j(\'2t\'),2S:V.8D(\'\')});8C(1F.O.5V)2t.44(2S);E 2e=1y.2d();2e.8A=/(^|.|\\r|\\n)(#\\{(.*?)\\})/;2e.O={2w:q(a,b){B.8x=a.24();B.8v=b||2e.8A},2p:q(f){C(I.1M(f.bp))f=f.bp();o B.8x.2i(B.8v,q(a){C(f==19)o\'\';E b=a[1]||\'\';C(b==\'\\\\\')o a[2];E c=f,5v=a[3];E d=/^([^.[]+|\\[((?:.*?[^\\\\])?)\\])(\\.|\\[|$)/,a=d.bm(5v);C(a==19)o\'\';1v(a!=19){E e=a[1].7L(\'[\')?a[2].2i(\'\\\\\\\\]\',\']\'):a[1];c=c[e];C(19==c||\'\'==a[3])1E;5v=5v.4a(\'[\'==a[3]?a[1].L:a[0].L);a=d.bm(5v)}o b+1F.4N(c)}.1i(B))}};E $1E={};E 22={1h:q(b,c){E d=0;b=b.1i(c);1N{B.2s(q(a){b(a,d++)})}1R(e){C(e!=$1E)3u e;}o B},bj:q(a,b,c){b=b?b.1i(c):14.K;E d=-a,8r=[],8q=B.2r();1v((d+=a)<8q.L)8r.W(8q.3m(d,d+a));o 8r.8n(b,c)},8m:q(c,d){c=c?c.1i(d):14.K;E e=1d;B.1h(q(a,b){e=e&&!!c(a,b);C(!e)3u $1E;});o e},bg:q(c,d){c=c?c.1i(d):14.K;E e=1f;B.1h(q(a,b){C(e=!!c(a,b))3u $1E;});o e},8n:q(c,d){c=c?c.1i(d):14.K;E e=[];B.1h(q(a,b){e.W(c(a,b))});o e},8j:q(c,d){c=c.1i(d);E e;B.1h(q(a,b){C(c(a,b)){e=a;3u $1E;}});o e},4B:q(c,d){c=c.1i(d);E e=[];B.1h(q(a,b){C(c(a,b))e.W(a)});o e},g2:q(c,d,e){d=d?d.1i(e):14.K;E f=[];C(I.2q(c))c=1c 3c(c);B.1h(q(a,b){C(c.1l(a))f.W(d(a,b))});o f},1p:q(b){C(I.1M(B.2T))o B.2T(b)!=-1;E c=1f;B.1h(q(a){C(a===b){c=1d;3u $1E;}});o c},g1:q(b,c){c=c===1q?19:c;o B.bj(b,q(a){1v(a.L<b)a.W(c);o a})},2N:q(c,d,e){d=d.1i(e);B.1h(q(a,b){c=d(c,a,b)});o c},6A:q(b){E c=$A(1b).3m(1);o B.2k(q(a){o a[b].2V(a,c)})},g0:q(c,d){c=c?c.1i(d):14.K;E e;B.1h(q(a,b){a=c(a,b);C(e==1q||a>=e)e=a});o e},fY:q(c,d){c=c?c.1i(d):14.K;E e;B.1h(q(a,b){a=c(a,b);C(e==1q||a<e)e=a});o e},fX:q(c,d){c=c?c.1i(d):14.K;E e=[],8g=[];B.1h(q(a,b){(c(a,b)?e:8g).W(a)});o[e,8g]},4d:q(b){E c=[];B.1h(q(a){c.W(a[b])});o c},fU:q(c,d){c=c.1i(d);E e=[];B.1h(q(a,b){C(!c(a,b))e.W(a)});o e},b4:q(e,f){e=e.1i(f);o B.2k(q(a,b){o{1n:a,50:e(a,b)}}).fP(q(c,d){E a=c.50,b=d.50;o a<b?-1:a>b?1:0}).4d(\'1n\')},2r:q(){o B.2k()},fN:q(){E c=14.K,2a=$A(1b);C(I.1M(2a.1O()))c=2a.fK();E d=[B].1z(2a).2k($A);o B.2k(q(a,b){o c(d.4d(b))})},b0:q(){o B.2r().L},1T:q(){o\'#<22:\'+B.2r().1T()+\'>\'}};I.M(22,{2k:22.8n,86:22.8j,21:22.4B,4x:22.4B,fF:22.1p,fD:22.2r,fB:22.8m,fA:22.bg});q $A(a){C(!a)o[];C(a.2r)o a.2r();15{E b=[];N(E i=0,L=a.L;i<L;i++)b.W(a[i]);o b}}C(14.1J.5p){q $A(a){C(!a)o[];C(!(I.1M(a)&&a==\'[2I fy]\')&&a.2r){o a.2r()}15{E b=[];N(E i=0,L=a.L;i<L;i++)b.W(a[i]);o b}}}1U.fw=$A;I.M(1U.O,22);C(!1U.O.7Y)1U.O.7Y=1U.O.5h;I.M(1U.O,{2s:q(a){N(E i=0,L=B.L;i<L;i++)a(B[i])},aU:q(){B.L=0;o B},2F:q(){o B[0]},1O:q(){o B[B.L-1]},fs:q(){o B.21(q(a){o a!=19})},aS:q(){o B.2N([],q(a,b){o a.1z(I.3d(b)?b.aS():[b])})},5e:q(){E b=$A(1b);o B.21(q(a){o!b.1p(a)})},5h:q(a){o(a!==1f?B:B.2r()).7Y()},fq:q(){o B.L>1?B:B[0]},aM:q(d){o B.2N([],q(a,b,c){C(0==c||(d?a.1O()!=b:!a.1p(b)))a.W(b);o a})},fp:q(b){o B.aM().4B(q(a){o b.1p(a)})},2l:q(){o[].1z(B)},b0:q(){o B.L},1T:q(){o\'[\'+B.2k(I.1T).1Y(\', \')+\']\'},26:q(){E c=[];B.1h(q(a){E b=I.26(a);C(b!==1q)c.W(b)});o\'[\'+c.1Y(\', \')+\']\'}});C(I.1M(1U.O.aK))1U.O.2s=1U.O.aK;C(!1U.O.2T)1U.O.2T=q(a,i){i||(i=0);E b=B.L;C(i<0)i=b+i;N(;i<b;i++)C(B[i]===a)o i;o-1};C(!1U.O.6O)1U.O.6O=q(a,i){i=fo(i)?B.L:(i<0?B.L+i:i)+1;E n=B.3m(0,i).5h().2T(a);o(n<0)?n:i-n-1};1U.O.2r=1U.O.2l;q $w(a){a=a.2K();o a?a.3V(/\\s+/):[]}C(14.1J.4e){1U.O.1z=q(){E a=[];N(E i=0,L=B.L;i<L;i++)a.W(B[i]);N(E i=0,L=1b.L;i<L;i++){C(I.3d(1b[i])){N(E j=0,aE=1b[i].L;j<aE;j++)a.W(1b[i][j])}15{a.W(1b[i])}}o a}}I.M(3R.O,{fn:q(){o B.3E(2,16)},73:q(){o B+1},5P:q(a){$R(0,B,1d).1h(a);o B},3E:q(a,b){E c=B.24(b||10);o\'0\'.5P(a-c.L)+c},26:q(){o fj(B)?B.24():\'19\'}});$w(\'fi fh ff fd\').1h(q(a){3R.O[a]=fc[a].3C()});E 1S=q(a){C(a 6T 1S)B.av(a);15 I.M(B,a||{})};I.M(1S,{3T:q(d){E e=[];e.6m=1b.3G.aq;B.O.2s.55(d,q(b){C(!b.2v)o;E c=b.1n;C(c&&3g c==\'2I\'){C(I.3d(c))c.1h(q(a){e.6m(b.2v,a)});o}e.6m(b.2v,c)});o e.1Y(\'&\')},26:q(c){E d=[];B.O.2s.55(c,q(a){E b=I.26(a.1n);C(b!==1q)d.W(a.2v.26()+\': \'+b)});o\'{\'+d.1Y(\', \')+\'}\'}});1S.3T.aq=q(a,b,c){a=ao(a);C(b===1q)B.W(a);15 B.W(a+\'=\'+(b==19?\'\':ao(b)))};I.M(1S.O,22);I.M(1S.O,{2s:q(a){N(E b 1t B){E c=B[b];C(c&&c==1S.O[b])2f;E d=[b,c];d.2v=b;d.1n=c;a(d)}},5N:q(){o B.4d(\'2v\')},2R:q(){o B.4d(\'1n\')},4M:q(b){E c=B.8j(q(a){o a.1n===b});o c&&c.2v},av:q(c){o $H(c).2N(B,q(a,b){a[b.2v]=b.1n;o a})},7F:q(){E a;N(E i=0,L=1b.L;i<L;i++){E b=B[1b[i]];C(b!==1q){C(a===1q)a=b;15{C(!I.3d(a))a=[a];a.W(b)}}5n B[1b[i]]}o a},3T:q(){o 1S.3T(B)},1T:q(){o\'#<1S:{\'+B.2k(q(a){o a.2k(I.1T).1Y(\': \')}).1Y(\', \')+\'}>\'},26:q(){o 1S.26(B)}});q $H(a){C(a 6T 1S)o a;o 1c 1S(a)};C(q(){E i=0,7Z=q(a){B.2v=a};7Z.O.2v=\'eW\';N(E b 1t 1c 7Z(\'eU\'))i++;o i>1}())1S.O.2s=q(a){E b=[];N(E c 1t B){E d=B[c];C((d&&d==1S.O[c])||b.1p(c))2f;b.W(c);E e=[c,d];e.2v=c;e.1n=d;a(e)}};6I=1y.2d();I.M(6I.O,22);I.M(6I.O,{2w:q(a,b,c){B.5m=a;B.7C=b;B.b8=c},2s:q(a){E b=B.5m;1v(B.1p(b)){a(b);b=b.73()}},1p:q(a){C(a<B.5m)o 1f;C(B.b8)o a<B.7C;o a<=B.7C}});E $R=q(a,b,c){o 1c 6I(a,b,c)};E 1m={8f:q(){o aP.aL(q(){o 1c a9()},q(){o 1c a7(\'ev.a5\')},q(){o 1c a7(\'eq.a5\')})||1f},7A:0};1m.4o={4K:[],2s:q(a){B.4K.2s(a)},a1:q(a){C(!B.1p(a))B.4K.W(a)},ei:q(a){B.4K=B.4K.5e(a)},6e:q(b,c,d,f){B.1h(q(a){C(I.1M(a[b])){1N{a[b].2V(a,[c,d,f])}1R(e){}}})}};I.M(1m.4o,22);1m.4o.a1({6d:q(){1m.7A++},2M:q(){1m.7A--}});1m.5z=q(){};1m.5z.O={6a:q(a){B.1a={1Z:\'4L\',67:1d,9T:\'65/x-eb-83-e9\',7w:\'e6-8\',3k:\'\',4r:1d,7u:1d};I.M(B.1a,a||{});B.1a.1Z=B.1a.1Z.1I();C(I.2q(B.1a.3k))B.1a.3k=B.1a.3k.5W()}};1m.3B=1y.2d();1m.3B.9M=[\'dS\',\'dP\',\'dN\',\'dK\',\'7n\'];1m.3B.O=I.M(1c 1m.5z(),{7m:1f,2w:q(a,b){B.1D=1m.8f();B.6a(b);B.3b(a)},3b:q(a){B.4s=a;B.1Z=B.1a.1Z;E b=I.2l(B.1a.3k);C(![\'9H\',\'4L\'].1p(B.1Z)){b[\'dy\']=B.1Z;B.1Z=\'4L\'}B.3k=b;C(b=1S.3T(b)){C(B.1Z==\'9H\')B.4s+=(B.4s.1p(\'?\')?\'&\':\'?\')+b;15 C(/dw|9t|ap/.2o(4n.4F))b+=\'&54=\'}1N{E c=1c 1m.5Y(B);C(B.1a.6d)B.1a.6d(c);1m.4o.6e(\'6d\',B,c);B.1D.ds(B.1Z.25(),B.4s,B.1a.67);C(B.1a.67)B.7h.1i(B).2L(1);B.1D.5Q=B.7f.1i(B);B.9C();B.1Q=B.1Z==\'4L\'?(B.1a.di||b):19;B.1D.dh(B.1Q);C(!B.1a.67&&B.1D.9x)B.7f()}1R(e){B.3I(e)}},7f:q(){E a=B.1D.2P;C(a>1&&!((a==4)&&B.7m))B.7h(B.1D.2P)},9C:q(){E b={\'X-df-de\':\'a9\',\'X-14-8s\':14.8s,\'dd\':\'2S/9u, 2S/dc, 65/9r, 2S/9r, */*\'};C(B.1Z==\'4L\'){b[\'76-1o\']=B.1a.9T+(B.1a.7w?\'; da=\'+B.1a.7w:\'\');C(B.1D.9x&&(4n.4F.1l(/6v\\/(\\d{4})/)||[0,9q])[1]<9q)b[\'d8\']=\'d7\'}C(3g B.1a.9o==\'2I\'){E c=B.1a.9o;C(I.1M(c.W))N(E i=0,L=c.L;i<L;i+=2)b[c[i]]=c[i+1];15 $H(c).1h(q(a){b[a.2v]=a.1n})}N(E d 1t b)B.1D.d5(d,b[d])},3t:q(){E a=B.4P();o!a||(a>=d1&&a<d0)},4P:q(){1N{o B.1D.5R||0}1R(e){o 0}},7h:q(a){E b=1m.3B.9M[a],2X=1c 1m.5Y(B);C(b==\'7n\'){1N{B.7m=1d;(B.1a[\'4c\'+2X.5R]||B.1a[\'4c\'+(B.3t()?\'cY\':\'cW\')]||14.3i)(2X,2X.5M)}1R(e){B.3I(e)}E c=2X.4l(\'76-1o\');C(B.1a.7u==\'9f\'||(B.1a.7u&&c&&c.1l(/^\\s*(2S|65)\\/(x-)?(cU|cT)5d(;.*)?\\s*$/i)))B.9e()}1N{(B.1a[\'4c\'+b]||14.3i)(2X,2X.5M);1m.4o.6e(\'4c\'+b,B,2X,2X.5M)}1R(e){B.3I(e)}C(b==\'7n\'){B.1D.5Q=14.3i}},4l:q(a){1N{o B.1D.70(a)}1R(e){o 19}},9e:q(){1N{o 5X((B.1D.51||\'\').6S())}1R(e){B.3I(e)}},3I:q(a){(B.1a.9c||14.3i)(B,a);1m.4o.6e(\'9c\',B,a)}});1m.5Y=1y.2d();1m.5Y.O={2w:q(a){B.3b=a;E b=B.1D=a.1D,2P=B.2P=b.2P;C((2P>2&&!14.1J.3s)||2P==4){B.5R=B.4P();B.7q=B.9a();B.51=1F.4N(b.51);B.5M=B.99()}C(2P==4){E c=b.9Z;B.9Z=c===1q?19:c;B.cM=B.9P()}},5R:0,7q:\'\',4P:1m.3B.O.4P,9a:q(){1N{o B.1D.7q||\'\'}1R(e){o\'\'}},4l:1m.3B.O.4l,cK:q(){1N{o B.7v()}1R(e){o 19}},70:q(a){o B.1D.70(a)},7v:q(){o B.1D.7v()},99:q(){E a=B.4l(\'X-8T\');1N{o a?a.4r(B.3b.1a.92):19}1R(e){B.3b.3I(e)}},9P:q(){E a=B.3b.1a;1N{C(a.4r==\'9f\'||(a.4r&&(B.4l(\'76-1o\')||\'\').1p(\'65/cI\')))o B.1D.51.4r(a.92);o 19}1R(e){B.3b.3I(e)}}};1m.6X=1y.2d();I.M(I.M(1m.6X.O,1m.3B.O),{2w:q(c,d,e){B.64={3t:(c.3t||c),6W:(c.6W||(c.3t?19:c))};B.1D=1m.8f();B.6a(e);E f=B.1a.2M||14.3i;B.1a.2M=(q(a,b){B.90(a.51);f(a,b)}).1i(B);B.3b(d)},90:q(a){E b=B.64[B.3t()?\'3t\':\'6W\'],1a=B.1a;C(!1a.3p)a=a.2A();C(b=$(b)){C(1a.6f){C(I.2q(1a.6f)){E c={};c[1a.6f]=a;b.1W(c)}15 1a.6f(b,a)}15 b.4Q(a)}C(B.3t()){C(B.2M)B.2M.1i(B).2L()}}});1m.a2=1y.2d();1m.a2.O=I.M(1c 1m.5z(),{2w:q(a,b,c){B.6a(c);B.2M=B.1a.2M;B.3S=(B.1a.3S||2);B.3X=(B.1a.3X||1);B.8i={};B.64=a;B.4s=b;B.5m()},5m:q(){B.1a.2M=B.8Y.1i(B);B.3M()},7D:q(){B.8i.1a.2M=1q;cv(B.4p);(B.2M||14.3i).2V(B,1b)},8Y:q(a){C(B.1a.3X){B.3X=(a==B.a6?B.3X*B.1a.3X:1);B.a6=a}B.4p=B.3M.1i(B).8k(B.3X*B.3S)},3M:q(){B.8i=1c 1m.6X(B.64,B.4s,B.1a)}});q $(a){C(1b.L>1){N(E i=0,7B=[],L=1b.L;i<L;i++)7B.W($(1b[i]));o 7B}C(I.2q(a))a=V.cp(a);o G.M(a)}C(14.2J.62){V.8l=q(a,b){E c=[];E d=V.2p(a,$(b)||V,19,cl.cj,19);N(E i=0,L=d.ci;i<L;i++)c.W(d.cf(i));o c}}C(!1k.6h)E 6h={};I.M(6h,{ce:1,cc:2,an:3,c8:4,c7:5,c5:6,c4:7,c1:8,bZ:9,bX:10,bW:11,bT:12});(q(){E d=B.G;B.G=q(a,b){b=b||{};a=a.1I();E c=G.5x;C(14.1J.3s&&b.1C){a=\'<\'+a+\' 1C="\'+b.1C+\'">\';5n b.1C;o G.57(V.3j(a),b)}C(!c[a])c[a]=G.M(V.3j(a));o G.57(c[a].bS(1f),b)};I.M(B.G,d||{})}).55(1k);G.5x={};G.Y={85:q(a){o $(a).17.3l!=\'5C\'},8L:q(a){a=$(a);G[G.85(a)?\'8K\':\'aW\'](a);o a},8K:q(a){$(a).17.3l=\'5C\';o a},aW:q(a){$(a).17.3l=\'\';o a},7F:q(a){a=$(a);a.1B.5l(a);o a},4Q:q(a,b){a=$(a);C(b&&b.29)b=b.29();C(I.2C(b))o a.4Q().1W(b);b=I.2G(b);a.3J=b.2A();b.3p.1i(b).2L();o a},1w:q(a,b){a=$(a);C(b&&b.29)b=b.29();15 C(!I.2C(b)){b=I.2G(b);E c=a.bb.87();c.bB(a);b.3p.1i(b).2L();b=c.b3(b.2A())}a.1B.8c(b,a);o a},1W:q(a,b){a=$(a);C(I.2q(b)||I.6j(b)||I.2C(b)||(b&&(b.29||b.2G)))b={3K:b};E c,t,6F;N(1g 1t b){c=b[1g];1g=1g.1I();t=G.3Y[1g];C(c&&c.29)c=c.29();C(I.2C(c)){t.1W(a,c);2f}c=I.2G(c);6F=a.bb.87();t.43(a,6F);t.1W(a,6F.b3(c.2A()));c.3p.1i(c).2L()}o a},6L:q(a,b,c){a=$(a);C(I.2C(b))$(b).57(c||{});15 C(I.2q(b))b=1c G(b,c);15 b=1c G(\'2t\',b);C(a.1B)a.1B.8c(b,a);b.44(a);o a},1T:q(d){d=$(d);E e=\'<\'+d.1j.1I();$H({\'1X\':\'1X\',\'1H\':\'4G\'}).1h(q(a){E b=a.2F(),8B=a.1O();E c=(d[b]||\'\').24();C(c)e+=\' \'+8B+\'=\'+c.1T(1d)});o e+\'>\'},5y:q(a,b){a=$(a);E c=[];1v(a=a[b])C(a.2u==1)c.W(G.M(a));o c},8z:q(a){o $(a).5y(\'1B\')},8y:q(a){o $A($(a).3h(\'*\')).1h(G.M)},8w:q(a){a=$(a).4C;1v(a&&a.2u!=1)a=a.47;o $(a)},bo:q(a){C(!(a=$(a).4C))o[];1v(a&&a.2u!=1)a=a.47;C(a)o[a].1z($(a).5w());o[]},8t:q(a){o $(a).5y(\'bn\')},5w:q(a){o $(a).5y(\'47\')},g9:q(a){a=$(a);o a.8t().5h().1z(a.5w())},1l:q(a,b){C(I.2q(b))b=1c U(b);o b.1l($(a))},bl:q(a,b,c){a=$(a);C(1b.L==1)o $(a.1B);E d=a.8z();o b?U.4D(d,b,c):d[c||0]},g7:q(a,b,c){a=$(a);C(1b.L==1)o a.8w();E d=a.8y();o b?U.4D(d,b,c):d[c||0]},g6:q(a,b,c){a=$(a);C(1b.L==1)o $(U.1A.5t(a));E d=a.8t();o b?U.4D(d,b,c):d[c||0]},bk:q(a,b,c){a=$(a);C(1b.L==1)o $(U.1A.5s(a));E d=a.5w();o b?U.4D(d,b,c):d[c||0]},21:q(){E a=$A(1b),1u=$(a.4E());o U.6K(1u,a)},42:q(){E a=$A(1b),1u=$(a.4E());o U.6K(1u.1B,a).5e(1u)},bi:q(a){a=$(a);E b=a.41(\'1X\'),8p=1b.3G;C(b)o b;do{b=\'g3\'+8p.bh++}1v($(b));a.57(\'1X\',b);o b},41:q(a,b){a=$(a);C(14.1J.3s){E t=G.2H.6J;C(t.2R[b])o t.2R[b](a,b);C(t.3n[b])b=t.3n[b];C(b.1p(\':\')){o(!a.6V||!a.6V[b])?19:a.6V[b].1n}}o a.7i(b)},57:q(a,b,c){a=$(a);E d={},t=G.2H.5q;C(3g b==\'2I\')d=b;15 d[b]=c===1q?1d:c;N(E e 1t d){E b=t.3n[e]||e,c=d[e];C(t.2R[e])b=t.2R[e](a,c);C(c===1f||c===19)a.bf(b);15 C(c===1d)a.bd(b,b);15 a.bd(b,c)}o a},bc:q(a){o $(a).4A().2Q},ba:q(a){o $(a).4A().2m},5o:q(a){o 1c G.6H(a)},6G:q(a,b){C(!(a=$(a)))o;E c=a.1H;o(c.L>0&&(c==b||c.1l(1c 3c("(^|\\\\s)"+b+"(\\\\s|$)"))))},b9:q(a,b){C(!(a=$(a)))o;C(!a.6G(b))a.1H+=(a.1H?\' \':\'\')+b;o a},b7:q(a,b){C(!(a=$(a)))o;a.1H=a.1H.1w(1c 3c("(^|\\\\s+)"+b+"(\\\\s+|$)"),\' \').2K();o a},fW:q(a,b){C(!(a=$(a)))o;o a[a.6G(b)?\'b7\':\'b9\'](b)},fV:q(a){a=$(a);E b=a.4C;1v(b){E c=b.47;C(b.2u==3&&!/\\S/.2o(b.79))a.5l(b);b=c}o a},5k:q(a){o $(a).3J.5i()},6E:q(a,b){a=$(a),b=$(b);1v(a=a.1B)C(a==b)o 1d;o 1f},b5:q(a){a=$(a);E b=a.3L();1k.b5(b[0],b[1]);o a},1L:q(a,b){a=$(a);b=b==\'8d\'?\'6D\':b.71();E c=a.17[b];C(!c){E d=V.fT.fR(a,19);c=d?d[b]:19}C(b==\'2z\')o c?4f(c):1.0;o c==\'6C\'?19:c},fM:q(a){o $(a).1L(\'2z\')},8a:q(a,b){a=$(a);E c=a.17,1l;C(I.2q(b)){a.17.89+=\';\'+b;o b.1p(\'2z\')?a.4y(b.1l(/2z:\\s*(\\d?\\.?\\d*)/)[1]):a}N(E d 1t b)C(d==\'2z\')a.4y(b[d]);15 c[(d==\'8d\'||d==\'6D\')?(c.88===1q?\'6D\':\'88\'):d]=b[d];o a},4y:q(a,b){a=$(a);a.17.2z=(b==1||b===\'\')?\'\':(b<0.5T)?0:b;o a},4A:q(a){a=$(a);E b=$(a).1L(\'3l\');C(b!=\'5C\'&&b!=19)o{2m:a.4h,2Q:a.4k};E c=a.17;E d=c.7d;E e=c.1g;E f=c.3l;c.7d=\'5U\';c.1g=\'3W\';c.3l=\'fG\';E g=a.aZ;E h=a.aY;c.3l=f;c.1g=e;c.7d=d;o{2m:g,2Q:h}},fE:q(a){a=$(a);E b=G.1L(a,\'1g\');C(b==\'6z\'||!b){a.82=1d;a.17.1g=\'5j\';C(1k.7g){a.17.2h=0;a.17.32=0}}o a},fz:q(a){a=$(a);C(a.82){a.82=1q;a.17.1g=a.17.2h=a.17.32=a.17.3K=a.17.aV=\'\'}o a},fx:q(a){a=$(a);C(a.4u)o a;a.4u=a.17.6y||\'6C\';C((G.1L(a,\'6y\')||\'85\')!=\'5U\')a.17.6y=\'5U\';o a},fu:q(a){a=$(a);C(!a.4u)o a;a.17.6y=a.4u==\'6C\'?\'\':a.4u;a.4u=19;o a},3L:q(a){E b=0,20=0;do{b+=a.3N||0;20+=a.3Q||0;a=a.3a}1v(a);o G.3O(20,b)},53:q(a){E b=0,20=0;do{b+=a.3N||0;20+=a.3Q||0;a=a.3a;C(a){C(a.1j==\'aT\')1E;E p=G.1L(a,\'1g\');C(p==\'5j\'||p==\'3W\')1E}}1v(a);o G.3O(20,b)},7V:q(a){a=$(a);C(a.1L(\'1g\')==\'3W\')o;E b=a.53();E c=b[1];E d=b[0];E e=a.aZ;E f=a.aY;a.aR=d-4f(a.17.32||0);a.aQ=c-4f(a.17.2h||0);a.aO=a.17.2m;a.aN=a.17.2Q;a.17.1g=\'3W\';a.17.2h=c+\'2D\';a.17.32=d+\'2D\';a.17.2m=e+\'2D\';a.17.2Q=f+\'2D\';o a},7U:q(a){a=$(a);C(a.1L(\'1g\')==\'5j\')o;a.17.1g=\'5j\';E b=4f(a.17.2h||0)-(a.aQ||0);E c=4f(a.17.32||0)-(a.aR||0);a.17.2h=b+\'2D\';a.17.32=c+\'2D\';a.17.2Q=a.aN;a.17.2m=a.aO;o a},7T:q(a){E b=0,20=0;do{b+=a.3x||0;20+=a.3v||0;a=a.1B}1v(a);o G.3O(20,b)},6u:q(a){C(a.3a)o $(a.3a);C(a==V.1Q)o $(a);1v((a=a.1B)&&a!=V.1Q)C(G.1L(a,\'1g\')!=\'6z\')o $(a);o $(V.1Q)},5c:q(a){E b=0,20=0;E c=a;do{b+=c.3N||0;20+=c.3Q||0;C(c.3a==V.1Q&&G.1L(c,\'1g\')==\'3W\')1E}1v(c=c.3a);c=a;do{C(!14.1J.4e||c.1j==\'aT\'){b-=c.3x||0;20-=c.3v||0}}1v(c=c.1B);o G.3O(20,b)},aJ:q(a,b){E c=I.M({aI:1d,aH:1d,aG:1d,aF:1d,3N:0,3Q:0},1b[2]||{});b=$(b);E p=b.5c();a=$(a);E d=[0,0];E e=19;C(G.1L(a,\'1g\')==\'3W\'){e=a.6u();d=e.5c()}C(e==V.1Q){d[0]-=V.1Q.3Q;d[1]-=V.1Q.3N}C(c.aI)a.17.32=(p[0]-d[0]+c.3Q)+\'2D\';C(c.aH)a.17.2h=(p[1]-d[1]+c.3N)+\'2D\';C(c.aG)a.17.2m=b.4h+\'2D\';C(c.aF)a.17.2Q=b.4k+\'2D\';o a}};G.Y.bi.bh=1;C(!V.6t)V.6t=q(f){q 7R(a){o a.5i()?19:"[5a(1z(\' \', @4G, \' \'), \' "+a+" \')]"}f.6t=14.2J.62?q(a,b){b=b.24().2K();E c=/\\s/.2o(b)?$w(b).2k(7R).1Y(\'\'):7R(b);o c?V.8l(\'.//*\'+c,a):[]}:q(b,c){c=c.24().2K();E d=[],5o=(/\\s/.2o(c)?$w(c):19);C(!5o&&!c)o d;E e=$(b).3h(\'*\');c=\' \'+c+\' \';N(E i=0,1s,cn;1s=e[i];i++){C(1s.1H&&(cn=\' \'+1s.1H+\' \')&&(cn.1p(c)||(5o&&5o.8m(q(a){o!a.24().5i()&&cn.1p(\' \'+a+\' \')}))))d.W(G.M(1s))}o d};o q(a,b){o $(b||V.1Q).6t(a)}}(G.Y);I.M(G.Y,{fm:G.Y.21,fk:G.Y.bo});G.2H={5q:{3n:{1H:\'4G\',aD:\'N\'},2R:{}}};C(!V.87||14.1J.4e){G.Y.1W=q(a,b){a=$(a);C(I.2q(b)||I.6j(b)||I.2C(b)||(b&&(b.29||b.2G)))b={3K:b};E t=G.3Y,2g,1g,59,1j;N(1g 1t b){2g=b[1g];1g=1g.1I();59=t[1g];C(2g&&2g.29)2g=2g.29();C(I.2C(2g)){59.1W(a,2g);2f}2g=I.2G(2g);1j=((1g==\'7M\'||1g==\'6s\')?a.1B:a).1j.25();C(t.3f[1j]){E c=G.6o(1j,2g.2A());C(1g==\'2h\'||1g==\'6s\')c.5h();c.1h(59.1W.8o(a))}15 a.fg(59.58,2g.2A());2g.3p.1i(2g).2L()}o a}}C(14.1J.4e){G.Y.7K=G.Y.1L;G.Y.1L=q(a,b){4Y(b){2c\'32\':2c\'2h\':2c\'aV\':2c\'3K\':C(G.7K(a,\'1g\')==\'6z\')o 19;6r:o G.7K(a,b)}};G.Y.az=G.Y.41;G.Y.41=q(a,b){C(b==\'6q\')o a.6q;o G.az(a,b)}}15 C(14.1J.3s){$w(\'53 6u 5c\').1h(q(e){G.Y[e]=G.Y[e].6L(q(a,b){b=$(b);E c=b.1L(\'1g\');C(c!=\'6z\')o a(b);b.8a({1g:\'5j\'});E d=a(b);b.8a({1g:c});o d})});G.Y.1L=q(a,b){a=$(a);b=(b==\'8d\'||b==\'6D\')?\'88\':b.71();E c=a.17[b];C(!c&&a.ay)c=a.ay[b];C(b==\'2z\'){C(c=(a.1L(\'4x\')||\'\').1l(/7J\\(2z=(.*)\\)/))C(c[1])o 4f(c[1])/aw;o 1.0}C(c==\'6C\'){C((b==\'2m\'||b==\'2Q\')&&(a.1L(\'3l\')!=\'5C\'))o a[\'2j\'+b.5G()]+\'2D\';o 19}o c};G.Y.4y=q(b,c){q 7I(a){o a.1w(/7J\\([^\\)]*\\)/9J,\'\')}b=$(b);E d=b.1L(\'4x\'),17=b.17;C(c==1||c===\'\'){(d=7I(d))?17.4x=d:17.bf(\'4x\');o b}15 C(c<0.5T)c=0;17.4x=7I(d)+\'7J(2z=\'+(c*aw)+\')\';o b};G.2H={6J:{3n:{\'4G\':\'1H\',\'N\':\'aD\'},2R:{6w:q(a,b){o a.7i(b,2)},au:q(a,b){E c=a.at(b);o c?c.1n:""},1K:q(a,b){E b=a.7i(b);o b?b.24().3m(23,-2):19},5b:q(a,b){o $(a).2E(b)?b:19},17:q(a){o a.17.89.1I()},6q:q(a){o a.6q}}}};G.2H.5q={3n:I.2l(G.2H.6J.3n),2R:{2O:q(a,b){a.2O=!!b},17:q(a,b){a.17.89=b?b:\'\'}}};G.2H.7G={};$w(\'f7 f4 f3 f2 f1 6k \'+\'f0 eZ eY eX\').1h(q(a){G.2H.5q.3n[a.1I()]=a;G.2H.7G[a.1I()]=a});(q(v){I.M(v,{ak:v.6w,aj:v.6w,1o:v.6w,4q:v.au,2x:v.5b,2O:v.5b,eQ:v.5b,eP:v.5b,eO:v.1K,ah:v.1K,eN:v.1K,eK:v.1K,eI:v.1K,eH:v.1K,eG:v.1K,eD:v.1K,eC:v.1K,eB:v.1K,eA:v.1K,ez:v.1K,ey:v.1K,ex:v.1K,ew:v.1K,eu:v.1K,et:v.1K,es:v.1K})})(G.2H.6J.2R)}15 C(14.1J.6v){G.Y.4y=q(a,b){a=$(a);a.17.2z=(b==1)?0.er:(b===\'\')?\'\':(b<0.5T)?0:b;o a}}15 C(14.1J.5p){G.Y.4y=q(a,b){a=$(a);a.17.2z=(b==1||b===\'\')?\'\':(b<0.5T)?0:b;C(b==1)C(a.1j==\'a4\'&&a.2m){a.2m++;a.2m--}15 1N{E n=V.8D(\' \');a.44(n);a.5l(n)}1R(e){}o a};G.Y.3L=q(a){E b=0,20=0;do{b+=a.3N||0;20+=a.3Q||0;C(a.3a==V.1Q)C(G.1L(a,\'1g\')==\'3W\')1E;a=a.3a}1v(a);o G.3O(20,b)}}C(14.1J.3s||14.1J.4e){G.Y.4Q=q(b,c){b=$(b);C(c&&c.29)c=c.29();C(I.2C(c))o b.4Q().1W(c);c=I.2G(c);E d=b.1j.25();C(d 1t G.3Y.3f){$A(b.2Z).1h(q(a){b.5l(a)});G.6o(d,c.2A()).1h(q(a){b.44(a)})}15 b.3J=c.2A();c.3p.1i(c).2L();o b}}C(V.3j(\'2t\').a3){G.Y.1w=q(b,c){b=$(b);C(c&&c.29)c=c.29();C(I.2C(c)){b.1B.8c(c,b);o b}c=I.2G(c);E d=b.1B,1j=d.1j.25();C(G.3Y.3f[1j]){E e=b.bk();E f=G.6o(1j,c.2A());d.5l(b);C(e)f.1h(q(a){d.6g(a,e)});15 f.1h(q(a){d.44(a)})}15 b.a3=c.2A();c.3p.1i(c).2L();o b}}G.3O=q(l,t){E a=[l,t];a.32=l;a.2h=t;o a};G.6o=q(a,b){E c=1c G(\'2t\'),t=G.3Y.3f[a];c.3J=t[0]+b+t[1];t[2].5P(q(){c=c.4C});o $A(c.2Z)};G.3Y={7M:{58:\'ep\',1W:q(a,b){a.1B.6g(b,a)},43:q(a,b){b.eo(a)}},2h:{58:\'em\',1W:q(a,b){a.6g(b,a.4C)},43:q(a,b){b.el(a);b.ek(1d)}},3K:{58:\'ej\',1W:q(a,b){a.44(b)}},6s:{58:\'eh\',1W:q(a,b){a.1B.6g(b,a.47)},43:q(a,b){b.eg(a)}},3f:{ef:[\'<3A>\',\'</3A>\',1],5A:[\'<3A><48>\',\'</48></3A>\',2],9Y:[\'<3A><48><6c>\',\'</6c></48></3A>\',3],7z:[\'<3A><48><6c><9X>\',\'</9X></6c></48></3A>\',4],9W:[\'<21>\',\'</21>\',1]}};(q(){B.3K.43=B.2h.43;I.M(B.3f,{9V:B.3f.5A,9U:B.3f.5A,8I:B.3f.7z})}).55(G.3Y);G.Y.69={2E:q(a,b){b=G.2H.7G[b]||b;E c=$(a).at(b);o c&&c.ed}};G.Y.2y={};I.M(G,G.Y);C(!14.2J.5g&&V.3j(\'2t\').4z){1k.4I={};1k.4I.O=V.3j(\'2t\').4z;14.2J.5g=1d}G.M=(q(){C(14.2J.6M)o 14.K;E c={},2y=G.Y.2y;E d=I.M(q(a){C(!a||a.68||a.2u!=1||a==1k)o a;E b=I.2l(c),1j=a.1j,4J,1n;C(2y[1j])I.M(b,2y[1j]);N(4J 1t b){1n=b[4J];C(I.1M(1n)&&!(4J 1t a))a[4J]=1n.3C()}a.68=14.3i;o a},{5B:q(){C(!14.2J.5g){I.M(c,G.Y);I.M(c,G.Y.69)}}});d.5B();o d})();G.2E=q(a,b){C(a.2E)o a.2E(b);o G.Y.69.2E(a,b)};G.7x=q(f){E F=14.2J,T=G.Y.2y;C(!f){I.M(1e,1e.Y);I.M(1e.G,1e.G.Y);I.M(G.Y.2y,{"ec":I.2l(1e.Y),"ea":I.2l(1e.G.Y),"9W":I.2l(1e.G.Y),"9S":I.2l(1e.G.Y)})}C(1b.L==2){E g=f;f=1b[1]}C(!g)I.M(G.Y,f||{});15{C(I.3d(g))g.1h(M);15 M(g)}q M(a){a=a.25();C(!G.Y.2y[a])G.Y.2y[a]={};I.M(G.Y.2y[a],f)}q 63(a,b,c){c=c||1f;N(E d 1t a){E e=a[d];C(!I.1M(e))2f;C(!c||!(d 1t b))b[d]=e.3C()}}q 9R(a){E b;E c={"e8":"e7","9S":"e5","P":"e4","e3":"e2","e1":"e0","dZ":"dX","dW":"dV","dU":"dR","dQ":"4b","dO":"4b","dM":"4b","dL":"4b","dJ":"4b","dI":"4b","Q":"dH","dG":"9K","dF":"9K","A":"dE","a4":"dD","dC":"dB","dA":"9I","dz":"9I","9V":"6U","9U":"6U","5A":"6U","9Y":"dx","8I":"9F","7z":"9F","dv":"du","dt":"dr"};C(c[a])b=\'7j\'+c[a]+\'G\';C(1k[b])o 1k[b];b=\'7j\'+a+\'G\';C(1k[b])o 1k[b];b=\'7j\'+a.5G()+\'G\';C(1k[b])o 1k[b];1k[b]={};1k[b].O=V.3j(a).4z;o 1k[b]}C(F.5g){63(G.Y,4I.O);63(G.Y.69,4I.O,1d)}C(F.6M){N(E h 1t G.Y.2y){E i=9R(h);C(I.8R(i))2f;63(T[h],i.O)}}I.M(G,G.Y);5n G.2y;C(G.M.5B)G.M.5B();G.5x={}};V.dl={4A:q(){E a={};$w(\'2m 2Q\').1h(q(d){E D=d.5G();a[d]=8p[\'dk\'+D]||(V.3q[\'9D\'+D]||V.1Q[\'9D\'+D])});o a},ba:q(){o B.4A().2m},bc:q(){o B.4A().2Q},dj:q(){o G.3O(1k.9B||V.3q.3v||V.1Q.3v,1k.9A||V.3q.3x||V.1Q.3x)}};E U=1y.2d();U.O={2w:q(a){B.3e=a.2K();B.9z()},9z:q(){C(14.2J.62&&!(/\\[[\\w-]*?:/).2o(B.3e))o B.9y();E e=B.3e,2Y=U.4R,h=U.1A,c=U.50,31,p,m;C(U.3H[e]){B.2B=U.3H[e];o}B.2B=["B.2B = q(9w) {","E r = 9w, h = U.1A, c = 1f, n;"];1v(e&&31!=e&&(/\\S/).2o(e)){31=e;N(E i 1t 2Y){p=2Y[i];C(m=e.1l(p)){B.2B.W(I.1M(c[i])?c[i](m):1c 2e(c[i]).2p(m));e=e.1w(m[0],\'\');1E}}}B.2B.W("o h.7c(n);\\n}");5X(B.2B.1Y(\'\\n\'));U.3H[B.3e]=B.2B},9y:q(){E e=B.3e,2Y=U.4R,x=U.1P,31,m;C(U.3H[e]){B.1P=U.3H[e];o}B.2B=[\'.//*\'];1v(e&&31!=e&&(/\\S/).2o(e)){31=e;N(E i 1t 2Y){C(m=e.1l(2Y[i])){B.2B.W(I.1M(x[i])?x[i](m):1c 2e(x[i]).2p(m));e=e.1w(m[0],\'\');1E}}}B.1P=B.2B.1Y(\'\');U.3H[B.3e]=B.1P},5r:q(a){a=a||V;C(B.1P)o V.8l(B.1P,a);o B.2B(a)},1l:q(a){B.7a=[];E e=B.3e,2Y=U.4R,as=U.6Y;E b,p,m;1v(e&&b!==e&&(/\\S/).2o(e)){b=e;N(E i 1t 2Y){p=2Y[i];C(m=e.1l(p)){C(as[i]){B.7a.W([i,I.2l(m)]);e=e.1w(m[0],\'\')}15{o B.5r(V).1p(a)}}}}E c=1d,1C,78;N(E i=0,5O;5O=B.7a[i];i++){1C=5O[0],78=5O[1];C(!U.6Y[1C](a,78)){c=1f;1E}}o c},24:q(){o B.3e},1T:q(){o"#<U:"+B.3e.1T()+">"}};I.M(U,{3H:{},1P:{28:"//*",1s:"/*",42:"/4Z-3r::*[1]",4T:\'/4Z-3r::*\',1j:q(m){C(m[1]==\'*\')o\'\';o"[9v-1C()=\'"+m[1].1I()+"\' 77 9v-1C()=\'"+m[1].25()+"\']"},1H:"[5a(1z(\' \', @4G, \' \'), \' #{1} \')]",1X:"[@1X=\'#{1}\']",4i:"[@#{1}]",4g:q(m){m[3]=m[5]||m[6];o 1c 2e(U.1P.4V[m[2]]).2p(m)},4X:q(m){E h=U.1P.1G[m[1]];C(!h)o\'\';C(I.1M(h))o h(m);o 1c 2e(U.1P.1G[m[1]]).2p(m)},4V:{\'=\':"[@#{1}=\'#{3}\']",\'!=\':"[@#{1}!=\'#{3}\']",\'^=\':"[db-8C(@#{1}, \'#{3}\')]",\'$=\':"[4a(@#{1}, (6B-L(@#{1}) - 6B-L(\'#{3}\') + 1))=\'#{3}\']",\'*=\':"[5a(@#{1}, \'#{3}\')]",\'~=\':"[5a(1z(\' \', @#{1}, \' \'), \' #{3} \')]",\'|=\':"[5a(1z(\'-\', @#{1}, \'-\'), \'-#{3}-\')]"},1G:{\'2F-1s\':\'[3o(75-3r::*)]\',\'1O-1s\':\'[3o(4Z-3r::*)]\',\'4W-1s\':\'[3o(75-3r::* 77 4Z-3r::*)]\',\'5k\':"[5S(*) = 0 74 (5S(2S()) = 0 77 d9(2S(), \' \\t\\r\\n\', \'\') = \'\')]",\'2O\':"[@2O]",\'2x\':"[@2x]",\'9p\':"[3o(@2x)]",\'3o\':q(m){E e=m[6],p=U.4R,x=U.1P,31,m,v;E a=[];1v(e&&31!=e&&(/\\S/).2o(e)){31=e;N(E i 1t p){C(m=e.1l(p[i])){v=I.1M(x[i])?x[i](m):1c 2e(x[i]).2p(m);a.W("("+v.4a(1,v.L-1)+")");e=e.1w(m[0],\'\');1E}}}o"[3o("+a.1Y(" 74 ")+")]"},\'1x-1s\':q(m){o U.1P.1G.1x("(5S(./75-3r::*) + 1) ",m)},\'1x-1O-1s\':q(m){o U.1P.1G.1x("(5S(./4Z-3r::*) + 1) ",m)},\'1x-1V-1o\':q(m){o U.1P.1G.1x("1g() ",m)},\'1x-1O-1V-1o\':q(m){o U.1P.1G.1x("(1O() + 1 - 1g()) ",m)},\'2F-1V-1o\':q(m){m[6]="1";o U.1P.1G[\'1x-1V-1o\'](m)},\'1O-1V-1o\':q(m){m[6]="1";o U.1P.1G[\'1x-1O-1V-1o\'](m)},\'4W-1V-1o\':q(m){E p=U.1P.1G;o p[\'2F-1V-1o\'](m)+p[\'1O-1V-1o\'](m)},1x:q(c,m){E d,3P=m[6],7b;C(3P==\'9n\')3P=\'2n+0\';C(3P==\'9m\')3P=\'2n+1\';C(d=3P.1l(/^(\\d+)$/))o\'[\'+c+"= "+d[1]+\']\';C(d=3P.1l(/^(-?\\d*)?n(([+-])(\\d+))?/)){C(d[1]=="-")d[1]=-1;E a=d[1]?3R(d[1]):1;E b=d[2]?3R(d[2]):0;7b="[((#{7e} - #{b}) d4 #{a} = 0) 74 "+"((#{7e} - #{b}) 2t #{a} >= 0)]";o 1c 2e(7b).2p({7e:c,a:a,b:b})}}}},50:{1j:\'n = h.1j(n, r, "#{1}", c);   c = 1f;\',1H:\'n = h.1H(n, r, "#{1}", c); c = 1f;\',1X:\'n = h.1X(n, r, "#{1}", c);        c = 1f;\',4i:\'n = h.4i(n, r, "#{1}"); c = 1f;\',4g:q(m){m[3]=(m[5]||m[6]);o 1c 2e(\'n = h.4g(n, r, "#{1}", "#{3}", "#{2}"); c = 1f;\').2p(m)},4X:q(m){C(m[6])m[6]=m[6].1w(/"/g,\'\\\\"\');o 1c 2e(\'n = h.4X(n, "#{1}", "#{6}", r, c); c = 1f;\').2p(m)},28:\'c = "28";\',1s:\'c = "1s";\',42:\'c = "42";\',4T:\'c = "4T";\'},4R:{4T:/^\\s*~\\s*/,1s:/^\\s*>\\s*/,42:/^\\s*\\+\\s*/,28:/^\\s/,1j:/^\\s*(\\*|[\\w\\-]+)(\\b|$)?/,1X:/^#([\\w\\-\\*]+)(\\b|$)/,1H:/^\\.([\\w\\-\\*]+)(\\b|$)/,4X:/^:((2F|1O|1x|1x-1O|4W)(-1s|-1V-1o)|5k|2O|(en|d3)dp|3o)(\\((.*?)\\))?(\\b|$|\\s|(?=:))/,4i:/^\\[([\\w]+)\\]/,4g:/\\[((?:[\\w-]*:)?[\\w-]+)\\s*(?:([!^$*~|]?=)\\s*(([\'"])([^\\4]*?)\\4|([^\'"][^\\]]*?)))?\\]/},6Y:{1j:q(a,b){o b[1].25()==a.1j.25()},1H:q(a,b){o G.6G(a,b[1])},1X:q(a,b){o a.1X===b[1]},4i:q(a,b){o G.2E(a,b[1])},4g:q(a,b){E c=G.41(a,b[1]);o U.4V[b[2]](c,b[3])}},1A:{1z:q(a,b){N(E i=0,J;J=b[i];i++)a.W(J);o a},6n:q(a){N(E i=0,J;J=a[i];i++)J.2U=1d;o a},4m:q(a){N(E i=0,J;J=a[i];i++)J.2U=1q;o a},4M:q(a,b,c){a.2U=1d;C(b){N(E d=a.2Z,i=d.L-1,j=1;i>=0;i--){J=d[i];C(J.2u==1&&(!c||J.2U))J.5Z=j++}}15{N(E i=0,j=1,d=a.2Z;J=d[i];i++)C(J.2u==1&&(!c||J.2U))J.5Z=j++}},7c:q(a){C(a.L==0)o a;E b=[],n;N(E i=0,l=a.L;i<l;i++)C(!(n=a[i]).2U){n.2U=1d;b.W(G.M(n))}o U.1A.4m(b)},28:q(a){E h=U.1A;N(E i=0,18=[],J;J=a[i];i++)h.1z(18,J.3h(\'*\'));o 18},1s:q(a){E h=U.1A;N(E i=0,18=[],J;J=a[i];i++){N(E j=0,cZ=[],1s;1s=J.2Z[j];j++)C(1s.2u==1&&1s.1j!=\'!\')18.W(1s)}o 18},42:q(a){N(E i=0,18=[],J;J=a[i];i++){E b=B.5s(J);C(b)18.W(b)}o 18},4T:q(a){E h=U.1A;N(E i=0,18=[],J;J=a[i];i++)h.1z(18,G.5w(J));o 18},5s:q(a){1v(a=a.47)C(a.2u==1)o a;o 19},5t:q(a){1v(a=a.bn)C(a.2u==1)o a;o 19},1j:q(a,b,c,d){c=c.25();E e=[],h=U.1A;C(a){C(d){C(d=="28"){N(E i=0,J;J=a[i];i++)h.1z(e,J.3h(c));o e}15 a=B[d](a);C(c=="*")o a}N(E i=0,J;J=a[i];i++)C(J.1j.25()==c)e.W(J);o e}15 o b.3h(c)},1X:q(a,b,c,d){E e=$(c),h=U.1A;C(!e)o[];C(!a&&b==V)o[e];C(a){C(d){C(d==\'1s\'){N(E i=0,J;J=a[i];i++)C(e.1B==J)o[e]}15 C(d==\'28\'){N(E i=0,J;J=a[i];i++)C(G.6E(e,J))o[e]}15 C(d==\'42\'){N(E i=0,J;J=a[i];i++)C(U.1A.5t(e)==J)o[e]}15 a=h[d](a)}N(E i=0,J;J=a[i];i++)C(J==e)o[e];o[]}o(e&&G.6E(e,b))?[e]:[]},1H:q(a,b,c,d){C(a&&d)a=B[d](a);o U.1A.9k(a,b,c)},9k:q(a,b,c){C(!a)a=U.1A.28([b]);E d=\' \'+c+\' \';N(E i=0,18=[],J,4U;J=a[i];i++){4U=J.1H;C(4U.L==0)2f;C(4U==c||(\' \'+4U+\' \').1p(d))18.W(J)}o 18},4i:q(a,b,c){E d=[];N(E i=0,J;J=a[i];i++)C(G.2E(J,c))d.W(J);o d},4g:q(a,b,c,d,e){C(!a)a=b.3h("*");E f=U.4V[e],18=[];N(E i=0,J;J=a[i];i++){E g=G.41(J,c);C(g===19)2f;C(f(g,d))18.W(J)}o 18},4X:q(a,b,c,d,e){C(a&&e)a=B[e](a);C(!a)a=d.3h("*");o U.1G[b](a,c,d)}},1G:{\'2F-1s\':q(a,b,c){N(E i=0,18=[],J;J=a[i];i++){C(U.1A.5t(J))2f;18.W(J)}o 18},\'1O-1s\':q(a,b,c){N(E i=0,18=[],J;J=a[i];i++){C(U.1A.5s(J))2f;18.W(J)}o 18},\'4W-1s\':q(a,b,c){E h=U.1A;N(E i=0,18=[],J;J=a[i];i++)C(!h.5t(J)&&!h.5s(J))18.W(J);o 18},\'1x-1s\':q(a,b,c){o U.1G.1x(a,b,c)},\'1x-1O-1s\':q(a,b,c){o U.1G.1x(a,b,c,1d)},\'1x-1V-1o\':q(a,b,c){o U.1G.1x(a,b,c,1f,1d)},\'1x-1O-1V-1o\':q(a,b,c){o U.1G.1x(a,b,c,1d,1d)},\'2F-1V-1o\':q(a,b,c){o U.1G.1x(a,"1",c,1f,1d)},\'1O-1V-1o\':q(a,b,c){o U.1G.1x(a,"1",c,1d,1d)},\'4W-1V-1o\':q(a,b,c){E p=U.1G;o p[\'1O-1V-1o\'](p[\'2F-1V-1o\'](a,b,c),b,c)},9j:q(a,b,d){C(a==0)o b>0?[b]:[];o $R(1,d).2N([],q(c,i){C(0==(i-b)%a&&(i-b)/a>=0)c.W(i);o c})},1x:q(c,d,e,f,g){C(c.L==0)o[];C(d==\'9n\')d=\'2n+0\';C(d==\'9m\')d=\'2n+1\';E h=U.1A,18=[],7k=[],m;h.6n(c);N(E i=0,J;J=c[i];i++){C(!J.1B.2U){h.4M(J.1B,f,g);7k.W(J.1B)}}C(d.1l(/^\\d+$/)){d=3R(d);N(E i=0,J;J=c[i];i++)C(J.5Z==d)18.W(J)}15 C(m=d.1l(/^(-?\\d*)?n(([+-])(\\d+))?/)){C(m[1]=="-")m[1]=-1;E a=m[1]?3R(m[1]):1;E b=m[2]?3R(m[2]):0;E k=U.1G.9j(a,b,c.L);N(E i=0,J,l=k.L;J=c[i];i++){N(E j=0;j<l;j++)C(J.5Z==k[j])18.W(J)}}h.4m(c);h.4m(7k);o 18},\'5k\':q(a,b,c){N(E i=0,18=[],J;J=a[i];i++){C(J.1j==\'!\'||(J.4C&&!J.3J.1l(/^\\s*$/)))2f;18.W(J)}o 18},\'3o\':q(a,b,c){E h=U.1A,cV,m;E d=1c U(b).5r(c);h.6n(d);N(E i=0,18=[],J;J=a[i];i++)C(!J.2U)18.W(J);h.4m(d);o 18},\'9p\':q(a,b,c){N(E i=0,18=[],J;J=a[i];i++)C(!J.2x)18.W(J);o 18},\'2x\':q(a,b,c){N(E i=0,18=[],J;J=a[i];i++)C(J.2x)18.W(J);o 18},\'2O\':q(a,b,c){N(E i=0,18=[],J;J=a[i];i++)C(J.2O)18.W(J);o 18}},4V:{\'=\':q(a,v){o a==v},\'!=\':q(a,v){o a!=v},\'^=\':q(a,v){o a.7L(v)},\'$=\':q(a,v){o a.8P(v)},\'*=\':q(a,v){o a.1p(v)},\'~=\':q(a,v){o(\' \'+a+\' \').1p(\' \'+v+\' \')},\'|=\':q(a,v){o(\'-\'+a.25()+\'-\').1p(\'-\'+v.25()+\'-\')}},9i:q(a,b){E c=1c U(b).5r(),h=U.1A;h.6n(c);N(E i=0,18=[],1u;1u=a[i];i++)C(1u.2U)18.W(1u);h.4m(c);o 18},4D:q(a,b,c){C(I.6j(b)){c=b;b=1f}o U.9i(a,b||\'*\')[c||0]},6K:q(a,b){E c=b.1Y(\',\'),b=[];c.9O(/(([\\w#:.~>+()\\s-]+|\\*|\\[.*?\\])+)\\s*(,|$)/,q(m){b.W(m[1].2K())});E d=[],h=U.1A;N(E i=0,l=b.L,72;i<l;i++){72=1c U(b[i].2K());h.1z(d,72.5r(a))}o(l>1)?h.7c(d):d}});q $$(){o U.6K(V,$A(1b))}E 1e={5L:q(a){$(a).5L();o a},9g:q(c,d){C(3g d!=\'2I\')d={60:!!d};15 C(d.60===1q)d.60=1d;E e,1n,7l=1f,3w=d.3w;E f=c.2N({},q(a,b){C(!b.2x&&b.1C){e=b.1C;1n=$(b).2b();C(1n!=19&&(b.1o!=\'3w\'||(!7l&&3w!==1f&&(!3w||e==3w)&&(7l=1d)))){C(e 1t a){C(!I.3d(a[e]))a[e]=[a[e]];a[e].W(1n)}15 a[e]=1n}}o a});o d.60?f:1S.3T(f)}};1e.Y={4O:q(a,b){o 1e.9g(1e.4j(a),b)},4j:q(c){o $A($(c).3h(\'*\')).2N([],q(a,b){C(1e.G.4v[b.1j.1I()])a.W(G.M(b));o a})},cS:q(a,b,c){a=$(a);E d=a.3h(\'5J\');C(!b&&!c)o $A(d).2k(G.M);N(E i=0,7o=[],L=d.L;i<L;i++){E e=d[i];C((b&&e.1o!=b)||(c&&e.1C!=c))2f;7o.W(G.M(e))}o 7o},7p:q(a){a=$(a);1e.4j(a).6A(\'7p\');o a},6Z:q(a){a=$(a);1e.4j(a).6A(\'6Z\');o a},9d:q(b){E c=$(b).4j().4B(q(a){o\'5U\'!=a.1o&&!a.2x});E d=c.4B(q(a){o a.2E(\'6k\')&&a.6k>=0}).b4(q(a){o a.6k}).2F();o d?d:c.86(q(a){o[\'5J\',\'21\',\'7s\'].1p(a.1j.1I())})},cR:q(a){a=$(a);a.9d().9b();o a},3b:q(a,b){a=$(a),b=I.2l(b||{});E c=b.3k,4q=a.41(\'4q\')||\'\';C(4q.5i())4q=1k.cQ.ak;b.3k=a.4O(1d);C(c){C(I.2q(c))c=c.5W();I.M(b.3k,c)}C(a.2E(\'1Z\')&&!b.1Z)b.1Z=a.1Z;o 1c 1m.3B(4q,b)}};1e.G={61:q(a){$(a).61();o a},21:q(a){$(a).21();o a}};1e.G.Y={4O:q(a){a=$(a);C(!a.2x&&a.1C){E b=a.2b();C(b!=1q){E c={};c[a.1C]=b;o 1S.3T(c)}}o\'\'},2b:q(a){a=$(a);E b=a.1j.1I();o 1e.G.4v[b](a)},cP:q(a,b){a=$(a);E c=a.1j.1I();1e.G.4v[c](a,b);o a},aU:q(a){$(a).1n=\'\';o a},cO:q(a){o $(a).1n!=\'\'},9b:q(a){a=$(a);1N{a.61();C(a.21&&(a.1j.1I()!=\'5J\'||![\'7t\',\'5L\',\'3w\'].1p(a.1o)))a.21()}1R(e){}o a},7p:q(a){a=$(a);a.9N();a.2x=1d;o a},6Z:q(a){a=$(a);a.2x=1f;o a}};E cN=1e.G;E $F=1e.G.Y.2b;1e.G.4v={5J:q(a,b){4Y(a.1o.1I()){2c\'98\':2c\'97\':o 1e.G.4v.96(a,b);6r:o 1e.G.4v.7s(a,b)}},96:q(a,b){C(b===1q)o a.2O?a.1n:19;15 a.2O=!!b},7s:q(a,b){C(b===1q)o a.1n;15 a.1n=b},21:q(a,b){C(b===1q)o B[a.1o==\'21-cL\'?\'95\':\'94\'](a);15{E c,1n,93=!I.3d(b);N(E i=0,L=a.L;i<L;i++){c=a.1a[i];1n=B.5I(c);C(93){C(1n==b){c.6Q=1d;o}}15 c.6Q=b.1p(1n)}}},95:q(a){E b=a.cJ;o b>=0?B.5I(a.1a[b]):19},94:q(a){E b,L=a.L;C(!L)o 19;N(E i=0,b=[];i<L;i++){E c=a.1a[i];C(c.6Q)b.W(B.5I(c))}o b},5I:q(a){o G.M(a).2E(\'1n\')?a.1n:a.2S}};2W.66=q(){};2W.66.O={2w:q(a,b,c){B.3S=b;B.1u=$(a);B.4t=c;B.3y=B.2b();B.3Z()},3Z:q(){7X(B.3M.1i(B),B.3S*8h)},3M:q(){E a=B.2b();E b=(I.2q(B.3y)&&I.2q(a)?B.3y!=a:1F(B.3y)!=1F(a));C(b){B.4t(B.1u,a);B.3y=a}}};1e.G.6i=1y.2d();1e.G.6i.O=I.M(1c 2W.66(),{2b:q(){o 1e.G.2b(B.1u)}});1e.6i=1y.2d();1e.6i.O=I.M(1c 2W.66(),{2b:q(){o 1e.4O(B.1u)}});2W.3z=q(){};2W.3z.O={2w:q(a,b){B.1u=$(a);B.4t=b;B.3y=B.2b();C(B.1u.1j.1I()==\'83\')B.91();15 B.3Z(B.1u)},7H:q(){E a=B.2b();C(B.3y!=a){B.4t(B.1u,a);B.3y=a}},91:q(){1e.4j(B.1u).1h(B.3Z.1i(B))},3Z:q(a){C(a.1o){4Y(a.1o.1I()){2c\'98\':2c\'97\':1r.3F(a,\'8Z\',B.7H.1i(B));1E;6r:1r.3F(a,\'a0\',B.7H.1i(B));1E}}}};1e.G.3z=1y.2d();1e.G.3z.O=I.M(1c 2W.3z(),{2b:q(){o 1e.G.2b(B.1u)}});1e.3z=1y.2d();1e.3z.O=I.M(1c 2W.3z(),{2b:q(){o 1e.4O(B.1u)}});C(!1k.1r)E 1r={};I.M(1r,{cG:8,cF:9,cE:13,cD:27,cC:37,cA:38,cz:39,cw:40,cu:46,cs:36,cr:35,co:33,cm:34,ck:45,ag:[\'8Z\',\'eF\',\'ch\',\'cg\',\'aa\',\'eJ\',\'8X\',\'ab\',\'af\',\'cd\',\'8W\',\'cb\',\'ca\',\'eR\',\'eS\',\'eT\',\'21\',\'a0\',\'3w\',\'5L\',\'61\',\'9N\'],5x:{},7E:q(a){E b;4Y(a.1o){2c\'aa\':b=a.eV;1E;2c\'8X\':b=a.29;1E;6r:o 19}o G.M(b)}});1r.Y={1u:q(a){E b=a.6R;o G.M(b.2u==6h.an?b.1B:b)},4D:q(a,b){E c=1r.1u(a);o c.1l(b)?c:c.bl(b)},c6:q(a){o(((a.8V)&&(a.8V==1))||((a.7t)&&(a.7t==1)))},5H:q(a){o{x:a.ai||(a.c3+(V.3q.3v||V.1Q.3v)),y:a.al||(a.c0+(V.3q.3x||V.1Q.3x))}},f5:q(a){o 1r.5H(a).x},f6:q(a){o 1r.5H(a).y},7D:q(a){a.ax();a.ar()}};1r.M=(q(){E c=I.5N(1r.Y).2N({},q(m,a){m[a]=1r.Y[a].3C();o m});C(14.1J.3s){I.M(c,{ar:q(){B.f9=1d},ax:q(){B.fa=1f},1T:q(){o"[2I 1r]"}});o q(a){C(!a)o 1f;C(a.68)o a;a.68=14.3i;E b=1r.5H(a);I.M(a,{6R:a.fb,7E:1r.7E(a),ai:b.x,al:b.y});o I.M(a,c)}}15{1r.O=1r.O||V.56("8U").4z;I.M(1r.O,c);o 14.K}})();I.M(1r,(q(){E g=1r.5x;q 81(a){C(a.7S)o a.7S;1b.3G.1X=1b.3G.1X||1;o a.7S=++1b.3G.1X}q 7O(a){C(!1r.ag.1p(a))o"aB";o{ab:"af"}[a]||a}q 5F(a){o g[a]=g[a]||{}}q 6p(a,b){E c=5F(a);o c[b]=c[b]||[]}q 8S(b,d,e){E c=6p(b,d);C(c.4d("5E").1p(e))o 1f;E f=q(a){C(a.6P&&a.6P!=d)o 1f;1r.M(a);e.55(a.6R,a)};f.5E=e;c.W(f);o f}q 7N(b,d,e){E c=6p(b,d);o c.86(q(a){o a.5E==e})}q 8Q(a,b,d){E c=5F(a);C(!c[b])o 1f;c[b]=c[b].5e(7N(a,b,d))}q aC(){N(E a 1t g)N(E b 1t g[a])g[a][b]=19}C(1k.6b){1k.6b("ah",aC)}o{3F:q(a,b,c){a=$(a);E d=81(a),1C=7O(b);E e=8S(d,b,c);C(!e)o a;C(a.5D){a.5D(1C,e,1f)}15{a.6b("4c"+1C,e)}o a},3U:q(b,c,d){b=$(b);E e=81(b),1C=7O(c);C(!d&&c){6p(e,c).1h(q(a){b.3U(c,a.5E)});o b}15 C(!c){I.5N(5F(e)).1h(q(a){b.3U(a)});o b}E f=7N(e,c,d);C(!f)o b;C(b.8O){b.8O(1C,f,1f)}15{b.bR("4c"+1C,f)}8Q(e,c,d);o b},4w:q(a,b,c){a=$(a);C(a==V&&V.56&&!a.8N)a=V.3q;C(V.56){E d=V.56("8U");d.bQ("aB",1d,1d)}15{E d=V.bP();d.8M="bO"}d.6P=b;d.bN=c||{};C(V.56){a.8N(d)}15{a.bM(d.8M,d)}o d}}})());I.M(1r,1r.Y);G.7x({4w:1r.4w,3F:1r.3F,3U:1r.3U});I.M(V,{4w:G.Y.4w.3C(),3F:G.Y.3F.3C(),3U:G.Y.3U.3C()});(q(){E a,80=1f;q 52(){C(80)o;C(a)1k.ad(a);V.4w("bL");80=1d}C(V.5D){C(14.1J.5p){a=1k.7X(q(){C(/bK|aX/.2o(V.2P))52()},0);1r.3F(1k,"8W",52)}15{V.5D("bJ",52,1f)}}15{V.5q("<5d 1X=8J 2L "+"aj=\'9u:bH(0)\'><\\/5d>");$("8J").5Q=q(){C(B.2P=="aX"){B.5Q=19;52()}}}})();E bG={3l:G.8L};G.Y.bF=G.Y.6E;E bE={bD:q(a,b){o G.1W(a,{7M:b})},bC:q(a,b){o G.1W(a,{2h:b})},fO:q(a,b){o G.1W(a,{3K:b})},bA:q(a,b){o G.1W(a,{6s:b})}};E $2f=1c bz(\'"3u $2f" by fS, bx "o" bw\');E 8b={8H:1f,6N:q(){B.8G=1k.9B||V.3q.3v||V.1Q.3v||0;B.b6=1k.9A||V.3q.3x||V.1Q.3x||0},bv:q(a,x,y){C(B.8H)o B.8E(a,x,y);B.4S=x;B.4H=y;B.2j=G.3L(a);o(y>=B.2j[1]&&y<B.2j[1]+a.4k&&x>=B.2j[0]&&x<B.2j[0]+a.4h)},8E:q(a,x,y){E b=G.7T(a);B.4S=x+b[0]-B.8G;B.4H=y+b[1]-B.b6;B.2j=G.3L(a);o(B.4H>=B.2j[1]&&B.4H<B.2j[1]+a.4k&&B.4S>=B.2j[0]&&B.4S<B.2j[0]+a.4h)},bt:q(a,b){C(!a)o 0;C(a==\'bs\')o((B.2j[1]+b.4k)-B.4H)/b.4k;C(a==\'br\')o((B.2j[0]+b.4h)-B.4S)/b.4h},3L:G.Y.3L,53:G.Y.53,7V:q(a){8b.6N();o G.7V(a)},7U:q(a){8b.6N();o G.7U(a)},bq:G.Y.7T,3a:G.Y.6u,g8:G.Y.5c,2l:q(a,b,c){c=c||{};o G.aJ(b,a,c)}};G.6H=1y.2d();G.6H.O={2w:q(a){B.1u=$(a)},2s:q(b){B.1u.1H.3V(/\\s+/).21(q(a){o a.L>0}).2s(b)},8u:q(a){B.1u.1H=a},6m:q(a){C(B.1p(a))o;B.8u($A(B).1z(a).1Y(\' \'))},7F:q(a){C(!B.1p(a))o;B.8u($A(B).5e(a).1Y(\' \'))},24:q(){o $A(B).1Y(\' \')}};I.M(G.6H.O,22);G.7x();',62,1003,'||||||||||||||||||||||||return||function|||||||||||this|if||var||Element||Object|node||length|extend|for|prototype||||||Selector|document|push||Methods||||||Prototype|else||style|results|null|options|arguments|new|true|Form|false|position|each|bind|tagName|window|match|Ajax|value|type|include|undefined|Event|child|in|element|while|replace|nth|Class|concat|handlers|parentNode|name|transport|break|String|pseudos|className|toLowerCase|Browser|_getEv|getStyle|isFunction|try|last|xpath|body|catch|Hash|inspect|Array|of|insert|id|join|method|valueL|select|Enumerable||toString|toUpperCase|toJSON||descendant|toElement|args|getValue|case|create|Template|continue|content|top|gsub|offset|map|clone|width||test|evaluate|isString|toArray|_each|div|nodeType|key|initialize|disabled|ByTag|opacity|stripScripts|matcher|isElement|px|hasAttribute|first|toHTML|_attributeTranslations|object|BrowserFeatures|strip|defer|onComplete|inject|checked|readyState|height|values|text|indexOf|_counted|apply|Abstract|response|ps|childNodes||le|left||||||||offsetParent|request|RegExp|isArray|expression|tags|typeof|getElementsByTagName|emptyFunction|createElement|parameters|display|slice|names|not|evalScripts|documentElement|sibling|IE|success|throw|scrollLeft|submit|scrollTop|lastValue|EventObserver|table|Request|methodize|source|toPaddedString|observe|callee|_cache|dispatchException|innerHTML|bottom|cumulativeOffset|onTimerEvent|offsetTop|_returnOffset|formula|offsetLeft|Number|frequency|toQueryString|stopObserving|split|absolute|decay|_insertionTranslations|registerCallback||readAttribute|adjacent|initializeRange|appendChild|||nextSibling|tbody|subclass|substring|Heading|on|pluck|Opera|parseFloat|attr|offsetWidth|attrPresence|getElements|offsetHeight|getHeader|unmark|navigator|Responders|timer|action|evalJSON|url|callback|_overflow|Serializers|fire|filter|setOpacity|__proto__|getDimensions|findAll|firstChild|findElement|shift|userAgent|class|ycomp|HTMLElement|property|responders|post|index|interpret|serialize|getStatus|update|patterns|xcomp|laterSibling|nodeClassName|operators|only|pseudo|switch|following|criteria|responseText|fireContentLoadedEvent|positionedOffset|_|call|createEvent|writeAttribute|adjacency|pos|contains|_flag|viewportOffset|script|without|extending|ElementExtensions|reverse|blank|relative|empty|removeChild|start|delete|classNames|WebKit|write|findElements|nextElementSibling|previousElementSibling|subclasses|expr|nextSiblings|cache|recursivelyCollect|Base|TBODY|refresh|none|addEventListener|handler|getCacheForID|capitalize|pointer|optionValue|input|charAt|reset|headerJSON|keys|token|times|onreadystatechange|status|count|00001|hidden|escapeHTML|toQueryParams|eval|Response|nodeIndex|hash|focus|XPath|copy|container|application|TimedObserver|asynchronous|_extendedByPrototype|Simulated|setOptions|attachEvent|tr|onCreate|dispatch|insertion|insertBefore|Node|Observer|isNumber|tabIndex|currentlyExecuting|add|mark|_getContentFromAnonymousElement|getWrappersForEventName|title|default|after|getElementsByClassName|getOffsetParent|Gecko|_getAttr|ScriptFragment|overflow|static|invoke|string|auto|cssFloat|descendantOf|range|hasClassName|ClassNames|ObjectRange|read|findChildElements|wrap|SpecificElementExtensions|prepare|lastIndexOf|eventName|selected|target|unfilterJSON|instanceof|TableSection|attributes|failure|Updater|assertions|enable|getResponseHeader|camelize|selector|succ|and|preceding|Content|or|matches|nodeValue|tokens|predicate|unique|visibility|fragment|onStateChange|opera|respondToReadyState|getAttribute|HTML|indexed|submitted|_complete|Complete|matchingInputs|disable|statusText|len|textarea|button|evalJS|getAllResponseHeaders|encoding|addMethods|prepareReplacement|TD|activeRequestCount|elements|end|stop|relatedTarget|remove|has|onElementEvent|stripAlpha|alpha|_getStyle|startsWith|before|findWrapper|getDOMEventName|ancestor|Function|iter|_eventID|cumulativeScrollOffset|relativize|absolutize|inherit|setInterval|_reverse|Test|fired|getEventID|_madePositioned|form|constructor|visible|find|createRange|styleFloat|cssText|setStyle|Position|replaceChild|float|_methodized|getTransport|falses|1000|updater|detect|delay|_getElementsByXPath|all|collect|curry|self|array|slices|Version|previousSiblings|set|pattern|firstDescendant|template|descendants|ancestors|Pattern|attribute|with|createTextNode|withinIncludingScrolloffsets|gt|deltaX|includeScrollOffsets|TH|__onDOMContentLoaded|hide|toggle|eventType|dispatchEvent|removeEventListener|endsWith|destroyWrapper|isUndefined|createWrapper|JSON|HTMLEvents|which|load|mouseout|updateComplete|click|updateContent|registerFormCallbacks|sanitizeJSON|single|selectMany|selectOne|inputSelector|radio|checkbox|getHeaderJSON|getStatusText|activate|onException|findFirstElement|evalResponse|force|serializeElements|charCodeAt|matchElements|getIndices|byClassName|decodeURIComponent|odd|even|requestHeaders|enabled|2005|xml|unescapeHTML|Safari|javascript|local|root|overrideMimeType|compileXPathMatcher|compileMatcher|pageYOffset|pageXOffset|setRequestHeaders|client|extractScripts|TableCell|img|get|TableCol|gi|Mod|stripTags|Events|blur|scan|getResponseJSON|sub|findDOMClass|TEXTAREA|contentType|TFOOT|THEAD|SELECT|td|TR|responseXML|change|register|PeriodicalUpdater|outerHTML|IMG|XMLHTTP|lastText|ActiveXObject|specialChar|XMLHttpRequest|mouseover|keypress|isJSON|clearInterval|MobileSafari|keydown|DOMEvents|onunload|pageX|src|href|pageY|JSONFilter|TEXT_NODE|encodeURIComponent|KHTML|addPair|stopPropagation||getAttributeNode|_getAttrNode|merge|100|preventDefault|currentStyle|_readAttribute|argumentNames|dataavailable|destroyCache|htmlFor|arrayLength|setHeight|setWidth|setTop|setLeft|clonePosition|forEach|these|uniq|_originalHeight|_originalWidth|Try|_originalTop|_originalLeft|flatten|BODY|clear|right|show|complete|clientHeight|clientWidth|size|amp|lt|createContextualFragment|sortBy|scrollTo|deltaY|removeClassName|exclusive|addClassName|getWidth|ownerDocument|getHeight|setAttribute|timeout|removeAttribute|any|counter|identify|eachSlice|next|up|exec|previousSibling|immediateDescendants|toTemplateReplacements|realOffset|horizontal|vertical|overlap|parseQuery|within|instead|use|is|Error|After|selectNode|Top|Before|Insertion|childOf|Toggle|void|interpolate|DOMContentLoaded|loaded|contentloaded|fireEvent|memo|ondataavailable|createEventObject|initEvent|detachEvent|cloneNode|NOTATION_NODE|formed|number|DOCUMENT_FRAGMENT_NODE|DOCUMENT_TYPE_NODE|Badly|DOCUMENT_NODE|clientY|COMMENT_NODE|SyntaxError|clientX|PROCESSING_INSTRUCTION_NODE|ENTITY_NODE|isLeftClick|ENTITY_REFERENCE_NODE|CDATA_SECTION_NODE|Eaeflnr|abort|unload|ATTRIBUTE_NODE|keyup|ELEMENT_NODE|snapshotItem|mouseup|mousedown|snapshotLength|ORDERED_NODE_SNAPSHOT_TYPE|KEY_INSERT|XPathResult|KEY_PAGEDOWN||KEY_PAGEUP|getElementById|u00|KEY_END|KEY_HOME|x1f|KEY_DELETE|clearTimeout|KEY_DOWN|x00|0_rc0|KEY_RIGHT|KEY_UP|dasherize|KEY_LEFT|KEY_ESC|KEY_RETURN|KEY_TAB|KEY_BACKSPACE|underscore|json|selectedIndex|getAllHeaders|one|responseJSON|Field|present|setValue|location|focusFirstElement|getInputs|ecma|java|selectorType|Failure|fromCharCode|Success|children|300|200|boolean|dis|mod|setRequestHeader|unknown|close|Connection|translate|charset|starts|html|Accept|With|Requested|data|send|postBody|getScrollOffsets|inner|viewport|im|RangeError||abled|Mobile|IFrame|open|IFRAME|FrameSet|FRAMESET|Konqueror|TableRow|_method|COLGROUP|COL|TableCaption|CAPTION|Image|Anchor|DEL|INS|Quote|H6|H5|Interactive|H4|H3|Loaded|H2|Loading|H1|Directory|Uninitialized|truncate|DIR|DList|DL|OList|iPhone|OL|UList|UL|FieldSet|FIELDSET|Paragraph|TextArea|UTF|OptGroup|OPTGROUP|urlencoded|INPUT|www|FORM|specified|mixin|TABLE|setStartAfter|afterEnd|unregister|beforeEnd|collapse|selectNodeContents|afterBegin||setStartBefore|beforeBegin|Microsoft|999999|onchange|onselect|onreset|Msxml2|onsubmit|onkeyup|onkeydown|onkeypress|onblur|onfocus|onmouseout|onmousemove|finally|dblclick|onmouseover|onmouseup|onmousedown|mousemove|ondblclick|valueOf|secure|onclick|onload|multiple|readonly|error|resize|scroll|bar|fromElement|foo|longDesc|readOnly|maxLength|encType|accessKey|dateTime|vAlign|rowSpan|pointerX|pointerY|colSpan|super|cancelBubble|returnValue|srcElement|Math|floor|PeriodicalExecuter|ceil|insertAdjacentHTML|round|abs|isFinite|childElements|escape|getElementsBySelector|toColorPart|isNaN|intersect|reduce|getSeconds|compact|getMinutes|undoClipping|getHours|from|makeClipping|NodeList|undoPositioned|some|every|getDate|entries|makePositioned|member|block|getMonth|AppleWebKit|getFullYear|pop|Date|getOpacity|zip|Bottom|sort|01|getComputedStyle|deprecated|defaultView|reject|cleanWhitespace|toggleClassName|partition|min|setTimeout|max|inGroupsOf|grep|anonymous_element_|event|bindAsEventListener|previous|down|page|siblings|superclass'.split('|'),0,{}))
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'
  },
  // note: For Firefox < 1.5, OPTION and OPTGROUP tags are currently broken,
  //       due to a Firefox bug
  node: function(elementName) {
    elementName = elementName.toUpperCase();
    
    // try innerHTML approach
    var parentTag = this.NODEMAP[elementName] || 'div';
    var parentElement = document.createElement(parentTag);
    try { // prevent IE "feature": http://dev.rubyonrails.org/ticket/2707
      parentElement.innerHTML = "<" + elementName + "></" + elementName + ">";
    } catch(e) {}
    var element = parentElement.firstChild || null;
      
    // see if browser added wrapping tags
    if(element && (element.tagName.toUpperCase() != elementName))
      element = element.getElementsByTagName(elementName)[0];
    
    // fallback to createElement approach
    if(!element) element = document.createElement(elementName);
    
    // abort if nothing could be created
    if(!element) return;

    // attributes (or text)
    if(arguments[1])
      if(this._isStringOrNumber(arguments[1]) ||
        (arguments[1] instanceof Array) ||
        arguments[1].tagName) {
          this._children(element, arguments[1]);
        } else {
          var attrs = this._attributes(arguments[1]);
          if(attrs.length) {
            try { // prevent IE "feature": http://dev.rubyonrails.org/ticket/2707
              parentElement.innerHTML = "<" +elementName + " " +
                attrs + "></" + elementName + ">";
            } catch(e) {}
            element = parentElement.firstChild || null;
            // workaround firefox 1.0.X bug
            if(!element) {
              element = document.createElement(elementName);
              for(attr in arguments[1]) 
                element[attr == 'class' ? 'className' : attr] = arguments[1][attr];
            }
            if(element.tagName.toUpperCase() != elementName)
              element = parentElement.getElementsByTagName(elementName)[0];
          }
        } 

    // text, or array of children
    if(arguments[2])
      this._children(element, arguments[2]);

     return element;
  },
  _text: function(text) {
     return document.createTextNode(text);
  },

  ATTR_MAP: {
    'className': 'class',
    'htmlFor': 'for'
  },

  _attributes: function(attributes) {
    var attrs = [];
    for(attribute in attributes)
      attrs.push((attribute in this.ATTR_MAP ? this.ATTR_MAP[attribute] : attribute) +
          '="' + attributes[attribute].toString().escapeHTML().gsub(/"/,'&quot;') + '"');
    return attrs.join(" ");
  },
  _children: function(element, children) {
    if(children.tagName) {
      element.appendChild(children);
      return;
    }
    if(typeof children=='object') { // array can hold nodes and text
      children.flatten().each( function(e) {
        if(typeof e=='object')
          element.appendChild(e)
        else
          if(Builder._isStringOrNumber(e))
            element.appendChild(Builder._text(e));
      });
    } else
      if(Builder._isStringOrNumber(children))
        element.appendChild(Builder._text(children));
  },
  _isStringOrNumber: function(param) {
    return(typeof param=='string' || typeof param=='number');
  },
  build: function(html) {
    var element = this.node('div');
    $(element).update(html.strip());
    return element.down();
  },
  dump: function(scope) { 
    if(typeof scope != 'object' && typeof scope != 'function') scope = window; //global scope 
  
    var tags = ("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+/);
  
    tags.each( function(tag){ 
      scope[tag] = function() { 
        return Builder.node.apply(Builder, [tag].concat($A(arguments)));  
      } 
    });
  }
}
String.prototype.parseColor = function() {  
  var color = '#';
  if(this.slice(0,4) == 'rgb(') {  
    var cols = this.slice(4,this.length-1).split(',');  
    var i=0; do { color += parseInt(cols[i]).toColorPart() } while (++i<3);  
  } else {  
    if(this.slice(0,1) == '#') {  
      if(this.length==4) for(var i=1;i<4;i++) color += (this.charAt(i) + this.charAt(i)).toLowerCase();  
      if(this.length==7) color = this.toLowerCase();  
    }  
  }  
  return(color.length==7 ? color : (arguments[0] || this));  
}

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

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

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

Element.setContentZoom = function(element, percent) {
  element = $(element);  
  element.setStyle({fontSize: (percent/100) + 'em'});   
  if(Prototype.Browser.WebKit) window.scrollBy(0,0);
  return element;
}

Element.getInlineOpacity = function(element){
  return $(element).style.opacity || '';
}

Element.forceRerendering = function(element) {
  try {
    element = $(element);
    var n = document.createTextNode(' ');
    element.appendChild(n);
    element.removeChild(n);
  } catch(e) { }
};

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

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

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

var Effect = {
  _elementDoesNotExistError: {
    name: 'ElementDoesNotExistError',
    message: 'The specified DOM element does not exist, but is required for this effect to operate'
  },
  tagifyText: function(element) {
    if(typeof Builder == 'undefined')
      throw("Effect.tagifyText requires including script.aculo.us' builder.js library");
      
    var tagifyStyle = 'position:relative';
    if(Prototype.Browser.IE) tagifyStyle += ';zoom:1';
    
    element = $(element);
    $A(element.childNodes).each( function(child) {
      if(child.nodeType==3) {
        child.nodeValue.toArray().each( function(character) {
          element.insertBefore(
            Builder.node('span',{style: tagifyStyle},
              character == ' ' ? String.fromCharCode(160) : character), 
              child);
        });
        Element.remove(child);
      }
    });
  },
  multiple: function(element, effect) {
    var elements;
    if(((typeof element == 'object') || 
        (typeof element == 'function')) && 
       (element.length))
      elements = element;
    else
      elements = $(element).childNodes;
      
    var options = Object.extend({
      speed: 0.1,
      delay: 0.0
    }, arguments[2] || {});
    var masterDelay = options.delay;

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

var Effect2 = Effect; // deprecated

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

Effect.Transitions = {
  linear: Prototype.K,
  sinoidal: function(pos) {
    return (-Math.cos(pos*Math.PI)/2) + 0.5;
  },
  reverse: function(pos) {
    return 1-pos;
  },
  flicker: function(pos) {
    var pos = ((-Math.cos(pos*Math.PI)/4) + 0.75) + Math.random()/4;
    return (pos > 1 ? 1 : pos);
  },
  wobble: function(pos) {
    return (-Math.cos(pos*Math.PI*(9*pos))/2) + 0.5;
  },
  pulse: function(pos, pulses) { 
    pulses = pulses || 5; 
    return (
      Math.round((pos % (1/pulses)) * pulses) == 0 ? 
            ((pos * pulses * 2) - Math.floor(pos * pulses * 2)) : 
        1 - ((pos * pulses * 2) - Math.floor(pos * pulses * 2))
      );
  },
  none: function(pos) {
    return 0;
  },
  full: function(pos) {
    return 1;
  }
};

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

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

    if(!effect.options.queue.limit || (this.effects.length < effect.options.queue.limit))
      this.effects.push(effect);
    
    if(!this.interval)
      this.interval = setInterval(this.loop.bind(this), 15);
  },
  remove: function(effect) {
    this.effects = this.effects.reject(function(e) { return e==effect });
    if(this.effects.length == 0) {
      clearInterval(this.interval);
      this.interval = null;
    }
  },
  loop: function() {
    var timePos = new Date().getTime();
    for(var i=0, len=this.effects.length;i<len;i++) 
      this.effects[i] && this.effects[i].loop(timePos);
  }
});

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

Effect.DefaultOptions = {
  transition: Effect.Transitions.sinoidal,
  duration:   1.0,   // seconds
  fps:        100,   // 100= assume 66fps max.
  sync:       false, // true for combining
  from:       0.0,
  to:         1.0,
  delay:      0.0,
  queue:      'parallel'
}

Effect.Base = function() {};
Effect.Base.prototype = {
  position: null,
  start: function(options) {
    function codeForEvent(options,eventName){
      return (
        (options[eventName+'Internal'] ? 'this.options.'+eventName+'Internal(this);' : '') +
        (options[eventName] ? 'this.options.'+eventName+'(this);' : '')
      );
    }
    if(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(options,'beforeSetup')+
      (this.setup ? 'this.setup();':'')+ 
      codeForEvent(options,'afterSetup')+
      '};if(this.state=="running"){'+
      'pos=this.options.transition(pos)*'+this.fromToDelta+'+'+this.options.from+';'+
      'this.position=pos;'+
      codeForEvent(options,'beforeUpdate')+
      (this.update ? 'this.update(pos);':'')+
      codeForEvent(options,'afterUpdate')+
      '}}');
    
    this.event('beforeStart');
    if(!this.options.sync)
      Effect.Queues.get(typeof this.options.queue == 'string' ? 
        'global' : this.options.queue.scope).add(this);
  },
  loop: function(timePos) {
    if(timePos >= this.startOn) {
      if(timePos >= this.finishOn) {
        this.render(1.0);
        this.cancel();
        this.event('beforeFinish');
        if(this.finish) this.finish(); 
        this.event('afterFinish');
        return;  
      }
      var pos   = (timePos - this.startOn) / this.totalTime,
          frame = Math.round(pos * this.totalFrames);
      if(frame > this.currentFrame) {
        this.render(pos);
        this.currentFrame = frame;
      }
    }
  },
  cancel: function() {
    if(!this.options.sync)
      Effect.Queues.get(typeof this.options.queue == 'string' ? 
        'global' : this.options.queue.scope).remove(this);
    this.state = 'finished';
  },
  event: function(eventName) {
    if(this.options[eventName + 'Internal']) this.options[eventName + 'Internal'](this);
    if(this.options[eventName]) this.options[eventName](this);
  },
  inspect: function() {
    var data = $H();
    for(property in this)
      if(typeof this[property] != 'function') data[property] = this[property];
    return '#<Effect:' + data.inspect() + ',options:' + $H(this.options).inspect() + '>';
  }
}

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

Effect.Event = Class.create();
Object.extend(Object.extend(Effect.Event.prototype, Effect.Base.prototype), {
  initialize: function() {
    var options = Object.extend({
      duration: 0
    }, arguments[0] || {});
    this.start(options);
  },
  update: Prototype.emptyFunction
});

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

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

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

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

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

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

/*--------------- my own effects ------------------*/


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

Effect.SlideLeft = function(element) {
  element = $(element);
  Element.cleanWhitespace(element);
  var oldInnerRight = Element.getStyle(element.firstChild, 'right');
  return new Effect.Scale(element, 0, 
   Object.extend({ scaleContent: false, 
    scaleY: false, 
    scaleMode: 'box',
    scaleFrom: 100,
    restoreAfterFinish: true,
    beforeStartInternal: function(effect) { with(Element) {
      makePositioned(effect.element);
      makePositioned(effect.element.firstChild);
      if(window.opera) setStyle(effect.element, {top: ''});
      makeClipping(effect.element);
      show(element); }},  
    afterUpdateInternal: function(effect) { with(Element) {
      setStyle(effect.element.firstChild, {right:
        (effect.dims[0] - effect.element.clientWidth) + 'px' }); }},
    afterFinishInternal: function(effect) { with(Element) {
        [hide, undoClipping].call(effect.element); 
        undoPositioned(effect.element.firstChild);
        undoPositioned(effect.element);
        setStyle(effect.element.firstChild, {right: oldInnerRight}); }}
   }, arguments[1] || {})
  );
}


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


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

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

Effect.Appear = function(element) {
  element = $(element);
  var options = Object.extend({
  from: (element.getStyle('display') == 'none' ? 0.0 : element.getOpacity() || 0.0),
  to:   1.0,
  // force Safari to render floated elements properly
  afterFinishInternal: function(effect) {
    effect.element.forceRerendering();
  },
  beforeSetup: function(effect) {
    effect.element.setOpacity(effect.options.from).show(); 
  }}, arguments[1] || {});
  return new Effect.Opacity(element,options);
}

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

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






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

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

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

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

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

Effect.SlideUp = function(element) {
  element = $(element).cleanWhitespace();
  var oldInnerBottom = element.down().getStyle('bottom');
  return new Effect.Scale(element, window.opera ? 0 : 1,
   Object.extend({ scaleContent: false, 
    scaleX: false, 
    scaleMode: 'box',
    scaleFrom: 100,
    restoreAfterFinish: true,
    beforeStartInternal: function(effect) {
      effect.element.makePositioned();
      effect.element.down().makePositioned();
      if(window.opera) effect.element.setStyle({top: ''});
      effect.element.makeClipping().show();
    },  
    afterUpdateInternal: function(effect) {
      effect.element.down().setStyle({bottom:
        (effect.dims[0] - effect.element.clientHeight) + 'px' });
    },
    afterFinishInternal: function(effect) {
      effect.element.hide().undoClipping().undoPositioned().setStyle({bottom: oldInnerBottom});
      effect.element.down().undoPositioned();
    }
   }, arguments[1] || {})
  );
}






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

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

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

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

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

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

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

Effect.Morph = Class.create();
Object.extend(Object.extend(Effect.Morph.prototype, Effect.Base.prototype), {
  initialize: function(element) {
    this.element = $(element);
    if(!this.element) throw(Effect._elementDoesNotExistError);
    var options = Object.extend({
      style: {}
    }, arguments[1] || {});
    if (typeof options.style == 'string') {
      if(options.style.indexOf(':') == -1) {
        var cssText = '', selector = '.' + options.style;
        $A(document.styleSheets).reverse().each(function(styleSheet) {
          if (styleSheet.cssRules) cssRules = styleSheet.cssRules;
          else if (styleSheet.rules) cssRules = styleSheet.rules;
          $A(cssRules).reverse().each(function(rule) {
            if (selector == rule.selectorText) {
              cssText = rule.style.cssText;
              throw $break;
            }
          });
          if (cssText) throw $break;
        });
        this.style = cssText.parseStyle();
        options.afterFinishInternal = function(effect){
          effect.element.addClassName(effect.options.style);
          effect.transforms.each(function(transform) {
            if(transform.style != 'opacity')
              effect.element.style[transform.style] = '';
          });
        }
      } else this.style = options.style.parseStyle();
    } else this.style = $H(options.style)
    this.start(options);
  },
  setup: function(){
    function parseColor(color){
      if(!color || ['rgba(0, 0, 0, 0)','transparent'].include(color)) color = '#ffffff';
      color = color.parseColor();
      return $R(0,2).map(function(i){
        return parseInt( color.slice(i*2+1,i*2+3), 16 ) 
      });
    }
    this.transforms = this.style.map(function(pair){
      var property = pair[0], value = pair[1], unit = null;

      if(value.parseColor('#zzzzzz') != '#zzzzzz') {
        value = value.parseColor();
        unit  = 'color';
      } else if(property == 'opacity') {
        value = parseFloat(value);
        if(Prototype.Browser.IE && (!this.element.currentStyle.hasLayout))
          this.element.setStyle({zoom: 1});
      } else if(Element.CSS_LENGTH.test(value)) {
          var components = value.match(/^([\+\-]?[0-9\.]+)(.*)$/);
          value = parseFloat(components[1]);
          unit = (components.length == 3) ? components[2] : null;
      }

      var originalValue = this.element.getStyle(property);
      return { 
        style: property.camelize(), 
        originalValue: unit=='color' ? parseColor(originalValue) : parseFloat(originalValue || 0), 
        targetValue: unit=='color' ? parseColor(value) : value,
        unit: unit
      };
    }.bind(this)).reject(function(transform){
      return (
        (transform.originalValue == transform.targetValue) ||
        (
          transform.unit != 'color' &&
          (isNaN(transform.originalValue) || isNaN(transform.targetValue))
        )
      )
    });
  },
  update: function(position) {
    var style = {}, transform, i = this.transforms.length;
    while(i--)
      style[(transform = this.transforms[i]).style] = 
        transform.unit=='color' ? '#'+
          (Math.round(transform.originalValue[0]+
            (transform.targetValue[0]-transform.originalValue[0])*position)).toColorPart() +
          (Math.round(transform.originalValue[1]+
            (transform.targetValue[1]-transform.originalValue[1])*position)).toColorPart() +
          (Math.round(transform.originalValue[2]+
            (transform.targetValue[2]-transform.originalValue[2])*position)).toColorPart() :
        transform.originalValue + Math.round(
          ((transform.targetValue - transform.originalValue) * position) * 1000)/1000 + transform.unit;
    this.element.setStyle(style, true);
  }
});

Effect.Transform = Class.create();
Object.extend(Effect.Transform.prototype, {
  initialize: function(tracks){
    this.tracks  = [];
    this.options = arguments[1] || {};
    this.addTracks(tracks);
  },
  addTracks: function(tracks){
    tracks.each(function(track){
      var data = $H(track).values().first();
      this.tracks.push($H({
        ids:     $H(track).keys().first(),
        effect:  Effect.Morph,
        options: { style: data }
      }));
    }.bind(this));
    return this;
  },
  play: function(){
    return new Effect.Parallel(
      this.tracks.map(function(track){
        var elements = [$(track.ids) || $$(track.ids)].flatten();
        return elements.map(function(e){ return new track.effect(e, Object.extend({ sync:true }, track.options)) });
      }).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.prototype.parseStyle = function(){
  var element = document.createElement('div');
  element.innerHTML = '<div style="' + this + '"></div>';
  var style = element.childNodes[0].style, styleRules = $H();
  
  Element.CSS_PROPERTIES.each(function(property){
    if(style[property]) styleRules[property] = style[property]; 
  });
  if(Prototype.Browser.IE && this.indexOf('opacity') > -1) {
    styleRules.opacity = this.match(/opacity:\s*((?:0|1)?(?:\.\d*)?)/)[1];
  }
  return styleRules;
};

Element.morph = function(element, style) {
  new Effect.Morph(element, Object.extend({ style: style }, arguments[2] || {}));
  return element;
};

['getInlineOpacity','forceRerendering','setContentZoom',
 'collectTextNodes','collectTextNodesIgnoreClass','morph'].each( 
  function(f) { Element.Methods[f] = Element[f]; }
);

Element.Methods.visualEffect = function(element, effect, options) {
  s = effect.dasherize().camelize();
  effect_class = s.charAt(0).toUpperCase() + s.substring(1);
  new Effect[effect_class](element, options);
  return $(element);
};

Element.addMethods();

if(typeof Effect == 'undefined')
  throw("dragdrop.js requires including script.aculo.us' effects.js library");

var Droppables = {
  drops: [],

  remove: function(element) {
    this.drops = this.drops.reject(function(d) { return d.element==$(element) });
  },

  add: function(element) {
    element = $(element);
    var options = Object.extend({
      greedy:     true,
      hoverclass: null,
      tree:       false
    }, arguments[1] || {});

    // cache containers
    if(options.containment) {
      options._containers = [];
      var containment = options.containment;
      if((typeof containment == 'object') && 
        (containment.constructor == Array)) {
        containment.each( function(c) { options._containers.push($(c)) });
      } else {
        options._containers.push($(containment));
      }
    }
    
    if(options.accept) options.accept = [options.accept].flatten();

    Element.makePositioned(element); // fix IE
    options.element = element;

    this.drops.push(options);
  },
  
  findDeepestChild: function(drops) {
    deepest = drops[0];
      
    for (i = 1; i < drops.length; ++i)
      if (Element.isParent(drops[i].element, deepest.element))
        deepest = drops[i];
    
    return deepest;
  },

  isContained: function(element, drop) {
    var containmentNode;
    if(drop.tree) {
      containmentNode = element.treeNode; 
    } else {
      containmentNode = element.parentNode;
    }
    return drop._containers.detect(function(c) { return containmentNode == c });
  },
  
  isAffected: function(point, element, drop) {
    return (
      (drop.element!=element) &&
      ((!drop._containers) ||
        this.isContained(element, drop)) &&
      ((!drop.accept) ||
        (Element.classNames(element).detect( 
          function(v) { return drop.accept.include(v) } ) )) &&
      Position.within(drop.element, point[0], point[1]) );
  },

  deactivate: function(drop) {
    if(drop.hoverclass)
      Element.removeClassName(drop.element, drop.hoverclass);
    this.last_active = null;
  },

  activate: function(drop) {
    if(drop.hoverclass)
      Element.addClassName(drop.element, drop.hoverclass);
    this.last_active = drop;
  },

  show: function(point, element) {
    if(!this.drops.length) return;
    var affected = [];
    
    if(this.last_active) this.deactivate(this.last_active);
    this.drops.each( function(drop) {
      if(Droppables.isAffected(point, element, drop))
        affected.push(drop);
    });
        
    if(affected.length>0) {
      drop = Droppables.findDeepestChild(affected);
      Position.within(drop.element, point[0], point[1]);
      if(drop.onHover)
        drop.onHover(element, drop.element, Position.overlap(drop.overlap, drop.element));
      
      Droppables.activate(drop);
    }
  },

  fire: function(event, element) {
    if(!this.last_active) return;
    Position.prepare();

    if (this.isAffected([Event.pointerX(event), Event.pointerY(event)], element, this.last_active))
      if (this.last_active.onDrop) {
        this.last_active.onDrop(element, this.last_active.element, event); 
        return true; 
      }
  },

  reset: function() {
    if(this.last_active)
      this.deactivate(this.last_active);
  }
}

var Draggables = {
  drags: [],
  observers: [],
  
  register: function(draggable) {
    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(draggable);
  },
  
  unregister: function(draggable) {
    this.drags = this.drags.reject(function(d) { return d==draggable });
    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(draggable) {
    if(draggable.options.delay) { 
      this._timeout = setTimeout(function() { 
        Draggables._timeout = null; 
        window.focus(); 
        Draggables.activeDraggable = draggable; 
      }.bind(this), draggable.options.delay); 
    } else {
      window.focus(); // allows keypress events if window isn't currently focused, fails for Safari
      this.activeDraggable = draggable;
    }
  },
  
  deactivate: function() {
    this.activeDraggable = null;
  },
  
  updateDrag: function(event) {
    if(!this.activeDraggable) return;
    var pointer = [Event.pointerX(event), Event.pointerY(event)];
    // Mozilla-based browsers fire successive mousemove events with
    // the same coordinates, prevent needless redrawing (moz bug?)
    if(this._lastPointer && (this._lastPointer.inspect() == pointer.inspect())) return;
    this._lastPointer = pointer;
    
    this.activeDraggable.updateDrag(event, pointer);
  },
  
  endDrag: function(event) {
    if(this._timeout) { 
      clearTimeout(this._timeout); 
      this._timeout = null; 
    }
    if(!this.activeDraggable) return;
    this._lastPointer = null;
    this.activeDraggable.endDrag(event);
    this.activeDraggable = null;
  },
  
  keyPress: function(event) {
    if(this.activeDraggable)
      this.activeDraggable.keyPress(event);
  },
  
  addObserver: function(observer) {
    this.observers.push(observer);
    this._cacheObserverCallbacks();
  },
  
  removeObserver: function(element) {  // element instead of observer fixes mem leaks
    this.observers = this.observers.reject( function(o) { return o.element==element });
    this._cacheObserverCallbacks();
  },
  
  notify: function(eventName, draggable, event) {  // 'onStart', 'onEnd', 'onDrag'
    if(this[eventName+'Count'] > 0)
      this.observers.each( function(o) {
        if(o[eventName]) o[eventName](eventName, draggable, event);
      });
    if(draggable.options[eventName]) draggable.options[eventName](draggable, event);
  },
  
  _cacheObserverCallbacks: function() {
    ['onStart','onEnd','onDrag'].each( function(eventName) {
      Draggables[eventName+'Count'] = Draggables.observers.select(
        function(o) { return o[eventName]; }
      ).length;
    });
  }
}

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

var Draggable = Class.create();
Draggable._dragging    = {};

Draggable.prototype = {
  initialize: function(element) {
    var defaults = {
      handle: false,
      reverteffect: function(element, top_offset, left_offset) {
        var dur = Math.sqrt(Math.abs(top_offset^2)+Math.abs(left_offset^2))*0.02;
        new Effect.Move(element, { x: -left_offset, y: -top_offset, duration: dur,
          queue: {scope:'_draggable', position:'end'}
        });
      },
      endeffect: function(element) {
        var toOpacity = typeof element._opacity == 'number' ? element._opacity : 1.0;
        new Effect.Opacity(element, {duration:0.2, from:0.7, to:toOpacity, 
          queue: {scope:'_draggable', position:'end'},
          afterFinish: function(){ 
            Draggable._dragging[element] = false 
          }
        }); 
      },
      zindex: 1000,
      revert: false,
      quiet: false,
      scroll: false,
      scrollSensitivity: 20,
      scrollSpeed: 15,
      snap: false,  // false, or xy or [x,y] or function(x,y){ return [x,y] }
      delay: 0
    };
    
    if(!arguments[1] || typeof arguments[1].endeffect == 'undefined')
      Object.extend(defaults, {
        starteffect: function(element) {
          element._opacity = Element.getOpacity(element);
          Draggable._dragging[element] = true;
          new Effect.Opacity(element, {duration:0.2, from:element._opacity, to:0.7}); 
        }
      });
    
    var options = Object.extend(defaults, arguments[1] || {});

    this.element = $(element);
    
    if(options.handle && (typeof options.handle == 'string'))
      this.handle = this.element.down('.'+options.handle, 0);
    
    if(!this.handle) this.handle = $(options.handle);
    if(!this.handle) this.handle = this.element;
    
    if(options.scroll && !options.scroll.scrollTo && !options.scroll.outerHTML) {
      options.scroll = $(options.scroll);
      this._isScrollChild = Element.childOf(this.element, options.scroll);
    }

    Element.makePositioned(this.element); // fix IE    

    this.delta    = this.currentDelta();
    this.options  = options;
    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(event) {
    if(typeof Draggable._dragging[this.element] != 'undefined' &&
      Draggable._dragging[this.element]) return;
    if(Event.isLeftClick(event)) {    
      // abort on form elements, fixes a Firefox issue
      var src = Event.element(event);
      if((tag_name = src.tagName.toUpperCase()) && (
        tag_name=='INPUT' ||
        tag_name=='SELECT' ||
        tag_name=='OPTION' ||
        tag_name=='BUTTON' ||
        tag_name=='TEXTAREA')) return;
        
      var pointer = [Event.pointerX(event), Event.pointerY(event)];
      var pos     = Position.cumulativeOffset(this.element);
      this.offset = [0,1].map( function(i) { return (pointer[i] - pos[i]) });
      
      Draggables.activate(this);
      Event.stop(event);
    }
  },
  
  startDrag: function(event) {
    this.dragging = true;
    
    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);
      Position.absolutize(this.element);
      this.element.parentNode.insertBefore(this._clone, this.element);
    }
    
    if(this.options.scroll) {
      if (this.options.scroll == window) {
        var where = this._getWindowScroll(this.options.scroll);
        this.originalScrollLeft = where.left;
        this.originalScrollTop = where.top;
      } else {
        this.originalScrollLeft = this.options.scroll.scrollLeft;
        this.originalScrollTop = this.options.scroll.scrollTop;
      }
    }
    
    Draggables.notify('onStart', this, event);
        
    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);
    }
    
    // fix AppleWebKit rendering
    if(Prototype.Browser.WebKit) window.scrollBy(0,0);
    
    Event.stop(event);
  },
  
  finishDrag: function(event, success) {
    this.dragging = false;
    
    if(this.options.quiet){
      Position.prepare();
      var pointer = [Event.pointerX(event), Event.pointerY(event)];
      Droppables.show(pointer, this.element);
    }

    if(this.options.ghosting) {
      Position.relativize(this.element);
      Element.remove(this._clone);
      this._clone = null;
    }

    var dropped = false; 
    if(success) { 
      dropped = Droppables.fire(event, this.element); 
      if (!dropped) dropped = false; 
    }
    if(dropped && this.options.onDropped) this.options.onDropped(this.element);
    Draggables.notify('onEnd', this, event);

    var revert = this.options.revert;
    if(revert && typeof revert == 'function') revert = revert(this.element);
    
    var d = this.currentDelta();
    if(revert && this.options.reverteffect) {
      if (dropped == 0 || revert != 'failure')
        this.options.reverteffect(this.element,
          d[1]-this.delta[1], d[0]-this.delta[0]);
    } else {
      this.delta = d;
    }

    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(event) {
    if(event.keyCode!=Event.KEY_ESC) return;
    this.finishDrag(event, false);
    Event.stop(event);
  },
  
  endDrag: function(event) {
    if(!this.dragging) return;
    this.stopScrolling();
    this.finishDrag(event, true);
    Event.stop(event);
  },
  
  draw: function(point) {
    var pos = Position.cumulativeOffset(this.element);
    if(this.options.ghosting) {
      var r   = Position.realOffset(this.element);
      pos[0] += r[0] - Position.deltaX; pos[1] += r[1] - Position.deltaY;
    }
    
    var d = this.currentDelta();
    pos[0] -= d[0]; pos[1] -= d[1];
    
    if(this.options.scroll && (this.options.scroll != window && this._isScrollChild)) {
      pos[0] -= this.options.scroll.scrollLeft-this.originalScrollLeft;
      pos[1] -= this.options.scroll.scrollTop-this.originalScrollTop;
    }
    
    var p = [0,1].map(function(i){ 
      return (point[i]-pos[i]-this.offset[i]) 
    }.bind(this));
    
    if(this.options.snap) {
      if(typeof this.options.snap == 'function') {
        p = this.options.snap(p[0],p[1],this);
      } else {
      if(this.options.snap instanceof Array) {
        p = p.map( function(v, i) {
          return Math.round(v/this.options.snap[i])*this.options.snap[i] }.bind(this))
      } else {
        p = p.map( function(v) {
          return Math.round(v/this.options.snap)*this.options.snap }.bind(this))
      }
    }}
    
    var style = this.element.style;
    if((!this.options.constraint) || (this.options.constraint=='horizontal'))
      style.left = p[0] + "px";
    if((!this.options.constraint) || (this.options.constraint=='vertical'))
      style.top  = p[1] + "px";
    
    if(style.visibility=="hidden") style.visibility = ""; // fix gecko rendering
  },
  
  stopScrolling: function() {
    if(this.scrollInterval) {
      clearInterval(this.scrollInterval);
      this.scrollInterval = null;
      Draggables._lastScrollPointer = null;
    }
  },
  
  startScrolling: function(speed) {
    if(!(speed[0] || speed[1])) return;
    this.scrollSpeed = [speed[0]*this.options.scrollSpeed,speed[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 };
  }
}

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

var SortableObserver = Class.create();
SortableObserver.prototype = {
  initialize: function(element, observer) {
    this.element   = $(element);
    this.observer  = observer;
    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(element) {
    while (element.tagName.toUpperCase() != "BODY") {  
      if(element.id && Sortable.sortables[element.id]) return element;
      element = element.parentNode;
    }
  },

  options: function(element) {
    element = Sortable._findRootElement($(element));
    if(!element) return;
    return Sortable.sortables[element.id];
  },
  
  destroy: function(element){
    var s = Sortable.options(element);
    
    if(s) {
      Draggables.removeObserver(s.element);
      s.droppables.each(function(d){ Droppables.remove(d) });
      s.draggables.invoke('destroy');
      
      delete Sortable.sortables[s.element.id];
    }
  },

  create: function(element) {
    element = $(element);
    var options = Object.extend({ 
      element:     element,
      tag:         'li',       // assumes li children, override with tag: 'tagname'
      dropOnEmpty: false,
      tree:        false,
      treeTag:     'ul',
      overlap:     'vertical', // one of 'vertical', 'horizontal'
      constraint:  'vertical', // one of 'vertical', 'horizontal', false
      containment: element,    // also takes array of elements (or id's); or false
      handle:      false,      // or a CSS class
      only:        false,
      delay:       0,
      hoverclass:  null,
      ghosting:    false,
      quiet:       false, 
      scroll:      false,
      scrollSensitivity: 20,
      scrollSpeed: 15,
      format:      this.SERIALIZE_RULE,
      
      // these take arrays of elements or ids and can be 
      // used for better initialization performance
      elements:    false,
      handles:     false,
      
      onChange:    Prototype.emptyFunction,
      onUpdate:    Prototype.emptyFunction
    }, arguments[1] || {});

    // clear any old sortable with same element
    this.destroy(element);

    // build options for the draggables
    var options_for_draggable = {
      revert:      true,
      quiet:       options.quiet,
      scroll:      options.scroll,
      scrollSpeed: options.scrollSpeed,
      scrollSensitivity: options.scrollSensitivity,
      delay:       options.delay,
      ghosting:    options.ghosting,
      constraint:  options.constraint,
      handle:      options.handle };

    if(options.starteffect)
      options_for_draggable.starteffect = options.starteffect;

    if(options.reverteffect)
      options_for_draggable.reverteffect = options.reverteffect;
    else
      if(options.ghosting) options_for_draggable.reverteffect = function(element) {
        element.style.top  = 0;
        element.style.left = 0;
      };

    if(options.endeffect)
      options_for_draggable.endeffect = options.endeffect;

    if(options.zindex)
      options_for_draggable.zindex = options.zindex;

    // build options for the droppables  
    var options_for_droppable = {
      overlap:     options.overlap,
      containment: options.containment,
      tree:        options.tree,
      hoverclass:  options.hoverclass,
      onHover:     Sortable.onHover
    }
    
    var options_for_tree = {
      onHover:      Sortable.onEmptyHover,
      overlap:      options.overlap,
      containment:  options.containment,
      hoverclass:   options.hoverclass
    }

    // fix for gecko engine
    Element.cleanWhitespace(element); 

    options.draggables = [];
    options.droppables = [];

    // drop on empty handling
    if(options.dropOnEmpty || options.tree) {
      Droppables.add(element, options_for_tree);
      options.droppables.push(element);
    }

    (options.elements || this.findElements(element, options) || []).each( function(e,i) {
      var handle = options.handles ? $(options.handles[i]) :
        (options.handle ? $(e).getElementsByClassName(options.handle)[0] : e); 
      options.draggables.push(
        new Draggable(e, Object.extend(options_for_draggable, { handle: handle })));
      Droppables.add(e, options_for_droppable);
      if(options.tree) e.treeNode = element;
      options.droppables.push(e);      
    });
    
    if(options.tree) {
      (Sortable.findTreeElements(element, options) || []).each( function(e) {
        Droppables.add(e, options_for_tree);
        e.treeNode = element;
        options.droppables.push(e);
      });
    }

    // keep reference
    this.sortables[element.id] = options;

    // for onupdate
    Draggables.addObserver(new SortableObserver(element, options.onUpdate));

  },

  // return all suitable-for-sortable elements in a guaranteed order
  findElements: function(element, options) {
    return Element.findChildren(
      element, options.only, options.tree ? true : false, options.tag);
  },
  
  findTreeElements: function(element, options) {
    return Element.findChildren(
      element, options.only, options.tree ? true : false, options.treeTag);
  },

  onHover: function(element, dropon, overlap) {
    if(Element.isParent(dropon, element)) return;

    if(overlap > .33 && overlap < .66 && Sortable.options(dropon).tree) {
      return;
    } else if(overlap>0.5) {
      Sortable.mark(dropon, 'before');
      if(dropon.previousSibling != element) {
        var oldParentNode = element.parentNode;
        element.style.visibility = "hidden"; // fix gecko rendering
        dropon.parentNode.insertBefore(element, dropon);
        if(dropon.parentNode!=oldParentNode) 
          Sortable.options(oldParentNode).onChange(element);
        Sortable.options(dropon.parentNode).onChange(element);
      }
    } else {
      Sortable.mark(dropon, 'after');
      var nextElement = dropon.nextSibling || null;
      if(nextElement != element) {
        var oldParentNode = element.parentNode;
        element.style.visibility = "hidden"; // fix gecko rendering
        dropon.parentNode.insertBefore(element, nextElement);
        if(dropon.parentNode!=oldParentNode) 
          Sortable.options(oldParentNode).onChange(element);
        Sortable.options(dropon.parentNode).onChange(element);
      }
    }
  },
  
  onEmptyHover: function(element, dropon, overlap) {
    var oldParentNode = element.parentNode;
    var droponOptions = Sortable.options(dropon);
        
    if(!Element.isParent(dropon, element)) {
      var index;
      
      var children = Sortable.findElements(dropon, {tag: droponOptions.tag, only: droponOptions.only});
      var child = null;
            
      if(children) {
        var offset = Element.offsetSize(dropon, droponOptions.overlap) * (1.0 - overlap);
        
        for (index = 0; index < children.length; index += 1) {
          if (offset - Element.offsetSize (children[index], droponOptions.overlap) >= 0) {
            offset -= Element.offsetSize (children[index], droponOptions.overlap);
          } else if (offset - (Element.offsetSize (children[index], droponOptions.overlap) / 2) >= 0) {
            child = index + 1 < children.length ? children[index + 1] : null;
            break;
          } else {
            child = children[index];
            break;
          }
        }
      }
      
      dropon.insertBefore(element, child);
      
      Sortable.options(oldParentNode).onChange(element);
      droponOptions.onChange(element);
    }
  },

  unmark: function() {
    if(Sortable._marker) Sortable._marker.hide();
  },

  mark: function(dropon, position) {
    // mark on ghosting only
    var sortable = Sortable.options(dropon.parentNode);
    if(sortable && !sortable.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 offsets = Position.cumulativeOffset(dropon);
    Sortable._marker.setStyle({left: offsets[0]+'px', top: offsets[1] + 'px'});
    
    if(position=='after')
      if(sortable.overlap == 'horizontal') 
        Sortable._marker.setStyle({left: (offsets[0]+dropon.clientWidth) + 'px'});
      else
        Sortable._marker.setStyle({top: (offsets[1]+dropon.clientHeight) + 'px'});
    
    Sortable._marker.show();
  },
  
  _tree: function(element, options, parent) {
    var children = Sortable.findElements(element, options) || [];
  
    for (var i = 0; i < children.length; ++i) {
      var match = children[i].id.match(options.format);

      if (!match) continue;
      
      var child = {
        id: encodeURIComponent(match ? match[1] : null),
        element: element,
        parent: parent,
        children: [],
        position: parent.children.length,
        container: $(children[i]).down(options.treeTag)
      }
      
      /* Get the element containing the children and recurse over it */
      if (child.container)
        this._tree(child.container, options, child)
      
      parent.children.push (child);
    }

    return parent; 
  },

  tree: function(element) {
    element = $(element);
    var sortableOptions = this.options(element);
    var options = Object.extend({
      tag: sortableOptions.tag,
      treeTag: sortableOptions.treeTag,
      only: sortableOptions.only,
      name: element.id,
      format: sortableOptions.format
    }, arguments[1] || {});
    
    var root = {
      id: null,
      parent: null,
      children: [],
      container: element,
      position: 0
    }
    
    return Sortable._tree(element, options, root);
  },

  /* Construct a [i] index for a particular node */
  _constructIndex: function(node) {
    var index = '';
    do {
      if (node.id) index = '[' + node.position + ']' + index;
    } while ((node = node.parent) != null);
    return index;
  },

  sequence: function(element) {
    element = $(element);
    var options = Object.extend(this.options(element), arguments[1] || {});
    
    return $(this.findElements(element, options) || []).map( function(item) {
      return item.id.match(options.format) ? item.id.match(options.format)[1] : '';
    });
  },

  setSequence: function(element, new_sequence) {
    element = $(element);
    var options = Object.extend(this.options(element), arguments[2] || {});
    
    var nodeMap = {};
    this.findElements(element, options).each( function(n) {
        if (n.id.match(options.format))
            nodeMap[n.id.match(options.format)[1]] = [n, n.parentNode];
        n.parentNode.removeChild(n);
    });
   
    new_sequence.each(function(ident) {
      var n = nodeMap[ident];
      if (n) {
        n[1].appendChild(n[0]);
        delete nodeMap[ident];
      }
    });
  },
  
  serialize: function(element) {
    element = $(element);
    var options = Object.extend(Sortable.options(element), arguments[1] || {});
    var name = encodeURIComponent(
      (arguments[1] && arguments[1].name) ? arguments[1].name : element.id);
    
    if (options.tree) {
      return Sortable.tree(element, arguments[1]).children.map( function (item) {
        return [name + Sortable._constructIndex(item) + "[id]=" + 
                encodeURIComponent(item.id)].concat(item.children.map(arguments.callee));
      }).flatten().join('&');
    } else {
      return Sortable.sequence(element, arguments[1]).map( function(item) {
        return name + "[]=" + encodeURIComponent(item);
      }).join('&');
    }
  }
}

// Returns true if child is contained within element
Element.isParent = function(child, element) {
  if (!child.parentNode || child == element) return false;
  if (child.parentNode == element) return true;
  return Element.isParent(child.parentNode, element);
}

Element.findChildren = function(element, only, recursive, tagName) {   
  if(!element.hasChildNodes()) return null;
  tagName = tagName.toUpperCase();
  if(only) only = [only].flatten();
  var elements = [];
  $A(element.childNodes).each( function(e) {
    if(e.tagName && e.tagName.toUpperCase()==tagName &&
      (!only || (Element.classNames(e).detect(function(v) { return only.include(v) }))))
        elements.push(e);
    if(recursive) {
      var grandchildren = Element.findChildren(e, only, recursive, tagName);
      if(grandchildren) elements.push(grandchildren);
    }
  });

  return (elements.length>0 ? elements.flatten() : []);
}

Element.offsetSize = function (element, type) {
  return element['offset' + ((type=='vertical' || type=='height') ? 'Height' : 'Width')];
}
if(typeof Effect == 'undefined')
  throw("controls.js requires including script.aculo.us' effects.js library");

var Autocompleter = {}
Autocompleter.Base = function() {};
Autocompleter.Base.prototype = {
  baseInitialize: function(element, update, options) {
    element          = $(element)
    this.element     = element; 
    this.update      = $(update);  
    this.hasFocus    = false; 
    this.changed     = false; 
    this.active      = false; 
    this.index       = 0;     
    this.entryCount  = 0;

    if(this.setOptions)
      this.setOptions(options);
    else
      this.options = options || {};

    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(element, update){ 
        if(!update.style.position || update.style.position=='absolute') {
          update.style.position = 'absolute';
          Position.clone(element, update, {
            setHeight: false, 
            offsetTop: element.offsetHeight
          });
        }
        Effect.Appear(update,{duration:0.15});
      };
    this.options.onHide = this.options.onHide || 
      function(element, update){ new Effect.Fade(update,{duration:0.15}) };

    if(typeof(this.options.tokens) == 'string') 
      this.options.tokens = new Array(this.options.tokens);

    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, 'keypress', this.onKeyPress.bindAsEventListener(this));

    Event.observe(window, 'beforeunload', function(){ 
      element.setAttribute('autocomplete', 'on'); 
    });
  },

  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(event) {
    if(this.active)
      switch(event.keyCode) {
       case Event.KEY_TAB:
       case Event.KEY_RETURN:
         this.selectEntry();
         Event.stop(event);
       case Event.KEY_ESC:
         this.hide();
         this.active = false;
         Event.stop(event);
         return;
       case Event.KEY_LEFT:
       case Event.KEY_RIGHT:
         return;
       case Event.KEY_UP:
         this.markPrevious();
         this.render();
         if(Prototype.Browser.WebKit) Event.stop(event);
         return;
       case Event.KEY_DOWN:
         this.markNext();
         this.render();
         if(Prototype.Browser.WebKit) Event.stop(event);
         return;
      }
     else 
       if(event.keyCode==Event.KEY_TAB || event.keyCode==Event.KEY_RETURN || 
         (Prototype.Browser.WebKit > 0 && event.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(event) {
    var element = Event.findElement(event, 'LI');
    if(this.index != element.autocompleteIndex) 
    {
        this.index = element.autocompleteIndex;
        this.render();
    }
    Event.stop(event);
  },
  
  onClick: function(event) {
    var element = Event.findElement(event, 'LI');
    this.index = element.autocompleteIndex;
    this.selectEntry();
    this.hide();
  },
  
  onBlur: function(event) {
    // needed to make click events working
    setTimeout(this.hide.bind(this), 250);
    this.hasFocus = false;
    this.active = false;     
  }, 
  
  render: function() {
    if(this.entryCount > 0) {
      for (var i = 0; i < this.entryCount; i++)
        this.index==i ? 
          Element.addClassName(this.getEntry(i),"selected") : 
          Element.removeClassName(this.getEntry(i),"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(index) {
    return this.update.firstChild.childNodes[index];
  },
  
  getCurrentEntry: function() {
    return this.getEntry(this.index);
  },
  
  selectEntry: function() {
    this.active = false;
    this.updateElement(this.getCurrentEntry());
  },

  updateElement: function(selectedElement) {
    if (this.options.updateElement) {
      this.options.updateElement(selectedElement);
      return;
    }
    var value = '';
    if (this.options.select) {
      var nodes = document.getElementsByClassName(this.options.select, selectedElement) || [];
      if(nodes.length>0) value = Element.collectTextNodes(nodes[0], this.options.select);
    } else
      value = Element.collectTextNodesIgnoreClass(selectedElement, 'informal');
    
    var lastTokenPos = this.findLastToken();
    if (lastTokenPos != -1) {
      var newValue = this.element.value.substr(0, lastTokenPos + 1);
      var whitespace = this.element.value.substr(lastTokenPos + 1).match(/^\s+/);
      if (whitespace)
        newValue += whitespace[0];
      this.element.value = newValue + value;
    } else {
      this.element.value = value;
    }
    this.element.focus();
    
    if (this.options.afterUpdateElement)
      this.options.afterUpdateElement(this.element, selectedElement);
  },

  updateChoices: function(choices) {
    if(!this.changed && this.hasFocus) {
      this.update.innerHTML = choices;
      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 i = 0; i < this.entryCount; i++) {
          var entry = this.getEntry(i);
          entry.autocompleteIndex = i;
          this.addObservers(entry);
        }
      } 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(element) {
    Event.observe(element, "mouseover", this.onHover.bindAsEventListener(this));
    Event.observe(element, "click", this.onClick.bindAsEventListener(this));
  },

  onObserverEvent: function() {
    this.changed = false;   
    if(this.getToken().length>=this.options.minChars) {
      this.getUpdatedChoices();
    } else {
      this.active = false;
      this.hide();
    }
  },

  getToken: function() {
    var tokenPos = this.findLastToken();
    if (tokenPos != -1)
      var ret = this.element.value.substr(tokenPos + 1).replace(/^\s+/,'').replace(/\s+$/,'');
    else
      var ret = this.element.value;

    return /\n/.test(ret) ? '' : ret;
  },

  findLastToken: function() {
    var lastTokenPos = -1;

    for (var i=0; i<this.options.tokens.length; i++) {
      var thisTokenPos = this.element.value.lastIndexOf(this.options.tokens[i]);
      if (thisTokenPos > lastTokenPos)
        lastTokenPos = thisTokenPos;
    }
    return lastTokenPos;
  }
}

Ajax.Autocompleter = Class.create();
Object.extend(Object.extend(Ajax.Autocompleter.prototype, Autocompleter.Base.prototype), {
  initialize: function(element, update, url, options) {
    this.baseInitialize(element, update, options);
    this.options.asynchronous  = true;
    this.options.onComplete    = this.onComplete.bind(this);
    this.options.defaultParams = this.options.parameters || null;
    this.url                   = url;
  },

  getUpdatedChoices: function() {
    this.startIndicator();
    
    var entry = encodeURIComponent(this.options.paramName) + '=' + 
      encodeURIComponent(this.getToken());

    this.options.parameters = this.options.callback ?
      this.options.callback(this.element, entry) : entry;

    if(this.options.defaultParams) 
      this.options.parameters += '&' + this.options.defaultParams;
    
    new Ajax.Request(this.url, this.options);
  },

  onComplete: function(request) {
    this.updateChoices(request.responseText);
  }

});
Autocompleter.Local = Class.create();
Autocompleter.Local.prototype = Object.extend(new Autocompleter.Base(), {
  initialize: function(element, update, array, options) {
    this.baseInitialize(element, update, options);
    this.options.array = array;
  },

  getUpdatedChoices: function() {
    this.updateChoices(this.options.selector(this));
  },

  setOptions: function(options) {
    this.options = Object.extend({
      choices: 10,
      partialSearch: true,
      partialChars: 2,
      ignoreCase: true,
      fullSearch: false,
      selector: function(instance) {
        var ret       = []; // Beginning matches
        var partial   = []; // Inside matches
        var entry     = instance.getToken();
        var count     = 0;

        for (var i = 0; i < instance.options.array.length &&  
          ret.length < instance.options.choices ; i++) { 

          var elem = instance.options.array[i];
          var foundPos = instance.options.ignoreCase ? 
            elem.toLowerCase().indexOf(entry.toLowerCase()) : 
            elem.indexOf(entry);

          while (foundPos != -1) {
            if (foundPos == 0 && elem.length != entry.length) { 
              ret.push("<li><strong>" + elem.substr(0, entry.length) + "</strong>" + 
                elem.substr(entry.length) + "</li>");
              break;
            } else if (entry.length >= instance.options.partialChars && 
              instance.options.partialSearch && foundPos != -1) {
              if (instance.options.fullSearch || /\s/.test(elem.substr(foundPos-1,1))) {
                partial.push("<li>" + elem.substr(0, foundPos) + "<strong>" +
                  elem.substr(foundPos, entry.length) + "</strong>" + elem.substr(
                  foundPos + entry.length) + "</li>");
                break;
              }
            }

            foundPos = instance.options.ignoreCase ? 
              elem.toLowerCase().indexOf(entry.toLowerCase(), foundPos + 1) : 
              elem.indexOf(entry, foundPos + 1);

          }
        }
        if (partial.length)
          ret = ret.concat(partial.slice(0, instance.options.choices - ret.length))
        return "<ul>" + ret.join('') + "</ul>";
      }
    }, options || {});
  }
});

Field.scrollFreeActivate = function(field) {
  setTimeout(function() {
    Field.activate(field);
  }, 1);
}

Ajax.InPlaceEditor = Class.create();
Ajax.InPlaceEditor.defaultHighlightColor = "#FFFF99";
Ajax.InPlaceEditor.prototype = {
  initialize: function(element, url, options) {
    this.url = url;
    this.element = $(element);

    this.options = Object.extend({
      paramName: "value",
      okButton: true,
      okLink: false,
      okText: "ok",
      cancelButton: false,
      cancelLink: true,
      cancelText: "cancel",
      textBeforeControls: '',
      textBetweenControls: '',
      textAfterControls: '',
      savingText: "Saving...",
      clickToEditText: "Click to edit",
      okText: "ok",
      rows: 1,
      onComplete: function(transport, element) {
        new Effect.Highlight(element, {startcolor: this.options.highlightcolor});
      },
      onFailure: function(transport) {
        alert("Error communicating with the server: " + transport.responseText.stripTags());
      },
      callback: function(form) {
        return Form.serialize(form);
      },
      handleLineBreaks: true,
      loadingText: 'Loading...',
      savingClassName: 'inplaceeditor-saving',
      loadingClassName: 'inplaceeditor-loading',
      formClassName: 'inplaceeditor-form',
      highlightcolor: Ajax.InPlaceEditor.defaultHighlightColor,
      highlightendcolor: "#FFFFFF",
      externalControl: null,
      submitOnBlur: false,
      ajaxOptions: {},
      evalScripts: false
    }, options || {});

    if(!this.options.formId && this.element.id) {
      this.options.formId = this.element.id + "-inplaceeditor";
      if ($(this.options.formId)) {
        // there's already a form with that name, don't specify an id
        this.options.formId = null;
      }
    }
    
    if (this.options.externalControl) {
      this.options.externalControl = $(this.options.externalControl);
    }
    
    this.originalBackground = Element.getStyle(this.element, 'background-color');
    if (!this.originalBackground) {
      this.originalBackground = "transparent";
    }
    
    this.element.title = this.options.clickToEditText;
    
    this.onclickListener = this.enterEditMode.bindAsEventListener(this);
    this.mouseoverListener = this.enterHover.bindAsEventListener(this);
    this.mouseoutListener = this.leaveHover.bindAsEventListener(this);
    Event.observe(this.element, 'click', this.onclickListener);
    Event.observe(this.element, 'mouseover', this.mouseoverListener);
    Event.observe(this.element, 'mouseout', this.mouseoutListener);
    if (this.options.externalControl) {
      Event.observe(this.options.externalControl, 'click', this.onclickListener);
      Event.observe(this.options.externalControl, 'mouseover', this.mouseoverListener);
      Event.observe(this.options.externalControl, 'mouseout', this.mouseoutListener);
    }
  },
  enterEditMode: function(evt) {
    if (this.saving) return;
    if (this.editing) return;
    this.editing = true;
    this.onEnterEditMode();
    if (this.options.externalControl) {
      Element.hide(this.options.externalControl);
    }
    Element.hide(this.element);
    this.createForm();
    this.element.parentNode.insertBefore(this.form, this.element);
    if (!this.options.loadTextURL) Field.scrollFreeActivate(this.editField);
    // stop the event to avoid a page refresh in Safari
    if (evt) {
      Event.stop(evt);
    }
    return false;
  },
  createForm: function() {
    this.form = document.createElement("form");
    this.form.id = this.options.formId;
    Element.addClassName(this.form, this.options.formClassName)
    this.form.onsubmit = this.onSubmit.bind(this);

    this.createEditField();

    if (this.options.textarea) {
      var br = document.createElement("br");
      this.form.appendChild(br);
    }
    
    if (this.options.textBeforeControls)
      this.form.appendChild(document.createTextNode(this.options.textBeforeControls));

    if (this.options.okButton) {
      var okButton = document.createElement("input");
      okButton.type = "submit";
      okButton.value = this.options.okText;
      okButton.className = 'editor_ok_button';
      this.form.appendChild(okButton);
    }
    
    if (this.options.okLink) {
      var okLink = document.createElement("a");
      okLink.href = "#";
      okLink.appendChild(document.createTextNode(this.options.okText));
      okLink.onclick = this.onSubmit.bind(this);
      okLink.className = 'editor_ok_link';
      this.form.appendChild(okLink);
    }
    
    if (this.options.textBetweenControls && 
      (this.options.okLink || this.options.okButton) && 
      (this.options.cancelLink || this.options.cancelButton))
      this.form.appendChild(document.createTextNode(this.options.textBetweenControls));
      
    if (this.options.cancelButton) {
      var cancelButton = document.createElement("input");
      cancelButton.type = "submit";
      cancelButton.value = this.options.cancelText;
      cancelButton.onclick = this.onclickCancel.bind(this);
      cancelButton.className = 'editor_cancel_button';
      this.form.appendChild(cancelButton);
    }

    if (this.options.cancelLink) {
      var cancelLink = document.createElement("a");
      cancelLink.href = "#";
      cancelLink.appendChild(document.createTextNode(this.options.cancelText));
      cancelLink.onclick = this.onclickCancel.bind(this);
      cancelLink.className = 'editor_cancel editor_cancel_link';      
      this.form.appendChild(cancelLink);
    }
    
    if (this.options.textAfterControls)
      this.form.appendChild(document.createTextNode(this.options.textAfterControls));
  },
  hasHTMLLineBreaks: function(string) {
    if (!this.options.handleLineBreaks) return false;
    return string.match(/<br/i) || string.match(/<p>/i);
  },
  convertHTMLLineBreaks: function(string) {
    return string.replace(/<br>/gi, "\n").replace(/<br\/>/gi, "\n").replace(/<\/p>/gi, "\n").replace(/<p>/gi, "");
  },
  createEditField: function() {
    var text;
    if(this.options.loadTextURL) {
      text = this.options.loadingText;
    } else {
      text = this.getText();
    }

    var obj = this;
    
    if (this.options.rows == 1 && !this.hasHTMLLineBreaks(text)) {
      this.options.textarea = false;
      var textField = document.createElement("input");
      textField.obj = this;
      textField.type = "text";
      textField.name = this.options.paramName;
      textField.value = text;
      textField.style.backgroundColor = this.options.highlightcolor;
      textField.className = 'editor_field';
      var size = this.options.size || this.options.cols || 0;
      if (size != 0) textField.size = size;
      if (this.options.submitOnBlur)
        textField.onblur = this.onSubmit.bind(this);
      this.editField = textField;
    } else {
      this.options.textarea = true;
      var textArea = document.createElement("textarea");
      textArea.obj = this;
      textArea.name = this.options.paramName;
      textArea.value = this.convertHTMLLineBreaks(text);
      textArea.rows = this.options.rows;
      textArea.cols = this.options.cols || 40;
      textArea.className = 'editor_field';      
      if (this.options.submitOnBlur)
        textArea.onblur = this.onSubmit.bind(this);
      this.editField = textArea;
    }
    
    if(this.options.loadTextURL) {
      this.loadExternalText();
    }
    this.form.appendChild(this.editField);
  },
  getText: function() {
    return this.element.innerHTML;
  },
  loadExternalText: function() {
    Element.addClassName(this.form, this.options.loadingClassName);
    this.editField.disabled = true;
    new Ajax.Request(
      this.options.loadTextURL,
      Object.extend({
        asynchronous: true,
        onComplete: this.onLoadedExternalText.bind(this)
      }, this.options.ajaxOptions)
    );
  },
  onLoadedExternalText: function(transport) {
    Element.removeClassName(this.form, this.options.loadingClassName);
    this.editField.disabled = false;
    this.editField.value = transport.responseText.stripTags();
    Field.scrollFreeActivate(this.editField);
  },
  onclickCancel: function() {
    this.onComplete();
    this.leaveEditMode();
    return false;
  },
  onFailure: function(transport) {
    this.options.onFailure(transport);
    if (this.oldInnerHTML) {
      this.element.innerHTML = this.oldInnerHTML;
      this.oldInnerHTML = null;
    }
    return false;
  },
  onSubmit: function() {
    // onLoading resets these so we need to save them away for the Ajax call
    var form = this.form;
    var value = this.editField.value;
    this.onLoading();
    
    if (this.options.evalScripts) {
      new Ajax.Request(
        this.url, Object.extend({
          parameters: this.options.callback(form, value),
          onComplete: this.onComplete.bind(this),
          onFailure: this.onFailure.bind(this),
          asynchronous:true, 
          evalScripts:true
        }, this.options.ajaxOptions));
    } else  {
      new Ajax.Updater(
        { success: this.element,
          // don't update on failure (this could be an option)
          failure: null }, 
        this.url, Object.extend({
          parameters: this.options.callback(form, value),
          onComplete: this.onComplete.bind(this),
          onFailure: this.onFailure.bind(this)
        }, this.options.ajaxOptions));
    }
    // stop the event to avoid a page refresh in Safari
    if (arguments.length > 1) {
      Event.stop(arguments[0]);
    }
    return false;
  },
  onLoading: function() {
    this.saving = true;
    this.removeForm();
    this.leaveHover();
    this.showSaving();
  },
  showSaving: function() {
    this.oldInnerHTML = this.element.innerHTML;
    this.element.innerHTML = this.options.savingText;
    Element.addClassName(this.element, this.options.savingClassName);
    this.element.style.backgroundColor = this.originalBackground;
    Element.show(this.element);
  },
  removeForm: function() {
    if(this.form) {
      if (this.form.parentNode) Element.remove(this.form);
      this.form = null;
    }
  },
  enterHover: function() {
    if (this.saving) return;
    this.element.style.backgroundColor = this.options.highlightcolor;
    if (this.effect) {
      this.effect.cancel();
    }
    Element.addClassName(this.element, this.options.hoverClassName)
  },
  leaveHover: function() {
    if (this.options.backgroundColor) {
      this.element.style.backgroundColor = this.oldBackground;
    }
    Element.removeClassName(this.element, this.options.hoverClassName)
    if (this.saving) return;
    this.effect = new Effect.Highlight(this.element, {
      startcolor: this.options.highlightcolor,
      endcolor: this.options.highlightendcolor,
      restorecolor: this.originalBackground
    });
  },
  leaveEditMode: function() {
    Element.removeClassName(this.element, this.options.savingClassName);
    this.removeForm();
    this.leaveHover();
    this.element.style.backgroundColor = this.originalBackground;
    Element.show(this.element);
    if (this.options.externalControl) {
      Element.show(this.options.externalControl);
    }
    this.editing = false;
    this.saving = false;
    this.oldInnerHTML = null;
    this.onLeaveEditMode();
  },
  onComplete: function(transport) {
    this.leaveEditMode();
    this.options.onComplete.bind(this)(transport, this.element);
  },
  onEnterEditMode: function() {},
  onLeaveEditMode: function() {},
  dispose: function() {
    if (this.oldInnerHTML) {
      this.element.innerHTML = this.oldInnerHTML;
    }
    this.leaveEditMode();
    Event.stopObserving(this.element, 'click', this.onclickListener);
    Event.stopObserving(this.element, 'mouseover', this.mouseoverListener);
    Event.stopObserving(this.element, 'mouseout', this.mouseoutListener);
    if (this.options.externalControl) {
      Event.stopObserving(this.options.externalControl, 'click', this.onclickListener);
      Event.stopObserving(this.options.externalControl, 'mouseover', this.mouseoverListener);
      Event.stopObserving(this.options.externalControl, 'mouseout', this.mouseoutListener);
    }
  }
};

Ajax.InPlaceCollectionEditor = Class.create();
Object.extend(Ajax.InPlaceCollectionEditor.prototype, Ajax.InPlaceEditor.prototype);
Object.extend(Ajax.InPlaceCollectionEditor.prototype, {
  createEditField: function() {
    if (!this.cached_selectTag) {
      var selectTag = document.createElement("select");
      var collection = this.options.collection || [];
      var optionTag;
      collection.each(function(e,i) {
        optionTag = document.createElement("option");
        optionTag.value = (e instanceof Array) ? e[0] : e;
        if((typeof this.options.value == 'undefined') && 
          ((e instanceof Array) ? this.element.innerHTML == e[1] : e == optionTag.value)) optionTag.selected = true;
        if(this.options.value==optionTag.value) optionTag.selected = true;
        optionTag.appendChild(document.createTextNode((e instanceof Array) ? e[1] : e));
        selectTag.appendChild(optionTag);
      }.bind(this));
      this.cached_selectTag = selectTag;
    }

    this.editField = this.cached_selectTag;
    if(this.options.loadTextURL) this.loadExternalText();
    this.form.appendChild(this.editField);
    this.options.callback = function(form, value) {
      return "value=" + encodeURIComponent(value);
    }
  }
});
Form.Element.DelayedObserver = Class.create();
Form.Element.DelayedObserver.prototype = {
  initialize: function(element, delay, callback) {
    this.delay     = delay || 0.5;
    this.element   = $(element);
    this.callback  = callback;
    this.timer     = null;
    this.lastValue = $F(this.element); 
    Event.observe(this.element,'keyup',this.delayedListener.bindAsEventListener(this));
  },
  delayedListener: function(event) {
    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();

Control.Slider.prototype = {
  initialize: function(handle, track, options) {
    var slider = this;
    
    if(handle instanceof Array) {
      this.handles = handle.collect( function(e) { return $(e) });
    } else {
      this.handles = [$(handle)];
    }
    
    this.track   = $(track);
    this.options = options || {};

    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; // assure backwards compat
    this.values    = this.handles.map( function() { return 0 });
    this.spans     = this.options.spans ? this.options.spans.map(function(s){ return $(s) }) : 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;

    // Will be used to align the handle onto the track, if necessary
    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();

    // Allowed values array
    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);

    // Initialize handles in reverse (make sure first handle is active)
    this.handles.each( function(h,i) {
      i = slider.handles.length-1-i;
      slider.setValue(parseFloat(
        (slider.options.sliderValue instanceof Array ? 
          slider.options.sliderValue[i] : slider.options.sliderValue) || 
         slider.range.start), i);
      Element.makePositioned(h); // fix IE
      Event.observe(h, "mousedown", slider.eventMouseDown);
    });
    
    Event.observe(this.track, "mousedown", this.eventMouseDown);
    Event.observe(document, "mouseup", this.eventMouseUp);
    Event.observe(document, "mousemove", this.eventMouseMove);
    
    this.initialized = true;
  },
  dispose: function() {
    var slider = this;    
    Event.stopObserving(this.track, "mousedown", this.eventMouseDown);
    Event.stopObserving(document, "mouseup", this.eventMouseUp);
    Event.stopObserving(document, "mousemove", this.eventMouseMove);
    this.handles.each( function(h) {
      Event.stopObserving(h, "mousedown", slider.eventMouseDown);
    });
  },
  setDisabled: function(){
    this.disabled = true;
  },
  setEnabled: function(){
    this.disabled = false;
  },  
  getNearestValue: function(value){
    if(this.allowedValues){
      if(value >= this.allowedValues.max()) return(this.allowedValues.max());
      if(value <= this.allowedValues.min()) return(this.allowedValues.min());
      
      var offset = Math.abs(this.allowedValues[0] - value);
      var newValue = this.allowedValues[0];
      this.allowedValues.each( function(v) {
        var currentOffset = Math.abs(v - value);
        if(currentOffset <= offset){
          newValue = v;
          offset = currentOffset;
        } 
      });
      return newValue;
    }
    if(value > this.range.end) return this.range.end;
    if(value < this.range.start) return this.range.start;
    return value;
  },
  setValue: function(sliderValue, handleIdx){
    if(!this.active) {
      this.activeHandleIdx = handleIdx || 0;
      this.activeHandle    = this.handles[this.activeHandleIdx];
      this.updateStyles();
    }
    handleIdx = handleIdx || this.activeHandleIdx || 0;
    if(this.initialized && this.restricted) {
      if((handleIdx>0) && (sliderValue<this.values[handleIdx-1]))
        sliderValue = this.values[handleIdx-1];
      if((handleIdx < (this.handles.length-1)) && (sliderValue>this.values[handleIdx+1]))
        sliderValue = this.values[handleIdx+1];
    }
    sliderValue = this.getNearestValue(sliderValue);
    this.values[handleIdx] = sliderValue;
    this.value = this.values[0]; // assure backwards compat
    
    this.handles[handleIdx].style[this.isVertical() ? 'top' : 'left'] = 
      this.translateToPx(sliderValue);
    
    this.drawSpans();
    if(!this.dragging || !this.event) this.updateFinished();
  },
  setValueBy: function(delta, handleIdx) {
    this.setValue(this.values[handleIdx || this.activeHandleIdx || 0] + delta, 
      handleIdx || this.activeHandleIdx || 0);
  },
  translateToPx: function(value) {
    return Math.round(
      ((this.trackLength-this.handleLength)/(this.range.end-this.range.start)) * 
      (value - this.range.start)) + "px";
  },
  translateToValue: function(offset) {
    return ((offset/(this.trackLength-this.handleLength) * 
      (this.range.end-this.range.start)) + this.range.start);
  },
  getRange: function(range) {
    var v = this.values.sortBy(Prototype.K); 
    range = range || 0;
    return $R(v[range],v[range+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.alignY);
  },  
  isVertical:  function(){
    return (this.axis == 'vertical');
  },
  drawSpans: function() {
    var slider = this;
    if(this.spans)
      $R(0, this.spans.length-1).each(function(r) { slider.setSpan(slider.spans[r], slider.getRange(r)) });
    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(span, range) {
    if(this.isVertical()) {
      span.style.top = this.translateToPx(range.start);
      span.style.height = this.translateToPx(range.end - range.start + this.range.start);
    } else {
      span.style.left = this.translateToPx(range.start);
      span.style.width = this.translateToPx(range.end - range.start + this.range.start);
    }
  },
  updateStyles: function() {
    this.handles.each( function(h){ Element.removeClassName(h, 'selected') });
    Element.addClassName(this.activeHandle, 'selected');
  },
  startDrag: function(event) {
    if(Event.isLeftClick(event)) {
      if(!this.disabled){
        this.active = true;
        
        var handle = Event.element(event);
        var pointer  = [Event.pointerX(event), Event.pointerY(event)];
        var track = handle;
        if(track==this.track) {
          var offsets  = Position.cumulativeOffset(this.track); 
          this.event = event;
          this.setValue(this.translateToValue( 
           (this.isVertical() ? pointer[1]-offsets[1] : pointer[0]-offsets[0])-(this.handleLength/2)
          ));
          var offsets  = Position.cumulativeOffset(this.activeHandle);
          this.offsetX = (pointer[0] - offsets[0]);
          this.offsetY = (pointer[1] - offsets[1]);
        } else {
          // find the handle (prevents issues with Safari)
          while((this.handles.indexOf(handle) == -1) && handle.parentNode) 
            handle = handle.parentNode;
            
          if(this.handles.indexOf(handle)!=-1) {
            this.activeHandle    = handle;
            this.activeHandleIdx = this.handles.indexOf(this.activeHandle);
            this.updateStyles();
            
            var offsets  = Position.cumulativeOffset(this.activeHandle);
            this.offsetX = (pointer[0] - offsets[0]);
            this.offsetY = (pointer[1] - offsets[1]);
          }
        }
      }
      Event.stop(event);
    }
  },
  update: function(event) {
   if(this.active) {
      if(!this.dragging) this.dragging = true;
      this.draw(event);
      if(Prototype.Browser.WebKit) window.scrollBy(0,0);
      Event.stop(event);
   }
  },
  draw: function(event) {
    var pointer = [Event.pointerX(event), Event.pointerY(event)];
    var offsets = Position.cumulativeOffset(this.track);
    pointer[0] -= this.offsetX + offsets[0];
    pointer[1] -= this.offsetY + offsets[1];
    this.event = event;
    this.setValue(this.translateToValue( this.isVertical() ? pointer[1] : pointer[0] ));
    if(this.initialized && this.options.onSlide)
      this.options.onSlide(this.values.length>1 ? this.values : this.value, this);
  },
  endDrag: function(event) {
    if(this.active && this.dragging) {
      this.finishDrag(event, true);
      Event.stop(event);
    }
    this.active = false;
    this.dragging = false;
  },  
  finishDrag: function(event, success) {
    this.active = false;
    this.dragging = false;
    this.updateFinished();
  },
  updateFinished: function() {
    if(this.initialized && this.options.onChange) 
      this.options.onChange(this.values.length>1 ? this.values : this.value, this);
    this.event = null;
  }
}

Sound = {
  tracks: {},
  _enabled: true,
  template:
    new Template('<embed style="height:0" id="sound_#{track}_#{id}" src="#{url}" loop="false" autostart="true" hidden="true"/>'),
  enable: function(){
    Sound._enabled = true;
  },
  disable: function(){
    Sound._enabled = false;
  },
  play: function(url){
    if(!Sound._enabled) return;
    var options = Object.extend({
      track: 'global', url: url, replace: false
    }, arguments[1] || {});
    
    if(options.replace && this.tracks[options.track]) {
      $R(0, this.tracks[options.track].id).each(function(id){
        var sound = $('sound_'+options.track+'_'+id);
        sound.Stop && sound.Stop();
        sound.remove();
      })
      this.tracks[options.track] = null;
    }
      
    if(!this.tracks[options.track])
      this.tracks[options.track] = { id: 0 }
    else
      this.tracks[options.track].id++;
      
    options.id = this.tracks[options.track].id;
    if (Prototype.Browser.IE) {
      var sound = document.createElement('bgsound');
      sound.setAttribute('id','sound_'+options.track+'_'+options.id);
      sound.setAttribute('src',options.url);
      sound.setAttribute('loop','1');
      sound.setAttribute('autostart','true');
      $$('body')[0].appendChild(sound);
    }  
    else
      new Insertion.Bottom($$('body')[0], Sound.template.evaluate(options));
  }
};

if(Prototype.Browser.Gecko && navigator.userAgent.indexOf("Win") > 0){
  if(navigator.plugins && $A(navigator.plugins).detect(function(p){ return p.name.indexOf('QuickTime') != -1 }))
    Sound.template = new Template('<object id="sound_#{track}_#{id}" width="0" height="0" type="audio/mpeg" data="#{url}"/>')
  else
    Sound.play = function(){}
}


//helpers.js
function scrollToElement(targetId){new Effect.ScrollTo($(targetId),{duration:0.3})}function MM_preloadImages(){var d=document;if(d.images){if(!d.MM_p)d.MM_p=new Array();var i,j=d.MM_p.length,a=MM_preloadImages.arguments;for(i=0;i<a.length;i++)if(a[i].indexOf("#")!=0){d.MM_p[j]=new Image;d.MM_p[j++].src=a[i]}}}function bookmark(url,title){if((navigator.appName=="Microsoft Internet Explorer")&&(parseInt(navigator.appVersion)>=4)){window.external.AddFavorite(url,title)}else if(navigator.appName=="Netscape"){window.sidebar.addPanel(title,url,"")}else{alert("Press CTRL-D (Netscape) or CTRL-T (Opera) to bookmark")}}var BrowserDetect={init:function(){this.browser=this.searchString(this.dataBrowser)||"An unknown browser";this.version=this.searchVersion(navigator.userAgent)||this.searchVersion(navigator.appVersion)||"an unknown version";this.OS=this.searchString(this.dataOS)||"an unknown OS"},searchString:function(data){for(var i=0;i<data.length;i++){var dataString=data[i].string;var dataProp=data[i].prop;this.versionSearchString=data[i].versionSearch||data[i].identity;if(dataString){if(dataString.indexOf(data[i].subString)!=-1)return data[i].identity}else if(dataProp)return data[i].identity}},searchVersion:function(dataString){var index=dataString.indexOf(this.versionSearchString);if(index==-1)return;return parseFloat(dataString.substring(index+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();
//cookies.js
function Cookiemanager(name,defaultExpiration,expirationUnits,defaultDomain,defaultPath){this.name=name;this.defaultExpiration=this.getExpiration(defaultExpiration,expirationUnits);this.defaultDomain=(defaultDomain)?defaultDomain:(document.domain.search(/[a-zA-Z]/)==-1)?document.domain:document.domain.substring(document.domain.indexOf('.')+1,document.domain.length);this.defaultPath=(defaultPath)?defaultPath:'/';this.cookies=new Object();this.expiration=new Object();this.domain=new Object();this.path=new Object();window.onunload=new Function(this.name+'.setDocumentCookies();');this.getDocumentCookies()}
Cookiemanager.prototype.getExpiration=function(expiration,units){expiration=(expiration)?expiration:7;units=(units)?units:'days';var date=new Date();switch(units){case'years':date.setFullYear(date.getFullYear()+expiration);break;case'months':date.setMonth(date.getMonth()+expiration);break;case'days':date.setTime(date.getTime()+(expiration*24*60*60*1000));break;case'hours':date.setTime(date.getTime()+(expiration*60*60*1000));break;case'minutes':date.setTime(date.getTime()+(expiration*60*1000));break;case'seconds':date.setTime(date.getTime()+(expiration*1000));break;default:date.setTime(date.getTime()+expiration);break}return date.toGMTString()}
Cookiemanager.prototype.getDocumentCookies=function(){var cookie,pair;var cookies=document.cookie.split(';');var len=cookies.length;for(var i=0;i<len;i++){cookie=cookies[i];while(cookie.charAt(0)==' ')cookie=cookie.substring(1,cookie.length);pair=cookie.split('=');this.cookies[pair[0]]=pair[1]}}
Cookiemanager.prototype.setDocumentCookies=function(){var expires='';var cookies='';var domain='';var path='';for(var name in this.cookies){expires=(this.expiration[name])?this.expiration[name]:this.defaultExpiration;path=(this.path[name])?this.path[name]:this.defaultPath;domain=(this.domain[name])?this.domain[name]:this.defaultDomain;cookies=name+'='+this.cookies[name]+'; expires='+expires+'; path='+path+'; domain='+domain;document.cookie=cookies}return true}
Cookiemanager.prototype.getCookie=function(cookieName){var cookie=this.cookies[cookieName];return (cookie)?cookie:false}
Cookiemanager.prototype.setCookie=function(cookieName,cookieValue,expiration,expirationUnits,domain,path){this.cookies[cookieName]=cookieValue;if(expiration)this.expiration[cookieName]=this.getExpiration(expiration,expirationUnits);if(domain)this.domain[cookieName]=domain;if(path)this.path[cookieName]=path;return true}
var cookieManager = new Cookiemanager('cookieManager',1,'years');
//verdinocom.js
Event.observe(document, 'contentloaded', loadingFunction, false);
Event.observe(window, 'load', initPageFunction, false);
Event.observe(window, 'unload', unloadFunction, false);

function loadingFunction () {
	BrowserDetect.init();	
	browserIsIE6 = false;
	browserIsIE7 = false;
	if((BrowserDetect.browser == "Explorer") &&  (BrowserDetect.version == 6))
	{
		browserIsIE6 = true;
		document.execCommand("BackgroundImageCache",false,true);
	}
	else if((BrowserDetect.browser == "Explorer") &&  (BrowserDetect.version == 7))
	{
		browserIsIE7 = true;			
	}
	if ($$(".listeninhalt").size()) {
		closeAllListItems();
	}
	if ($$(".mitarbeiter_text_wrapper").size()) {
		closeAllMitarbeiterItems();
	}
	cookieManager.getDocumentCookies();
	var news_slideshow_running;
	if ($$(".eineStartNews").size()) {
		var news_timeout = 6000;
		start_addNewsIds($$(".eineStartNews"));
		buildNewsNavigation();
		slideshow_navigation = $$(".startnews_nav_number");
		slideshow_navigation.each(function(arrayitem) {
			Event.observe(arrayitem,'click',function(event) { 
				switch_to_slide(get_current_slide(), arrayitem.readAttribute('id') );
				news_slideshow_running = false;
				Event.stop(event); 											 
				});	
		});	
		Event.observe($('startseite_play_button'),'click',
			function(event) {
				if(news_slideshow_running == true) {
					stop_slideshow();
					$('startseite_play_button').innerHTML ="play";
					news_slideshow_running = false;
				} 
				else if(news_slideshow_running == false){
					start_slideshow_from(get_current_slide(), 1, $$(".eineStartNews").length, news_timeout);  
					$('startseite_play_button').innerHTML ="stop";
					news_slideshow_running = true;
				}
		 		Event.stop(event);
			}
		);   
		start_slideshow(1, $$(".eineStartNews").length, news_timeout);
		news_slideshow_running = true;
	}
	if ($$(".projektliste li.clearfix")) {
		var listtoggleprojectitems = $$(".projektliste li.clearfix");
		listtoggleprojectitems.each(function(arrayitem) {
			arrayitem.observe('click', toggleListElement, false);	
			var image = arrayitem.down("img");
			placeholder= 'fileadmin/css/img/1px_placeholder.gif';
			if(image != undefined) {
				var original_source =  $(image).readAttribute('src');
				$(image).writeAttribute({
						src: placeholder
					}).writeAttribute({
						'rel': original_source
				});
				}
		})
	}
	if ($$(".mitarbeiter_text_wrapper")) {
		var mitarbeitertoggle = $$(".mitarbeiter_text_wrapper");
		mitarbeitertoggle.each(function(arrayitem) {
			arrayitem.observe('click', toggleMitarbeiterElement, false);
		})
	}
	if ($$(".jobliste li.clearfix")) {
		var listtogglejobitem = $$(".jobliste li.clearfix");
		listtogglejobitem.each(function(arrayitem) {
			arrayitem.observe('click', toggleJobListElement, false);
		})
	}
	if ($$(".kundenliste_wrapper")) {
		var kundenliste_wrapper_array = $$(".kundenliste_wrapper");
		kundenliste_wrapper_array.each(function(arrayitem) {
			arrayitem.hide();
		})
		var kundenliste_icon_second_array = $$(".kundenliste_icon_second");
		kundenliste_icon_second_array.each(function(arrayitem) {
			arrayitem.hide();
		})
		var kundenliste_array = $$(".kundenliste_icon");
		kundenliste_array.each(function(arrayitem) {
			arrayitem.observe('mouseover', swapKundenlisteIconIn, false);
		})
		var kundenliste_array = $$(".kundenliste_icon");
		kundenliste_array.each(function(arrayitem) {
			arrayitem.observe('mouseout', swapKundenlisteIconOut, false);
		})
		var kundenliste_wrapper_array = $$(".kundenliste_wrapper");
			kundenliste_wrapper_array.each(function(arrayitem) {
					randomizeKundenliste(arrayitem);
					new Insertion.Bottom(arrayitem, "<div class='clearfix'></div>");		
					arrayitem.show();
		})
	}
}
function initPageFunction () {
	$("loading").hide();
	$("page_margins").show();
	if ($$(".image_with_dropshadow")) {
		var shadow_array = $$(".image_with_dropshadow");
		shadow_array.each(function(arrayitem) {
			addShadow(arrayitem);
		})
	}
	if ($$("#referenzenliste_accordion_wrapper").size()) {
		loadCaseStudyAccordions();
	}
	if ($$("#news_accordion_wrapper").size()) {
		loadNewsAccordions();
	}
	disableAllAnchors();
}
function unloadFunction () {
	if ($$("#news_accordion_wrapper").size()) {
		saveLastAccordion($$('#news_accordion_wrapper .toggleWrapper'), ".toggleWrapper_active", "lastNews");
	}
	if ($$("#referenzenliste_accordion_wrapper").size()) {
		saveLastAccordion($$('#referenzenliste_accordion_wrapper .toggle_element'), ".toggle_element_active", "lastCaseStudy");
	}
	cookieManager.setCookie("alreadyVisited",1, 6, 'months');	
	cookieManager.setDocumentCookies();
	if ($$("#google_maps_wrapper").size()) {
		GUnload();
	}
	if(browserIsIE6 == true) {
		document.execCommand("BackgroundImageCache",false,false);	
	}
}
function loadCaseStudyAccordions(){
	var casestudyAccordion = new accordion('#referenzenliste_accordion_wrapper', {
		classNames: {
			toggle: 'toggle_element',
			toggleActive: 'toggle_element_active',
			content: 'content'
		}
	});
	var lastAccordion = cookieManager.getCookie('lastCaseStudy');
	
	if (lastAccordion == false) {
		casestudyAccordion.activate($$('#referenzenliste_accordion_wrapper .toggle_element')[0]);
	}
	else {
		casestudyAccordion.activate($$('#referenzenliste_accordion_wrapper .toggle_element')[lastAccordion]);
	}	
}
	function loadNewsAccordions(){
		newsAccordion = new accordion('#news_accordion_wrapper', {
			classNames: {
				toggle: 'toggleWrapper',
				toggleActive: 'toggleWrapper_active',
				content: 'content'
			}
		});
		var lastAccordion = cookieManager.getCookie('lastNews');
		if (lastAccordion == false) {
			newsAccordion.activate($$('#news_accordion_wrapper .toggleWrapper')[0]);
		}
		else {
			newsAccordion.activate($$('#news_accordion_wrapper .toggleWrapper')[lastAccordion]);
		}
	}
	function saveLastAccordion(accordion, togglewrapper, cookie){
		var i = 0;
		accordion.each(function(item){
			if (Element.match(item, togglewrapper)) {
				cookieManager.setCookie(cookie, i, 1, 'days');
				cookieManager.setDocumentCookies();
			}
			i++;
		});
	}
	function disableAllAnchors(){
		if ($('topAnchorId')) {
			$('topAnchorId').remove();
		}
	}
	function closeAllMitarbeiterItems(){
		var allListItems = $$(".mitarbeiter_text");
		allListItems.each(function(arrayitem){
			arrayitem.hide();
			return false;
		})	
	}
	function closeAllListItems(){
	
		var allListItems = $$(".listeninhalt");
		allListItems.each(function(arrayitem){
			arrayitem.hide();
			return false;
		})
	}
	function toggleListElement(){
		element = $(this);
		if (element.nodeName != "A") {
		
			while (element.nodeName != "LI" || !Element.match(element, ".clearfix")) {
				element = element.up();
			}
			var image =	element.down("img");
			if(image != undefined) {
				var original_source =  $(image).readAttribute('rel');
				$(image).writeAttribute({
					src: original_source
				});
				 
				}
			var elementToToggle = element.down(0).next(0);
			new Effect.toggle(elementToToggle, 'blind', {
				duration: 0.5,
				beforeStart: beforeStartFunction,
				afterFinish: afterFinishFunction
			});
		}
		function beforeStartFunction(){
			if (elementToToggle.getStyle('display') == "none") {
				$(element).addClassName('keinpfeil');
				elementToToggle.addClassName('listeoffen');
			}
		}
		function afterFinishFunction(){
			if (element.previous()) {
				if (element.previous().match('.greenborder')) {
					element.previous().removeClassName('greenborder');
				}
				else {
					element.previous().addClassName('greenborder');
				}
			}
			if (elementToToggle.getStyle('display') == "none") {
				$(element).removeClassName('keinpfeil');
				elementToToggle.removeClassName('listeoffen');
			}
		}
	}
	function toggleJobListElement(){
		element = $(this);
		if (element.nodeName != "A") {
			while (element.nodeName != "LI" || !Element.match(element, ".clearfix")) {
				element = element.up();
			}
			new Effect.toggle(element.down(0).next(0), 'blind', {
				duration: 0.2,
				beforeStart: beforeStartFunction,
				afterFinish: afterFinishFunction
			});
		}
		function beforeStartFunction(){
			if (element.down(0).next(0).getStyle('display') == "none") {
				$(element).addClassName('keinpfeil');
				$(element).down(0).next(0).addClassName('listeoffen');
				$(element).down(0).down(0).addClassName('offenesmaxerl');
				$(element).down(0).down(0).removeClassName('maxerl');
			}
		}
		function afterFinishFunction(){
			if (element.previous()) {
			
				if (element.previous().match('.greenborder')) {
					element.previous().removeClassName('greenborder');
				}
				else {
					element.previous().addClassName('greenborder');
				}
			}
			if (element.down(0).next(0).getStyle('display') == "none") {
				$(element).removeClassName('keinpfeil');
				$(element).down(0).next(0).removeClassName('listeoffen');
				$(element).down(0).down(0).removeClassName('offenesmaxerl');
				$(element).down(0).down(0).addClassName('maxerl');
			}
		}
	}
function toggleMitarbeiterElement(){
		if ($(this).readAttribute('class') == 'mitarbeiter_text_togglelink') {
			element = $(this);
		}
		else {
			element = $(this).up('.mitarbeiter_wrapper').down('.mitarbeiter_text_togglelink');
		}
		new Effect.toggle(element.next(), 'blind', {
			duration: 0.2
		});
		if (element.up().match('.mitarbeiter_text_wrapper_offen')) {
			element.up().removeClassName('mitarbeiter_text_wrapper_offen');
			element.up().addClassName('mitarbeiter_text_wrapper');
		}
		else {
			element.up().addClassName('mitarbeiter_text_wrapper_offen');
		}
	}
	function addShadow(imageElement){
		html = "               <table class='shadow_table' cellpadding='0' cellspacing='0'>";
		html += "                          <tr>";
		html += "                            <td colspan='2' class='imagediv'>";
		html += "                       			<img src='" + $(imageElement).src + "' alt='" + $(imageElement).getAttribute("alt") + "' title='" + $(imageElement).getAttribute("title") + "'/>";
		html += "                            </td>";
		html += "                            <td class='shadow_right'>";
		
		html += "                            </td>";
		html += "                          </tr>";
		html += "                          <tr>";
		html += "                            <td class='shadow_bottomleft_edge'></td>";
		html += "                            <td class='shadow_bottom'></td>";
		html += "                            <td class='shadow_bottom_right'></td>";
		html += "                          </tr>";
		html += "                    </table>";
		new Insertion.After(imageElement, html);
		$(imageElement).remove();
	}
	function toTop(){
		scrollToElement('page_margins');
	}
	var slideshow_timeout_handler;
	function start_slideshow(start_frame, end_frame, delay){
		slideshow_timeout_handler = setTimeout(switch_slides(start_frame, start_frame, end_frame, delay), delay);
	}
	function start_slideshow_from(start_from_frame, start_frame, end_frame, delay){
		setTimeout(switch_slides(start_from_frame, start_frame, end_frame, delay), 500);
	}
	function switch_slides(current_frame, start_frame, end_frame, delay){
		return (function(){
			var fadeElement = 'News' + current_frame;
		new Effect.Fade(fadeElement);
			if (current_frame == end_frame) {
				next_frame = start_frame;
			}
			else {
				next_frame = current_frame;
				next_frame++;
			}		
			set_current_slide(next_frame, current_frame);
			setTimeout("Effect.Appear('News" + next_frame + "');", 850);
			slideshow_timeout_handler = setTimeout(switch_slides(next_frame, start_frame, end_frame, delay), delay + 850);
		})
	}
	function switch_to_slide(current_frame, next_frame){	
			clearTimeout(slideshow_timeout_handler);
			$('startseite_play_button').innerHTML ="play";	
		if (current_frame != next_frame) {		
			var fadeElement = 'News' + current_frame;
			new Effect.Fade(fadeElement);
			setTimeout("Effect.Appear('News" + next_frame + "');", 850);
			set_current_slide(next_frame, current_frame);			
		}
	}	
	function stop_slideshow() {				
			clearTimeout(slideshow_timeout_handler);
	}	
	function start_addNewsIds(newsarray){
		var allNews = newsarray;
		
		for (var i = 1; i <= allNews.length; i++) {
			newsarray[i - 1].setAttribute('id', 'News' + i);
		}
	}
	function buildNewsNavigation() {
		var allNewsItems = $$(".eineStartNews");		  
		var i = 1;				 
		allNewsItems.each( function(item) {					
			if(i == 1) {
				new Insertion.Bottom($('startnews_navigation'), "<div class='startnews_nav_number first activeslide' id='" + i + "'>01</div>");
			}
			else if(i < 10 ) {
				new Insertion.Bottom($('startnews_navigation'), "<div class='startnews_nav_number' id='" + i + "'>0" + i  +"</div>");		
				item.style.display = "none";
			}
			else {
				new Insertion.Bottom($('startnews_navigation'), "<div class='startnews_nav_number'  id='" + i + "'>" + i  +"</div>");	
				item.style.display = "none";
			}			
			i++;			
		});			
		new Insertion.Bottom($('startnews_navigation'), "<div class='clearfix'></div>");		
	}	
	function set_current_slide(new_frame, old_frame) {		
		$(old_frame.toString()).removeClassName('activeslide');
		$(new_frame.toString()).addClassName('activeslide');		
	}	
	function get_current_slide() {		
		return $$('.activeslide')[0].readAttribute('id');		
	}
	function randomizeKundenliste(list){		
		array = list.childElements();		
		function randOrd(){
			return (Math.round(Math.random()) - 0.5);
		}		
		array.sort(randOrd);		
		array.each(function(item){		
			list.appendChild(item);		
		});		
	}
	function swapKundenlisteIconIn(){	
		var element = $(this);		
		while (element.nodeName != "LI") {
			element = element.up();
		}
		element.down().setStyle({
			display: 'none'
		});
		element.down().next().setStyle({
			display: 'block'
		});
	}
	function swapKundenlisteIconOut(){	
		var element = $(this);
		while (element.nodeName != "LI") {
			element = element.up();			
		}
		element.down().setStyle({
			display: 'block'
		});
		element.down().next().setStyle({
			display: 'none'
		});		
	}
	function switchGender(whichgender){
		if (whichgender == "frau") {
			$('newsletter_maxerl').removeClassName('neutral');
			$('newsletter_maxerl').removeClassName('mann');
			$('newsletter_maxerl').addClassName('frau');			
			if (browserIsIE6) {
				$('newsletter_maxerl').setStyle({
					backgroundImage: 'url(fileadmin/css/img/newsletter_frau.gif)'
				});
			}
		}
		if (whichgender == "mann") {
			$('newsletter_maxerl').removeClassName('neutral');
			$('newsletter_maxerl').removeClassName('frau');
			$('newsletter_maxerl').addClassName('mann');			
			if (browserIsIE6) {
				$('newsletter_maxerl').setStyle({
					backgroundImage: 'url(fileadmin/css/img/newsletter_mann.gif)'
				});
			}
		}
	}
	Element.addMethods({
		lazyload: function(element, options){		
			function $restore(){
				if (true === $(element).hasAttribute('_src')) {
					$(element).writeAttribute({
						src: $(element).readAttribute('_src')
					});
				}
			}
			function $scroll(){
				var scroll_y = self.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop;
				return parseInt(scroll_y);
			}
			function $height(){
				var window_height = window.innerHeight || document.documentElement.clientHeight;
				return parseInt(window_height);
			}
			var element = $(element);
			var options = Object.extend({
				threshold: 0,
				placeholder: 'fileadmin/css/img/1px_placeholder.gif',
				event: 'scroll',
				frequency: 0.1
			}, options ||
			{});		
			var offset = $(element).cumulativeOffset()[1];
			var activate_on = (offset - options.threshold) - $height();			
			var old_source = $(element).readAttribute('src');
			var new_source = options.placeholder;			
			$(element).writeAttribute({
				src: new_source
			}).writeAttribute({
				'_src': old_source
			});			
			if ('scroll' === options.event) {
				new PeriodicalExecuter(function($executor){
					if (activate_on <= $scroll()) {
						$restore();
						$executor.stop();
					}
				}, options.frequency);
			}
			else {
				$(element).observe(options.event, function(event){
					$restore();
					$(element).stopObserving();
				});
			}
			return $(element);
		}
	});
	var BrowserDetect = {
		init: function(){
			this.browser = this.searchString(this.dataBrowser) || "An unknown browser";
			this.version = this.searchVersion(navigator.userAgent) ||
			this.searchVersion(navigator.appVersion) ||
			"an unknown version";
			this.OS = this.searchString(this.dataOS) || "an unknown OS";
		},
		searchString: function(data){
			for (var i = 0; i < data.length; i++) {
				var dataString = data[i].string;
				var dataProp = data[i].prop;
				this.versionSearchString = data[i].versionSearch || data[i].identity;
				if (dataString) {
					if (dataString.indexOf(data[i].subString) != -1) 
						return data[i].identity;
				}
				else 
					if (dataProp) 
						return data[i].identity;
			}
		},
		searchVersion: function(dataString){
			var index = dataString.indexOf(this.versionSearchString);
			if (index == -1) 
				return;
			return parseFloat(dataString.substring(index + 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"
		}]
	};
