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}('D 65={9p:"1.8.2",cK:u(a){19.cL(\'<56 67="7s/9q" 68="\'+a+\'"><\\/56>\')},7t:"1.6.0.3",9r:u(){u a(b){D c=b.3B(/2P.*|\\./g,"");c=2r(c+"0".cM(4-c.14));G b.2g("2P")>-1?c-1:c}v((26 1n=="3C")||(26 V=="3C")||(26 V.57=="3C")||(a(1n.9p)<a(65.7t))){2Q("56.7u.7v 7w 9s 1n cN cO >= "+65.7t)}}};65.9r();D 3D={9t:{9u:"1I",9v:"2R",9w:"2R",9x:"2R",9y:"cP",9z:"2S",7x:"2S",9A:"46",9B:"2R",9C:"2R",9D:"2R",9E:"2R",9F:"2R",9G:"2R"},7y:u(h){h=h.2T();D j=t.9t[h]||"47";D e=19.1Z(j);7z{e.2U="<"+h+"></"+h+">"}7A(k){}D l=e.58||17;v(l&&(l.2V.2T()!=h)){l=l.7B(h)[0]}v(!l){l=19.1Z(h)}v(!l){G}v(12[1]){v(t.69(12[1])||(12[1]cQ 9H)||12[1].2V){t.7C(l,12[1])}13{D m=t.9I(12[1]);v(m.14){7z{e.2U="<"+h+" "+m+"></"+h+">"}7A(k){}l=e.58||17;v(!l){l=19.1Z(h);1T(6a 3E 12[1]){l[6a=="9J"?"48":6a]=12[1][6a]}}v(l.2V.2T()!=h){l=e.7B(h)[0]}}}}v(12[2]){t.7C(l,12[2])}G $(l)},49:u(b){G 19.59(b)},7D:{48:"9J",cR:"1T"},9I:u(d){D c=[];1T(5a 3E d){c.1y((5a 3E t.7D?t.7D[5a]:5a)+\'="\'+d[5a].cS().cT().cU(/"/,"&cV;")+\'"\')}G c.4a(" ")},7C:u(c,d){v(d.2V){c.1L(d);G}v(26 d=="46"){d.2s().1f(u(a){v(26 a=="46"){c.1L(a)}13{v(3D.69(a)){c.1L(3D.49(a))}}})}13{v(3D.69(d)){c.1L(3D.49(d))}}},69:u(b){G(26 b=="9K"||26 b=="cW")},cX:u(c){D d=t.7y("47");$(d).1c(c.5b());G d.21()},cY:u(c){v(26 c!="46"&&26 c!="u"){c=1M}D d=("A cZ d0 d1 d2 9u B d3 d4 d5 d6 d7 9L d8 9M 9v d9 da db 9w 9x dc dd de df 9N dg dh di dj dk dl dm dn dp dq dr ds dt du dv dw dx I dy dz 9O dA dB dC dD 9y 7E dE dF dG dH dI dJ dK dL 9z 7x P 9A dM Q S dN dO 9P dP dQ dR dS dT dU dV dW 9B 9C 9Q 9D 9E 9F dX 9G dY U dZ e0").9R(/\\s+/);d.1f(u(a){c[a]=u(){G 3D.7y.e1(3D,[a].7F($A(12)))}})}};4b.4c.5c=u(){D e="#";v(t.3F(0,4)=="e2("){D f=t.3F(4,t.14-1).9R(",");D d=0;do{e+=2r(f[d]).5d()}4d(++d<3)}13{v(t.3F(0,1)=="#"){v(t.14==4){1T(D d=1;d<4;d++){e+=(t.6b(d)+t.6b(d)).3f()}}v(t.14==7){e=t.3f()}}}G(e.14==7?e:(12[0]||t))};V.6c=u(b){G $A($(b).2W).7G(u(a){G(a.7H==3?a.7I:(a.7J()?V.6c(a):""))}).2s().4a("")};V.6d=u(d,c){G $A($(d).2W).7G(u(a){G(a.7H==3?a.7I:((a.7J()&&!V.e3(a,c))?V.6d(a,c):""))}).2s().4a("")};V.9S=u(d,c){d=$(d);d.1i({3G:(c/2t)+"em"});v(1n.2h.5e){1M.7K(0,0)}G d};V.2X=u(b){G $(b).O.1R||""};V.7L=u(e){7z{e=$(e);D f=19.59(" ");e.1L(f);e.9T(f)}7A(d){}};D J={4e:{3g:"e4",e5:"e6 e7 e8 E e9 ea eb, ec ed ee 1T t 7M 1U ef"},2u:{7N:1n.K,4f:u(b){G(-1q.4g(b*1q.4h)/2)+0.5},eg:u(b){G 1-b},9U:u(b){D b=((-1q.4g(b*1q.4h)/4)+0.75)+1q.eh()/4;G b>1?1:b},ei:u(b){G(-1q.4g(b*1q.4h*(9*b))/2)+0.5},ej:u(c,d){G(-1q.4g((c*((d||5)-0.5)*2)*1q.4h)/2)+0.5},ek:u(b){G 1-(1q.4g(b*4.5*1q.4h)*1q.el(-b*6))},3h:u(b){G 0},9V:u(b){G 1}},3H:{1u:1,9W:2t,22:M,23:0,1U:1,27:0,28:"en"},eo:u(d){D c="1C:9X";v(1n.2h.4i){c+=";7O:1"}d=$(d);$A(d.2W).1f(u(a){v(a.7H==3){a.7I.ep().1f(u(b){d.4j(X V("eq",{O:c}).1c(b==" "?4b.er(es):b),a)});V.3i(a)}})},et:u(f,k){D h;v(((26 f=="46")||N.4k(f))&&(f.14)){h=f}13{h=$(f).2W}D g=N.11({3j:0.1,27:0},12[2]||{});D j=g.27;$A(h).1f(u(a,b){X k(a,N.11(g,{27:b*g.3j+j}))})},7P:{eu:["9Y","9Z"],ev:["a0","a1"],7Q:["6e","7R"]},ew:u(d,f){d=$(d);f=(f||"7Q").3f();D e=N.11({28:{1C:"2v",5f:(d.18||"5g"),7S:1}},12[2]||{});J[d.ey()?J.7P[f][1]:J.7P[f][0]](d,e)}};J.3H.29=J.2u.4f;J.a2=1D.1z(ez,{1G:u(){t.1m=[];t.5h=17},a3:u(b){t.1m.a3(b)},4l:u(d){D f=X 6f().a4();D e=N.3k(d.C.28)?d.C.28:d.C.28.1C;6g(e){1E"eA":t.1m.eB(u(a){G a.4m=="7T"}).1f(u(a){a.3l+=d.3m;a.3m+=d.3m});1N;1E"5i-a5":f=t.1m.a6("3l").4n()||f;1N;1E"2v":f=t.1m.a6("3m").4n()||f;1N}d.3l+=f;d.3m+=f;v(!d.C.28.7S||(t.1m.14<d.C.28.7S)){t.1m.1y(d)}v(!t.5h){t.5h=a7(t.4o.1g(t),15)}},3i:u(b){t.1m=t.1m.4p(u(a){G a==b});v(t.1m.14==0){a8(t.5h);t.5h=17}},4o:u(){D f=X 6f().a4();1T(D d=0,e=t.1m.14;d<e;d++){t.1m[d]&&t.1m[d].4o(f)}}});J.6h={7U:$H(),2i:u(b){v(!N.3k(b)){G b}G t.7U.2i(b)||t.7U.6i(b,X J.a2())}};J.eC=J.6h.2i("5g");J.2a=1D.1z({1C:17,1v:u(d){u c(a,b){G((a[b+"4q"]?"t.C."+b+"4q(t);":"")+(a[b]?"t.C."+b+"(t);":""))}v(d&&d.29===M){d.29=J.2u.7N}t.C=N.11(N.11({},J.3H),d||{});t.7V=0;t.4m="7T";t.3l=t.C.27*2D;t.3m=t.3l+(t.C.1u*2D);t.a9=t.C.1U-t.C.23;t.aa=t.3m-t.3l;t.ab=t.C.9W*t.C.1u;t.2E=(u(){u a(b,f){v(b.C[f+"4q"]){b.C[f+"4q"](b)}v(b.C[f]){b.C[f](b)}}G u(b){v(t.4m==="7T"){t.4m="ac";a(t,"3I");v(t.4r){t.4r()}a(t,"6j")}v(t.4m==="ac"){b=(t.C.29(b)*t.a9)+t.C.23;t.1C=b;a(t,"eD");v(t.1c){t.1c(b)}a(t,"eE")}}})();t.24("eF");v(!t.C.22){J.6h.2i(N.3k(t.C.28)?"5g":t.C.28.5f).4l(t)}},4o:u(f){v(f>=t.3l){v(f>=t.3m){t.2E(1);t.2Y();t.24("ad");v(t.3J){t.3J()}t.24("7W");G}D d=(f-t.3l)/t.aa,e=(d*t.ab).2b();v(e>t.7V){t.2E(d);t.7V=e}}},2Y:u(){v(!t.C.22){J.6h.2i(N.3k(t.C.28)?"5g":t.C.28.5f).3i(t)}t.4m="eG"},24:u(b){v(t.C[b+"4q"]){t.C[b+"4q"](t)}v(t.C[b]){t.C[b](t)}},5j:u(){D b=$H();1T(6k 3E t){v(!N.4k(t[6k])){b.6i(6k,t[6k])}}G"#<J:"+b.5j()+",C:"+$H(t.C).5j()+">"}});J.4s=1D.1z(J.2a,{1G:u(b){t.1m=b||[];t.1v(12[1])},1c:u(b){t.1m.ae("2E",b)},3J:u(b){t.1m.1f(u(a){a.2E(1);a.2Y();a.24("ad");v(a.3J){a.3J(b)}a.24("7W")})}});J.af=1D.1z(J.2a,{1G:u(m,j,k){m=N.3k(m)?$(m):m;D g=$A(12),l=g.a5(),h=g.14==5?g[3]:17;t.4t=N.4k(l)?l.1g(m):N.4k(m[l])?m[l].1g(m):u(a){m[l]=a};t.1v(N.11({23:j,1U:k},h||{}))},1c:u(b){t.4t(b)}});J.Z=1D.1z(J.2a,{1G:u(){t.1v(N.11({1u:0},12[0]||{}))},1c:1n.3K});J.2F=1D.1z(J.2a,{1G:u(c){t.E=$(c);v(!t.E){2Q(J.4e)}v(1n.2h.4i&&(!t.E.7X.ag)){t.E.1i({7O:1})}D d=N.11({23:t.E.5k()||0,1U:1},12[1]||{});t.1v(d)},1c:u(b){t.E.ah(b)}});J.2c=1D.1z(J.2a,{1G:u(c){t.E=$(c);v(!t.E){2Q(J.4e)}D d=N.11({x:0,y:0,ai:"9X"},12[1]||{});t.1v(d)},4r:u(){t.E.2j();t.5l=2Z(t.E.1o("1e")||"0");t.5m=2Z(t.E.1o("1b")||"0");v(t.C.ai=="3n"){t.C.x=t.C.x-t.5l;t.C.y=t.C.y-t.5m}},1c:u(b){t.E.1i({1e:(t.C.x*b+t.5l).2b()+"1w",1b:(t.C.y*b+t.5m).2b()+"1w"})}});J.eH=u(d,e,f){G X J.2c(d,N.11({x:f,y:e},12[3]||{}))};J.2k=1D.1z(J.2a,{1G:u(d,f){t.E=$(d);v(!t.E){2Q(J.4e)}D e=N.11({2G:Y,5n:Y,2H:Y,6l:M,2I:"7Y",3L:2t,aj:f},12[2]||{});t.1v(e)},4r:u(){t.2l=t.C.2l||M;t.ak=t.E.1o("1C");t.7Z={};["1b","1e","1j","1d","3G"].1f(u(a){t.7Z[a]=t.E.O[a]}.1g(t));t.5m=t.E.al;t.5l=t.E.eI;D b=t.E.1o("eJ-5o")||"2t%";["em","1w","%","am"].1f(u(a){v(b.2g(a)>0){t.3G=2Z(b);t.ao=a}}.1g(t));t.ap=(t.C.aj-t.C.3L)/2t;t.2w=17;v(t.C.2I=="7Y"){t.2w=[t.E.3o,t.E.3M]}v(/^eK/.5p(t.C.2I)){t.2w=[t.E.eL,t.E.eM]}v(!t.2w){t.2w=[t.C.2I.5q,t.C.2I.5r]}},1c:u(d){D c=(t.C.3L/2t)+(t.ap*d);v(t.C.2H&&t.3G){t.E.1i({3G:t.3G*c+t.ao})}t.aq(t.2w[0]*c,t.2w[1]*c)},3J:u(b){v(t.2l){t.E.1i(t.7Z)}},aq:u(g,j){D h={};v(t.C.2G){h.1j=j.2b()+"1w"}v(t.C.5n){h.1d=g.2b()+"1w"}v(t.C.6l){D k=(g-t.2w[0])/2;D d=(j-t.2w[1])/2;v(t.ak=="3n"){v(t.C.5n){h.1b=t.5m-k+"1w"}v(t.C.2G){h.1e=t.5l-d+"1w"}}13{v(t.C.5n){h.1b=-k+"1w"}v(t.C.2G){h.1e=-d+"1w"}}}t.E.1i(h)}});J.6m=1D.1z(J.2a,{1G:u(c){t.E=$(c);v(!t.E){2Q(J.4e)}D d=N.11({6n:"#ar"},12[1]||{});t.1v(d)},4r:u(){v(t.E.1o("5s")=="3h"){t.2Y();G}t.80={};v(!t.C.81){t.80.as=t.E.1o("6o-eN");t.E.1i({as:"3h"})}v(!t.C.6p){t.C.6p=t.E.1o("6o-30").5c("#82")}v(!t.C.6q){t.C.6q=t.E.1o("6o-30")}t.83=$R(0,2).1I(u(b){G 2r(t.C.6n.3F(b*2+1,b*2+3),16)}.1g(t));t.at=$R(0,2).1I(u(b){G 2r(t.C.6p.3F(b*2+1,b*2+3),16)-t.83[b]}.1g(t))},1c:u(b){t.E.1i({4u:$R(0,2).84("#",u(a,f,e){G a+((t.83[e]+(t.at[e]*b)).2b().5d())}.1g(t))})},3J:u(){t.E.1i(N.11(t.80,{4u:t.C.6q}))}});J.eO=u(h){D e=12[1]||{},f=19.eP.eQ(),g=$(h).3p();v(e.5t){g[1]+=e.5t}G X J.af(17,f.1b,g[1],e,u(a){85(f.1e,a.2b())})};J.7R=u(f){f=$(f);D e=f.2X();D d=N.11({23:f.5k()||1,1U:0,1r:u(a){v(a.C.1U!=0){G}a.E.1s().1i({1R:e})}},12[1]||{});G X J.2F(f,d)};J.6e=u(c){c=$(c);D d=N.11({23:(c.1o("5s")=="3h"?0:c.5k()||0),1U:1,1r:u(a){a.E.7L()},3I:u(a){a.E.ah(a.C.23).1O()}},12[1]||{});G X J.2F(c,d)};J.eR=u(c){c=$(c);D d={1R:c.2X(),1C:c.1o("1C"),1b:c.O.1b,1e:c.O.1e,1j:c.O.1j,1d:c.O.1d};G X J.4s([X J.2k(c,eS,{22:Y,6l:Y,2H:Y,2l:Y}),X J.2F(c,{22:Y,1U:0})],N.11({1u:1,eT:u(a){1p.au(a.1m[0].E)},1r:u(a){a.1m[0].E.1s().1i(d)}},12[1]||{}))};J.a1=u(b){b=$(b);b.31();G X J.2k(b,0,N.11({2H:M,2G:M,2l:Y,1r:u(a){a.E.1s().32()}},12[1]||{}))};J.a0=u(c){c=$(c);D d=c.5u();G X J.2k(c,2t,N.11({2H:M,2G:M,3L:0,2I:{5q:d.1d,5r:d.1j},2l:Y,6j:u(a){a.E.31().1i({1d:"86"}).1O()},1r:u(a){a.E.32()}},12[1]||{}))};J.eU=u(c){c=$(c);D d=c.2X();G X J.6e(c,N.11({1u:0.4,23:0,29:J.2u.9U,1r:u(a){X J.2k(a.E,1,{1u:0.3,6l:Y,2G:M,2H:M,2l:Y,3I:u(b){b.E.2j().31()},1r:u(b){b.E.1s().32().34().1i({1R:d})}})}},12[1]||{}))};J.eV=u(c){c=$(c);D d={1b:c.1o("1b"),1e:c.1o("1e"),1R:c.2X()};G X J.4s([X J.2c(c,{x:0,y:2t,22:Y}),X J.2F(c,{22:Y,1U:0})],N.11({1u:0.5,3I:u(a){a.1m[0].E.2j()},1r:u(a){a.1m[0].E.1s().34().1i(d)}},12[1]||{}))};J.eW=u(j){j=$(j);D f=N.11({av:20,1u:0.5},12[1]||{});D h=2Z(f.av);D k=2Z(f.1u)/10;D g={1b:j.1o("1b"),1e:j.1o("1e")};G X J.2c(j,{x:h,y:0,1u:k,1r:u(a){X J.2c(a.E,{x:-h*2,y:0,1u:k*2,1r:u(b){X J.2c(b.E,{x:h*2,y:0,1u:k*2,1r:u(c){X J.2c(c.E,{x:-h*2,y:0,1u:k*2,1r:u(d){X J.2c(d.E,{x:h*2,y:0,1u:k*2,1r:u(e){X J.2c(e.E,{x:-h,y:0,1u:k,1r:u(l){l.E.34().1i(g)}})}})}})}})}})}})};J.9Y=u(f){f=$(f).5v();D e=f.21().1o("2x");D d=f.5u();G X J.2k(f,2t,N.11({2H:M,2G:M,3L:1M.3N?0:1,2I:{5q:d.1d,5r:d.1j},2l:Y,6j:u(a){a.E.2j();a.E.21().2j();v(1M.3N){a.E.1i({1b:""})}a.E.31().1i({1d:"86"}).1O()},aw:u(a){a.E.21().1i({2x:(a.2w[0]-a.E.6r)+"1w"})},1r:u(a){a.E.32().34();a.E.21().34().1i({2x:e})}},12[1]||{}))};J.9Z=u(f){f=$(f).5v();D e=f.21().1o("2x");D d=f.5u();G X J.2k(f,1M.3N?0:1,N.11({2H:M,2G:M,2I:"7Y",3L:2t,2I:{5q:d.1d,5r:d.1j},2l:Y,6j:u(a){a.E.2j();a.E.21().2j();v(1M.3N){a.E.1i({1b:""})}a.E.31().1O()},aw:u(a){a.E.21().1i({2x:(a.2w[0]-a.E.6r)+"1w"})},1r:u(a){a.E.1s().32().34();a.E.21().34().1i({2x:e})}},12[1]||{}))};J.eX=u(b){G X J.2k(b,1M.3N?1:0,{2l:Y,3I:u(a){a.E.31()},1r:u(a){a.E.1s().32()}})};J.eY=u(q){q=$(q);D j=N.11({6s:"6t",6u:J.2u.4f,6v:J.2u.4f,6w:J.2u.9V},12[1]||{});D k={1b:q.O.1b,1e:q.O.1e,1d:q.O.1d,1j:q.O.1j,1R:q.2X()};D m=q.5u();D l,n;D o,p;6g(j.6s){1E"1b-1e":l=n=o=p=0;1N;1E"1b-5w":l=m.1j;n=p=0;o=-m.1j;1N;1E"2x-1e":l=o=0;n=m.1d;p=-m.1d;1N;1E"2x-5w":l=m.1j;n=m.1d;o=-m.1j;p=-m.1d;1N;1E"6t":l=m.1j/2;n=m.1d/2;o=-m.1j/2;p=-m.1d/2;1N}G X J.2c(q,{x:l,y:n,1u:0.eZ,3I:u(a){a.E.1s().31().2j()},1r:u(a){X J.4s([X J.2F(a.E,{22:Y,1U:1,23:0,29:j.6w}),X J.2c(a.E,{x:o,y:p,22:Y,29:j.6u}),X J.2k(a.E,2t,{2I:{5q:m.1d,5r:m.1j},22:Y,3L:1M.3N?1:0,29:j.6v,2l:Y})],N.11({3I:u(b){b.1m[0].E.1i({1d:"86"}).1O()},1r:u(b){b.1m[0].E.32().34().1i(k)}},j))}})};J.f0=u(m){m=$(m);D g=N.11({6s:"6t",6u:J.2u.4f,6v:J.2u.4f,6w:J.2u.3h},12[1]||{});D h={1b:m.O.1b,1e:m.O.1e,1d:m.O.1d,1j:m.O.1j,1R:m.2X()};D j=m.5u();D k,l;6g(g.6s){1E"1b-1e":k=l=0;1N;1E"1b-5w":k=j.1j;l=0;1N;1E"2x-1e":k=0;l=j.1d;1N;1E"2x-5w":k=j.1j;l=j.1d;1N;1E"6t":k=j.1j/2;l=j.1d/2;1N}G X J.4s([X J.2F(m,{22:Y,1U:0,23:1,29:g.6w}),X J.2k(m,1M.3N?1:0,{22:Y,29:g.6v,2l:Y}),X J.2c(m,{x:k,y:l,22:Y,29:g.6u})],N.11({f1:u(a){a.1m[0].E.2j().31()},1r:u(a){a.1m[0].E.1s().32().34().1i(h)}},g))};J.f2=u(k){k=$(k);D f=12[1]||{},g=k.2X(),h=f.29||J.2u.7N,j=u(a){G 1-h((-1q.4g((a*(f.f3||5)*2)*1q.4h)/2)+0.5)};G X J.2F(k,N.11(N.11({1u:2,23:0,1r:u(a){a.E.1i({1R:g})}},f),{29:j}))};J.f4=u(c){c=$(c);D d={1b:c.O.1b,1e:c.O.1e,1j:c.O.1j,1d:c.O.1d};c.31();G X J.2k(c,5,N.11({2H:M,2G:M,1r:u(a){X J.2k(c,1,{2H:M,5n:M,1r:u(b){b.E.1s().32().1i(d)}})}},12[1]||{}))};J.87=1D.1z(J.2a,{1G:u(f){t.E=$(f);v(!t.E){2Q(J.4e)}D e=N.11({O:{}},12[1]||{});v(!N.3k(e.O)){t.O=$H(e.O)}13{v(e.O.4v(":")){t.O=e.O.ax()}13{t.E.2y(e.O);t.O=$H(t.E.5x());t.E.3q(e.O);D d=t.E.5x();t.O=t.O.4p(u(a){G a.1k==d[a.4w]});e.1r=u(a){a.E.2y(a.C.O);a.6x.1f(u(b){a.E.O[b.O]=""})}}}t.1v(e)},4r:u(){u b(a){v(!a||["f5(0, 0, 0, 0)","ay"].4v(a)){a="#82"}a=a.5c();G $R(0,2).1I(u(d){G 2r(a.3F(d*2+1,d*2+3),16)})}t.6x=t.O.1I(u(h){D j=h[0],k=h[1],l=17;v(k.5c("#az")!="#az"){k=k.5c();l="30"}13{v(j=="1R"){k=2Z(k);v(1n.2h.4i&&(!t.E.7X.ag)){t.E.1i({7O:1})}}13{v(V.aA.5p(k)){D m=k.3r(/^([\\+\\-]?[0-9\\.]+)(.*)$/);k=2Z(m[1]);l=(m.14==3)?m[2]:17}}}D a=t.E.1o(j);G{O:j.aB(),2z:l=="30"?b(a):2Z(a||0),3O:l=="30"?b(k):k,5y:l}}.1g(t)).4p(u(a){G((a.2z==a.3O)||(a.5y!="30"&&(aC(a.2z)||aC(a.3O))))})},1c:u(f){D g={},e,h=t.6x.14;4d(h--){g[(e=t.6x[h]).O]=e.5y=="30"?"#"+(1q.2b(e.2z[0]+(e.3O[0]-e.2z[0])*f)).5d()+(1q.2b(e.2z[1]+(e.3O[1]-e.2z[1])*f)).5d()+(1q.2b(e.2z[2]+(e.3O[2]-e.2z[2])*f)).5d():(e.2z+(e.3O-e.2z)*f).f6(3)+(e.5y===17?"":e.5y)}t.E.1i(g,Y)}});J.f7=1D.1z({1G:u(b){t.2A=[];t.C=12[1]||{};t.aD(b)},aD:u(b){b.1f(u(a){a=$H(a);D d=a.1J().aE();t.2A.1y($H({aF:a.f8().aE(),7M:J.87,C:{O:d}}))}.1g(t));G t},88:u(){G X J.4s(t.2A.1I(u(g){D j=g.2i("aF"),k=g.2i("7M"),f=g.2i("C");D h=[$(j)||$$(j)].2s();G h.1I(u(a){G X k(a,N.11({22:Y},f))})}).2s(),t.C)}});V.6y=$w("4u f9 fa fb fc fd fe ff fg fh fi fj fk fl fm 2x fn 30 3G fo 1d 1e fp fq fr fs ft fu fv fw fx fy fz 1R fA fB fC fD fE fF fG 5w fH 1b 1j fI 5z");V.aA=/^(([\\+\\-]?[0-9\\.]+)(em|ex|1w|3E|cm|fJ|am|fK|\\%))|0$/;4b.89=19.1Z("47");4b.4c.ax=u(){D c,d=$H();v(1n.2h.5e){c=X V("47",{O:t}).O}13{4b.89.2U=\'<47 O="\'+t+\'"></47>\';c=4b.89.2W[0].O}V.6y.1f(u(a){v(c[a]){d.6i(a,c[a])}});v(1n.2h.4i&&t.4v("1R")){d.6i("1R",t.3r(/1R:\\s*((?:0|1)?(?:\\.\\d*)?)/)[1])}G d};v(19.8a&&19.8a.aG){V.5x=u(c){D d=19.8a.aG($(c),17);G V.6y.84({},u(b,a){b[a]=d[a];G b})}}13{V.5x=u(d){d=$(d);D e=d.7X,f;f=V.6y.84({},u(b,a){b[a]=e[a];G b});v(!f.1R){f.1R=d.5k()}G f}}J.57={fL:u(d,c){d=$(d);X J.87(d,N.11({O:c},12[2]||{}));G d},fM:u(k,h,f){k=$(k);D j=h.fN().aB(),g=j.6b(0).2T()+j.8b(1);X J[g](k,f);G k},fO:u(c,d){c=$(c);X J.6m(c,d);G c}};$w("fP 7Q fQ fR fS fT fU fV fW fX fY fZ g0 g1 g2").1f(u(b){J.57[b]=u(d,a){d=$(d);J[b.6b(0).2T()+b.8b(1)](d,a);G d}});$w("2X 7L 9S 6c 6d 5x").1f(u(b){J.57[b]=V[b]});V.g3(J.57);v(N.8c(J)){2Q("g4.3P 7w aH 56.7u.7v\' 1m.3P aI")}D 2d={4x:[],3i:u(b){t.4x=t.4x.4p(u(a){G a.E==$(b)})},4l:u(d){d=$(d);D e=N.11({g5:Y,2J:17,25:M},12[1]||{});v(e.3Q){e.5A=[];D f=e.3Q;v(N.6z(f)){f.1f(u(a){e.5A.1y($(a))})}13{e.5A.1y($(f))}}v(e.5B){e.5B=[e.5B].2s()}V.2j(d);e.E=d;t.4x.1y(e)},aJ:u(b){6A=b[0];1T(i=1;i<b.14;++i){v(V.5C(b[i].E,6A.E)){6A=b[i]}}G 6A},aK:u(d,e){D f;v(e.25){f=d.8d}13{f=d.1A}G e.5A.6B(u(a){G f==a})},8e:u(e,f,d){G((d.E!=f)&&((!d.5A)||t.aK(f,d))&&((!d.5B)||(V.aL(f).6B(u(a){G d.5B.4v(a)})))&&1p.aM(d.E,e[0],e[1]))},5D:u(b){v(b.2J){V.3q(b.E,b.2J)}t.2e=17},3s:u(b){v(b.2J){V.2y(b.E,b.2J)}t.2e=b},1O:u(f,h){v(!t.4x.14){G}D e,g=[];t.4x.1f(u(a){v(2d.8e(f,h,a)){g.1y(a)}});v(g.14>0){e=2d.aJ(g)}v(t.2e&&t.2e!=e){t.5D(t.2e)}v(e){1p.aM(e.E,f[0],f[1]);v(e.3t){e.3t(h,e.E,1p.2m(e.2m,e.E))}v(e!=t.2e){2d.3s(e)}}},aN:u(c,d){v(!t.2e){G}1p.6C();v(t.8e([Z.4y(c),Z.4z(c)],d,t.2e)){v(t.2e.aO){t.2e.aO(d,t.2e.E,c);G Y}}},aP:u(){v(t.2e){t.5D(t.2e)}}};D 1l={4A:[],4B:[],aQ:u(b){v(t.4A.14==0){t.4C=t.4D.2n(t);t.4E=t.6D.2n(t);t.8f=t.6E.2n(t);Z.1V(19,"6F",t.4C);Z.1V(19,"6G",t.4E);Z.1V(19,"aR",t.8f)}t.4A.1y(b)},aS:u(b){t.4A=t.4A.4p(u(a){G a==b});v(t.4A.14==0){Z.2K(19,"6F",t.4C);Z.2K(19,"6G",t.4E);Z.2K(19,"aR",t.8f)}},3s:u(b){v(b.C.27){t.5E=4F(u(){1l.5E=17;1M.5F();1l.2L=b}.1g(t),b.C.27)}13{1M.5F();t.2L=b}},5D:u(){t.2L=17},6D:u(d){v(!t.2L){G}D c=[Z.4y(d),Z.4z(d)];v(t.4G&&(t.4G.5j()==c.5j())){G}t.4G=c;t.2L.6D(d,c)},4D:u(b){v(t.5E){8g(t.5E);t.5E=17}v(!t.2L){G}t.4G=17;t.2L.4D(b);t.2L=17},6E:u(b){v(t.2L){t.2L.6E(b)}},aT:u(b){t.4B.1y(b);t.8h()},aU:u(b){t.4B=t.4B.4p(u(a){G a.E==b});t.8h()},5G:u(d,e,f){v(t[d+"aV"]>0){t.4B.1f(u(a){v(a[d]){a[d](d,e,f)}})}v(e.C[d]){e.C[d](e,f)}},8h:u(){["8i","8j","8k"].1f(u(b){1l[b+"aV"]=1l.4B.2S(u(a){G a[b]}).14})}};D 3R=1D.1z({1G:u(d){D f={1K:M,3S:u(b,c,h){D a=1q.g6(1q.6H(c^2)+1q.6H(h^2))*0.g7;X J.2c(b,{x:-h,y:-c,1u:a,28:{5f:"aW",1C:"2v"}})},3T:u(a){D b=N.g8(a.6I)?a.6I:1;X J.2F(a,{1u:0.2,23:0.7,1U:b,28:{5f:"aW",1C:"2v"},7W:u(){3R.5H[a]=M}})},3U:2D,8l:M,4H:M,1h:M,2o:20,1W:15,35:M,27:0};v(!12[1]||N.8c(12[1].3T)){N.11(f,{4I:u(a){a.6I=V.5k(a);3R.5H[a]=Y;X J.2F(a,{1u:0.2,23:a.6I,1U:0.7})}})}D e=N.11(f,12[1]||{});t.E=$(d);v(e.1K&&N.3k(e.1K)){t.1K=t.E.21("."+e.1K,0)}v(!t.1K){t.1K=$(e.1K)}v(!t.1K){t.1K=t.E}v(e.1h&&!e.1h.85&&!e.1h.g9){e.1h=$(e.1h);t.8m=V.ga(t.E,e.1h)}V.2j(t.E);t.C=e;t.2p=M;t.3u=t.aX.2n(t);Z.1V(t.1K,"4J",t.3u);1l.aQ(t)},4K:u(){Z.2K(t.1K,"4J",t.3u);1l.aS(t)},6J:u(){G([2r(V.1o(t.E,"1e")||"0"),2r(V.1o(t.E,"1b")||"0")])},aX:u(f){v(!N.8c(3R.5H[t.E])&&3R.5H[t.E]){G}v(Z.aY(f)){D h=Z.E(f);v((4L=h.2V.2T())&&(4L=="9O"||4L=="9P"||4L=="7x"||4L=="9M"||4L=="9Q")){G}D e=[Z.4y(f),Z.4z(f)];D g=1p.3p(t.E);t.5t=[0,1].1I(u(a){G(e[a]-g[a])});1l.3s(t);Z.1X(f)}},6K:u(c){t.2p=Y;v(!t.2B){t.2B=t.6J()}v(t.C.3U){t.aZ=2r(V.1o(t.E,"z-1S")||0);t.E.O.5z=t.C.3U}v(t.C.3v){t.6L=t.E.gb(Y);t.6M=(t.E.1o("1C")=="3n");v(!t.6M){1p.au(t.E)}t.E.1A.4j(t.6L,t.E)}v(t.C.1h){v(t.C.1h==1M){D d=t.6N(t.C.1h);t.8n=d.1e;t.8o=d.1b}13{t.8n=t.C.1h.4M;t.8o=t.C.1h.3V}}1l.5G("8i",t,c);v(t.C.4I){t.C.4I(t.E)}},6D:u(24,2C){v(!t.2p){t.6K(24)}v(!t.C.4H){1p.6C();2d.1O(2C,t.E)}1l.5G("8k",t,24);t.5I(2C);v(t.C.6O){t.C.6O(t)}v(t.C.1h){t.8p();D p;v(t.C.1h==1M){5i(t.6N(t.C.1h)){p=[1e,1b,1e+1j,1b+1d]}}13{p=1p.gc(t.C.1h);p[0]+=t.C.1h.4M+1p.b0;p[1]+=t.C.1h.3V+1p.b1;p.1y(p[0]+t.C.1h.3M);p.1y(p[1]+t.C.1h.3o)}D 3j=[0,0];v(2C[0]<(p[0]+t.C.2o)){3j[0]=2C[0]-(p[0]+t.C.2o)}v(2C[1]<(p[1]+t.C.2o)){3j[1]=2C[1]-(p[1]+t.C.2o)}v(2C[0]>(p[2]-t.C.2o)){3j[0]=2C[0]-(p[2]-t.C.2o)}v(2C[1]>(p[3]-t.C.2o)){3j[1]=2C[1]-(p[3]-t.C.2o)}t.b2(3j)}v(1n.2h.5e){1M.7K(0,0)}Z.1X(24)},5J:u(d,k){t.2p=M;v(t.C.4H){1p.6C();D l=[Z.4y(d),Z.4z(d)];2d.1O(l,t.E)}v(t.C.3v){v(!t.6M){1p.gd(t.E)}8q t.6M;V.3i(t.6L);t.6L=17}D j=M;v(k){j=2d.aN(d,t.E);v(!j){j=M}}v(j&&t.C.b3){t.C.b3(t.E)}1l.5G("8j",t,d);D h=t.C.8l;v(h&&N.4k(h)){h=h(t.E)}D m=t.6J();v(h&&t.C.3S){v(j==0||h!="ge"){t.C.3S(t.E,m[1]-t.2B[1],m[0]-t.2B[0])}}13{t.2B=m}v(t.C.3U){t.E.O.5z=t.aZ}v(t.C.3T){t.C.3T(t.E)}1l.5D(t);2d.aP()},6E:u(b){v(b.3W!=Z.8r){G}t.5J(b,M);Z.1X(b)},4D:u(b){v(!t.2p){G}t.8p();t.5J(b,Y);Z.1X(b)},5I:u(h){D j=1p.3p(t.E);v(t.C.3v){D m=1p.gf(t.E);j[0]+=m[0]-1p.b0;j[1]+=m[1]-1p.b1}D k=t.6J();j[0]-=k[0];j[1]-=k[1];v(t.C.1h&&(t.C.1h!=1M&&t.8m)){j[0]-=t.C.1h.4M-t.8n;j[1]-=t.C.1h.3V-t.8o}D l=[0,1].1I(u(a){G(h[a]-j[a]-t.5t[a])}.1g(t));v(t.C.35){v(N.4k(t.C.35)){l=t.C.35(l[0],l[1],t)}13{v(N.6z(t.C.35)){l=l.1I(u(b,a){G(b/t.C.35[a]).2b()*t.C.35[a]}.1g(t))}13{l=l.1I(u(a){G(a/t.C.35).2b()*t.C.35}.1g(t))}}}D d=t.E.O;v((!t.C.3X)||(t.C.3X=="8s")){d.1e=l[0]+"1w"}v((!t.C.3X)||(t.C.3X=="5K")){d.1b=l[1]+"1w"}v(d.6P=="6Q"){d.6P=""}},8p:u(){v(t.6R){a8(t.6R);t.6R=17;1l.2M=17}},b2:u(b){v(!(b[0]||b[1])){G}t.1W=[b[0]*t.C.1W,b[1]*t.C.1W];t.8t=X 6f();t.6R=a7(t.1h.1g(t),10)},1h:u(){D 8u=X 6f();D 2B=8u-t.8t;t.8t=8u;v(t.C.1h==1M){5i(t.6N(t.C.1h)){v(t.1W[0]||t.1W[1]){D d=2B/2D;t.C.1h.85(1e+d*t.1W[0],1b+d*t.1W[1])}}}13{t.C.1h.4M+=t.1W[0]*2B/2D;t.C.1h.3V+=t.1W[1]*2B/2D}1p.6C();2d.1O(1l.4G,t.E);1l.5G("8k",t);v(t.8m){1l.2M=1l.2M||$A(1l.4G);1l.2M[0]+=t.1W[0]*2B/2D;1l.2M[1]+=t.1W[1]*2B/2D;v(1l.2M[0]<0){1l.2M[0]=0}v(1l.2M[1]<0){1l.2M[1]=0}t.5I(1l.2M)}v(t.C.6O){t.C.6O(t)}},6N:u(w){D T,L,W,H;5i(w.19){v(w.19.3w&&3w.3V){T=3w.3V;L=3w.4M}13{v(w.19.3Y){T=3Y.3V;L=3Y.4M}}v(w.b4){W=w.b4;H=w.gg}13{v(w.19.3w&&3w.8v){W=3w.8v;H=3w.6r}13{W=3Y.3M;H=3Y.3o}}}G{1b:T,1e:L,1j:W,1d:H}}});3R.5H={};D b5=1D.1z({1G:u(c,d){t.E=$(c);t.4N=d;t.4O=1a.5L(t.E)},8i:u(){t.4O=1a.5L(t.E)},8j:u(){1a.b6();v(t.4O!=1a.5L(t.E)){t.4N(t.E)}}});D 1a={b7:/^[^2P\\-](?:[A-gh-gi-9\\-\\2P]*)[2P](.*)$/,4P:{},b8:u(b){4d(b.2V.2T()!="9L"){v(b.18&&1a.4P[b.18]){G b}b=b.1A}},C:u(b){b=1a.b8($(b));v(!b){G}G 1a.4P[b.18]},4K:u(d){d=$(d);D c=1a.4P[d.18];v(c){1l.aU(c.E);c.5M.1f(u(a){2d.3i(a)});c.8w.ae("4K");8q 1a.4P[c.E.18]}},1z:u(k){k=$(k);D f=N.11({E:k,4Q:"5N",b9:M,25:M,5O:"8x",2m:"5K",3X:"5K",3Q:k,1K:M,3Z:M,27:0,2J:17,3v:M,4H:M,1h:M,2o:20,1W:15,3x:t.b7,ba:M,1x:M,36:1n.3K,bb:1n.3K},12[1]||{});t.4K(k);D g={8l:Y,4H:f.4H,1h:f.1h,1W:f.1W,2o:f.2o,27:f.27,3v:f.3v,3X:f.3X,1K:f.1K};v(f.4I){g.4I=f.4I}v(f.3S){g.3S=f.3S}13{v(f.3v){g.3S=u(a){a.O.1b=0;a.O.1e=0}}}v(f.3T){g.3T=f.3T}v(f.3U){g.3U=f.3U}D j={2m:f.2m,3Q:f.3Q,25:f.25,2J:f.2J,3t:1a.3t};D h={3t:1a.bc,2m:f.2m,3Q:f.3Q,2J:f.2J};V.5v(k);f.8w=[];f.5M=[];v(f.b9||f.25){2d.4l(k,h);f.5M.1y(k)}(f.ba||t.4R(k,f)||[]).1f(u(a,c){D b=f.1x?$(f.1x[c]):(f.1K?$(a).2S("."+f.1K)[0]:a);f.8w.1y(X 3R(a,N.11(g,{1K:b})));2d.4l(a,j);v(f.25){a.8d=k}f.5M.1y(a)});v(f.25){(1a.bd(k,f)||[]).1f(u(a){2d.4l(a,h);a.8d=k;f.5M.1y(a)})}t.4P[k.18]=f;1l.aT(X b5(k,f.bb))},4R:u(c,d){G V.6S(c,d.3Z,d.25?Y:M,d.4Q)},bd:u(c,d){G V.6S(c,d.3Z,d.25?Y:M,d.5O)},3t:u(h,j,g){v(V.5C(j,h)){G}v(g>0.33&&g<0.66&&1a.C(j).25){G}13{v(g>0.5){1a.8y(j,"gj");v(j.gk!=h){D f=h.1A;h.O.6P="6Q";j.1A.4j(h,j);v(j.1A!=f){1a.C(f).36(h)}1a.C(j.1A).36(h)}}13{1a.8y(j,"be");D k=j.gl||17;v(k!=h){D f=h.1A;h.O.6P="6Q";j.1A.4j(h,k);v(j.1A!=f){1a.C(f).36(h)}1a.C(j.1A).36(h)}}}},bc:u(o,m,l){D k=o.1A;D s=1a.C(m);v(!V.5C(m,o)){D n;D q=1a.4R(m,{4Q:s.4Q,3Z:s.3Z});D r=17;v(q){D p=V.5P(m,s.2m)*(1-l);1T(n=0;n<q.14;n+=1){v(p-V.5P(q[n],s.2m)>=0){p-=V.5P(q[n],s.2m)}13{v(p-(V.5P(q[n],s.2m)/2)>=0){r=n+1<q.14?q[n+1]:17;1N}13{r=q[n];1N}}}}m.4j(o,r);1a.C(k).36(o);s.36(o)}},b6:u(){v(1a.37){1a.37.1s()}},8y:u(e,f){D g=1a.C(e.1A);v(g&&!g.3v){G}v(!1a.37){1a.37=($("bf")||V.11(19.1Z("9N"))).1s().2y("bf").1i({1C:"3n"});19.7B("3Y").gm(0).1L(1a.37)}D h=1p.3p(e);1a.37.1i({1e:h[0]+"1w",1b:h[1]+"1w"});v(f=="be"){v(g.2m=="8s"){1a.37.1i({1e:(h[0]+e.8v)+"1w"})}13{1a.37.1i({1b:(h[1]+e.6r)+"1w"})}}1a.37.1O()},8z:u(m,h,l){D n=1a.4R(m,h)||[];1T(D o=0;o<n.14;++o){D j=n[o].18.3r(h.3x);v(!j){gn}D k={18:38(j?j[1]:17),E:m,8A:l,4S:[],1C:l.4S.14,6T:$(n[o]).21(h.5O)};v(k.6T){t.8z(k.6T,h,k)}l.4S.1y(k)}G l},25:u(g){g=$(g);D h=t.C(g);D e=N.11({4Q:h.4Q,5O:h.5O,3Z:h.3Z,3g:g.18,3x:h.3x},12[1]||{});D f={18:17,8A:17,4S:[],6T:g,1C:0};G 1a.8z(g,e,f)},bg:u(c){D d="";do{v(c.18){d="["+c.1C+"]"+d}}4d((c=c.8A)!=17);G d},bh:u(c){c=$(c);D d=N.11(t.C(c),12[1]||{});G $(t.4R(c,d)||[]).1I(u(a){G a.18.3r(d.3x)?a.18.3r(d.3x)[1]:""})},go:u(e,h){e=$(e);D f=N.11(t.C(e),12[2]||{});D g={};t.4R(e,f).1f(u(a){v(a.18.3r(f.3x)){g[a.18.3r(f.3x)[1]]=[a,a.1A]}a.1A.9T(a)});h.1f(u(b){D a=g[b];v(a){a[1].1L(a[0]);8q g[b]}})},5L:u(f){f=$(f);D d=N.11(1a.C(f),12[1]||{});D e=38((12[1]&&12[1].3g)?12[1].3g:f.18);v(d.25){G 1a.25(f,12[1]).4S.1I(u(a){G[e+1a.bg(a)+"[18]="+38(a.18)].7F(a.4S.1I(12.8B))}).2s().4a("&")}13{G 1a.bh(f,12[1]).1I(u(a){G e+"[]="+38(a)}).4a("&")}}};V.5C=u(c,d){v(!c.1A||c==d){G M}v(c.1A==d){G Y}G V.5C(c.1A,d)};V.6S=u(j,f,g,k){v(!j.7J()){G 17}k=k.2T();v(f){f=[f].2s()}D h=[];$A(j.2W).1f(u(a){v(a.2V&&a.2V.2T()==k&&(!f||(V.aL(a).6B(u(c){G f.4v(c)})))){h.1y(a)}v(g){D b=V.6S(a,f,g,k);v(b){h.1y(b)}}});G(h.14>0?h.2s():[])};V.5P=u(d,c){G d["5t"+((c=="5K"||c=="1d")?"gp":"gq")]};v(26 J=="3C"){2Q("gr.3P 7w aH 56.7u.7v\' 1m.3P aI")}D 41={};41.2a=1D.1z({8C:u(d,f,e){d=$(d);t.E=d;t.1c=$(f);t.4T=M;t.5Q=M;t.1Y=M;t.1S=0;t.39=0;t.5R=t.E.1k;v(t.8D){t.8D(e)}13{t.C=e||{}}t.C.4U=t.C.4U||t.E.3g;t.C.2N=t.C.2N||[];t.C.8E=t.C.8E||0.4;t.C.8F=t.C.8F||1;t.C.8G=t.C.8G||u(b,a){v(!a.O.1C||a.O.1C=="3n"){a.O.1C="3n";1p.8H(b,a,{gs:M,al:b.3o})}J.6e(a,{1u:0.15})};t.C.8I=t.C.8I||u(b,a){X J.7R(a,{1u:0.15})};v(26(t.C.2N)=="9K"){t.C.2N=X 9H(t.C.2N)}v(!t.C.2N.4v("\\n")){t.C.2N.1y("\\n")}t.4N=17;t.E.gt("gu","gv");V.1s(t.1c);Z.1V(t.E,"gw",t.bi.2n(t));Z.1V(t.E,"bj",t.bk.2n(t))},1O:u(){v(V.1o(t.1c,"5s")=="3h"){t.C.8G(t.E,t.1c)}v(!t.3y&&(1n.2h.4i)&&(V.1o(t.1c,"1C")=="3n")){X gx.bl(t.1c,\'<bm 18="\'+t.1c.18+\'bn" O="5s:3h;1C:3n;gy:gz:gA.gB.gC(1R=0);" 68="9q:M;" gD="0" gE="gF"></bm>\');t.3y=$(t.1c.18+"bn")}v(t.3y){4F(t.bo.1g(t),50)}},bo:u(){1p.8H(t.1c,t.3y,{gG:(!t.1c.O.1d)});t.3y.O.5z=1;t.1c.O.5z=2;V.1O(t.3y)},1s:u(){t.8J();v(V.1o(t.1c,"5s")!="3h"){t.C.8I(t.E,t.1c)}v(t.3y){V.1s(t.3y)}},bp:u(){v(t.C.6U){V.1O(t.C.6U)}},8J:u(){v(t.C.6U){V.1s(t.C.6U)}},bk:u(b){v(t.1Y){6g(b.3W){1E Z.bq:1E Z.8K:t.6V();Z.1X(b);1E Z.8r:t.1s();t.1Y=M;Z.1X(b);G;1E Z.gH:1E Z.gI:G;1E Z.gJ:t.bs();t.2E();Z.1X(b);G;1E Z.gK:t.bt();t.2E();Z.1X(b);G}}13{v(b.3W==Z.bq||b.3W==Z.8K||(1n.2h.5e>0&&b.3W==0)){G}}t.5Q=Y;t.4T=Y;v(t.4N){8g(t.4N)}t.4N=4F(t.bu.1g(t),t.C.8E*2D)},3s:u(){t.5Q=M;t.4T=Y;t.6W()},3t:u(c){D d=Z.bv(c,"7E");v(t.1S!=d.6X){t.1S=d.6X;t.2E()}Z.1X(c)},bw:u(c){D d=Z.bv(c,"7E");t.1S=d.6X;t.6V();t.1s()},bi:u(b){4F(t.1s.1g(t),gL);t.4T=M;t.1Y=M},2E:u(){v(t.39>0){1T(D b=0;b<t.39;b++){t.1S==b?V.2y(t.42(b),"4V"):V.3q(t.42(b),"4V")}v(t.4T){t.1O();t.1Y=Y}}13{t.1Y=M;t.1s()}},bs:u(){v(t.1S>0){t.1S--}13{t.1S=t.39-1}t.42(t.1S).bx(Y)},bt:u(){v(t.1S<t.39-1){t.1S++}13{t.1S=0}t.42(t.1S).bx(M)},42:u(b){G t.1c.58.2W[b]},by:u(){G t.42(t.1S)},6V:u(){t.1Y=M;t.6Y(t.by())},6Y:u(j){v(t.C.6Y){t.C.6Y(j);G}D l="";v(t.C.2S){D h=$(j).2S("."+t.C.2S)||[];v(h.14>0){l=V.6c(h[0],t.C.2S)}}13{l=V.6d(j,"gM")}D m=t.6Z();v(m[0]!=-1){D k=t.E.1k.3a(0,m[0]);D g=t.E.1k.3a(m[0]).3r(/^\\s+/);v(g){k+=g[0]}t.E.1k=k+l+t.E.1k.3a(m[1])}13{t.E.1k=l}t.5R=t.E.1k;t.E.5F();v(t.C.bz){t.C.bz(t.E,j)}},8L:u(f){v(!t.5Q&&t.4T){t.1c.2U=f;V.5v(t.1c);V.5v(t.1c.21());v(t.1c.58&&t.1c.21().2W){t.39=t.1c.21().2W.14;1T(D e=0;e<t.39;e++){D d=t.42(e);d.6X=e;t.bA(d)}}13{t.39=0}t.8J();t.1S=0;v(t.39==1&&t.C.gN){t.6V();t.1s()}13{t.2E()}}},bA:u(b){Z.1V(b,"bB",t.3t.2n(t));Z.1V(b,"bC",t.bw.2n(t))},bu:u(){t.5Q=M;t.70=17;v(t.71().14>=t.C.8F){t.6W()}13{t.1Y=M;t.1s()}t.5R=t.E.1k},71:u(){D b=t.6Z();G t.E.1k.8b(b[0],b[1]).5b()},6Z:u(){v(17!=t.70){G t.70}D o=t.E.1k;v(o.5b().gO()){G[-1,0]}D n=12.8B.bD(o,t.5R);D l=(n==t.5R.14?1:0);D p=-1,q=o.14;D m;1T(D j=0,k=t.C.2N.14;j<k;++j){m=o.gP(t.C.2N[j],n+l-1);v(m>p){p=m}m=o.2g(t.C.2N[j],n+l);v(-1!=m&&m<q){q=m}}G(t.70=[p+1,q])}});41.2a.4c.6Z.bD=u(h,f){D g=1q.5S(h.14,f.14);1T(D e=0;e<g;++e){v(h[e]!=f[e]){G e}}G g};1H.41=1D.1z(41.2a,{1G:u(h,g,e,f){t.8C(h,g,f);t.C.gQ=Y;t.C.2O=t.2O.1g(t);t.C.8M=t.C.3z||17;t.3b=e},6W:u(){t.bp();D b=38(t.C.4U)+"="+38(t.71());t.C.3z=t.C.4W?t.C.4W(t.E,b):b;v(t.C.8M){t.C.3z+="&"+t.C.8M}X 1H.5T(t.3b,t.C)},2O:u(b){t.8L(b.5U)}});41.gR=1D.1z(41.2a,{1G:u(e,g,h,f){t.8C(e,g,f);t.C.8N=h},6W:u(){t.8L(t.C.bE(t))},8D:u(b){t.C=N.11({8O:10,bF:Y,bG:2,8P:Y,bH:M,bE:u(a){D p=[];D q=[];D l=a.71();D m=0;1T(D o=0;o<a.C.8N.14&&p.14<a.C.8O;o++){D n=a.C.8N[o];D k=a.C.8P?n.3f().2g(l.3f()):n.2g(l);4d(k!=-1){v(k==0&&n.14!=l.14){p.1y("<5N><72>"+n.3a(0,l.14)+"</72>"+n.3a(l.14)+"</5N>");1N}13{v(l.14>=a.C.bG&&a.C.bF&&k!=-1){v(a.C.bH||/\\s/.5p(n.3a(k-1,1))){q.1y("<5N>"+n.3a(0,k)+"<72>"+n.3a(k,l.14)+"</72>"+n.3a(k+l.14)+"</5N>");1N}}}k=a.C.8P?n.3f().2g(l.3f(),k+1):n.2g(l,k+1)}}v(q.14){p=p.7F(q.3F(0,a.C.8O-p.14))}G"<8x>"+p.4a("")+"</8x>"}},b||{})}});8Q.bI=u(b){4F(u(){8Q.3s(b)},1)};1H.3c=1D.1z({1G:u(f,d,e){t.3b=d;t.E=f=$(f);t.bJ();t.1B={};12.8B.bK(e);N.11(t.C,e||{});v(!t.C.4X&&t.E.18){t.C.4X=t.E.18+"-73";v($(t.C.4X)){t.C.4X=""}}v(t.C.2f){t.C.2f=$(t.C.2f)}v(!t.C.2f){t.C.74=M}t.76=t.E.1o("6o-30")||"ay";t.E.gS=t.C.bL;t.77=t.8R.1g(t);t.bM=(t.C.2O||1n.3K).1g(t);t.78=t.bN.1g(t);t.5V=t.8S.1g(t);t.8T=t.8U.1g(t);t.bO()},bP:u(b){v(!t.79||b.gT||b.gU||b.gV){G}v(Z.8r==b.3W){t.8R(b)}13{v(Z.8K==b.3W){t.8S(b)}}},8V:u(k,o,h){D m=t.C[k+"7a"];D l=t.C[k+"gW"];v("7b"==m){D j=19.1Z("bQ");j.67="gX";j.1k=l;j.48="bR"+k+"gY";v("2Y"==k){j.bS=t.77}t.1F.1L(j);t.1B[k]=j}13{v("7c"==m){D n=19.1Z("a");n.gZ="#";n.1L(19.59(l));n.bS="2Y"==k?t.77:t.5V;n.48="bR"+k+"h0";v(h){n.48+=" "+h}t.1F.1L(n);t.1B[k]=n}}},8W:u(){D f=(t.C.4Y?t.C.8X:t.7d());D d;v(1>=t.C.5W&&!/\\r|\\n/.5p(t.7d())){d=19.1Z("bQ");d.67="7s";D e=t.C.5o||t.C.8Y||0;v(0<e){d.5o=e}}13{d=19.1Z("bT");d.5W=(1>=t.C.5W?t.C.bU:t.C.5W);d.8Y=t.C.8Y||40}d.3g=t.C.4U;d.1k=f;d.48="h1";v(t.C.bV){d.h2=t.5V}t.1B.1P=d;v(t.C.4Y){t.7e()}t.1F.1L(t.1B.1P)},bW:u(){D c=t;u d(b,a){D f=c.C["7s"+b+"h3"];v(!f||a===M){G}c.1F.1L(19.59(f))}t.1F=$(19.1Z("bX"));t.1F.18=t.C.4X;t.1F.2y(t.C.bY);t.1F.h4=t.5V;t.8W();v("bT"==t.1B.1P.2V.3f()){t.1F.1L(19.1Z("br"))}v(t.C.8Z){t.C.8Z(t,t.1F)}d("h5",t.C.5X||t.C.5Y);t.8V("bZ",t.5V);d("h6",t.C.5X&&t.C.5Y);t.8V("2Y",t.77,"h7");d("bl",t.C.5X||t.C.5Y)},4K:u(){v(t.43){t.E.2U=t.43}t.90();t.c0()},c1:u(b){v(t.5Z||t.79){G}t.79=Y;t.4Z("c2");v(t.C.2f){t.C.2f.1s()}t.E.1s();t.bW();t.E.1A.4j(t.1F,t.E);v(!t.C.4Y){t.91()}v(b){Z.1X(b)}},c3:u(b){v(t.C.60){t.E.2y(t.C.60)}v(t.5Z){G}t.4Z("c4")},7d:u(){G t.E.2U.h8()},bN:u(b){t.4Z("3d",b);v(t.43){t.E.2U=t.43;t.43=17}},8R:u(b){t.8U();v(b){Z.1X(b)}},8S:u(j){D e=t.1F;D k=$F(t.1B.1P);t.c5();D h=t.C.4W(e,k)||"";v(N.3k(h)){h=h.h9()}h.7f=t.E.18;v(t.C.c6){D g=N.11({ha:Y},t.C.51);N.11(g,{3z:h,2O:t.8T,3d:t.78});X 1H.hb({hc:t.E},t.3b,g)}13{D g=N.11({4t:"2i"},t.C.51);N.11(g,{3z:h,2O:t.8T,3d:t.78});X 1H.5T(t.3b,g)}v(j){Z.1X(j)}},90:u(){t.E.3q(t.C.92);t.93();t.7g();t.E.O.4u=t.76;t.E.1O();v(t.C.2f){t.C.2f.1O()}t.5Z=M;t.79=M;t.43=17;t.4Z("c7")},7g:u(b){v(t.C.60){t.E.3q(t.C.60)}v(t.5Z){G}t.4Z("c8")},7e:u(){t.1F.2y(t.C.61);t.1B.1P.3e=Y;D b=N.11({4t:"2i"},t.C.51);N.11(b,{3z:"7f="+38(t.E.18),2O:1n.3K,94:u(d){t.1F.3q(t.C.61);D a=d.5U;v(t.C.c9){a=a.95()}t.1B.1P.1k=a;t.1B.1P.3e=M;t.91()}.1g(t),3d:t.78});X 1H.5T(t.C.4Y,b)},91:u(){D b=t.C.ca;v(b){$(t.1B.1P)["5F"==b?"5F":"3s"]()}},bJ:u(){t.C=N.8H(1H.3c.3H);N.11(t.C,1H.3c.cb);[t.cc].2s().hd().1f(u(b){N.11(t.C,b)}.1g(t))},c5:u(){t.5Z=Y;t.93();t.7g();t.cd()},bO:u(){t.96={};D b;$H(1H.3c.ce).1f(u(a){b=t[a.1k].1g(t);t.96[a.4w]=b;v(!t.C.74){t.E.1V(a.4w,b)}v(t.C.2f){t.C.2f.1V(a.4w,b)}}.1g(t))},93:u(){v(!t.1F){G}t.1F.3i();t.1F=17;t.1B={}},cd:u(){t.43=t.E.2U;t.E.2U=t.C.cf;t.E.2y(t.C.92);t.E.O.4u=t.76;t.E.1O()},4Z:u(c,d){v("u"==26 t.C[c]){t.C[c](t,d)}},c0:u(){$H(t.96).1f(u(b){v(!t.C.74){t.E.2K(b.4w,b.1k)}v(t.C.2f){t.C.2f.2K(b.4w,b.1k)}}.1g(t))},8U:u(b){t.90();t.bM(b,t.E)}});N.11(1H.3c.4c,{cg:1H.3c.4c.4K});1H.97=1D.1z(1H.3c,{1G:u($ch,f,d,e){t.cc=1H.97.3H;$ch(f,d,e)},8W:u(){D b=19.1Z("2S");b.3g=t.C.4U;b.5o=1;t.1B.1P=b;t.52=t.C.ci||[];v(t.C.cj){t.ck()}13{t.98()}t.1F.1L(t.1B.1P)},ck:u(){t.1F.2y(t.C.61);t.99(t.C.cl);D C=N.11({4t:"2i"},t.C.51);N.11(C,{3z:"7f="+38(t.E.18),2O:1n.3K,94:u(cn){D 3P=cn.5U.5b();v(!/^\\[.*\\]$/.5p(3P)){2Q("he hf an hg ci hh.")}t.52=hi(3P);t.98()}.1g(t),3d:t.3d});X 1H.5T(t.C.cj,C)},99:u(c){t.1B.1P.3e=Y;D d=t.1B.1P.58;v(!d){d=19.1Z("co");d.1k="";t.1B.1P.1L(d);d.4V=Y}d.1c((c||"").hj().95())},98:u(){t.49=t.7d();v(t.C.4Y){t.7e()}13{t.9a()}},7e:u(){t.99(t.C.8X);D b=N.11({4t:"2i"},t.C.51);N.11(b,{3z:"7f="+38(t.E.18),2O:1n.3K,94:u(a){t.49=a.5U.5b();t.9a()}.1g(t),3d:t.3d});X 1H.5T(t.C.4Y,b)},9a:u(){t.1F.3q(t.C.61);t.52=t.52.1I(u(a){G 2===a.14?a:[a,a].2s()});D d=("1k"3E t.C)?t.C.1k:t.49;D e=t.52.hk(u(a){G a[0]==d}.1g(t));t.1B.1P.1c("");D f;t.52.1f(u(a,b){f=19.1Z("co");f.1k=a[0];f.4V=e?a[0]==d:0==b;f.1L(19.59(a[1]));t.1B.1P.1L(f)}.1g(t));t.1B.1P.3e=M;8Q.bI(t.1B.1P)}});1H.3c.4c.1G.bK=u(d){v(!d){G}u c(b,a){v(b 3E d||a===3C){G}d[b]=a}c("5Y",(d.cp?"7c":(d.cq?"7b":d.cp==d.cq==M?M:3C)));c("5X",(d.cr?"7c":(d.cs?"7b":d.cr==d.cs==M?M:3C)));c("62",d.hl);c("9b",d.hm)};N.11(1H.3c,{3H:{51:{},bU:3,5Y:"7c",hn:"2Y",bL:"ho 1U hp",2f:17,74:M,ca:"3s",bY:"73-bX",4X:17,62:"#ar",9b:"#82",60:"",c6:Y,61:"73-hq",8X:"ct...",5X:"7b",hr:"bZ",4U:"1k",5W:1,92:"73-hs",cf:"ht...",5o:0,c9:M,bV:M,hu:"",hv:"",hw:""},cb:{4W:u(b){G cu.5L(b)},2O:u(c,d){X J.6m(d,{6n:t.C.62,81:Y})},c2:17,c4:u(b){b.E.O.4u=b.C.62;v(b.9c){b.9c.2Y()}},3d:u(c,d){hx("hy hz 5i 9s hA: "+c.5U.95())},8Z:17,c7:17,c8:u(b){b.9c=X J.6m(b.E,{6n:b.C.62,6p:b.C.9b,6q:b.76,81:Y})}},ce:{bC:"c1",bj:"bP",bB:"c3",hB:"7g"}});1H.97.3H={cl:"ct C..."};cu.V.hC=1D.1z({1G:u(d,e,f){t.27=e||0.5;t.E=$(d);t.4W=f;t.63=17;t.4O=$F(t.E);Z.1V(t.E,"hD",t.cv.2n(t))},cv:u(b){v(t.4O==$F(t.E)){G}v(t.63){8g(t.63)}t.63=4F(t.cw.1g(t),t.27*2D);t.4O=$F(t.E)},cw:u(){t.63=17;t.4W(t.E,$F(t.E))}});v(!7a){D 7a={}}7a.hE=1D.1z({1G:u(g,f,e){D h=t;v(N.6z(g)){t.1x=g.7G(u(a){G $(a)})}13{t.1x=[$(g)]}t.1t=$(f);t.C=e||{};t.9d=t.C.9d||"8s";t.cx=t.C.cx||1;t.cy=2r(t.C.cy||"1");t.1Q=t.C.1Q||$R(0,1);t.1k=0;t.1J=t.1x.1I(u(){G 0});t.44=t.C.44?t.C.44.1I(u(a){G $(a)}):M;t.C.7h=$(t.C.7h||17);t.C.7i=$(t.C.7i||17);t.9e=t.C.9e||M;t.7j=t.C.7j||t.1Q.2v;t.9f=t.C.9f||t.1Q.1v;t.7k=2r(t.C.7k||"0");t.7l=2r(t.C.7l||"0");t.9g=t.cz()-t.cA();t.7m=t.3A()?(t.1x[0].3o!=0?t.1x[0].3o:t.1x[0].O.1d.3B(/1w$/,"")):(t.1x[0].3M!=0?t.1x[0].3M:t.1x[0].O.1j.3B(/1w$/,""));t.1Y=M;t.2p=M;t.3e=M;v(t.C.3e){t.cB()}t.2q=t.C.1J?t.C.1J.cC(1n.K):M;v(t.2q){t.9f=t.2q.5S();t.7j=t.2q.4n()}t.3u=t.6K.2n(t);t.4C=t.4D.2n(t);t.4E=t.1c.2n(t);t.1x.1f(u(a,b){b=h.1x.14-1-b;h.64(2Z((N.6z(h.C.9h)?h.C.9h[b]:h.C.9h)||h.1Q.1v),b);a.2j().1V("4J",h.3u)});t.1t.1V("4J",t.3u);19.1V("6F",t.4C);19.1V("6G",t.4E);t.7n=Y},cg:u(){D b=t;Z.2K(t.1t,"4J",t.3u);Z.2K(19,"6F",t.4C);Z.2K(19,"6G",t.4E);t.1x.1f(u(a){Z.2K(a,"4J",b.3u)})},cB:u(){t.3e=Y},hF:u(){t.3e=M},cD:u(e){v(t.2q){v(e>=t.2q.4n()){G(t.2q.4n())}v(e<=t.2q.5S()){G(t.2q.5S())}D f=1q.6H(t.2q[0]-e);D d=t.2q[0];t.2q.1f(u(b){D a=1q.6H(b-e);v(a<=f){d=b;f=a}});G d}v(e>t.1Q.2v){G t.1Q.2v}v(e<t.1Q.1v){G t.1Q.1v}G e},64:u(c,d){v(!t.1Y){t.53=d||0;t.54=t.1x[t.53];t.9i()}d=d||t.53||0;v(t.7n&&t.9e){v((d>0)&&(c<t.1J[d-1])){c=t.1J[d-1]}v((d<(t.1x.14-1))&&(c>t.1J[d+1])){c=t.1J[d+1]}}c=t.cD(c);t.1J[d]=c;t.1k=t.1J[0];t.1x[d].O[t.3A()?"1b":"1e"]=t.55(c);t.cE();v(!t.2p||!t.24){t.9j()}},hG:u(c,d){t.64(t.1J[d||t.53||0]+c,d||t.53||0)},55:u(b){G 1q.2b(((t.9g-t.7m)/(t.1Q.2v-t.1Q.1v))*(b-t.1Q.1v))+"1w"},9k:u(b){G((b/(t.9g-t.7m)*(t.1Q.2v-t.1Q.1v))+t.1Q.1v)},7o:u(c){D d=t.1J.cC(1n.K);c=c||0;G $R(d[c],d[c+1])},cA:u(){G(t.3A()?t.7l:t.7k)},cz:u(){G(t.3A()?(t.1t.3o!=0?t.1t.3o:t.1t.O.1d.3B(/1w$/,""))-t.7l:(t.1t.3M!=0?t.1t.3M:t.1t.O.1j.3B(/1w$/,""))-t.7k)},3A:u(){G(t.9d=="5K")},cE:u(){D b=t;v(t.44){$R(0,t.44.14-1).1f(u(a){b.7p(b.44[a],b.7o(a))})}v(t.C.7h){t.7p(t.C.7h,$R(0,t.1J.14>1?t.7o(0).5S():t.1k))}v(t.C.7i){t.7p(t.C.7i,$R(t.1J.14>1?t.7o(t.44.14-1).4n():t.1k,t.7j))}},7p:u(c,d){v(t.3A()){c.O.1b=t.55(d.1v);c.O.1d=t.55(d.2v-d.1v+t.1Q.1v)}13{c.O.1e=t.55(d.1v);c.O.1j=t.55(d.2v-d.1v+t.1Q.1v)}},9i:u(){t.1x.1f(u(b){V.3q(b,"4V")});V.2y(t.54,"4V")},6K:u(k){v(Z.aY(k)){v(!t.3e){t.1Y=Y;D j=Z.E(k);D h=[Z.4y(k),Z.4z(k)];D g=j;v(g==t.1t){D f=1p.3p(t.1t);t.24=k;t.64(t.9k((t.3A()?h[1]-f[1]:h[0]-f[0])-(t.7m/2)));D f=1p.3p(t.54);t.9l=(h[0]-f[0]);t.9m=(h[1]-f[1])}13{4d((t.1x.2g(j)==-1)&&j.1A){j=j.1A}v(t.1x.2g(j)!=-1){t.54=j;t.53=t.1x.2g(t.54);t.9i();D f=1p.3p(t.54);t.9l=(h[0]-f[0]);t.9m=(h[1]-f[1])}}}Z.1X(k)}},1c:u(b){v(t.1Y){v(!t.2p){t.2p=Y}t.5I(b);v(1n.2h.5e){1M.7K(0,0)}Z.1X(b)}},5I:u(d){D f=[Z.4y(d),Z.4z(d)];D e=1p.3p(t.1t);f[0]-=t.9l+e[0];f[1]-=t.9m+e[1];t.24=d;t.64(t.9k(t.3A()?f[1]:f[0]));v(t.7n&&t.C.cF){t.C.cF(t.1J.14>1?t.1J:t.1k,t)}},4D:u(b){v(t.1Y&&t.2p){t.5J(b,Y);Z.1X(b)}t.1Y=M;t.2p=M},5J:u(d,c){t.1Y=M;t.2p=M;t.9j()},9j:u(){v(t.7n&&t.C.36){t.C.36(t.1J.14>1?t.1J:t.1k,t)}t.24=17}});45={2A:{},7q:Y,9n:X cG(\'<hH O="1d:0" 18="7r#{1t}2P#{18}" 68="#{3b}" 4o="M" cH="Y" 6Q="Y"/>\'),hI:u(){45.7q=Y},hJ:u(){45.7q=M},88:u(c){v(!45.7q){G}D d=N.11({1t:"5g",3b:c,3B:M},12[1]||{});v(d.3B&&t.2A[d.1t]){$R(0,t.2A[d.1t].18).1f(u(a){D b=$("7r"+d.1t+"2P"+a);b.cI&&b.cI();b.3i()});t.2A[d.1t]=17}v(!t.2A[d.1t]){t.2A[d.1t]={18:0}}13{t.2A[d.1t].18++}d.18=t.2A[d.1t].18;$$("3Y")[0].hK(1n.2h.4i?X V("hL",{18:"7r"+d.1t+"2P"+d.18,68:d.3b,4o:1,cH:Y}):45.9n.hM(d))}};v(1n.2h.hN&&9o.hO.2g("hP")>0){v(9o.cJ&&$A(9o.cJ).6B(u(b){G b.3g.2g("hQ")!=-1})){45.9n=X cG(\'<46 18="7r#{1t}2P#{18}" 1j="0" 1d="0" 67="hR/hS" hT="#{3b}"/>\')}13{45.88=u(){}}};',62,1110,'|||||||||||||||||||||||||||||this|function|if|||||||options|var|element||return|||Effect|||false|Object|style|||||||Element||new|true|Event||extend|arguments|else|length|||null|id|document|Sortable|top|update|height|left|each|bind|scroll|setStyle|width|value|Draggables|effects|Prototype|getStyle|Position|Math|afterFinishInternal|hide|track|duration|start|px|handles|push|create|parentNode|_controls|position|Class|case|_form|initialize|Ajax|map|values|handle|appendChild|window|break|show|editor|range|opacity|index|for|to|observe|scrollSpeed|stop|active|createElement||down|sync|from|event|tree|typeof|delay|queue|transition|Base|round|Move|Droppables|last_active|externalControl|indexOf|Browser|get|makePositioned|Scale|restoreAfterFinish|overlap|bindAsEventListener|scrollSensitivity|dragging|allowedValues|parseInt|flatten|100|Transitions|end|dims|bottom|addClassName|originalValue|tracks|delta|pointer|1000|render|Opacity|scaleX|scaleContent|scaleMode|hoverclass|stopObserving|activeDraggable|_lastScrollPointer|tokens|onComplete|_|throw|table|select|toUpperCase|innerHTML|tagName|childNodes|getInlineOpacity|cancel|parseFloat|color|makeClipping|undoClipping||undoPositioned|snap|onChange|_marker|encodeURIComponent|entryCount|substr|url|InPlaceEditor|onFailure|disabled|toLowerCase|name|none|remove|speed|isString|startOn|finishOn|absolute|offsetHeight|cumulativeOffset|removeClassName|match|activate|onHover|eventMouseDown|ghosting|documentElement|format|iefix|parameters|isVertical|replace|undefined|Builder|in|slice|fontSize|DefaultOptions|beforeSetup|finish|emptyFunction|scaleFrom|offsetWidth|opera|targetValue|js|containment|Draggable|reverteffect|endeffect|zindex|scrollTop|keyCode|constraint|body|only||Autocompleter|getEntry|_oldInnerHTML|spans|Sound|object|div|className|_text|join|String|prototype|while|_elementDoesNotExistError|sinoidal|cos|PI|IE|insertBefore|isFunction|add|state|max|loop|reject|Internal|setup|Parallel|method|backgroundColor|include|key|drops|pointerX|pointerY|drags|observers|eventMouseUp|endDrag|eventMouseMove|setTimeout|_lastPointer|quiet|starteffect|mousedown|destroy|tag_name|scrollLeft|observer|lastValue|sortables|tag|findElements|children|hasFocus|paramName|selected|callback|formId|loadTextURL|triggerCallback||ajaxOptions|_collection|activeHandleIdx|activeHandle|translateToPx|script|Methods|firstChild|createTextNode|attribute|strip|parseColor|toColorPart|WebKit|scope|global|interval|with|inspect|getOpacity|originalLeft|originalTop|scaleY|size|test|originalHeight|originalWidth|display|offset|getDimensions|cleanWhitespace|right|getStyles|unit|zIndex|_containers|accept|isParent|deactivate|_timeout|focus|notify|_dragging|draw|finishDrag|vertical|serialize|droppables|li|treeTag|offsetSize|changed|oldElementValue|min|Request|responseText|_boundSubmitHandler|rows|okControl|cancelControl|_saving|hoverClassName|loadingClassName|highlightColor|timer|setValue|Scriptaculous||type|src|_isStringOrNumber|attr|charAt|collectTextNodes|collectTextNodesIgnoreClass|Appear|Date|switch|Queues|set|afterSetup|property|scaleFromCenter|Highlight|startcolor|background|endcolor|restorecolor|clientHeight|direction|center|moveTransition|scaleTransition|opacityTransition|transforms|CSS_PROPERTIES|isArray|deepest|detect|prepare|updateDrag|keyPress|mouseup|mousemove|abs|_opacity|currentDelta|startDrag|_clone|_originallyAbsolute|_getWindowScroll|change|visibility|hidden|scrollInterval|findChildren|container|indicator|selectEntry|getUpdatedChoices|autocompleteIndex|updateElement|getTokenBounds|tokenBounds|getToken|strong|inplaceeditor|externalControlOnly||_originalBackground|_boundCancelHandler|_boundFailureHandler|_editing|Control|button|link|getText|loadExternalText|editorId|leaveHover|startSpan|endSpan|maximum|alignX|alignY|handleLength|initialized|getRange|setSpan|_enabled|sound_|text|REQUIRED_PROTOTYPE|aculo|us|requires|OPTION|node|try|catch|getElementsByTagName|_children|ATTR_MAP|LI|concat|collect|nodeType|nodeValue|hasChildNodes|scrollBy|forceRerendering|effect|linear|zoom|PAIRS|appear|Fade|limit|idle|instances|currentFrame|afterFinish|currentStyle|box|originalStyle|oldStyle|keepBackgroundImage|ffffff|_base|inject|scrollTo|0px|Morph|play|__parseStyleElement|defaultView|substring|isUndefined|treeNode|isAffected|eventKeypress|clearTimeout|_cacheObserverCallbacks|onStart|onEnd|onDrag|revert|_isScrollChild|originalScrollLeft|originalScrollTop|stopScrolling|delete|KEY_ESC|horizontal|lastScrolled|current|clientWidth|draggables|ul|mark|_tree|parent|callee|baseInitialize|setOptions|frequency|minChars|onShow|clone|onHide|stopIndicator|KEY_RETURN|updateChoices|defaultParams|array|choices|ignoreCase|Field|handleFormCancellation|handleFormSubmission|_boundWrapperHandler|wrapUp|createControl|createEditField|loadingText|cols|onFormCustomization|leaveEditMode|postProcessEditField|savingClassName|removeForm|onSuccess|stripTags|_listeners|InPlaceCollectionEditor|checkForExternalText|showLoadingText|buildOptionList|highlightEndColor|_effect|axis|restricted|minimum|trackLength|sliderValue|updateStyles|updateFinished|translateToValue|offsetX|offsetY|template|navigator|Version|javascript|load|the|NODEMAP|AREA|CAPTION|COL|COLGROUP|LEGEND|OPTGROUP|PARAM|TBODY|TD|TFOOT|TH|THEAD|TR|Array|_attributes|class|string|BODY|BUTTON|DIV|INPUT|SELECT|TEXTAREA|split|setContentZoom|removeChild|flicker|full|fps|relative|SlideDown|SlideUp|BlindDown|BlindUp|ScopedQueue|_each|getTime|last|pluck|setInterval|clearInterval|fromToDelta|totalTime|totalFrames|running|beforeFinish|invoke|Tween|hasLayout|setOpacity|mode|scaleTo|elementPositioning|offsetTop|pt||fontSizeType|factor|setDimensions|ffff99|backgroundImage|_delta|absolutize|distance|afterUpdateInternal|parseStyle|transparent|zzzzzz|CSS_LENGTH|camelize|isNaN|addTracks|first|ids|getComputedStyle|including|library|findDeepestChild|isContained|classNames|within|fire|onDrop|reset|register|keypress|unregister|addObserver|removeObserver|Count|_draggable|initDrag|isLeftClick|originalZ|deltaX|deltaY|startScrolling|onDropped|innerWidth|SortableObserver|unmark|SERIALIZE_RULE|_findRootElement|dropOnEmpty|elements|onUpdate|onEmptyHover|findTreeElements|after|dropmarker|_constructIndex|sequence|onBlur|keydown|onKeyPress|After|iframe|_iefix|fixIEOverlapping|startIndicator|KEY_TAB||markPrevious|markNext|onObserverEvent|findElement|onClick|scrollIntoView|getCurrentEntry|afterUpdateElement|addObservers|mouseover|click|getFirstDifferencePos|selector|partialSearch|partialChars|fullSearch|scrollFreeActivate|prepareOptions|dealWithDeprecatedOptions|clickToEditText|_boundComplete|handleAJAXFailure|registerListeners|checkForEscapeOrReturn|input|editor_|onclick|textarea|autoRows|submitOnBlur|createForm|form|formClassName|ok|unregisterListeners|enterEditMode|onEnterEditMode|enterHover|onEnterHover|prepareSubmission|htmlResponse|onLeaveEditMode|onLeaveHover|stripLoadedTextTags|fieldPostCreation|DefaultCallbacks|_extraDefaultOptions|showSaving|Listeners|savingText|dispose|super|collection|loadCollectionURL|loadCollection|loadingCollectionText||transport|option|cancelLink|cancelButton|okLink|okButton|Loading|Form|delayedListener|onTimerEvent|increment|step|maximumOffset|minimumOffset|setDisabled|sortBy|getNearestValue|drawSpans|onSlide|Template|autostart|Stop|plugins|require|write|times|JavaScript|framework|fieldset|instanceof|htmlFor|toString|escapeHTML|gsub|quot|number|build|dump|ABBR|ACRONYM|ADDRESS|APPLET|BASE|BASEFONT|BDO|BIG|BLOCKQUOTE|BR|CENTER|CITE|CODE|DD|DEL|DFN|DIR|DL|DT|EM|FIELDSET|FONT|FORM|FRAME|FRAMESET||H1|H2|H3|H4|H5|H6|HEAD|HR|HTML|IFRAME|IMG|INS|ISINDEX|KBD|LABEL|LINK|MAP|MENU|META|NOFRAMES|NOSCRIPT|OBJECT|OL|PRE|SAMP|SCRIPT|SMALL|SPAN|STRIKE|STRONG|STYLE|SUB|SUP|TABLE|TITLE|TT|UL|VAR|apply|rgb|hasClassName|ElementDoesNotExistError|message|The|specified|DOM|does|not|exist|but|is|required|operate|reverse|random|wobble|pulse|spring|exp||parallel|tagifyText|toArray|span|fromCharCode|160|multiple|slide|blind|toggle||visible|Enumerable|front|findAll|Queue|beforeUpdate|afterUpdate|beforeStart|finished|MoveBy|offsetLeft|font|content|scrollHeight|scrollWidth|image|ScrollTo|viewport|getScrollOffsets|Puff|200|beforeSetupInternal|SwitchOff|DropOut|Shake|Squish|Grow|01|Shrink|beforeStartInternal|Pulsate|pulses|Fold|rgba|toFixed|Transform|keys|backgroundPosition|borderBottomColor|borderBottomStyle|borderBottomWidth|borderLeftColor|borderLeftStyle|borderLeftWidth|borderRightColor|borderRightStyle|borderRightWidth|borderSpacing|borderTopColor|borderTopStyle|borderTopWidth|clip|fontWeight|letterSpacing|lineHeight|marginBottom|marginLeft|marginRight|marginTop|markerOffset|maxHeight|maxWidth|minHeight|minWidth|outlineColor|outlineOffset|outlineWidth|paddingBottom|paddingLeft|paddingRight|paddingTop|textIndent|wordSpacing|mm|pc|morph|visualEffect|dasherize|highlight|fade|grow|shrink|fold|blindUp|blindDown|slideUp|slideDown|pulsate|shake|puff|squish|switchOff|dropOut|addMethods|dragdrop|greedy|sqrt|02|isNumber|outerHTML|childOf|cloneNode|page|relativize|failure|realOffset|innerHeight|Za|z0|before|previousSibling|nextSibling|item|continue|setSequence|Height|Width|controls|setHeight|setAttribute|autocomplete|off|blur|Insertion|filter|progid|DXImageTransform|Microsoft|Alpha|frameborder|scrolling|no|setTop|KEY_LEFT|KEY_RIGHT|KEY_UP|KEY_DOWN|250|informal|autoSelect|empty|lastIndexOf|asynchronous|Local|title|ctrlKey|altKey|shiftKey|Text|submit|_button|href|_link|editor_field|onblur|Controls|onsubmit|Before|Between|editor_cancel|unescapeHTML|toQueryParams|evalScripts|Updater|success|compact|Server|returned|invalid|representation|eval|stripScripts|any|highlightcolor|highlightendcolor|cancelText|Click|edit|loading|okText|saving|Saving|textAfterControls|textBeforeControls|textBetweenControls|alert|Error|communication|server|mouseout|DelayedObserver|keyup|Slider|setEnabled|setValueBy|embed|enable|disable|insert|bgsound|evaluate|Gecko|userAgent|Win|QuickTime|audio|mpeg|data'.split('|'),0,{}))

// script.aculo.us builder.js v1.8.2, Tue Nov 18 18:30:58 +0100 2008

// Copyright (c) 2005-2008 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
//
// script.aculo.us is freely distributable under the terms of an MIT-style license.
// For details, see the script.aculo.us web site: http://script.aculo.us/

var Builder = {
  NODEMAP: {
    AREA: 'map',
    CAPTION: 'table',
    COL: 'table',
    COLGROUP: 'table',
    LEGEND: 'fieldset',
    OPTGROUP: 'select',
    OPTION: 'select',
    PARAM: 'object',
    TBODY: 'table',
    TD: 'table',
    TFOOT: 'table',
    TH: 'table',
    THEAD: 'table',
    TR: 'table'
  },
  // 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)));
      };
    });
  }
};

// script.aculo.us effects.js v1.8.2, Tue Nov 18 18:30:58 +0100 2008

// Copyright (c) 2005-2008 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
// Contributors:
//  Justin Palmer (http://encytemedia.com/)
//  Mark Pilgrim (http://diveintomark.org/)
//  Martin Bialasinki
//
// script.aculo.us is freely distributable under the terms of an MIT-style license.
// For details, see the script.aculo.us web site: http://script.aculo.us/

// converts rgb() and #xxx to #xxxxxx format,
// returns self (or first argument) if not convertable
String.prototype.parseColor = function() {
  var color = '#';
  if (this.slice(0,4) == 'rgb(') {
    var cols = this.slice(4,this.length-1).split(',');
    var i=0; do { color += parseInt(cols[i]).toColorPart() } while (++i<3);
  } else {
    if (this.slice(0,1) == '#') {
      if (this.length==4) for(var i=1;i<4;i++) color += (this.charAt(i) + this.charAt(i)).toLowerCase();
      if (this.length==7) color = this.toLowerCase();
    }
  }
  return (color.length==7 ? color : (arguments[0] || this));
};

/*--------------------------------------------------------------------------*/

Element.collectTextNodes = function(element) {
  return $A($(element).childNodes).collect( function(node) {
    return (node.nodeType==3 ? node.nodeValue :
      (node.hasChildNodes() ? Element.collectTextNodes(node) : ''));
  }).flatten().join('');
};

Element.collectTextNodesIgnoreClass = function(element, className) {
  return $A($(element).childNodes).collect( function(node) {
    return (node.nodeType==3 ? node.nodeValue :
      ((node.hasChildNodes() && !Element.hasClassName(node,className)) ?
        Element.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) { }
};

/*--------------------------------------------------------------------------*/

var Effect = {
  _elementDoesNotExistError: {
    name: 'ElementDoesNotExistError',
    message: 'The specified DOM element does not exist, but is required for this effect to operate'
  },
  Transitions: {
    linear: Prototype.K,
    sinoidal: function(pos) {
      return (-Math.cos(pos*Math.PI)/2) + .5;
    },
    reverse: function(pos) {
      return 1-pos;
    },
    flicker: function(pos) {
      var pos = ((-Math.cos(pos*Math.PI)/4) + .75) + Math.random()/4;
      return pos > 1 ? 1 : pos;
    },
    wobble: function(pos) {
      return (-Math.cos(pos*Math.PI*(9*pos))/2) + .5;
    },
    pulse: function(pos, pulses) {
      return (-Math.cos((pos*((pulses||5)-.5)*2)*Math.PI)/2) + .5;
    },
    spring: function(pos) {
      return 1 - (Math.cos(pos * 4.5 * Math.PI) * Math.exp(-pos * 6));
    },
    none: function(pos) {
      return 0;
    },
    full: function(pos) {
      return 1;
    }
  },
  DefaultOptions: {
    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'
  },
  tagifyText: function(element) {
    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(
            new Element('span', {style: tagifyStyle}).update(
              character == ' ' ? String.fromCharCode(160) : character),
              child);
        });
        Element.remove(child);
      }
    });
  },
  multiple: function(element, effect) {
    var elements;
    if (((typeof element == 'object') ||
        Object.isFunction(element)) &&
       (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);
  }
};

Effect.DefaultOptions.transition = Effect.Transitions.sinoidal;

/* ------------- core effects ------------- */

Effect.ScopedQueue = Class.create(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 = Object.isString(effect.options.queue) ?
      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 (!Object.isString(queueName)) return queueName;

    return this.instances.get(queueName) ||
      this.instances.set(queueName, new Effect.ScopedQueue());
  }
};
Effect.Queue = Effect.Queues.get('global');

Effect.Base = Class.create({
  position: null,
  start: function(options) {
    function codeForEvent(options,eventName){
      return (
        (options[eventName+'Internal'] ? 'this.options.'+eventName+'Internal(this);' : '') +
        (options[eventName] ? 'this.options.'+eventName+'(this);' : '')
      );
    }
    if (options && options.transition === false) options.transition = Effect.Transitions.linear;
    this.options      = Object.extend(Object.extend({ },Effect.DefaultOptions), options || { });
    this.currentFrame = 0;
    this.state        = 'idle';
    this.startOn      = this.options.delay*1000;
    this.finishOn     = this.startOn+(this.options.duration*1000);
    this.fromToDelta  = this.options.to-this.options.from;
    this.totalTime    = this.finishOn-this.startOn;
    this.totalFrames  = this.options.fps*this.options.duration;

    this.render = (function() {
      function dispatch(effect, eventName) {
        if (effect.options[eventName + 'Internal'])
          effect.options[eventName + 'Internal'](effect);
        if (effect.options[eventName])
          effect.options[eventName](effect);
      }

      return function(pos) {
        if (this.state === "idle") {
          this.state = "running";
          dispatch(this, 'beforeSetup');
          if (this.setup) this.setup();
          dispatch(this, 'afterSetup');
        }
        if (this.state === "running") {
          pos = (this.options.transition(pos) * this.fromToDelta) + this.options.from;
          this.position = pos;
          dispatch(this, 'beforeUpdate');
          if (this.update) this.update(pos);
          dispatch(this, 'afterUpdate');
        }
      };
    })();

    this.event('beforeStart');
    if (!this.options.sync)
      Effect.Queues.get(Object.isString(this.options.queue) ?
        'global' : this.options.queue.scope).add(this);
  },
  loop: function(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 = (pos * this.totalFrames).round();
      if (frame > this.currentFrame) {
        this.render(pos);
        this.currentFrame = frame;
      }
    }
  },
  cancel: function() {
    if (!this.options.sync)
      Effect.Queues.get(Object.isString(this.options.queue) ?
        'global' : this.options.queue.scope).remove(this);
    this.state = 'finished';
  },
  event: function(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 (!Object.isFunction(this[property])) data.set(property, this[property]);
    return '#<Effect:' + data.inspect() + ',options:' + $H(this.options).inspect() + '>';
  }
});

Effect.Parallel = Class.create(Effect.Base, {
  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.Tween = Class.create(Effect.Base, {
  initialize: function(object, from, to) {
    object = Object.isString(object) ? $(object) : object;
    var args = $A(arguments), method = args.last(),
      options = args.length == 5 ? args[3] : null;
    this.method = Object.isFunction(method) ? method.bind(object) :
      Object.isFunction(object[method]) ? object[method].bind(object) :
      function(value) { object[method] = value };
    this.start(Object.extend({ from: from, to: to }, options || { }));
  },
  update: function(position) {
    this.method(position);
  }
});

Effect.Event = Class.create(Effect.Base, {
  initialize: function() {
    this.start(Object.extend({ duration: 0 }, arguments[0] || { }));
  },
  update: Prototype.emptyFunction
});

Effect.Opacity = Class.create(Effect.Base, {
  initialize: function(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(Effect.Base, {
  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() {
    this.element.makePositioned();
    this.originalLeft = parseFloat(this.element.getStyle('left') || '0');
    this.originalTop  = parseFloat(this.element.getStyle('top')  || '0');
    if (this.options.mode == 'absolute') {
      this.options.x = this.options.x - this.originalLeft;
      this.options.y = this.options.y - this.originalTop;
    }
  },
  update: function(position) {
    this.element.setStyle({
      left: (this.options.x  * position + this.originalLeft).round() + 'px',
      top:  (this.options.y  * position + this.originalTop).round()  + '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(Effect.Base, {
  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 = width.round() + 'px';
    if (this.options.scaleY) d.height = height.round() + '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(Effect.Base, {
  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+((this._base[i]+(this._delta[i]*position)).round().toColorPart()); }.bind(this)) });
  },
  finish: function() {
    this.element.setStyle(Object.extend(this.oldStyle, {
      backgroundColor: this.options.restorecolor
    }));
  }
});

Effect.ScrollTo = function(element) {
  var options = arguments[1] || { },
  scrollOffsets = document.viewport.getScrollOffsets(),
  elementOffsets = $(element).cumulativeOffset();

  if (options.offset) elementOffsets[1] += options.offset;

  return new Effect.Tween(null,
    scrollOffsets.top,
    elementOffsets[1],
    options,
    function(p){ scrollTo(scrollOffsets.left, p.round()); }
  );
};

/* ------------- 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 options = Object.extend({
    distance: 20,
    duration: 0.5
  }, arguments[1] || {});
  var distance = parseFloat(options.distance);
  var split = parseFloat(options.duration) / 10.0;
  var oldStyle = {
    top: element.getStyle('top'),
    left: element.getStyle('left') };
    return new Effect.Move(element,
      { x:  distance, y: 0, duration: split, afterFinishInternal: function(effect) {
    new Effect.Move(effect.element,
      { x: -distance*2, y: 0, duration: split*2,  afterFinishInternal: function(effect) {
    new Effect.Move(effect.element,
      { x:  distance*2, y: 0, duration: split*2,  afterFinishInternal: function(effect) {
    new Effect.Move(effect.element,
      { x: -distance*2, y: 0, duration: split*2,  afterFinishInternal: function(effect) {
    new Effect.Move(effect.element,
      { x:  distance*2, y: 0, duration: split*2,  afterFinishInternal: function(effect) {
    new Effect.Move(effect.element,
      { x: -distance, y: 0, duration: split, 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');
  var elementDimensions = element.getDimensions();
  return new Effect.Scale(element, window.opera ? 0 : 1,
   Object.extend({ scaleContent: false,
    scaleX: false,
    scaleMode: 'box',
    scaleFrom: 100,
    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().show();
    },
    afterUpdateInternal: function(effect) {
      effect.element.down().setStyle({bottom:
        (effect.dims[0] - effect.element.clientHeight) + 'px' });
    },
    afterFinishInternal: function(effect) {
      effect.element.hide().undoClipping().undoPositioned();
      effect.element.down().undoPositioned().setStyle({bottom: oldInnerBottom});
    }
   }, arguments[1] || { })
  );
};

// Bug in opera makes the TD containing this element expand for a instance after finish
Effect.Squish = function(element) {
  return new Effect.Scale(element, window.opera ? 1 : 0, {
    restoreAfterFinish: true,
    beforeSetup: function(effect) {
      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] || { },
    oldOpacity = element.getInlineOpacity(),
    transition = options.transition || Effect.Transitions.linear,
    reverser   = function(pos){
      return 1 - transition((-Math.cos((pos*(options.pulses||5)*2)*Math.PI)/2) + .5);
    };

  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(Effect.Base, {
  initialize: function(element) {
    this.element = $(element);
    if (!this.element) throw(Effect._elementDoesNotExistError);
    var options = Object.extend({
      style: { }
    }, arguments[1] || { });

    if (!Object.isString(options.style)) this.style = $H(options.style);
    else {
      if (options.style.include(':'))
        this.style = options.style.parseStyle();
      else {
        this.element.addClassName(options.style);
        this.style = $H(this.element.getStyles());
        this.element.removeClassName(options.style);
        var css = this.element.getStyles();
        this.style = this.style.reject(function(style) {
          return style.value == css[style.key];
        });
        options.afterFinishInternal = function(effect) {
          effect.element.addClassName(effect.options.style);
          effect.transforms.each(function(transform) {
            effect.element.style[transform.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 +
          (transform.targetValue - transform.originalValue) * position).toFixed(3) +
            (transform.unit === null ? '' : transform.unit);
    this.element.setStyle(style, true);
  }
});

Effect.Transform = Class.create({
  initialize: function(tracks){
    this.tracks  = [];
    this.options = arguments[1] || { };
    this.addTracks(tracks);
  },
  addTracks: function(tracks){
    tracks.each(function(track){
      track = $H(track);
      var data = track.values().first();
      this.tracks.push($H({
        ids:     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 ids = track.get('ids'), effect = track.get('effect'), options = track.get('options');
        var elements = [$(ids) || $$(ids)].flatten();
        return elements.map(function(e){ return new effect(e, Object.extend({ sync:true }, 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.__parseStyleElement = document.createElement('div');
String.prototype.parseStyle = function(){
  var style, styleRules = $H();
  if (Prototype.Browser.WebKit)
    style = new Element('div',{style:this}).style;
  else {
    String.__parseStyleElement.innerHTML = '<div style="' + this + '"></div>';
    style = String.__parseStyleElement.childNodes[0].style;
  }

  Element.CSS_PROPERTIES.each(function(property){
    if (style[property]) styleRules.set(property, style[property]);
  });

  if (Prototype.Browser.IE && this.include('opacity'))
    styleRules.set('opacity', this.match(/opacity:\s*((?:0|1)?(?:\.\d*)?)/)[1]);

  return styleRules;
};

if (document.defaultView && document.defaultView.getComputedStyle) {
  Element.getStyles = function(element) {
    var css = document.defaultView.getComputedStyle($(element), null);
    return Element.CSS_PROPERTIES.inject({ }, function(styles, property) {
      styles[property] = css[property];
      return styles;
    });
  };
} else {
  Element.getStyles = function(element) {
    element = $(element);
    var css = element.currentStyle, styles;
    styles = Element.CSS_PROPERTIES.inject({ }, function(results, property) {
      results[property] = css[property];
      return results;
    });
    if (!styles.opacity) styles.opacity = element.getOpacity();
    return styles;
  };
}

Effect.Methods = {
  morph: function(element, style) {
    element = $(element);
    new Effect.Morph(element, Object.extend({ style: style }, arguments[2] || { }));
    return element;
  },
  visualEffect: function(element, effect, options) {
    element = $(element);
    var s = effect.dasherize().camelize(), klass = s.charAt(0).toUpperCase() + s.substring(1);
    new Effect[klass](element, options);
    return element;
  },
  highlight: function(element, options) {
    element = $(element);
    new Effect.Highlight(element, options);
    return element;
  }
};

$w('fade appear grow shrink fold blindUp blindDown slideUp slideDown '+
  'pulsate shake puff squish switchOff dropOut').each(
  function(effect) {
    Effect.Methods[effect] = function(element, options){
      element = $(element);
      Effect[effect.charAt(0).toUpperCase() + effect.substring(1)](element, options);
      return element;
    };
  }
);

$w('getInlineOpacity forceRerendering setContentZoom collectTextNodes collectTextNodesIgnoreClass getStyles').each(
  function(f) { Effect.Methods[f] = Element[f]; }
);

Element.addMethods(Effect.Methods);

// script.aculo.us dragdrop.js v1.8.2, Tue Nov 18 18:30:58 +0100 2008

// Copyright (c) 2005-2008 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
//           (c) 2005-2008 Sammi Williams (http://www.oriontransfer.co.nz, sammi@oriontransfer.co.nz)
//
// script.aculo.us is freely distributable under the terms of an MIT-style license.
// For details, see the script.aculo.us web site: http://script.aculo.us/

if(Object.isUndefined(Effect))
  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(Object.isArray(containment)) {
        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 drop, affected = [];

    this.drops.each( function(drop) {
      if(Droppables.isAffected(point, element, drop))
        affected.push(drop);
    });

    if(affected.length>0)
      drop = Droppables.findDeepestChild(affected);

    if(this.last_active && this.last_active != drop) this.deactivate(this.last_active);
    if (drop) {
      Position.within(drop.element, point[0], point[1]);
      if(drop.onHover)
        drop.onHover(element, drop.element, Position.overlap(drop.overlap, drop.element));

      if (drop != this.last_active) 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({
  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 = Object.isNumber(element._opacity) ? 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] || Object.isUndefined(arguments[1].endeffect))
      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 && Object.isString(options.handle))
      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.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(!Object.isUndefined(Draggable._dragging[this.element]) &&
      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.delta)
      this.delta = this.currentDelta();

    if(this.options.zindex) {
      this.originalZ = parseInt(Element.getStyle(this.element,'z-index') || 0);
      this.element.style.zIndex = this.options.zindex;
    }

    if(this.options.ghosting) {
      this._clone = this.element.cloneNode(true);
      this._originallyAbsolute = (this.element.getStyle('position') == 'absolute');
      if (!this._originallyAbsolute)
        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) {
      if (!this._originallyAbsolute)
        Position.relativize(this.element);
      delete this._originallyAbsolute;
      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 && Object.isFunction(revert)) 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(Object.isFunction(this.options.snap)) {
        p = this.options.snap(p[0],p[1],this);
      } else {
      if(Object.isArray(this.options.snap)) {
        p = p.map( function(v, i) {
          return (v/this.options.snap[i]).round()*this.options.snap[i] }.bind(this));
      } else {
        p = p.map( function(v) {
          return (v/this.options.snap).round()*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 };
  }
});

Draggable._dragging = { };

/*--------------------------------------------------------------------------*/

var SortableObserver = Class.create({
  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){
    element = $(element);
    var s = Sortable.sortables[element.id];

    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).select('.' + 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')];
};

// script.aculo.us controls.js v1.8.2, Tue Nov 18 18:30:58 +0100 2008

// Copyright (c) 2005-2008 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
//           (c) 2005-2008 Ivan Krstic (http://blogs.law.harvard.edu/ivan)
//           (c) 2005-2008 Jon Tirsen (http://www.tirsen.com)
// Contributors:
//  Richard Livsey
//  Rahul Bhargava
//  Rob Wills
//
// script.aculo.us is freely distributable under the terms of an MIT-style license.
// For details, see the script.aculo.us web site: http://script.aculo.us/

// Autocompleter.Base handles all the autocompletion functionality
// that's independent of the data source for autocompletion. This
// includes drawing the autocompletion menu, observing keyboard
// and mouse events, and similar.
//
// Specific autocompleters need to provide, at the very least,
// a getUpdatedChoices function that will be invoked every time
// the text inside the monitored textbox changes. This method
// should get the text for which to provide autocompletion by
// invoking this.getToken(), NOT by directly accessing
// this.element.value. This is to allow incremental tokenized
// autocompletion. Specific auto-completion logic (AJAX, etc)
// belongs in getUpdatedChoices.
//
// Tokenized incremental autocompletion is enabled automatically
// when an autocompleter is instantiated with the 'tokens' option
// in the options parameter, e.g.:
// new Ajax.Autocompleter('id','upd', '/url/', { tokens: ',' });
// will incrementally autocomplete with a comma as the token.
// Additionally, ',' in the above example can be replaced with
// a token array, e.g. { tokens: [',', '\n'] } which
// enables autocompletion on multiple tokens. This is most
// useful when one of the tokens is \n (a newline), as it
// allows smart autocompletion after linebreaks.

if(typeof Effect == 'undefined')
  throw("controls.js requires including script.aculo.us' effects.js library");

var Autocompleter = { };
Autocompleter.Base = Class.create({
  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;
    this.oldElementValue = this.element.value;

    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);
    // Force carriage returns as token delimiters anyway
    if (!this.options.tokens.include('\n'))
      this.options.tokens.push('\n');

    this.observer = null;

    this.element.setAttribute('autocomplete','off');

    Element.hide(this.update);

    Event.observe(this.element, 'blur', this.onBlur.bindAsEventListener(this));
    Event.observe(this.element, 'keydown', this.onKeyPress.bindAsEventListener(this));
  },

  show: function() {
    if(Element.getStyle(this.update, 'display')=='none') this.options.onShow(this.element, this.update);
    if(!this.iefix &&
      (Prototype.Browser.IE) &&
      (Element.getStyle(this.update, 'position')=='absolute')) {
      new Insertion.After(this.update,
       '<iframe id="' + this.update.id + '_iefix" '+
       'style="display:none;position:absolute;filter:progid:DXImageTransform.Microsoft.Alpha(opacity=0);" ' +
       'src="javascript:false;" frameborder="0" scrolling="no"></iframe>');
      this.iefix = $(this.update.id+'_iefix');
    }
    if(this.iefix) setTimeout(this.fixIEOverlapping.bind(this), 50);
  },

  fixIEOverlapping: function() {
    Position.clone(this.update, this.iefix, {setTop:(!this.update.style.height)});
    this.iefix.style.zIndex = 1;
    this.update.style.zIndex = 2;
    Element.show(this.iefix);
  },

  hide: function() {
    this.stopIndicator();
    if(Element.getStyle(this.update, 'display')!='none') this.options.onHide(this.element, this.update);
    if(this.iefix) Element.hide(this.iefix);
  },

  startIndicator: function() {
    if(this.options.indicator) Element.show(this.options.indicator);
  },

  stopIndicator: function() {
    if(this.options.indicator) Element.hide(this.options.indicator);
  },

  onKeyPress: function(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();
         Event.stop(event);
         return;
       case Event.KEY_DOWN:
         this.markNext();
         this.render();
         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 = $(selectedElement).select('.' + this.options.select) || [];
      if(nodes.length>0) value = Element.collectTextNodes(nodes[0], this.options.select);
    } else
      value = Element.collectTextNodesIgnoreClass(selectedElement, 'informal');

    var bounds = this.getTokenBounds();
    if (bounds[0] != -1) {
      var newValue = this.element.value.substr(0, bounds[0]);
      var whitespace = this.element.value.substr(bounds[0]).match(/^\s+/);
      if (whitespace)
        newValue += whitespace[0];
      this.element.value = newValue + value + this.element.value.substr(bounds[1]);
    } else {
      this.element.value = value;
    }
    this.oldElementValue = this.element.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;
    this.tokenBounds = null;
    if(this.getToken().length>=this.options.minChars) {
      this.getUpdatedChoices();
    } else {
      this.active = false;
      this.hide();
    }
    this.oldElementValue = this.element.value;
  },

  getToken: function() {
    var bounds = this.getTokenBounds();
    return this.element.value.substring(bounds[0], bounds[1]).strip();
  },

  getTokenBounds: function() {
    if (null != this.tokenBounds) return this.tokenBounds;
    var value = this.element.value;
    if (value.strip().empty()) return [-1, 0];
    var diff = arguments.callee.getFirstDifferencePos(value, this.oldElementValue);
    var offset = (diff == this.oldElementValue.length ? 1 : 0);
    var prevTokenPos = -1, nextTokenPos = value.length;
    var tp;
    for (var index = 0, l = this.options.tokens.length; index < l; ++index) {
      tp = value.lastIndexOf(this.options.tokens[index], diff + offset - 1);
      if (tp > prevTokenPos) prevTokenPos = tp;
      tp = value.indexOf(this.options.tokens[index], diff + offset);
      if (-1 != tp && tp < nextTokenPos) nextTokenPos = tp;
    }
    return (this.tokenBounds = [prevTokenPos + 1, nextTokenPos]);
  }
});

Autocompleter.Base.prototype.getTokenBounds.getFirstDifferencePos = function(newS, oldS) {
  var boundary = Math.min(newS.length, oldS.length);
  for (var index = 0; index < boundary; ++index)
    if (newS[index] != oldS[index])
      return index;
  return boundary;
};

Ajax.Autocompleter = Class.create(Autocompleter.Base, {
  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);
  }
});

// The local array autocompleter. Used when you'd prefer to
// inject an array of autocompletion options into the page, rather
// than sending out Ajax queries, which can be quite slow sometimes.
//
// The constructor takes four parameters. The first two are, as usual,
// the id of the monitored textbox, and id of the autocompletion menu.
// The third is the array you want to autocomplete from, and the fourth
// is the options block.
//
// Extra local autocompletion options:
// - choices - How many autocompletion choices to offer
//
// - partialSearch - If false, the autocompleter will match entered
//                    text only at the beginning of strings in the
//                    autocomplete array. Defaults to true, which will
//                    match text at the beginning of any *word* in the
//                    strings in the autocomplete array. If you want to
//                    search anywhere in the string, additionally set
//                    the option fullSearch to true (default: off).
//
// - fullSsearch - Search anywhere in autocomplete array strings.
//
// - partialChars - How many characters to enter before triggering
//                   a partial match (unlike minChars, which defines
//                   how many characters are required to do any match
//                   at all). Defaults to 2.
//
// - ignoreCase - Whether to ignore case when autocompleting.
//                 Defaults to true.
//
// It's possible to pass in a custom function as the 'selector'
// option, if you prefer to write your own autocompletion logic.
// In that case, the other options above will not apply unless
// you support them.

Autocompleter.Local = Class.create(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 || { });
  }
});

// AJAX in-place editor and collection editor
// Full rewrite by Christophe Porteneuve <tdd@tddsworld.com> (April 2007).

// Use this if you notice weird scrolling problems on some browsers,
// the DOM might be a bit confused when this gets called so do this
// waits 1 ms (with setTimeout) until it does the activation
Field.scrollFreeActivate = function(field) {
  setTimeout(function() {
    Field.activate(field);
  }, 1);
};

Ajax.InPlaceEditor = Class.create({
  initialize: function(element, url, options) {
    this.url = url;
    this.element = element = $(element);
    this.prepareOptions();
    this._controls = { };
    arguments.callee.dealWithDeprecatedOptions(options); // DEPRECATION LAYER!!!
    Object.extend(this.options, options || { });
    if (!this.options.formId && this.element.id) {
      this.options.formId = this.element.id + '-inplaceeditor';
      if ($(this.options.formId))
        this.options.formId = '';
    }
    if (this.options.externalControl)
      this.options.externalControl = $(this.options.externalControl);
    if (!this.options.externalControl)
      this.options.externalControlOnly = false;
    this._originalBackground = this.element.getStyle('background-color') || 'transparent';
    this.element.title = this.options.clickToEditText;
    this._boundCancelHandler = this.handleFormCancellation.bind(this);
    this._boundComplete = (this.options.onComplete || Prototype.emptyFunction).bind(this);
    this._boundFailureHandler = this.handleAJAXFailure.bind(this);
    this._boundSubmitHandler = this.handleFormSubmission.bind(this);
    this._boundWrapperHandler = this.wrapUp.bind(this);
    this.registerListeners();
  },
  checkForEscapeOrReturn: function(e) {
    if (!this._editing || e.ctrlKey || e.altKey || e.shiftKey) return;
    if (Event.KEY_ESC == e.keyCode)
      this.handleFormCancellation(e);
    else if (Event.KEY_RETURN == e.keyCode)
      this.handleFormSubmission(e);
  },
  createControl: function(mode, handler, extraClasses) {
    var control = this.options[mode + 'Control'];
    var text = this.options[mode + 'Text'];
    if ('button' == control) {
      var btn = document.createElement('input');
      btn.type = 'submit';
      btn.value = text;
      btn.className = 'editor_' + mode + '_button';
      if ('cancel' == mode)
        btn.onclick = this._boundCancelHandler;
      this._form.appendChild(btn);
      this._controls[mode] = btn;
    } else if ('link' == control) {
      var link = document.createElement('a');
      link.href = '#';
      link.appendChild(document.createTextNode(text));
      link.onclick = 'cancel' == mode ? this._boundCancelHandler : this._boundSubmitHandler;
      link.className = 'editor_' + mode + '_link';
      if (extraClasses)
        link.className += ' ' + extraClasses;
      this._form.appendChild(link);
      this._controls[mode] = link;
    }
  },
  createEditField: function() {
    var text = (this.options.loadTextURL ? this.options.loadingText : this.getText());
    var fld;
    if (1 >= this.options.rows && !/\r|\n/.test(this.getText())) {
      fld = document.createElement('input');
      fld.type = 'text';
      var size = this.options.size || this.options.cols || 0;
      if (0 < size) fld.size = size;
    } else {
      fld = document.createElement('textarea');
      fld.rows = (1 >= this.options.rows ? this.options.autoRows : this.options.rows);
      fld.cols = this.options.cols || 40;
    }
    fld.name = this.options.paramName;
    fld.value = text; // No HTML breaks conversion anymore
    fld.className = 'editor_field';
    if (this.options.submitOnBlur)
      fld.onblur = this._boundSubmitHandler;
    this._controls.editor = fld;
    if (this.options.loadTextURL)
      this.loadExternalText();
    this._form.appendChild(this._controls.editor);
  },
  createForm: function() {
    var ipe = this;
    function addText(mode, condition) {
      var text = ipe.options['text' + mode + 'Controls'];
      if (!text || condition === false) return;
      ipe._form.appendChild(document.createTextNode(text));
    };
    this._form = $(document.createElement('form'));
    this._form.id = this.options.formId;
    this._form.addClassName(this.options.formClassName);
    this._form.onsubmit = this._boundSubmitHandler;
    this.createEditField();
    if ('textarea' == this._controls.editor.tagName.toLowerCase())
      this._form.appendChild(document.createElement('br'));
    if (this.options.onFormCustomization)
      this.options.onFormCustomization(this, this._form);
    addText('Before', this.options.okControl || this.options.cancelControl);
    this.createControl('ok', this._boundSubmitHandler);
    addText('Between', this.options.okControl && this.options.cancelControl);
    this.createControl('cancel', this._boundCancelHandler, 'editor_cancel');
    addText('After', this.options.okControl || this.options.cancelControl);
  },
  destroy: function() {
    if (this._oldInnerHTML)
      this.element.innerHTML = this._oldInnerHTML;
    this.leaveEditMode();
    this.unregisterListeners();
  },
  enterEditMode: function(e) {
    if (this._saving || this._editing) return;
    this._editing = true;
    this.triggerCallback('onEnterEditMode');
    if (this.options.externalControl)
      this.options.externalControl.hide();
    this.element.hide();
    this.createForm();
    this.element.parentNode.insertBefore(this._form, this.element);
    if (!this.options.loadTextURL)
      this.postProcessEditField();
    if (e) Event.stop(e);
  },
  enterHover: function(e) {
    if (this.options.hoverClassName)
      this.element.addClassName(this.options.hoverClassName);
    if (this._saving) return;
    this.triggerCallback('onEnterHover');
  },
  getText: function() {
    return this.element.innerHTML.unescapeHTML();
  },
  handleAJAXFailure: function(transport) {
    this.triggerCallback('onFailure', transport);
    if (this._oldInnerHTML) {
      this.element.innerHTML = this._oldInnerHTML;
      this._oldInnerHTML = null;
    }
  },
  handleFormCancellation: function(e) {
    this.wrapUp();
    if (e) Event.stop(e);
  },
  handleFormSubmission: function(e) {
    var form = this._form;
    var value = $F(this._controls.editor);
    this.prepareSubmission();
    var params = this.options.callback(form, value) || '';
    if (Object.isString(params))
      params = params.toQueryParams();
    params.editorId = this.element.id;
    if (this.options.htmlResponse) {
      var options = Object.extend({ evalScripts: true }, this.options.ajaxOptions);
      Object.extend(options, {
        parameters: params,
        onComplete: this._boundWrapperHandler,
        onFailure: this._boundFailureHandler
      });
      new Ajax.Updater({ success: this.element }, this.url, options);
    } else {
      var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
      Object.extend(options, {
        parameters: params,
        onComplete: this._boundWrapperHandler,
        onFailure: this._boundFailureHandler
      });
      new Ajax.Request(this.url, options);
    }
    if (e) Event.stop(e);
  },
  leaveEditMode: function() {
    this.element.removeClassName(this.options.savingClassName);
    this.removeForm();
    this.leaveHover();
    this.element.style.backgroundColor = this._originalBackground;
    this.element.show();
    if (this.options.externalControl)
      this.options.externalControl.show();
    this._saving = false;
    this._editing = false;
    this._oldInnerHTML = null;
    this.triggerCallback('onLeaveEditMode');
  },
  leaveHover: function(e) {
    if (this.options.hoverClassName)
      this.element.removeClassName(this.options.hoverClassName);
    if (this._saving) return;
    this.triggerCallback('onLeaveHover');
  },
  loadExternalText: function() {
    this._form.addClassName(this.options.loadingClassName);
    this._controls.editor.disabled = true;
    var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
    Object.extend(options, {
      parameters: 'editorId=' + encodeURIComponent(this.element.id),
      onComplete: Prototype.emptyFunction,
      onSuccess: function(transport) {
        this._form.removeClassName(this.options.loadingClassName);
        var text = transport.responseText;
        if (this.options.stripLoadedTextTags)
          text = text.stripTags();
        this._controls.editor.value = text;
        this._controls.editor.disabled = false;
        this.postProcessEditField();
      }.bind(this),
      onFailure: this._boundFailureHandler
    });
    new Ajax.Request(this.options.loadTextURL, options);
  },
  postProcessEditField: function() {
    var fpc = this.options.fieldPostCreation;
    if (fpc)
      $(this._controls.editor)['focus' == fpc ? 'focus' : 'activate']();
  },
  prepareOptions: function() {
    this.options = Object.clone(Ajax.InPlaceEditor.DefaultOptions);
    Object.extend(this.options, Ajax.InPlaceEditor.DefaultCallbacks);
    [this._extraDefaultOptions].flatten().compact().each(function(defs) {
      Object.extend(this.options, defs);
    }.bind(this));
  },
  prepareSubmission: function() {
    this._saving = true;
    this.removeForm();
    this.leaveHover();
    this.showSaving();
  },
  registerListeners: function() {
    this._listeners = { };
    var listener;
    $H(Ajax.InPlaceEditor.Listeners).each(function(pair) {
      listener = this[pair.value].bind(this);
      this._listeners[pair.key] = listener;
      if (!this.options.externalControlOnly)
        this.element.observe(pair.key, listener);
      if (this.options.externalControl)
        this.options.externalControl.observe(pair.key, listener);
    }.bind(this));
  },
  removeForm: function() {
    if (!this._form) return;
    this._form.remove();
    this._form = null;
    this._controls = { };
  },
  showSaving: function() {
    this._oldInnerHTML = this.element.innerHTML;
    this.element.innerHTML = this.options.savingText;
    this.element.addClassName(this.options.savingClassName);
    this.element.style.backgroundColor = this._originalBackground;
    this.element.show();
  },
  triggerCallback: function(cbName, arg) {
    if ('function' == typeof this.options[cbName]) {
      this.options[cbName](this, arg);
    }
  },
  unregisterListeners: function() {
    $H(this._listeners).each(function(pair) {
      if (!this.options.externalControlOnly)
        this.element.stopObserving(pair.key, pair.value);
      if (this.options.externalControl)
        this.options.externalControl.stopObserving(pair.key, pair.value);
    }.bind(this));
  },
  wrapUp: function(transport) {
    this.leaveEditMode();
    // Can't use triggerCallback due to backward compatibility: requires
    // binding + direct element
    this._boundComplete(transport, this.element);
  }
});

Object.extend(Ajax.InPlaceEditor.prototype, {
  dispose: Ajax.InPlaceEditor.prototype.destroy
});

Ajax.InPlaceCollectionEditor = Class.create(Ajax.InPlaceEditor, {
  initialize: function($super, element, url, options) {
    this._extraDefaultOptions = Ajax.InPlaceCollectionEditor.DefaultOptions;
    $super(element, url, options);
  },

  createEditField: function() {
    var list = document.createElement('select');
    list.name = this.options.paramName;
    list.size = 1;
    this._controls.editor = list;
    this._collection = this.options.collection || [];
    if (this.options.loadCollectionURL)
      this.loadCollection();
    else
      this.checkForExternalText();
    this._form.appendChild(this._controls.editor);
  },

  loadCollection: function() {
    this._form.addClassName(this.options.loadingClassName);
    this.showLoadingText(this.options.loadingCollectionText);
    var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
    Object.extend(options, {
      parameters: 'editorId=' + encodeURIComponent(this.element.id),
      onComplete: Prototype.emptyFunction,
      onSuccess: function(transport) {
        var js = transport.responseText.strip();
        if (!/^\[.*\]$/.test(js)) // TODO: improve sanity check
          throw('Server returned an invalid collection representation.');
        this._collection = eval(js);
        this.checkForExternalText();
      }.bind(this),
      onFailure: this.onFailure
    });
    new Ajax.Request(this.options.loadCollectionURL, options);
  },

  showLoadingText: function(text) {
    this._controls.editor.disabled = true;
    var tempOption = this._controls.editor.firstChild;
    if (!tempOption) {
      tempOption = document.createElement('option');
      tempOption.value = '';
      this._controls.editor.appendChild(tempOption);
      tempOption.selected = true;
    }
    tempOption.update((text || '').stripScripts().stripTags());
  },

  checkForExternalText: function() {
    this._text = this.getText();
    if (this.options.loadTextURL)
      this.loadExternalText();
    else
      this.buildOptionList();
  },

  loadExternalText: function() {
    this.showLoadingText(this.options.loadingText);
    var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
    Object.extend(options, {
      parameters: 'editorId=' + encodeURIComponent(this.element.id),
      onComplete: Prototype.emptyFunction,
      onSuccess: function(transport) {
        this._text = transport.responseText.strip();
        this.buildOptionList();
      }.bind(this),
      onFailure: this.onFailure
    });
    new Ajax.Request(this.options.loadTextURL, options);
  },

  buildOptionList: function() {
    this._form.removeClassName(this.options.loadingClassName);
    this._collection = this._collection.map(function(entry) {
      return 2 === entry.length ? entry : [entry, entry].flatten();
    });
    var marker = ('value' in this.options) ? this.options.value : this._text;
    var textFound = this._collection.any(function(entry) {
      return entry[0] == marker;
    }.bind(this));
    this._controls.editor.update('');
    var option;
    this._collection.each(function(entry, index) {
      option = document.createElement('option');
      option.value = entry[0];
      option.selected = textFound ? entry[0] == marker : 0 == index;
      option.appendChild(document.createTextNode(entry[1]));
      this._controls.editor.appendChild(option);
    }.bind(this));
    this._controls.editor.disabled = false;
    Field.scrollFreeActivate(this._controls.editor);
  }
});

//**** DEPRECATION LAYER FOR InPlace[Collection]Editor! ****
//**** This only  exists for a while,  in order to  let ****
//**** users adapt to  the new API.  Read up on the new ****
//**** API and convert your code to it ASAP!            ****

Ajax.InPlaceEditor.prototype.initialize.dealWithDeprecatedOptions = function(options) {
  if (!options) return;
  function fallback(name, expr) {
    if (name in options || expr === undefined) return;
    options[name] = expr;
  };
  fallback('cancelControl', (options.cancelLink ? 'link' : (options.cancelButton ? 'button' :
    options.cancelLink == options.cancelButton == false ? false : undefined)));
  fallback('okControl', (options.okLink ? 'link' : (options.okButton ? 'button' :
    options.okLink == options.okButton == false ? false : undefined)));
  fallback('highlightColor', options.highlightcolor);
  fallback('highlightEndColor', options.highlightendcolor);
};

Object.extend(Ajax.InPlaceEditor, {
  DefaultOptions: {
    ajaxOptions: { },
    autoRows: 3,                                // Use when multi-line w/ rows == 1
    cancelControl: 'link',                      // 'link'|'button'|false
    cancelText: 'cancel',
    clickToEditText: 'Click to edit',
    externalControl: null,                      // id|elt
    externalControlOnly: false,
    fieldPostCreation: 'activate',              // 'activate'|'focus'|false
    formClassName: 'inplaceeditor-form',
    formId: null,                               // id|elt
    highlightColor: '#ffff99',
    highlightEndColor: '#ffffff',
    hoverClassName: '',
    htmlResponse: true,
    loadingClassName: 'inplaceeditor-loading',
    loadingText: 'Loading...',
    okControl: 'button',                        // 'link'|'button'|false
    okText: 'ok',
    paramName: 'value',
    rows: 1,                                    // If 1 and multi-line, uses autoRows
    savingClassName: 'inplaceeditor-saving',
    savingText: 'Saving...',
    size: 0,
    stripLoadedTextTags: false,
    submitOnBlur: false,
    textAfterControls: '',
    textBeforeControls: '',
    textBetweenControls: ''
  },
  DefaultCallbacks: {
    callback: function(form) {
      return Form.serialize(form);
    },
    onComplete: function(transport, element) {
      // For backward compatibility, this one is bound to the IPE, and passes
      // the element directly.  It was too often customized, so we don't break it.
      new Effect.Highlight(element, {
        startcolor: this.options.highlightColor, keepBackgroundImage: true });
    },
    onEnterEditMode: null,
    onEnterHover: function(ipe) {
      ipe.element.style.backgroundColor = ipe.options.highlightColor;
      if (ipe._effect)
        ipe._effect.cancel();
    },
    onFailure: function(transport, ipe) {
      alert('Error communication with the server: ' + transport.responseText.stripTags());
    },
    onFormCustomization: null, // Takes the IPE and its generated form, after editor, before controls.
    onLeaveEditMode: null,
    onLeaveHover: function(ipe) {
      ipe._effect = new Effect.Highlight(ipe.element, {
        startcolor: ipe.options.highlightColor, endcolor: ipe.options.highlightEndColor,
        restorecolor: ipe._originalBackground, keepBackgroundImage: true
      });
    }
  },
  Listeners: {
    click: 'enterEditMode',
    keydown: 'checkForEscapeOrReturn',
    mouseover: 'enterHover',
    mouseout: 'leaveHover'
  }
});

Ajax.InPlaceCollectionEditor.DefaultOptions = {
  loadingCollectionText: 'Loading options...'
};

// Delayed observer, like Form.Element.Observer,
// but waits for delay after last key input
// Ideal for live-search fields

Form.Element.DelayedObserver = Class.create({
  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));
  }
});

// script.aculo.us slider.js v1.8.2, Tue Nov 18 18:30:58 +0100 2008

// Copyright (c) 2005-2008 Marty Haught, Thomas Fuchs
//
// script.aculo.us is freely distributable under the terms of an MIT-style license.
// For details, see the script.aculo.us web site: http://script.aculo.us/

if (!Control) var Control = { };

// options:
//  axis: 'vertical', or 'horizontal' (default)
//
// callbacks:
//  onChange(value)
//  onSlide(value)
Control.Slider = Class.create({
  initialize: function(handle, track, options) {
    var slider = this;

    if (Object.isArray(handle)) {
      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(
        (Object.isArray(slider.options.sliderValue) ?
          slider.options.sliderValue[i] : slider.options.sliderValue) ||
         slider.range.start), i);
      h.makePositioned().observe("mousedown", slider.eventMouseDown);
    });

    this.track.observe("mousedown", this.eventMouseDown);
    document.observe("mouseup", this.eventMouseUp);
    document.observe("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.alignX);
  },
  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;
  }
});

// script.aculo.us sound.js v1.8.2, Tue Nov 18 18:30:58 +0100 2008

// Copyright (c) 2005-2008 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
//
// Based on code created by Jules Gravinese (http://www.webveteran.com/)
//
// script.aculo.us is freely distributable under the terms of an MIT-style license.
// For details, see the script.aculo.us web site: http://script.aculo.us/

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;
    $$('body')[0].insert(
      Prototype.Browser.IE ? new Element('bgsound',{
        id: 'sound_'+options.track+'_'+options.id,
        src: options.url, loop: 1, autostart: true
      }) : 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(){};
}

