/*
 * jQuery 1.2.6 - New Wave Javascript
 *
 * Copyright (c) 2008 John Resig (jquery.com)
 * Dual licensed under the MIT (MIT-LICENSE.txt)
 * and GPL (GPL-LICENSE.txt) licenses.
 *
 * $Date: 2008-05-24 14:22:17 -0400 (Sat, 24 May 2008) $
 * $Rev: 5685 $
 */
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}('(H(){J w=1b.4M,3m$=1b.$;J D=1b.4M=1b.$=H(a,b){I 2B D.17.5j(a,b)};J u=/^[^<]*(<(.|\\s)+>)[^>]*$|^#(\\w+)$/,62=/^.[^:#\\[\\.]*$/,12;D.17=D.44={5j:H(d,b){d=d||S;G(d.16){7[0]=d;7.K=1;I 7}G(1j d=="23"){J c=u.2D(d);G(c&&(c[1]||!b)){G(c[1])d=D.4h([c[1]],b);N{J a=S.61(c[3]);G(a){G(a.2v!=c[3])I D().2q(d);I D(a)}d=[]}}N I D(b).2q(d)}N G(D.1D(d))I D(S)[D.17.27?"27":"43"](d);I 7.6Y(D.2d(d))},5w:"1.2.6",8G:H(){I 7.K},K:0,3p:H(a){I a==12?D.2d(7):7[a]},2I:H(b){J a=D(b);a.5n=7;I a},6Y:H(a){7.K=0;2p.44.1p.1w(7,a);I 7},P:H(a,b){I D.P(7,a,b)},5i:H(b){J a=-1;I D.2L(b&&b.5w?b[0]:b,7)},1K:H(c,a,b){J d=c;G(c.1q==56)G(a===12)I 7[0]&&D[b||"1K"](7[0],c);N{d={};d[c]=a}I 7.P(H(i){R(c 1n d)D.1K(b?7.V:7,c,D.1i(7,d[c],b,i,c))})},1g:H(b,a){G((b==\'2h\'||b==\'1Z\')&&3d(a)<0)a=12;I 7.1K(b,a,"2a")},1r:H(b){G(1j b!="49"&&b!=U)I 7.4E().3v((7[0]&&7[0].2z||S).5F(b));J a="";D.P(b||7,H(){D.P(7.3t,H(){G(7.16!=8)a+=7.16!=1?7.76:D.17.1r([7])})});I a},5z:H(b){G(7[0])D(b,7[0].2z).5y().39(7[0]).2l(H(){J a=7;1B(a.1x)a=a.1x;I a}).3v(7);I 7},8Y:H(a){I 7.P(H(){D(7).6Q().5z(a)})},8R:H(a){I 7.P(H(){D(7).5z(a)})},3v:H(){I 7.3W(19,M,Q,H(a){G(7.16==1)7.3U(a)})},6F:H(){I 7.3W(19,M,M,H(a){G(7.16==1)7.39(a,7.1x)})},6E:H(){I 7.3W(19,Q,Q,H(a){7.1d.39(a,7)})},5q:H(){I 7.3W(19,Q,M,H(a){7.1d.39(a,7.2H)})},3l:H(){I 7.5n||D([])},2q:H(b){J c=D.2l(7,H(a){I D.2q(b,a)});I 7.2I(/[^+>] [^+>]/.11(b)||b.1h("..")>-1?D.4r(c):c)},5y:H(e){J f=7.2l(H(){G(D.14.1f&&!D.4n(7)){J a=7.6o(M),5h=S.3h("1v");5h.3U(a);I D.4h([5h.4H])[0]}N I 7.6o(M)});J d=f.2q("*").5c().P(H(){G(7[E]!=12)7[E]=U});G(e===M)7.2q("*").5c().P(H(i){G(7.16==3)I;J c=D.L(7,"3w");R(J a 1n c)R(J b 1n c[a])D.W.1e(d[i],a,c[a][b],c[a][b].L)});I f},1E:H(b){I 7.2I(D.1D(b)&&D.3C(7,H(a,i){I b.1k(a,i)})||D.3g(b,7))},4Y:H(b){G(b.1q==56)G(62.11(b))I 7.2I(D.3g(b,7,M));N b=D.3g(b,7);J a=b.K&&b[b.K-1]!==12&&!b.16;I 7.1E(H(){I a?D.2L(7,b)<0:7!=b})},1e:H(a){I 7.2I(D.4r(D.2R(7.3p(),1j a==\'23\'?D(a):D.2d(a))))},3F:H(a){I!!a&&D.3g(a,7).K>0},7T:H(a){I 7.3F("."+a)},6e:H(b){G(b==12){G(7.K){J c=7[0];G(D.Y(c,"2A")){J e=c.64,63=[],15=c.15,2V=c.O=="2A-2V";G(e<0)I U;R(J i=2V?e:0,2f=2V?e+1:15.K;i<2f;i++){J d=15[i];G(d.2W){b=D.14.1f&&!d.at.2x.an?d.1r:d.2x;G(2V)I b;63.1p(b)}}I 63}N I(7[0].2x||"").1o(/\\r/g,"")}I 12}G(b.1q==4L)b+=\'\';I 7.P(H(){G(7.16!=1)I;G(b.1q==2p&&/5O|5L/.11(7.O))7.4J=(D.2L(7.2x,b)>=0||D.2L(7.34,b)>=0);N G(D.Y(7,"2A")){J a=D.2d(b);D("9R",7).P(H(){7.2W=(D.2L(7.2x,a)>=0||D.2L(7.1r,a)>=0)});G(!a.K)7.64=-1}N 7.2x=b})},2K:H(a){I a==12?(7[0]?7[0].4H:U):7.4E().3v(a)},7b:H(a){I 7.5q(a).21()},79:H(i){I 7.3s(i,i+1)},3s:H(){I 7.2I(2p.44.3s.1w(7,19))},2l:H(b){I 7.2I(D.2l(7,H(a,i){I b.1k(a,i,a)}))},5c:H(){I 7.1e(7.5n)},L:H(d,b){J a=d.1R(".");a[1]=a[1]?"."+a[1]:"";G(b===12){J c=7.5C("9z"+a[1]+"!",[a[0]]);G(c===12&&7.K)c=D.L(7[0],d);I c===12&&a[1]?7.L(a[0]):c}N I 7.1P("9u"+a[1]+"!",[a[0],b]).P(H(){D.L(7,d,b)})},3b:H(a){I 7.P(H(){D.3b(7,a)})},3W:H(g,f,h,d){J e=7.K>1,3x;I 7.P(H(){G(!3x){3x=D.4h(g,7.2z);G(h)3x.9o()}J b=7;G(f&&D.Y(7,"1T")&&D.Y(3x[0],"4F"))b=7.3H("22")[0]||7.3U(7.2z.3h("22"));J c=D([]);D.P(3x,H(){J a=e?D(7).5y(M)[0]:7;G(D.Y(a,"1m"))c=c.1e(a);N{G(a.16==1)c=c.1e(D("1m",a).21());d.1k(b,a)}});c.P(6T)})}};D.17.5j.44=D.17;H 6T(i,a){G(a.4d)D.3Y({1a:a.4d,31:Q,1O:"1m"});N D.5u(a.1r||a.6O||a.4H||"");G(a.1d)a.1d.37(a)}H 1z(){I+2B 8J}D.1l=D.17.1l=H(){J b=19[0]||{},i=1,K=19.K,4x=Q,15;G(b.1q==8I){4x=b;b=19[1]||{};i=2}G(1j b!="49"&&1j b!="H")b={};G(K==i){b=7;--i}R(;i<K;i++)G((15=19[i])!=U)R(J c 1n 15){J a=b[c],2w=15[c];G(b===2w)6M;G(4x&&2w&&1j 2w=="49"&&!2w.16)b[c]=D.1l(4x,a||(2w.K!=U?[]:{}),2w);N G(2w!==12)b[c]=2w}I b};J E="4M"+1z(),6K=0,5r={},6G=/z-?5i|8B-?8A|1y|6B|8v-?1Z/i,3P=S.3P||{};D.1l({8u:H(a){1b.$=3m$;G(a)1b.4M=w;I D},1D:H(a){I!!a&&1j a!="23"&&!a.Y&&a.1q!=2p&&/^[\\s[]?H/.11(a+"")},4n:H(a){I a.1C&&!a.1c||a.2j&&a.2z&&!a.2z.1c},5u:H(a){a=D.3k(a);G(a){J b=S.3H("6w")[0]||S.1C,1m=S.3h("1m");1m.O="1r/4t";G(D.14.1f)1m.1r=a;N 1m.3U(S.5F(a));b.39(1m,b.1x);b.37(1m)}},Y:H(b,a){I b.Y&&b.Y.2r()==a.2r()},1Y:{},L:H(c,d,b){c=c==1b?5r:c;J a=c[E];G(!a)a=c[E]=++6K;G(d&&!D.1Y[a])D.1Y[a]={};G(b!==12)D.1Y[a][d]=b;I d?D.1Y[a][d]:a},3b:H(c,b){c=c==1b?5r:c;J a=c[E];G(b){G(D.1Y[a]){2U D.1Y[a][b];b="";R(b 1n D.1Y[a])1X;G(!b)D.3b(c)}}N{1U{2U c[E]}1V(e){G(c.5l)c.5l(E)}2U D.1Y[a]}},P:H(d,a,c){J e,i=0,K=d.K;G(c){G(K==12){R(e 1n d)G(a.1w(d[e],c)===Q)1X}N R(;i<K;)G(a.1w(d[i++],c)===Q)1X}N{G(K==12){R(e 1n d)G(a.1k(d[e],e,d[e])===Q)1X}N R(J b=d[0];i<K&&a.1k(b,i,b)!==Q;b=d[++i]){}}I d},1i:H(b,a,c,i,d){G(D.1D(a))a=a.1k(b,i);I a&&a.1q==4L&&c=="2a"&&!6G.11(d)?a+"2X":a},1F:{1e:H(c,b){D.P((b||"").1R(/\\s+/),H(i,a){G(c.16==1&&!D.1F.3T(c.1F,a))c.1F+=(c.1F?" ":"")+a})},21:H(c,b){G(c.16==1)c.1F=b!=12?D.3C(c.1F.1R(/\\s+/),H(a){I!D.1F.3T(b,a)}).6s(" "):""},3T:H(b,a){I D.2L(a,(b.1F||b).6r().1R(/\\s+/))>-1}},6q:H(b,c,a){J e={};R(J d 1n c){e[d]=b.V[d];b.V[d]=c[d]}a.1k(b);R(J d 1n c)b.V[d]=e[d]},1g:H(d,e,c){G(e=="2h"||e=="1Z"){J b,3X={30:"5x",5g:"1G",18:"3I"},35=e=="2h"?["5e","6k"]:["5G","6i"];H 5b(){b=e=="2h"?d.8f:d.8c;J a=0,2C=0;D.P(35,H(){a+=3d(D.2a(d,"57"+7,M))||0;2C+=3d(D.2a(d,"2C"+7+"4b",M))||0});b-=29.83(a+2C)}G(D(d).3F(":4j"))5b();N D.6q(d,3X,5b);I 29.2f(0,b)}I D.2a(d,e,c)},2a:H(f,l,k){J e,V=f.V;H 3E(b){G(!D.14.2k)I Q;J a=3P.54(b,U);I!a||a.52("3E")==""}G(l=="1y"&&D.14.1f){e=D.1K(V,"1y");I e==""?"1":e}G(D.14.2G&&l=="18"){J d=V.50;V.50="0 7Y 7W";V.50=d}G(l.1I(/4i/i))l=y;G(!k&&V&&V[l])e=V[l];N G(3P.54){G(l.1I(/4i/i))l="4i";l=l.1o(/([A-Z])/g,"-$1").3y();J c=3P.54(f,U);G(c&&!3E(f))e=c.52(l);N{J g=[],2E=[],a=f,i=0;R(;a&&3E(a);a=a.1d)2E.6h(a);R(;i<2E.K;i++)G(3E(2E[i])){g[i]=2E[i].V.18;2E[i].V.18="3I"}e=l=="18"&&g[2E.K-1]!=U?"2F":(c&&c.52(l))||"";R(i=0;i<g.K;i++)G(g[i]!=U)2E[i].V.18=g[i]}G(l=="1y"&&e=="")e="1"}N G(f.4g){J h=l.1o(/\\-(\\w)/g,H(a,b){I b.2r()});e=f.4g[l]||f.4g[h];G(!/^\\d+(2X)?$/i.11(e)&&/^\\d/.11(e)){J j=V.1A,66=f.65.1A;f.65.1A=f.4g.1A;V.1A=e||0;e=V.aM+"2X";V.1A=j;f.65.1A=66}}I e},4h:H(l,h){J k=[];h=h||S;G(1j h.3h==\'12\')h=h.2z||h[0]&&h[0].2z||S;D.P(l,H(i,d){G(!d)I;G(d.1q==4L)d+=\'\';G(1j d=="23"){d=d.1o(/(<(\\w+)[^>]*?)\\/>/g,H(b,a,c){I c.1I(/^(aK|4f|7E|aG|4T|7A|aB|3n|az|ay|av)$/i)?b:a+"></"+c+">"});J f=D.3k(d).3y(),1v=h.3h("1v");J e=!f.1h("<au")&&[1,"<2A 7w=\'7w\'>","</2A>"]||!f.1h("<ar")&&[1,"<7v>","</7v>"]||f.1I(/^<(aq|22|am|ak|ai)/)&&[1,"<1T>","</1T>"]||!f.1h("<4F")&&[2,"<1T><22>","</22></1T>"]||(!f.1h("<af")||!f.1h("<ad"))&&[3,"<1T><22><4F>","</4F></22></1T>"]||!f.1h("<7E")&&[2,"<1T><22></22><7q>","</7q></1T>"]||D.14.1f&&[1,"1v<1v>","</1v>"]||[0,"",""];1v.4H=e[1]+d+e[2];1B(e[0]--)1v=1v.5T;G(D.14.1f){J g=!f.1h("<1T")&&f.1h("<22")<0?1v.1x&&1v.1x.3t:e[1]=="<1T>"&&f.1h("<22")<0?1v.3t:[];R(J j=g.K-1;j>=0;--j)G(D.Y(g[j],"22")&&!g[j].3t.K)g[j].1d.37(g[j]);G(/^\\s/.11(d))1v.39(h.5F(d.1I(/^\\s*/)[0]),1v.1x)}d=D.2d(1v.3t)}G(d.K===0&&(!D.Y(d,"3V")&&!D.Y(d,"2A")))I;G(d[0]==12||D.Y(d,"3V")||d.15)k.1p(d);N k=D.2R(k,d)});I k},1K:H(d,f,c){G(!d||d.16==3||d.16==8)I 12;J e=!D.4n(d),40=c!==12,1f=D.14.1f;f=e&&D.3X[f]||f;G(d.2j){J g=/5Q|4d|V/.11(f);G(f=="2W"&&D.14.2k)d.1d.64;G(f 1n d&&e&&!g){G(40){G(f=="O"&&D.Y(d,"4T")&&d.1d)7p"O a3 a1\'t 9V 9U";d[f]=c}G(D.Y(d,"3V")&&d.7i(f))I d.7i(f).76;I d[f]}G(1f&&e&&f=="V")I D.1K(d.V,"9T",c);G(40)d.9Q(f,""+c);J h=1f&&e&&g?d.4G(f,2):d.4G(f);I h===U?12:h}G(1f&&f=="1y"){G(40){d.6B=1;d.1E=(d.1E||"").1o(/7f\\([^)]*\\)/,"")+(3r(c)+\'\'=="9L"?"":"7f(1y="+c*7a+")")}I d.1E&&d.1E.1h("1y=")>=0?(3d(d.1E.1I(/1y=([^)]*)/)[1])/7a)+\'\':""}f=f.1o(/-([a-z])/9H,H(a,b){I b.2r()});G(40)d[f]=c;I d[f]},3k:H(a){I(a||"").1o(/^\\s+|\\s+$/g,"")},2d:H(b){J a=[];G(b!=U){J i=b.K;G(i==U||b.1R||b.4I||b.1k)a[0]=b;N 1B(i)a[--i]=b[i]}I a},2L:H(b,a){R(J i=0,K=a.K;i<K;i++)G(a[i]===b)I i;I-1},2R:H(a,b){J i=0,T,2S=a.K;G(D.14.1f){1B(T=b[i++])G(T.16!=8)a[2S++]=T}N 1B(T=b[i++])a[2S++]=T;I a},4r:H(a){J c=[],2o={};1U{R(J i=0,K=a.K;i<K;i++){J b=D.L(a[i]);G(!2o[b]){2o[b]=M;c.1p(a[i])}}}1V(e){c=a}I c},3C:H(c,a,d){J b=[];R(J i=0,K=c.K;i<K;i++)G(!d!=!a(c[i],i))b.1p(c[i]);I b},2l:H(d,a){J c=[];R(J i=0,K=d.K;i<K;i++){J b=a(d[i],i);G(b!=U)c[c.K]=b}I c.7d.1w([],c)}});J v=9B.9A.3y();D.14={5B:(v.1I(/.+(?:9y|9x|9w|9v)[\\/: ]([\\d.]+)/)||[])[1],2k:/75/.11(v),2G:/2G/.11(v),1f:/1f/.11(v)&&!/2G/.11(v),42:/42/.11(v)&&!/(9s|75)/.11(v)};J y=D.14.1f?"7o":"72";D.1l({71:!D.14.1f||S.70=="6Z",3X:{"R":"9n","9k":"1F","4i":y,72:y,7o:y,9h:"9f",9e:"9d",9b:"99"}});D.P({6W:H(a){I a.1d},97:H(a){I D.4S(a,"1d")},95:H(a){I D.3a(a,2,"2H")},91:H(a){I D.3a(a,2,"4l")},8Z:H(a){I D.4S(a,"2H")},8X:H(a){I D.4S(a,"4l")},8W:H(a){I D.5v(a.1d.1x,a)},8V:H(a){I D.5v(a.1x)},6Q:H(a){I D.Y(a,"8U")?a.8T||a.8S.S:D.2d(a.3t)}},H(c,d){D.17[c]=H(b){J a=D.2l(7,d);G(b&&1j b=="23")a=D.3g(b,a);I 7.2I(D.4r(a))}});D.P({6P:"3v",8Q:"6F",39:"6E",8P:"5q",8O:"7b"},H(c,b){D.17[c]=H(){J a=19;I 7.P(H(){R(J i=0,K=a.K;i<K;i++)D(a[i])[b](7)})}});D.P({8N:H(a){D.1K(7,a,"");G(7.16==1)7.5l(a)},8M:H(a){D.1F.1e(7,a)},8L:H(a){D.1F.21(7,a)},8K:H(a){D.1F[D.1F.3T(7,a)?"21":"1e"](7,a)},21:H(a){G(!a||D.1E(a,[7]).r.K){D("*",7).1e(7).P(H(){D.W.21(7);D.3b(7)});G(7.1d)7.1d.37(7)}},4E:H(){D(">*",7).21();1B(7.1x)7.37(7.1x)}},H(a,b){D.17[a]=H(){I 7.P(b,19)}});D.P(["6N","4b"],H(i,c){J b=c.3y();D.17[b]=H(a){I 7[0]==1b?D.14.2G&&S.1c["5t"+c]||D.14.2k&&1b["5s"+c]||S.70=="6Z"&&S.1C["5t"+c]||S.1c["5t"+c]:7[0]==S?29.2f(29.2f(S.1c["4y"+c],S.1C["4y"+c]),29.2f(S.1c["2i"+c],S.1C["2i"+c])):a==12?(7.K?D.1g(7[0],b):U):7.1g(b,a.1q==56?a:a+"2X")}});H 25(a,b){I a[0]&&3r(D.2a(a[0],b,M),10)||0}J C=D.14.2k&&3r(D.14.5B)<8H?"(?:[\\\\w*3m-]|\\\\\\\\.)":"(?:[\\\\w\\8F-\\8E*3m-]|\\\\\\\\.)",6L=2B 4v("^>\\\\s*("+C+"+)"),6J=2B 4v("^("+C+"+)(#)("+C+"+)"),6I=2B 4v("^([#.]?)("+C+"*)");D.1l({6H:{"":H(a,i,m){I m[2]=="*"||D.Y(a,m[2])},"#":H(a,i,m){I a.4G("2v")==m[2]},":":{8D:H(a,i,m){I i<m[3]-0},8C:H(a,i,m){I i>m[3]-0},3a:H(a,i,m){I m[3]-0==i},79:H(a,i,m){I m[3]-0==i},3o:H(a,i){I i==0},3S:H(a,i,m,r){I i==r.K-1},6D:H(a,i){I i%2==0},6C:H(a,i){I i%2},"3o-4u":H(a){I a.1d.3H("*")[0]==a},"3S-4u":H(a){I D.3a(a.1d.5T,1,"4l")==a},"8z-4u":H(a){I!D.3a(a.1d.5T,2,"4l")},6W:H(a){I a.1x},4E:H(a){I!a.1x},8y:H(a,i,m){I(a.6O||a.8x||D(a).1r()||"").1h(m[3])>=0},4j:H(a){I"1G"!=a.O&&D.1g(a,"18")!="2F"&&D.1g(a,"5g")!="1G"},1G:H(a){I"1G"==a.O||D.1g(a,"18")=="2F"||D.1g(a,"5g")=="1G"},8w:H(a){I!a.3R},3R:H(a){I a.3R},4J:H(a){I a.4J},2W:H(a){I a.2W||D.1K(a,"2W")},1r:H(a){I"1r"==a.O},5O:H(a){I"5O"==a.O},5L:H(a){I"5L"==a.O},5p:H(a){I"5p"==a.O},3Q:H(a){I"3Q"==a.O},5o:H(a){I"5o"==a.O},6A:H(a){I"6A"==a.O},6z:H(a){I"6z"==a.O},2s:H(a){I"2s"==a.O||D.Y(a,"2s")},4T:H(a){I/4T|2A|6y|2s/i.11(a.Y)},3T:H(a,i,m){I D.2q(m[3],a).K},8t:H(a){I/h\\d/i.11(a.Y)},8s:H(a){I D.3C(D.3O,H(b){I a==b.T}).K}}},6x:[/^(\\[) *@?([\\w-]+) *([!*$^~=]*) *(\'?"?)(.*?)\\4 *\\]/,/^(:)([\\w-]+)\\("?\'?(.*?(\\(.*?\\))?[^(]*?)"?\'?\\)/,2B 4v("^([:.#]*)("+C+"+)")],3g:H(a,c,b){J d,1t=[];1B(a&&a!=d){d=a;J f=D.1E(a,c,b);a=f.t.1o(/^\\s*,\\s*/,"");1t=b?c=f.r:D.2R(1t,f.r)}I 1t},2q:H(t,o){G(1j t!="23")I[t];G(o&&o.16!=1&&o.16!=9)I[];o=o||S;J d=[o],2o=[],3S,Y;1B(t&&3S!=t){J r=[];3S=t;t=D.3k(t);J l=Q,3j=6L,m=3j.2D(t);G(m){Y=m[1].2r();R(J i=0;d[i];i++)R(J c=d[i].1x;c;c=c.2H)G(c.16==1&&(Y=="*"||c.Y.2r()==Y))r.1p(c);d=r;t=t.1o(3j,"");G(t.1h(" ")==0)6M;l=M}N{3j=/^([>+~])\\s*(\\w*)/i;G((m=3j.2D(t))!=U){r=[];J k={};Y=m[2].2r();m=m[1];R(J j=0,3i=d.K;j<3i;j++){J n=m=="~"||m=="+"?d[j].2H:d[j].1x;R(;n;n=n.2H)G(n.16==1){J g=D.L(n);G(m=="~"&&k[g])1X;G(!Y||n.Y.2r()==Y){G(m=="~")k[g]=M;r.1p(n)}G(m=="+")1X}}d=r;t=D.3k(t.1o(3j,""));l=M}}G(t&&!l){G(!t.1h(",")){G(o==d[0])d.4s();2o=D.2R(2o,d);r=d=[o];t=" "+t.6v(1,t.K)}N{J h=6J;J m=h.2D(t);G(m){m=[0,m[2],m[3],m[1]]}N{h=6I;m=h.2D(t)}m[2]=m[2].1o(/\\\\/g,"");J f=d[d.K-1];G(m[1]=="#"&&f&&f.61&&!D.4n(f)){J p=f.61(m[2]);G((D.14.1f||D.14.2G)&&p&&1j p.2v=="23"&&p.2v!=m[2])p=D(\'[@2v="\'+m[2]+\'"]\',f)[0];d=r=p&&(!m[3]||D.Y(p,m[3]))?[p]:[]}N{R(J i=0;d[i];i++){J a=m[1]=="#"&&m[3]?m[3]:m[1]!=""||m[0]==""?"*":m[2];G(a=="*"&&d[i].Y.3y()=="49")a="3n";r=D.2R(r,d[i].3H(a))}G(m[1]==".")r=D.5m(r,m[2]);G(m[1]=="#"){J e=[];R(J i=0;r[i];i++)G(r[i].4G("2v")==m[2]){e=[r[i]];1X}r=e}d=r}t=t.1o(h,"")}}G(t){J b=D.1E(t,r);d=r=b.r;t=D.3k(b.t)}}G(t)d=[];G(d&&o==d[0])d.4s();2o=D.2R(2o,d);I 2o},5m:H(r,m,a){m=" "+m+" ";J c=[];R(J i=0;r[i];i++){J b=(" "+r[i].1F+" ").1h(m)>=0;G(!a&&b||a&&!b)c.1p(r[i])}I c},1E:H(t,r,h){J d;1B(t&&t!=d){d=t;J p=D.6x,m;R(J i=0;p[i];i++){m=p[i].2D(t);G(m){t=t.8r(m[0].K);m[2]=m[2].1o(/\\\\/g,"");1X}}G(!m)1X;G(m[1]==":"&&m[2]=="4Y")r=62.11(m[3])?D.1E(m[3],r,M).r:D(r).4Y(m[3]);N G(m[1]==".")r=D.5m(r,m[2],h);N G(m[1]=="["){J g=[],O=m[3];R(J i=0,3i=r.K;i<3i;i++){J a=r[i],z=a[D.3X[m[2]]||m[2]];G(z==U||/5Q|4d|2W/.11(m[2]))z=D.1K(a,m[2])||\'\';G((O==""&&!!z||O=="="&&z==m[5]||O=="!="&&z!=m[5]||O=="^="&&z&&!z.1h(m[5])||O=="$="&&z.6v(z.K-m[5].K)==m[5]||(O=="*="||O=="~=")&&z.1h(m[5])>=0)^h)g.1p(a)}r=g}N G(m[1]==":"&&m[2]=="3a-4u"){J e={},g=[],11=/(-?)(\\d*)n((?:\\+|-)?\\d*)/.2D(m[3]=="6D"&&"2n"||m[3]=="6C"&&"2n+1"||!/\\D/.11(m[3])&&"8q+"+m[3]||m[3]),3o=(11[1]+(11[2]||1))-0,d=11[3]-0;R(J i=0,3i=r.K;i<3i;i++){J j=r[i],1d=j.1d,2v=D.L(1d);G(!e[2v]){J c=1;R(J n=1d.1x;n;n=n.2H)G(n.16==1)n.4q=c++;e[2v]=M}J b=Q;G(3o==0){G(j.4q==d)b=M}N G((j.4q-d)%3o==0&&(j.4q-d)/3o>=0)b=M;G(b^h)g.1p(j)}r=g}N{J f=D.6H[m[1]];G(1j f=="49")f=f[m[2]];G(1j f=="23")f=6u("Q||H(a,i){I "+f+";}");r=D.3C(r,H(a,i){I f(a,i,m,r)},h)}}I{r:r,t:t}},4S:H(b,c){J a=[],1t=b[c];1B(1t&&1t!=S){G(1t.16==1)a.1p(1t);1t=1t[c]}I a},3a:H(a,e,c,b){e=e||1;J d=0;R(;a;a=a[c])G(a.16==1&&++d==e)1X;I a},5v:H(n,a){J r=[];R(;n;n=n.2H){G(n.16==1&&n!=a)r.1p(n)}I r}});D.W={1e:H(f,i,g,e){G(f.16==3||f.16==8)I;G(D.14.1f&&f.4I)f=1b;G(!g.24)g.24=7.24++;G(e!=12){J h=g;g=7.3M(h,H(){I h.1w(7,19)});g.L=e}J j=D.L(f,"3w")||D.L(f,"3w",{}),1H=D.L(f,"1H")||D.L(f,"1H",H(){G(1j D!="12"&&!D.W.5k)I D.W.1H.1w(19.3L.T,19)});1H.T=f;D.P(i.1R(/\\s+/),H(c,b){J a=b.1R(".");b=a[0];g.O=a[1];J d=j[b];G(!d){d=j[b]={};G(!D.W.2t[b]||D.W.2t[b].4p.1k(f)===Q){G(f.3K)f.3K(b,1H,Q);N G(f.6t)f.6t("4o"+b,1H)}}d[g.24]=g;D.W.26[b]=M});f=U},24:1,26:{},21:H(e,h,f){G(e.16==3||e.16==8)I;J i=D.L(e,"3w"),1L,5i;G(i){G(h==12||(1j h=="23"&&h.8p(0)=="."))R(J g 1n i)7.21(e,g+(h||""));N{G(h.O){f=h.2y;h=h.O}D.P(h.1R(/\\s+/),H(b,a){J c=a.1R(".");a=c[0];G(i[a]){G(f)2U i[a][f.24];N R(f 1n i[a])G(!c[1]||i[a][f].O==c[1])2U i[a][f];R(1L 1n i[a])1X;G(!1L){G(!D.W.2t[a]||D.W.2t[a].4A.1k(e)===Q){G(e.6p)e.6p(a,D.L(e,"1H"),Q);N G(e.6n)e.6n("4o"+a,D.L(e,"1H"))}1L=U;2U i[a]}}})}R(1L 1n i)1X;G(!1L){J d=D.L(e,"1H");G(d)d.T=U;D.3b(e,"3w");D.3b(e,"1H")}}},1P:H(h,c,f,g,i){c=D.2d(c);G(h.1h("!")>=0){h=h.3s(0,-1);J a=M}G(!f){G(7.26[h])D("*").1e([1b,S]).1P(h,c)}N{G(f.16==3||f.16==8)I 12;J b,1L,17=D.1D(f[h]||U),W=!c[0]||!c[0].32;G(W){c.6h({O:h,2J:f,32:H(){},3J:H(){},4C:1z()});c[0][E]=M}c[0].O=h;G(a)c[0].6m=M;J d=D.L(f,"1H");G(d)b=d.1w(f,c);G((!17||(D.Y(f,\'a\')&&h=="4V"))&&f["4o"+h]&&f["4o"+h].1w(f,c)===Q)b=Q;G(W)c.4s();G(i&&D.1D(i)){1L=i.1w(f,b==U?c:c.7d(b));G(1L!==12)b=1L}G(17&&g!==Q&&b!==Q&&!(D.Y(f,\'a\')&&h=="4V")){7.5k=M;1U{f[h]()}1V(e){}}7.5k=Q}I b},1H:H(b){J a,1L,38,5f,4m;b=19[0]=D.W.6l(b||1b.W);38=b.O.1R(".");b.O=38[0];38=38[1];5f=!38&&!b.6m;4m=(D.L(7,"3w")||{})[b.O];R(J j 1n 4m){J c=4m[j];G(5f||c.O==38){b.2y=c;b.L=c.L;1L=c.1w(7,19);G(a!==Q)a=1L;G(1L===Q){b.32();b.3J()}}}I a},6l:H(b){G(b[E]==M)I b;J d=b;b={8o:d};J c="8n 8m 8l 8k 2s 8j 47 5d 6j 5E 8i L 8h 8g 4K 2y 5a 59 8e 8b 58 6f 8a 88 4k 87 86 84 6d 2J 4C 6c O 82 81 35".1R(" ");R(J i=c.K;i;i--)b[c[i]]=d[c[i]];b[E]=M;b.32=H(){G(d.32)d.32();d.80=Q};b.3J=H(){G(d.3J)d.3J();d.7Z=M};b.4C=b.4C||1z();G(!b.2J)b.2J=b.6d||S;G(b.2J.16==3)b.2J=b.2J.1d;G(!b.4k&&b.4K)b.4k=b.4K==b.2J?b.6c:b.4K;G(b.58==U&&b.5d!=U){J a=S.1C,1c=S.1c;b.58=b.5d+(a&&a.2e||1c&&1c.2e||0)-(a.6b||0);b.6f=b.6j+(a&&a.2c||1c&&1c.2c||0)-(a.6a||0)}G(!b.35&&((b.47||b.47===0)?b.47:b.5a))b.35=b.47||b.5a;G(!b.59&&b.5E)b.59=b.5E;G(!b.35&&b.2s)b.35=(b.2s&1?1:(b.2s&2?3:(b.2s&4?2:0)));I b},3M:H(a,b){b.24=a.24=a.24||b.24||7.24++;I b},2t:{27:{4p:H(){55();I},4A:H(){I}},3D:{4p:H(){G(D.14.1f)I Q;D(7).2O("53",D.W.2t.3D.2y);I M},4A:H(){G(D.14.1f)I Q;D(7).4e("53",D.W.2t.3D.2y);I M},2y:H(a){G(F(a,7))I M;a.O="3D";I D.W.1H.1w(7,19)}},3N:{4p:H(){G(D.14.1f)I Q;D(7).2O("51",D.W.2t.3N.2y);I M},4A:H(){G(D.14.1f)I Q;D(7).4e("51",D.W.2t.3N.2y);I M},2y:H(a){G(F(a,7))I M;a.O="3N";I D.W.1H.1w(7,19)}}}};D.17.1l({2O:H(c,a,b){I c=="4X"?7.2V(c,a,b):7.P(H(){D.W.1e(7,c,b||a,b&&a)})},2V:H(d,b,c){J e=D.W.3M(c||b,H(a){D(7).4e(a,e);I(c||b).1w(7,19)});I 7.P(H(){D.W.1e(7,d,e,c&&b)})},4e:H(a,b){I 7.P(H(){D.W.21(7,a,b)})},1P:H(c,a,b){I 7.P(H(){D.W.1P(c,a,7,M,b)})},5C:H(c,a,b){I 7[0]&&D.W.1P(c,a,7[0],Q,b)},2m:H(b){J c=19,i=1;1B(i<c.K)D.W.3M(b,c[i++]);I 7.4V(D.W.3M(b,H(a){7.4Z=(7.4Z||0)%i;a.32();I c[7.4Z++].1w(7,19)||Q}))},7X:H(a,b){I 7.2O(\'3D\',a).2O(\'3N\',b)},27:H(a){55();G(D.2Q)a.1k(S,D);N D.3A.1p(H(){I a.1k(7,D)});I 7}});D.1l({2Q:Q,3A:[],27:H(){G(!D.2Q){D.2Q=M;G(D.3A){D.P(D.3A,H(){7.1k(S)});D.3A=U}D(S).5C("27")}}});J x=Q;H 55(){G(x)I;x=M;G(S.3K&&!D.14.2G)S.3K("69",D.27,Q);G(D.14.1f&&1b==1S)(H(){G(D.2Q)I;1U{S.1C.7V("1A")}1V(3e){3B(19.3L,0);I}D.27()})();G(D.14.2G)S.3K("69",H(){G(D.2Q)I;R(J i=0;i<S.4W.K;i++)G(S.4W[i].3R){3B(19.3L,0);I}D.27()},Q);G(D.14.2k){J a;(H(){G(D.2Q)I;G(S.3f!="68"&&S.3f!="1J"){3B(19.3L,0);I}G(a===12)a=D("V, 7A[7U=7S]").K;G(S.4W.K!=a){3B(19.3L,0);I}D.27()})()}D.W.1e(1b,"43",D.27)}D.P(("7R,7Q,43,85,4y,4X,4V,7P,"+"7O,7N,89,53,51,7M,2A,"+"5o,7L,7K,8d,3e").1R(","),H(i,b){D.17[b]=H(a){I a?7.2O(b,a):7.1P(b)}});J F=H(a,c){J b=a.4k;1B(b&&b!=c)1U{b=b.1d}1V(3e){b=c}I b==c};D(1b).2O("4X",H(){D("*").1e(S).4e()});D.17.1l({67:D.17.43,43:H(g,d,c){G(1j g!=\'23\')I 7.67(g);J e=g.1h(" ");G(e>=0){J i=g.3s(e,g.K);g=g.3s(0,e)}c=c||H(){};J f="2P";G(d)G(D.1D(d)){c=d;d=U}N{d=D.3n(d);f="6g"}J h=7;D.3Y({1a:g,O:f,1O:"2K",L:d,1J:H(a,b){G(b=="1W"||b=="7J")h.2K(i?D("<1v/>").3v(a.4U.1o(/<1m(.|\\s)*?\\/1m>/g,"")).2q(i):a.4U);h.P(c,[a.4U,b,a])}});I 7},aL:H(){I D.3n(7.7I())},7I:H(){I 7.2l(H(){I D.Y(7,"3V")?D.2d(7.aH):7}).1E(H(){I 7.34&&!7.3R&&(7.4J||/2A|6y/i.11(7.Y)||/1r|1G|3Q/i.11(7.O))}).2l(H(i,c){J b=D(7).6e();I b==U?U:b.1q==2p?D.2l(b,H(a,i){I{34:c.34,2x:a}}):{34:c.34,2x:b}}).3p()}});D.P("7H,7G,7F,7D,7C,7B".1R(","),H(i,o){D.17[o]=H(f){I 7.2O(o,f)}});J B=1z();D.1l({3p:H(d,b,a,c){G(D.1D(b)){a=b;b=U}I D.3Y({O:"2P",1a:d,L:b,1W:a,1O:c})},aE:H(b,a){I D.3p(b,U,a,"1m")},aD:H(c,b,a){I D.3p(c,b,a,"3z")},aC:H(d,b,a,c){G(D.1D(b)){a=b;b={}}I D.3Y({O:"6g",1a:d,L:b,1W:a,1O:c})},aA:H(a){D.1l(D.60,a)},60:{1a:5Z.5Q,26:M,O:"2P",2T:0,7z:"4R/x-ax-3V-aw",7x:M,31:M,L:U,5Y:U,3Q:U,4Q:{2N:"4R/2N, 1r/2N",2K:"1r/2K",1m:"1r/4t, 4R/4t",3z:"4R/3z, 1r/4t",1r:"1r/as",4w:"*/*"}},4z:{},3Y:H(s){s=D.1l(M,s,D.1l(M,{},D.60,s));J g,2Z=/=\\?(&|$)/g,1u,L,O=s.O.2r();G(s.L&&s.7x&&1j s.L!="23")s.L=D.3n(s.L);G(s.1O=="4P"){G(O=="2P"){G(!s.1a.1I(2Z))s.1a+=(s.1a.1I(/\\?/)?"&":"?")+(s.4P||"7u")+"=?"}N G(!s.L||!s.L.1I(2Z))s.L=(s.L?s.L+"&":"")+(s.4P||"7u")+"=?";s.1O="3z"}G(s.1O=="3z"&&(s.L&&s.L.1I(2Z)||s.1a.1I(2Z))){g="4P"+B++;G(s.L)s.L=(s.L+"").1o(2Z,"="+g+"$1");s.1a=s.1a.1o(2Z,"="+g+"$1");s.1O="1m";1b[g]=H(a){L=a;1W();1J();1b[g]=12;1U{2U 1b[g]}1V(e){}G(i)i.37(h)}}G(s.1O=="1m"&&s.1Y==U)s.1Y=Q;G(s.1Y===Q&&O=="2P"){J j=1z();J k=s.1a.1o(/(\\?|&)3m=.*?(&|$)/,"$ap="+j+"$2");s.1a=k+((k==s.1a)?(s.1a.1I(/\\?/)?"&":"?")+"3m="+j:"")}G(s.L&&O=="2P"){s.1a+=(s.1a.1I(/\\?/)?"&":"?")+s.L;s.L=U}G(s.26&&!D.4O++)D.W.1P("7H");J n=/^(?:\\w+:)?\\/\\/([^\\/?#]+)/;G(s.1O=="1m"&&O=="2P"&&n.11(s.1a)&&n.2D(s.1a)[1]!=5Z.al){J i=S.3H("6w")[0];J h=S.3h("1m");h.4d=s.1a;G(s.7t)h.aj=s.7t;G(!g){J l=Q;h.ah=h.ag=H(){G(!l&&(!7.3f||7.3f=="68"||7.3f=="1J")){l=M;1W();1J();i.37(h)}}}i.3U(h);I 12}J m=Q;J c=1b.7s?2B 7s("ae.ac"):2B 7r();G(s.5Y)c.6R(O,s.1a,s.31,s.5Y,s.3Q);N c.6R(O,s.1a,s.31);1U{G(s.L)c.4B("ab-aa",s.7z);G(s.5S)c.4B("a9-5R-a8",D.4z[s.1a]||"a7, a6 a5 a4 5N:5N:5N a2");c.4B("X-9Z-9Y","7r");c.4B("9W",s.1O&&s.4Q[s.1O]?s.4Q[s.1O]+", */*":s.4Q.4w)}1V(e){}G(s.7m&&s.7m(c,s)===Q){s.26&&D.4O--;c.7l();I Q}G(s.26)D.W.1P("7B",[c,s]);J d=H(a){G(!m&&c&&(c.3f==4||a=="2T")){m=M;G(f){7k(f);f=U}1u=a=="2T"&&"2T"||!D.7j(c)&&"3e"||s.5S&&D.7h(c,s.1a)&&"7J"||"1W";G(1u=="1W"){1U{L=D.6X(c,s.1O,s.9S)}1V(e){1u="5J"}}G(1u=="1W"){J b;1U{b=c.5I("7g-5R")}1V(e){}G(s.5S&&b)D.4z[s.1a]=b;G(!g)1W()}N D.5H(s,c,1u);1J();G(s.31)c=U}};G(s.31){J f=4I(d,13);G(s.2T>0)3B(H(){G(c){c.7l();G(!m)d("2T")}},s.2T)}1U{c.9P(s.L)}1V(e){D.5H(s,c,U,e)}G(!s.31)d();H 1W(){G(s.1W)s.1W(L,1u);G(s.26)D.W.1P("7C",[c,s])}H 1J(){G(s.1J)s.1J(c,1u);G(s.26)D.W.1P("7F",[c,s]);G(s.26&&!--D.4O)D.W.1P("7G")}I c},5H:H(s,a,b,e){G(s.3e)s.3e(a,b,e);G(s.26)D.W.1P("7D",[a,s,e])},4O:0,7j:H(a){1U{I!a.1u&&5Z.9O=="5p:"||(a.1u>=7e&&a.1u<9N)||a.1u==7c||a.1u==9K||D.14.2k&&a.1u==12}1V(e){}I Q},7h:H(a,c){1U{J b=a.5I("7g-5R");I a.1u==7c||b==D.4z[c]||D.14.2k&&a.1u==12}1V(e){}I Q},6X:H(a,c,b){J d=a.5I("9J-O"),2N=c=="2N"||!c&&d&&d.1h("2N")>=0,L=2N?a.9I:a.4U;G(2N&&L.1C.2j=="5J")7p"5J";G(b)L=b(L,c);G(c=="1m")D.5u(L);G(c=="3z")L=6u("("+L+")");I L},3n:H(a){J s=[];G(a.1q==2p||a.5w)D.P(a,H(){s.1p(3u(7.34)+"="+3u(7.2x))});N R(J j 1n a)G(a[j]&&a[j].1q==2p)D.P(a[j],H(){s.1p(3u(j)+"="+3u(7))});N s.1p(3u(j)+"="+3u(D.1D(a[j])?a[j]():a[j]));I s.6s("&").1o(/%20/g,"+")}});D.17.1l({1N:H(c,b){I c?7.2g({1Z:"1N",2h:"1N",1y:"1N"},c,b):7.1E(":1G").P(H(){7.V.18=7.5D||"";G(D.1g(7,"18")=="2F"){J a=D("<"+7.2j+" />").6P("1c");7.V.18=a.1g("18");G(7.V.18=="2F")7.V.18="3I";a.21()}}).3l()},1M:H(b,a){I b?7.2g({1Z:"1M",2h:"1M",1y:"1M"},b,a):7.1E(":4j").P(H(){7.5D=7.5D||D.1g(7,"18");7.V.18="2F"}).3l()},78:D.17.2m,2m:H(a,b){I D.1D(a)&&D.1D(b)?7.78.1w(7,19):a?7.2g({1Z:"2m",2h:"2m",1y:"2m"},a,b):7.P(H(){D(7)[D(7).3F(":1G")?"1N":"1M"]()})},9G:H(b,a){I 7.2g({1Z:"1N"},b,a)},9F:H(b,a){I 7.2g({1Z:"1M"},b,a)},9E:H(b,a){I 7.2g({1Z:"2m"},b,a)},9D:H(b,a){I 7.2g({1y:"1N"},b,a)},9M:H(b,a){I 7.2g({1y:"1M"},b,a)},9C:H(c,a,b){I 7.2g({1y:a},c,b)},2g:H(k,j,i,g){J h=D.77(j,i,g);I 7[h.36===Q?"P":"36"](H(){G(7.16!=1)I Q;J f=D.1l({},h),p,1G=D(7).3F(":1G"),46=7;R(p 1n k){G(k[p]=="1M"&&1G||k[p]=="1N"&&!1G)I f.1J.1k(7);G(p=="1Z"||p=="2h"){f.18=D.1g(7,"18");f.33=7.V.33}}G(f.33!=U)7.V.33="1G";f.45=D.1l({},k);D.P(k,H(c,a){J e=2B D.28(46,f,c);G(/2m|1N|1M/.11(a))e[a=="2m"?1G?"1N":"1M":a](k);N{J b=a.6r().1I(/^([+-]=)?([\\d+-.]+)(.*)$/),2b=e.1t(M)||0;G(b){J d=3d(b[2]),2M=b[3]||"2X";G(2M!="2X"){46.V[c]=(d||1)+2M;2b=((d||1)/e.1t(M))*2b;46.V[c]=2b+2M}G(b[1])d=((b[1]=="-="?-1:1)*d)+2b;e.3G(2b,d,2M)}N e.3G(2b,a,"")}});I M})},36:H(a,b){G(D.1D(a)||(a&&a.1q==2p)){b=a;a="28"}G(!a||(1j a=="23"&&!b))I A(7[0],a);I 7.P(H(){G(b.1q==2p)A(7,a,b);N{A(7,a).1p(b);G(A(7,a).K==1)b.1k(7)}})},9X:H(b,c){J a=D.3O;G(b)7.36([]);7.P(H(){R(J i=a.K-1;i>=0;i--)G(a[i].T==7){G(c)a[i](M);a.7n(i,1)}});G(!c)7.5A();I 7}});J A=H(b,c,a){G(b){c=c||"28";J q=D.L(b,c+"36");G(!q||a)q=D.L(b,c+"36",D.2d(a))}I q};D.17.5A=H(a){a=a||"28";I 7.P(H(){J q=A(7,a);q.4s();G(q.K)q[0].1k(7)})};D.1l({77:H(b,a,c){J d=b&&b.1q==a0?b:{1J:c||!c&&a||D.1D(b)&&b,2u:b,41:c&&a||a&&a.1q!=9t&&a};d.2u=(d.2u&&d.2u.1q==4L?d.2u:D.28.5K[d.2u])||D.28.5K.74;d.5M=d.1J;d.1J=H(){G(d.36!==Q)D(7).5A();G(D.1D(d.5M))d.5M.1k(7)};I d},41:{73:H(p,n,b,a){I b+a*p},5P:H(p,n,b,a){I((-29.9r(p*29.9q)/2)+0.5)*a+b}},3O:[],48:U,28:H(b,c,a){7.15=c;7.T=b;7.1i=a;G(!c.3Z)c.3Z={}}});D.28.44={4D:H(){G(7.15.2Y)7.15.2Y.1k(7.T,7.1z,7);(D.28.2Y[7.1i]||D.28.2Y.4w)(7);G(7.1i=="1Z"||7.1i=="2h")7.T.V.18="3I"},1t:H(a){G(7.T[7.1i]!=U&&7.T.V[7.1i]==U)I 7.T[7.1i];J r=3d(D.1g(7.T,7.1i,a));I r&&r>-9p?r:3d(D.2a(7.T,7.1i))||0},3G:H(c,b,d){7.5V=1z();7.2b=c;7.3l=b;7.2M=d||7.2M||"2X";7.1z=7.2b;7.2S=7.4N=0;7.4D();J e=7;H t(a){I e.2Y(a)}t.T=7.T;D.3O.1p(t);G(D.48==U){D.48=4I(H(){J a=D.3O;R(J i=0;i<a.K;i++)G(!a[i]())a.7n(i--,1);G(!a.K){7k(D.48);D.48=U}},13)}},1N:H(){7.15.3Z[7.1i]=D.1K(7.T.V,7.1i);7.15.1N=M;7.3G(0,7.1t());G(7.1i=="2h"||7.1i=="1Z")7.T.V[7.1i]="9m";D(7.T).1N()},1M:H(){7.15.3Z[7.1i]=D.1K(7.T.V,7.1i);7.15.1M=M;7.3G(7.1t(),0)},2Y:H(a){J t=1z();G(a||t>7.15.2u+7.5V){7.1z=7.3l;7.2S=7.4N=1;7.4D();7.15.45[7.1i]=M;J b=M;R(J i 1n 7.15.45)G(7.15.45[i]!==M)b=Q;G(b){G(7.15.18!=U){7.T.V.33=7.15.33;7.T.V.18=7.15.18;G(D.1g(7.T,"18")=="2F")7.T.V.18="3I"}G(7.15.1M)7.T.V.18="2F";G(7.15.1M||7.15.1N)R(J p 1n 7.15.45)D.1K(7.T.V,p,7.15.3Z[p])}G(b)7.15.1J.1k(7.T);I Q}N{J n=t-7.5V;7.4N=n/7.15.2u;7.2S=D.41[7.15.41||(D.41.5P?"5P":"73")](7.4N,n,0,1,7.15.2u);7.1z=7.2b+((7.3l-7.2b)*7.2S);7.4D()}I M}};D.1l(D.28,{5K:{9l:9j,9i:7e,74:9g},2Y:{2e:H(a){a.T.2e=a.1z},2c:H(a){a.T.2c=a.1z},1y:H(a){D.1K(a.T.V,"1y",a.1z)},4w:H(a){a.T.V[a.1i]=a.1z+a.2M}}});D.17.2i=H(){J b=0,1S=0,T=7[0],3q;G(T)ao(D.14){J d=T.1d,4a=T,1s=T.1s,1Q=T.2z,5U=2k&&3r(5B)<9c&&!/9a/i.11(v),1g=D.2a,3c=1g(T,"30")=="3c";G(T.7y){J c=T.7y();1e(c.1A+29.2f(1Q.1C.2e,1Q.1c.2e),c.1S+29.2f(1Q.1C.2c,1Q.1c.2c));1e(-1Q.1C.6b,-1Q.1C.6a)}N{1e(T.5X,T.5W);1B(1s){1e(1s.5X,1s.5W);G(42&&!/^t(98|d|h)$/i.11(1s.2j)||2k&&!5U)2C(1s);G(!3c&&1g(1s,"30")=="3c")3c=M;4a=/^1c$/i.11(1s.2j)?4a:1s;1s=1s.1s}1B(d&&d.2j&&!/^1c|2K$/i.11(d.2j)){G(!/^96|1T.*$/i.11(1g(d,"18")))1e(-d.2e,-d.2c);G(42&&1g(d,"33")!="4j")2C(d);d=d.1d}G((5U&&(3c||1g(4a,"30")=="5x"))||(42&&1g(4a,"30")!="5x"))1e(-1Q.1c.5X,-1Q.1c.5W);G(3c)1e(29.2f(1Q.1C.2e,1Q.1c.2e),29.2f(1Q.1C.2c,1Q.1c.2c))}3q={1S:1S,1A:b}}H 2C(a){1e(D.2a(a,"6V",M),D.2a(a,"6U",M))}H 1e(l,t){b+=3r(l,10)||0;1S+=3r(t,10)||0}I 3q};D.17.1l({30:H(){J a=0,1S=0,3q;G(7[0]){J b=7.1s(),2i=7.2i(),4c=/^1c|2K$/i.11(b[0].2j)?{1S:0,1A:0}:b.2i();2i.1S-=25(7,\'94\');2i.1A-=25(7,\'aF\');4c.1S+=25(b,\'6U\');4c.1A+=25(b,\'6V\');3q={1S:2i.1S-4c.1S,1A:2i.1A-4c.1A}}I 3q},1s:H(){J a=7[0].1s;1B(a&&(!/^1c|2K$/i.11(a.2j)&&D.1g(a,\'30\')==\'93\'))a=a.1s;I D(a)}});D.P([\'5e\',\'5G\'],H(i,b){J c=\'4y\'+b;D.17[c]=H(a){G(!7[0])I;I a!=12?7.P(H(){7==1b||7==S?1b.92(!i?a:D(1b).2e(),i?a:D(1b).2c()):7[c]=a}):7[0]==1b||7[0]==S?46[i?\'aI\':\'aJ\']||D.71&&S.1C[c]||S.1c[c]:7[0][c]}});D.P(["6N","4b"],H(i,b){J c=i?"5e":"5G",4f=i?"6k":"6i";D.17["5s"+b]=H(){I 7[b.3y()]()+25(7,"57"+c)+25(7,"57"+4f)};D.17["90"+b]=H(a){I 7["5s"+b]()+25(7,"2C"+c+"4b")+25(7,"2C"+4f+"4b")+(a?25(7,"6S"+c)+25(7,"6S"+4f):0)}})})();',62,669,'|||||||this|||||||||||||||||||||||||||||||||||if|function|return|var|length|data|true|else|type|each|false|for|document|elem|null|style|event||nodeName|||test|undefined||browser|options|nodeType|fn|display|arguments|url|window|body|parentNode|add|msie|css|indexOf|prop|typeof|call|extend|script|in|replace|push|constructor|text|offsetParent|cur|status|div|apply|firstChild|opacity|now|left|while|documentElement|isFunction|filter|className|hidden|handle|match|complete|attr|ret|hide|show|dataType|trigger|doc|split|top|table|try|catch|success|break|cache|height||remove|tbody|string|guid|num|global|ready|fx|Math|curCSS|start|scrollTop|makeArray|scrollLeft|max|animate|width|offset|tagName|safari|map|toggle||done|Array|find|toUpperCase|button|special|duration|id|copy|value|handler|ownerDocument|select|new|border|exec|stack|none|opera|nextSibling|pushStack|target|html|inArray|unit|xml|bind|GET|isReady|merge|pos|timeout|delete|one|selected|px|step|jsre|position|async|preventDefault|overflow|name|which|queue|removeChild|namespace|insertBefore|nth|removeData|fixed|parseFloat|error|readyState|multiFilter|createElement|rl|re|trim|end|_|param|first|get|results|parseInt|slice|childNodes|encodeURIComponent|append|events|elems|toLowerCase|json|readyList|setTimeout|grep|mouseenter|color|is|custom|getElementsByTagName|block|stopPropagation|addEventListener|callee|proxy|mouseleave|timers|defaultView|password|disabled|last|has|appendChild|form|domManip|props|ajax|orig|set|easing|mozilla|load|prototype|curAnim|self|charCode|timerId|object|offsetChild|Width|parentOffset|src|unbind|br|currentStyle|clean|float|visible|relatedTarget|previousSibling|handlers|isXMLDoc|on|setup|nodeIndex|unique|shift|javascript|child|RegExp|_default|deep|scroll|lastModified|teardown|setRequestHeader|timeStamp|update|empty|tr|getAttribute|innerHTML|setInterval|checked|fromElement|Number|jQuery|state|active|jsonp|accepts|application|dir|input|responseText|click|styleSheets|unload|not|lastToggle|outline|mouseout|getPropertyValue|mouseover|getComputedStyle|bindReady|String|padding|pageX|metaKey|keyCode|getWH|andSelf|clientX|Left|all|visibility|container|index|init|triggered|removeAttribute|classFilter|prevObject|submit|file|after|windowData|inner|client|globalEval|sibling|jquery|absolute|clone|wrapAll|dequeue|version|triggerHandler|oldblock|ctrlKey|createTextNode|Top|handleError|getResponseHeader|parsererror|speeds|checkbox|old|00|radio|swing|href|Modified|ifModified|lastChild|safari2|startTime|offsetTop|offsetLeft|username|location|ajaxSettings|getElementById|isSimple|values|selectedIndex|runtimeStyle|rsLeft|_load|loaded|DOMContentLoaded|clientTop|clientLeft|toElement|srcElement|val|pageY|POST|unshift|Bottom|clientY|Right|fix|exclusive|detachEvent|cloneNode|removeEventListener|swap|toString|join|attachEvent|eval|substr|head|parse|textarea|reset|image|zoom|odd|even|before|prepend|exclude|expr|quickClass|quickID|uuid|quickChild|continue|Height|textContent|appendTo|contents|open|margin|evalScript|borderTopWidth|borderLeftWidth|parent|httpData|setArray|CSS1Compat|compatMode|boxModel|cssFloat|linear|def|webkit|nodeValue|speed|_toggle|eq|100|replaceWith|304|concat|200|alpha|Last|httpNotModified|getAttributeNode|httpSuccess|clearInterval|abort|beforeSend|splice|styleFloat|throw|colgroup|XMLHttpRequest|ActiveXObject|scriptCharset|callback|fieldset|multiple|processData|getBoundingClientRect|contentType|link|ajaxSend|ajaxSuccess|ajaxError|col|ajaxComplete|ajaxStop|ajaxStart|serializeArray|notmodified|keypress|keydown|change|mouseup|mousedown|dblclick|focus|blur|stylesheet|hasClass|rel|doScroll|black|hover|solid|cancelBubble|returnValue|wheelDelta|view|round|shiftKey|resize|screenY|screenX|relatedNode|mousemove|prevValue|originalTarget|offsetHeight|keyup|newValue|offsetWidth|eventPhase|detail|currentTarget|cancelable|bubbles|attrName|attrChange|altKey|originalEvent|charAt|0n|substring|animated|header|noConflict|line|enabled|innerText|contains|only|weight|font|gt|lt|uFFFF|u0128|size|417|Boolean|Date|toggleClass|removeClass|addClass|removeAttr|replaceAll|insertAfter|prependTo|wrap|contentWindow|contentDocument|iframe|children|siblings|prevAll|wrapInner|nextAll|outer|prev|scrollTo|static|marginTop|next|inline|parents|able|cellSpacing|adobeair|cellspacing|522|maxLength|maxlength|readOnly|400|readonly|fast|600|class|slow|1px|htmlFor|reverse|10000|PI|cos|compatible|Function|setData|ie|ra|it|rv|getData|userAgent|navigator|fadeTo|fadeIn|slideToggle|slideUp|slideDown|ig|responseXML|content|1223|NaN|fadeOut|300|protocol|send|setAttribute|option|dataFilter|cssText|changed|be|Accept|stop|With|Requested|Object|can|GMT|property|1970|Jan|01|Thu|Since|If|Type|Content|XMLHTTP|th|Microsoft|td|onreadystatechange|onload|cap|charset|colg|host|tfoot|specified|with|1_|thead|leg|plain|attributes|opt|embed|urlencoded|www|area|hr|ajaxSetup|meta|post|getJSON|getScript|marginLeft|img|elements|pageYOffset|pageXOffset|abbr|serialize|pixelLeft'.split('|'),0,{}));
$(document).ready(function() {
    /*
    if ( $('.featured_article').size() > 0 ) {
        var $totalHeight = 0;
        $('.featured_article').each(function(i) {
            console.log($(this).height() );
	        $totalHeight = $totalHeight + $(this).height() + 15;
        });
        $('#featured_container').height($totalHeight + 50 );
        $('#featured').height($totalHeight);
    }
    */
});
/*
 * jQuery UI 1.5.2
 *
 * Copyright (c) 2008 Paul Bakaus (ui.jquery.com)
 * Dual licensed under the MIT (MIT-LICENSE.txt)
 * and GPL (GPL-LICENSE.txt) licenses.
 *
 * http://docs.jquery.com/UI
 */
;(function($) {

$.ui = {
	plugin: {
		add: function(module, option, set) {
			var proto = $.ui[module].prototype;
			for(var i in set) {
				proto.plugins[i] = proto.plugins[i] || [];
				proto.plugins[i].push([option, set[i]]);
			}
		},
		call: function(instance, name, args) {
			var set = instance.plugins[name];
			if(!set) { return; }
			
			for (var i = 0; i < set.length; i++) {
				if (instance.options[set[i][0]]) {
					set[i][1].apply(instance.element, args);
				}
			}
		}	
	},
	cssCache: {},
	css: function(name) {
		if ($.ui.cssCache[name]) { return $.ui.cssCache[name]; }
		var tmp = $('<div class="ui-gen">').addClass(name).css({position:'absolute', top:'-5000px', left:'-5000px', display:'block'}).appendTo('body');
		
		//if (!$.browser.safari)
			//tmp.appendTo('body'); 
		
		//Opera and Safari set width and height to 0px instead of auto
		//Safari returns rgba(0,0,0,0) when bgcolor is not set
		$.ui.cssCache[name] = !!(
			(!(/auto|default/).test(tmp.css('cursor')) || (/^[1-9]/).test(tmp.css('height')) || (/^[1-9]/).test(tmp.css('width')) || 
			!(/none/).test(tmp.css('backgroundImage')) || !(/transparent|rgba\(0, 0, 0, 0\)/).test(tmp.css('backgroundColor')))
		);
		try { $('body').get(0).removeChild(tmp.get(0));	} catch(e){}
		return $.ui.cssCache[name];
	},
	disableSelection: function(el) {
		$(el).attr('unselectable', 'on').css('MozUserSelect', 'none');
	},
	enableSelection: function(el) {
		$(el).attr('unselectable', 'off').css('MozUserSelect', '');
	},
	hasScroll: function(e, a) {
		var scroll = /top/.test(a||"top") ? 'scrollTop' : 'scrollLeft', has = false;
		if (e[scroll] > 0) return true; e[scroll] = 1;
		has = e[scroll] > 0 ? true : false; e[scroll] = 0;
		return has;
	}
};


/** jQuery core modifications and additions **/

var _remove = $.fn.remove;
$.fn.remove = function() {
	$("*", this).add(this).triggerHandler("remove");
	return _remove.apply(this, arguments );
};

// $.widget is a factory to create jQuery plugins
// taking some boilerplate code out of the plugin code
// created by Scott González and Jörn Zaefferer
function getter(namespace, plugin, method) {
	var methods = $[namespace][plugin].getter || [];
	methods = (typeof methods == "string" ? methods.split(/,?\s+/) : methods);
	return ($.inArray(method, methods) != -1);
}

$.widget = function(name, prototype) {
	var namespace = name.split(".")[0];
	name = name.split(".")[1];
	
	// create plugin method
	$.fn[name] = function(options) {
		var isMethodCall = (typeof options == 'string'),
			args = Array.prototype.slice.call(arguments, 1);
		
		if (isMethodCall && getter(namespace, name, options)) {
			var instance = $.data(this[0], name);
			return (instance ? instance[options].apply(instance, args)
				: undefined);
		}
		
		return this.each(function() {
			var instance = $.data(this, name);
			if (isMethodCall && instance && $.isFunction(instance[options])) {
				instance[options].apply(instance, args);
			} else if (!isMethodCall) {
				$.data(this, name, new $[namespace][name](this, options));
			}
		});
	};
	
	// create widget constructor
	$[namespace][name] = function(element, options) {
		var self = this;
		
		this.widgetName = name;
		this.widgetBaseClass = namespace + '-' + name;
		
		this.options = $.extend({}, $.widget.defaults, $[namespace][name].defaults, options);
		this.element = $(element)
			.bind('setData.' + name, function(e, key, value) {
				return self.setData(key, value);
			})
			.bind('getData.' + name, function(e, key) {
				return self.getData(key);
			})
			.bind('remove', function() {
				return self.destroy();
			});
		this.init();
	};
	
	// add widget prototype
	$[namespace][name].prototype = $.extend({}, $.widget.prototype, prototype);
};

$.widget.prototype = {
	init: function() {},
	destroy: function() {
		this.element.removeData(this.widgetName);
	},
	
	getData: function(key) {
		return this.options[key];
	},
	setData: function(key, value) {
		this.options[key] = value;
		
		if (key == 'disabled') {
			this.element[value ? 'addClass' : 'removeClass'](
				this.widgetBaseClass + '-disabled');
		}
	},
	
	enable: function() {
		this.setData('disabled', false);
	},
	disable: function() {
		this.setData('disabled', true);
	}
};

$.widget.defaults = {
	disabled: false
};


/** Mouse Interaction Plugin **/

$.ui.mouse = {
	mouseInit: function() {
		var self = this;
	
		this.element.bind('mousedown.'+this.widgetName, function(e) {
			return self.mouseDown(e);
		});
		
		// Prevent text selection in IE
		if ($.browser.msie) {
			this._mouseUnselectable = this.element.attr('unselectable');
			this.element.attr('unselectable', 'on');
		}
		
		this.started = false;
	},
	
	// TODO: make sure destroying one instance of mouse doesn't mess with
	// other instances of mouse
	mouseDestroy: function() {
		this.element.unbind('.'+this.widgetName);
		
		// Restore text selection in IE
		($.browser.msie
			&& this.element.attr('unselectable', this._mouseUnselectable));
	},
	
	mouseDown: function(e) {
		// we may have missed mouseup (out of window)
		(this._mouseStarted && this.mouseUp(e));
		
		this._mouseDownEvent = e;
		
		var self = this,
			btnIsLeft = (e.which == 1),
			elIsCancel = (typeof this.options.cancel == "string" ? $(e.target).parents().add(e.target).filter(this.options.cancel).length : false);
		if (!btnIsLeft || elIsCancel || !this.mouseCapture(e)) {
			return true;
		}
		
		this._mouseDelayMet = !this.options.delay;
		if (!this._mouseDelayMet) {
			this._mouseDelayTimer = setTimeout(function() {
				self._mouseDelayMet = true;
			}, this.options.delay);
		}
		
		if (this.mouseDistanceMet(e) && this.mouseDelayMet(e)) {
			this._mouseStarted = (this.mouseStart(e) !== false);
			if (!this._mouseStarted) {
				e.preventDefault();
				return true;
			}
		}
		
		// these delegates are required to keep context
		this._mouseMoveDelegate = function(e) {
			return self.mouseMove(e);
		};
		this._mouseUpDelegate = function(e) {
			return self.mouseUp(e);
		};
		$(document)
			.bind('mousemove.'+this.widgetName, this._mouseMoveDelegate)
			.bind('mouseup.'+this.widgetName, this._mouseUpDelegate);
		
		return false;
	},
	
	mouseMove: function(e) {
		// IE mouseup check - mouseup happened when mouse was out of window
		if ($.browser.msie && !e.button) {
			return this.mouseUp(e);
		}
		
		if (this._mouseStarted) {
			this.mouseDrag(e);
			return false;
		}
		
		if (this.mouseDistanceMet(e) && this.mouseDelayMet(e)) {
			this._mouseStarted =
				(this.mouseStart(this._mouseDownEvent, e) !== false);
			(this._mouseStarted ? this.mouseDrag(e) : this.mouseUp(e));
		}
		
		return !this._mouseStarted;
	},
	
	mouseUp: function(e) {
		$(document)
			.unbind('mousemove.'+this.widgetName, this._mouseMoveDelegate)
			.unbind('mouseup.'+this.widgetName, this._mouseUpDelegate);
		
		if (this._mouseStarted) {
			this._mouseStarted = false;
			this.mouseStop(e);
		}
		
		return false;
	},
	
	mouseDistanceMet: function(e) {
		return (Math.max(
				Math.abs(this._mouseDownEvent.pageX - e.pageX),
				Math.abs(this._mouseDownEvent.pageY - e.pageY)
			) >= this.options.distance
		);
	},
	
	mouseDelayMet: function(e) {
		return this._mouseDelayMet;
	},
	
	// These are placeholder methods, to be overriden by extending plugin
	mouseStart: function(e) {},
	mouseDrag: function(e) {},
	mouseStop: function(e) {},
	mouseCapture: function(e) { return true; }
};

$.ui.mouse.defaults = {
	cancel: null,
	distance: 1,
	delay: 0
};

})(jQuery);
/*
 * jQuery UI Draggable
 *
 * Copyright (c) 2008 Paul Bakaus
 * Dual licensed under the MIT (MIT-LICENSE.txt)
 * and GPL (GPL-LICENSE.txt) licenses.
 * 
 * http://docs.jquery.com/UI/Draggables
 *
 * Depends:
 *	ui.core.js
 */
(function($) {

$.widget("ui.draggable", $.extend({}, $.ui.mouse, {
	init: function() {
		
		//Initialize needed constants
		var o = this.options;

		//Position the node
		if (o.helper == 'original' && !(/(relative|absolute|fixed)/).test(this.element.css('position')))
			this.element.css('position', 'relative');

		this.element.addClass('ui-draggable');
		(o.disabled && this.element.addClass('ui-draggable-disabled'));
		
		this.mouseInit();
		
	},
	mouseStart: function(e) {
		var o = this.options;
		
		if (this.helper || o.disabled || $(e.target).is('.ui-resizable-handle')) return false;
		
		var handle = !this.options.handle || !$(this.options.handle, this.element).length ? true : false;
		
	
		$(this.options.handle, this.element).find("*").andSelf().each(function() {
			if(this == e.target) handle = true;
		});
		if (!handle) return false;
		
		if($.ui.ddmanager) $.ui.ddmanager.current = this;
		
		//Create and append the visible helper
		this.helper = $.isFunction(o.helper) ? $(o.helper.apply(this.element[0], [e])) : (o.helper == 'clone' ? this.element.clone() : this.element);
		if(!this.helper.parents('body').length) this.helper.appendTo((o.appendTo == 'parent' ? this.element[0].parentNode : o.appendTo));
		if(this.helper[0] != this.element[0] && !(/(fixed|absolute)/).test(this.helper.css("position"))) this.helper.css("position", "absolute");
		
		/*
		 * - Position generation -
		 * This block generates everything position related - it's the core of draggables.
		 */
		
		this.margins = {																				//Cache the margins
			left: (parseInt(this.element.css("marginLeft"),10) || 0),
			top: (parseInt(this.element.css("marginTop"),10) || 0)
		};		
		
		this.cssPosition = this.helper.css("position");													//Store the helper's css position
		this.offset = this.element.offset();															//The element's absolute position on the page
		this.offset = {																					//Substract the margins from the element's absolute offset
			top: this.offset.top - this.margins.top,
			left: this.offset.left - this.margins.left
		};
		
		this.offset.click = {																			//Where the click happened, relative to the element
			left: e.pageX - this.offset.left,
			top: e.pageY - this.offset.top
		};
		
		this.offsetParent = this.helper.offsetParent(); var po = this.offsetParent.offset();			//Get the offsetParent and cache its position
		if(this.offsetParent[0] == document.body && $.browser.mozilla) po = { top: 0, left: 0 };		//Ugly FF3 fix
		this.offset.parent = {																			//Store its position plus border
			top: po.top + (parseInt(this.offsetParent.css("borderTopWidth"),10) || 0),
			left: po.left + (parseInt(this.offsetParent.css("borderLeftWidth"),10) || 0)
		};
		
		var p = this.element.position();																//This is a relative to absolute position minus the actual position calculation - only used for relative positioned helpers
		this.offset.relative = this.cssPosition == "relative" ? {
			top: p.top - (parseInt(this.helper.css("top"),10) || 0) + this.offsetParent[0].scrollTop,
			left: p.left - (parseInt(this.helper.css("left"),10) || 0) + this.offsetParent[0].scrollLeft
		} : { top: 0, left: 0 };
		
		this.originalPosition = this.generatePosition(e);												//Generate the original position
		this.helperProportions = { width: this.helper.outerWidth(), height: this.helper.outerHeight() };//Cache the helper size
		
		if(o.cursorAt) {
			if(o.cursorAt.left != undefined) this.offset.click.left = o.cursorAt.left + this.margins.left;
			if(o.cursorAt.right != undefined) this.offset.click.left = this.helperProportions.width - o.cursorAt.right + this.margins.left;
			if(o.cursorAt.top != undefined) this.offset.click.top = o.cursorAt.top + this.margins.top;
			if(o.cursorAt.bottom != undefined) this.offset.click.top = this.helperProportions.height - o.cursorAt.bottom + this.margins.top;
		}
		
		
		/*
		 * - Position constraining -
		 * Here we prepare position constraining like grid and containment.
		 */	
		
		if(o.containment) {
			if(o.containment == 'parent') o.containment = this.helper[0].parentNode;
			if(o.containment == 'document' || o.containment == 'window') this.containment = [
				0 - this.offset.relative.left - this.offset.parent.left,
				0 - this.offset.relative.top - this.offset.parent.top,
				$(o.containment == 'document' ? document : window).width() - this.offset.relative.left - this.offset.parent.left - this.helperProportions.width - this.margins.left - (parseInt(this.element.css("marginRight"),10) || 0),
				($(o.containment == 'document' ? document : window).height() || document.body.parentNode.scrollHeight) - this.offset.relative.top - this.offset.parent.top - this.helperProportions.height - this.margins.top - (parseInt(this.element.css("marginBottom"),10) || 0)
			];
			
			if(!(/^(document|window|parent)$/).test(o.containment)) {
				var ce = $(o.containment)[0];
				var co = $(o.containment).offset();
				
				this.containment = [
					co.left + (parseInt($(ce).css("borderLeftWidth"),10) || 0) - this.offset.relative.left - this.offset.parent.left,
					co.top + (parseInt($(ce).css("borderTopWidth"),10) || 0) - this.offset.relative.top - this.offset.parent.top,
					co.left+Math.max(ce.scrollWidth,ce.offsetWidth) - (parseInt($(ce).css("borderLeftWidth"),10) || 0) - this.offset.relative.left - this.offset.parent.left - this.helperProportions.width - this.margins.left - (parseInt(this.element.css("marginRight"),10) || 0),
					co.top+Math.max(ce.scrollHeight,ce.offsetHeight) - (parseInt($(ce).css("borderTopWidth"),10) || 0) - this.offset.relative.top - this.offset.parent.top - this.helperProportions.height - this.margins.top - (parseInt(this.element.css("marginBottom"),10) || 0)
				];
			}
		}
		
		//Call plugins and callbacks
		this.propagate("start", e);
		
		this.helperProportions = { width: this.helper.outerWidth(), height: this.helper.outerHeight() };//Recache the helper size
		if ($.ui.ddmanager && !o.dropBehaviour) $.ui.ddmanager.prepareOffsets(this, e);
		
		this.helper.addClass("ui-draggable-dragging");
		this.mouseDrag(e); //Execute the drag once - this causes the helper not to be visible before getting its correct position
		return true;
	},
	convertPositionTo: function(d, pos) {
		if(!pos) pos = this.position;
		var mod = d == "absolute" ? 1 : -1;
		return {
			top: (
				pos.top																	// the calculated relative position
				+ this.offset.relative.top	* mod										// Only for relative positioned nodes: Relative offset from element to offset parent
				+ this.offset.parent.top * mod											// The offsetParent's offset without borders (offset + border)
				- (this.cssPosition == "fixed" || (this.cssPosition == "absolute" && this.offsetParent[0] == document.body) ? 0 : this.offsetParent[0].scrollTop) * mod	// The offsetParent's scroll position, not if the element is fixed
				+ (this.cssPosition == "fixed" ? $(document).scrollTop() : 0) * mod
				+ this.margins.top * mod												//Add the margin (you don't want the margin counting in intersection methods)
			),
			left: (
				pos.left																// the calculated relative position
				+ this.offset.relative.left	* mod										// Only for relative positioned nodes: Relative offset from element to offset parent
				+ this.offset.parent.left * mod											// The offsetParent's offset without borders (offset + border)
				- (this.cssPosition == "fixed" || (this.cssPosition == "absolute" && this.offsetParent[0] == document.body) ? 0 : this.offsetParent[0].scrollLeft) * mod	// The offsetParent's scroll position, not if the element is fixed
				+ (this.cssPosition == "fixed" ? $(document).scrollLeft() : 0) * mod
				+ this.margins.left * mod												//Add the margin (you don't want the margin counting in intersection methods)
			)
		};
	},
	generatePosition: function(e) {
		
		var o = this.options;
		var position = {
			top: (
				e.pageY																	// The absolute mouse position
				- this.offset.click.top													// Click offset (relative to the element)
				- this.offset.relative.top												// Only for relative positioned nodes: Relative offset from element to offset parent
				- this.offset.parent.top												// The offsetParent's offset without borders (offset + border)
				+ (this.cssPosition == "fixed" || (this.cssPosition == "absolute" && this.offsetParent[0] == document.body) ? 0 : this.offsetParent[0].scrollTop)	// The offsetParent's scroll position, not if the element is fixed
				- (this.cssPosition == "fixed" ? $(document).scrollTop() : 0)
			),
			left: (
				e.pageX																	// The absolute mouse position
				- this.offset.click.left												// Click offset (relative to the element)
				- this.offset.relative.left												// Only for relative positioned nodes: Relative offset from element to offset parent
				- this.offset.parent.left												// The offsetParent's offset without borders (offset + border)
				+ (this.cssPosition == "fixed" || (this.cssPosition == "absolute" && this.offsetParent[0] == document.body) ? 0 : this.offsetParent[0].scrollLeft)	// The offsetParent's scroll position, not if the element is fixed
				- (this.cssPosition == "fixed" ? $(document).scrollLeft() : 0)
			)
		};
		
		if(!this.originalPosition) return position;										//If we are not dragging yet, we won't check for options
		
		/*
		 * - Position constraining -
		 * Constrain the position to a mix of grid, containment.
		 */
		if(this.containment) {
			if(position.left < this.containment[0]) position.left = this.containment[0];
			if(position.top < this.containment[1]) position.top = this.containment[1];
			if(position.left > this.containment[2]) position.left = this.containment[2];
			if(position.top > this.containment[3]) position.top = this.containment[3];
		}
		
		if(o.grid) {
			var top = this.originalPosition.top + Math.round((position.top - this.originalPosition.top) / o.grid[1]) * o.grid[1];
			position.top = this.containment ? (!(top < this.containment[1] || top > this.containment[3]) ? top : (!(top < this.containment[1]) ? top - o.grid[1] : top + o.grid[1])) : top;
			
			var left = this.originalPosition.left + Math.round((position.left - this.originalPosition.left) / o.grid[0]) * o.grid[0];
			position.left = this.containment ? (!(left < this.containment[0] || left > this.containment[2]) ? left : (!(left < this.containment[0]) ? left - o.grid[0] : left + o.grid[0])) : left;
		}
		
		return position;
	},
	mouseDrag: function(e) {
		
		//Compute the helpers position
		this.position = this.generatePosition(e);
		this.positionAbs = this.convertPositionTo("absolute");
		
		//Call plugins and callbacks and use the resulting position if something is returned		
		this.position = this.propagate("drag", e) || this.position;
		
		if(!this.options.axis || this.options.axis != "y") this.helper[0].style.left = this.position.left+'px';
		if(!this.options.axis || this.options.axis != "x") this.helper[0].style.top = this.position.top+'px';
		if($.ui.ddmanager) $.ui.ddmanager.drag(this, e);
		
		return false;
	},
	mouseStop: function(e) {
		
		//If we are using droppables, inform the manager about the drop
		var dropped = false;
		if ($.ui.ddmanager && !this.options.dropBehaviour)
			var dropped = $.ui.ddmanager.drop(this, e);		
		
		if((this.options.revert == "invalid" && !dropped) || (this.options.revert == "valid" && dropped) || this.options.revert === true) {
			var self = this;
			$(this.helper).animate(this.originalPosition, parseInt(this.options.revert, 10) || 500, function() {
				self.propagate("stop", e);
				self.clear();
			});
		} else {
			this.propagate("stop", e);
			this.clear();
		}
		
		return false;
	},
	clear: function() {
		this.helper.removeClass("ui-draggable-dragging");
		if(this.options.helper != 'original' && !this.cancelHelperRemoval) this.helper.remove();
		//if($.ui.ddmanager) $.ui.ddmanager.current = null;
		this.helper = null;
		this.cancelHelperRemoval = false;
	},
	
	// From now on bulk stuff - mainly helpers
	plugins: {},
	uiHash: function(e) {
		return {
			helper: this.helper,
			position: this.position,
			absolutePosition: this.positionAbs,
			options: this.options			
		};
	},
	propagate: function(n,e) {
		$.ui.plugin.call(this, n, [e, this.uiHash()]);
		if(n == "drag") this.positionAbs = this.convertPositionTo("absolute"); //The absolute position has to be recalculated after plugins
		return this.element.triggerHandler(n == "drag" ? n : "drag"+n, [e, this.uiHash()], this.options[n]);
	},
	destroy: function() {
		if(!this.element.data('draggable')) return;
		this.element.removeData("draggable").unbind(".draggable").removeClass('ui-draggable');
		this.mouseDestroy();
	}
}));

$.extend($.ui.draggable, {
	defaults: {
		appendTo: "parent",
		axis: false,
		cancel: ":input",
		delay: 0,
		distance: 1,
		helper: "original"
	}
});

$.ui.plugin.add("draggable", "cursor", {
	start: function(e, ui) {
		var t = $('body');
		if (t.css("cursor")) ui.options._cursor = t.css("cursor");
		t.css("cursor", ui.options.cursor);
	},
	stop: function(e, ui) {
		if (ui.options._cursor) $('body').css("cursor", ui.options._cursor);
	}
});

$.ui.plugin.add("draggable", "zIndex", {
	start: function(e, ui) {
		var t = $(ui.helper);
		if(t.css("zIndex")) ui.options._zIndex = t.css("zIndex");
		t.css('zIndex', ui.options.zIndex);
	},
	stop: function(e, ui) {
		if(ui.options._zIndex) $(ui.helper).css('zIndex', ui.options._zIndex);
	}
});

$.ui.plugin.add("draggable", "opacity", {
	start: function(e, ui) {
		var t = $(ui.helper);
		if(t.css("opacity")) ui.options._opacity = t.css("opacity");
		t.css('opacity', ui.options.opacity);
	},
	stop: function(e, ui) {
		if(ui.options._opacity) $(ui.helper).css('opacity', ui.options._opacity);
	}
});

$.ui.plugin.add("draggable", "iframeFix", {
	start: function(e, ui) {
		$(ui.options.iframeFix === true ? "iframe" : ui.options.iframeFix).each(function() {					
			$('<div class="ui-draggable-iframeFix" style="background: #fff;"></div>')
			.css({
				width: this.offsetWidth+"px", height: this.offsetHeight+"px",
				position: "absolute", opacity: "0.001", zIndex: 1000
			})
			.css($(this).offset())
			.appendTo("body");
		});
	},
	stop: function(e, ui) {
		$("div.DragDropIframeFix").each(function() { this.parentNode.removeChild(this); }); //Remove frame helpers	
	}
});

$.ui.plugin.add("draggable", "scroll", {
	start: function(e, ui) {
		var o = ui.options;
		var i = $(this).data("draggable");
		o.scrollSensitivity	= o.scrollSensitivity || 20;
		o.scrollSpeed		= o.scrollSpeed || 20;
		
		i.overflowY = function(el) {
			do { if(/auto|scroll/.test(el.css('overflow')) || (/auto|scroll/).test(el.css('overflow-y'))) return el; el = el.parent(); } while (el[0].parentNode);
			return $(document);
		}(this);
		i.overflowX = function(el) {
			do { if(/auto|scroll/.test(el.css('overflow')) || (/auto|scroll/).test(el.css('overflow-x'))) return el; el = el.parent(); } while (el[0].parentNode);
			return $(document);
		}(this);
		
		if(i.overflowY[0] != document && i.overflowY[0].tagName != 'HTML') i.overflowYOffset = i.overflowY.offset();
		if(i.overflowX[0] != document && i.overflowX[0].tagName != 'HTML') i.overflowXOffset = i.overflowX.offset();
		
	},
	drag: function(e, ui) {
		
		var o = ui.options;
		var i = $(this).data("draggable");
		
		if(i.overflowY[0] != document && i.overflowY[0].tagName != 'HTML') {
			if((i.overflowYOffset.top + i.overflowY[0].offsetHeight) - e.pageY < o.scrollSensitivity)
				i.overflowY[0].scrollTop = i.overflowY[0].scrollTop + o.scrollSpeed;
			if(e.pageY - i.overflowYOffset.top < o.scrollSensitivity)
				i.overflowY[0].scrollTop = i.overflowY[0].scrollTop - o.scrollSpeed;
							
		} else {
			if(e.pageY - $(document).scrollTop() < o.scrollSensitivity)
				$(document).scrollTop($(document).scrollTop() - o.scrollSpeed);
			if($(window).height() - (e.pageY - $(document).scrollTop()) < o.scrollSensitivity)
				$(document).scrollTop($(document).scrollTop() + o.scrollSpeed);
		}
		
		if(i.overflowX[0] != document && i.overflowX[0].tagName != 'HTML') {
			if((i.overflowXOffset.left + i.overflowX[0].offsetWidth) - e.pageX < o.scrollSensitivity)
				i.overflowX[0].scrollLeft = i.overflowX[0].scrollLeft + o.scrollSpeed;
			if(e.pageX - i.overflowXOffset.left < o.scrollSensitivity)
				i.overflowX[0].scrollLeft = i.overflowX[0].scrollLeft - o.scrollSpeed;
		} else {
			if(e.pageX - $(document).scrollLeft() < o.scrollSensitivity)
				$(document).scrollLeft($(document).scrollLeft() - o.scrollSpeed);
			if($(window).width() - (e.pageX - $(document).scrollLeft()) < o.scrollSensitivity)
				$(document).scrollLeft($(document).scrollLeft() + o.scrollSpeed);
		}
		
	}
});

$.ui.plugin.add("draggable", "snap", {
	start: function(e, ui) {
		
		var inst = $(this).data("draggable");
		inst.snapElements = [];
		$(ui.options.snap === true ? '.ui-draggable' : ui.options.snap).each(function() {
			var $t = $(this); var $o = $t.offset();
			if(this != inst.element[0]) inst.snapElements.push({
				item: this,
				width: $t.outerWidth(), height: $t.outerHeight(),
				top: $o.top, left: $o.left
			});
		});
		
	},
	drag: function(e, ui) {
		
		var inst = $(this).data("draggable");
		var d = ui.options.snapTolerance || 20;
		var x1 = ui.absolutePosition.left, x2 = x1 + inst.helperProportions.width,
			y1 = ui.absolutePosition.top, y2 = y1 + inst.helperProportions.height;
		
		for (var i = inst.snapElements.length - 1; i >= 0; i--){
			
			var l = inst.snapElements[i].left, r = l + inst.snapElements[i].width, 
				t = inst.snapElements[i].top, b = t + inst.snapElements[i].height;
			
			//Yes, I know, this is insane ;)
			if(!((l-d < x1 && x1 < r+d && t-d < y1 && y1 < b+d) || (l-d < x1 && x1 < r+d && t-d < y2 && y2 < b+d) || (l-d < x2 && x2 < r+d && t-d < y1 && y1 < b+d) || (l-d < x2 && x2 < r+d && t-d < y2 && y2 < b+d))) continue;
			
			if(ui.options.snapMode != 'inner') {
				var ts = Math.abs(t - y2) <= 20;
				var bs = Math.abs(b - y1) <= 20;
				var ls = Math.abs(l - x2) <= 20;
				var rs = Math.abs(r - x1) <= 20;
				if(ts) ui.position.top = inst.convertPositionTo("relative", { top: t - inst.helperProportions.height, left: 0 }).top;
				if(bs) ui.position.top = inst.convertPositionTo("relative", { top: b, left: 0 }).top;
				if(ls) ui.position.left = inst.convertPositionTo("relative", { top: 0, left: l - inst.helperProportions.width }).left;
				if(rs) ui.position.left = inst.convertPositionTo("relative", { top: 0, left: r }).left;
			}
			
			if(ui.options.snapMode != 'outer') {
				var ts = Math.abs(t - y1) <= 20;
				var bs = Math.abs(b - y2) <= 20;
				var ls = Math.abs(l - x1) <= 20;
				var rs = Math.abs(r - x2) <= 20;
				if(ts) ui.position.top = inst.convertPositionTo("relative", { top: t, left: 0 }).top;
				if(bs) ui.position.top = inst.convertPositionTo("relative", { top: b - inst.helperProportions.height, left: 0 }).top;
				if(ls) ui.position.left = inst.convertPositionTo("relative", { top: 0, left: l }).left;
				if(rs) ui.position.left = inst.convertPositionTo("relative", { top: 0, left: r - inst.helperProportions.width }).left;
			}
			
		};
	}
});

$.ui.plugin.add("draggable", "connectToSortable", {
	start: function(e,ui) {
	
		var inst = $(this).data("draggable");
		inst.sortables = [];
		$(ui.options.connectToSortable).each(function() {
			if($.data(this, 'sortable')) {
				var sortable = $.data(this, 'sortable');
				inst.sortables.push({
					instance: sortable,
					shouldRevert: sortable.options.revert
				});
				sortable.refreshItems();	//Do a one-time refresh at start to refresh the containerCache	
				sortable.propagate("activate", e, inst);
			}
		});

	},
	stop: function(e,ui) {
		
		//If we are still over the sortable, we fake the stop event of the sortable, but also remove helper
		var inst = $(this).data("draggable");
		
		$.each(inst.sortables, function() {
			if(this.instance.isOver) {
				this.instance.isOver = 0;
				inst.cancelHelperRemoval = true; //Don't remove the helper in the draggable instance
				this.instance.cancelHelperRemoval = false; //Remove it in the sortable instance (so sortable plugins like revert still work)
				if(this.shouldRevert) this.instance.options.revert = true; //revert here
				this.instance.mouseStop(e);
				
				//Also propagate receive event, since the sortable is actually receiving a element
				this.instance.element.triggerHandler("sortreceive", [e, $.extend(this.instance.ui(), { sender: inst.element })], this.instance.options["receive"]);

				this.instance.options.helper = this.instance.options._helper;
			} else {
				this.instance.propagate("deactivate", e, inst);
			}

		});
		
	},
	drag: function(e,ui) {

		var inst = $(this).data("draggable"), self = this;
		
		var checkPos = function(o) {
				
			var l = o.left, r = l + o.width,
				t = o.top, b = t + o.height;

			return (l < (this.positionAbs.left + this.offset.click.left) && (this.positionAbs.left + this.offset.click.left) < r
					&& t < (this.positionAbs.top + this.offset.click.top) && (this.positionAbs.top + this.offset.click.top) < b);				
		};
		
		$.each(inst.sortables, function(i) {

			if(checkPos.call(inst, this.instance.containerCache)) {

				//If it intersects, we use a little isOver variable and set it once, so our move-in stuff gets fired only once
				if(!this.instance.isOver) {
					this.instance.isOver = 1;

					//Now we fake the start of dragging for the sortable instance,
					//by cloning the list group item, appending it to the sortable and using it as inst.currentItem
					//We can then fire the start event of the sortable with our passed browser event, and our own helper (so it doesn't create a new one)
					this.instance.currentItem = $(self).clone().appendTo(this.instance.element).data("sortable-item", true);
					this.instance.options._helper = this.instance.options.helper; //Store helper option to later restore it
					this.instance.options.helper = function() { return ui.helper[0]; };
				
					e.target = this.instance.currentItem[0];
					this.instance.mouseCapture(e, true);
					this.instance.mouseStart(e, true, true);

					//Because the browser event is way off the new appended portlet, we modify a couple of variables to reflect the changes
					this.instance.offset.click.top = inst.offset.click.top;
					this.instance.offset.click.left = inst.offset.click.left;
					this.instance.offset.parent.left -= inst.offset.parent.left - this.instance.offset.parent.left;
					this.instance.offset.parent.top -= inst.offset.parent.top - this.instance.offset.parent.top;
					
					inst.propagate("toSortable", e);
				
				}
				
				//Provided we did all the previous steps, we can fire the drag event of the sortable on every draggable drag, when it intersects with the sortable
				if(this.instance.currentItem) this.instance.mouseDrag(e);
				
			} else {
				
				//If it doesn't intersect with the sortable, and it intersected before,
				//we fake the drag stop of the sortable, but make sure it doesn't remove the helper by using cancelHelperRemoval
				if(this.instance.isOver) {
					this.instance.isOver = 0;
					this.instance.cancelHelperRemoval = true;
					this.instance.options.revert = false; //No revert here
					this.instance.mouseStop(e, true);
					this.instance.options.helper = this.instance.options._helper;
					
					//Now we remove our currentItem, the list group clone again, and the placeholder, and animate the helper back to it's original size
					this.instance.currentItem.remove();
					if(this.instance.placeholder) this.instance.placeholder.remove();
					
					inst.propagate("fromSortable", e);
				}
				
			};

		});

	}
});

$.ui.plugin.add("draggable", "stack", {
	start: function(e,ui) {
		var group = $.makeArray($(ui.options.stack.group)).sort(function(a,b) {
			return (parseInt($(a).css("zIndex"),10) || ui.options.stack.min) - (parseInt($(b).css("zIndex"),10) || ui.options.stack.min);
		});
		
		$(group).each(function(i) {
			this.style.zIndex = ui.options.stack.min + i;
		});
		
		this[0].style.zIndex = ui.options.stack.min + group.length;
	}
});

})(jQuery);
/*
 * jQuery UI Droppable
 *
 * Copyright (c) 2008 Paul Bakaus
 * Dual licensed under the MIT (MIT-LICENSE.txt)
 * and GPL (GPL-LICENSE.txt) licenses.
 * 
 * http://docs.jquery.com/UI/Droppables
 *
 * Depends:
 *	ui.core.js
 *	ui.draggable.js
 */
(function($) {

$.widget("ui.droppable", {
	init: function() {

		this.element.addClass("ui-droppable");
		this.isover = 0; this.isout = 1;
		
		//Prepare the passed options
		var o = this.options, accept = o.accept;
		o = $.extend(o, {
			accept: o.accept && o.accept.constructor == Function ? o.accept : function(d) {
				return $(d).is(accept);
			}
		});
		
		//Store the droppable's proportions
		this.proportions = { width: this.element[0].offsetWidth, height: this.element[0].offsetHeight };
		
		// Add the reference and positions to the manager
		$.ui.ddmanager.droppables.push(this);
		
	},
	plugins: {},
	ui: function(c) {
		return {
			draggable: (c.currentItem || c.element),
			helper: c.helper,
			position: c.position,
			absolutePosition: c.positionAbs,
			options: this.options,
			element: this.element
		};
	},
	destroy: function() {
		var drop = $.ui.ddmanager.droppables;
		for ( var i = 0; i < drop.length; i++ )
			if ( drop[i] == this )
				drop.splice(i, 1);
		
		this.element
			.removeClass("ui-droppable ui-droppable-disabled")
			.removeData("droppable")
			.unbind(".droppable");
	},
	over: function(e) {
		
		var draggable = $.ui.ddmanager.current;
		if (!draggable || (draggable.currentItem || draggable.element)[0] == this.element[0]) return; // Bail if draggable and droppable are same element
		
		if (this.options.accept.call(this.element,(draggable.currentItem || draggable.element))) {
			$.ui.plugin.call(this, 'over', [e, this.ui(draggable)]);
			this.element.triggerHandler("dropover", [e, this.ui(draggable)], this.options.over);
		}
		
	},
	out: function(e) {
		
		var draggable = $.ui.ddmanager.current;
		if (!draggable || (draggable.currentItem || draggable.element)[0] == this.element[0]) return; // Bail if draggable and droppable are same element
		
		if (this.options.accept.call(this.element,(draggable.currentItem || draggable.element))) {
			$.ui.plugin.call(this, 'out', [e, this.ui(draggable)]);
			this.element.triggerHandler("dropout", [e, this.ui(draggable)], this.options.out);
		}
		
	},
	drop: function(e,custom) {
		
		var draggable = custom || $.ui.ddmanager.current;
		if (!draggable || (draggable.currentItem || draggable.element)[0] == this.element[0]) return false; // Bail if draggable and droppable are same element
		
		var childrenIntersection = false;
		this.element.find(".ui-droppable").not(".ui-draggable-dragging").each(function() {
			var inst = $.data(this, 'droppable');
			if(inst.options.greedy && $.ui.intersect(draggable, $.extend(inst, { offset: inst.element.offset() }), inst.options.tolerance)) {
				childrenIntersection = true; return false;
			}
		});
		if(childrenIntersection) return false;
		
		if(this.options.accept.call(this.element,(draggable.currentItem || draggable.element))) {
			$.ui.plugin.call(this, 'drop', [e, this.ui(draggable)]);
			this.element.triggerHandler("drop", [e, this.ui(draggable)], this.options.drop);
			return true;
		}
		
		return false;
		
	},
	activate: function(e) {
		
		var draggable = $.ui.ddmanager.current;
		$.ui.plugin.call(this, 'activate', [e, this.ui(draggable)]);
		if(draggable) this.element.triggerHandler("dropactivate", [e, this.ui(draggable)], this.options.activate);
		
	},
	deactivate: function(e) {
		
		var draggable = $.ui.ddmanager.current;
		$.ui.plugin.call(this, 'deactivate', [e, this.ui(draggable)]);
		if(draggable) this.element.triggerHandler("dropdeactivate", [e, this.ui(draggable)], this.options.deactivate);
		
	}
});

$.extend($.ui.droppable, {
	defaults: {
		disabled: false,
		tolerance: 'intersect'
	}
});

$.ui.intersect = function(draggable, droppable, toleranceMode) {
	
	if (!droppable.offset) return false;
	
	var x1 = (draggable.positionAbs || draggable.position.absolute).left, x2 = x1 + draggable.helperProportions.width,
		y1 = (draggable.positionAbs || draggable.position.absolute).top, y2 = y1 + draggable.helperProportions.height;
	var l = droppable.offset.left, r = l + droppable.proportions.width,
		t = droppable.offset.top, b = t + droppable.proportions.height;
	
	switch (toleranceMode) {
		case 'fit':
			return (l < x1 && x2 < r
				&& t < y1 && y2 < b);
			break;
		case 'intersect':
			return (l < x1 + (draggable.helperProportions.width / 2) // Right Half
				&& x2 - (draggable.helperProportions.width / 2) < r // Left Half
				&& t < y1 + (draggable.helperProportions.height / 2) // Bottom Half
				&& y2 - (draggable.helperProportions.height / 2) < b ); // Top Half
			break;
		case 'pointer':
			return (l < ((draggable.positionAbs || draggable.position.absolute).left + (draggable.clickOffset || draggable.offset.click).left) && ((draggable.positionAbs || draggable.position.absolute).left + (draggable.clickOffset || draggable.offset.click).left) < r
				&& t < ((draggable.positionAbs || draggable.position.absolute).top + (draggable.clickOffset || draggable.offset.click).top) && ((draggable.positionAbs || draggable.position.absolute).top + (draggable.clickOffset || draggable.offset.click).top) < b);
			break;
		case 'touch':
			return (
					(y1 >= t && y1 <= b) ||	// Top edge touching
					(y2 >= t && y2 <= b) ||	// Bottom edge touching
					(y1 < t && y2 > b)		// Surrounded vertically
				) && (
					(x1 >= l && x1 <= r) ||	// Left edge touching
					(x2 >= l && x2 <= r) ||	// Right edge touching
					(x1 < l && x2 > r)		// Surrounded horizontally
				);
			break;
		default:
			return false;
			break;
		}
	
};

/*
	This manager tracks offsets of draggables and droppables
*/
$.ui.ddmanager = {
	current: null,
	droppables: [],
	prepareOffsets: function(t, e) {
		
		var m = $.ui.ddmanager.droppables;
		var type = e ? e.type : null; // workaround for #2317

		for (var i = 0; i < m.length; i++) {
			if(m[i].options.disabled || (t && !m[i].options.accept.call(m[i].element,(t.currentItem || t.element)))) continue;
			m[i].visible = m[i].element.css("display") != "none"; if(!m[i].visible) continue; //If the element is not visible, continue
			m[i].offset = m[i].element.offset();
			m[i].proportions = { width: m[i].element[0].offsetWidth, height: m[i].element[0].offsetHeight };
			
			if(type == "dragstart" || type == "sortactivate") m[i].activate.call(m[i], e); //Activate the droppable if used directly from draggables
		}
		
	},
	drop: function(draggable, e) {
		
		var dropped = false;
		$.each($.ui.ddmanager.droppables, function() {
			
			if(!this.options) return;
			if (!this.options.disabled && this.visible && $.ui.intersect(draggable, this, this.options.tolerance))
				dropped = this.drop.call(this, e);
			
			if (!this.options.disabled && this.visible && this.options.accept.call(this.element,(draggable.currentItem || draggable.element))) {
				this.isout = 1; this.isover = 0;
				this.deactivate.call(this, e);
			}
			
		});
		return dropped;
		
	},
	drag: function(draggable, e) {
		
		//If you have a highly dynamic page, you might try this option. It renders positions every time you move the mouse.
		if(draggable.options.refreshPositions) $.ui.ddmanager.prepareOffsets(draggable, e);
		
		//Run through all droppables and check their positions based on specific tolerance options

		$.each($.ui.ddmanager.droppables, function() {
			
			if(this.options.disabled || this.greedyChild || !this.visible) return;
			var intersects = $.ui.intersect(draggable, this, this.options.tolerance);
			
			var c = !intersects && this.isover == 1 ? 'isout' : (intersects && this.isover == 0 ? 'isover' : null);
			if(!c) return;
			
			var parentInstance;
			if (this.options.greedy) {
				var parent = this.element.parents('.ui-droppable:eq(0)');
				if (parent.length) {
					parentInstance = $.data(parent[0], 'droppable');
					parentInstance.greedyChild = (c == 'isover' ? 1 : 0);
				}
			}
			
			// we just moved into a greedy child
			if (parentInstance && c == 'isover') {
				parentInstance['isover'] = 0;
				parentInstance['isout'] = 1;
				parentInstance.out.call(parentInstance, e);
			}
			
			this[c] = 1; this[c == 'isout' ? 'isover' : 'isout'] = 0;
			this[c == "isover" ? "over" : "out"].call(this, e);
			
			// we just moved out of a greedy child
			if (parentInstance && c == 'isout') {
				parentInstance['isout'] = 0;
				parentInstance['isover'] = 1;
				parentInstance.over.call(parentInstance, e);
			}
		});
		
	}
};

/*
 * Droppable Extensions
 */

$.ui.plugin.add("droppable", "activeClass", {
	activate: function(e, ui) {
		$(this).addClass(ui.options.activeClass);
	},
	deactivate: function(e, ui) {
		$(this).removeClass(ui.options.activeClass);
	},
	drop: function(e, ui) {
		$(this).removeClass(ui.options.activeClass);
	}
});

$.ui.plugin.add("droppable", "hoverClass", {
	over: function(e, ui) {
		$(this).addClass(ui.options.hoverClass);
	},
	out: function(e, ui) {
		$(this).removeClass(ui.options.hoverClass);
	},
	drop: function(e, ui) {
		$(this).removeClass(ui.options.hoverClass);
	}
});

})(jQuery);
/*
 * jQuery UI Resizable
 *
 * Copyright (c) 2008 Paul Bakaus
 * Dual licensed under the MIT (MIT-LICENSE.txt)
 * and GPL (GPL-LICENSE.txt) licenses.
 * 
 * http://docs.jquery.com/UI/Resizables
 *
 * Depends:
 *	ui.core.js
 */
(function($) {

$.widget("ui.resizable", $.extend({}, $.ui.mouse, {
	init: function() {

		var self = this, o = this.options;

		var elpos = this.element.css('position');
		
		this.originalElement = this.element;
		
		// simulate .ui-resizable { position: relative; }
		this.element.addClass("ui-resizable").css({ position: /static/.test(elpos) ? 'relative' : elpos });
		
		$.extend(o, {
			_aspectRatio: !!(o.aspectRatio),
			helper: o.helper || o.ghost || o.animate ? o.helper || 'proxy' : null,
			knobHandles: o.knobHandles === true ? 'ui-resizable-knob-handle' : o.knobHandles
		});
		
		//Default Theme
		var aBorder = '1px solid #DEDEDE';
		
		o.defaultTheme = {
			'ui-resizable': { display: 'block' },
			'ui-resizable-handle': { position: 'absolute', background: '#F2F2F2', fontSize: '0.1px' },
			'ui-resizable-n': { cursor: 'n-resize', height: '4px', left: '0px', right: '0px', borderTop: aBorder },
			'ui-resizable-s': { cursor: 's-resize', height: '4px', left: '0px', right: '0px', borderBottom: aBorder },
			'ui-resizable-e': { cursor: 'e-resize', width: '4px', top: '0px', bottom: '0px', borderRight: aBorder },
			'ui-resizable-w': { cursor: 'w-resize', width: '4px', top: '0px', bottom: '0px', borderLeft: aBorder },
			'ui-resizable-se': { cursor: 'se-resize', width: '4px', height: '4px', borderRight: aBorder, borderBottom: aBorder },
			'ui-resizable-sw': { cursor: 'sw-resize', width: '4px', height: '4px', borderBottom: aBorder, borderLeft: aBorder },
			'ui-resizable-ne': { cursor: 'ne-resize', width: '4px', height: '4px', borderRight: aBorder, borderTop: aBorder },
			'ui-resizable-nw': { cursor: 'nw-resize', width: '4px', height: '4px', borderLeft: aBorder, borderTop: aBorder }
		};
		
		o.knobTheme = {
			'ui-resizable-handle': { background: '#F2F2F2', border: '1px solid #808080', height: '8px', width: '8px' },
			'ui-resizable-n': { cursor: 'n-resize', top: '0px', left: '45%' },
			'ui-resizable-s': { cursor: 's-resize', bottom: '0px', left: '45%' },
			'ui-resizable-e': { cursor: 'e-resize', right: '0px', top: '45%' },
			'ui-resizable-w': { cursor: 'w-resize', left: '0px', top: '45%' },
			'ui-resizable-se': { cursor: 'se-resize', right: '0px', bottom: '0px' },
			'ui-resizable-sw': { cursor: 'sw-resize', left: '0px', bottom: '0px' },
			'ui-resizable-nw': { cursor: 'nw-resize', left: '0px', top: '0px' },
			'ui-resizable-ne': { cursor: 'ne-resize', right: '0px', top: '0px' }
		};
		
		o._nodeName = this.element[0].nodeName;
		
		//Wrap the element if it cannot hold child nodes
		if(o._nodeName.match(/canvas|textarea|input|select|button|img/i)) {
			var el = this.element;
			
			//Opera fixing relative position
			if (/relative/.test(el.css('position')) && $.browser.opera)
				el.css({ position: 'relative', top: 'auto', left: 'auto' });
			
			//Create a wrapper element and set the wrapper to the new current internal element
			el.wrap(
				$('<div class="ui-wrapper"	style="overflow: hidden;"></div>').css( {
					position: el.css('position'),
					width: el.outerWidth(),
					height: el.outerHeight(),
					top: el.css('top'),
					left: el.css('left')
				})
			);
			
			var oel = this.element; this.element = this.element.parent();
			
			// store instance on wrapper
			this.element.data('resizable', this); 
			
			//Move margins to the wrapper
			this.element.css({ marginLeft: oel.css("marginLeft"), marginTop: oel.css("marginTop"),
				marginRight: oel.css("marginRight"), marginBottom: oel.css("marginBottom")
			});
			
			oel.css({ marginLeft: 0, marginTop: 0, marginRight: 0, marginBottom: 0});
			
			//Prevent Safari textarea resize
			if ($.browser.safari && o.preventDefault) oel.css('resize', 'none');
			
			o.proportionallyResize = oel.css({ position: 'static', zoom: 1, display: 'block' });
			
			// avoid IE jump
			this.element.css({ margin: oel.css('margin') });
			
			// fix handlers offset
			this._proportionallyResize();
		}
		
		if(!o.handles) o.handles = !$('.ui-resizable-handle', this.element).length ? "e,s,se" : { n: '.ui-resizable-n', e: '.ui-resizable-e', s: '.ui-resizable-s', w: '.ui-resizable-w', se: '.ui-resizable-se', sw: '.ui-resizable-sw', ne: '.ui-resizable-ne', nw: '.ui-resizable-nw' };
		if(o.handles.constructor == String) {
			
			o.zIndex = o.zIndex || 1000;
			
			if(o.handles == 'all') o.handles = 'n,e,s,w,se,sw,ne,nw';
			
			var n = o.handles.split(","); o.handles = {};
			
			// insertions are applied when don't have theme loaded
			var insertionsDefault = {
				handle: 'position: absolute; display: none; overflow:hidden;',
				n: 'top: 0pt; width:100%;',
				e: 'right: 0pt; height:100%;',
				s: 'bottom: 0pt; width:100%;',
				w: 'left: 0pt; height:100%;',
				se: 'bottom: 0pt; right: 0px;',
				sw: 'bottom: 0pt; left: 0px;',
				ne: 'top: 0pt; right: 0px;',
				nw: 'top: 0pt; left: 0px;'
			};
			
			for(var i = 0; i < n.length; i++) {
				var handle = $.trim(n[i]), dt = o.defaultTheme, hname = 'ui-resizable-'+handle, loadDefault = !$.ui.css(hname) && !o.knobHandles, userKnobClass = $.ui.css('ui-resizable-knob-handle'), 
							allDefTheme = $.extend(dt[hname], dt['ui-resizable-handle']), allKnobTheme = $.extend(o.knobTheme[hname], !userKnobClass ? o.knobTheme['ui-resizable-handle'] : {});
				
				// increase zIndex of sw, se, ne, nw axis
				var applyZIndex = /sw|se|ne|nw/.test(handle) ? { zIndex: ++o.zIndex } : {};
				
				var defCss = (loadDefault ? insertionsDefault[handle] : ''), 
					axis = $(['<div class="ui-resizable-handle ', hname, '" style="', defCss, insertionsDefault.handle, '"></div>'].join('')).css( applyZIndex );
				o.handles[handle] = '.ui-resizable-'+handle;
				
				this.element.append(
					//Theme detection, if not loaded, load o.defaultTheme
					axis.css( loadDefault ? allDefTheme : {} )
						// Load the knobHandle css, fix width, height, top, left...
						.css( o.knobHandles ? allKnobTheme : {} ).addClass(o.knobHandles ? 'ui-resizable-knob-handle' : '').addClass(o.knobHandles)
				);
			}
			
			if (o.knobHandles) this.element.addClass('ui-resizable-knob').css( !$.ui.css('ui-resizable-knob') ? { /*border: '1px #fff dashed'*/ } : {} );
		}
		
		this._renderAxis = function(target) {
			target = target || this.element;
			
			for(var i in o.handles) {
				if(o.handles[i].constructor == String) 
					o.handles[i] = $(o.handles[i], this.element).show();
				
				if (o.transparent)
					o.handles[i].css({opacity:0});
				
				//Apply pad to wrapper element, needed to fix axis position (textarea, inputs, scrolls)
				if (this.element.is('.ui-wrapper') && 
					o._nodeName.match(/textarea|input|select|button/i)) {
					
					var axis = $(o.handles[i], this.element), padWrapper = 0;
					
					//Checking the correct pad and border
					padWrapper = /sw|ne|nw|se|n|s/.test(i) ? axis.outerHeight() : axis.outerWidth();
					
					//The padding type i have to apply...
					var padPos = [ 'padding', 
						/ne|nw|n/.test(i) ? 'Top' :
						/se|sw|s/.test(i) ? 'Bottom' : 
						/^e$/.test(i) ? 'Right' : 'Left' ].join(""); 
					
					if (!o.transparent)
						target.css(padPos, padWrapper);
					
					this._proportionallyResize();
				}
				if(!$(o.handles[i]).length) continue;
			}
		};
		
		this._renderAxis(this.element);
		o._handles = $('.ui-resizable-handle', self.element);
		
		if (o.disableSelection)
			o._handles.each(function(i, e) { $.ui.disableSelection(e); });
		
		//Matching axis name
		o._handles.mouseover(function() {
			if (!o.resizing) {
				if (this.className) 
					var axis = this.className.match(/ui-resizable-(se|sw|ne|nw|n|e|s|w)/i);
				//Axis, default = se
				self.axis = o.axis = axis && axis[1] ? axis[1] : 'se';
			}
		});
		
		//If we want to auto hide the elements
		if (o.autoHide) {
			o._handles.hide();
			$(self.element).addClass("ui-resizable-autohide").hover(function() {
				$(this).removeClass("ui-resizable-autohide");
				o._handles.show();
			},
			function(){
				if (!o.resizing) {
					$(this).addClass("ui-resizable-autohide");
					o._handles.hide();
				}
			});
		}
		
		this.mouseInit();
	},
	plugins: {},
	ui: function() {
		return {
			originalElement: this.originalElement,
			element: this.element,
			helper: this.helper,
			position: this.position,
			size: this.size,
			options: this.options,
			originalSize: this.originalSize,
			originalPosition: this.originalPosition
		};
	},
	propagate: function(n,e) {
		$.ui.plugin.call(this, n, [e, this.ui()]);
		if (n != "resize") this.element.triggerHandler(["resize", n].join(""), [e, this.ui()], this.options[n]);
	},
	destroy: function() {
		var el = this.element, wrapped = el.children(".ui-resizable").get(0);
		
		this.mouseDestroy();
		
		var _destroy = function(exp) {
			$(exp).removeClass("ui-resizable ui-resizable-disabled")
				.removeData("resizable").unbind(".resizable").find('.ui-resizable-handle').remove();
		};
		
		_destroy(el);
		
		if (el.is('.ui-wrapper') && wrapped) {
			el.parent().append(
				$(wrapped).css({
					position: el.css('position'),
					width: el.outerWidth(),
					height: el.outerHeight(),
					top: el.css('top'),
					left: el.css('left')
				})
			).end().remove();
			
			_destroy(wrapped);
		}
	},
	mouseStart: function(e) {
		if(this.options.disabled) return false;
		
		var handle = false;
		for(var i in this.options.handles) {
			if($(this.options.handles[i])[0] == e.target) handle = true;
		}
		if (!handle) return false;
		
		var o = this.options, iniPos = this.element.position(), el = this.element, 
			num = function(v) { return parseInt(v, 10) || 0; }, ie6 = $.browser.msie && $.browser.version < 7;
		o.resizing = true;
		o.documentScroll = { top: $(document).scrollTop(), left: $(document).scrollLeft() };
		
		// bugfix #1749
		if (el.is('.ui-draggable') || (/absolute/).test(el.css('position'))) {
			
			// sOffset decides if document scrollOffset will be added to the top/left of the resizable element
			var sOffset = $.browser.msie && !o.containment && (/absolute/).test(el.css('position')) && !(/relative/).test(el.parent().css('position'));
			var dscrollt = sOffset ? o.documentScroll.top : 0, dscrolll = sOffset ? o.documentScroll.left : 0;
			
			el.css({ position: 'absolute', top: (iniPos.top + dscrollt), left: (iniPos.left + dscrolll) });
		}
		
		//Opera fixing relative position
		if ($.browser.opera && /relative/.test(el.css('position')))
			el.css({ position: 'relative', top: 'auto', left: 'auto' });
		
		this._renderProxy();
		
		var curleft = num(this.helper.css('left')), curtop = num(this.helper.css('top'));
		
		if (o.containment) {
			curleft += $(o.containment).scrollLeft()||0;
			curtop += $(o.containment).scrollTop()||0;
		}
		
		//Store needed variables
		this.offset = this.helper.offset();
		this.position = { left: curleft, top: curtop };
		this.size = o.helper || ie6 ? { width: el.outerWidth(), height: el.outerHeight() } : { width: el.width(), height: el.height() };
		this.originalSize = o.helper || ie6 ? { width: el.outerWidth(), height: el.outerHeight() } : { width: el.width(), height: el.height() };
		this.originalPosition = { left: curleft, top: curtop };
		this.sizeDiff = { width: el.outerWidth() - el.width(), height: el.outerHeight() - el.height() };
		this.originalMousePosition = { left: e.pageX, top: e.pageY };
		
		//Aspect Ratio
		o.aspectRatio = (typeof o.aspectRatio == 'number') ? o.aspectRatio : ((this.originalSize.height / this.originalSize.width)||1);
		
		if (o.preserveCursor)
			$('body').css('cursor', this.axis + '-resize');
			
		this.propagate("start", e);
		return true;
	},
	mouseDrag: function(e) {
		
		//Increase performance, avoid regex
		var el = this.helper, o = this.options, props = {},
			self = this, smp = this.originalMousePosition, a = this.axis;
		
		var dx = (e.pageX-smp.left)||0, dy = (e.pageY-smp.top)||0;
		var trigger = this._change[a];
		if (!trigger) return false;
		
		// Calculate the attrs that will be change
		var data = trigger.apply(this, [e, dx, dy]), ie6 = $.browser.msie && $.browser.version < 7, csdif = this.sizeDiff;
		
		if (o._aspectRatio || e.shiftKey)
			data = this._updateRatio(data, e);
		
		data = this._respectSize(data, e);
		
		// plugins callbacks need to be called first
		this.propagate("resize", e);
		
		el.css({
			top: this.position.top + "px", left: this.position.left + "px", 
			width: this.size.width + "px", height: this.size.height + "px"
		});
		
		if (!o.helper && o.proportionallyResize)
			this._proportionallyResize();
		
		this._updateCache(data);
		
		// calling the user callback at the end
		this.element.triggerHandler("resize", [e, this.ui()], this.options["resize"]);
		
		return false;
	},
	mouseStop: function(e) {
		
		this.options.resizing = false;
		var o = this.options, num = function(v) { return parseInt(v, 10) || 0; }, self = this;
		
		if(o.helper) {
			var pr = o.proportionallyResize, ista = pr && (/textarea/i).test(pr.get(0).nodeName), 
						soffseth = ista && $.ui.hasScroll(pr.get(0), 'left') /* TODO - jump height */ ? 0 : self.sizeDiff.height,
							soffsetw = ista ? 0 : self.sizeDiff.width;
			
			var s = { width: (self.size.width - soffsetw), height: (self.size.height - soffseth) },
				left = (parseInt(self.element.css('left'), 10) + (self.position.left - self.originalPosition.left)) || null, 
				top = (parseInt(self.element.css('top'), 10) + (self.position.top - self.originalPosition.top)) || null;
			
			if (!o.animate)
				this.element.css($.extend(s, { top: top, left: left }));
			
			if (o.helper && !o.animate) this._proportionallyResize();
		}
		
		if (o.preserveCursor)
			$('body').css('cursor', 'auto');
		
		this.propagate("stop", e);
		
		if (o.helper) this.helper.remove();
		
		return false;
	},
	_updateCache: function(data) {
		var o = this.options;
		this.offset = this.helper.offset();
		if (data.left) this.position.left = data.left;
		if (data.top) this.position.top = data.top;
		if (data.height) this.size.height = data.height;
		if (data.width) this.size.width = data.width;
	},
	_updateRatio: function(data, e) {
		var o = this.options, cpos = this.position, csize = this.size, a = this.axis;
		
		if (data.height) data.width = (csize.height / o.aspectRatio);
		else if (data.width) data.height = (csize.width * o.aspectRatio);
		
		if (a == 'sw') {
			data.left = cpos.left + (csize.width - data.width);
			data.top = null;
		}
		if (a == 'nw') { 
			data.top = cpos.top + (csize.height - data.height);
			data.left = cpos.left + (csize.width - data.width);
		}
		
		return data;
	},
	_respectSize: function(data, e) {
		
		var el = this.helper, o = this.options, pRatio = o._aspectRatio || e.shiftKey, a = this.axis, 
				ismaxw = data.width && o.maxWidth && o.maxWidth < data.width, ismaxh = data.height && o.maxHeight && o.maxHeight < data.height,
					isminw = data.width && o.minWidth && o.minWidth > data.width, isminh = data.height && o.minHeight && o.minHeight > data.height;
		
		if (isminw) data.width = o.minWidth;
		if (isminh) data.height = o.minHeight;
		if (ismaxw) data.width = o.maxWidth;
		if (ismaxh) data.height = o.maxHeight;
		
		var dw = this.originalPosition.left + this.originalSize.width, dh = this.position.top + this.size.height;
		var cw = /sw|nw|w/.test(a), ch = /nw|ne|n/.test(a);
		
		if (isminw && cw) data.left = dw - o.minWidth;
		if (ismaxw && cw) data.left = dw - o.maxWidth;
		if (isminh && ch)	data.top = dh - o.minHeight;
		if (ismaxh && ch)	data.top = dh - o.maxHeight;
		
		// fixing jump error on top/left - bug #2330
		var isNotwh = !data.width && !data.height;
		if (isNotwh && !data.left && data.top) data.top = null;
		else if (isNotwh && !data.top && data.left) data.left = null;
		
		return data;
	},
	_proportionallyResize: function() {
		var o = this.options;
		if (!o.proportionallyResize) return;
		var prel = o.proportionallyResize, el = this.helper || this.element;
		
		if (!o.borderDif) {
			var b = [prel.css('borderTopWidth'), prel.css('borderRightWidth'), prel.css('borderBottomWidth'), prel.css('borderLeftWidth')],
				p = [prel.css('paddingTop'), prel.css('paddingRight'), prel.css('paddingBottom'), prel.css('paddingLeft')];
			
			o.borderDif = $.map(b, function(v, i) {
				var border = parseInt(v,10)||0, padding = parseInt(p[i],10)||0;
				return border + padding; 
			});
		}
		prel.css({
			height: (el.height() - o.borderDif[0] - o.borderDif[2]) + "px",
			width: (el.width() - o.borderDif[1] - o.borderDif[3]) + "px"
		});
	},
	_renderProxy: function() {
		var el = this.element, o = this.options;
		this.elementOffset = el.offset();
		
		if(o.helper) {
			this.helper = this.helper || $('<div style="overflow:hidden;"></div>');
			
			// fix ie6 offset
			var ie6 = $.browser.msie && $.browser.version < 7, ie6offset = (ie6 ? 1 : 0),
			pxyoffset = ( ie6 ? 2 : -1 );
			
			this.helper.addClass(o.helper).css({
				width: el.outerWidth() + pxyoffset,
				height: el.outerHeight() + pxyoffset,
				position: 'absolute',
				left: this.elementOffset.left - ie6offset +'px',
				top: this.elementOffset.top - ie6offset +'px',
				zIndex: ++o.zIndex
			});
			
			this.helper.appendTo("body");
			
			if (o.disableSelection)
				$.ui.disableSelection(this.helper.get(0));
			
		} else {
			this.helper = el; 
		}
	},
	_change: {
		e: function(e, dx, dy) {
			return { width: this.originalSize.width + dx };
		},
		w: function(e, dx, dy) {
			var o = this.options, cs = this.originalSize, sp = this.originalPosition;
			return { left: sp.left + dx, width: cs.width - dx };
		},
		n: function(e, dx, dy) {
			var o = this.options, cs = this.originalSize, sp = this.originalPosition;
			return { top: sp.top + dy, height: cs.height - dy };
		},
		s: function(e, dx, dy) {
			return { height: this.originalSize.height + dy };
		},
		se: function(e, dx, dy) {
			return $.extend(this._change.s.apply(this, arguments), this._change.e.apply(this, [e, dx, dy]));
		},
		sw: function(e, dx, dy) {
			return $.extend(this._change.s.apply(this, arguments), this._change.w.apply(this, [e, dx, dy]));
		},
		ne: function(e, dx, dy) {
			return $.extend(this._change.n.apply(this, arguments), this._change.e.apply(this, [e, dx, dy]));
		},
		nw: function(e, dx, dy) {
			return $.extend(this._change.n.apply(this, arguments), this._change.w.apply(this, [e, dx, dy]));
		}
	}
}));

$.extend($.ui.resizable, {
	defaults: {
		cancel: ":input",
		distance: 1,
		delay: 0,
		preventDefault: true,
		transparent: false,
		minWidth: 10,
		minHeight: 10,
		aspectRatio: false,
		disableSelection: true,
		preserveCursor: true,
		autoHide: false,
		knobHandles: false
	}
});

/*
 * Resizable Extensions
 */

$.ui.plugin.add("resizable", "containment", {
	
	start: function(e, ui) {
		var o = ui.options, self = $(this).data("resizable"), el = self.element;
		var oc = o.containment,	ce = (oc instanceof $) ? oc.get(0) : (/parent/.test(oc)) ? el.parent().get(0) : oc;
		if (!ce) return;
		
		self.containerElement = $(ce);
		
		if (/document/.test(oc) || oc == document) {
			self.containerOffset = { left: 0, top: 0 };
			self.containerPosition = { left: 0, top: 0 };
			
			self.parentData = { 
				element: $(document), left: 0, top: 0, 
				width: $(document).width(), height: $(document).height() || document.body.parentNode.scrollHeight
			};
		}
		
				
		// i'm a node, so compute top, left, right, bottom
		else{
			self.containerOffset = $(ce).offset();
			self.containerPosition = $(ce).position();
			self.containerSize = { height: $(ce).innerHeight(), width: $(ce).innerWidth() };
		
			var co = self.containerOffset, ch = self.containerSize.height,	cw = self.containerSize.width, 
						width = ($.ui.hasScroll(ce, "left") ? ce.scrollWidth : cw ), height = ($.ui.hasScroll(ce) ? ce.scrollHeight : ch);
		
			self.parentData = { 
				element: ce, left: co.left, top: co.top, width: width, height: height
			};
		}
	},
	
	resize: function(e, ui) {
		var o = ui.options, self = $(this).data("resizable"), 
				ps = self.containerSize, co = self.containerOffset, cs = self.size, cp = self.position,
				pRatio = o._aspectRatio || e.shiftKey, cop = { top:0, left:0 }, ce = self.containerElement;
		
		if (ce[0] != document && /static/.test(ce.css('position')))
			cop = self.containerPosition;
		
		if (cp.left < (o.helper ? co.left : cop.left)) {
			self.size.width = self.size.width + (o.helper ? (self.position.left - co.left) : (self.position.left - cop.left));
			if (pRatio) self.size.height = self.size.width * o.aspectRatio;
			self.position.left = o.helper ? co.left : cop.left;
		}
		
		if (cp.top < (o.helper ? co.top : 0)) {
			self.size.height = self.size.height + (o.helper ? (self.position.top - co.top) : self.position.top);
			if (pRatio) self.size.width = self.size.height / o.aspectRatio;
			self.position.top = o.helper ? co.top : 0;
		}
		
		var woset = (o.helper ? self.offset.left - co.left : (self.position.left - cop.left)) + self.sizeDiff.width, 
					hoset = (o.helper ? self.offset.top - co.top : self.position.top) + self.sizeDiff.height;
		
		if (woset + self.size.width >= self.parentData.width) {
			self.size.width = self.parentData.width - woset;
			if (pRatio) self.size.height = self.size.width * o.aspectRatio;
		}
		
		if (hoset + self.size.height >= self.parentData.height) {
			self.size.height = self.parentData.height - hoset;
			if (pRatio) self.size.width = self.size.height / o.aspectRatio;
		}
	},
	
	stop: function(e, ui){
		var o = ui.options, self = $(this).data("resizable"), cp = self.position,
				co = self.containerOffset, cop = self.containerPosition, ce = self.containerElement;
		
		var helper = $(self.helper), ho = helper.offset(), w = helper.innerWidth(), h = helper.innerHeight();
		
		
		if (o.helper && !o.animate && /relative/.test(ce.css('position')))
			$(this).css({ left: (ho.left - co.left), top: (ho.top - co.top), width: w, height: h });
		
		if (o.helper && !o.animate && /static/.test(ce.css('position')))
			$(this).css({ left: cop.left + (ho.left - co.left), top: cop.top + (ho.top - co.top), width: w, height: h });
		
	}
});

$.ui.plugin.add("resizable", "grid", {
	
	resize: function(e, ui) {
		var o = ui.options, self = $(this).data("resizable"), cs = self.size, os = self.originalSize, op = self.originalPosition, a = self.axis, ratio = o._aspectRatio || e.shiftKey;
		o.grid = typeof o.grid == "number" ? [o.grid, o.grid] : o.grid;
		var ox = Math.round((cs.width - os.width) / (o.grid[0]||1)) * (o.grid[0]||1), oy = Math.round((cs.height - os.height) / (o.grid[1]||1)) * (o.grid[1]||1);
		
		if (/^(se|s|e)$/.test(a)) {
			self.size.width = os.width + ox;
			self.size.height = os.height + oy;
		}
		else if (/^(ne)$/.test(a)) {
			self.size.width = os.width + ox;
			self.size.height = os.height + oy;
			self.position.top = op.top - oy;
		}
		else if (/^(sw)$/.test(a)) {
			self.size.width = os.width + ox;
			self.size.height = os.height + oy;
			self.position.left = op.left - ox;
		}
		else {
			self.size.width = os.width + ox;
			self.size.height = os.height + oy;
			self.position.top = op.top - oy;
			self.position.left = op.left - ox;
		}
	}
	
});

$.ui.plugin.add("resizable", "animate", {
	
	stop: function(e, ui) {
		var o = ui.options, self = $(this).data("resizable");
		
		var pr = o.proportionallyResize, ista = pr && (/textarea/i).test(pr.get(0).nodeName), 
						soffseth = ista && $.ui.hasScroll(pr.get(0), 'left') /* TODO - jump height */ ? 0 : self.sizeDiff.height,
							soffsetw = ista ? 0 : self.sizeDiff.width;
		
		var style = { width: (self.size.width - soffsetw), height: (self.size.height - soffseth) },
					left = (parseInt(self.element.css('left'), 10) + (self.position.left - self.originalPosition.left)) || null, 
						top = (parseInt(self.element.css('top'), 10) + (self.position.top - self.originalPosition.top)) || null; 
		
		self.element.animate(
			$.extend(style, top && left ? { top: top, left: left } : {}), { 
				duration: o.animateDuration || "slow", easing: o.animateEasing || "swing", 
				step: function() {
					
					var data = {
						width: parseInt(self.element.css('width'), 10),
						height: parseInt(self.element.css('height'), 10),
						top: parseInt(self.element.css('top'), 10),
						left: parseInt(self.element.css('left'), 10)
					};
					
					if (pr) pr.css({ width: data.width, height: data.height });
					
					// propagating resize, and updating values for each animation step
					self._updateCache(data);
					self.propagate("animate", e);
					
				}
			}
		);
	}
	
});

$.ui.plugin.add("resizable", "ghost", {
	
	start: function(e, ui) {
		var o = ui.options, self = $(this).data("resizable"), pr = o.proportionallyResize, cs = self.size;
		
		if (!pr) self.ghost = self.element.clone();
		else self.ghost = pr.clone();
		
		self.ghost.css(
			{ opacity: .25, display: 'block', position: 'relative', height: cs.height, width: cs.width, margin: 0, left: 0, top: 0 }
		)
		.addClass('ui-resizable-ghost').addClass(typeof o.ghost == 'string' ? o.ghost : '');
		
		self.ghost.appendTo(self.helper);
		
	},
	
	resize: function(e, ui){
		var o = ui.options, self = $(this).data("resizable"), pr = o.proportionallyResize;
		
		if (self.ghost) self.ghost.css({ position: 'relative', height: self.size.height, width: self.size.width });
		
	},
	
	stop: function(e, ui){
		var o = ui.options, self = $(this).data("resizable"), pr = o.proportionallyResize;
		if (self.ghost && self.helper) self.helper.get(0).removeChild(self.ghost.get(0));
	}
	
});

$.ui.plugin.add("resizable", "alsoResize", {
	
	start: function(e, ui) {
		var o = ui.options, self = $(this).data("resizable"), 
		
		_store = function(exp) {
			$(exp).each(function() {
				$(this).data("resizable-alsoresize", {
					width: parseInt($(this).width(), 10), height: parseInt($(this).height(), 10),
					left: parseInt($(this).css('left'), 10), top: parseInt($(this).css('top'), 10)
				});
			});
		};
		
		if (typeof(o.alsoResize) == 'object') {
			if (o.alsoResize.length) { o.alsoResize = o.alsoResize[0];	_store(o.alsoResize); }
			else { $.each(o.alsoResize, function(exp, c) { _store(exp); }); }
		}else{
			_store(o.alsoResize);
		} 
	},
	
	resize: function(e, ui){
		var o = ui.options, self = $(this).data("resizable"), os = self.originalSize, op = self.originalPosition;
		
		var delta = { 
			height: (self.size.height - os.height) || 0, width: (self.size.width - os.width) || 0,
			top: (self.position.top - op.top) || 0, left: (self.position.left - op.left) || 0
		},
		
		_alsoResize = function(exp, c) {
			$(exp).each(function() {
				var start = $(this).data("resizable-alsoresize"), style = {}, css = c && c.length ? c : ['width', 'height', 'top', 'left'];
				
				$.each(css || ['width', 'height', 'top', 'left'], function(i, prop) {
					var sum = (start[prop]||0) + (delta[prop]||0);
					if (sum && sum >= 0)
						style[prop] = sum || null;
				});
				$(this).css(style);
			});
		};
		
		if (typeof(o.alsoResize) == 'object') {
			$.each(o.alsoResize, function(exp, c) { _alsoResize(exp, c); });
		}else{
			_alsoResize(o.alsoResize);
		}
	},
	
	stop: function(e, ui){
		$(this).removeData("resizable-alsoresize-start");
	}
});

})(jQuery);
/*
 * jQuery UI Selectable
 *
 * Copyright (c) 2008 Richard D. Worth (rdworth.org)
 * Dual licensed under the MIT (MIT-LICENSE.txt)
 * and GPL (GPL-LICENSE.txt) licenses.
 * 
 * http://docs.jquery.com/UI/Selectables
 *
 * Depends:
 *	ui.core.js
 */
(function($) {

$.widget("ui.selectable", $.extend({}, $.ui.mouse, {
	init: function() {
		var self = this;
		
		this.element.addClass("ui-selectable");
		
		this.dragged = false;

		// cache selectee children based on filter
		var selectees;
		this.refresh = function() {
			selectees = $(self.options.filter, self.element[0]);
			selectees.each(function() {
				var $this = $(this);
				var pos = $this.offset();
				$.data(this, "selectable-item", {
					element: this,
					$element: $this,
					left: pos.left,
					top: pos.top,
					right: pos.left + $this.width(),
					bottom: pos.top + $this.height(),
					startselected: false,
					selected: $this.hasClass('ui-selected'),
					selecting: $this.hasClass('ui-selecting'),
					unselecting: $this.hasClass('ui-unselecting')
				});
			});
		};
		this.refresh();

		this.selectees = selectees.addClass("ui-selectee");
		
		this.mouseInit();
		
		this.helper = $(document.createElement('div')).css({border:'1px dotted black'});
	},
	toggle: function() {
		if(this.options.disabled){
			this.enable();
		} else {
			this.disable();
		}
	},
	destroy: function() {
		this.element
			.removeClass("ui-selectable ui-selectable-disabled")
			.removeData("selectable")
			.unbind(".selectable");
		this.mouseDestroy();
	},
	mouseStart: function(e) {
		var self = this;
		
		this.opos = [e.pageX, e.pageY];
		
		if (this.options.disabled)
			return;

		var options = this.options;

		this.selectees = $(options.filter, this.element[0]);

		// selectable START callback
		this.element.triggerHandler("selectablestart", [e, {
			"selectable": this.element[0],
			"options": options
		}], options.start);

		$('body').append(this.helper);
		// position helper (lasso)
		this.helper.css({
			"z-index": 100,
			"position": "absolute",
			"left": e.clientX,
			"top": e.clientY,
			"width": 0,
			"height": 0
		});

		if (options.autoRefresh) {
			this.refresh();
		}

		this.selectees.filter('.ui-selected').each(function() {
			var selectee = $.data(this, "selectable-item");
			selectee.startselected = true;
			if (!e.ctrlKey) {
				selectee.$element.removeClass('ui-selected');
				selectee.selected = false;
				selectee.$element.addClass('ui-unselecting');
				selectee.unselecting = true;
				// selectable UNSELECTING callback
				self.element.triggerHandler("selectableunselecting", [e, {
					selectable: self.element[0],
					unselecting: selectee.element,
					options: options
				}], options.unselecting);
			}
		});
		
		var isSelectee = false;
		$(e.target).parents().andSelf().each(function() {
			if($.data(this, "selectable-item")) isSelectee = true;
		});
		return this.options.keyboard ? !isSelectee : true;
	},
	mouseDrag: function(e) {
		var self = this;
		this.dragged = true;
		
		if (this.options.disabled)
			return;

		var options = this.options;

		var x1 = this.opos[0], y1 = this.opos[1], x2 = e.pageX, y2 = e.pageY;
		if (x1 > x2) { var tmp = x2; x2 = x1; x1 = tmp; }
		if (y1 > y2) { var tmp = y2; y2 = y1; y1 = tmp; }
		this.helper.css({left: x1, top: y1, width: x2-x1, height: y2-y1});

		this.selectees.each(function() {
			var selectee = $.data(this, "selectable-item");
			//prevent helper from being selected if appendTo: selectable
			if (!selectee || selectee.element == self.element[0])
				return;
			var hit = false;
			if (options.tolerance == 'touch') {
				hit = ( !(selectee.left > x2 || selectee.right < x1 || selectee.top > y2 || selectee.bottom < y1) );
			} else if (options.tolerance == 'fit') {
				hit = (selectee.left > x1 && selectee.right < x2 && selectee.top > y1 && selectee.bottom < y2);
			}

			if (hit) {
				// SELECT
				if (selectee.selected) {
					selectee.$element.removeClass('ui-selected');
					selectee.selected = false;
				}
				if (selectee.unselecting) {
					selectee.$element.removeClass('ui-unselecting');
					selectee.unselecting = false;
				}
				if (!selectee.selecting) {
					selectee.$element.addClass('ui-selecting');
					selectee.selecting = true;
					// selectable SELECTING callback
					self.element.triggerHandler("selectableselecting", [e, {
						selectable: self.element[0],
						selecting: selectee.element,
						options: options
					}], options.selecting);
				}
			} else {
				// UNSELECT
				if (selectee.selecting) {
					if (e.ctrlKey && selectee.startselected) {
						selectee.$element.removeClass('ui-selecting');
						selectee.selecting = false;
						selectee.$element.addClass('ui-selected');
						selectee.selected = true;
					} else {
						selectee.$element.removeClass('ui-selecting');
						selectee.selecting = false;
						if (selectee.startselected) {
							selectee.$element.addClass('ui-unselecting');
							selectee.unselecting = true;
						}
						// selectable UNSELECTING callback
						self.element.triggerHandler("selectableunselecting", [e, {
							selectable: self.element[0],
							unselecting: selectee.element,
							options: options
						}], options.unselecting);
					}
				}
				if (selectee.selected) {
					if (!e.ctrlKey && !selectee.startselected) {
						selectee.$element.removeClass('ui-selected');
						selectee.selected = false;

						selectee.$element.addClass('ui-unselecting');
						selectee.unselecting = true;
						// selectable UNSELECTING callback
						self.element.triggerHandler("selectableunselecting", [e, {
							selectable: self.element[0],
							unselecting: selectee.element,
							options: options
						}], options.unselecting);
					}
				}
			}
		});
		
		return false;
	},
	mouseStop: function(e) {
		var self = this;
		
		this.dragged = false;
		
		var options = this.options;

		$('.ui-unselecting', this.element[0]).each(function() {
			var selectee = $.data(this, "selectable-item");
			selectee.$element.removeClass('ui-unselecting');
			selectee.unselecting = false;
			selectee.startselected = false;
			self.element.triggerHandler("selectableunselected", [e, {
				selectable: self.element[0],
				unselected: selectee.element,
				options: options
			}], options.unselected);
		});
		$('.ui-selecting', this.element[0]).each(function() {
			var selectee = $.data(this, "selectable-item");
			selectee.$element.removeClass('ui-selecting').addClass('ui-selected');
			selectee.selecting = false;
			selectee.selected = true;
			selectee.startselected = true;
			self.element.triggerHandler("selectableselected", [e, {
				selectable: self.element[0],
				selected: selectee.element,
				options: options
			}], options.selected);
		});
		this.element.triggerHandler("selectablestop", [e, {
			selectable: self.element[0],
			options: this.options
		}], this.options.stop);
		
		this.helper.remove();
		
		return false;
	}
}));

$.extend($.ui.selectable, {
	defaults: {
		distance: 1,
		delay: 0,
		cancel: ":input",
		appendTo: 'body',
		autoRefresh: true,
		filter: '*',
		tolerance: 'touch'
	}
});

})(jQuery);
/*
 * jQuery UI Sortable
 *
 * Copyright (c) 2008 Paul Bakaus
 * Dual licensed under the MIT (MIT-LICENSE.txt)
 * and GPL (GPL-LICENSE.txt) licenses.
 * 
 * http://docs.jquery.com/UI/Sortables
 *
 * Depends:
 *	ui.core.js
 */
(function($) {

function contains(a, b) { 
    var safari2 = $.browser.safari && $.browser.version < 522; 
    if (a.contains && !safari2) { 
        return a.contains(b); 
    } 
    if (a.compareDocumentPosition) 
        return !!(a.compareDocumentPosition(b) & 16); 
    while (b = b.parentNode) 
          if (b == a) return true; 
    return false; 
};

$.widget("ui.sortable", $.extend({}, $.ui.mouse, {
	init: function() {

		var o = this.options;
		this.containerCache = {};
		this.element.addClass("ui-sortable");
	
		//Get the items
		this.refresh();

		//Let's determine if the items are floating
		this.floating = this.items.length ? (/left|right/).test(this.items[0].item.css('float')) : false;
		
		//Let's determine the parent's offset
		if(!(/(relative|absolute|fixed)/).test(this.element.css('position'))) this.element.css('position', 'relative');
		this.offset = this.element.offset();

		//Initialize mouse events for interaction
		this.mouseInit();
		
	},
	plugins: {},
	ui: function(inst) {
		return {
			helper: (inst || this)["helper"],
			placeholder: (inst || this)["placeholder"] || $([]),
			position: (inst || this)["position"],
			absolutePosition: (inst || this)["positionAbs"],
			options: this.options,
			element: this.element,
			item: (inst || this)["currentItem"],
			sender: inst ? inst.element : null
		};		
	},
	propagate: function(n,e,inst, noPropagation) {
		$.ui.plugin.call(this, n, [e, this.ui(inst)]);
		if(!noPropagation) this.element.triggerHandler(n == "sort" ? n : "sort"+n, [e, this.ui(inst)], this.options[n]);
	},
	serialize: function(o) {

		var items = ($.isFunction(this.options.items) ? this.options.items.call(this.element) : $(this.options.items, this.element)).not('.ui-sortable-helper'); //Only the items of the sortable itself
		var str = []; o = o || {};
		
		items.each(function() {
			var res = ($(this).attr(o.attribute || 'id') || '').match(o.expression || (/(.+)[-=_](.+)/));
			if(res) str.push((o.key || res[1])+'[]='+(o.key && o.expression ? res[1] : res[2]));
		});
		
		return str.join('&');
		
	},
	toArray: function(attr) {
		
		var items = ($.isFunction(this.options.items) ? this.options.items.call(this.element) : $(this.options.items, this.element)).not('.ui-sortable-helper'); //Only the items of the sortable itself
		var ret = [];

		items.each(function() { ret.push($(this).attr(attr || 'id')); });
		return ret;
		
	},
	/* Be careful with the following core functions */
	intersectsWith: function(item) {
		
		var x1 = this.positionAbs.left, x2 = x1 + this.helperProportions.width,
		y1 = this.positionAbs.top, y2 = y1 + this.helperProportions.height;
		var l = item.left, r = l + item.width, 
		t = item.top, b = t + item.height;

		if(this.options.tolerance == "pointer" || this.options.forcePointerForContainers || (this.options.tolerance == "guess" && this.helperProportions[this.floating ? 'width' : 'height'] > item[this.floating ? 'width' : 'height'])) {
			return (y1 + this.offset.click.top > t && y1 + this.offset.click.top < b && x1 + this.offset.click.left > l && x1 + this.offset.click.left < r);
		} else {
		
			return (l < x1 + (this.helperProportions.width / 2) // Right Half
				&& x2 - (this.helperProportions.width / 2) < r // Left Half
				&& t < y1 + (this.helperProportions.height / 2) // Bottom Half
				&& y2 - (this.helperProportions.height / 2) < b ); // Top Half
		
		}
		
	},
	intersectsWithEdge: function(item) {	
		var x1 = this.positionAbs.left, x2 = x1 + this.helperProportions.width,
			y1 = this.positionAbs.top, y2 = y1 + this.helperProportions.height;
		var l = item.left, r = l + item.width, 
			t = item.top, b = t + item.height;

		if(this.options.tolerance == "pointer" || (this.options.tolerance == "guess" && this.helperProportions[this.floating ? 'width' : 'height'] > item[this.floating ? 'width' : 'height'])) {

			if(!(y1 + this.offset.click.top > t && y1 + this.offset.click.top < b && x1 + this.offset.click.left > l && x1 + this.offset.click.left < r)) return false;
			
			if(this.floating) {
				if(x1 + this.offset.click.left > l && x1 + this.offset.click.left < l + item.width/2) return 2;
				if(x1 + this.offset.click.left > l+item.width/2 && x1 + this.offset.click.left < r) return 1;
			} else {
				if(y1 + this.offset.click.top > t && y1 + this.offset.click.top < t + item.height/2) return 2;
				if(y1 + this.offset.click.top > t+item.height/2 && y1 + this.offset.click.top < b) return 1;
			}

		} else {
		
			if (!(l < x1 + (this.helperProportions.width / 2) // Right Half
				&& x2 - (this.helperProportions.width / 2) < r // Left Half
				&& t < y1 + (this.helperProportions.height / 2) // Bottom Half
				&& y2 - (this.helperProportions.height / 2) < b )) return false; // Top Half
			
			if(this.floating) {
				if(x2 > l && x1 < l) return 2; //Crosses left edge
				if(x1 < r && x2 > r) return 1; //Crosses right edge
			} else {
				if(y2 > t && y1 < t) return 1; //Crosses top edge
				if(y1 < b && y2 > b) return 2; //Crosses bottom edge
			}
		
		}
		
		return false;
		
	},
	refresh: function() {
		this.refreshItems();
		this.refreshPositions();
	},
	refreshItems: function() {
		
		this.items = [];
		this.containers = [this];
		var items = this.items;
		var self = this;
		var queries = [[$.isFunction(this.options.items) ? this.options.items.call(this.element, null, { options: this.options, item: this.currentItem }) : $(this.options.items, this.element), this]];
	
		if(this.options.connectWith) {
			for (var i = this.options.connectWith.length - 1; i >= 0; i--){
				var cur = $(this.options.connectWith[i]);
				for (var j = cur.length - 1; j >= 0; j--){
					var inst = $.data(cur[j], 'sortable');
					if(inst && !inst.options.disabled) {
						queries.push([$.isFunction(inst.options.items) ? inst.options.items.call(inst.element) : $(inst.options.items, inst.element), inst]);
						this.containers.push(inst);
					}
				};
			};
		}

		for (var i = queries.length - 1; i >= 0; i--){
			queries[i][0].each(function() {
				$.data(this, 'sortable-item', queries[i][1]); // Data for target checking (mouse manager)
				items.push({
					item: $(this),
					instance: queries[i][1],
					width: 0, height: 0,
					left: 0, top: 0
				});
			});
		};

	},
	refreshPositions: function(fast) {

		//This has to be redone because due to the item being moved out/into the offsetParent, the offsetParent's position will change
		if(this.offsetParent) {
			var po = this.offsetParent.offset();
			this.offset.parent = { top: po.top + this.offsetParentBorders.top, left: po.left + this.offsetParentBorders.left };
		}

		for (var i = this.items.length - 1; i >= 0; i--){		
			
			//We ignore calculating positions of all connected containers when we're not over them
			if(this.items[i].instance != this.currentContainer && this.currentContainer && this.items[i].item[0] != this.currentItem[0])
				continue;
				
			var t = this.options.toleranceElement ? $(this.options.toleranceElement, this.items[i].item) : this.items[i].item;
			
			if(!fast) {
				this.items[i].width = t[0].offsetWidth;
				this.items[i].height = t[0].offsetHeight;
			}
			
			var p = t.offset();
			this.items[i].left = p.left;
			this.items[i].top = p.top;
			
		};

		if(this.options.custom && this.options.custom.refreshContainers) {
			this.options.custom.refreshContainers.call(this);
		} else {
			for (var i = this.containers.length - 1; i >= 0; i--){
				var p =this.containers[i].element.offset();
				this.containers[i].containerCache.left = p.left;
				this.containers[i].containerCache.top = p.top;
				this.containers[i].containerCache.width	= this.containers[i].element.outerWidth();
				this.containers[i].containerCache.height = this.containers[i].element.outerHeight();
			};
		}

	},
	destroy: function() {
		this.element
			.removeClass("ui-sortable ui-sortable-disabled")
			.removeData("sortable")
			.unbind(".sortable");
		this.mouseDestroy();
		
		for ( var i = this.items.length - 1; i >= 0; i-- )
			this.items[i].item.removeData("sortable-item");
	},
	createPlaceholder: function(that) {
		
		var self = that || this, o = self.options;

		if(o.placeholder.constructor == String) {
			var className = o.placeholder;
			o.placeholder = {
				element: function() {
					return $('<div></div>').addClass(className)[0];
				},
				update: function(i, p) {
					p.css(i.offset()).css({ width: i.outerWidth(), height: i.outerHeight() });
				}
			};
		}
		
		self.placeholder = $(o.placeholder.element.call(self.element, self.currentItem)).appendTo('body').css({ position: 'absolute' });
		o.placeholder.update.call(self.element, self.currentItem, self.placeholder);
	},
	contactContainers: function(e) {
		for (var i = this.containers.length - 1; i >= 0; i--){

			if(this.intersectsWith(this.containers[i].containerCache)) {
				if(!this.containers[i].containerCache.over) {
					

					if(this.currentContainer != this.containers[i]) {
						
						//When entering a new container, we will find the item with the least distance and append our item near it
						var dist = 10000; var itemWithLeastDistance = null; var base = this.positionAbs[this.containers[i].floating ? 'left' : 'top'];
						for (var j = this.items.length - 1; j >= 0; j--) {
							if(!contains(this.containers[i].element[0], this.items[j].item[0])) continue;
							var cur = this.items[j][this.containers[i].floating ? 'left' : 'top'];
							if(Math.abs(cur - base) < dist) {
								dist = Math.abs(cur - base); itemWithLeastDistance = this.items[j];
							}
						}
						
						if(!itemWithLeastDistance && !this.options.dropOnEmpty) //Check if dropOnEmpty is enabled
							continue;
						
						//We also need to exchange the placeholder
						if(this.placeholder) this.placeholder.remove();
						if(this.containers[i].options.placeholder) {
							this.containers[i].createPlaceholder(this);
						} else {
							this.placeholder = null;;
						}
						
						this.currentContainer = this.containers[i];
						itemWithLeastDistance ? this.rearrange(e, itemWithLeastDistance, null, true) : this.rearrange(e, null, this.containers[i].element, true);
						this.propagate("change", e); //Call plugins and callbacks
						this.containers[i].propagate("change", e, this); //Call plugins and callbacks

					}
					
					this.containers[i].propagate("over", e, this);
					this.containers[i].containerCache.over = 1;
				}
			} else {
				if(this.containers[i].containerCache.over) {
					this.containers[i].propagate("out", e, this);
					this.containers[i].containerCache.over = 0;
				}
			}
			
		};			
	},
	mouseCapture: function(e, overrideHandle) {
	
		if(this.options.disabled || this.options.type == 'static') return false;

		//We have to refresh the items data once first
		this.refreshItems();

		//Find out if the clicked node (or one of its parents) is a actual item in this.items
		var currentItem = null, self = this, nodes = $(e.target).parents().each(function() {	
			if($.data(this, 'sortable-item') == self) {
				currentItem = $(this);
				return false;
			}
		});
		if($.data(e.target, 'sortable-item') == self) currentItem = $(e.target);

		if(!currentItem) return false;
		if(this.options.handle && !overrideHandle) {
			var validHandle = false;
			
			$(this.options.handle, currentItem).find("*").andSelf().each(function() { if(this == e.target) validHandle = true; });
			if(!validHandle) return false;
		}
			
		this.currentItem = currentItem;
		return true;	
			
	},
	mouseStart: function(e, overrideHandle, noActivation) {

		var o = this.options;
		this.currentContainer = this;

		//We only need to call refreshPositions, because the refreshItems call has been moved to mouseCapture
		this.refreshPositions();

		//Create and append the visible helper			
		this.helper = typeof o.helper == 'function' ? $(o.helper.apply(this.element[0], [e, this.currentItem])) : this.currentItem.clone();
		if (!this.helper.parents('body').length) $(o.appendTo != 'parent' ? o.appendTo : this.currentItem[0].parentNode)[0].appendChild(this.helper[0]); //Add the helper to the DOM if that didn't happen already
		this.helper.css({ position: 'absolute', clear: 'both' }).addClass('ui-sortable-helper'); //Position it absolutely and add a helper class

		/*
		 * - Position generation -
		 * This block generates everything position related - it's the core of draggables.
		 */

		this.margins = {																				//Cache the margins
			left: (parseInt(this.currentItem.css("marginLeft"),10) || 0),
			top: (parseInt(this.currentItem.css("marginTop"),10) || 0)
		};		
	
		this.offset = this.currentItem.offset();														//The element's absolute position on the page
		this.offset = {																					//Substract the margins from the element's absolute offset
			top: this.offset.top - this.margins.top,
			left: this.offset.left - this.margins.left
		};
		
		this.offset.click = {																			//Where the click happened, relative to the element
			left: e.pageX - this.offset.left,
			top: e.pageY - this.offset.top
		};
		
		this.offsetParent = this.helper.offsetParent();													//Get the offsetParent and cache its position
		var po = this.offsetParent.offset();			

		this.offsetParentBorders = {
			top: (parseInt(this.offsetParent.css("borderTopWidth"),10) || 0),
			left: (parseInt(this.offsetParent.css("borderLeftWidth"),10) || 0)
		};
		this.offset.parent = {																			//Store its position plus border
			top: po.top + this.offsetParentBorders.top,
			left: po.left + this.offsetParentBorders.left
		};
	
		this.originalPosition = this.generatePosition(e);												//Generate the original position
		this.domPosition = { prev: this.currentItem.prev()[0], parent: this.currentItem.parent()[0] };  //Cache the former DOM position
		
		//If o.placeholder is used, create a new element at the given position with the class
		this.helperProportions = { width: this.helper.outerWidth(), height: this.helper.outerHeight() };//Cache the helper size
		if(o.placeholder) this.createPlaceholder();
		
		//Call plugins and callbacks
		this.propagate("start", e);
		this.helperProportions = { width: this.helper.outerWidth(), height: this.helper.outerHeight() };//Recache the helper size
		
		if(o.cursorAt) {
			if(o.cursorAt.left != undefined) this.offset.click.left = o.cursorAt.left;
			if(o.cursorAt.right != undefined) this.offset.click.left = this.helperProportions.width - o.cursorAt.right;
			if(o.cursorAt.top != undefined) this.offset.click.top = o.cursorAt.top;
			if(o.cursorAt.bottom != undefined) this.offset.click.top = this.helperProportions.height - o.cursorAt.bottom;
		}

		/*
		 * - Position constraining -
		 * Here we prepare position constraining like grid and containment.
		 */	
		
		if(o.containment) {
			if(o.containment == 'parent') o.containment = this.helper[0].parentNode;
			if(o.containment == 'document' || o.containment == 'window') this.containment = [
				0 - this.offset.parent.left,
				0 - this.offset.parent.top,
				$(o.containment == 'document' ? document : window).width() - this.offset.parent.left - this.helperProportions.width - this.margins.left - (parseInt(this.element.css("marginRight"),10) || 0),
				($(o.containment == 'document' ? document : window).height() || document.body.parentNode.scrollHeight) - this.offset.parent.top - this.helperProportions.height - this.margins.top - (parseInt(this.element.css("marginBottom"),10) || 0)
			];

			if(!(/^(document|window|parent)$/).test(o.containment)) {
				var ce = $(o.containment)[0];
				var co = $(o.containment).offset();
				
				this.containment = [
					co.left + (parseInt($(ce).css("borderLeftWidth"),10) || 0) - this.offset.parent.left,
					co.top + (parseInt($(ce).css("borderTopWidth"),10) || 0) - this.offset.parent.top,
					co.left+Math.max(ce.scrollWidth,ce.offsetWidth) - (parseInt($(ce).css("borderLeftWidth"),10) || 0) - this.offset.parent.left - this.helperProportions.width - this.margins.left - (parseInt(this.currentItem.css("marginRight"),10) || 0),
					co.top+Math.max(ce.scrollHeight,ce.offsetHeight) - (parseInt($(ce).css("borderTopWidth"),10) || 0) - this.offset.parent.top - this.helperProportions.height - this.margins.top - (parseInt(this.currentItem.css("marginBottom"),10) || 0)
				];
			}
		}

		//Set the original element visibility to hidden to still fill out the white space
		if(this.options.placeholder != 'clone')
			this.currentItem.css('visibility', 'hidden');
		
		//Post 'activate' events to possible containers
		if(!noActivation) {
			 for (var i = this.containers.length - 1; i >= 0; i--) { this.containers[i].propagate("activate", e, this); }
		}
		
		//Prepare possible droppables
		if($.ui.ddmanager) $.ui.ddmanager.current = this;
		if ($.ui.ddmanager && !o.dropBehaviour) $.ui.ddmanager.prepareOffsets(this, e);

		this.dragging = true;

		this.mouseDrag(e); //Execute the drag once - this causes the helper not to be visible before getting its correct position
		return true;


	},
	convertPositionTo: function(d, pos) {
		if(!pos) pos = this.position;
		var mod = d == "absolute" ? 1 : -1;
		return {
			top: (
				pos.top																	// the calculated relative position
				+ this.offset.parent.top * mod											// The offsetParent's offset without borders (offset + border)
				- (this.offsetParent[0] == document.body ? 0 : this.offsetParent[0].scrollTop) * mod	// The offsetParent's scroll position
				+ this.margins.top * mod												//Add the margin (you don't want the margin counting in intersection methods)
			),
			left: (
				pos.left																// the calculated relative position
				+ this.offset.parent.left * mod											// The offsetParent's offset without borders (offset + border)
				- (this.offsetParent[0] == document.body ? 0 : this.offsetParent[0].scrollLeft) * mod	// The offsetParent's scroll position
				+ this.margins.left * mod												//Add the margin (you don't want the margin counting in intersection methods)
			)
		};
	},
	generatePosition: function(e) {
		
		var o = this.options;
		var position = {
			top: (
				e.pageY																	// The absolute mouse position
				- this.offset.click.top													// Click offset (relative to the element)
				- this.offset.parent.top												// The offsetParent's offset without borders (offset + border)
				+ (this.offsetParent[0] == document.body ? 0 : this.offsetParent[0].scrollTop)	// The offsetParent's scroll position, not if the element is fixed
			),
			left: (
				e.pageX																	// The absolute mouse position
				- this.offset.click.left												// Click offset (relative to the element)
				- this.offset.parent.left												// The offsetParent's offset without borders (offset + border)
				+ (this.offsetParent[0] == document.body ? 0 : this.offsetParent[0].scrollLeft)	// The offsetParent's scroll position, not if the element is fixed
			)
		};
		
		if(!this.originalPosition) return position;										//If we are not dragging yet, we won't check for options
		
		/*
		 * - Position constraining -
		 * Constrain the position to a mix of grid, containment.
		 */
		if(this.containment) {
			if(position.left < this.containment[0]) position.left = this.containment[0];
			if(position.top < this.containment[1]) position.top = this.containment[1];
			if(position.left > this.containment[2]) position.left = this.containment[2];
			if(position.top > this.containment[3]) position.top = this.containment[3];
		}
		
		if(o.grid) {
			var top = this.originalPosition.top + Math.round((position.top - this.originalPosition.top) / o.grid[1]) * o.grid[1];
			position.top = this.containment ? (!(top < this.containment[1] || top > this.containment[3]) ? top : (!(top < this.containment[1]) ? top - o.grid[1] : top + o.grid[1])) : top;
			
			var left = this.originalPosition.left + Math.round((position.left - this.originalPosition.left) / o.grid[0]) * o.grid[0];
			position.left = this.containment ? (!(left < this.containment[0] || left > this.containment[2]) ? left : (!(left < this.containment[0]) ? left - o.grid[0] : left + o.grid[0])) : left;
		}
		
		return position;
	},
	mouseDrag: function(e) {

		//Compute the helpers position
		this.position = this.generatePosition(e);
		this.positionAbs = this.convertPositionTo("absolute");

		//Call the internal plugins
		$.ui.plugin.call(this, "sort", [e, this.ui()]);
		
		//Regenerate the absolute position used for position checks
		this.positionAbs = this.convertPositionTo("absolute");
		
		//Set the helper's position
		this.helper[0].style.left = this.position.left+'px';
		this.helper[0].style.top = this.position.top+'px';

		//Rearrange
		for (var i = this.items.length - 1; i >= 0; i--) {
			var intersection = this.intersectsWithEdge(this.items[i]);
			if(!intersection) continue;
			
			if(this.items[i].item[0] != this.currentItem[0] //cannot intersect with itself
				&&	this.currentItem[intersection == 1 ? "next" : "prev"]()[0] != this.items[i].item[0] //no useless actions that have been done before
				&&	!contains(this.currentItem[0], this.items[i].item[0]) //no action if the item moved is the parent of the item checked
				&& (this.options.type == 'semi-dynamic' ? !contains(this.element[0], this.items[i].item[0]) : true)
			) {
				
				this.direction = intersection == 1 ? "down" : "up";
				this.rearrange(e, this.items[i]);
				this.propagate("change", e); //Call plugins and callbacks
				break;
			}
		}
		
		//Post events to containers
		this.contactContainers(e);
		
		//Interconnect with droppables
		if($.ui.ddmanager) $.ui.ddmanager.drag(this, e);

		//Call callbacks
		this.element.triggerHandler("sort", [e, this.ui()], this.options["sort"]);

		return false;
		
	},
	rearrange: function(e, i, a, hardRefresh) {
		a ? a[0].appendChild(this.currentItem[0]) : i.item[0].parentNode.insertBefore(this.currentItem[0], (this.direction == 'down' ? i.item[0] : i.item[0].nextSibling));
		
		//Various things done here to improve the performance:
		// 1. we create a setTimeout, that calls refreshPositions
		// 2. on the instance, we have a counter variable, that get's higher after every append
		// 3. on the local scope, we copy the counter variable, and check in the timeout, if it's still the same
		// 4. this lets only the last addition to the timeout stack through
		this.counter = this.counter ? ++this.counter : 1;
		var self = this, counter = this.counter;

		window.setTimeout(function() {
			if(counter == self.counter) self.refreshPositions(!hardRefresh); //Precompute after each DOM insertion, NOT on mousemove
		},0);
		
		if(this.options.placeholder)
			this.options.placeholder.update.call(this.element, this.currentItem, this.placeholder);
	},
	mouseStop: function(e, noPropagation) {

		//If we are using droppables, inform the manager about the drop
		if ($.ui.ddmanager && !this.options.dropBehaviour)
			$.ui.ddmanager.drop(this, e);
			
		if(this.options.revert) {
			var self = this;
			var cur = self.currentItem.offset();

			//Also animate the placeholder if we have one
			if(self.placeholder) self.placeholder.animate({ opacity: 'hide' }, (parseInt(this.options.revert, 10) || 500)-50);

			$(this.helper).animate({
				left: cur.left - this.offset.parent.left - self.margins.left + (this.offsetParent[0] == document.body ? 0 : this.offsetParent[0].scrollLeft),
				top: cur.top - this.offset.parent.top - self.margins.top + (this.offsetParent[0] == document.body ? 0 : this.offsetParent[0].scrollTop)
			}, parseInt(this.options.revert, 10) || 500, function() {
				self.clear(e);
			});
		} else {
			this.clear(e, noPropagation);
		}

		return false;
		
	},
	clear: function(e, noPropagation) {

		if(this.domPosition.prev != this.currentItem.prev().not(".ui-sortable-helper")[0] || this.domPosition.parent != this.currentItem.parent()[0]) this.propagate("update", e, null, noPropagation); //Trigger update callback if the DOM position has changed
		if(!contains(this.element[0], this.currentItem[0])) { //Node was moved out of the current element
			this.propagate("remove", e, null, noPropagation);
			for (var i = this.containers.length - 1; i >= 0; i--){
				if(contains(this.containers[i].element[0], this.currentItem[0])) {
					this.containers[i].propagate("update", e, this, noPropagation);
					this.containers[i].propagate("receive", e, this, noPropagation);
				}
			};
		};
		
		//Post events to containers
		for (var i = this.containers.length - 1; i >= 0; i--){
			this.containers[i].propagate("deactivate", e, this, noPropagation);
			if(this.containers[i].containerCache.over) {
				this.containers[i].propagate("out", e, this);
				this.containers[i].containerCache.over = 0;
			}
		}
		
		this.dragging = false;
		if(this.cancelHelperRemoval) {
			this.propagate("stop", e, null, noPropagation);
			return false;
		}
		
		$(this.currentItem).css('visibility', '');
		if(this.placeholder) this.placeholder.remove();
		this.helper.remove(); this.helper = null;
		this.propagate("stop", e, null, noPropagation);
		
		return true;
		
	}
}));

$.extend($.ui.sortable, {
	getter: "serialize toArray",
	defaults: {
		helper: "clone",
		tolerance: "guess",
		distance: 1,
		delay: 0,
		scroll: true,
		scrollSensitivity: 20,
		scrollSpeed: 20,
		cancel: ":input",
		items: '> *',
		zIndex: 1000,
		dropOnEmpty: true,
		appendTo: "parent"
	}
});

/*
 * Sortable Extensions
 */

$.ui.plugin.add("sortable", "cursor", {
	start: function(e, ui) {
		var t = $('body');
		if (t.css("cursor")) ui.options._cursor = t.css("cursor");
		t.css("cursor", ui.options.cursor);
	},
	stop: function(e, ui) {
		if (ui.options._cursor) $('body').css("cursor", ui.options._cursor);
	}
});

$.ui.plugin.add("sortable", "zIndex", {
	start: function(e, ui) {
		var t = ui.helper;
		if(t.css("zIndex")) ui.options._zIndex = t.css("zIndex");
		t.css('zIndex', ui.options.zIndex);
	},
	stop: function(e, ui) {
		if(ui.options._zIndex) $(ui.helper).css('zIndex', ui.options._zIndex);
	}
});

$.ui.plugin.add("sortable", "opacity", {
	start: function(e, ui) {
		var t = ui.helper;
		if(t.css("opacity")) ui.options._opacity = t.css("opacity");
		t.css('opacity', ui.options.opacity);
	},
	stop: function(e, ui) {
		if(ui.options._opacity) $(ui.helper).css('opacity', ui.options._opacity);
	}
});

$.ui.plugin.add("sortable", "scroll", {
	start: function(e, ui) {
		var o = ui.options;
		var i = $(this).data("sortable");
	
		i.overflowY = function(el) {
			do { if(/auto|scroll/.test(el.css('overflow')) || (/auto|scroll/).test(el.css('overflow-y'))) return el; el = el.parent(); } while (el[0].parentNode);
			return $(document);
		}(i.currentItem);
		i.overflowX = function(el) {
			do { if(/auto|scroll/.test(el.css('overflow')) || (/auto|scroll/).test(el.css('overflow-x'))) return el; el = el.parent(); } while (el[0].parentNode);
			return $(document);
		}(i.currentItem);
		
		if(i.overflowY[0] != document && i.overflowY[0].tagName != 'HTML') i.overflowYOffset = i.overflowY.offset();
		if(i.overflowX[0] != document && i.overflowX[0].tagName != 'HTML') i.overflowXOffset = i.overflowX.offset();
		
	},
	sort: function(e, ui) {
		
		var o = ui.options;
		var i = $(this).data("sortable");
		
		if(i.overflowY[0] != document && i.overflowY[0].tagName != 'HTML') {
			if((i.overflowYOffset.top + i.overflowY[0].offsetHeight) - e.pageY < o.scrollSensitivity)
				i.overflowY[0].scrollTop = i.overflowY[0].scrollTop + o.scrollSpeed;
			if(e.pageY - i.overflowYOffset.top < o.scrollSensitivity)
				i.overflowY[0].scrollTop = i.overflowY[0].scrollTop - o.scrollSpeed;
		} else {
			if(e.pageY - $(document).scrollTop() < o.scrollSensitivity)
				$(document).scrollTop($(document).scrollTop() - o.scrollSpeed);
			if($(window).height() - (e.pageY - $(document).scrollTop()) < o.scrollSensitivity)
				$(document).scrollTop($(document).scrollTop() + o.scrollSpeed);
		}
		
		if(i.overflowX[0] != document && i.overflowX[0].tagName != 'HTML') {
			if((i.overflowXOffset.left + i.overflowX[0].offsetWidth) - e.pageX < o.scrollSensitivity)
				i.overflowX[0].scrollLeft = i.overflowX[0].scrollLeft + o.scrollSpeed;
			if(e.pageX - i.overflowXOffset.left < o.scrollSensitivity)
				i.overflowX[0].scrollLeft = i.overflowX[0].scrollLeft - o.scrollSpeed;
		} else {
			if(e.pageX - $(document).scrollLeft() < o.scrollSensitivity)
				$(document).scrollLeft($(document).scrollLeft() - o.scrollSpeed);
			if($(window).width() - (e.pageX - $(document).scrollLeft()) < o.scrollSensitivity)
				$(document).scrollLeft($(document).scrollLeft() + o.scrollSpeed);
		}
		
	}
});

$.ui.plugin.add("sortable", "axis", {
	sort: function(e, ui) {
		
		var i = $(this).data("sortable");
		
		if(ui.options.axis == "y") i.position.left = i.originalPosition.left;
		if(ui.options.axis == "x") i.position.top = i.originalPosition.top;
		
	}
});

})(jQuery);
/*
 * jQuery UI Accordion
 * 
 * Copyright (c) 2007, 2008 Jörn Zaefferer
 * Dual licensed under the MIT (MIT-LICENSE.txt)
 * and GPL (GPL-LICENSE.txt) licenses.
 *
 * http://docs.jquery.com/UI/Accordion
 *
 * Depends:
 *	ui.core.js
 */
(function($) {

$.widget("ui.accordion", {
	init: function() {
		var options = this.options;
		
		if ( options.navigation ) {
			var current = this.element.find("a").filter(options.navigationFilter);
			if ( current.length ) {
				if ( current.filter(options.header).length ) {
					options.active = current;
				} else {
					options.active = current.parent().parent().prev();
					current.addClass("current");
				}
			}
		}
		
		// calculate active if not specified, using the first header
		options.headers = this.element.find(options.header);
		options.active = findActive(options.headers, options.active);
		
		// IE7-/Win - Extra vertical space in Lists fixed
		if ($.browser.msie) {
			this.element.find('a').css('zoom', '1');
		}
		
		if (!this.element.hasClass("ui-accordion")) {
			this.element.addClass("ui-accordion");
			$("<span class='ui-accordion-left'/>").insertBefore(options.headers);
			$("<span class='ui-accordion-right'/>").appendTo(options.headers);
			options.headers.addClass("ui-accordion-header").attr("tabindex", "0");
		}
		
		var maxHeight;
		if ( options.fillSpace ) {
			maxHeight = this.element.parent().height();
			options.headers.each(function() {
				maxHeight -= $(this).outerHeight();
			});
			var maxPadding = 0;
			options.headers.next().each(function() {
				maxPadding = Math.max(maxPadding, $(this).innerHeight() - $(this).height());
			}).height(maxHeight - maxPadding);
		} else if ( options.autoHeight ) {
			maxHeight = 0;
			options.headers.next().each(function() {
				maxHeight = Math.max(maxHeight, $(this).outerHeight());
			}).height(maxHeight);
		}
	
		options.headers
			.not(options.active || "")
			.next()
			.hide();
		options.active.parent().andSelf().addClass(options.selectedClass);
		
		if (options.event) {
			this.element.bind((options.event) + ".accordion", clickHandler);
		}
	},
	activate: function(index) {
		// call clickHandler with custom event
		clickHandler.call(this.element[0], {
			target: findActive( this.options.headers, index )[0]
		});
	},
	destroy: function() {
		this.options.headers.next().css("display", "");
		if ( this.options.fillSpace || this.options.autoHeight ) {
			this.options.headers.next().css("height", "");
		}
		$.removeData(this.element[0], "accordion");
		this.element.removeClass("ui-accordion").unbind(".accordion");
	}
});

function scopeCallback(callback, scope) {
	return function() {
		return callback.apply(scope, arguments);
	};
};

function completed(cancel) {
	// if removed while animated data can be empty
	if (!$.data(this, "accordion")) {
		return;
	}
	
	var instance = $.data(this, "accordion");
	var options = instance.options;
	options.running = cancel ? 0 : --options.running;
	if ( options.running ) {
		return;
	}
	if ( options.clearStyle ) {
		options.toShow.add(options.toHide).css({
			height: "",
			overflow: ""
		});
	}
	$(this).triggerHandler("accordionchange", [$.event.fix({type: 'accordionchange', target: instance.element[0]}), options.data], options.change);
}

function toggle(toShow, toHide, data, clickedActive, down) {
	var options = $.data(this, "accordion").options;
	options.toShow = toShow;
	options.toHide = toHide;
	options.data = data;
	var complete = scopeCallback(completed, this);
	
	// count elements to animate
	options.running = toHide.size() === 0 ? toShow.size() : toHide.size();
	
	if ( options.animated ) {
		if ( !options.alwaysOpen && clickedActive ) {
			$.ui.accordion.animations[options.animated]({
				toShow: jQuery([]),
				toHide: toHide,
				complete: complete,
				down: down,
				autoHeight: options.autoHeight
			});
		} else {
			$.ui.accordion.animations[options.animated]({
				toShow: toShow,
				toHide: toHide,
				complete: complete,
				down: down,
				autoHeight: options.autoHeight
			});
		}
	} else {
		if ( !options.alwaysOpen && clickedActive ) {
			toShow.toggle();
		} else {
			toHide.hide();
			toShow.show();
		}
		complete(true);
	}
}

function clickHandler(event) {
	var options = $.data(this, "accordion").options;
	if (options.disabled) {
		return false;
	}
	
	// called only when using activate(false) to close all parts programmatically
	if ( !event.target && !options.alwaysOpen ) {
		options.active.parent().andSelf().toggleClass(options.selectedClass);
		var toHide = options.active.next(),
			data = {
				options: options,
				newHeader: jQuery([]),
				oldHeader: options.active,
				newContent: jQuery([]),
				oldContent: toHide
			},
			toShow = (options.active = $([]));
		toggle.call(this, toShow, toHide, data );
		return false;
	}
	// get the click target
	var clicked = $(event.target);
	
	// due to the event delegation model, we have to check if one
	// of the parent elements is our actual header, and find that
	// otherwise stick with the initial target
	clicked = $( clicked.parents(options.header)[0] || clicked );
	
	var clickedActive = clicked[0] == options.active[0];
	
	// if animations are still active, or the active header is the target, ignore click
	if (options.running || (options.alwaysOpen && clickedActive)) {
		return false;
	}
	if (!clicked.is(options.header)) {
		return;
	}
	
	// switch classes
	options.active.parent().andSelf().toggleClass(options.selectedClass);
	if ( !clickedActive ) {
		clicked.parent().andSelf().addClass(options.selectedClass);
	}
	
	// find elements to show and hide
	var toShow = clicked.next(),
		toHide = options.active.next(),
		//data = [clicked, options.active, toShow, toHide],
		data = {
			options: options,
			newHeader: clicked,
			oldHeader: options.active,
			newContent: toShow,
			oldContent: toHide
		},
		down = options.headers.index( options.active[0] ) > options.headers.index( clicked[0] );
	
	options.active = clickedActive ? $([]) : clicked;
	toggle.call(this, toShow, toHide, data, clickedActive, down );

	return false;
};

function findActive(headers, selector) {
	return selector != undefined
		? typeof selector == "number"
			? headers.filter(":eq(" + selector + ")")
			: headers.not(headers.not(selector))
		: selector === false
			? $([])
			: headers.filter(":eq(0)");
}

$.extend($.ui.accordion, {
	defaults: {
		selectedClass: "selected",
		alwaysOpen: true,
		animated: 'slide',
		event: "click",
		header: "a",
		autoHeight: true,
		running: 0,
		navigationFilter: function() {
			return this.href.toLowerCase() == location.href.toLowerCase();
		}
	},
	animations: {
		slide: function(options, additions) {
			options = $.extend({
				easing: "swing",
				duration: 300
			}, options, additions);
			if ( !options.toHide.size() ) {
				options.toShow.animate({height: "show"}, options);
				return;
			}
			var hideHeight = options.toHide.height(),
				showHeight = options.toShow.height(),
				difference = showHeight / hideHeight;
			options.toShow.css({ height: 0, overflow: 'hidden' }).show();
			options.toHide.filter(":hidden").each(options.complete).end().filter(":visible").animate({height:"hide"},{
				step: function(now) {
					var current = (hideHeight - now) * difference;
					if ($.browser.msie || $.browser.opera) {
						current = Math.ceil(current);
					}
					options.toShow.height( current );
				},
				duration: options.duration,
				easing: options.easing,
				complete: function() {
					if ( !options.autoHeight ) {
						options.toShow.css("height", "auto");
					}
					options.complete();
				}
			});
		},
		bounceslide: function(options) {
			this.slide(options, {
				easing: options.down ? "bounceout" : "swing",
				duration: options.down ? 1000 : 200
			});
		},
		easeslide: function(options) {
			this.slide(options, {
				easing: "easeinout",
				duration: 700
			});
		}
	}
});

// deprecated, use accordion("activate", index) instead
$.fn.activate = function(index) {
	return this.accordion("activate", index);
};

})(jQuery);
/*
 * jQuery UI Dialog
 *
 * Copyright (c) 2008 Richard D. Worth (rdworth.org)
 * Dual licensed under the MIT (MIT-LICENSE.txt)
 * and GPL (GPL-LICENSE.txt) licenses.
 * 
 * http://docs.jquery.com/UI/Dialog
 *
 * Depends:
 *	ui.core.js
 *	ui.draggable.js
 *	ui.resizable.js
 */
(function($) {

var setDataSwitch = {
	dragStart: "start.draggable",
	drag: "drag.draggable",
	dragStop: "stop.draggable",
	maxHeight: "maxHeight.resizable",
	minHeight: "minHeight.resizable",
	maxWidth: "maxWidth.resizable",
	minWidth: "minWidth.resizable",
	resizeStart: "start.resizable",
	resize: "drag.resizable",
	resizeStop: "stop.resizable"
};

$.widget("ui.dialog", {
	init: function() {
		var self = this,
			options = this.options,
			resizeHandles = typeof options.resizable == 'string'
				? options.resizable
				: 'n,e,s,w,se,sw,ne,nw',
			
			uiDialogContent = this.element
				.addClass('ui-dialog-content')
				.wrap('<div/>')
				.wrap('<div/>'),
			
			uiDialogContainer = (this.uiDialogContainer = uiDialogContent.parent()
				.addClass('ui-dialog-container')
				.css({position: 'relative', width: '100%', height: '100%'})),
			
			title = options.title || uiDialogContent.attr('title') || '',
			uiDialogTitlebar = (this.uiDialogTitlebar =
				$('<div class="ui-dialog-titlebar"/>'))
				.append('<span class="ui-dialog-title">' + title + '</span>')
				.append('<a href="#" class="ui-dialog-titlebar-close"><span>X</span></a>')
				.prependTo(uiDialogContainer),
			
			uiDialog = (this.uiDialog = uiDialogContainer.parent())
				.appendTo(document.body)
				.hide()
				.addClass('ui-dialog')
				.addClass(options.dialogClass)
				// add content classes to dialog
				// to inherit theme at top level of element
				.addClass(uiDialogContent.attr('className'))
					.removeClass('ui-dialog-content')
				.css({
					position: 'absolute',
					width: options.width,
					height: options.height,
					overflow: 'hidden',
					zIndex: options.zIndex
				})
				// setting tabIndex makes the div focusable
				// setting outline to 0 prevents a border on focus in Mozilla
				.attr('tabIndex', -1).css('outline', 0).keydown(function(ev) {
					if (options.closeOnEscape) {
						var ESC = 27;
						(ev.keyCode && ev.keyCode == ESC && self.close());
					}
				})
				.mousedown(function() {
					self.moveToTop();
				}),
			
			uiDialogButtonPane = (this.uiDialogButtonPane = $('<div/>'))
				.addClass('ui-dialog-buttonpane').css({ position: 'absolute', bottom: 0 })
				.appendTo(uiDialog);
		
		this.uiDialogTitlebarClose = $('.ui-dialog-titlebar-close', uiDialogTitlebar)
			.hover(
				function() {
					$(this).addClass('ui-dialog-titlebar-close-hover');
				},
				function() {
					$(this).removeClass('ui-dialog-titlebar-close-hover');
				}
			)
			.mousedown(function(ev) {
				ev.stopPropagation();
			})
			.click(function() {
				self.close();
				return false;
			});

		this.uiDialogTitlebar.find("*").add(this.uiDialogTitlebar).each(function() {
			$.ui.disableSelection(this);
		});

		if ($.fn.draggable) {
			uiDialog.draggable({
				cancel: '.ui-dialog-content',
				helper: options.dragHelper,
				handle: '.ui-dialog-titlebar',
				start: function(e, ui) {
					self.moveToTop();
					(options.dragStart && options.dragStart.apply(self.element[0], arguments));
				},
				drag: function(e, ui) {
					(options.drag && options.drag.apply(self.element[0], arguments));
				},
				stop: function(e, ui) {
					(options.dragStop && options.dragStop.apply(self.element[0], arguments));
					$.ui.dialog.overlay.resize();
				}
			});
			(options.draggable || uiDialog.draggable('disable'));
		}
		
		if ($.fn.resizable) {
			uiDialog.resizable({
				cancel: '.ui-dialog-content',
				helper: options.resizeHelper,
				maxWidth: options.maxWidth,
				maxHeight: options.maxHeight,
				minWidth: options.minWidth,
				minHeight: options.minHeight,
				start: function() {
					(options.resizeStart && options.resizeStart.apply(self.element[0], arguments));
				},
				resize: function(e, ui) {
					(options.autoResize && self.size.apply(self));
					(options.resize && options.resize.apply(self.element[0], arguments));
				},
				handles: resizeHandles,
				stop: function(e, ui) {
					(options.autoResize && self.size.apply(self));
					(options.resizeStop && options.resizeStop.apply(self.element[0], arguments));
					$.ui.dialog.overlay.resize();
				}
			});
			(options.resizable || uiDialog.resizable('disable'));
		}
		
		this.createButtons(options.buttons);
		this.isOpen = false;
		
		(options.bgiframe && $.fn.bgiframe && uiDialog.bgiframe());
		(options.autoOpen && this.open());
	},
	
	setData: function(key, value){
		(setDataSwitch[key] && this.uiDialog.data(setDataSwitch[key], value));
		switch (key) {
			case "buttons":
				this.createButtons(value);
				break;
			case "draggable":
				this.uiDialog.draggable(value ? 'enable' : 'disable');
				break;
			case "height":
				this.uiDialog.height(value);
				break;
			case "position":
				this.position(value);
				break;
			case "resizable":
				(typeof value == 'string' && this.uiDialog.data('handles.resizable', value));
				this.uiDialog.resizable(value ? 'enable' : 'disable');
				break;
			case "title":
				$(".ui-dialog-title", this.uiDialogTitlebar).text(value);
				break;
			case "width":
				this.uiDialog.width(value);
				break;
		}
		
		$.widget.prototype.setData.apply(this, arguments);
	},
	
	position: function(pos) {
		var wnd = $(window), doc = $(document),
			pTop = doc.scrollTop(), pLeft = doc.scrollLeft(),
			minTop = pTop;
		
		if ($.inArray(pos, ['center','top','right','bottom','left']) >= 0) {
			pos = [
				pos == 'right' || pos == 'left' ? pos : 'center',
				pos == 'top' || pos == 'bottom' ? pos : 'middle'
			];
		}
		if (pos.constructor != Array) {
			pos = ['center', 'middle'];
		}
		if (pos[0].constructor == Number) {
			pLeft += pos[0];
		} else {
			switch (pos[0]) {
				case 'left':
					pLeft += 0;
					break;
				case 'right':
					pLeft += wnd.width() - this.uiDialog.width();
					break;
				default:
				case 'center':
					pLeft += (wnd.width() - this.uiDialog.width()) / 2;
			}
		}
		if (pos[1].constructor == Number) {
			pTop += pos[1];
		} else {
			switch (pos[1]) {
				case 'top':
					pTop += 0;
					break;
				case 'bottom':
					pTop += wnd.height() - this.uiDialog.height();
					break;
				default:
				case 'middle':
					pTop += (wnd.height() - this.uiDialog.height()) / 2;
			}
		}
		
		// prevent the dialog from being too high (make sure the titlebar
		// is accessible)
		pTop = Math.max(pTop, minTop);
		this.uiDialog.css({top: pTop, left: pLeft});
	},

	size: function() {
		var container = this.uiDialogContainer,
			titlebar = this.uiDialogTitlebar,
			content = this.element,
			tbMargin = parseInt(content.css('margin-top'),10) + parseInt(content.css('margin-bottom'),10),
			lrMargin = parseInt(content.css('margin-left'),10) + parseInt(content.css('margin-right'),10);
		content.height(container.height() - titlebar.outerHeight() - tbMargin);
		content.width(container.width() - lrMargin);
	},
	
	open: function() {
		if (this.isOpen) { return; }
		
		this.overlay = this.options.modal ? new $.ui.dialog.overlay(this) : null;
		(this.uiDialog.next().length > 0) && this.uiDialog.appendTo('body');
		this.position(this.options.position);
		this.uiDialog.show(this.options.show);
		this.options.autoResize && this.size();
		this.moveToTop(true);
		
		// CALLBACK: open
		var openEV = null;
		var openUI = {
			options: this.options
		};
		this.uiDialogTitlebarClose.focus();
		this.element.triggerHandler("dialogopen", [openEV, openUI], this.options.open);
		
		this.isOpen = true;
	},
	
	// the force parameter allows us to move modal dialogs to their correct
	// position on open
	moveToTop: function(force) {
		if ((this.options.modal && !force)
			|| (!this.options.stack && !this.options.modal)) { return this.element.triggerHandler("dialogfocus", [null, { options: this.options }], this.options.focus); }
		
		var maxZ = this.options.zIndex, options = this.options;
		$('.ui-dialog:visible').each(function() {
			maxZ = Math.max(maxZ, parseInt($(this).css('z-index'), 10) || options.zIndex);
		});
		(this.overlay && this.overlay.$el.css('z-index', ++maxZ));
		this.uiDialog.css('z-index', ++maxZ);
		
		this.element.triggerHandler("dialogfocus", [null, { options: this.options }], this.options.focus);
	},
	
	close: function() {
		(this.overlay && this.overlay.destroy());
		this.uiDialog.hide(this.options.hide);

		// CALLBACK: close
		var closeEV = null;
		var closeUI = {
			options: this.options
		};
		this.element.triggerHandler("dialogclose", [closeEV, closeUI], this.options.close);
		$.ui.dialog.overlay.resize();
		
		this.isOpen = false;
	},
	
	destroy: function() {
		(this.overlay && this.overlay.destroy());
		this.uiDialog.hide();
		this.element
			.unbind('.dialog')
			.removeData('dialog')
			.removeClass('ui-dialog-content')
			.hide().appendTo('body');
		this.uiDialog.remove();
	},
	
	createButtons: function(buttons) {
		var self = this,
			hasButtons = false,
			uiDialogButtonPane = this.uiDialogButtonPane;
		
		// remove any existing buttons
		uiDialogButtonPane.empty().hide();
		
		$.each(buttons, function() { return !(hasButtons = true); });
		if (hasButtons) {
			uiDialogButtonPane.show();
			$.each(buttons, function(name, fn) {
				$('<button/>')
					.text(name)
					.click(function() { fn.apply(self.element[0], arguments); })
					.appendTo(uiDialogButtonPane);
			});
		}
	}
});

$.extend($.ui.dialog, {
	defaults: {
		autoOpen: true,
		autoResize: true,
		bgiframe: false,
		buttons: {},
		closeOnEscape: true,
		draggable: true,
		height: 200,
		minHeight: 100,
		minWidth: 150,
		modal: false,
		overlay: {},
		position: 'center',
		resizable: true,
		stack: true,
		width: 300,
		zIndex: 1000
	},
	
	overlay: function(dialog) {
		this.$el = $.ui.dialog.overlay.create(dialog);
	}
});

$.extend($.ui.dialog.overlay, {
	instances: [],
	events: $.map('focus,mousedown,mouseup,keydown,keypress,click'.split(','),
		function(e) { return e + '.dialog-overlay'; }).join(' '),
	create: function(dialog) {
		if (this.instances.length === 0) {
			// prevent use of anchors and inputs
			// we use a setTimeout in case the overlay is created from an
			// event that we're going to be cancelling (see #2804)
			setTimeout(function() {
				$('a, :input').bind($.ui.dialog.overlay.events, function() {
					// allow use of the element if inside a dialog and
					// - there are no modal dialogs
					// - there are modal dialogs, but we are in front of the topmost modal
					var allow = false;
					var $dialog = $(this).parents('.ui-dialog');
					if ($dialog.length) {
						var $overlays = $('.ui-dialog-overlay');
						if ($overlays.length) {
							var maxZ = parseInt($overlays.css('z-index'), 10);
							$overlays.each(function() {
								maxZ = Math.max(maxZ, parseInt($(this).css('z-index'), 10));
							});
							allow = parseInt($dialog.css('z-index'), 10) > maxZ;
						} else {
							allow = true;
						}
					}
					return allow;
				});
			}, 1);
			
			// allow closing by pressing the escape key
			$(document).bind('keydown.dialog-overlay', function(e) {
				var ESC = 27;
				(e.keyCode && e.keyCode == ESC && dialog.close()); 
			});
			
			// handle window resize
			$(window).bind('resize.dialog-overlay', $.ui.dialog.overlay.resize);
		}
		
		var $el = $('<div/>').appendTo(document.body)
			.addClass('ui-dialog-overlay').css($.extend({
				borderWidth: 0, margin: 0, padding: 0,
				position: 'absolute', top: 0, left: 0,
				width: this.width(),
				height: this.height()
			}, dialog.options.overlay));
		
		(dialog.options.bgiframe && $.fn.bgiframe && $el.bgiframe());
		
		this.instances.push($el);
		return $el;
	},
	
	destroy: function($el) {
		this.instances.splice($.inArray(this.instances, $el), 1);
		
		if (this.instances.length === 0) {
			$('a, :input').add([document, window]).unbind('.dialog-overlay');
		}
		
		$el.remove();
	},
	
	height: function() {
		if ($.browser.msie && $.browser.version < 7) {
			var scrollHeight = Math.max(
				document.documentElement.scrollHeight,
				document.body.scrollHeight
			);
			var offsetHeight = Math.max(
				document.documentElement.offsetHeight,
				document.body.offsetHeight
			);
			
			if (scrollHeight < offsetHeight) {
				return $(window).height() + 'px';
			} else {
				return scrollHeight + 'px';
			}
		} else {
			return $(document).height() + 'px';
		}
	},
	
	width: function() {
		if ($.browser.msie && $.browser.version < 7) {
			var scrollWidth = Math.max(
				document.documentElement.scrollWidth,
				document.body.scrollWidth
			);
			var offsetWidth = Math.max(
				document.documentElement.offsetWidth,
				document.body.offsetWidth
			);
			
			if (scrollWidth < offsetWidth) {
				return $(window).width() + 'px';
			} else {
				return scrollWidth + 'px';
			}
		} else {
			return $(document).width() + 'px';
		}
	},
	
	resize: function() {
		/* If the dialog is draggable and the user drags it past the
		 * right edge of the window, the document becomes wider so we
		 * need to stretch the overlay. If the user then drags the
		 * dialog back to the left, the document will become narrower,
		 * so we need to shrink the overlay to the appropriate size.
		 * This is handled by shrinking the overlay before setting it
		 * to the full document size.
		 */
		var $overlays = $([]);
		$.each($.ui.dialog.overlay.instances, function() {
			$overlays = $overlays.add(this);
		});
		
		$overlays.css({
			width: 0,
			height: 0
		}).css({
			width: $.ui.dialog.overlay.width(),
			height: $.ui.dialog.overlay.height()
		});
	}
});

$.extend($.ui.dialog.overlay.prototype, {
	destroy: function() {
		$.ui.dialog.overlay.destroy(this.$el);
	}
});

})(jQuery);
/*
 * jQuery UI Slider
 *
 * Copyright (c) 2008 Paul Bakaus
 * Dual licensed under the MIT (MIT-LICENSE.txt)
 * and GPL (GPL-LICENSE.txt) licenses.
 * 
 * http://docs.jquery.com/UI/Slider
 *
 * Depends:
 *	ui.core.js
 */
(function($) {

$.fn.unwrap = $.fn.unwrap || function(expr) {
  return this.each(function(){
     $(this).parents(expr).eq(0).after(this).remove();
  });
};

$.widget("ui.slider", {
	plugins: {},
	ui: function(e) {
		return {
			options: this.options,
			handle: this.currentHandle,
			value: this.options.axis != "both" || !this.options.axis ? Math.round(this.value(null,this.options.axis == "vertical" ? "y" : "x")) : {
				x: Math.round(this.value(null,"x")),
				y: Math.round(this.value(null,"y"))
			},
			range: this.getRange()
		};
	},
	propagate: function(n,e) {
		$.ui.plugin.call(this, n, [e, this.ui()]);
		this.element.triggerHandler(n == "slide" ? n : "slide"+n, [e, this.ui()], this.options[n]);
	},
	destroy: function() {
		
		this.element
			.removeClass("ui-slider ui-slider-disabled")
			.removeData("slider")
			.unbind(".slider");
		
		if(this.handle && this.handle.length) {
			this.handle
				.unwrap("a");
			this.handle.each(function() {
				$(this).data("mouse").mouseDestroy();
			});
		}
		
		this.generated && this.generated.remove();
		
	},
	setData: function(key, value) {
		$.widget.prototype.setData.apply(this, arguments);
		if (/min|max|steps/.test(key)) {
			this.initBoundaries();
		}
		
		if(key == "range") {
			value ? this.handle.length == 2 && this.createRange() : this.removeRange();
		}
		
	},

	init: function() {
		
		var self = this;
		this.element.addClass("ui-slider");
		this.initBoundaries();
		
		// Initialize mouse and key events for interaction
		this.handle = $(this.options.handle, this.element);
		if (!this.handle.length) {
			self.handle = self.generated = $(self.options.handles || [0]).map(function() {
				var handle = $("<div/>").addClass("ui-slider-handle").appendTo(self.element);
				if (this.id)
					handle.attr("id", this.id);
				return handle[0];
			});
		}
		
		
		var handleclass = function(el) {
			this.element = $(el);
			this.element.data("mouse", this);
			this.options = self.options;
			
			this.element.bind("mousedown", function() {
				if(self.currentHandle) this.blur(self.currentHandle);
				self.focus(this,1);
			});
			
			this.mouseInit();
		};
		
		$.extend(handleclass.prototype, $.ui.mouse, {
			mouseStart: function(e) { return self.start.call(self, e, this.element[0]); },
			mouseStop: function(e) { return self.stop.call(self, e, this.element[0]); },
			mouseDrag: function(e) { return self.drag.call(self, e, this.element[0]); },
			mouseCapture: function() { return true; },
			trigger: function(e) { this.mouseDown(e); }
		});
		
		
		$(this.handle)
			.each(function() {
				new handleclass(this);
			})
			.wrap('<a href="javascript:void(0)" style="outline:none;border:none;"></a>')
			.parent()
				.bind('focus', function(e) { self.focus(this.firstChild); })
				.bind('blur', function(e) { self.blur(this.firstChild); })
				.bind('keydown', function(e) { if(!self.options.noKeyboard) self.keydown(e.keyCode, this.firstChild); })
		;
		
		// Bind the click to the slider itself
		this.element.bind('mousedown.slider', function(e) {
			self.click.apply(self, [e]);
			self.currentHandle.data("mouse").trigger(e);
			self.firstValue = self.firstValue + 1; //This is for always triggering the change event
		});
		
		// Move the first handle to the startValue
		$.each(this.options.handles || [], function(index, handle) {
			self.moveTo(handle.start, index, true);
		});
		if (!isNaN(this.options.startValue))
			this.moveTo(this.options.startValue, 0, true);

		this.previousHandle = $(this.handle[0]); //set the previous handle to the first to allow clicking before selecting the handle
		if(this.handle.length == 2 && this.options.range) this.createRange();
	},
	initBoundaries: function() {
		
		var element = this.element[0], o = this.options;
		this.actualSize = { width: this.element.outerWidth() , height: this.element.outerHeight() };			
		
		$.extend(o, {
			axis: o.axis || (element.offsetWidth < element.offsetHeight ? 'vertical' : 'horizontal'),
			max: !isNaN(parseInt(o.max,10)) ? { x: parseInt(o.max, 10), y: parseInt(o.max, 10) } : ({ x: o.max && o.max.x || 100, y: o.max && o.max.y || 100 }),
			min: !isNaN(parseInt(o.min,10)) ? { x: parseInt(o.min, 10), y: parseInt(o.min, 10) } : ({ x: o.min && o.min.x || 0, y: o.min && o.min.y || 0 })
		});
		//Prepare the real maxValue
		o.realMax = {
			x: o.max.x - o.min.x,
			y: o.max.y - o.min.y
		};
		//Calculate stepping based on steps
		o.stepping = {
			x: o.stepping && o.stepping.x || parseInt(o.stepping, 10) || (o.steps ? o.realMax.x/(o.steps.x || parseInt(o.steps, 10) || o.realMax.x) : 0),
			y: o.stepping && o.stepping.y || parseInt(o.stepping, 10) || (o.steps ? o.realMax.y/(o.steps.y || parseInt(o.steps, 10) || o.realMax.y) : 0)
		};
	},

	
	keydown: function(keyCode, handle) {
		if(/(37|38|39|40)/.test(keyCode)) {
			this.moveTo({
				x: /(37|39)/.test(keyCode) ? (keyCode == 37 ? '-' : '+') + '=' + this.oneStep("x") : 0,
				y: /(38|40)/.test(keyCode) ? (keyCode == 38 ? '-' : '+') + '=' + this.oneStep("y") : 0
			}, handle);
		}
	},
	focus: function(handle,hard) {
		this.currentHandle = $(handle).addClass('ui-slider-handle-active');
		if (hard)
			this.currentHandle.parent()[0].focus();
	},
	blur: function(handle) {
		$(handle).removeClass('ui-slider-handle-active');
		if(this.currentHandle && this.currentHandle[0] == handle) { this.previousHandle = this.currentHandle; this.currentHandle = null; };
	},
	click: function(e) {
		// This method is only used if:
		// - The user didn't click a handle
		// - The Slider is not disabled
		// - There is a current, or previous selected handle (otherwise we wouldn't know which one to move)
		
		var pointer = [e.pageX,e.pageY];
		
		var clickedHandle = false;
		this.handle.each(function() {
			if(this == e.target)
				clickedHandle = true;
		});
		if (clickedHandle || this.options.disabled || !(this.currentHandle || this.previousHandle))
			return;

		// If a previous handle was focussed, focus it again
		if (!this.currentHandle && this.previousHandle)
			this.focus(this.previousHandle, true);
		
		// propagate only for distance > 0, otherwise propagation is done my drag
		this.offset = this.element.offset();

		this.moveTo({
			y: this.convertValue(e.pageY - this.offset.top - this.currentHandle[0].offsetHeight/2, "y"),
			x: this.convertValue(e.pageX - this.offset.left - this.currentHandle[0].offsetWidth/2, "x")
		}, null, !this.options.distance);
	},
	


	createRange: function() {
		if(this.rangeElement) return;
		this.rangeElement = $('<div></div>')
			.addClass('ui-slider-range')
			.css({ position: 'absolute' })
			.appendTo(this.element);
		this.updateRange();
	},
	removeRange: function() {
		this.rangeElement.remove();
		this.rangeElement = null;
	},
	updateRange: function() {
			var prop = this.options.axis == "vertical" ? "top" : "left";
			var size = this.options.axis == "vertical" ? "height" : "width";
			this.rangeElement.css(prop, (parseInt($(this.handle[0]).css(prop),10) || 0) + this.handleSize(0, this.options.axis == "vertical" ? "y" : "x")/2);
			this.rangeElement.css(size, (parseInt($(this.handle[1]).css(prop),10) || 0) - (parseInt($(this.handle[0]).css(prop),10) || 0));
	},
	getRange: function() {
		return this.rangeElement ? this.convertValue(parseInt(this.rangeElement.css(this.options.axis == "vertical" ? "height" : "width"),10), this.options.axis == "vertical" ? "y" : "x") : null;
	},

	handleIndex: function() {
		return this.handle.index(this.currentHandle[0]);
	},
	value: function(handle, axis) {
		if(this.handle.length == 1) this.currentHandle = this.handle;
		if(!axis) axis = this.options.axis == "vertical" ? "y" : "x";

		var curHandle = $(handle != undefined && handle !== null ? this.handle[handle] || handle : this.currentHandle);
		
		if(curHandle.data("mouse").sliderValue) {
			return parseInt(curHandle.data("mouse").sliderValue[axis],10);
		} else {
			return parseInt(((parseInt(curHandle.css(axis == "x" ? "left" : "top"),10) / (this.actualSize[axis == "x" ? "width" : "height"] - this.handleSize(handle,axis))) * this.options.realMax[axis]) + this.options.min[axis],10);
		}

	},
	convertValue: function(value,axis) {
		return this.options.min[axis] + (value / (this.actualSize[axis == "x" ? "width" : "height"] - this.handleSize(null,axis))) * this.options.realMax[axis];
	},
	
	translateValue: function(value,axis) {
		return ((value - this.options.min[axis]) / this.options.realMax[axis]) * (this.actualSize[axis == "x" ? "width" : "height"] - this.handleSize(null,axis));
	},
	translateRange: function(value,axis) {
		if (this.rangeElement) {
			if (this.currentHandle[0] == this.handle[0] && value >= this.translateValue(this.value(1),axis))
				value = this.translateValue(this.value(1,axis) - this.oneStep(axis), axis);
			if (this.currentHandle[0] == this.handle[1] && value <= this.translateValue(this.value(0),axis))
				value = this.translateValue(this.value(0,axis) + this.oneStep(axis), axis);
		}
		if (this.options.handles) {
			var handle = this.options.handles[this.handleIndex()];
			if (value < this.translateValue(handle.min,axis)) {
				value = this.translateValue(handle.min,axis);
			} else if (value > this.translateValue(handle.max,axis)) {
				value = this.translateValue(handle.max,axis);
			}
		}
		return value;
	},
	translateLimits: function(value,axis) {
		if (value >= this.actualSize[axis == "x" ? "width" : "height"] - this.handleSize(null,axis))
			value = this.actualSize[axis == "x" ? "width" : "height"] - this.handleSize(null,axis);
		if (value <= 0)
			value = 0;
		return value;
	},
	handleSize: function(handle,axis) {
		return $(handle != undefined && handle !== null ? this.handle[handle] : this.currentHandle)[0]["offset"+(axis == "x" ? "Width" : "Height")];	
	},
	oneStep: function(axis) {
		return this.options.stepping[axis] || 1;
	},


	start: function(e, handle) {
	
		var o = this.options;
		if(o.disabled) return false;

		// Prepare the outer size
		this.actualSize = { width: this.element.outerWidth() , height: this.element.outerHeight() };
	
		// This is a especially ugly fix for strange blur events happening on mousemove events
		if (!this.currentHandle)
			this.focus(this.previousHandle, true); 

		this.offset = this.element.offset();
		
		this.handleOffset = this.currentHandle.offset();
		this.clickOffset = { top: e.pageY - this.handleOffset.top, left: e.pageX - this.handleOffset.left };
		
		this.firstValue = this.value();
		
		this.propagate('start', e);
		this.drag(e, handle);
		return true;
					
	},
	stop: function(e) {
		this.propagate('stop', e);
		if (this.firstValue != this.value())
			this.propagate('change', e);
		// This is a especially ugly fix for strange blur events happening on mousemove events
		this.focus(this.currentHandle, true);
		return false;
	},
	drag: function(e, handle) {

		var o = this.options;
		var position = { top: e.pageY - this.offset.top - this.clickOffset.top, left: e.pageX - this.offset.left - this.clickOffset.left};
		if(!this.currentHandle) this.focus(this.previousHandle, true); //This is a especially ugly fix for strange blur events happening on mousemove events

		position.left = this.translateLimits(position.left, "x");
		position.top = this.translateLimits(position.top, "y");
		
		if (o.stepping.x) {
			var value = this.convertValue(position.left, "x");
			value = Math.round(value / o.stepping.x) * o.stepping.x;
			position.left = this.translateValue(value, "x");	
		}
		if (o.stepping.y) {
			var value = this.convertValue(position.top, "y");
			value = Math.round(value / o.stepping.y) * o.stepping.y;
			position.top = this.translateValue(value, "y");	
		}
		
		position.left = this.translateRange(position.left, "x");
		position.top = this.translateRange(position.top, "y");

		if(o.axis != "vertical") this.currentHandle.css({ left: position.left });
		if(o.axis != "horizontal") this.currentHandle.css({ top: position.top });
		
		//Store the slider's value
		this.currentHandle.data("mouse").sliderValue = {
			x: Math.round(this.convertValue(position.left, "x")) || 0,
			y: Math.round(this.convertValue(position.top, "y")) || 0
		};
		
		if (this.rangeElement)
			this.updateRange();
		this.propagate('slide', e);
		return false;
	},
	
	moveTo: function(value, handle, noPropagation) {

		var o = this.options;

		// Prepare the outer size
		this.actualSize = { width: this.element.outerWidth() , height: this.element.outerHeight() };

		//If no handle has been passed, no current handle is available and we have multiple handles, return false
		if (handle == undefined && !this.currentHandle && this.handle.length != 1)
			return false; 
		
		//If only one handle is available, use it
		if (handle == undefined && !this.currentHandle)
			handle = 0;
		
		if (handle != undefined)
			this.currentHandle = this.previousHandle = $(this.handle[handle] || handle);


		if(value.x !== undefined && value.y !== undefined) {
			var x = value.x, y = value.y;
		} else {
			var x = value, y = value;
		}

		if(x !== undefined && x.constructor != Number) {
			var me = /^\-\=/.test(x), pe = /^\+\=/.test(x);
			if(me || pe) {
				x = this.value(null, "x") + parseInt(x.replace(me ? '=' : '+=', ''), 10);
			} else {
				x = isNaN(parseInt(x, 10)) ? undefined : parseInt(x, 10);
			}
		}
		
		if(y !== undefined && y.constructor != Number) {
			var me = /^\-\=/.test(y), pe = /^\+\=/.test(y);
			if(me || pe) {
				y = this.value(null, "y") + parseInt(y.replace(me ? '=' : '+=', ''), 10);
			} else {
				y = isNaN(parseInt(y, 10)) ? undefined : parseInt(y, 10);
			}
		}

		if(o.axis != "vertical" && x !== undefined) {
			if(o.stepping.x) x = Math.round(x / o.stepping.x) * o.stepping.x;
			x = this.translateValue(x, "x");
			x = this.translateLimits(x, "x");
			x = this.translateRange(x, "x");

			o.animate ? this.currentHandle.stop().animate({ left: x }, (Math.abs(parseInt(this.currentHandle.css("left")) - x)) * (!isNaN(parseInt(o.animate)) ? o.animate : 5)) : this.currentHandle.css({ left: x });
		}

		if(o.axis != "horizontal" && y !== undefined) {
			if(o.stepping.y) y = Math.round(y / o.stepping.y) * o.stepping.y;
			y = this.translateValue(y, "y");
			y = this.translateLimits(y, "y");
			y = this.translateRange(y, "y");
			o.animate ? this.currentHandle.stop().animate({ top: y }, (Math.abs(parseInt(this.currentHandle.css("top")) - y)) * (!isNaN(parseInt(o.animate)) ? o.animate : 5)) : this.currentHandle.css({ top: y });
		}
		
		if (this.rangeElement)
			this.updateRange();
			
		//Store the slider's value
		this.currentHandle.data("mouse").sliderValue = {
			x: Math.round(this.convertValue(x, "x")) || 0,
			y: Math.round(this.convertValue(y, "y")) || 0
		};
	
		if (!noPropagation) {
			this.propagate('start', null);
			this.propagate('stop', null);
			this.propagate('change', null);
			this.propagate("slide", null);
		}
	}
});

$.ui.slider.getter = "value";

$.ui.slider.defaults = {
	handle: ".ui-slider-handle",
	distance: 1,
	animate: false
};

})(jQuery);
/*
 * jQuery UI Tabs
 *
 * Copyright (c) 2007, 2008 Klaus Hartl (stilbuero.de)
 * Dual licensed under the MIT (MIT-LICENSE.txt)
 * and GPL (GPL-LICENSE.txt) licenses.
 *
 * http://docs.jquery.com/UI/Tabs
 *
 * Depends:
 *	ui.core.js
 */
(function($) {

$.widget("ui.tabs", {
	init: function() {
		this.options.event += '.tabs'; // namespace event
		
		// create tabs
		this.tabify(true);
	},
	setData: function(key, value) {
		if ((/^selected/).test(key))
			this.select(value);
		else {
			this.options[key] = value;
			this.tabify();
		}
	},
	length: function() {
		return this.$tabs.length;
	},
	tabId: function(a) {
		return a.title && a.title.replace(/\s/g, '_').replace(/[^A-Za-z0-9\-_:\.]/g, '')
			|| this.options.idPrefix + $.data(a);
	},
	ui: function(tab, panel) {
		return {
			options: this.options,
			tab: tab,
			panel: panel,
			index: this.$tabs.index(tab)
		};
	},
	tabify: function(init) {

		this.$lis = $('li:has(a[href])', this.element);
		this.$tabs = this.$lis.map(function() { return $('a', this)[0]; });
		this.$panels = $([]);

		var self = this, o = this.options;

		this.$tabs.each(function(i, a) {
			// inline tab
			if (a.hash && a.hash.replace('#', '')) // Safari 2 reports '#' for an empty hash
				self.$panels = self.$panels.add(a.hash);
			// remote tab
			else if ($(a).attr('href') != '#') { // prevent loading the page itself if href is just "#"
				$.data(a, 'href.tabs', a.href); // required for restore on destroy
				$.data(a, 'load.tabs', a.href); // mutable
				var id = self.tabId(a);
				a.href = '#' + id;
				var $panel = $('#' + id);
				if (!$panel.length) {
					$panel = $(o.panelTemplate).attr('id', id).addClass(o.panelClass)
						.insertAfter( self.$panels[i - 1] || self.element );
					$panel.data('destroy.tabs', true);
				}
				self.$panels = self.$panels.add( $panel );
			}
			// invalid tab href
			else
				o.disabled.push(i + 1);
		});

		if (init) {

			// attach necessary classes for styling if not present
			this.element.addClass(o.navClass);
			this.$panels.each(function() {
				var $this = $(this);
				$this.addClass(o.panelClass);
			});

			// Selected tab
			// use "selected" option or try to retrieve:
			// 1. from fragment identifier in url
			// 2. from cookie
			// 3. from selected class attribute on <li>
			if (o.selected === undefined) {
				if (location.hash) {
					this.$tabs.each(function(i, a) {
						if (a.hash == location.hash) {
							o.selected = i;
							// prevent page scroll to fragment
							if ($.browser.msie || $.browser.opera) { // && !o.remote
								var $toShow = $(location.hash), toShowId = $toShow.attr('id');
								$toShow.attr('id', '');
								setTimeout(function() {
									$toShow.attr('id', toShowId); // restore id
								}, 500);
							}
							scrollTo(0, 0);
							return false; // break
						}
					});
				}
				else if (o.cookie) {
					var index = parseInt($.cookie('ui-tabs' + $.data(self.element)),10);
					if (index && self.$tabs[index])
						o.selected = index;
				}
				else if (self.$lis.filter('.' + o.selectedClass).length)
					o.selected = self.$lis.index( self.$lis.filter('.' + o.selectedClass)[0] );
			}
			o.selected = o.selected === null || o.selected !== undefined ? o.selected : 0; // first tab selected by default

			// Take disabling tabs via class attribute from HTML
			// into account and update option properly.
			// A selected tab cannot become disabled.
			o.disabled = $.unique(o.disabled.concat(
				$.map(this.$lis.filter('.' + o.disabledClass),
					function(n, i) { return self.$lis.index(n); } )
			)).sort();
			if ($.inArray(o.selected, o.disabled) != -1)
				o.disabled.splice($.inArray(o.selected, o.disabled), 1);
			
			// highlight selected tab
			this.$panels.addClass(o.hideClass);
			this.$lis.removeClass(o.selectedClass);
			if (o.selected !== null) {
				this.$panels.eq(o.selected).show().removeClass(o.hideClass); // use show and remove class to show in any case no matter how it has been hidden before
				this.$lis.eq(o.selected).addClass(o.selectedClass);
				
				// seems to be expected behavior that the show callback is fired
				var onShow = function() {
					$(self.element).triggerHandler('tabsshow',
						[self.fakeEvent('tabsshow'), self.ui(self.$tabs[o.selected], self.$panels[o.selected])], o.show);
				}; 

				// load if remote tab
				if ($.data(this.$tabs[o.selected], 'load.tabs'))
					this.load(o.selected, onShow);
				// just trigger show event
				else
					onShow();
				
			}
			
			// clean up to avoid memory leaks in certain versions of IE 6
			$(window).bind('unload', function() {
				self.$tabs.unbind('.tabs');
				self.$lis = self.$tabs = self.$panels = null;
			});

		}

		// disable tabs
		for (var i = 0, li; li = this.$lis[i]; i++)
			$(li)[$.inArray(i, o.disabled) != -1 && !$(li).hasClass(o.selectedClass) ? 'addClass' : 'removeClass'](o.disabledClass);

		// reset cache if switching from cached to not cached
		if (o.cache === false)
			this.$tabs.removeData('cache.tabs');
		
		// set up animations
		var hideFx, showFx, baseFx = { 'min-width': 0, duration: 1 }, baseDuration = 'normal';
		if (o.fx && o.fx.constructor == Array)
			hideFx = o.fx[0] || baseFx, showFx = o.fx[1] || baseFx;
		else
			hideFx = showFx = o.fx || baseFx;

		// reset some styles to maintain print style sheets etc.
		var resetCSS = { display: '', overflow: '', height: '' };
		if (!$.browser.msie) // not in IE to prevent ClearType font issue
			resetCSS.opacity = '';

		// Hide a tab, animation prevents browser scrolling to fragment,
		// $show is optional.
		function hideTab(clicked, $hide, $show) {
			$hide.animate(hideFx, hideFx.duration || baseDuration, function() { //
				$hide.addClass(o.hideClass).css(resetCSS); // maintain flexible height and accessibility in print etc.
				if ($.browser.msie && hideFx.opacity)
					$hide[0].style.filter = '';
				if ($show)
					showTab(clicked, $show, $hide);
			});
		}

		// Show a tab, animation prevents browser scrolling to fragment,
		// $hide is optional.
		function showTab(clicked, $show, $hide) {
			if (showFx === baseFx)
				$show.css('display', 'block'); // prevent occasionally occuring flicker in Firefox cause by gap between showing and hiding the tab panels
			$show.animate(showFx, showFx.duration || baseDuration, function() {
				$show.removeClass(o.hideClass).css(resetCSS); // maintain flexible height and accessibility in print etc.
				if ($.browser.msie && showFx.opacity)
					$show[0].style.filter = '';

				// callback
				$(self.element).triggerHandler('tabsshow',
					[self.fakeEvent('tabsshow'), self.ui(clicked, $show[0])], o.show);

			});
		}

		// switch a tab
		function switchTab(clicked, $li, $hide, $show) {
			/*if (o.bookmarkable && trueClick) { // add to history only if true click occured, not a triggered click
				$.ajaxHistory.update(clicked.hash);
			}*/
			$li.addClass(o.selectedClass)
				.siblings().removeClass(o.selectedClass);
			hideTab(clicked, $hide, $show);
		}

		// attach tab event handler, unbind to avoid duplicates from former tabifying...
		this.$tabs.unbind('.tabs').bind(o.event, function() {

			//var trueClick = e.clientX; // add to history only if true click occured, not a triggered click
			var $li = $(this).parents('li:eq(0)'),
				$hide = self.$panels.filter(':visible'),
				$show = $(this.hash);

			// If tab is already selected and not unselectable or tab disabled or 
			// or is already loading or click callback returns false stop here.
			// Check if click handler returns false last so that it is not executed
			// for a disabled or loading tab!
			if (($li.hasClass(o.selectedClass) && !o.unselect)
				|| $li.hasClass(o.disabledClass) 
				|| $(this).hasClass(o.loadingClass)
				|| $(self.element).triggerHandler('tabsselect', [self.fakeEvent('tabsselect'), self.ui(this, $show[0])], o.select) === false
				) {
				this.blur();
				return false;
			}

			self.options.selected = self.$tabs.index(this);

			// if tab may be closed
			if (o.unselect) {
				if ($li.hasClass(o.selectedClass)) {
					self.options.selected = null;
					$li.removeClass(o.selectedClass);
					self.$panels.stop();
					hideTab(this, $hide);
					this.blur();
					return false;
				} else if (!$hide.length) {
					self.$panels.stop();
					var a = this;
					self.load(self.$tabs.index(this), function() {
						$li.addClass(o.selectedClass).addClass(o.unselectClass);
						showTab(a, $show);
					});
					this.blur();
					return false;
				}
			}

			if (o.cookie)
				$.cookie('ui-tabs' + $.data(self.element), self.options.selected, o.cookie);

			// stop possibly running animations
			self.$panels.stop();

			// show new tab
			if ($show.length) {

				// prevent scrollbar scrolling to 0 and than back in IE7, happens only if bookmarking/history is enabled
				/*if ($.browser.msie && o.bookmarkable) {
					var showId = this.hash.replace('#', '');
					$show.attr('id', '');
					setTimeout(function() {
						$show.attr('id', showId); // restore id
					}, 0);
				}*/

				var a = this;
				self.load(self.$tabs.index(this), $hide.length ? 
					function() {
						switchTab(a, $li, $hide, $show);
					} :
					function() {
						$li.addClass(o.selectedClass);
						showTab(a, $show);
					}
				);

				// Set scrollbar to saved position - need to use timeout with 0 to prevent browser scroll to target of hash
				/*var scrollX = window.pageXOffset || document.documentElement && document.documentElement.scrollLeft || document.body.scrollLeft || 0;
				var scrollY = window.pageYOffset || document.documentElement && document.documentElement.scrollTop || document.body.scrollTop || 0;
				setTimeout(function() {
					scrollTo(scrollX, scrollY);
				}, 0);*/

			} else
				throw 'jQuery UI Tabs: Mismatching fragment identifier.';

			// Prevent IE from keeping other link focussed when using the back button
			// and remove dotted border from clicked link. This is controlled in modern
			// browsers via CSS, also blur removes focus from address bar in Firefox
			// which can become a usability and annoying problem with tabsRotate.
			if ($.browser.msie)
				this.blur();

			//return o.bookmarkable && !!trueClick; // convert trueClick == undefined to Boolean required in IE
			return false;

		});

		// disable click if event is configured to something else
		if (!(/^click/).test(o.event))
			this.$tabs.bind('click.tabs', function() { return false; });

	},
	add: function(url, label, index) {
		if (index == undefined) 
			index = this.$tabs.length; // append by default

		var o = this.options;
		var $li = $(o.tabTemplate.replace(/#\{href\}/g, url).replace(/#\{label\}/g, label));
		$li.data('destroy.tabs', true);

		var id = url.indexOf('#') == 0 ? url.replace('#', '') : this.tabId( $('a:first-child', $li)[0] );

		// try to find an existing element before creating a new one
		var $panel = $('#' + id);
		if (!$panel.length) {
			$panel = $(o.panelTemplate).attr('id', id)
				.addClass(o.hideClass)
				.data('destroy.tabs', true);
		}
		$panel.addClass(o.panelClass);
		if (index >= this.$lis.length) {
			$li.appendTo(this.element);
			$panel.appendTo(this.element[0].parentNode);
		} else {
			$li.insertBefore(this.$lis[index]);
			$panel.insertBefore(this.$panels[index]);
		}
		
		o.disabled = $.map(o.disabled,
			function(n, i) { return n >= index ? ++n : n });
			
		this.tabify();

		if (this.$tabs.length == 1) {
			$li.addClass(o.selectedClass);
			$panel.removeClass(o.hideClass);
			var href = $.data(this.$tabs[0], 'load.tabs');
			if (href)
				this.load(index, href);
		}

		// callback
		this.element.triggerHandler('tabsadd',
			[this.fakeEvent('tabsadd'), this.ui(this.$tabs[index], this.$panels[index])], o.add
		);
	},
	remove: function(index) {
		var o = this.options, $li = this.$lis.eq(index).remove(),
			$panel = this.$panels.eq(index).remove();

		// If selected tab was removed focus tab to the right or
		// in case the last tab was removed the tab to the left.
		if ($li.hasClass(o.selectedClass) && this.$tabs.length > 1)
			this.select(index + (index + 1 < this.$tabs.length ? 1 : -1));

		o.disabled = $.map($.grep(o.disabled, function(n, i) { return n != index; }),
			function(n, i) { return n >= index ? --n : n });

		this.tabify();

		// callback
		this.element.triggerHandler('tabsremove',
			[this.fakeEvent('tabsremove'), this.ui($li.find('a')[0], $panel[0])], o.remove
		);
	},
	enable: function(index) {
		var o = this.options;
		if ($.inArray(index, o.disabled) == -1)
			return;
			
		var $li = this.$lis.eq(index).removeClass(o.disabledClass);
		if ($.browser.safari) { // fix disappearing tab (that used opacity indicating disabling) after enabling in Safari 2...
			$li.css('display', 'inline-block');
			setTimeout(function() {
				$li.css('display', 'block');
			}, 0);
		}

		o.disabled = $.grep(o.disabled, function(n, i) { return n != index; });

		// callback
		this.element.triggerHandler('tabsenable',
			[this.fakeEvent('tabsenable'), this.ui(this.$tabs[index], this.$panels[index])], o.enable
		);

	},
	disable: function(index) {
		var self = this, o = this.options;
		if (index != o.selected) { // cannot disable already selected tab
			this.$lis.eq(index).addClass(o.disabledClass);

			o.disabled.push(index);
			o.disabled.sort();

			// callback
			this.element.triggerHandler('tabsdisable',
				[this.fakeEvent('tabsdisable'), this.ui(this.$tabs[index], this.$panels[index])], o.disable
			);
		}
	},
	select: function(index) {
		if (typeof index == 'string')
			index = this.$tabs.index( this.$tabs.filter('[href$=' + index + ']')[0] );
		this.$tabs.eq(index).trigger(this.options.event);
	},
	load: function(index, callback) { // callback is for internal usage only
		
		var self = this, o = this.options, $a = this.$tabs.eq(index), a = $a[0],
				bypassCache = callback == undefined || callback === false, url = $a.data('load.tabs');

		callback = callback || function() {};
		
		// no remote or from cache - just finish with callback
		if (!url || !bypassCache && $.data(a, 'cache.tabs')) {
			callback();
			return;
		}

		// load remote from here on
		
		var inner = function(parent) {
			var $parent = $(parent), $inner = $parent.find('*:last');
			return $inner.length && $inner.is(':not(img)') && $inner || $parent;
		};
		var cleanup = function() {
			self.$tabs.filter('.' + o.loadingClass).removeClass(o.loadingClass)
						.each(function() {
							if (o.spinner)
								inner(this).parent().html(inner(this).data('label.tabs'));
						});
			self.xhr = null;
		};
		
		if (o.spinner) {
			var label = inner(a).html();
			inner(a).wrapInner('<em></em>')
				.find('em').data('label.tabs', label).html(o.spinner);
		}

		var ajaxOptions = $.extend({}, o.ajaxOptions, {
			url: url,
			success: function(r, s) {
				$(a.hash).html(r);
				cleanup();
				
				if (o.cache)
					$.data(a, 'cache.tabs', true); // if loaded once do not load them again

				// callbacks
				$(self.element).triggerHandler('tabsload',
					[self.fakeEvent('tabsload'), self.ui(self.$tabs[index], self.$panels[index])], o.load
				);
				o.ajaxOptions.success && o.ajaxOptions.success(r, s);
				
				// This callback is required because the switch has to take
				// place after loading has completed. Call last in order to 
				// fire load before show callback...
				callback();
			}
		});
		if (this.xhr) {
			// terminate pending requests from other tabs and restore tab label
			this.xhr.abort();
			cleanup();
		}
		$a.addClass(o.loadingClass);
		setTimeout(function() { // timeout is again required in IE, "wait" for id being restored
			self.xhr = $.ajax(ajaxOptions);
		}, 0);

	},
	url: function(index, url) {
		this.$tabs.eq(index).removeData('cache.tabs').data('load.tabs', url);
	},
	destroy: function() {
		var o = this.options;
		this.element.unbind('.tabs')
			.removeClass(o.navClass).removeData('tabs');
		this.$tabs.each(function() {
			var href = $.data(this, 'href.tabs');
			if (href)
				this.href = href;
			var $this = $(this).unbind('.tabs');
			$.each(['href', 'load', 'cache'], function(i, prefix) {
				$this.removeData(prefix + '.tabs');
			});
		});
		this.$lis.add(this.$panels).each(function() {
			if ($.data(this, 'destroy.tabs'))
				$(this).remove();
			else
				$(this).removeClass([o.selectedClass, o.unselectClass,
					o.disabledClass, o.panelClass, o.hideClass].join(' '));
		});
	},
	fakeEvent: function(type) {
		return $.event.fix({
			type: type,
			target: this.element[0]
		});
	}
});

$.ui.tabs.defaults = {
	// basic setup
	unselect: false,
	event: 'click',
	disabled: [],
	cookie: null, // e.g. { expires: 7, path: '/', domain: 'jquery.com', secure: true }
	// TODO history: false,

	// Ajax
	spinner: 'Loading&#8230;',
	cache: false,
	idPrefix: 'ui-tabs-',
	ajaxOptions: {},

	// animations
	fx: null, // e.g. { height: 'toggle', opacity: 'toggle', duration: 200 }

	// templates
	tabTemplate: '<li><a href="#{href}"><span>#{label}</span></a></li>',
	panelTemplate: '<div></div>',

	// CSS classes
	navClass: 'ui-tabs-nav',
	selectedClass: 'ui-tabs-selected',
	unselectClass: 'ui-tabs-unselect',
	disabledClass: 'ui-tabs-disabled',
	panelClass: 'ui-tabs-panel',
	hideClass: 'ui-tabs-hide',
	loadingClass: 'ui-tabs-loading'
};

$.ui.tabs.getter = "length";

/*
 * Tabs Extensions
 */

/*
 * Rotate
 */
$.extend($.ui.tabs.prototype, {
	rotation: null,
	rotate: function(ms, continuing) {
		
		continuing = continuing || false;
		
		var self = this, t = this.options.selected;
		
		function start() {
			self.rotation = setInterval(function() {
				t = ++t < self.$tabs.length ? t : 0;
				self.select(t);
			}, ms); 
		}
		
		function stop(e) {
			if (!e || e.clientX) { // only in case of a true click
				clearInterval(self.rotation);
			}
		}
		
		// start interval
		if (ms) {
			start();
			if (!continuing)
				this.$tabs.bind(this.options.event, stop);
			else
				this.$tabs.bind(this.options.event, function() {
					stop();
					t = self.options.selected;
					start();
				});
		}
		// stop interval
		else {
			stop();
			this.$tabs.unbind(this.options.event, stop);
		}
	}
});

})(jQuery);
/*
 * jQuery UI Datepicker
 *
 * Copyright (c) 2006, 2007, 2008 Marc Grabanski
 * Dual licensed under the MIT (MIT-LICENSE.txt)
 * and GPL (GPL-LICENSE.txt) licenses.
 * 
 * http://docs.jquery.com/UI/Datepicker
 *
 * Depends:
 *	ui.core.js
 *
 * Marc Grabanski (m@marcgrabanski.com) and Keith Wood (kbwood@virginbroadband.com.au).
 */
   
(function($) { // hide the namespace

var PROP_NAME = 'datepicker';

/* Date picker manager.
   Use the singleton instance of this class, $.datepicker, to interact with the date picker.
   Settings for (groups of) date pickers are maintained in an instance object,
   allowing multiple different settings on the same page. */

function Datepicker() {
	this.debug = false; // Change this to true to start debugging
	this._curInst = null; // The current instance in use
	this._disabledInputs = []; // List of date picker inputs that have been disabled
	this._datepickerShowing = false; // True if the popup picker is showing , false if not
	this._inDialog = false; // True if showing within a "dialog", false if not
	this._mainDivId = 'ui-datepicker-div'; // The ID of the main datepicker division
	this._appendClass = 'ui-datepicker-append'; // The name of the append marker class
	this._triggerClass = 'ui-datepicker-trigger'; // The name of the trigger marker class
	this._dialogClass = 'ui-datepicker-dialog'; // The name of the dialog marker class
	this._promptClass = 'ui-datepicker-prompt'; // The name of the dialog prompt marker class
	this._unselectableClass = 'ui-datepicker-unselectable'; // The name of the unselectable cell marker class
	this._currentClass = 'ui-datepicker-current-day'; // The name of the current day marker class
	this.regional = []; // Available regional settings, indexed by language code
	this.regional[''] = { // Default regional settings
		clearText: 'Clear', // Display text for clear link
		clearStatus: 'Erase the current date', // Status text for clear link
		closeText: 'Close', // Display text for close link
		closeStatus: 'Close without change', // Status text for close link
		prevText: '&#x3c;Prev', // Display text for previous month link
		prevStatus: 'Show the previous month', // Status text for previous month link
		nextText: 'Next&#x3e;', // Display text for next month link
		nextStatus: 'Show the next month', // Status text for next month link
		currentText: 'Today', // Display text for current month link
		currentStatus: 'Show the current month', // Status text for current month link
		monthNames: ['January','February','March','April','May','June',
			'July','August','September','October','November','December'], // Names of months for drop-down and formatting
		monthNamesShort: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], // For formatting
		monthStatus: 'Show a different month', // Status text for selecting a month
		yearStatus: 'Show a different year', // Status text for selecting a year
		weekHeader: 'Wk', // Header for the week of the year column
		weekStatus: 'Week of the year', // Status text for the week of the year column
		dayNames: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'], // For formatting
		dayNamesShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'], // For formatting
		dayNamesMin: ['Su','Mo','Tu','We','Th','Fr','Sa'], // Column headings for days starting at Sunday
		dayStatus: 'Set DD as first week day', // Status text for the day of the week selection
		dateStatus: 'Select DD, M d', // Status text for the date selection
		dateFormat: 'mm/dd/yy', // See format options on parseDate
		firstDay: 0, // The first day of the week, Sun = 0, Mon = 1, ...
		initStatus: 'Select a date', // Initial Status text on opening
		isRTL: false // True if right-to-left language, false if left-to-right
	};
	this._defaults = { // Global defaults for all the date picker instances
		showOn: 'focus', // 'focus' for popup on focus,
			// 'button' for trigger button, or 'both' for either
		showAnim: 'show', // Name of jQuery animation for popup
		showOptions: {}, // Options for enhanced animations
		defaultDate: null, // Used when field is blank: actual date,
			// +/-number for offset from today, null for today
		appendText: '', // Display text following the input box, e.g. showing the format
		buttonText: '...', // Text for trigger button
		buttonImage: '', // URL for trigger button image
		buttonImageOnly: false, // True if the image appears alone, false if it appears on a button
		closeAtTop: true, // True to have the clear/close at the top,
			// false to have them at the bottom
		mandatory: false, // True to hide the Clear link, false to include it
		hideIfNoPrevNext: false, // True to hide next/previous month links
			// if not applicable, false to just disable them
		navigationAsDateFormat: false, // True if date formatting applied to prev/today/next links
		gotoCurrent: false, // True if today link goes back to current selection instead
		changeMonth: true, // True if month can be selected directly, false if only prev/next
		changeYear: true, // True if year can be selected directly, false if only prev/next
		yearRange: '-10:+10', // Range of years to display in drop-down,
			// either relative to current year (-nn:+nn) or absolute (nnnn:nnnn)
		changeFirstDay: true, // True to click on day name to change, false to remain as set
		highlightWeek: false, // True to highlight the selected week
		showOtherMonths: false, // True to show dates in other months, false to leave blank
		showWeeks: false, // True to show week of the year, false to omit
		calculateWeek: this.iso8601Week, // How to calculate the week of the year,
			// takes a Date and returns the number of the week for it
		shortYearCutoff: '+10', // Short year values < this are in the current century,
			// > this are in the previous century, 
			// string value starting with '+' for current year + value
		showStatus: false, // True to show status bar at bottom, false to not show it
		statusForDate: this.dateStatus, // Function to provide status text for a date -
			// takes date and instance as parameters, returns display text
		minDate: null, // The earliest selectable date, or null for no limit
		maxDate: null, // The latest selectable date, or null for no limit
		duration: 'normal', // Duration of display/closure
		beforeShowDay: null, // Function that takes a date and returns an array with
			// [0] = true if selectable, false if not, [1] = custom CSS class name(s) or '', 
			// [2] = cell title (optional), e.g. $.datepicker.noWeekends
		beforeShow: null, // Function that takes an input field and
			// returns a set of custom settings for the date picker
		onSelect: null, // Define a callback function when a date is selected
		onChangeMonthYear: null, // Define a callback function when the month or year is changed
		onClose: null, // Define a callback function when the datepicker is closed
		numberOfMonths: 1, // Number of months to show at a time
		stepMonths: 1, // Number of months to step back/forward
		rangeSelect: false, // Allows for selecting a date range on one date picker
		rangeSeparator: ' - ', // Text between two dates in a range
		altField: '', // Selector for an alternate field to store selected dates into
		altFormat: '' // The date format to use for the alternate field
	};
	$.extend(this._defaults, this.regional['']);
	this.dpDiv = $('<div id="' + this._mainDivId + '" style="display: none;"></div>');
}

$.extend(Datepicker.prototype, {
	/* Class name added to elements to indicate already configured with a date picker. */
	markerClassName: 'hasDatepicker',

	/* Debug logging (if enabled). */
	log: function () {
		if (this.debug)
			console.log.apply('', arguments);
	},
	
	/* Override the default settings for all instances of the date picker. 
	   @param  settings  object - the new settings to use as defaults (anonymous object)
	   @return the manager object */
	setDefaults: function(settings) {
		extendRemove(this._defaults, settings || {});
		return this;
	},

	/* Attach the date picker to a jQuery selection.
	   @param  target    element - the target input field or division or span
	   @param  settings  object - the new settings to use for this date picker instance (anonymous) */
	_attachDatepicker: function(target, settings) {
		// check for settings on the control itself - in namespace 'date:'
		var inlineSettings = null;
		for (attrName in this._defaults) {
			var attrValue = target.getAttribute('date:' + attrName);
			if (attrValue) {
				inlineSettings = inlineSettings || {};
				try {
					inlineSettings[attrName] = eval(attrValue);
				} catch (err) {
					inlineSettings[attrName] = attrValue;
				}
			}
		}
		var nodeName = target.nodeName.toLowerCase();
		var inline = (nodeName == 'div' || nodeName == 'span');
		if (!target.id)
			target.id = 'dp' + new Date().getTime();
		var inst = this._newInst($(target), inline);
		inst.settings = $.extend({}, settings || {}, inlineSettings || {}); 
		if (nodeName == 'input') {
			this._connectDatepicker(target, inst);
		} else if (inline) {
			this._inlineDatepicker(target, inst);
		}
	},

	/* Create a new instance object. */
	_newInst: function(target, inline) {
		return {id: target[0].id, input: target, // associated target
			selectedDay: 0, selectedMonth: 0, selectedYear: 0, // current selection
			drawMonth: 0, drawYear: 0, // month being drawn
			inline: inline, // is datepicker inline or not
			dpDiv: (!inline ? this.dpDiv : // presentation div
			$('<div class="ui-datepicker-inline"></div>'))};
	},

	/* Attach the date picker to an input field. */
	_connectDatepicker: function(target, inst) {
		var input = $(target);
		if (input.hasClass(this.markerClassName))
			return;
		var appendText = this._get(inst, 'appendText');
		var isRTL = this._get(inst, 'isRTL');
		if (appendText)
			input[isRTL ? 'before' : 'after']('<span class="' + this._appendClass + '">' + appendText + '</span>');
		var showOn = this._get(inst, 'showOn');
		if (showOn == 'focus' || showOn == 'both') // pop-up date picker when in the marked field
			input.focus(this._showDatepicker);
		if (showOn == 'button' || showOn == 'both') { // pop-up date picker when button clicked
			var buttonText = this._get(inst, 'buttonText');
			var buttonImage = this._get(inst, 'buttonImage');
			var trigger = $(this._get(inst, 'buttonImageOnly') ? 
				$('<img/>').addClass(this._triggerClass).
					attr({ src: buttonImage, alt: buttonText, title: buttonText }) :
				$('<button type="button"></button>').addClass(this._triggerClass).
					html(buttonImage == '' ? buttonText : $('<img/>').attr(
					{ src:buttonImage, alt:buttonText, title:buttonText })));
			input[isRTL ? 'before' : 'after'](trigger);
			trigger.click(function() {
				if ($.datepicker._datepickerShowing && $.datepicker._lastInput == target)
					$.datepicker._hideDatepicker();
				else
					$.datepicker._showDatepicker(target);
				return false;
			});
		}
		input.addClass(this.markerClassName).keydown(this._doKeyDown).keypress(this._doKeyPress).
			bind("setData.datepicker", function(event, key, value) {
				inst.settings[key] = value;
			}).bind("getData.datepicker", function(event, key) {
				return this._get(inst, key);
			});
		$.data(target, PROP_NAME, inst);
	},

	/* Attach an inline date picker to a div. */
	_inlineDatepicker: function(target, inst) {
		var input = $(target);
		if (input.hasClass(this.markerClassName))
			return;
		input.addClass(this.markerClassName).append(inst.dpDiv).
			bind("setData.datepicker", function(event, key, value){
				inst.settings[key] = value;
			}).bind("getData.datepicker", function(event, key){
				return this._get(inst, key);
			});
		$.data(target, PROP_NAME, inst);
		this._setDate(inst, this._getDefaultDate(inst));
		this._updateDatepicker(inst);
	},

	/* Tidy up after displaying the date picker. */
	_inlineShow: function(inst) {
		var numMonths = this._getNumberOfMonths(inst); // fix width for dynamic number of date pickers
		inst.dpDiv.width(numMonths[1] * $('.ui-datepicker', inst.dpDiv[0]).width());
	}, 

	/* Pop-up the date picker in a "dialog" box.
	   @param  input     element - ignored
	   @param  dateText  string - the initial date to display (in the current format)
	   @param  onSelect  function - the function(dateText) to call when a date is selected
	   @param  settings  object - update the dialog date picker instance's settings (anonymous object)
	   @param  pos       int[2] - coordinates for the dialog's position within the screen or
	                     event - with x/y coordinates or
	                     leave empty for default (screen centre)
	   @return the manager object */
	_dialogDatepicker: function(input, dateText, onSelect, settings, pos) {
		var inst = this._dialogInst; // internal instance
		if (!inst) {
			var id = 'dp' + new Date().getTime();
			this._dialogInput = $('<input type="text" id="' + id +
				'" size="1" style="position: absolute; top: -100px;"/>');
			this._dialogInput.keydown(this._doKeyDown);
			$('body').append(this._dialogInput);
			inst = this._dialogInst = this._newInst(this._dialogInput, false);
			inst.settings = {};
			$.data(this._dialogInput[0], PROP_NAME, inst);
		}
		extendRemove(inst.settings, settings || {});
		this._dialogInput.val(dateText);

		this._pos = (pos ? (pos.length ? pos : [pos.pageX, pos.pageY]) : null);
		if (!this._pos) {
			var browserWidth = window.innerWidth || document.documentElement.clientWidth ||	document.body.clientWidth;
			var browserHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight;
			var scrollX = document.documentElement.scrollLeft || document.body.scrollLeft;
			var scrollY = document.documentElement.scrollTop || document.body.scrollTop;
			this._pos = // should use actual width/height below
				[(browserWidth / 2) - 100 + scrollX, (browserHeight / 2) - 150 + scrollY];
		}

		// move input on screen for focus, but hidden behind dialog
		this._dialogInput.css('left', this._pos[0] + 'px').css('top', this._pos[1] + 'px');
		inst.settings.onSelect = onSelect;
		this._inDialog = true;
		this.dpDiv.addClass(this._dialogClass);
		this._showDatepicker(this._dialogInput[0]);
		if ($.blockUI)
			$.blockUI(this.dpDiv);
		$.data(this._dialogInput[0], PROP_NAME, inst);
		return this;
	},

	/* Detach a datepicker from its control.
	   @param  target    element - the target input field or division or span */
	_destroyDatepicker: function(target) {
		var nodeName = target.nodeName.toLowerCase();
		var $target = $(target);
		$.removeData(target, PROP_NAME);
		if (nodeName == 'input') {
			$target.siblings('.' + this._appendClass).remove().end().
				siblings('.' + this._triggerClass).remove().end().
				removeClass(this.markerClassName).
				unbind('focus', this._showDatepicker).
				unbind('keydown', this._doKeyDown).
				unbind('keypress', this._doKeyPress);
		} else if (nodeName == 'div' || nodeName == 'span')
			$target.removeClass(this.markerClassName).empty();
	},

	/* Enable the date picker to a jQuery selection.
	   @param  target    element - the target input field or division or span */
	_enableDatepicker: function(target) {
		target.disabled = false;
		$(target).siblings('button.' + this._triggerClass).each(function() { this.disabled = false; }).end().
			siblings('img.' + this._triggerClass).css({opacity: '1.0', cursor: ''});
		this._disabledInputs = $.map(this._disabledInputs,
			function(value) { return (value == target ? null : value); }); // delete entry
	},

	/* Disable the date picker to a jQuery selection.
	   @param  target    element - the target input field or division or span */
	_disableDatepicker: function(target) {
		target.disabled = true;
		$(target).siblings('button.' + this._triggerClass).each(function() { this.disabled = true; }).end().
			siblings('img.' + this._triggerClass).css({opacity: '0.5', cursor: 'default'});
		this._disabledInputs = $.map(this._disabledInputs,
			function(value) { return (value == target ? null : value); }); // delete entry
		this._disabledInputs[this._disabledInputs.length] = target;
	},

	/* Is the first field in a jQuery collection disabled as a datepicker?
	   @param  target    element - the target input field or division or span
	   @return boolean - true if disabled, false if enabled */
	_isDisabledDatepicker: function(target) {
		if (!target)
			return false;
		for (var i = 0; i < this._disabledInputs.length; i++) {
			if (this._disabledInputs[i] == target)
				return true;
		}
		return false;
	},

	/* Update the settings for a date picker attached to an input field or division.
	   @param  target  element - the target input field or division or span
	   @param  name    object - the new settings to update or
	                   string - the name of the setting to change or
	   @param  value   any - the new value for the setting (omit if above is an object) */
	_changeDatepicker: function(target, name, value) {
		var settings = name || {};
		if (typeof name == 'string') {
			settings = {};
			settings[name] = value;
		}
		if (inst = $.data(target, PROP_NAME)) {
			extendRemove(inst.settings, settings);
			this._updateDatepicker(inst);
		}
	},

	/* Set the dates for a jQuery selection.
	   @param  target   element - the target input field or division or span
	   @param  date     Date - the new date
	   @param  endDate  Date - the new end date for a range (optional) */
	_setDateDatepicker: function(target, date, endDate) {
		var inst = $.data(target, PROP_NAME);
		if (inst) {
			this._setDate(inst, date, endDate);
			this._updateDatepicker(inst);
		}
	},

	/* Get the date(s) for the first entry in a jQuery selection.
	   @param  target  element - the target input field or division or span
	   @return Date - the current date or
	           Date[2] - the current dates for a range */
	_getDateDatepicker: function(target) {
		var inst = $.data(target, PROP_NAME);
		if (inst)
			this._setDateFromField(inst); 
		return (inst ? this._getDate(inst) : null);
	},

	/* Handle keystrokes. */
	_doKeyDown: function(e) {
		var inst = $.data(e.target, PROP_NAME);
		var handled = true;
		if ($.datepicker._datepickerShowing)
			switch (e.keyCode) {
				case 9:  $.datepicker._hideDatepicker(null, '');
						break; // hide on tab out
				case 13: $.datepicker._selectDay(e.target, inst.selectedMonth, inst.selectedYear,
							$('td.ui-datepicker-days-cell-over', inst.dpDiv)[0]);
						return false; // don't submit the form
						break; // select the value on enter
				case 27: $.datepicker._hideDatepicker(null, $.datepicker._get(inst, 'duration'));
						break; // hide on escape
				case 33: $.datepicker._adjustDate(e.target, (e.ctrlKey ? -1 :
							-$.datepicker._get(inst, 'stepMonths')), (e.ctrlKey ? 'Y' : 'M'));
						break; // previous month/year on page up/+ ctrl
				case 34: $.datepicker._adjustDate(e.target, (e.ctrlKey ? +1 :
							+$.datepicker._get(inst, 'stepMonths')), (e.ctrlKey ? 'Y' : 'M'));
						break; // next month/year on page down/+ ctrl
				case 35: if (e.ctrlKey) $.datepicker._clearDate(e.target);
						break; // clear on ctrl+end
				case 36: if (e.ctrlKey) $.datepicker._gotoToday(e.target);
						break; // current on ctrl+home
				case 37: if (e.ctrlKey) $.datepicker._adjustDate(e.target, -1, 'D');
						break; // -1 day on ctrl+left
				case 38: if (e.ctrlKey) $.datepicker._adjustDate(e.target, -7, 'D');
						break; // -1 week on ctrl+up
				case 39: if (e.ctrlKey) $.datepicker._adjustDate(e.target, +1, 'D');
						break; // +1 day on ctrl+right
				case 40: if (e.ctrlKey) $.datepicker._adjustDate(e.target, +7, 'D');
						break; // +1 week on ctrl+down
				default: handled = false;
			}
		else if (e.keyCode == 36 && e.ctrlKey) // display the date picker on ctrl+home
			$.datepicker._showDatepicker(this);
		else
			handled = false;
		if (handled) {
			e.preventDefault();
			e.stopPropagation();
		}
	},

	/* Filter entered characters - based on date format. */
	_doKeyPress: function(e) {
		var inst = $.data(e.target, PROP_NAME);
		var chars = $.datepicker._possibleChars($.datepicker._get(inst, 'dateFormat'));
		var chr = String.fromCharCode(e.charCode == undefined ? e.keyCode : e.charCode);
		return e.ctrlKey || (chr < ' ' || !chars || chars.indexOf(chr) > -1);
	},

	/* Pop-up the date picker for a given input field.
	   @param  input  element - the input field attached to the date picker or
	                  event - if triggered by focus */
	_showDatepicker: function(input) {
		input = input.target || input;
		if (input.nodeName.toLowerCase() != 'input') // find from button/image trigger
			input = $('input', input.parentNode)[0];
		if ($.datepicker._isDisabledDatepicker(input) || $.datepicker._lastInput == input) // already here
			return;
		var inst = $.data(input, PROP_NAME);
		var beforeShow = $.datepicker._get(inst, 'beforeShow');
		extendRemove(inst.settings, (beforeShow ? beforeShow.apply(input, [input, inst]) : {}));
		$.datepicker._hideDatepicker(null, '');
		$.datepicker._lastInput = input;
		$.datepicker._setDateFromField(inst);
		if ($.datepicker._inDialog) // hide cursor
			input.value = '';
		if (!$.datepicker._pos) { // position below input
			$.datepicker._pos = $.datepicker._findPos(input);
			$.datepicker._pos[1] += input.offsetHeight; // add the height
		}
		var isFixed = false;
		$(input).parents().each(function() {
			isFixed |= $(this).css('position') == 'fixed';
			return !isFixed;
		});
		if (isFixed && $.browser.opera) { // correction for Opera when fixed and scrolled
			$.datepicker._pos[0] -= document.documentElement.scrollLeft;
			$.datepicker._pos[1] -= document.documentElement.scrollTop;
		}
		var offset = {left: $.datepicker._pos[0], top: $.datepicker._pos[1]};
		$.datepicker._pos = null;
		inst.rangeStart = null;
		// determine sizing offscreen
		inst.dpDiv.css({position: 'absolute', display: 'block', top: '-1000px'});
		$.datepicker._updateDatepicker(inst);
		// fix width for dynamic number of date pickers
		inst.dpDiv.width($.datepicker._getNumberOfMonths(inst)[1] *
			$('.ui-datepicker', inst.dpDiv[0])[0].offsetWidth);
		// and adjust position before showing
		offset = $.datepicker._checkOffset(inst, offset, isFixed);
		inst.dpDiv.css({position: ($.datepicker._inDialog && $.blockUI ?
			'static' : (isFixed ? 'fixed' : 'absolute')), display: 'none',
			left: offset.left + 'px', top: offset.top + 'px'});
		if (!inst.inline) {
			var showAnim = $.datepicker._get(inst, 'showAnim') || 'show';
			var duration = $.datepicker._get(inst, 'duration');
			var postProcess = function() {
				$.datepicker._datepickerShowing = true;
				if ($.browser.msie && parseInt($.browser.version) < 7) // fix IE < 7 select problems
					$('iframe.ui-datepicker-cover').css({width: inst.dpDiv.width() + 4,
						height: inst.dpDiv.height() + 4});
			};
			if ($.effects && $.effects[showAnim])
				inst.dpDiv.show(showAnim, $.datepicker._get(inst, 'showOptions'), duration, postProcess);
			else
				inst.dpDiv[showAnim](duration, postProcess);
			if (duration == '')
				postProcess();
			if (inst.input[0].type != 'hidden')
				inst.input[0].focus();
			$.datepicker._curInst = inst;
		}
	},

	/* Generate the date picker content. */
	_updateDatepicker: function(inst) {
		var dims = {width: inst.dpDiv.width() + 4,
			height: inst.dpDiv.height() + 4};
		inst.dpDiv.empty().append(this._generateDatepicker(inst)).
			find('iframe.ui-datepicker-cover').
			css({width: dims.width, height: dims.height});
		var numMonths = this._getNumberOfMonths(inst);
		inst.dpDiv[(numMonths[0] != 1 || numMonths[1] != 1 ? 'add' : 'remove') +
			'Class']('ui-datepicker-multi');
		inst.dpDiv[(this._get(inst, 'isRTL') ? 'add' : 'remove') +
			'Class']('ui-datepicker-rtl');
		if (inst.input && inst.input[0].type != 'hidden')
			$(inst.input[0]).focus();
	},

	/* Check positioning to remain on screen. */
	_checkOffset: function(inst, offset, isFixed) {
		var pos = inst.input ? this._findPos(inst.input[0]) : null;
		var browserWidth = window.innerWidth || document.documentElement.clientWidth;
		var browserHeight = window.innerHeight || document.documentElement.clientHeight;
		var scrollX = document.documentElement.scrollLeft || document.body.scrollLeft;
		var scrollY = document.documentElement.scrollTop || document.body.scrollTop;
		// reposition date picker horizontally if outside the browser window
		if (this._get(inst, 'isRTL') || (offset.left + inst.dpDiv.width() - scrollX) > browserWidth)
			offset.left = Math.max((isFixed ? 0 : scrollX),
				pos[0] + (inst.input ? inst.input.width() : 0) - (isFixed ? scrollX : 0) - inst.dpDiv.width() -
				(isFixed && $.browser.opera ? document.documentElement.scrollLeft : 0));
		else
			offset.left -= (isFixed ? scrollX : 0);
		// reposition date picker vertically if outside the browser window
		if ((offset.top + inst.dpDiv.height() - scrollY) > browserHeight)
			offset.top = Math.max((isFixed ? 0 : scrollY),
				pos[1] - (isFixed ? scrollY : 0) - (this._inDialog ? 0 : inst.dpDiv.height()) -
				(isFixed && $.browser.opera ? document.documentElement.scrollTop : 0));
		else
			offset.top -= (isFixed ? scrollY : 0);
		return offset;
	},
	
	/* Find an object's position on the screen. */
	_findPos: function(obj) {
        while (obj && (obj.type == 'hidden' || obj.nodeType != 1)) {
            obj = obj.nextSibling;
        }
        var position = $(obj).offset();
	    return [position.left, position.top];
	},

	/* Hide the date picker from view.
	   @param  input  element - the input field attached to the date picker
	   @param  duration  string - the duration over which to close the date picker */
	_hideDatepicker: function(input, duration) {
		var inst = this._curInst;
		if (!inst)
			return;
		var rangeSelect = this._get(inst, 'rangeSelect');
		if (rangeSelect && this._stayOpen)
			this._selectDate('#' + inst.id, this._formatDate(inst,
				inst.currentDay, inst.currentMonth, inst.currentYear));
		this._stayOpen = false;
		if (this._datepickerShowing) {
			duration = (duration != null ? duration : this._get(inst, 'duration'));
			var showAnim = this._get(inst, 'showAnim');
			var postProcess = function() {
				$.datepicker._tidyDialog(inst);
			};
			if (duration != '' && $.effects && $.effects[showAnim])
				inst.dpDiv.hide(showAnim, $.datepicker._get(inst, 'showOptions'),
					duration, postProcess);
			else
				inst.dpDiv[(duration == '' ? 'hide' : (showAnim == 'slideDown' ? 'slideUp' :
					(showAnim == 'fadeIn' ? 'fadeOut' : 'hide')))](duration, postProcess);
			if (duration == '')
				this._tidyDialog(inst);
			var onClose = this._get(inst, 'onClose');
			if (onClose)
				onClose.apply((inst.input ? inst.input[0] : null),
					[this._getDate(inst), inst]);  // trigger custom callback
			this._datepickerShowing = false;
			this._lastInput = null;
			inst.settings.prompt = null;
			if (this._inDialog) {
				this._dialogInput.css({ position: 'absolute', left: '0', top: '-100px' });
				if ($.blockUI) {
					$.unblockUI();
					$('body').append(this.dpDiv);
				}
			}
			this._inDialog = false;
		}
		this._curInst = null;
	},

	/* Tidy up after a dialog display. */
	_tidyDialog: function(inst) {
		inst.dpDiv.removeClass(this._dialogClass).unbind('.ui-datepicker');
		$('.' + this._promptClass, inst.dpDiv).remove();
	},

	/* Close date picker if clicked elsewhere. */
	_checkExternalClick: function(event) {
		if (!$.datepicker._curInst)
			return;
		var $target = $(event.target);
		if (($target.parents('#' + $.datepicker._mainDivId).length == 0) &&
				!$target.hasClass($.datepicker.markerClassName) &&
				!$target.hasClass($.datepicker._triggerClass) &&
				$.datepicker._datepickerShowing && !($.datepicker._inDialog && $.blockUI))
			$.datepicker._hideDatepicker(null, '');
	},

	/* Adjust one of the date sub-fields. */
	_adjustDate: function(id, offset, period) {
		var target = $(id);
		var inst = $.data(target[0], PROP_NAME);
		this._adjustInstDate(inst, offset, period);
		this._updateDatepicker(inst);
	},

	/* Action for current link. */
	_gotoToday: function(id) {
		var target = $(id);
		var inst = $.data(target[0], PROP_NAME);
		if (this._get(inst, 'gotoCurrent') && inst.currentDay) {
			inst.selectedDay = inst.currentDay;
			inst.drawMonth = inst.selectedMonth = inst.currentMonth;
			inst.drawYear = inst.selectedYear = inst.currentYear;
		}
		else {
		var date = new Date();
		inst.selectedDay = date.getDate();
		inst.drawMonth = inst.selectedMonth = date.getMonth();
		inst.drawYear = inst.selectedYear = date.getFullYear();
		}
		this._adjustDate(target);
		this._notifyChange(inst);
	},

	/* Action for selecting a new month/year. */
	_selectMonthYear: function(id, select, period) {
		var target = $(id);
		var inst = $.data(target[0], PROP_NAME);
		inst._selectingMonthYear = false;
		inst[period == 'M' ? 'drawMonth' : 'drawYear'] =
			select.options[select.selectedIndex].value - 0;
		this._adjustDate(target);
		this._notifyChange(inst);
	},

	/* Restore input focus after not changing month/year. */
	_clickMonthYear: function(id) {
		var target = $(id);
		var inst = $.data(target[0], PROP_NAME);
		if (inst.input && inst._selectingMonthYear && !$.browser.msie)
			inst.input[0].focus();
		inst._selectingMonthYear = !inst._selectingMonthYear;
	},

	/* Action for changing the first week day. */
	_changeFirstDay: function(id, day) {
		var target = $(id);
		var inst = $.data(target[0], PROP_NAME);
		inst.settings.firstDay = day;
		this._updateDatepicker(inst);
	},

	/* Action for selecting a day. */
	_selectDay: function(id, month, year, td) {
		if ($(td).hasClass(this._unselectableClass))
			return;
		var target = $(id);
		var inst = $.data(target[0], PROP_NAME);
		var rangeSelect = this._get(inst, 'rangeSelect');
		if (rangeSelect) {
			this._stayOpen = !this._stayOpen;
			if (this._stayOpen) {
				$('.ui-datepicker td').removeClass(this._currentClass);
				$(td).addClass(this._currentClass);
			} 
		}
		inst.selectedDay = inst.currentDay = $('a', td).html();
		inst.selectedMonth = inst.currentMonth = month;
		inst.selectedYear = inst.currentYear = year;
		if (this._stayOpen) {
			inst.endDay = inst.endMonth = inst.endYear = null;
		}
		else if (rangeSelect) {
			inst.endDay = inst.currentDay;
			inst.endMonth = inst.currentMonth;
			inst.endYear = inst.currentYear;
		}
		this._selectDate(id, this._formatDate(inst,
			inst.currentDay, inst.currentMonth, inst.currentYear));
		if (this._stayOpen) {
			inst.rangeStart = new Date(inst.currentYear, inst.currentMonth, inst.currentDay);
			this._updateDatepicker(inst);
		}
		else if (rangeSelect) {
			inst.selectedDay = inst.currentDay = inst.rangeStart.getDate();
			inst.selectedMonth = inst.currentMonth = inst.rangeStart.getMonth();
			inst.selectedYear = inst.currentYear = inst.rangeStart.getFullYear();
			inst.rangeStart = null;
			if (inst.inline)
				this._updateDatepicker(inst);
		}
	},

	/* Erase the input field and hide the date picker. */
	_clearDate: function(id) {
		var target = $(id);
		var inst = $.data(target[0], PROP_NAME);
		if (this._get(inst, 'mandatory'))
			return;
		this._stayOpen = false;
		inst.endDay = inst.endMonth = inst.endYear = inst.rangeStart = null;
		this._selectDate(target, '');
	},

	/* Update the input field with the selected date. */
	_selectDate: function(id, dateStr) {
		var target = $(id);
		var inst = $.data(target[0], PROP_NAME);
		dateStr = (dateStr != null ? dateStr : this._formatDate(inst));
		if (this._get(inst, 'rangeSelect') && dateStr)
			dateStr = (inst.rangeStart ? this._formatDate(inst, inst.rangeStart) :
				dateStr) + this._get(inst, 'rangeSeparator') + dateStr;
		if (inst.input)
			inst.input.val(dateStr);
		this._updateAlternate(inst);
		var onSelect = this._get(inst, 'onSelect');
		if (onSelect)
			onSelect.apply((inst.input ? inst.input[0] : null), [dateStr, inst]);  // trigger custom callback
		else if (inst.input)
			inst.input.trigger('change'); // fire the change event
		if (inst.inline)
			this._updateDatepicker(inst);
		else if (!this._stayOpen) {
			this._hideDatepicker(null, this._get(inst, 'duration'));
			this._lastInput = inst.input[0];
			if (typeof(inst.input[0]) != 'object')
				inst.input[0].focus(); // restore focus
			this._lastInput = null;
		}
	},
	
	/* Update any alternate field to synchronise with the main field. */
	_updateAlternate: function(inst) {
		var altField = this._get(inst, 'altField');
		if (altField) { // update alternate field too
			var altFormat = this._get(inst, 'altFormat');
			var date = this._getDate(inst);
			dateStr = (isArray(date) ? (!date[0] && !date[1] ? '' :
				this.formatDate(altFormat, date[0], this._getFormatConfig(inst)) +
				this._get(inst, 'rangeSeparator') + this.formatDate(
				altFormat, date[1] || date[0], this._getFormatConfig(inst))) :
				this.formatDate(altFormat, date, this._getFormatConfig(inst)));
			$(altField).each(function() { $(this).val(dateStr); });
		}
	},

	/* Set as beforeShowDay function to prevent selection of weekends.
	   @param  date  Date - the date to customise
	   @return [boolean, string] - is this date selectable?, what is its CSS class? */
	noWeekends: function(date) {
		var day = date.getDay();
		return [(day > 0 && day < 6), ''];
	},
	
	/* Set as calculateWeek to determine the week of the year based on the ISO 8601 definition.
	   @param  date  Date - the date to get the week for
	   @return  number - the number of the week within the year that contains this date */
	iso8601Week: function(date) {
		var checkDate = new Date(date.getFullYear(), date.getMonth(), date.getDate(), (date.getTimezoneOffset() / -60));
		var firstMon = new Date(checkDate.getFullYear(), 1 - 1, 4); // First week always contains 4 Jan
		var firstDay = firstMon.getDay() || 7; // Day of week: Mon = 1, ..., Sun = 7
		firstMon.setDate(firstMon.getDate() + 1 - firstDay); // Preceding Monday
		if (firstDay < 4 && checkDate < firstMon) { // Adjust first three days in year if necessary
			checkDate.setDate(checkDate.getDate() - 3); // Generate for previous year
			return $.datepicker.iso8601Week(checkDate);
		} else if (checkDate > new Date(checkDate.getFullYear(), 12 - 1, 28)) { // Check last three days in year
			firstDay = new Date(checkDate.getFullYear() + 1, 1 - 1, 4).getDay() || 7;
			if (firstDay > 4 && (checkDate.getDay() || 7) < firstDay - 3) { // Adjust if necessary
				checkDate.setDate(checkDate.getDate() + 3); // Generate for next year
				return $.datepicker.iso8601Week(checkDate);
			}
		}
		return Math.floor(((checkDate - firstMon) / 86400000) / 7) + 1; // Weeks to given date
	},
	
	/* Provide status text for a particular date.
	   @param  date  the date to get the status for
	   @param  inst  the current datepicker instance
	   @return  the status display text for this date */
	dateStatus: function(date, inst) {
		return $.datepicker.formatDate($.datepicker._get(inst, 'dateStatus'),
			date, $.datepicker._getFormatConfig(inst));
	},

	/* Parse a string value into a date object.
	   See formatDate below for the possible formats.

	   @param  format    string - the expected format of the date
	   @param  value     string - the date in the above format
	   @param  settings  Object - attributes include:
	                     shortYearCutoff  number - the cutoff year for determining the century (optional)
	                     dayNamesShort    string[7] - abbreviated names of the days from Sunday (optional)
	                     dayNames         string[7] - names of the days from Sunday (optional)
	                     monthNamesShort  string[12] - abbreviated names of the months (optional)
	                     monthNames       string[12] - names of the months (optional)
	   @return  Date - the extracted date value or null if value is blank */
	parseDate: function (format, value, settings) {
		if (format == null || value == null)
			throw 'Invalid arguments';
		value = (typeof value == 'object' ? value.toString() : value + '');
		if (value == '')
			return null;
		var shortYearCutoff = (settings ? settings.shortYearCutoff : null) || this._defaults.shortYearCutoff;
		var dayNamesShort = (settings ? settings.dayNamesShort : null) || this._defaults.dayNamesShort;
		var dayNames = (settings ? settings.dayNames : null) || this._defaults.dayNames;
		var monthNamesShort = (settings ? settings.monthNamesShort : null) || this._defaults.monthNamesShort;
		var monthNames = (settings ? settings.monthNames : null) || this._defaults.monthNames;
		var year = -1;
		var month = -1;
		var day = -1;
		var literal = false;
		// Check whether a format character is doubled
		var lookAhead = function(match) {
			var matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) == match);
			if (matches)
				iFormat++;
			return matches;	
		};
		// Extract a number from the string value
		var getNumber = function(match) {
			lookAhead(match);
			var origSize = (match == '@' ? 14 : (match == 'y' ? 4 : 2));
			var size = origSize;
			var num = 0;
			while (size > 0 && iValue < value.length &&
					value.charAt(iValue) >= '0' && value.charAt(iValue) <= '9') {
				num = num * 10 + (value.charAt(iValue++) - 0);
				size--;
			}
			if (size == origSize)
				throw 'Missing number at position ' + iValue;
			return num;
		};
		// Extract a name from the string value and convert to an index
		var getName = function(match, shortNames, longNames) {
			var names = (lookAhead(match) ? longNames : shortNames);
			var size = 0;
			for (var j = 0; j < names.length; j++)
				size = Math.max(size, names[j].length);
			var name = '';
			var iInit = iValue;
			while (size > 0 && iValue < value.length) {
				name += value.charAt(iValue++);
				for (var i = 0; i < names.length; i++)
					if (name == names[i])
						return i + 1;
				size--;
			}
			throw 'Unknown name at position ' + iInit;
		};
		// Confirm that a literal character matches the string value
		var checkLiteral = function() {
			if (value.charAt(iValue) != format.charAt(iFormat))
				throw 'Unexpected literal at position ' + iValue;
			iValue++;
		};
		var iValue = 0;
		for (var iFormat = 0; iFormat < format.length; iFormat++) {
			if (literal)
				if (format.charAt(iFormat) == "'" && !lookAhead("'"))
					literal = false;
				else
					checkLiteral();
			else
				switch (format.charAt(iFormat)) {
					case 'd':
						day = getNumber('d');
						break;
					case 'D': 
						getName('D', dayNamesShort, dayNames);
						break;
					case 'm': 
						month = getNumber('m');
						break;
					case 'M':
						month = getName('M', monthNamesShort, monthNames); 
						break;
					case 'y':
						year = getNumber('y');
						break;
					case '@':
						var date = new Date(getNumber('@'));
						year = date.getFullYear();
						month = date.getMonth() + 1;
						day = date.getDate();
						break;
					case "'":
						if (lookAhead("'"))
							checkLiteral();
						else
							literal = true;
						break;
					default:
						checkLiteral();
				}
		}
		if (year < 100)
			year += new Date().getFullYear() - new Date().getFullYear() % 100 +
				(year <= shortYearCutoff ? 0 : -100);
		var date = new Date(year, month - 1, day);
		if (date.getFullYear() != year || date.getMonth() + 1 != month || date.getDate() != day)
			throw 'Invalid date'; // E.g. 31/02/*
		return date;
	},

	/* Standard date formats. */
	ATOM: 'yy-mm-dd', // RFC 3339 (ISO 8601)
	COOKIE: 'D, dd M yy',
	ISO_8601: 'yy-mm-dd',
	RFC_822: 'D, d M y',
	RFC_850: 'DD, dd-M-y',
	RFC_1036: 'D, d M y',
	RFC_1123: 'D, d M yy',
	RFC_2822: 'D, d M yy',
	RSS: 'D, d M y', // RFC 822
	TIMESTAMP: '@',
	W3C: 'yy-mm-dd', // ISO 8601

	/* Format a date object into a string value.
	   The format can be combinations of the following:
	   d  - day of month (no leading zero)
	   dd - day of month (two digit)
	   D  - day name short
	   DD - day name long
	   m  - month of year (no leading zero)
	   mm - month of year (two digit)
	   M  - month name short
	   MM - month name long
	   y  - year (two digit)
	   yy - year (four digit)
	   @ - Unix timestamp (ms since 01/01/1970)
	   '...' - literal text
	   '' - single quote

	   @param  format    string - the desired format of the date
	   @param  date      Date - the date value to format
	   @param  settings  Object - attributes include:
	                     dayNamesShort    string[7] - abbreviated names of the days from Sunday (optional)
	                     dayNames         string[7] - names of the days from Sunday (optional)
	                     monthNamesShort  string[12] - abbreviated names of the months (optional)
	                     monthNames       string[12] - names of the months (optional)
	   @return  string - the date in the above format */
	formatDate: function (format, date, settings) {
		if (!date)
			return '';
		var dayNamesShort = (settings ? settings.dayNamesShort : null) || this._defaults.dayNamesShort;
		var dayNames = (settings ? settings.dayNames : null) || this._defaults.dayNames;
		var monthNamesShort = (settings ? settings.monthNamesShort : null) || this._defaults.monthNamesShort;
		var monthNames = (settings ? settings.monthNames : null) || this._defaults.monthNames;
		// Check whether a format character is doubled
		var lookAhead = function(match) {
			var matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) == match);
			if (matches)
				iFormat++;
			return matches;	
		};
		// Format a number, with leading zero if necessary
		var formatNumber = function(match, value) {
			return (lookAhead(match) && value < 10 ? '0' : '') + value;
		};
		// Format a name, short or long as requested
		var formatName = function(match, value, shortNames, longNames) {
			return (lookAhead(match) ? longNames[value] : shortNames[value]);
		};
		var output = '';
		var literal = false;
		if (date)
			for (var iFormat = 0; iFormat < format.length; iFormat++) {
				if (literal)
					if (format.charAt(iFormat) == "'" && !lookAhead("'"))
						literal = false;
					else
						output += format.charAt(iFormat);
				else
					switch (format.charAt(iFormat)) {
						case 'd':
							output += formatNumber('d', date.getDate()); 
							break;
						case 'D': 
							output += formatName('D', date.getDay(), dayNamesShort, dayNames);
							break;
						case 'm': 
							output += formatNumber('m', date.getMonth() + 1); 
							break;
						case 'M':
							output += formatName('M', date.getMonth(), monthNamesShort, monthNames); 
							break;
						case 'y':
							output += (lookAhead('y') ? date.getFullYear() : 
								(date.getYear() % 100 < 10 ? '0' : '') + date.getYear() % 100);
							break;
						case '@':
							output += date.getTime(); 
							break;
						case "'":
							if (lookAhead("'"))
								output += "'";
							else
								literal = true;
							break;
						default:
							output += format.charAt(iFormat);
					}
			}
		return output;
	},

	/* Extract all possible characters from the date format. */
	_possibleChars: function (format) {
		var chars = '';
		var literal = false;
		for (var iFormat = 0; iFormat < format.length; iFormat++)
			if (literal)
				if (format.charAt(iFormat) == "'" && !lookAhead("'"))
					literal = false;
				else
					chars += format.charAt(iFormat);
			else
				switch (format.charAt(iFormat)) {
					case 'd': case 'm': case 'y': case '@':
						chars += '0123456789'; 
						break;
					case 'D': case 'M':
						return null; // Accept anything
					case "'":
						if (lookAhead("'"))
							chars += "'";
						else
							literal = true;
						break;
					default:
						chars += format.charAt(iFormat);
				}
		return chars;
	},

	/* Get a setting value, defaulting if necessary. */
	_get: function(inst, name) {
		return inst.settings[name] !== undefined ?
			inst.settings[name] : this._defaults[name];
	},

	/* Parse existing date and initialise date picker. */
	_setDateFromField: function(inst) {
		var dateFormat = this._get(inst, 'dateFormat');
		var dates = inst.input ? inst.input.val().split(this._get(inst, 'rangeSeparator')) : null; 
		inst.endDay = inst.endMonth = inst.endYear = null;
		var date = defaultDate = this._getDefaultDate(inst);
		if (dates.length > 0) {
			var settings = this._getFormatConfig(inst);
			if (dates.length > 1) {
				date = this.parseDate(dateFormat, dates[1], settings) || defaultDate;
				inst.endDay = date.getDate();
				inst.endMonth = date.getMonth();
				inst.endYear = date.getFullYear();
			}
			try {
				date = this.parseDate(dateFormat, dates[0], settings) || defaultDate;
			} catch (e) {
				this.log(e);
				date = defaultDate;
			}
		}
		inst.selectedDay = date.getDate();
		inst.drawMonth = inst.selectedMonth = date.getMonth();
		inst.drawYear = inst.selectedYear = date.getFullYear();
		inst.currentDay = (dates[0] ? date.getDate() : 0);
		inst.currentMonth = (dates[0] ? date.getMonth() : 0);
		inst.currentYear = (dates[0] ? date.getFullYear() : 0);
		this._adjustInstDate(inst);
	},
	
	/* Retrieve the default date shown on opening. */
	_getDefaultDate: function(inst) {
		var date = this._determineDate(this._get(inst, 'defaultDate'), new Date());
		var minDate = this._getMinMaxDate(inst, 'min', true);
		var maxDate = this._getMinMaxDate(inst, 'max');
		date = (minDate && date < minDate ? minDate : date);
		date = (maxDate && date > maxDate ? maxDate : date);
		return date;
	},

	/* A date may be specified as an exact value or a relative one. */
	_determineDate: function(date, defaultDate) {
		var offsetNumeric = function(offset) {
			var date = new Date();
			date.setUTCDate(date.getUTCDate() + offset);
			return date;
		};
		var offsetString = function(offset, getDaysInMonth) {
			var date = new Date();
			var year = date.getFullYear();
			var month = date.getMonth();
			var day = date.getDate();
			var pattern = /([+-]?[0-9]+)\s*(d|D|w|W|m|M|y|Y)?/g;
			var matches = pattern.exec(offset);
			while (matches) {
				switch (matches[2] || 'd') {
					case 'd' : case 'D' :
						day += (matches[1] - 0); break;
					case 'w' : case 'W' :
						day += (matches[1] * 7); break;
					case 'm' : case 'M' :
						month += (matches[1] - 0); 
						day = Math.min(day, getDaysInMonth(year, month));
						break;
					case 'y': case 'Y' :
						year += (matches[1] - 0);
						day = Math.min(day, getDaysInMonth(year, month));
						break;
				}
				matches = pattern.exec(offset);
			}
			return new Date(year, month, day);
		};
		return (date == null ? defaultDate :
			(typeof date == 'string' ? offsetString(date, this._getDaysInMonth) :
			(typeof date == 'number' ? offsetNumeric(date) : date)));
	},

	/* Set the date(s) directly. */
	_setDate: function(inst, date, endDate) {
		var clear = !(date);
		date = this._determineDate(date, new Date());
		inst.selectedDay = inst.currentDay = date.getDate();
		inst.drawMonth = inst.selectedMonth = inst.currentMonth = date.getMonth();
		inst.drawYear = inst.selectedYear = inst.currentYear = date.getFullYear();
		if (this._get(inst, 'rangeSelect')) {
			if (endDate) {
				endDate = this._determineDate(endDate, null);
				inst.endDay = endDate.getDate();
				inst.endMonth = endDate.getMonth();
				inst.endYear = endDate.getFullYear();
			} else {
				inst.endDay = inst.currentDay;
				inst.endMonth = inst.currentMonth;
				inst.endYear = inst.currentYear;
			}
		}
		this._adjustInstDate(inst);
		if (inst.input)
			inst.input.val(clear ? '' : this._formatDate(inst) +
				(!this._get(inst, 'rangeSelect') ? '' : this._get(inst, 'rangeSeparator') +
				this._formatDate(inst, inst.endDay, inst.endMonth, inst.endYear)));
	},

	/* Retrieve the date(s) directly. */
	_getDate: function(inst) {
		var startDate = (!inst.currentYear || (inst.input && inst.input.val() == '') ? null :
			new Date(inst.currentYear, inst.currentMonth, inst.currentDay));
		if (this._get(inst, 'rangeSelect')) {
			return [inst.rangeStart || startDate, (!inst.endYear ? null :
				new Date(inst.endYear, inst.endMonth, inst.endDay))];
		} else
			return startDate;
	},

	/* Generate the HTML for the current state of the date picker. */
	_generateDatepicker: function(inst) {
		var today = new Date();
		today = new Date(today.getFullYear(), today.getMonth(), today.getDate()); // clear time
		var showStatus = this._get(inst, 'showStatus');
		var isRTL = this._get(inst, 'isRTL');
		// build the date picker HTML
		var clear = (this._get(inst, 'mandatory') ? '' :
			'<div class="ui-datepicker-clear"><a onclick="jQuery.datepicker._clearDate(\'#' + inst.id + '\');"' +
			(showStatus ? this._addStatus(inst, this._get(inst, 'clearStatus') || '&#xa0;') : '') + '>' +
			this._get(inst, 'clearText') + '</a></div>');
		var controls = '<div class="ui-datepicker-control">' + (isRTL ? '' : clear) +
			'<div class="ui-datepicker-close"><a onclick="jQuery.datepicker._hideDatepicker();"' +
			(showStatus ? this._addStatus(inst, this._get(inst, 'closeStatus') || '&#xa0;') : '') + '>' +
			this._get(inst, 'closeText') + '</a></div>' + (isRTL ? clear : '')  + '</div>';
		var prompt = this._get(inst, 'prompt');
		var closeAtTop = this._get(inst, 'closeAtTop');
		var hideIfNoPrevNext = this._get(inst, 'hideIfNoPrevNext');
		var navigationAsDateFormat = this._get(inst, 'navigationAsDateFormat');
		var numMonths = this._getNumberOfMonths(inst);
		var stepMonths = this._get(inst, 'stepMonths');
		var isMultiMonth = (numMonths[0] != 1 || numMonths[1] != 1);
		var currentDate = (!inst.currentDay ? new Date(9999, 9, 9) :
			new Date(inst.currentYear, inst.currentMonth, inst.currentDay));
		var minDate = this._getMinMaxDate(inst, 'min', true);
		var maxDate = this._getMinMaxDate(inst, 'max');
		var drawMonth = inst.drawMonth;
		var drawYear = inst.drawYear;
		if (maxDate) {
			var maxDraw = new Date(maxDate.getFullYear(),
				maxDate.getMonth() - numMonths[1] + 1, maxDate.getDate());
			maxDraw = (minDate && maxDraw < minDate ? minDate : maxDraw);
			while (new Date(drawYear, drawMonth, 1) > maxDraw) {
				drawMonth--;
				if (drawMonth < 0) {
					drawMonth = 11;
					drawYear--;
				}
			}
		}
		// controls and links
		var prevText = this._get(inst, 'prevText');
		prevText = (!navigationAsDateFormat ? prevText : this.formatDate(
			prevText, new Date(drawYear, drawMonth - stepMonths, 1), this._getFormatConfig(inst)));
		var prev = '<div class="ui-datepicker-prev">' + (this._canAdjustMonth(inst, -1, drawYear, drawMonth) ? 
			'<a onclick="jQuery.datepicker._adjustDate(\'#' + inst.id + '\', -' + stepMonths + ', \'M\');"' +
			(showStatus ? this._addStatus(inst, this._get(inst, 'prevStatus') || '&#xa0;') : '') + '>' + prevText + '</a>' :
			(hideIfNoPrevNext ? '' : '<label>' + prevText + '</label>')) + '</div>';
		var nextText = this._get(inst, 'nextText');
		nextText = (!navigationAsDateFormat ? nextText : this.formatDate(
			nextText, new Date(drawYear, drawMonth + stepMonths, 1), this._getFormatConfig(inst)));
		var next = '<div class="ui-datepicker-next">' + (this._canAdjustMonth(inst, +1, drawYear, drawMonth) ?
			'<a onclick="jQuery.datepicker._adjustDate(\'#' + inst.id + '\', +' + stepMonths + ', \'M\');"' +
			(showStatus ? this._addStatus(inst, this._get(inst, 'nextStatus') || '&#xa0;') : '') + '>' + nextText + '</a>' :
			(hideIfNoPrevNext ? '' : '<label>' + nextText + '</label>')) + '</div>';
		var currentText = this._get(inst, 'currentText');
		currentText = (!navigationAsDateFormat ? currentText: this.formatDate(
			currentText, today, this._getFormatConfig(inst)));
		var html = (prompt ? '<div class="' + this._promptClass + '">' + prompt + '</div>' : '') +
			(closeAtTop && !inst.inline ? controls : '') +
			'<div class="ui-datepicker-links">' + (isRTL ? next : prev) +
			(this._isInRange(inst, (this._get(inst, 'gotoCurrent') && inst.currentDay ?
			currentDate : today)) ? '<div class="ui-datepicker-current">' +
			'<a onclick="jQuery.datepicker._gotoToday(\'#' + inst.id + '\');"' +
			(showStatus ? this._addStatus(inst, this._get(inst, 'currentStatus') || '&#xa0;') : '') + '>' +
			currentText + '</a></div>' : '') + (isRTL ? prev : next) + '</div>';
		var firstDay = this._get(inst, 'firstDay');
		var changeFirstDay = this._get(inst, 'changeFirstDay');
		var dayNames = this._get(inst, 'dayNames');
		var dayNamesShort = this._get(inst, 'dayNamesShort');
		var dayNamesMin = this._get(inst, 'dayNamesMin');
		var monthNames = this._get(inst, 'monthNames');
		var beforeShowDay = this._get(inst, 'beforeShowDay');
		var highlightWeek = this._get(inst, 'highlightWeek');
		var showOtherMonths = this._get(inst, 'showOtherMonths');
		var showWeeks = this._get(inst, 'showWeeks');
		var calculateWeek = this._get(inst, 'calculateWeek') || this.iso8601Week;
		var status = (showStatus ? this._get(inst, 'dayStatus') || '&#xa0;' : '');
		var dateStatus = this._get(inst, 'statusForDate') || this.dateStatus;
		var endDate = inst.endDay ? new Date(inst.endYear, inst.endMonth, inst.endDay) : currentDate;
		for (var row = 0; row < numMonths[0]; row++)
			for (var col = 0; col < numMonths[1]; col++) {
				var selectedDate = new Date(drawYear, drawMonth, inst.selectedDay);
				html += '<div class="ui-datepicker-one-month' + (col == 0 ? ' ui-datepicker-new-row' : '') + '">' +
					this._generateMonthYearHeader(inst, drawMonth, drawYear, minDate, maxDate,
					selectedDate, row > 0 || col > 0, showStatus, monthNames) + // draw month headers
					'<table class="ui-datepicker" cellpadding="0" cellspacing="0"><thead>' + 
					'<tr class="ui-datepicker-title-row">' +
					(showWeeks ? '<td>' + this._get(inst, 'weekHeader') + '</td>' : '');
				for (var dow = 0; dow < 7; dow++) { // days of the week
					var day = (dow + firstDay) % 7;
					var dayStatus = (status.indexOf('DD') > -1 ? status.replace(/DD/, dayNames[day]) :
						status.replace(/D/, dayNamesShort[day]));
					html += '<td' + ((dow + firstDay + 6) % 7 >= 5 ? ' class="ui-datepicker-week-end-cell"' : '') + '>' +
						(!changeFirstDay ? '<span' :
						'<a onclick="jQuery.datepicker._changeFirstDay(\'#' + inst.id + '\', ' + day + ');"') + 
						(showStatus ? this._addStatus(inst, dayStatus) : '') + ' title="' + dayNames[day] + '">' +
						dayNamesMin[day] + (changeFirstDay ? '</a>' : '</span>') + '</td>';
				}
				html += '</tr></thead><tbody>';
				var daysInMonth = this._getDaysInMonth(drawYear, drawMonth);
				if (drawYear == inst.selectedYear && drawMonth == inst.selectedMonth)
					inst.selectedDay = Math.min(inst.selectedDay, daysInMonth);
				var leadDays = (this._getFirstDayOfMonth(drawYear, drawMonth) - firstDay + 7) % 7;
				var printDate = new Date(drawYear, drawMonth, 1 - leadDays);
				var numRows = (isMultiMonth ? 6 : Math.ceil((leadDays + daysInMonth) / 7)); // calculate the number of rows to generate
				for (var dRow = 0; dRow < numRows; dRow++) { // create date picker rows
					html += '<tr class="ui-datepicker-days-row">' +
						(showWeeks ? '<td class="ui-datepicker-week-col">' + calculateWeek(printDate) + '</td>' : '');
					for (var dow = 0; dow < 7; dow++) { // create date picker days
						var daySettings = (beforeShowDay ?
							beforeShowDay.apply((inst.input ? inst.input[0] : null), [printDate]) : [true, '']);
						var otherMonth = (printDate.getMonth() != drawMonth);
						var unselectable = otherMonth || !daySettings[0] ||
							(minDate && printDate < minDate) || (maxDate && printDate > maxDate);
						html += '<td class="ui-datepicker-days-cell' +
							((dow + firstDay + 6) % 7 >= 5 ? ' ui-datepicker-week-end-cell' : '') + // highlight weekends
							(otherMonth ? ' ui-datepicker-otherMonth' : '') + // highlight days from other months
							(printDate.getTime() == selectedDate.getTime() && drawMonth == inst.selectedMonth ?
							' ui-datepicker-days-cell-over' : '') + // highlight selected day
							(unselectable ? ' ' + this._unselectableClass : '') +  // highlight unselectable days
							(otherMonth && !showOtherMonths ? '' : ' ' + daySettings[1] + // highlight custom dates
							(printDate.getTime() >= currentDate.getTime() && printDate.getTime() <= endDate.getTime() ?  // in current range
							' ' + this._currentClass : '') + // highlight selected day
							(printDate.getTime() == today.getTime() ? ' ui-datepicker-today' : '')) + '"' + // highlight today (if different)
							((!otherMonth || showOtherMonths) && daySettings[2] ? ' title="' + daySettings[2] + '"' : '') + // cell title
							(unselectable ? (highlightWeek ? ' onmouseover="jQuery(this).parent().addClass(\'ui-datepicker-week-over\');"' + // highlight selection week
							' onmouseout="jQuery(this).parent().removeClass(\'ui-datepicker-week-over\');"' : '') : // unhighlight selection week
							' onmouseover="jQuery(this).addClass(\'ui-datepicker-days-cell-over\')' + // highlight selection
							(highlightWeek ? '.parent().addClass(\'ui-datepicker-week-over\')' : '') + ';' + // highlight selection week
							(!showStatus || (otherMonth && !showOtherMonths) ? '' : 'jQuery(\'#ui-datepicker-status-' +
							inst.id + '\').html(\'' + (dateStatus.apply((inst.input ? inst.input[0] : null),
							[printDate, inst]) || '&#xa0;') +'\');') + '"' +
							' onmouseout="jQuery(this).removeClass(\'ui-datepicker-days-cell-over\')' + // unhighlight selection
							(highlightWeek ? '.parent().removeClass(\'ui-datepicker-week-over\')' : '') + ';' + // unhighlight selection week
							(!showStatus || (otherMonth && !showOtherMonths) ? '' : 'jQuery(\'#ui-datepicker-status-' +
							inst.id + '\').html(\'&#xa0;\');') + '" onclick="jQuery.datepicker._selectDay(\'#' +
							inst.id + '\',' + drawMonth + ',' + drawYear + ', this);"') + '>' + // actions
							(otherMonth ? (showOtherMonths ? printDate.getDate() : '&#xa0;') : // display for other months
							(unselectable ? printDate.getDate() : '<a>' + printDate.getDate() + '</a>')) + '</td>'; // display for this month
						printDate.setUTCDate(printDate.getUTCDate() + 1);
					}
					html += '</tr>';
				}
				drawMonth++;
				if (drawMonth > 11) {
					drawMonth = 0;
					drawYear++;
				}
				html += '</tbody></table></div>';
			}
		html += (showStatus ? '<div style="clear: both;"></div><div id="ui-datepicker-status-' + inst.id + 
			'" class="ui-datepicker-status">' + (this._get(inst, 'initStatus') || '&#xa0;') + '</div>' : '') +
			(!closeAtTop && !inst.inline ? controls : '') +
			'<div style="clear: both;"></div>' + 
			($.browser.msie && parseInt($.browser.version) < 7 && !inst.inline ? 
			'<iframe src="javascript:false;" class="ui-datepicker-cover"></iframe>' : '');
		return html;
	},
	
	/* Generate the month and year header. */
	_generateMonthYearHeader: function(inst, drawMonth, drawYear, minDate, maxDate,
			selectedDate, secondary, showStatus, monthNames) {
		minDate = (inst.rangeStart && minDate && selectedDate < minDate ? selectedDate : minDate);
		var html = '<div class="ui-datepicker-header">';
		// month selection
		if (secondary || !this._get(inst, 'changeMonth'))
			html += monthNames[drawMonth] + '&#xa0;';
		else {
			var inMinYear = (minDate && minDate.getFullYear() == drawYear);
			var inMaxYear = (maxDate && maxDate.getFullYear() == drawYear);
			html += '<select class="ui-datepicker-new-month" ' +
				'onchange="jQuery.datepicker._selectMonthYear(\'#' + inst.id + '\', this, \'M\');" ' +
				'onclick="jQuery.datepicker._clickMonthYear(\'#' + inst.id + '\');"' +
				(showStatus ? this._addStatus(inst, this._get(inst, 'monthStatus') || '&#xa0;') : '') + '>';
			for (var month = 0; month < 12; month++) {
				if ((!inMinYear || month >= minDate.getMonth()) &&
						(!inMaxYear || month <= maxDate.getMonth()))
					html += '<option value="' + month + '"' +
						(month == drawMonth ? ' selected="selected"' : '') +
						'>' + monthNames[month] + '</option>';
			}
			html += '</select>';
		}
		// year selection
		if (secondary || !this._get(inst, 'changeYear'))
			html += drawYear;
		else {
			// determine range of years to display
			var years = this._get(inst, 'yearRange').split(':');
			var year = 0;
			var endYear = 0;
			if (years.length != 2) {
				year = drawYear - 10;
				endYear = drawYear + 10;
			} else if (years[0].charAt(0) == '+' || years[0].charAt(0) == '-') {
				year = endYear = new Date().getFullYear();
				year += parseInt(years[0], 10);
				endYear += parseInt(years[1], 10);
			} else {
				year = parseInt(years[0], 10);
				endYear = parseInt(years[1], 10);
			}
			year = (minDate ? Math.max(year, minDate.getFullYear()) : year);
			endYear = (maxDate ? Math.min(endYear, maxDate.getFullYear()) : endYear);
			html += '<select class="ui-datepicker-new-year" ' +
				'onchange="jQuery.datepicker._selectMonthYear(\'#' + inst.id + '\', this, \'Y\');" ' +
				'onclick="jQuery.datepicker._clickMonthYear(\'#' + inst.id + '\');"' +
				(showStatus ? this._addStatus(inst, this._get(inst, 'yearStatus') || '&#xa0;') : '') + '>';
			for (; year <= endYear; year++) {
				html += '<option value="' + year + '"' +
					(year == drawYear ? ' selected="selected"' : '') +
					'>' + year + '</option>';
			}
			html += '</select>';
		}
		html += '</div>'; // Close datepicker_header
		return html;
	},

	/* Provide code to set and clear the status panel. */
	_addStatus: function(inst, text) {
		return ' onmouseover="jQuery(\'#ui-datepicker-status-' + inst.id + '\').html(\'' + text + '\');" ' +
			'onmouseout="jQuery(\'#ui-datepicker-status-' + inst.id + '\').html(\'&#xa0;\');"';
	},

	/* Adjust one of the date sub-fields. */
	_adjustInstDate: function(inst, offset, period) {
		var year = inst.drawYear + (period == 'Y' ? offset : 0);
		var month = inst.drawMonth + (period == 'M' ? offset : 0);
		var day = Math.min(inst.selectedDay, this._getDaysInMonth(year, month)) +
			(period == 'D' ? offset : 0);
		var date = new Date(year, month, day);
		// ensure it is within the bounds set
		var minDate = this._getMinMaxDate(inst, 'min', true);
		var maxDate = this._getMinMaxDate(inst, 'max');
		date = (minDate && date < minDate ? minDate : date);
		date = (maxDate && date > maxDate ? maxDate : date);
		inst.selectedDay = date.getDate();
		inst.drawMonth = inst.selectedMonth = date.getMonth();
		inst.drawYear = inst.selectedYear = date.getFullYear();
		if (period == 'M' || period == 'Y')
			this._notifyChange(inst);
	},

	/* Notify change of month/year. */
	_notifyChange: function(inst) {
		var onChange = this._get(inst, 'onChangeMonthYear');
		if (onChange)
			onChange.apply((inst.input ? inst.input[0] : null),
				[new Date(inst.selectedYear, inst.selectedMonth, 1), inst]);
	},
	
	/* Determine the number of months to show. */
	_getNumberOfMonths: function(inst) {
		var numMonths = this._get(inst, 'numberOfMonths');
		return (numMonths == null ? [1, 1] : (typeof numMonths == 'number' ? [1, numMonths] : numMonths));
	},

	/* Determine the current maximum date - ensure no time components are set - may be overridden for a range. */
	_getMinMaxDate: function(inst, minMax, checkRange) {
		var date = this._determineDate(this._get(inst, minMax + 'Date'), null);
		if (date) {
			date.setHours(0);
			date.setMinutes(0);
			date.setSeconds(0);
			date.setMilliseconds(0);
		}
		return (!checkRange || !inst.rangeStart ? date :
			(!date || inst.rangeStart > date ? inst.rangeStart : date));
	},

	/* Find the number of days in a given month. */
	_getDaysInMonth: function(year, month) {
		return 32 - new Date(year, month, 32).getDate();
	},

	/* Find the day of the week of the first of a month. */
	_getFirstDayOfMonth: function(year, month) {
		return new Date(year, month, 1).getDay();
	},

	/* Determines if we should allow a "next/prev" month display change. */
	_canAdjustMonth: function(inst, offset, curYear, curMonth) {
		var numMonths = this._getNumberOfMonths(inst);
		var date = new Date(curYear, curMonth + (offset < 0 ? offset : numMonths[1]), 1);
		if (offset < 0)
			date.setDate(this._getDaysInMonth(date.getFullYear(), date.getMonth()));
		return this._isInRange(inst, date);
	},

	/* Is the given date in the accepted range? */
	_isInRange: function(inst, date) {
		// during range selection, use minimum of selected date and range start
		var newMinDate = (!inst.rangeStart ? null :
			new Date(inst.selectedYear, inst.selectedMonth, inst.selectedDay));
		newMinDate = (newMinDate && inst.rangeStart < newMinDate ? inst.rangeStart : newMinDate);
		var minDate = newMinDate || this._getMinMaxDate(inst, 'min');
		var maxDate = this._getMinMaxDate(inst, 'max');
		return ((!minDate || date >= minDate) && (!maxDate || date <= maxDate));
	},
	
	/* Provide the configuration settings for formatting/parsing. */
	_getFormatConfig: function(inst) {
		var shortYearCutoff = this._get(inst, 'shortYearCutoff');
		shortYearCutoff = (typeof shortYearCutoff != 'string' ? shortYearCutoff :
			new Date().getFullYear() % 100 + parseInt(shortYearCutoff, 10));
		return {shortYearCutoff: shortYearCutoff,
			dayNamesShort: this._get(inst, 'dayNamesShort'), dayNames: this._get(inst, 'dayNames'),
			monthNamesShort: this._get(inst, 'monthNamesShort'), monthNames: this._get(inst, 'monthNames')};
	},

	/* Format the given date for display. */
	_formatDate: function(inst, day, month, year) {
		if (!day) {
			inst.currentDay = inst.selectedDay;
			inst.currentMonth = inst.selectedMonth;
			inst.currentYear = inst.selectedYear;
		}
		var date = (day ? (typeof day == 'object' ? day : new Date(year, month, day)) :
			new Date(inst.currentYear, inst.currentMonth, inst.currentDay));
		return this.formatDate(this._get(inst, 'dateFormat'), date, this._getFormatConfig(inst));
	}
});

/* jQuery extend now ignores nulls! */
function extendRemove(target, props) {
	$.extend(target, props);
	for (var name in props)
		if (props[name] == null || props[name] == undefined)
			target[name] = props[name];
	return target;
};

/* Determine whether an object is an array. */
function isArray(a) {
	return (a && (($.browser.safari && typeof a == 'object' && a.length) ||
		(a.constructor && a.constructor.toString().match(/\Array\(\)/))));
};

/* Invoke the datepicker functionality.
   @param  options  string - a command, optionally followed by additional parameters or
                    Object - settings for attaching new datepicker functionality
   @return  jQuery object */
$.fn.datepicker = function(options){
	var otherArgs = Array.prototype.slice.call(arguments, 1);
	if (typeof options == 'string' && (options == 'isDisabled' || options == 'getDate'))
		return $.datepicker['_' + options + 'Datepicker'].
			apply($.datepicker, [this[0]].concat(otherArgs));
	return this.each(function() {
		typeof options == 'string' ?
			$.datepicker['_' + options + 'Datepicker'].
				apply($.datepicker, [this].concat(otherArgs)) :
			$.datepicker._attachDatepicker(this, options);
	});
};

$.datepicker = new Datepicker(); // singleton instance
	
/* Initialise the date picker. */
$(document).ready(function() {
	$(document.body).append($.datepicker.dpDiv).
		mousedown($.datepicker._checkExternalClick);
});

})(jQuery);
/*
 * jQuery UI Effects 1.5.2
 *
 * Copyright (c) 2008 Aaron Eisenberger (aaronchi@gmail.com)
 * Dual licensed under the MIT (MIT-LICENSE.txt)
 * and GPL (GPL-LICENSE.txt) licenses.
 * 
 * http://docs.jquery.com/UI/Effects/
 */
;(function($) {

$.effects = $.effects || {}; //Add the 'effects' scope

$.extend($.effects, {
	save: function(el, set) {
		for(var i=0;i<set.length;i++) {
			if(set[i] !== null) $.data(el[0], "ec.storage."+set[i], el[0].style[set[i]]);
		}
	},
	restore: function(el, set) {
		for(var i=0;i<set.length;i++) {
			if(set[i] !== null) el.css(set[i], $.data(el[0], "ec.storage."+set[i]));
		}
	},
	setMode: function(el, mode) {
		if (mode == 'toggle') mode = el.is(':hidden') ? 'show' : 'hide'; // Set for toggle
		return mode;
	},
	getBaseline: function(origin, original) { // Translates a [top,left] array into a baseline value
		// this should be a little more flexible in the future to handle a string & hash
		var y, x;
		switch (origin[0]) {
			case 'top': y = 0; break;
			case 'middle': y = 0.5; break;
			case 'bottom': y = 1; break;
			default: y = origin[0] / original.height;
		};
		switch (origin[1]) {
			case 'left': x = 0; break;
			case 'center': x = 0.5; break;
			case 'right': x = 1; break;
			default: x = origin[1] / original.width;
		};
		return {x: x, y: y};
	},
	createWrapper: function(el) {
		if (el.parent().attr('id') == 'fxWrapper')
			return el;
		var props = {width: el.outerWidth({margin:true}), height: el.outerHeight({margin:true}), 'float': el.css('float')};
		el.wrap('<div id="fxWrapper" style="font-size:100%;background:transparent;border:none;margin:0;padding:0"></div>');
		var wrapper = el.parent();
		if (el.css('position') == 'static'){
			wrapper.css({position: 'relative'});
			el.css({position: 'relative'});
		} else {
			var top = el.css('top'); if(isNaN(parseInt(top))) top = 'auto';
			var left = el.css('left'); if(isNaN(parseInt(left))) left = 'auto';
			wrapper.css({ position: el.css('position'), top: top, left: left, zIndex: el.css('z-index') }).show();
			el.css({position: 'relative', top:0, left:0});
		}
		wrapper.css(props);
		return wrapper;
	},
	removeWrapper: function(el) {
		if (el.parent().attr('id') == 'fxWrapper')
			return el.parent().replaceWith(el);
		return el;
	},
	setTransition: function(el, list, factor, val) {
		val = val || {};
		$.each(list,function(i, x){
			unit = el.cssUnit(x);
			if (unit[0] > 0) val[x] = unit[0] * factor + unit[1];
		});
		return val;
	},
	animateClass: function(value, duration, easing, callback) {

		var cb = (typeof easing == "function" ? easing : (callback ? callback : null));
		var ea = (typeof easing == "object" ? easing : null);

		return this.each(function() {

			var offset = {}; var that = $(this); var oldStyleAttr = that.attr("style") || '';
			if(typeof oldStyleAttr == 'object') oldStyleAttr = oldStyleAttr["cssText"]; /* Stupidly in IE, style is a object.. */
			if(value.toggle) { that.hasClass(value.toggle) ? value.remove = value.toggle : value.add = value.toggle; }

			//Let's get a style offset
			var oldStyle = $.extend({}, (document.defaultView ? document.defaultView.getComputedStyle(this,null) : this.currentStyle));
			if(value.add) that.addClass(value.add); if(value.remove) that.removeClass(value.remove);
			var newStyle = $.extend({}, (document.defaultView ? document.defaultView.getComputedStyle(this,null) : this.currentStyle));
			if(value.add) that.removeClass(value.add); if(value.remove) that.addClass(value.remove);

			// The main function to form the object for animation
			for(var n in newStyle) {
				if( typeof newStyle[n] != "function" && newStyle[n] /* No functions and null properties */
				&& n.indexOf("Moz") == -1 && n.indexOf("length") == -1 /* No mozilla spezific render properties. */
				&& newStyle[n] != oldStyle[n] /* Only values that have changed are used for the animation */
				&& (n.match(/color/i) || (!n.match(/color/i) && !isNaN(parseInt(newStyle[n],10)))) /* Only things that can be parsed to integers or colors */
				&& (oldStyle.position != "static" || (oldStyle.position == "static" && !n.match(/left|top|bottom|right/))) /* No need for positions when dealing with static positions */
				) offset[n] = newStyle[n];
			}

			that.animate(offset, duration, ea, function() { // Animate the newly constructed offset object
				// Change style attribute back to original. For stupid IE, we need to clear the damn object.
				if(typeof $(this).attr("style") == 'object') { $(this).attr("style")["cssText"] = ""; $(this).attr("style")["cssText"] = oldStyleAttr; } else $(this).attr("style", oldStyleAttr);
				if(value.add) $(this).addClass(value.add); if(value.remove) $(this).removeClass(value.remove);
				if(cb) cb.apply(this, arguments);
			});

		});
	}
});

//Extend the methods of jQuery
$.fn.extend({
	//Save old methods
	_show: $.fn.show,
	_hide: $.fn.hide,
	__toggle: $.fn.toggle,
	_addClass: $.fn.addClass,
	_removeClass: $.fn.removeClass,
	_toggleClass: $.fn.toggleClass,
	// New ec methods
	effect: function(fx,o,speed,callback) {
		return $.effects[fx] ? $.effects[fx].call(this, {method: fx, options: o || {}, duration: speed, callback: callback }) : null;
	},
	show: function() {
		if(!arguments[0] || (arguments[0].constructor == Number || /(slow|normal|fast)/.test(arguments[0])))
			return this._show.apply(this, arguments);
		else {
			var o = arguments[1] || {}; o['mode'] = 'show';
			return this.effect.apply(this, [arguments[0], o, arguments[2] || o.duration, arguments[3] || o.callback]);
		}
	},
	hide: function() {
		if(!arguments[0] || (arguments[0].constructor == Number || /(slow|normal|fast)/.test(arguments[0])))
			return this._hide.apply(this, arguments);
		else {
			var o = arguments[1] || {}; o['mode'] = 'hide';
			return this.effect.apply(this, [arguments[0], o, arguments[2] || o.duration, arguments[3] || o.callback]);
		}
	},
	toggle: function(){
		if(!arguments[0] || (arguments[0].constructor == Number || /(slow|normal|fast)/.test(arguments[0])) || (arguments[0].constructor == Function))
			return this.__toggle.apply(this, arguments);
		else {
			var o = arguments[1] || {}; o['mode'] = 'toggle';
			return this.effect.apply(this, [arguments[0], o, arguments[2] || o.duration, arguments[3] || o.callback]);
		}
	},
	addClass: function(classNames,speed,easing,callback) {
		return speed ? $.effects.animateClass.apply(this, [{ add: classNames },speed,easing,callback]) : this._addClass(classNames);
	},
	removeClass: function(classNames,speed,easing,callback) {
		return speed ? $.effects.animateClass.apply(this, [{ remove: classNames },speed,easing,callback]) : this._removeClass(classNames);
	},
	toggleClass: function(classNames,speed,easing,callback) {
		return speed ? $.effects.animateClass.apply(this, [{ toggle: classNames },speed,easing,callback]) : this._toggleClass(classNames);
	},
	morph: function(remove,add,speed,easing,callback) {
		return $.effects.animateClass.apply(this, [{ add: add, remove: remove },speed,easing,callback]);
	},
	switchClass: function() {
		return this.morph.apply(this, arguments);
	},
	// helper functions
	cssUnit: function(key) {
		var style = this.css(key), val = [];
		$.each( ['em','px','%','pt'], function(i, unit){
			if(style.indexOf(unit) > 0)
				val = [parseFloat(style), unit];
		});
		return val;
	}
});

/*
 * jQuery Color Animations
 * Copyright 2007 John Resig
 * Released under the MIT and GPL licenses.
 */

// We override the animation for all of these color styles
jQuery.each(['backgroundColor', 'borderBottomColor', 'borderLeftColor', 'borderRightColor', 'borderTopColor', 'color', 'outlineColor'], function(i,attr){
		jQuery.fx.step[attr] = function(fx){
				if ( fx.state == 0 ) {
						fx.start = getColor( fx.elem, attr );
						fx.end = getRGB( fx.end );
				}

				fx.elem.style[attr] = "rgb(" + [
						Math.max(Math.min( parseInt((fx.pos * (fx.end[0] - fx.start[0])) + fx.start[0]), 255), 0),
						Math.max(Math.min( parseInt((fx.pos * (fx.end[1] - fx.start[1])) + fx.start[1]), 255), 0),
						Math.max(Math.min( parseInt((fx.pos * (fx.end[2] - fx.start[2])) + fx.start[2]), 255), 0)
				].join(",") + ")";
		}
});

// Color Conversion functions from highlightFade
// By Blair Mitchelmore
// http://jquery.offput.ca/highlightFade/

// Parse strings looking for color tuples [255,255,255]
function getRGB(color) {
		var result;

		// Check if we're already dealing with an array of colors
		if ( color && color.constructor == Array && color.length == 3 )
				return color;

		// Look for rgb(num,num,num)
		if (result = /rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(color))
				return [parseInt(result[1]), parseInt(result[2]), parseInt(result[3])];

		// Look for rgb(num%,num%,num%)
		if (result = /rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(color))
				return [parseFloat(result[1])*2.55, parseFloat(result[2])*2.55, parseFloat(result[3])*2.55];

		// Look for #a0b1c2
		if (result = /#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(color))
				return [parseInt(result[1],16), parseInt(result[2],16), parseInt(result[3],16)];

		// Look for #fff
		if (result = /#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(color))
				return [parseInt(result[1]+result[1],16), parseInt(result[2]+result[2],16), parseInt(result[3]+result[3],16)];

		// Look for rgba(0, 0, 0, 0) == transparent in Safari 3
		if (result = /rgba\(0, 0, 0, 0\)/.exec(color))
				return colors['transparent']

		// Otherwise, we're most likely dealing with a named color
		return colors[jQuery.trim(color).toLowerCase()];
}

function getColor(elem, attr) {
		var color;

		do {
				color = jQuery.curCSS(elem, attr);

				// Keep going until we find an element that has color, or we hit the body
				if ( color != '' && color != 'transparent' || jQuery.nodeName(elem, "body") )
						break;

				attr = "backgroundColor";
		} while ( elem = elem.parentNode );

		return getRGB(color);
};

// Some named colors to work with
// From Interface by Stefan Petre
// http://interface.eyecon.ro/

var colors = {
	aqua:[0,255,255],
	azure:[240,255,255],
	beige:[245,245,220],
	black:[0,0,0],
	blue:[0,0,255],
	brown:[165,42,42],
	cyan:[0,255,255],
	darkblue:[0,0,139],
	darkcyan:[0,139,139],
	darkgrey:[169,169,169],
	darkgreen:[0,100,0],
	darkkhaki:[189,183,107],
	darkmagenta:[139,0,139],
	darkolivegreen:[85,107,47],
	darkorange:[255,140,0],
	darkorchid:[153,50,204],
	darkred:[139,0,0],
	darksalmon:[233,150,122],
	darkviolet:[148,0,211],
	fuchsia:[255,0,255],
	gold:[255,215,0],
	green:[0,128,0],
	indigo:[75,0,130],
	khaki:[240,230,140],
	lightblue:[173,216,230],
	lightcyan:[224,255,255],
	lightgreen:[144,238,144],
	lightgrey:[211,211,211],
	lightpink:[255,182,193],
	lightyellow:[255,255,224],
	lime:[0,255,0],
	magenta:[255,0,255],
	maroon:[128,0,0],
	navy:[0,0,128],
	olive:[128,128,0],
	orange:[255,165,0],
	pink:[255,192,203],
	purple:[128,0,128],
	violet:[128,0,128],
	red:[255,0,0],
	silver:[192,192,192],
	white:[255,255,255],
	yellow:[255,255,0],
	transparent: [255,255,255]
};
	
/*
 * jQuery Easing v1.3 - http://gsgd.co.uk/sandbox/jquery/easing/
 *
 * Uses the built in easing capabilities added In jQuery 1.1
 * to offer multiple easing options
 *
 * TERMS OF USE - jQuery Easing
 * 
 * Open source under the BSD License. 
 * 
 * Copyright © 2008 George McGinley Smith
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without modification, 
 * are permitted provided that the following conditions are met:
 * 
 * Redistributions of source code must retain the above copyright notice, this list of 
 * conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright notice, this list 
 * of conditions and the following disclaimer in the documentation and/or other materials 
 * provided with the distribution.
 * 
 * Neither the name of the author nor the names of contributors may be used to endorse 
 * or promote products derived from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 
 * OF THE POSSIBILITY OF SUCH DAMAGE. 
 *
*/

// t: current time, b: begInnIng value, c: change In value, d: duration
jQuery.easing['jswing'] = jQuery.easing['swing'];

jQuery.extend( jQuery.easing,
{
	def: 'easeOutQuad',
	swing: function (x, t, b, c, d) {
		//alert(jQuery.easing.default);
		return jQuery.easing[jQuery.easing.def](x, t, b, c, d);
	},
	easeInQuad: function (x, t, b, c, d) {
		return c*(t/=d)*t + b;
	},
	easeOutQuad: function (x, t, b, c, d) {
		return -c *(t/=d)*(t-2) + b;
	},
	easeInOutQuad: function (x, t, b, c, d) {
		if ((t/=d/2) < 1) return c/2*t*t + b;
		return -c/2 * ((--t)*(t-2) - 1) + b;
	},
	easeInCubic: function (x, t, b, c, d) {
		return c*(t/=d)*t*t + b;
	},
	easeOutCubic: function (x, t, b, c, d) {
		return c*((t=t/d-1)*t*t + 1) + b;
	},
	easeInOutCubic: function (x, t, b, c, d) {
		if ((t/=d/2) < 1) return c/2*t*t*t + b;
		return c/2*((t-=2)*t*t + 2) + b;
	},
	easeInQuart: function (x, t, b, c, d) {
		return c*(t/=d)*t*t*t + b;
	},
	easeOutQuart: function (x, t, b, c, d) {
		return -c * ((t=t/d-1)*t*t*t - 1) + b;
	},
	easeInOutQuart: function (x, t, b, c, d) {
		if ((t/=d/2) < 1) return c/2*t*t*t*t + b;
		return -c/2 * ((t-=2)*t*t*t - 2) + b;
	},
	easeInQuint: function (x, t, b, c, d) {
		return c*(t/=d)*t*t*t*t + b;
	},
	easeOutQuint: function (x, t, b, c, d) {
		return c*((t=t/d-1)*t*t*t*t + 1) + b;
	},
	easeInOutQuint: function (x, t, b, c, d) {
		if ((t/=d/2) < 1) return c/2*t*t*t*t*t + b;
		return c/2*((t-=2)*t*t*t*t + 2) + b;
	},
	easeInSine: function (x, t, b, c, d) {
		return -c * Math.cos(t/d * (Math.PI/2)) + c + b;
	},
	easeOutSine: function (x, t, b, c, d) {
		return c * Math.sin(t/d * (Math.PI/2)) + b;
	},
	easeInOutSine: function (x, t, b, c, d) {
		return -c/2 * (Math.cos(Math.PI*t/d) - 1) + b;
	},
	easeInExpo: function (x, t, b, c, d) {
		return (t==0) ? b : c * Math.pow(2, 10 * (t/d - 1)) + b;
	},
	easeOutExpo: function (x, t, b, c, d) {
		return (t==d) ? b+c : c * (-Math.pow(2, -10 * t/d) + 1) + b;
	},
	easeInOutExpo: function (x, t, b, c, d) {
		if (t==0) return b;
		if (t==d) return b+c;
		if ((t/=d/2) < 1) return c/2 * Math.pow(2, 10 * (t - 1)) + b;
		return c/2 * (-Math.pow(2, -10 * --t) + 2) + b;
	},
	easeInCirc: function (x, t, b, c, d) {
		return -c * (Math.sqrt(1 - (t/=d)*t) - 1) + b;
	},
	easeOutCirc: function (x, t, b, c, d) {
		return c * Math.sqrt(1 - (t=t/d-1)*t) + b;
	},
	easeInOutCirc: function (x, t, b, c, d) {
		if ((t/=d/2) < 1) return -c/2 * (Math.sqrt(1 - t*t) - 1) + b;
		return c/2 * (Math.sqrt(1 - (t-=2)*t) + 1) + b;
	},
	easeInElastic: function (x, t, b, c, d) {
		var s=1.70158;var p=0;var a=c;
		if (t==0) return b;  if ((t/=d)==1) return b+c;  if (!p) p=d*.3;
		if (a < Math.abs(c)) { a=c; var s=p/4; }
		else var s = p/(2*Math.PI) * Math.asin (c/a);
		return -(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;
	},
	easeOutElastic: function (x, t, b, c, d) {
		var s=1.70158;var p=0;var a=c;
		if (t==0) return b;  if ((t/=d)==1) return b+c;  if (!p) p=d*.3;
		if (a < Math.abs(c)) { a=c; var s=p/4; }
		else var s = p/(2*Math.PI) * Math.asin (c/a);
		return a*Math.pow(2,-10*t) * Math.sin( (t*d-s)*(2*Math.PI)/p ) + c + b;
	},
	easeInOutElastic: function (x, t, b, c, d) {
		var s=1.70158;var p=0;var a=c;
		if (t==0) return b;  if ((t/=d/2)==2) return b+c;  if (!p) p=d*(.3*1.5);
		if (a < Math.abs(c)) { a=c; var s=p/4; }
		else var s = p/(2*Math.PI) * Math.asin (c/a);
		if (t < 1) return -.5*(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;
		return a*Math.pow(2,-10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )*.5 + c + b;
	},
	easeInBack: function (x, t, b, c, d, s) {
		if (s == undefined) s = 1.70158;
		return c*(t/=d)*t*((s+1)*t - s) + b;
	},
	easeOutBack: function (x, t, b, c, d, s) {
		if (s == undefined) s = 1.70158;
		return c*((t=t/d-1)*t*((s+1)*t + s) + 1) + b;
	},
	easeInOutBack: function (x, t, b, c, d, s) {
		if (s == undefined) s = 1.70158; 
		if ((t/=d/2) < 1) return c/2*(t*t*(((s*=(1.525))+1)*t - s)) + b;
		return c/2*((t-=2)*t*(((s*=(1.525))+1)*t + s) + 2) + b;
	},
	easeInBounce: function (x, t, b, c, d) {
		return c - jQuery.easing.easeOutBounce (x, d-t, 0, c, d) + b;
	},
	easeOutBounce: function (x, t, b, c, d) {
		if ((t/=d) < (1/2.75)) {
			return c*(7.5625*t*t) + b;
		} else if (t < (2/2.75)) {
			return c*(7.5625*(t-=(1.5/2.75))*t + .75) + b;
		} else if (t < (2.5/2.75)) {
			return c*(7.5625*(t-=(2.25/2.75))*t + .9375) + b;
		} else {
			return c*(7.5625*(t-=(2.625/2.75))*t + .984375) + b;
		}
	},
	easeInOutBounce: function (x, t, b, c, d) {
		if (t < d/2) return jQuery.easing.easeInBounce (x, t*2, 0, c, d) * .5 + b;
		return jQuery.easing.easeOutBounce (x, t*2-d, 0, c, d) * .5 + c*.5 + b;
	}
});

/*
 *
 * TERMS OF USE - EASING EQUATIONS
 * 
 * Open source under the BSD License. 
 * 
 * Copyright © 2001 Robert Penner
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without modification, 
 * are permitted provided that the following conditions are met:
 * 
 * Redistributions of source code must retain the above copyright notice, this list of 
 * conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright notice, this list 
 * of conditions and the following disclaimer in the documentation and/or other materials 
 * provided with the distribution.
 * 
 * Neither the name of the author nor the names of contributors may be used to endorse 
 * or promote products derived from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 
 * OF THE POSSIBILITY OF SUCH DAMAGE. 
 *
 */

})(jQuery);
/*
 * jQuery UI Effects Blind
 *
 * Copyright (c) 2008 Aaron Eisenberger (aaronchi@gmail.com)
 * Dual licensed under the MIT (MIT-LICENSE.txt)
 * and GPL (GPL-LICENSE.txt) licenses.
 * 
 * http://docs.jquery.com/UI/Effects/Blind
 *
 * Depends:
 *	effects.core.js
 */
(function($) {

$.effects.blind = function(o) {

	return this.queue(function() {

		// Create element
		var el = $(this), props = ['position','top','left'];
		
		// Set options
		var mode = $.effects.setMode(el, o.options.mode || 'hide'); // Set Mode
		var direction = o.options.direction || 'vertical'; // Default direction
		
		// Adjust
		$.effects.save(el, props); el.show(); // Save & Show
		var wrapper = $.effects.createWrapper(el).css({overflow:'hidden'}); // Create Wrapper
		var ref = (direction == 'vertical') ? 'height' : 'width';
		var distance = (direction == 'vertical') ? wrapper.height() : wrapper.width();
		if(mode == 'show') wrapper.css(ref, 0); // Shift
		
		// Animation
		var animation = {};
		animation[ref] = mode == 'show' ? distance : 0;
	 
		// Animate
		wrapper.animate(animation, o.duration, o.options.easing, function() {
			if(mode == 'hide') el.hide(); // Hide
			$.effects.restore(el, props); $.effects.removeWrapper(el); // Restore
			if(o.callback) o.callback.apply(el[0], arguments); // Callback
			el.dequeue();
		});
		
	});
	
};

})(jQuery);
/*
 * jQuery UI Effects Bounce
 *
 * Copyright (c) 2008 Aaron Eisenberger (aaronchi@gmail.com)
 * Dual licensed under the MIT (MIT-LICENSE.txt)
 * and GPL (GPL-LICENSE.txt) licenses.
 * 
 * http://docs.jquery.com/UI/Effects/Bounce
 *
 * Depends:
 *	effects.core.js
 */
(function($) {

$.effects.bounce = function(o) {

	return this.queue(function() {

		// Create element
		var el = $(this), props = ['position','top','left'];

		// Set options
		var mode = $.effects.setMode(el, o.options.mode || 'effect'); // Set Mode
		var direction = o.options.direction || 'up'; // Default direction
		var distance = o.options.distance || 20; // Default distance
		var times = o.options.times || 5; // Default # of times
		var speed = o.duration || 250; // Default speed per bounce
		if (/show|hide/.test(mode)) props.push('opacity'); // Avoid touching opacity to prevent clearType and PNG issues in IE

		// Adjust
		$.effects.save(el, props); el.show(); // Save & Show
		$.effects.createWrapper(el); // Create Wrapper
		var ref = (direction == 'up' || direction == 'down') ? 'top' : 'left';
		var motion = (direction == 'up' || direction == 'left') ? 'pos' : 'neg';
		var distance = o.options.distance || (ref == 'top' ? el.outerHeight({margin:true}) / 3 : el.outerWidth({margin:true}) / 3);
		if (mode == 'show') el.css('opacity', 0).css(ref, motion == 'pos' ? -distance : distance); // Shift
		if (mode == 'hide') distance = distance / (times * 2);
		if (mode != 'hide') times--;
		
		// Animate
		if (mode == 'show') { // Show Bounce
			var animation = {opacity: 1};
			animation[ref] = (motion == 'pos' ? '+=' : '-=') + distance;
			el.animate(animation, speed / 2, o.options.easing);
			distance = distance / 2;
			times--;
		};
		for (var i = 0; i < times; i++) { // Bounces
			var animation1 = {}, animation2 = {};
			animation1[ref] = (motion == 'pos' ? '-=' : '+=') + distance;
			animation2[ref] = (motion == 'pos' ? '+=' : '-=') + distance;
			el.animate(animation1, speed / 2, o.options.easing).animate(animation2, speed / 2, o.options.easing);
			distance = (mode == 'hide') ? distance * 2 : distance / 2;
		};
		if (mode == 'hide') { // Last Bounce
			var animation = {opacity: 0};
			animation[ref] = (motion == 'pos' ? '-=' : '+=')  + distance;
			el.animate(animation, speed / 2, o.options.easing, function(){
				el.hide(); // Hide
				$.effects.restore(el, props); $.effects.removeWrapper(el); // Restore
				if(o.callback) o.callback.apply(this, arguments); // Callback
			});
		} else {
			var animation1 = {}, animation2 = {};
			animation1[ref] = (motion == 'pos' ? '-=' : '+=') + distance;
			animation2[ref] = (motion == 'pos' ? '+=' : '-=') + distance;
			el.animate(animation1, speed / 2, o.options.easing).animate(animation2, speed / 2, o.options.easing, function(){
				$.effects.restore(el, props); $.effects.removeWrapper(el); // Restore
				if(o.callback) o.callback.apply(this, arguments); // Callback
			});
		};
		el.queue('fx', function() { el.dequeue(); });
		el.dequeue();
	});
	
};

})(jQuery);
/*
 * jQuery UI Effects Clip
 *
 * Copyright (c) 2008 Aaron Eisenberger (aaronchi@gmail.com)
 * Dual licensed under the MIT (MIT-LICENSE.txt)
 * and GPL (GPL-LICENSE.txt) licenses.
 * 
 * http://docs.jquery.com/UI/Effects/Clip
 *
 * Depends:
 *	effects.core.js
 */
(function($) {

$.effects.clip = function(o) {

	return this.queue(function() {

		// Create element
		var el = $(this), props = ['position','top','left','height','width'];
		
		// Set options
		var mode = $.effects.setMode(el, o.options.mode || 'hide'); // Set Mode
		var direction = o.options.direction || 'vertical'; // Default direction
		
		// Adjust
		$.effects.save(el, props); el.show(); // Save & Show
		var wrapper = $.effects.createWrapper(el).css({overflow:'hidden'}); // Create Wrapper
		var animate = el[0].tagName == 'IMG' ? wrapper : el;
		var ref = {
			size: (direction == 'vertical') ? 'height' : 'width',
			position: (direction == 'vertical') ? 'top' : 'left'
		};
		var distance = (direction == 'vertical') ? animate.height() : animate.width();
		if(mode == 'show') { animate.css(ref.size, 0); animate.css(ref.position, distance / 2); } // Shift
		
		// Animation
		var animation = {};
		animation[ref.size] = mode == 'show' ? distance : 0;
		animation[ref.position] = mode == 'show' ? 0 : distance / 2;
			
		// Animate
		animate.animate(animation, { queue: false, duration: o.duration, easing: o.options.easing, complete: function() {
			if(mode == 'hide') el.hide(); // Hide
			$.effects.restore(el, props); $.effects.removeWrapper(el); // Restore
			if(o.callback) o.callback.apply(el[0], arguments); // Callback
			el.dequeue();
		}}); 
		
	});
	
};

})(jQuery);
/*
 * jQuery UI Effects Drop
 *
 * Copyright (c) 2008 Aaron Eisenberger (aaronchi@gmail.com)
 * Dual licensed under the MIT (MIT-LICENSE.txt)
 * and GPL (GPL-LICENSE.txt) licenses.
 * 
 * http://docs.jquery.com/UI/Effects/Drop
 *
 * Depends:
 *	effects.core.js
 */
(function($) {

$.effects.drop = function(o) {

	return this.queue(function() {

		// Create element
		var el = $(this), props = ['position','top','left','opacity'];
		
		// Set options
		var mode = $.effects.setMode(el, o.options.mode || 'hide'); // Set Mode
		var direction = o.options.direction || 'left'; // Default Direction
		
		// Adjust
		$.effects.save(el, props); el.show(); // Save & Show
		$.effects.createWrapper(el); // Create Wrapper
		var ref = (direction == 'up' || direction == 'down') ? 'top' : 'left';
		var motion = (direction == 'up' || direction == 'left') ? 'pos' : 'neg';
		var distance = o.options.distance || (ref == 'top' ? el.outerHeight({margin:true}) / 2 : el.outerWidth({margin:true}) / 2);
		if (mode == 'show') el.css('opacity', 0).css(ref, motion == 'pos' ? -distance : distance); // Shift
		
		// Animation
		var animation = {opacity: mode == 'show' ? 1 : 0};
		animation[ref] = (mode == 'show' ? (motion == 'pos' ? '+=' : '-=') : (motion == 'pos' ? '-=' : '+=')) + distance;
		
		// Animate
		el.animate(animation, { queue: false, duration: o.duration, easing: o.options.easing, complete: function() {
			if(mode == 'hide') el.hide(); // Hide
			$.effects.restore(el, props); $.effects.removeWrapper(el); // Restore
			if(o.callback) o.callback.apply(this, arguments); // Callback
			el.dequeue();
		}});
		
	});
	
};

})(jQuery);
/*
 * jQuery UI Effects Explode
 *
 * Copyright (c) 2008 Paul Bakaus (ui.jquery.com)
 * Dual licensed under the MIT (MIT-LICENSE.txt)
 * and GPL (GPL-LICENSE.txt) licenses.
 * 
 * http://docs.jquery.com/UI/Effects/Explode
 *
 * Depends:
 *	effects.core.js
 */
(function($) {

$.effects.explode = function(o) {

	return this.queue(function() {

	var rows = o.options.pieces ? Math.round(Math.sqrt(o.options.pieces)) : 3;
	var cells = o.options.pieces ? Math.round(Math.sqrt(o.options.pieces)) : 3;
	
	o.options.mode = o.options.mode == 'toggle' ? ($(this).is(':visible') ? 'hide' : 'show') : o.options.mode;
	var el = $(this).show().css('visibility', 'hidden');
	var offset = el.offset();
	
	//Substract the margins - not fixing the problem yet.
	offset.top -= parseInt(el.css("marginTop")) || 0;
	offset.left -= parseInt(el.css("marginLeft")) || 0;
	
	var width = el.outerWidth(true);
	var height = el.outerHeight(true);

	for(var i=0;i<rows;i++) { // =
		for(var j=0;j<cells;j++) { // ||
			el
				.clone()
				.appendTo('body')
				.wrap('<div></div>')
				.css({
					position: 'absolute',
					visibility: 'visible',
					left: -j*(width/cells),
					top: -i*(height/rows)
				})
				.parent()
				.addClass('effects-explode')
				.css({
					position: 'absolute',
					overflow: 'hidden',
					width: width/cells,
					height: height/rows,
					left: offset.left + j*(width/cells) + (o.options.mode == 'show' ? (j-Math.floor(cells/2))*(width/cells) : 0),
					top: offset.top + i*(height/rows) + (o.options.mode == 'show' ? (i-Math.floor(rows/2))*(height/rows) : 0),
					opacity: o.options.mode == 'show' ? 0 : 1
				}).animate({
					left: offset.left + j*(width/cells) + (o.options.mode == 'show' ? 0 : (j-Math.floor(cells/2))*(width/cells)),
					top: offset.top + i*(height/rows) + (o.options.mode == 'show' ? 0 : (i-Math.floor(rows/2))*(height/rows)),
					opacity: o.options.mode == 'show' ? 1 : 0
				}, o.duration || 500);
		}
	}

	// Set a timeout, to call the callback approx. when the other animations have finished
	setTimeout(function() {
		
		o.options.mode == 'show' ? el.css({ visibility: 'visible' }) : el.css({ visibility: 'visible' }).hide();
				if(o.callback) o.callback.apply(el[0]); // Callback
				el.dequeue();
				
				$('.effects-explode').remove();
		
	}, o.duration || 500);
	
		
	});
	
};

})(jQuery);
/*
 * jQuery UI Effects Fold
 *
 * Copyright (c) 2008 Aaron Eisenberger (aaronchi@gmail.com)
 * Dual licensed under the MIT (MIT-LICENSE.txt)
 * and GPL (GPL-LICENSE.txt) licenses.
 * 
 * http://docs.jquery.com/UI/Effects/Fold
 *
 * Depends:
 *	effects.core.js
 */
(function($) {

$.effects.fold = function(o) {

	return this.queue(function() {

		// Create element
		var el = $(this), props = ['position','top','left'];
		
		// Set options
		var mode = $.effects.setMode(el, o.options.mode || 'hide'); // Set Mode
		var size = o.options.size || 15; // Default fold size
		var horizFirst = !(!o.options.horizFirst); // Ensure a boolean value
		
		// Adjust
		$.effects.save(el, props); el.show(); // Save & Show
		var wrapper = $.effects.createWrapper(el).css({overflow:'hidden'}); // Create Wrapper
		var widthFirst = ((mode == 'show') != horizFirst);
		var ref = widthFirst ? ['width', 'height'] : ['height', 'width'];
		var distance = widthFirst ? [wrapper.width(), wrapper.height()] : [wrapper.height(), wrapper.width()];
		var percent = /([0-9]+)%/.exec(size);
		if(percent) size = parseInt(percent[1]) / 100 * distance[mode == 'hide' ? 0 : 1];
		if(mode == 'show') wrapper.css(horizFirst ? {height: 0, width: size} : {height: size, width: 0}); // Shift
		
		// Animation
		var animation1 = {}, animation2 = {};
		animation1[ref[0]] = mode == 'show' ? distance[0] : size;
		animation2[ref[1]] = mode == 'show' ? distance[1] : 0;
		
		// Animate
		wrapper.animate(animation1, o.duration / 2, o.options.easing)
		.animate(animation2, o.duration / 2, o.options.easing, function() {
			if(mode == 'hide') el.hide(); // Hide
			$.effects.restore(el, props); $.effects.removeWrapper(el); // Restore
			if(o.callback) o.callback.apply(el[0], arguments); // Callback
			el.dequeue();
		});
		
	});
	
};

})(jQuery);
/*
 * jQuery UI Effects Highlight
 *
 * Copyright (c) 2008 Aaron Eisenberger (aaronchi@gmail.com)
 * Dual licensed under the MIT (MIT-LICENSE.txt)
 * and GPL (GPL-LICENSE.txt) licenses.
 * 
 * http://docs.jquery.com/UI/Effects/Highlight
 *
 * Depends:
 *	effects.core.js
 */
;(function($) {

$.effects.highlight = function(o) {

	return this.queue(function() {
		
		// Create element
		var el = $(this), props = ['backgroundImage','backgroundColor','opacity'];
		
		// Set options
		var mode = $.effects.setMode(el, o.options.mode || 'show'); // Set Mode
		var color = o.options.color || "#ffff99"; // Default highlight color
		var oldColor = el.css("backgroundColor");
		
		// Adjust
		$.effects.save(el, props); el.show(); // Save & Show
		el.css({backgroundImage: 'none', backgroundColor: color}); // Shift
		
		// Animation
		var animation = {backgroundColor: oldColor };
		if (mode == "hide") animation['opacity'] = 0;
		
		// Animate
		el.animate(animation, { queue: false, duration: o.duration, easing: o.options.easing, complete: function() {
			if(mode == "hide") el.hide();
			$.effects.restore(el, props);
		if (mode == "show" && jQuery.browser.msie) this.style.removeAttribute('filter'); 
			if(o.callback) o.callback.apply(this, arguments);
			el.dequeue();
		}});
		
	});
	
};

})(jQuery);
/*
 * jQuery UI Effects Pulsate
 *
 * Copyright (c) 2008 Aaron Eisenberger (aaronchi@gmail.com)
 * Dual licensed under the MIT (MIT-LICENSE.txt)
 * and GPL (GPL-LICENSE.txt) licenses.
 * 
 * http://docs.jquery.com/UI/Effects/Pulsate
 *
 * Depends:
 *	effects.core.js
 */
(function($) {

$.effects.pulsate = function(o) {

	return this.queue(function() {
		
		// Create element
		var el = $(this);
		
		// Set options
		var mode = $.effects.setMode(el, o.options.mode || 'show'); // Set Mode
		var times = o.options.times || 5; // Default # of times
		
		// Adjust
		if (mode == 'hide') times--;
		if (el.is(':hidden')) { // Show fadeIn
			el.css('opacity', 0);
			el.show(); // Show
			el.animate({opacity: 1}, o.duration / 2, o.options.easing);
			times = times-2;
		}
		
		// Animate
		for (var i = 0; i < times; i++) { // Pulsate
			el.animate({opacity: 0}, o.duration / 2, o.options.easing).animate({opacity: 1}, o.duration / 2, o.options.easing);
		};
		if (mode == 'hide') { // Last Pulse
			el.animate({opacity: 0}, o.duration / 2, o.options.easing, function(){
				el.hide(); // Hide
				if(o.callback) o.callback.apply(this, arguments); // Callback
			});
		} else {
			el.animate({opacity: 0}, o.duration / 2, o.options.easing).animate({opacity: 1}, o.duration / 2, o.options.easing, function(){
				if(o.callback) o.callback.apply(this, arguments); // Callback
			});
		};
		el.queue('fx', function() { el.dequeue(); });
		el.dequeue();
	});
	
};

})(jQuery);
/*
 * jQuery UI Effects Scale
 *
 * Copyright (c) 2008 Aaron Eisenberger (aaronchi@gmail.com)
 * Dual licensed under the MIT (MIT-LICENSE.txt)
 * and GPL (GPL-LICENSE.txt) licenses.
 * 
 * http://docs.jquery.com/UI/Effects/Scale
 *
 * Depends:
 *	effects.core.js
 */
(function($) {

$.effects.puff = function(o) {

	return this.queue(function() {

		// Create element
		var el = $(this);
	
		// Set options
		var options = $.extend(true, {}, o.options);
		var mode = $.effects.setMode(el, o.options.mode || 'hide'); // Set Mode
		var percent = parseInt(o.options.percent) || 150; // Set default puff percent
		options.fade = true; // It's not a puff if it doesn't fade! :)
		var original = {height: el.height(), width: el.width()}; // Save original
	
		// Adjust
		var factor = percent / 100;
		el.from = (mode == 'hide') ? original : {height: original.height * factor, width: original.width * factor};
	
		// Animation
		options.from = el.from;
		options.percent = (mode == 'hide') ? percent : 100;
		options.mode = mode;
	
		// Animate
		el.effect('scale', options, o.duration, o.callback);
		el.dequeue();
	});
	
};

$.effects.scale = function(o) {
	
	return this.queue(function() {
	
		// Create element
		var el = $(this);

		// Set options
		var options = $.extend(true, {}, o.options);
		var mode = $.effects.setMode(el, o.options.mode || 'effect'); // Set Mode
		var percent = parseInt(o.options.percent) || (parseInt(o.options.percent) == 0 ? 0 : (mode == 'hide' ? 0 : 100)); // Set default scaling percent
		var direction = o.options.direction || 'both'; // Set default axis
		var origin = o.options.origin; // The origin of the scaling
		if (mode != 'effect') { // Set default origin and restore for show/hide
			options.origin = origin || ['middle','center'];
			options.restore = true;
		}
		var original = {height: el.height(), width: el.width()}; // Save original
		el.from = o.options.from || (mode == 'show' ? {height: 0, width: 0} : original); // Default from state
	
		// Adjust
		var factor = { // Set scaling factor
			y: direction != 'horizontal' ? (percent / 100) : 1,
			x: direction != 'vertical' ? (percent / 100) : 1
		};
		el.to = {height: original.height * factor.y, width: original.width * factor.x}; // Set to state
		
		if (o.options.fade) { // Fade option to support puff
			if (mode == 'show') {el.from.opacity = 0; el.to.opacity = 1;};
			if (mode == 'hide') {el.from.opacity = 1; el.to.opacity = 0;};
		};
	
		// Animation
		options.from = el.from; options.to = el.to; options.mode = mode;
	
		// Animate
		el.effect('size', options, o.duration, o.callback);
		el.dequeue();
	});
	
};

$.effects.size = function(o) {

	return this.queue(function() {
		
		// Create element
		var el = $(this), props = ['position','top','left','width','height','overflow','opacity'];
		var props1 = ['position','top','left','overflow','opacity']; // Always restore
		var props2 = ['width','height','overflow']; // Copy for children
		var cProps = ['fontSize'];
		var vProps = ['borderTopWidth', 'borderBottomWidth', 'paddingTop', 'paddingBottom'];
		var hProps = ['borderLeftWidth', 'borderRightWidth', 'paddingLeft', 'paddingRight'];
		
		// Set options
		var mode = $.effects.setMode(el, o.options.mode || 'effect'); // Set Mode
		var restore = o.options.restore || false; // Default restore
		var scale = o.options.scale || 'both'; // Default scale mode
		var origin = o.options.origin; // The origin of the sizing
		var original = {height: el.height(), width: el.width()}; // Save original
		el.from = o.options.from || original; // Default from state
		el.to = o.options.to || original; // Default to state
		// Adjust
		if (origin) { // Calculate baseline shifts
			var baseline = $.effects.getBaseline(origin, original);
			el.from.top = (original.height - el.from.height) * baseline.y;
			el.from.left = (original.width - el.from.width) * baseline.x;
			el.to.top = (original.height - el.to.height) * baseline.y;
			el.to.left = (original.width - el.to.width) * baseline.x;
		};
		var factor = { // Set scaling factor
			from: {y: el.from.height / original.height, x: el.from.width / original.width},
			to: {y: el.to.height / original.height, x: el.to.width / original.width}
		};
		if (scale == 'box' || scale == 'both') { // Scale the css box
			if (factor.from.y != factor.to.y) { // Vertical props scaling
				props = props.concat(vProps);
				el.from = $.effects.setTransition(el, vProps, factor.from.y, el.from);
				el.to = $.effects.setTransition(el, vProps, factor.to.y, el.to);
			};
			if (factor.from.x != factor.to.x) { // Horizontal props scaling
				props = props.concat(hProps);
				el.from = $.effects.setTransition(el, hProps, factor.from.x, el.from);
				el.to = $.effects.setTransition(el, hProps, factor.to.x, el.to);
			};
		};
		if (scale == 'content' || scale == 'both') { // Scale the content
			if (factor.from.y != factor.to.y) { // Vertical props scaling
				props = props.concat(cProps);
				el.from = $.effects.setTransition(el, cProps, factor.from.y, el.from);
				el.to = $.effects.setTransition(el, cProps, factor.to.y, el.to);
			};
		};
		$.effects.save(el, restore ? props : props1); el.show(); // Save & Show
		$.effects.createWrapper(el); // Create Wrapper
		el.css('overflow','hidden').css(el.from); // Shift
		
		// Animate
		if (scale == 'content' || scale == 'both') { // Scale the children
			vProps = vProps.concat(['marginTop','marginBottom']).concat(cProps); // Add margins/font-size
			hProps = hProps.concat(['marginLeft','marginRight']); // Add margins
			props2 = props.concat(vProps).concat(hProps); // Concat
			el.find("*[width]").each(function(){
				child = $(this);
				if (restore) $.effects.save(child, props2);
				var c_original = {height: child.height(), width: child.width()}; // Save original
				child.from = {height: c_original.height * factor.from.y, width: c_original.width * factor.from.x};
				child.to = {height: c_original.height * factor.to.y, width: c_original.width * factor.to.x};
				if (factor.from.y != factor.to.y) { // Vertical props scaling
					child.from = $.effects.setTransition(child, vProps, factor.from.y, child.from);
					child.to = $.effects.setTransition(child, vProps, factor.to.y, child.to);
				};
				if (factor.from.x != factor.to.x) { // Horizontal props scaling
					child.from = $.effects.setTransition(child, hProps, factor.from.x, child.from);
					child.to = $.effects.setTransition(child, hProps, factor.to.x, child.to);
				};
				child.css(child.from); // Shift children
				child.animate(child.to, o.duration, o.options.easing, function(){
					if (restore) $.effects.restore(child, props2); // Restore children
				}); // Animate children
			});
		};
		
		// Animate
		el.animate(el.to, { queue: false, duration: o.duration, easing: o.options.easing, complete: function() {
			if(mode == 'hide') el.hide(); // Hide
			$.effects.restore(el, restore ? props : props1); $.effects.removeWrapper(el); // Restore
			if(o.callback) o.callback.apply(this, arguments); // Callback
			el.dequeue();
		}}); 
		
	});

};

})(jQuery);
/*
 * jQuery UI Effects Shake
 *
 * Copyright (c) 2008 Aaron Eisenberger (aaronchi@gmail.com)
 * Dual licensed under the MIT (MIT-LICENSE.txt)
 * and GPL (GPL-LICENSE.txt) licenses.
 * 
 * http://docs.jquery.com/UI/Effects/Shake
 *
 * Depends:
 *	effects.core.js
 */
(function($) {

$.effects.shake = function(o) {

	return this.queue(function() {

		// Create element
		var el = $(this), props = ['position','top','left'];
		
		// Set options
		var mode = $.effects.setMode(el, o.options.mode || 'effect'); // Set Mode
		var direction = o.options.direction || 'left'; // Default direction
		var distance = o.options.distance || 20; // Default distance
		var times = o.options.times || 3; // Default # of times
		var speed = o.duration || o.options.duration || 140; // Default speed per shake
		
		// Adjust
		$.effects.save(el, props); el.show(); // Save & Show
		$.effects.createWrapper(el); // Create Wrapper
		var ref = (direction == 'up' || direction == 'down') ? 'top' : 'left';
		var motion = (direction == 'up' || direction == 'left') ? 'pos' : 'neg';
		
		// Animation
		var animation = {}, animation1 = {}, animation2 = {};
		animation[ref] = (motion == 'pos' ? '-=' : '+=')  + distance;
		animation1[ref] = (motion == 'pos' ? '+=' : '-=')  + distance * 2;
		animation2[ref] = (motion == 'pos' ? '-=' : '+=')  + distance * 2;
		
		// Animate
		el.animate(animation, speed, o.options.easing);
		for (var i = 1; i < times; i++) { // Shakes
			el.animate(animation1, speed, o.options.easing).animate(animation2, speed, o.options.easing);
		};
		el.animate(animation1, speed, o.options.easing).
		animate(animation, speed / 2, o.options.easing, function(){ // Last shake
			$.effects.restore(el, props); $.effects.removeWrapper(el); // Restore
			if(o.callback) o.callback.apply(this, arguments); // Callback
		});
		el.queue('fx', function() { el.dequeue(); });
		el.dequeue();
	});
	
};

})(jQuery);
/*
 * jQuery UI Effects Slide
 *
 * Copyright (c) 2008 Aaron Eisenberger (aaronchi@gmail.com)
 * Dual licensed under the MIT (MIT-LICENSE.txt)
 * and GPL (GPL-LICENSE.txt) licenses.
 * 
 * http://docs.jquery.com/UI/Effects/Slide
 *
 * Depends:
 *	effects.core.js
 */
(function($) {

$.effects.slide = function(o) {

	return this.queue(function() {

		// Create element
		var el = $(this), props = ['position','top','left'];
		
		// Set options
		var mode = $.effects.setMode(el, o.options.mode || 'show'); // Set Mode
		var direction = o.options.direction || 'left'; // Default Direction
		
		// Adjust
		$.effects.save(el, props); el.show(); // Save & Show
		$.effects.createWrapper(el).css({overflow:'hidden'}); // Create Wrapper
		var ref = (direction == 'up' || direction == 'down') ? 'top' : 'left';
		var motion = (direction == 'up' || direction == 'left') ? 'pos' : 'neg';
		var distance = o.options.distance || (ref == 'top' ? el.outerHeight({margin:true}) : el.outerWidth({margin:true}));
		if (mode == 'show') el.css(ref, motion == 'pos' ? -distance : distance); // Shift
		
		// Animation
		var animation = {};
		animation[ref] = (mode == 'show' ? (motion == 'pos' ? '+=' : '-=') : (motion == 'pos' ? '-=' : '+=')) + distance;
		
		// Animate
		el.animate(animation, { queue: false, duration: o.duration, easing: o.options.easing, complete: function() {
			if(mode == 'hide') el.hide(); // Hide
			$.effects.restore(el, props); $.effects.removeWrapper(el); // Restore
			if(o.callback) o.callback.apply(this, arguments); // Callback
			el.dequeue();
		}});
		
	});
	
};

})(jQuery);
/*
 * jQuery UI Effects Transfer
 *
 * Copyright (c) 2008 Aaron Eisenberger (aaronchi@gmail.com)
 * Dual licensed under the MIT (MIT-LICENSE.txt)
 * and GPL (GPL-LICENSE.txt) licenses.
 * 
 * http://docs.jquery.com/UI/Effects/Transfer
 *
 * Depends:
 *	effects.core.js
 */
(function($) {

$.effects.transfer = function(o) {

	return this.queue(function() {

		// Create element
		var el = $(this);
		
		// Set options
		var mode = $.effects.setMode(el, o.options.mode || 'effect'); // Set Mode
		var target = $(o.options.to); // Find Target
		var position = el.offset();
		var transfer = $('<div class="ui-effects-transfer"></div>').appendTo(document.body);
		if(o.options.className) transfer.addClass(o.options.className);
		
		// Set target css
		transfer.addClass(o.options.className);
		transfer.css({
			top: position.top,
			left: position.left,
			height: el.outerHeight() - parseInt(transfer.css('borderTopWidth')) - parseInt(transfer.css('borderBottomWidth')),
			width: el.outerWidth() - parseInt(transfer.css('borderLeftWidth')) - parseInt(transfer.css('borderRightWidth')),
			position: 'absolute'
		});
		
		// Animation
		position = target.offset();
		animation = {
			top: position.top,
			left: position.left,
			height: target.outerHeight() - parseInt(transfer.css('borderTopWidth')) - parseInt(transfer.css('borderBottomWidth')),
			width: target.outerWidth() - parseInt(transfer.css('borderLeftWidth')) - parseInt(transfer.css('borderRightWidth'))
		};
		
		// Animate
		transfer.animate(animation, o.duration, o.options.easing, function() {
			transfer.remove(); // Remove div
			if(o.callback) o.callback.apply(el[0], arguments); // Callback
			el.dequeue();
		}); 
		
	});
	
};

})(jQuery);
/**
 * jQuery.ScrollTo
 * Copyright (c) 2008 Ariel Flesler - aflesler(at)gmail(dot)com | http://flesler.blogspot.com
 * Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php)
 * and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses.
 * Date: 2/19/2008
 *
 * @projectDescription Easy element scrolling using jQuery.
 * Tested with jQuery 1.2.1. On FF 2.0.0.11, IE 6, Opera 9.22 and Safari 3 beta. on Windows.
 *
 * @author Ariel Flesler
 * @version 1.3.3
 *
 * @id jQuery.scrollTo
 * @id jQuery.fn.scrollTo
 * @param {String, Number, DOMElement, jQuery, Object} target Where to scroll the matched elements.
 *	  The different options for target are:
 *		- A number position (will be applied to all axes).
 *		- A string position ('44', '100px', '+=90', etc ) will be applied to all axes
 *		- A jQuery/DOM element ( logically, child of the element to scroll )
 *		- A string selector, that will be relative to the element to scroll ( 'li:eq(2)', etc )
 *		- A hash { top:x, left:y }, x and y can be any kind of number/string like above.
 * @param {Number} duration The OVERALL length of the animation, this argument can be the settings object instead.
 * @param {Object} settings Hash of settings, optional.
 *	 @option {String} axis Which axis must be scrolled, use 'x', 'y', 'xy' or 'yx'.
 *	 @option {Number} duration The OVERALL length of the animation.
 *	 @option {String} easing The easing method for the animation.
 *	 @option {Boolean} margin If true, the margin of the target element will be deducted from the final position.
 *	 @option {Object, Number} offset Add/deduct from the end position. One number for both axes or { top:x, left:y }.
 *	 @option {Object, Number} over Add/deduct the height/width multiplied by 'over', can be { top:x, left:y } when using both axes.
 *	 @option {Boolean} queue If true, and both axis are given, the 2nd axis will only be animated after the first one ends.
 *	 @option {Function} onAfter Function to be called after the scrolling ends. 
 *	 @option {Function} onAfterFirst If queuing is activated, this function will be called after the first scrolling ends.
 * @return {jQuery} Returns the same jQuery object, for chaining.
 *
 * @example $('div').scrollTo( 340 );
 *
 * @example $('div').scrollTo( '+=340px', { axis:'y' } );
 *
 * @example $('div').scrollTo( 'p.paragraph:eq(2)', 500, { easing:'swing', queue:true, axis:'xy' } );
 *
 * @example var second_child = document.getElementById('container').firstChild.nextSibling;
 *			$('#container').scrollTo( second_child, { duration:500, axis:'x', onAfter:function(){
 *				alert('scrolled!!');																   
 *			}});
 *
 * @example $('div').scrollTo( { top: 300, left:'+=200' }, { offset:-20 } );
 *
 * Notes:
 *  - jQuery.scrollTo will make the whole window scroll, it accepts the same arguments as jQuery.fn.scrollTo.
 *	- If you are interested in animated anchor navigation, check http://jquery.com/plugins/project/LocalScroll.
 *	- The options margin, offset and over are ignored, if the target is not a jQuery object or a DOM element.
 *	- The option 'queue' won't be taken into account, if only 1 axis is given.
 */
;(function( $ ){

	var $scrollTo = $.scrollTo = function( target, duration, settings ){
		$scrollTo.window().scrollTo( target, duration, settings );
	};

	$scrollTo.defaults = {
		axis:'y',
		duration:1
	};

	//returns the element that needs to be animated to scroll the window
	$scrollTo.window = function(){
		return $( $.browser.safari ? 'body' : 'html' );
	};

	$.fn.scrollTo = function( target, duration, settings ){
		if( typeof duration == 'object' ){
			settings = duration;
			duration = 0;
		}
		settings = $.extend( {}, $scrollTo.defaults, settings );
		duration = duration || settings.speed || settings.duration;//speed is still recognized for backwards compatibility
		settings.queue = settings.queue && settings.axis.length > 1;//make sure the settings are given right
		if( settings.queue )
			duration /= 2;//let's keep the overall speed, the same.
		settings.offset = both( settings.offset );
		settings.over = both( settings.over );

		return this.each(function(){
			var elem = this, $elem = $(elem),
				t = target, toff, attr = {},
				win = $elem.is('html,body');
			switch( typeof t ){
				case 'number'://will pass the regex
				case 'string':
					if( /^([+-]=)?\d+(px)?$/.test(t) ){
						t = both( t );
						break;//we are done
					}
					t = $(t,this);// relative selector, no break!
				case 'object':
					if( t.is || t.style )//DOM/jQuery
						toff = (t = $(t)).offset();//get the real position of the target 
			}
			$.each( settings.axis.split(''), function( i, axis ){
				var Pos	= axis == 'x' ? 'Left' : 'Top',
					pos = Pos.toLowerCase(),
					key = 'scroll' + Pos,
					act = elem[key],
					Dim = axis == 'x' ? 'Width' : 'Height',
					dim = Dim.toLowerCase();

				if( toff ){//jQuery/DOM
					attr[key] = toff[pos] + ( win ? 0 : act - $elem.offset()[pos] );

					if( settings.margin ){//if it's a dom element, reduce the margin
						attr[key] -= parseInt(t.css('margin'+Pos)) || 0;
						attr[key] -= parseInt(t.css('border'+Pos+'Width')) || 0;
					}
					
					attr[key] += settings.offset[pos] || 0;//add/deduct the offset
					
					if( settings.over[pos] )//scroll to a fraction of its width/height
						attr[key] += t[dim]() * settings.over[pos];
				}else
					attr[key] = t[pos];//remove the unnecesary 'px'

				if( /^\d+$/.test(attr[key]) )//number or 'number'
					attr[key] = attr[key] <= 0 ? 0 : Math.min( attr[key], max(Dim) );//check the limits

				if( !i && settings.queue ){//queueing each axis is required					
					if( act != attr[key] )//don't waste time animating, if there's no need.
						animate( settings.onAfterFirst );//intermediate animation
					delete attr[key];//don't animate this axis again in the next iteration.
				}
			});			
			animate( settings.onAfter );			

			function animate( callback ){
				$elem.animate( attr, duration, settings.easing, callback && function(){
					callback.call(this, target);
				});
			};
			function max( Dim ){
				var el = win ? $.browser.opera ? document.body : document.documentElement : elem;
				return el['scroll'+Dim] - el['client'+Dim];
			};
		});
	};

	function both( val ){
		return typeof val == 'object' ? val : { top:val, left:val };
	};

})( jQuery );/*
 * jqModal - Minimalist Modaling with jQuery
 *
 * Copyright (c) 2007 Brice Burgess <bhb@iceburg.net>, http://www.iceburg.net
 * Licensed under the MIT License:
 * http://www.opensource.org/licenses/mit-license.php
 * 
 * $Version: 2007.08.17 +r11
 * 
 */
(function($) {
$.fn.jqm=function(o){
var _o = {
zIndex: 3000,
overlay: 50,
overlayClass: 'jqmOverlay',
closeClass: 'jqmClose',
trigger: '.jqModal',
ajax: false,
target: false,
modal: false,
toTop: false,
onShow: false,
onHide: false,
onLoad: false
};
return this.each(function(){if(this._jqm)return; s++; this._jqm=s;
H[s]={c:$.extend(_o, o),a:false,w:$(this).addClass('jqmID'+s),s:s};
if(_o.trigger)$(this).jqmAddTrigger(_o.trigger);
});};

$.fn.jqmAddClose=function(e){hs(this,e,'jqmHide'); return this;};
$.fn.jqmAddTrigger=function(e){hs(this,e,'jqmShow'); return this;};
$.fn.jqmShow=function(t){return this.each(function(){if(!H[this._jqm].a)$.jqm.open(this._jqm,t)});};
$.fn.jqmHide=function(t){return this.each(function(){if(H[this._jqm].a)$.jqm.close(this._jqm,t)});};

$.jqm = {
hash:{},
open:function(s,t){var h=H[s],c=h.c,cc='.'+c.closeClass,z=(/^\d+$/.test(h.w.css('z-index')))?h.w.css('z-index'):c.zIndex,o=$('<div></div>').css({height:'100%',width:'100%',position:'fixed',left:0,top:0,'z-index':z-1,opacity:c.overlay/100});h.t=t;h.a=true;h.w.css('z-index',z);
 if(c.modal) {if(!A[0])F('bind');A.push(s);o.css('cursor','wait');}
 else if(c.overlay > 0)h.w.jqmAddClose(o);
 else o=false;

 h.o=(o)?o.addClass(c.overlayClass).prependTo('body'):false;
 if(ie6){$('html,body').css({height:'100%',width:'100%'});if(o){o=o.css({position:'absolute'})[0];for(var y in {Top:1,Left:1})o.style.setExpression(y.toLowerCase(),"(_=(document.documentElement.scroll"+y+" || document.body.scroll"+y+"))+'px'");}}

 if(c.ajax) {var r=c.target||h.w,u=c.ajax,r=(typeof r == 'string')?$(r,h.w):$(r),u=(u.substr(0,1) == '@')?$(t).attr(u.substring(1)):u;
  r.load(u,function(){if(c.onLoad)c.onLoad.call(this,h);if(cc)h.w.jqmAddClose($(cc,h.w));e(h);});}
 else if(cc)h.w.jqmAddClose($(cc,h.w));

 if(c.toTop&&h.o)h.w.before('<span id="jqmP'+h.w[0]._jqm+'"></span>').insertAfter(h.o);	
 (c.onShow)?c.onShow(h):h.w.show();e(h);return false;
},
close:function(s){var h=H[s];h.a=false;
 if(A[0]){A.pop();if(!A[0])F('unbind');}
 if(h.c.toTop&&h.o)$('#jqmP'+h.w[0]._jqm).after(h.w).remove();
 if(h.c.onHide)h.c.onHide(h);else{h.w.hide();if(h.o)h.o.remove();} return false;
}};
var s=0,H=$.jqm.hash,A=[],ie6=$.browser.msie&&($.browser.version == "6.0"),
i=$('<iframe src="javascript:false;document.write(\'\');" class="jqm"></iframe>').css({opacity:0}),
e=function(h){if(ie6)if(h.o)h.o.html('<p style="width:100%;height:100%"/>').prepend(i);else if(!$('iframe.jqm',h.w)[0])h.w.prepend(i); f(h);},
f=function(h){try{$(':input:visible',h.w)[0].focus();}catch(e){}},
F=function(t){$()[t]("keypress",m)[t]("keydown",m)[t]("mousedown",m);},
m=function(e){var h=H[A[A.length-1]],r=(!$(e.target).parents('.jqmID'+h.s)[0]);if(r)f(h);return !r;},
hs=function(w,e,y){var s=[];w.each(function(){s.push(this._jqm)});
 $(e).each(function(){if(this[y])$.extend(this[y],s);else{this[y]=s;$(this).click(function(){for(var i in {jqmShow:1,jqmHide:1})for(var s in this[i])if(H[this[i][s]])H[this[i][s]].w[i](this);return false;});}});};
})(jQuery);/* http://keith-wood.name/timeEntry.html
   Time entry for jQuery v1.3.1.
   Written by Keith Wood (kbwood@virginbroadband.com.au) June 2007.
   Dual licensed under the GPL (http://dev.jquery.com/browser/trunk/jquery/GPL-LICENSE.txt) and 
   MIT (http://dev.jquery.com/browser/trunk/jquery/MIT-LICENSE.txt) licenses. 
   Please attribute the author if you use it. */

/* Turn an input field into an entry point for a time value.
   The time can be entered via directly typing the value,
   via the arrow keys, or via spinner buttons.
   It is configurable to show 12 or 24-hour time, to show or hide seconds,
   to enforce a minimum and/or maximum time, to change the spinner image,
   and to constrain the time to steps, e.g. only on the quarter hours.
   Attach it with $('input selector').timeEntry(); for default settings,
   or configure it with options like:
   $('input selector').timeEntry(
      {spinnerImage: 'timeEntry2.png', spinnerSize: [20, 20, 0]}); */

(function($) { // Hide scope, no $ conflict

/* TimeEntry manager.
   Use the singleton instance of this class, $.timeEntry, to interact with the time entry
   functionality. Settings for (groups of) fields are maintained in an instance object
   (TimeEntryInstance), allowing multiple different settings on the same page. */
function TimeEntry() {
	this._nextId = 0; // Next ID for a time entry instance
	this._inst = []; // List of instances indexed by ID
	this._disabledInputs = []; // List of time entry inputs that have been disabled
	this.regional = []; // Available regional settings, indexed by language code
	this.regional[''] = { // Default regional settings
		show24Hours: false, // True to use 24 hour time, false for 12 hour (AM/PM)
		separator: ':', // The separator between time fields
		ampmPrefix: '', // The separator before the AM/PM text
		ampmNames: ['AM', 'PM'], // Names of morning/evening markers
		spinnerTexts: ['Now', 'Previous field', 'Next field', 'Increment', 'Decrement']
		// The popup texts for the spinner image areas
	};
	this._defaults = {
		appendText: '', // Display text following the input box, e.g. showing the format
		showSeconds: false, // True to show seconds as well, false for hours/minutes only
		timeSteps: [1, 1, 1], // Steps for each of hours/minutes/seconds when incrementing/decrementing
		initialField: 0, // The field to highlight initially, 0 = hours, 1 = minutes, ...
		useMouseWheel: true, // True to use mouse wheel for increment/decrement if possible,
			// false to never use it
		defaultTime: null, // The time to use if none has been set, leave at null for now
		minTime: null, // The earliest selectable time, or null for no limit
		maxTime: null, // The latest selectable time, or null for no limit
		spinnerImage: 'timeEntry.png', // The URL of the images to use for the time spinner
			// Six images packed horizontally for normal and then each button pressed
		spinnerSize: [20, 20, 8], // The width and height of the spinner image,
			// and size of centre button for current time
		spinnerIncDecOnly: false, // True for increment/decrement buttons only, false for all
		spinnerRepeat: [500, 250], // Initial and subsequent waits in milliseconds
			// for repeats on the spinner buttons
		beforeShow: null // Function that takes an input field and
			// returns a set of custom settings for the time entry
	};
	$.extend(this._defaults, this.regional['']);
}

$.extend(TimeEntry.prototype, {
	/* Class name added to elements to indicate already configured with time entry. */
	markerClassName: 'hasTimeEntry',
	
	/* Register a new time entry instance - with custom settings. */
	_register: function(inst) {
		var id = this._nextId++;
		this._inst[id] = inst;
		return id;
	},

	/* Retrieve a particular time entry instance based on its ID. */
	_getInst: function(id) {
		return this._inst[id] || id;
	},

	/* Override the default settings for all instances of the time entry.
	   @param  settings  object - the new settings to use as defaults (anonymous object)
	   @return void */
	setDefaults: function(settings) {
		extendRemove(this._defaults, settings || {});
	},

	/* Initialise time entry. */
	_doFocus: function(target) {
		var input = (target.nodeName && target.nodeName.toLowerCase() == 'input' ? target : this);
		if ($.timeEntry._lastInput == input) { // already here
			return;
		}
		if ($.timeEntry._isDisabledTimeEntry(input)) {
			return;
		}
		var inst = $.timeEntry._getInst(input._timeId);
		inst._input = $(input);
		$.timeEntry._focussed = true;
		$.timeEntry._lastInput = input;
		$.timeEntry._blurredInput = null;
		var beforeShow = inst._get('beforeShow');
		extendRemove(inst._settings, (beforeShow ? beforeShow(input) : {}));
		inst._parseTime();
	},

	/* Note that the field has been exited. */
	_doBlur: function(event) {
		$.timeEntry._blurredInput = $.timeEntry._lastInput;
		$.timeEntry._lastInput = null;
	},

	/* Select appropriate field portion on click, if already in the field. */
	_doClick: function(event) {
		var input = event.target;
		var inst = $.timeEntry._getInst(input._timeId);
		if (!$.timeEntry._focussed) {
			var fieldSize = inst._get('separator').length + 2;
			inst._field = 0;
			if ($.browser.msie) { // check against bounding boxes
				var value = input.value;
				var offsetX = event.clientX + $.timeEntry._findScroll(event.srcElement)[0] -
					$.timeEntry._findPos(event.srcElement)[0];
				for (var field = 0; field <= Math.max(1, inst._secondField, inst._ampmField); field++) {
					var end = (field != inst._ampmField ? (field * fieldSize) + 2 :
						(inst._ampmField * fieldSize) + inst._get('ampmPrefix').length +
						inst._get('ampmNames')[0].length);
					input.value = value.substring(0, end); // trim to this size
					var range = input.createTextRange();
					if (offsetX < range.boundingWidth) { // and compare
						inst._field = field;
						break;
					}
				}
				input.value = value; // restore original value
			}
			else { // use input select range
				for (var field = 0; field <= Math.max(1, inst._secondField, inst._ampmField); field++) {
					var start = (field != inst._ampmField ? (field * fieldSize) + 2 :
						(inst._ampmField * fieldSize) + inst._get('ampmPrefix').length +
						inst._get('ampmNames')[0].length);
					if (input.selectionStart < start) {
						inst._field = field;
						break;
					}
				}
			}
		}
		inst._showField();
		$.timeEntry._focussed = false;
	},

	/* Handle keystrokes in the field. */
	_doKeyDown: function(event) {
		if (event.keyCode >= 48) { // >= '0'
			return true;
		}
		var inst = $.timeEntry._getInst(this._timeId);
		switch (event.keyCode) {
			case 9: return (event.shiftKey ?
						// move to previous time field, or out if at the beginning
						inst._previousField(true) :
						// move to next time field, or out if at the end
						inst._nextField(true));
			case 35: if (event.ctrlKey) { // clear time on ctrl+end
						inst._setValue('');
					}
					else { // last field on end
						inst._field = Math.max(1, inst._secondField, inst._ampmField);
						inst._adjustField(0);
					}
					break;
			case 36: if (event.ctrlKey) { // current time on ctrl+home
						inst._setTime();
					}
					else { // first field on home
						inst._field = 0;
						inst._adjustField(0);
					}
					break;
			case 37: inst._previousField(false); break; // previous field on left
			case 38: inst._adjustField(+1); break; // increment time field on up
			case 39: inst._nextField(false); break; // next field on right
			case 40: inst._adjustField(-1); break; // decrement time field on down
			case 46: inst._setValue(''); break; // clear time on delete
		}
		return false;
	},

	/* Disallow unwanted characters. */
	_doKeyPress: function(event) {
		var chr = String.fromCharCode(event.charCode == undefined ? event.keyCode : event.charCode);
		if (chr < ' ') {
			return true;
		}
		var inst = $.timeEntry._getInst(this._timeId);
		inst._handleKeyPress(chr);
		return false;
	},

	/* Increment/decrement on mouse wheel activity. */
	_doMouseWheel: function(event, delta) {
		delta = ($.browser.opera ? -delta / Math.abs(delta) : delta);
		var inst = $.timeEntry._getInst(this._timeId);
		inst._adjustField(delta);
		event.preventDefault();
	},

	/* Attach the time entry handler to an input field. */
	_connectTimeEntry: function(target, inst) {
		var input = $(target);
		if (input.is('.' + this.markerClassName)) {
			return;
		}
		var spinnerImage = inst._get('spinnerImage');
		var spinnerText = inst._get('spinnerText');
		var spinnerSize = inst._get('spinnerSize');
		var appendText = inst._get('appendText');
		var spinner = (!spinnerImage ? null : 
			$('<span class="timeEntry_control" _timeid="' + inst._id +
			'" style="display: inline-block; background: url(\'' + spinnerImage + '\') 0 0 no-repeat; ' +
			'width: ' + spinnerSize[0] + 'px; height: ' + spinnerSize[1] + 'px;' +
			($.browser.mozilla && $.browser.version.substr(0, 3) != '1.9' ?
			' padding-left: ' + spinnerSize[0] + 'px; padding-bottom: ' +
			(spinnerSize[1] - 18) + 'px;' : '') + '"></span>'));
		input.wrap('<span class="timeEntry_wrap"></span>').
			after(appendText ? '<span class="timeEntry_append">' + appendText + '</span>' : '').
			after(spinner || '');
		input.addClass(this.markerClassName).bind('focus.timeEntry', this._doFocus).
			bind('blur.timeEntry', this._doBlur).bind('click.timeEntry', this._doClick).
			bind('keydown.timeEntry', this._doKeyDown).bind('keypress.timeEntry', this._doKeyPress);
		// check pastes
		if ($.browser.mozilla) {
			input.bind('input.timeEntry', function(event) { inst._parseTime(); });
		}
		if ($.browser.msie) {
			input.bind('paste.timeEntry', 
				function(event) { setTimeout(function() { inst._parseTime(); }, 1); });
		}
		// allow mouse wheel usage
		if (inst._get('useMouseWheel') && $.fn.mousewheel) {
			input.mousewheel(this._doMouseWheel);
		}
		input[0]._timeId = inst._id;
		if (spinner) {
			spinner.mousedown(this._handleSpinner).mouseup(this._endSpinner).
				mouseout(this._endSpinner).mousemove(this._describeSpinner);
			spinner[0]._timeId = inst._id;
		}
	},

	/* Enable a time entry input and any associated spinner.
	   @param  input  element - single input field
	   @return void */
	_enableTimeEntry: function(input) {
		this._enableDisable(input, false);
	},

	/* Disable a time entry input and any associated spinner.
	   @param  input  element - single input field
	   @return void */
	_disableTimeEntry: function(input) {
		this._enableDisable(input, true);
	},

	/* Enable or disable a time entry input and any associated spinner.
	   @param  input    element - single input field
	   @param  disable  boolean - true to disable, false to enable
	   @return void */
	_enableDisable: function(input, disable) {
		var inst = $.timeEntry._getInst(input._timeId);
		if (!inst) {
			return;
		}
		input.disabled = disable;
		if (input.nextSibling && input.nextSibling.nodeName.toLowerCase() == 'span') {
			$.timeEntry._changeSpinner(inst, input.nextSibling, (disable ? 5 : -1));
		}
		$.timeEntry._disabledInputs = $.map($.timeEntry._disabledInputs,
			function(value) { return (value == input ? null : value); }); // delete entry
		if (disable) {
			$.timeEntry._disabledInputs[$.timeEntry._disabledInputs.length] = input;
		}
	},

	/* Check whether an input field has been disabled.
	   @param  input  element - input field to check
	   @return true if this field has been disabled, false if it is enabled */
	_isDisabledTimeEntry: function(input) {
		for (var i = 0; i < this._disabledInputs.length; i++) {
			if (this._disabledInputs[i] == input) {
				return true;
			}
		}
		return false;
	},

	/* Reconfigure the settings for a time entry field.
	   @param  input     element - input field to change
	   @param  settings  object - new settings to add
	   @return  void */
	_changeTimeEntry: function(input, settings) {
		var inst = this._getInst(input._timeId);
		if (inst) {
			var currentTime = inst._extractTime();
			extendRemove(inst._settings, settings || {});
			if (currentTime) {
				inst._setTime(new Date(0, 0, 0, currentTime[0], currentTime[1], currentTime[2]));
			}
		}
	},

	/* Remove the time entry functionality from an input.
	   @param  input  element - input field to affect
	   @return  void */
	_destroyTimeEntry: function(input) {
		$input = $(input);
		if (!$input.is('.' + this.markerClassName)) {
			return;
		}
		$input.removeClass(this.markerClassName).unbind('focus.timeEntry').
			unbind('blur.timeEntry').unbind('click.timeEntry').
			unbind('keydown.timeEntry').unbind('keypress.timeEntry');
		// check pastes
		if ($.browser.mozilla) {
			$input.unbind('input.timeEntry');
		}
		if ($.browser.msie) {
			$input.unbind('paste.timeEntry');
		}
		if ($.fn.mousewheel) {
			$input.unmousewheel();
		}
		this._inst[input._timeId] = null;
		input._timeId = undefined;
		this._disabledInputs = $.map(this._disabledInputs,
			function(value) { return (value == input ? null : value); }); // delete entry
		input.parentNode.parentNode.replaceChild(input, input.parentNode);
	},

	/* Initialise the current time for a time entry input field.
	   @param  input  element - input field to update
	   @param  time   Date - the new time (year/month/day ignored) or null for now */
	_setTimeTimeEntry: function(input, time) {
		var inst = this._getInst(input._timeId);
		if (inst) {
			inst._input = $(input);
			inst._setTime(time ? (typeof time == 'object' ?
				new Date(time.getTime()) : time) : null);
		}
	},

	/* Retrieve the current time for a time entry input field.
	   @param  input  element - input field to update
	   @return  Date  with the set time (year/month/day zero) or null if none */
	_getTimeTimeEntry: function(input) {
		var inst = this._getInst(input._timeId);
		var currentTime = (inst ? inst._extractTime() : null);
		return (!currentTime ? null :
			new Date(0, 0, 0, currentTime[0], currentTime[1], currentTime[2]));
	},

	/* Change the title based on position within the spinner. */
	_describeSpinner: function(event) {
		var spinner = $.timeEntry._getSpinnerTarget(event);
		var inst = $.timeEntry._getInst(spinner._timeId);
		spinner.title = inst._get('spinnerTexts')[$.timeEntry._getSpinnerRegion(inst, event)];
	},

	/* Handle a click on the spinner. */
	_handleSpinner: function(event) {
		var spinner = $.timeEntry._getSpinnerTarget(event);
		var input = spinner.previousSibling;
		if ($.timeEntry._isDisabledTimeEntry(input)) {
			return;
		}
		if (input == $.timeEntry._blurredInput) {
			$.timeEntry._lastInput = input;
			$.timeEntry._blurredInput = null;
		}
		var inst = $.timeEntry._getInst(input._timeId);
		$.timeEntry._doFocus(input);
		var region = $.timeEntry._getSpinnerRegion(inst, event);
		$.timeEntry._changeSpinner(inst, spinner, region);
		$.timeEntry._actionSpinner(inst, region);
		var spinnerRepeat = inst._get('spinnerRepeat');
		if (region >= 3 && spinnerRepeat[0]) { // repeat increment/decrement
			$.timeEntry._timer = setTimeout(
				function() { $.timeEntry._repeatSpinner(inst, region); },
				spinnerRepeat[0]);
			$(spinner).one('mouseout', $.timeEntry._releaseSpinner).
				one('mouseup', $.timeEntry._releaseSpinner);
		}
	},

	/* Action a click on the spinner. */
	_actionSpinner: function(inst, region) {
		switch (region) {
			case 0: inst._setTime(); break;
			case 1: inst._previousField(false); break;
			case 2: inst._nextField(false); break;
			case 3: inst._adjustField(+1); break;
			case 4: inst._adjustField(-1); break;
		}
	},

	/* Repeat a click on the spinner. */
	_repeatSpinner: function(inst, region) {
		$.timeEntry._lastInput = $.timeEntry._blurredInput;
		this._actionSpinner(inst, region);
		this._timer = setTimeout(
			function() { $.timeEntry._repeatSpinner(inst, region); },
			inst._get('spinnerRepeat')[1]);
	},

	/* Stop a spinner repeat. */
	_releaseSpinner: function(event) {
		clearTimeout($.timeEntry._timer);
	},

	/* Tidy up after a spinner click. */
	_endSpinner: function(event) {
		var spinner = $.timeEntry._getSpinnerTarget(event);
		var inst = $.timeEntry._getInst(spinner._timeId);
		if (!$.timeEntry._isDisabledTimeEntry(spinner.previousSibling)) {
			$.timeEntry._changeSpinner(inst, spinner, -1);
		}
		if (!$.browser.opera) {
			$.timeEntry._lastInput = $.timeEntry._blurredInput;
		}
		if ($.timeEntry._lastInput) {
			inst._showField();
		}
	},

	/* Retrieve the spinner from the event. */
	_getSpinnerTarget: function(event) {
		return (event.target ? event.target : event.srcElement);
	},

	/* Determine which "button" within the spinner was clicked. */
	_getSpinnerRegion: function(inst, event) {
		var spinner = this._getSpinnerTarget(event);
		var pos = this._findPos(spinner);
		var scrolled = this._findScroll(spinner);
		var spinnerIncDecOnly = inst._get('spinnerIncDecOnly');
		var left = (spinnerIncDecOnly ? 99 :
			event.clientX + scrolled[0] - pos[0] - ($.browser.msie ? 1 : 0));
		var top = event.clientY + scrolled[1] - pos[1] - ($.browser.msie ? 1 : 0);
		var spinnerSize = inst._get('spinnerSize');
		var right = (spinnerIncDecOnly ? 99 : spinnerSize[0] - left);
		var bottom = spinnerSize[1] - top;
		if (spinnerSize[2] > 0 && Math.abs(left - right) <= spinnerSize[2] &&
				Math.abs(top - bottom) <= spinnerSize[2]) {
			return 0; // centre button
		}
		var min = Math.min(left, top, right, bottom);
		return (min == left ? 1 : (min == right ? 2 : (min == top ? 3 : 4))); // nearest edge
	},

	/* Change the spinner image depending on button clicked. */
	_changeSpinner: function(inst, spinner, region) {
		$(spinner).css('background-position',
			'-' + ((region + 1) * inst._get('spinnerSize')[0]) + 'px 0px');
	},

	/* Find an object's position on the screen. */
	_findPos: function(obj) {
		var curLeft = curTop = 0;
		if (obj.offsetParent) {
			curLeft = obj.offsetLeft;
			curTop = obj.offsetTop;
			while (obj = obj.offsetParent) {
				var origCurLeft = curLeft;
				curLeft += obj.offsetLeft;
				if (curLeft < 0) {
					curLeft = origCurLeft;
				}
				curTop += obj.offsetTop;
			}
		}
		return [curLeft, curTop];
	},

	/* Find an object's scroll offset on the screen. */
	_findScroll: function(obj) {
		var isFixed = false;
		$(obj).parents().each(function() {
			isFixed |= $(this).css('position') == 'fixed';
		});
		if (isFixed && !$.browser.opera) {
			return [0, 0];
		}
		var scrollLeft = ($.browser.opera ? document.body.scrollLeft : obj.scrollLeft);
		var scrollTop = ($.browser.opera ? document.body.scrollTop : obj.scrollTop);;
		if (!$.browser.opera) {
			while (obj = obj.parentNode) {
				scrollLeft += obj.scrollLeft || 0;
				scrollTop += obj.scrollTop || 0;
			}
		}
		return [scrollLeft, scrollTop];
	}
});

/* Individualised settings for time entries applied to one or more related inputs.
   Instances are managed and manipulated through the TimeEntry manager. */
function TimeEntryInstance(settings) {
	this._id = $.timeEntry._register(this);
	this._selectedHour = 0; // The currently selected hour
	this._selectedMinute = 0; // The currently selected minute
	this._selectedSecond = 0; // The currently selected second
	this._field = 0; // The selected subfield
	this._input = null; // The attached input field
	// customise the time entry object - uses manager defaults if not overridden
	this._settings = extendRemove({}, settings || {}); // clone
}

$.extend(TimeEntryInstance.prototype, {
	/* Get a setting value, defaulting if necessary. */
	_get: function(name) {
		return (this._settings[name] != null ? this._settings[name] : $.timeEntry._defaults[name]);
	},

	/* Extract the time value from the input field, or default to now. */
	_parseTime: function() {
		var currentTime = this._extractTime();
		var showSeconds = this._get('showSeconds');
		if (currentTime) {
			this._selectedHour = currentTime[0];
			this._selectedMinute = currentTime[1];
			this._selectedSecond = currentTime[2];
		}
		else {
			var now = this._constrainTime();
			this._selectedHour = now[0];
			this._selectedMinute = now[1];
			this._selectedSecond = (showSeconds ? now[2] : 0);
		}
		this._secondField = (showSeconds ? 2 : -1);
		this._ampmField = (this._get('show24Hours') ? -1 : (showSeconds ? 3 : 2));
		this._lastChr = '';
		this._field = Math.max(0, Math.min(
			Math.max(1, this._secondField, this._ampmField), this._get('initialField')));
		if (this._input.val() != '') {
			this._showTime();
		}
	},

	/* Extract the time value from the input field as an array of values, or default to null. */
	_extractTime: function() {
		var value = (this._input ? this._input.val() : '');
		var separator = this._get('separator');
		var currentTime = value.split(separator);
		if (separator == '' && value != '') {
			currentTime[0] = value.substring(0, 2);
			currentTime[1] = value.substring(2, 4);
			currentTime[2] = value.substring(4, 6);
		}
		var ampmNames = this._get('ampmNames');
		var show24Hours = this._get('show24Hours');
		if (currentTime.length >= 2) {
			var isAM = !show24Hours && (value.indexOf(ampmNames[0]) > -1);
			var isPM = !show24Hours && (value.indexOf(ampmNames[1]) > -1);
			var hour = parseInt(currentTime[0], 10);
			hour = (isNaN(hour) ? 0 : hour);
			hour = ((isAM || isPM) && hour == 12 ? 0 : hour) + (isPM ? 12 : 0);
			var minute = parseInt(currentTime[1], 10);
			minute = (isNaN(minute) ? 0 : minute);
			var second = (currentTime.length >= 3 ?
				parseInt(currentTime[2], 10) : 0);
			second = (isNaN(second) || !this._get('showSeconds') ? 0 : second);
			return this._constrainTime([hour, minute, second]);
		} 
		return null;
	},

	/* Constrain the given/current time to the time steps. */
	_constrainTime: function(fields) {
		var specified = (fields != null);
		if (!specified) {
			var now = this._determineTime(this._get('defaultTime')) || new Date();
			fields = [now.getHours(), now.getMinutes(), now.getSeconds()];
		}
		var reset = false;
		var timeSteps = this._get('timeSteps');
		for (var i = 0; i < timeSteps.length; i++) {
			if (reset) {
				fields[i] = 0;
			}
			else if (timeSteps[i] > 1) {
				fields[i] = Math.round(fields[i] / timeSteps[i]) * timeSteps[i];
				reset = true;
			}
		}
		return fields;
	},

	/* Set the selected time into the input field. */
	_showTime: function() {
		var show24Hours = this._get('show24Hours');
		var separator = this._get('separator');
		var currentTime = (this._formatNumber(show24Hours ? this._selectedHour :
			((this._selectedHour + 11) % 12) + 1) + separator +
			this._formatNumber(this._selectedMinute) +
			(this._get('showSeconds') ? separator + this._formatNumber(this._selectedSecond) : '') +
			(show24Hours ?  '' : this._get('ampmPrefix') +
			this._get('ampmNames')[(this._selectedHour < 12 ? 0 : 1)]));
		this._setValue(currentTime);
		this._showField();
	},

	/* Highlight the current time field. */
	_showField: function() {
		if (!this._input) {
			return;
		}
		var input = this._input[0];
		var separator = this._get('separator');
		var fieldSize = separator.length + 2;
		var start = (this._field != this._ampmField ? (this._field * fieldSize) :
			(this._ampmField * fieldSize) - separator.length + this._get('ampmPrefix').length);
		var end = start + (this._field != this._ampmField ? 2 : this._get('ampmNames')[0].length);
		if (input.setSelectionRange) { // Mozilla
			input.setSelectionRange(start, end);
		}
		else if (input.createTextRange) { // IE
			var range = input.createTextRange();
			range.moveStart('character', start);
			range.moveEnd('character', end - this._input.val().length);
			range.select();
		}
		if (!input.disabled) {
			input.focus();
		}
	},

	/* Ensure displayed single number has a leading zero. */
	_formatNumber: function(value) {
		return (value < 10 ? '0' : '') + value;
	},

	/* Update the input field and notify listeners. */
	_setValue: function(value) {
		this._input.val(value);
		this._input.trigger('change');
	},

	/* Move to previous field, or out of field altogether if appropriate (return  true). */
	_previousField: function(moveOut) {
		var atFirst = (this._input.val() == '' || this._field == 0);
		if (!atFirst) {
			this._field--;
		}
		this._showField();
		this._lastChr = '';
		return (atFirst && moveOut);
	},

	/* Move to next field, or out of field altogether if appropriate (return  true). */
	_nextField: function(moveOut) {
		var atLast = (this._input.val() == '' ||
			this._field == Math.max(1, this._secondField, this._ampmField));
		if (!atLast) {
			this._field++;
		}
		this._showField();
		this._lastChr = '';
		return (atLast && moveOut);
	},

	/* Update the current field in the direction indicated. */
	_adjustField: function(offset) {
		if (this._input && this._input.val() == '') {
			offset = 0;
		}
		var timeSteps = this._get('timeSteps');
		this._setTime(new Date(0, 0, 0,
			this._selectedHour + (this._field == 0 ? offset * timeSteps[0] : 0) +
			(this._field == this._ampmField ? offset * 12 : 0),
			this._selectedMinute + (this._field == 1 ? offset * timeSteps[1] : 0),
			this._selectedSecond + (this._field == this._secondField ? offset * timeSteps[2] : 0)));
	},

	/* Check against minimum/maximum and display time. */
	_setTime: function(time) {
		time = this._determineTime(time);
		var fields = this._constrainTime(time ? [time.getHours(), time.getMinutes(), time.getSeconds()] : null);
		time = new Date(0, 0, 0, fields[0], fields[1], fields[2]);
		// normalise to base date
		var time = this._normaliseTime(time);
		var minTime = this._normaliseTime(this._determineTime(this._get('minTime')));
		var maxTime = this._normaliseTime(this._determineTime(this._get('maxTime')));
		// ensure it is within the bounds set
		time = (minTime && time < minTime ? minTime :
			(maxTime && time > maxTime ? maxTime : time));
		this._selectedHour = time.getHours();
		this._selectedMinute = time.getMinutes();
		this._selectedSecond = time.getSeconds();
		this._showTime();
	},

	/* A time may be specified as an exact value or a relative one. */
	_determineTime: function(setting) {
		var offsetNumeric = function(offset) { // e.g. +300, -2
			var time = new Date();
			time.setTime(time.getTime() + offset * 1000);
			return time;
		};
		var offsetString = function(offset) { // e.g. '+2m', '-4h', '+3h +30m'
			var time = new Date();
			var hour = time.getHours();
			var minute = time.getMinutes();
			var second = time.getSeconds();
			var pattern = /([+-]?[0-9]+)\s*(s|S|m|M|h|H)?/g;
			var matches = pattern.exec(offset);
			while (matches) {
				switch (matches[2] || 's') {
					case 's' : case 'S' :
						second += parseInt(matches[1]); break;
					case 'm' : case 'M' :
						minute += parseInt(matches[1]); break;
					case 'h' : case 'H' :
						hour += parseInt(matches[1]); break;
				}
				matches = pattern.exec(offset);
			}
			time = new Date(0, 0, 10, hour, minute, second, 0);
			if (/^!/.test(offset)) { // no wrapping
				if (time.getDate() > 10) {
					time = new Date(0, 0, 10, 23, 59, 59);
				}
				else if (time.getDate() < 10) {
					time = new Date(0, 0, 10, 0, 0, 0);
				}
			}
			return time;
		};
		return (setting ? (typeof setting == 'string' ? offsetString(setting) :
			(typeof setting == 'number' ? offsetNumeric(setting) : setting)) : null);
	},

	/* Normalise time object to a common date. */
	_normaliseTime: function(time) {
		if (!time) {
			return null;
		}
		time.setFullYear(2001);
		time.setMonth(1 - 1);
		time.setDate(26);
		return time;
	},

	/* Update time based on keystroke entered. */
	_handleKeyPress: function(chr) {
		if (chr == this._get('separator')) {
			this._nextField(false);
		}
		else if (chr >= '0' && chr <= '9') { // allow direct entry of time
			var value = (this._lastChr + chr) * 1;
			var show24Hours = this._get('show24Hours');
			var hour = (this._field == 0 && ((show24Hours && value < 24) ||
				(value >= 1 && value <= 12)) ?
				value + (!show24Hours && this._selectedHour >= 12 ? 12 : 0) : this._selectedHour);
			var minute = (this._field == 1 && value < 60 ? value : this._selectedMinute);
			var second = (this._field == this._secondField && value < 60 ?
				value : this._selectedSecond);
			var fields = this._constrainTime([hour, minute, second]);
			this._setTime(new Date(0, 0, 0, fields[0], fields[1], fields[2]));
			this._lastChr = chr;
		}
		else if (!this._get('show24Hours')) { // set am/pm based on first char of names
			var ampmNames = this._get('ampmNames');
			if ((chr == ampmNames[0].substring(0, 1).toLowerCase() &&
					this._selectedHour >= 12) ||
					(chr == ampmNames[1].substring(0, 1).toLowerCase() &&
					this._selectedHour < 12)) {
				var saveField = this._field;
				this._field = this._ampmField;
				this._adjustField(+1);
				this._field = saveField;
				this._showField();
			}
		}
	}
});

/* jQuery extend now ignores nulls! */
function extendRemove(target, props) {
	$.extend(target, props);
	for (var name in props) {
		if (props[name] == null) {
			target[name] = null;
		}
	}
	return target;
}

/* Attach the time entry functionality to a jQuery selection.
   @param  command  string - the command to run (optional, default 'attach')
   @param  options  object - the new settings to use for these countdown instances
   @return jQuery object - for chaining further calls */
$.fn.timeEntry = function(options) {
	var otherArgs = Array.prototype.slice.call(arguments, 1);
	if (typeof options == 'string' && (options == 'isDisabled' || options == 'getTime')) {
		return $.timeEntry['_' + options + 'TimeEntry'].apply($.timeEntry, [this[0]].concat(otherArgs));
	}
	return this.each(function() {
		var nodeName = this.nodeName.toLowerCase();
		if (nodeName == 'input') {
			if (typeof options == 'string') {
				$.timeEntry['_' + options + 'TimeEntry'].apply($.timeEntry, [this].concat(otherArgs));
			}
			else {
				// check for settings on the control itself - in namespace 'time:'
				var inlineSettings = null;
				for (attrName in $.timeEntry._defaults) {
					var attrValue = this.getAttribute('time:' + attrName);
					if (attrValue) {
						inlineSettings = inlineSettings || {};
						try {
							inlineSettings[attrName] = eval(attrValue);
						}
						catch (err) {
							inlineSettings[attrName] = attrValue;
						}
					}
				}
				var inst = (inst && !inlineSettings ? inst :
					new TimeEntryInstance(!inlineSettings ? options :
					$.extend(inlineSettings, options)));
				$.timeEntry._connectTimeEntry(this, inst);
			}
		} 
	});
};

/* Initialise the time entry functionality. */
$(document).ready(function() {
   $.timeEntry = new TimeEntry(); // singleton instance
});

})(jQuery);
/*
Date Input 1.1.6
Requires jQuery version: 1.2.6

Copyright (c) 2007-2008 Jonathan Leighton & Torchbox Ltd

Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
*/

DateInput = (function($) { // Localise the $ function

function DateInput(el, opts) {
  if (typeof(opts) != "object") opts = {};
  $.extend(this, DateInput.DEFAULT_OPTS, opts);
  
  this.input = $(el);
  this.bindMethodsToObj("show", "hide", "hideIfClickOutside", "selectDate", "prevMonth", "nextMonth");
  
  this.build();
  this.selectDate();
  this.hide();
};
DateInput.DEFAULT_OPTS = {
  month_names: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
  short_month_names: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
  short_day_names: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
  start_of_week: 1
};
DateInput.prototype = {
  build: function() {
    this.monthNameSpan = $('<span class="month_name"></span>');
    var monthNav = $('<p class="month_nav"></p>').append(
      $('<a href="#" class="prev">&laquo;</a>').click(this.prevMonth),
      " ", this.monthNameSpan, " ",
      $('<a href="#" class="next">&raquo;</a>').click(this.nextMonth)
    );
    
    var tableShell = "<table><thead><tr>";
    $(this.adjustDays(this.short_day_names)).each(function() {
      tableShell += "<th>" + this + "</th>";
    });
    tableShell += "</tr></thead><tbody></tbody></table>";
    
    this.dateSelector = this.rootLayers = $('<div class="date_selector"></div>').append(monthNav, tableShell).appendTo(document.body);
    
    if ($.browser.msie && $.browser.version < 7) {
      this.ieframe = $('<iframe class="date_selector_ieframe" frameborder="0" src="#"></iframe>').insertBefore(this.dateSelector);
      this.rootLayers = this.rootLayers.add(this.ieframe);
    };
    
    this.tbody = $("tbody", this.dateSelector);
    
    // The anon function ensures the event is discarded
    this.input.change(this.bindToObj(function() { this.selectDate(); }));
  },
  
  selectMonth: function(date) {
    this.currentMonth = new Date(date.getFullYear(), date.getMonth(), 1);
    
    var rangeStart = this.rangeStart(date), rangeEnd = this.rangeEnd(date);
    var numDays = this.daysBetween(rangeStart, rangeEnd);
    var dayCells = "";
    
    for (var i = 0; i <= numDays; i++) {
      var currentDay = new Date(rangeStart.getFullYear(), rangeStart.getMonth(), rangeStart.getDate() + i);
      
      if (this.isFirstDayOfWeek(currentDay)) dayCells += "<tr>";
      
      if (currentDay.getMonth() == date.getMonth()) {
        dayCells += '<td date="' + this.dateToString(currentDay) + '"><a href="#">' + currentDay.getDate() + '</a></td>';
      } else {
        dayCells += '<td class="unselected_month" date="' + this.dateToString(currentDay) + '">' + currentDay.getDate() + '</td>';
      };
      
      if (this.isLastDayOfWeek(currentDay)) dayCells += "</tr>";
    };
    
    this.monthNameSpan.empty().append(this.monthName(date) + " " + date.getFullYear());
    this.tbody.empty().append(dayCells);
    
    $("a", this.tbody).click(this.bindToObj(function(event) {
      this.selectDate(this.stringToDate($(event.target).parent().attr("date")));
      this.hide();
      return false;
    }));
    
    $("td[date=" + this.dateToString(new Date()) + "]", this.tbody).addClass("today");
  },
  
  selectDate: function(date) {
    if (typeof(date) == "undefined") {
      date = this.stringToDate(this.input.val());
    };
    
    if (date) {
      this.selectedDate = date;
      this.selectMonth(date);
      var stringDate = this.dateToString(date);
      $('td[date=' + stringDate + ']', this.tbody).addClass("selected");
      
      if (this.input.val() != stringDate) {
        this.input.val(stringDate).change();
      };
    } else {
      this.selectMonth(new Date());
    };
  },
  
  show: function() {
    this.rootLayers.css("display", "block");
    this.setPosition();
    this.input.unbind("focus", this.show);
    $([window, document.body]).click(this.hideIfClickOutside);
  },
  
  hide: function() {
    this.rootLayers.css("display", "none");
    $([window, document.body]).unbind("click", this.hideIfClickOutside);
    this.input.focus(this.show);
  },
  
  hideIfClickOutside: function(event) {
    if (event.target != this.input[0] && !this.insideSelector(event)) {
      this.hide();
    };
  },
  
  stringToDate: function(string) {
    var matches;
    if (matches = string.match(/^(\d{1,2}) ([^\s]+) (\d{4,4})$/)) {
      return new Date(matches[3], this.shortMonthNum(matches[2]), matches[1]);
    } else {
      return null;
    };
  },
  
  dateToString: function(date) {
    return date.getDate() + " " + this.short_month_names[date.getMonth()] + " " + date.getFullYear();
  },
  
  setPosition: function() {
    var offset = this.input.offset();
    this.rootLayers.css({
      top: offset.top + this.input.outerHeight(),
      left: offset.left
    });
    
    if (this.ieframe) {
      this.ieframe.css({
        width: this.dateSelector.outerWidth(),
        height: this.dateSelector.outerHeight()
      });
    };
  },
  
  moveMonthBy: function(amount) {
    this.selectMonth(new Date(this.currentMonth.setMonth(this.currentMonth.getMonth() + amount)));
  },
  
  prevMonth: function() {
    this.moveMonthBy(-1);
    return false;
  },
  
  nextMonth: function() {
    this.moveMonthBy(1);
    return false;
  },
  
  monthName: function(date) {
    return this.month_names[date.getMonth()];
  },
  
  insideSelector: function(event) {
    var offset = this.dateSelector.offset();
    offset.right = offset.left + this.dateSelector.outerWidth();
    offset.bottom = offset.top + this.dateSelector.outerHeight();
    
    return event.pageY < offset.bottom &&
           event.pageY > offset.top &&
           event.pageX < offset.right &&
           event.pageX > offset.left;
  },
  
  bindToObj: function(fn) {
    var self = this;
    return function() { return fn.apply(self, arguments) };
  },
  
  bindMethodsToObj: function() {
    for (var i = 0; i < arguments.length; i++) {
      this[arguments[i]] = this.bindToObj(this[arguments[i]]);
    };
  },
  
  indexFor: function(array, value) {
    for (var i = 0; i < array.length; i++) {
      if (value == array[i]) return i;
    };
  },
  
  monthNum: function(month_name) {
    return this.indexFor(this.month_names, month_name);
  },
  
  shortMonthNum: function(month_name) {
    return this.indexFor(this.short_month_names, month_name);
  },
  
  shortDayNum: function(day_name) {
    return this.indexFor(this.short_day_names, day_name);
  },
  
  daysBetween: function(start, end) {
    start = Date.UTC(start.getFullYear(), start.getMonth(), start.getDate());
    end = Date.UTC(end.getFullYear(), end.getMonth(), end.getDate());
    return (end - start) / 86400000;
  },
  
  changeDayTo: function(to, date, direction) {
    var difference = direction * (Math.abs(date.getDay() - to - (direction * 7)) % 7);
    return new Date(date.getFullYear(), date.getMonth(), date.getDate() + difference);
  },
  
  rangeStart: function(date) {
    return this.changeDayTo(this.start_of_week, new Date(date.getFullYear(), date.getMonth()), -1);
  },
  
  rangeEnd: function(date) {
    return this.changeDayTo((this.start_of_week - 1) % 7, new Date(date.getFullYear(), date.getMonth() + 1, 0), 1);
  },
  
  isFirstDayOfWeek: function(date) {
    return date.getDay() == this.start_of_week;
  },
  
  isLastDayOfWeek: function(date) {
    return date.getDay() == (this.start_of_week - 1) % 7;
  },
  
  adjustDays: function(days) {
    var newDays = [];
    for (var i = 0; i < days.length; i++) {
      newDays[i] = days[(i + this.start_of_week) % 7];
    };
    return newDays;
  }
};

$.fn.date_input = function(opts) {
  return this.each(function() { new DateInput(this, opts); });
};
$.date_input = { initialize: function(opts) {
  $("input.date_input").date_input(opts);
} };

return DateInput;
})(jQuery); // End localisation of the $ function
/*
  Version       :    0.01
  Release Date  :    5 June 2007
  Author        :    Shawn Grover, jqdates@open2space.com
  License       :     
      This library is made available to the general public under the 
      Creative Commons Attribution-ShareAlike 2.5 Canada license.
      (http://creativecommons.org/licenses/by-sa/2.5/ca/)
      
      In short, use it as you will, but please do not claim the work as your own.
      
  Library Description:
      This library aims to add more robust date functionality to Javascript.
      The goal is to allow developers to use dates in a natural way, without 
      worrying about what format a date may be in, or the underlying math for 
      the date calculations.
  
  Credits:
      The parseDate() method is heavily based on Matt Kruse's logic within 
      his dates library.  ( http://www.mattkruse.com/javascript/date/ )
      Matt's work is licensed under the GPL.  If you use or modify the 
      parseDate routine for your own puposes, please give Matt the credit
      he is due.
      
      The date picker drawing routines are based on code found at
      http://jszen.blogspot.com/2007/03/how-to-build-simple-calendar-with.html
      I do not see any license or copyright notice there at this time 
      (I could be blind).  But this blogger deserves credit for their posting
      which lead me to the code contained herein.
      
      All other code was written from scratch by Shawn Grover, based on 
      accumulated experience with Javascript and dates.


  Usage:
      - Only two global variables are used for the routine.  The names have 
        been chosen to avoid likely name conflicts with other libraries.
      - All the code, with the exception of the two global variables and the
        jQuery plugin code is contained within a single object called "o2s".  
        This has been done to emulate a namespace and minimize name conflicts.
  
      - Include your libraries:
          <script type="text/javascript" src="jquery.js"></script>
          <script type="text/javascript" src="jquery.dates.js"></script>
          
      - After the libraries are included, you have access to the functions.
       
      DATE PICKER
      ===========
        The date picker calendar will appear directly below the element it 
        is associated with.
        
        Possible Options:
            {
              //the date format to use for the selected date
              //the format string is based on the formatting function defined below
              format    : "yyyy-mm-dd",
              
              //what date should be highlight by default when the date picker
              //initially becomes visible?
              //if null or not set, defaults to the date in the text box, 
              //or the current date if the text box does not contain a date
              date      :   new Date(),
              
              //should a different form element be update
              //used when adding an anchor, image, or text that will trigger
              //the date picker, but the date should be stored somewhere else
              //once selected
              //- the referenced object must be a DOM element.
              parent    :   object
            }
          
        Samples
          
          - adding a datepicker to a textbox, with default settings:
                $("#mytextboxID").datePicker();
            
          - adding a datepicker to an anchor tag, but updating a text box
                $("#myanchorID").datePicker({parent : $("#mytextboxID")[0] });
                
      IS DATE
      =======
         Often we need to know if a string of text is a valid date.  This method
         returns true/false if the text string can be seen as a date.
         
         Usage
          o2s.isDate("datestring", formatstring);
          
          $("#mytextboxID").isDate(formatstring);
              
              **  note the jQuery .dateAdd() does not return a
              jQuery object.
              
        The formatstring parameter is optional.  It is the same type of format 
        string used in the format() and parseDate() methods below.
        
         
      DATE FORMAT
      ===========
        The date format function can be called directly via:
        
            o2s.format(date, format);
            
        OR a jQuery method is available for use with text boxes that contain a date:
        
            $("#mytextboxID").dateFormat("ddd, dd mmmm yyyy");
            
            - this will make sure the value within the textbox is formatted to the 
              desired structure, if possible.
              
        The format string uses the following tokens to determine what values to insert:

Formatting Tokens
  d     - day of month (not padded)       h     - hour, 12 hour clock (not padded)
  dd    - day of month (zero padded)      hh    - hour, 12 hour clock (zero padded)
  ddd   - day name, abbreviated           H     - hour, 24 hour clock (not padded)
  dddd  - day name, full                  HH    - hour, 24 hour clock (zero padded)
  m     - month number (not padded)       i     - minutes (not padded)
  mm    - month number (zero padded)      ii    - minutes (zero padded)
  mmm   - month name, abbreviated         s     - seconds (not padded)
  mmmm  - month name, full                ss    - seconds (zero padded)
  y     - two digit year (not padded)     a     - am / pm (lowercase)
  yy    - two digit year (zero padded)    A     - AM / PM (uppercase)
  yyyy  - four digit year                 O     - timze zone offset from GMT.  
  j     - ordinal day/julian date                 (that is a letter OH, not zero)
  jjj   - ordinal day/julian date (same as "j")
  
Prebuilt formats
  short       - "m/d/yy"              
  medium      - "mmm d yyyy"          
  long        - "mmmm d yyyy"         
  shorttime   - "m/d/yy HH:ii"        
  mediumtime  - "mmm d yyyy HH:ii"    
  longtime    - "mmmm d yyyy hh:ii:ss A O"
  iso8610     - "yyyy-mm-ddTHH:ii:ss O"
  yyyymmdd    - "yyyy-mm-dd"

        Any text in the formatting string that is not one of the tokens above,
        will be inserted into the output directly.
        
        Examples:     (assuming a date of 1 Jun 2007 15:05:30)
        
        "dddd, dd mmmm yyyy"    =>    "Friday, 01 June 2007"
        "d : mmm : yy"          =>    "1 : Jun : 07"
        "shorttime"             =>    "1/7/07 15:05"
        "hh:ii a = HH:ii"       =>    "03:05 pm = 15:05"
        
    PARSE DATE
    ==========
      The parseDate function attempts to extract a date object from an arbitrary 
      string.  A format string can be specified to facilitate a quicker check.
      If the format string is omitted, the formats found in the commonformats
      array will be tried until a valid date object is found, or the full list 
      has been completed.
      
      NOTE: It is possible for a date string to match more than one date format.
      In this event you will see odd dates appear.  Specify a format string to 
      minimize these odd behaviors, or thoroughly test the formats you are 
      expecting to make sure the desired behavior is seen.  If needed, 
      modify the commonformats array to suit your needs.
      
      The format string uses the same tokens for the format method described above.
      
      If a date object cannot be determined, a null value is returned
      
      Usage:
        standard javascript:
            var d = o2s.parseDate(date, format);
            
        jQuery
            var d = $("#mytextboxID").parseDate(format);
            
    DATE DIFF
    =========
      This function determines the quantity of a given unit between two dates.
      For instance, how many hours between now and chirstmas, how many years
      between events, how many hours from the start time to now (i.e. duration)
    
      Units:  ms  - milliseconds      d - days
              s   - seconds           w - weeks
              i   - minutes           y - years
              h   - hours
      
      Usage:  
        standard Javascript
          var d = o2s.dateDiff("unit",startdate, enddate);
          
        jQuery
          var d = $("#mytextboxID").dateDiff("unit", date);
          
          **  note the jQuery .dateDiff() does not return a
              jQuery object.
          
    DATE ADD
    ========
      This function adds a specified number of units to a date, to derive 
      a new date.  (i.e. add 3 weeks to the current date, what is the date 45 
      days after chirstmas, etc.)
    
      Units:  ms  - milliseconds      d - days
              s   - seconds           w - weeks
              i   - minutes           y - years
              h   - hours
      
      Usage:  
        standard Javascript
          var d = o2s.dateAdd("unit",qty, date);
          
        jQuery
          var d = $("#mytextboxID").dateAdd("unit", qty);
    
              **  note the jQuery .dateAdd() does not return a
              jQuery object.

        
*/

o2sCalObject = null;    //track which calendar item is to be displayed
o2sCalShowTime = false; //indicate if the time part of the popup should be visible.

var o2s = {
    //********************************
    //Configuration Variables
    //********************************
    popupTextLastMonth  :   "&lt;&lt;",
    popupTextLastYear   :   "&nbsp;&lt;&nbsp;",
    popupTextNextMonth  :   "&gt;&gt;",
    popupTextNextYear   :   "&nbsp;&gt;&nbsp;",
    popupTextToday      :   "Today",
    
    //Calendar CSS classes
    classCal            :   "o2sCalTable",           //The calendar table
    classCalHeader      :   "o2sCalHeader",          //The day name header row
    classCalHeaderDay   :   "o2sCalHeaderDay",      //The day names themselves
    classCalDay         :   "o2sCalDay",             //A regular day in the calendar
    classCalDayTarget   :   "o2sCalDayTarget",      //The target/default date
    classCalHover       :   "o2sCalHover",            //the class when a day is being hovered over.
    
    //********************************
    // Date collections and values
    // arrays of date related items.
    //********************************
    dayletter     : [ "S", "M", "T", "W", "T", "F", "S" ],
    dayshort      : [ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" ],
    daylong       : [ "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" ],
    monthshort    : ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" ],
    monthlong     : ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
    monthdays     : [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31],
    millisecond   : 1,
    second        : 1000,                 //1000 milliseconds
    minute        : 1000 * 60,     // 60 seconds
    hour          : 1000 * 60 * 60,     // 60 minutes
    day           : 1000 * 60 * 60 * 24,       // 24 hours
    week          : 1000 * 60 * 60 * 24 * 7,          // 7 days
    year          : 1000 * 60 * 60 * 24 * 365,        // 365 days
    
    
    //the common formats are used to aide in parsing a date string when a format is not known.
    //NOTE: One date string may match more than one format.  If abnormal behavior is observed, specify a format when using the parseDate() method,
    //      or using an function that relies on it (such as the date picker).
    commonformats : [
                      //little endian
                      "d/m/yyyy", "dd/mm/yyyy", "dd-mm-yyyy", "dd-mm-yy", "d mmmm yyyy", "dd mmmm yyyy", "dd mmmm yy", "d mmmm yy", 
                      "dd.mm.yyyy", "d.m.yyyy", "d.m.y", "d. mmmm yy", "d. mmmm yyyy",
                      "d mmm yyyy", "dd mmm yyyy", "d mmm yy", "dd mmm yy",
                      
                      //big endian
                      "yyyy mmmm dd", "yy mmmm dd", "yyyy mmmm d", "yy mmmm d", "yyyy-mm-dd", "yyyy.mm.dd",
                      
                      //middle endian
                      "mmmm d, yyyy", "mmmm dd, yyyy", "mmmm d, yy", "mmmm dd, yy", "mmm d, yy", "mmm d, yyyy", "mmm dd, yy",
                      "mmm. d, yyyy", "mmm. dd, yyyy", "mmm. d, yy", "mmm. dd, yy", "mm/dd/yyyy", "mm-dd-yyyy", "mm.dd.yy",
                      "m/d/yy", "mm/dd/yy", "mm/dd/yyyy",
                      
                      //weekday names
                      "ddd, dd mmm yyyy",
                      "ddd mmm dd yyyy HH:ii:ss",   //layout from javascript date().toString()
                      
                      //julian date
                      "yyyy-j", "yyyy-jjj", "yyyyjjjj", "yyyyj"
                    ],
                      
                      
    //********************************
    //Misc utility methods
    //********************************
    
    // ***********************************************
    //   PAD
    //  
    //  zero pads a number if it is less than 10.
    //    Usage:  o2s.padd(number);
    //
    //  Returns a string
    // ***********************************************
    pad       :   function (x) { return (x<0||x>9?"":"0") + x; },
    
    // ***********************************************
    //   GET TOKENS
    //  
    //  Finds similar, consecutive items in a string.
    //  (i.e. "aaaabbbbcccc" will return an array with 3 elements - "aaaa", "bbbb", "cccc")
    //  This function is used internally by the format and parseDate routines.
    //    Usage:  o2s.getTokens("string");
    //
    //  Returns an array of strings.
    // ***********************************************
    getTokens :   function (str) {
        var tokens = [];
        var curtoken = "";
        var curchar = "";
        var lastchar = "";
        var idx = 0;
        
        while (idx < str.length) {
          curchar = str.charAt(idx);
          curtoken = "";
          while (str.charAt(idx) == curchar && idx < str.length) {
            curtoken += str.charAt(idx++);
          };
          tokens.push(curtoken);
        };
        return tokens;
    },
    
    //********************************
    // DATE UTILITIES
    //********************************
    
    // ***********************************************
    //   DATE DIFFERENCE
    //  
    //  Finds the difference between two dates, in a specified unit.
    //    Usage:  o2s.dateDiff("unit",startdate, enddate);
    //
    //    Units:  ms  - milliseconds      d - days
    //            s   - seconds           w - weeks
    //            i   - minutes           y - years
    //            h   - hours
    //
    //  Returns a numeric value, or null if the process cannot complete
    // ***********************************************
    dateDiff : function (unit, start, end) {
      start = this.parseDate(start);
      end = this.parseDate(end);
    
      if (!start || !end) { return null; }
      
      var diff = end.getTime() - start.getTime();
      switch (unit.toLowerCase()) {
        case "ms"     : { diff = diff; break; };
        case "s"      : { diff = diff / this.second; break; };
        case "i"      : { diff = diff / this.minute; break; };
        case "h"      : { diff = diff / this.hour; break; };
        case "d"      : { diff = diff / this.day; break; };
        case "w"      : { diff = diff / this.week; break; };
        case "y"      : { diff = diff / this.year; break; };
      };
      return diff;
    },
    
    // ***********************************************
    //   DATE ADD
    //  
    //  Adds a specified amout of the specified unit to a date.
    //    Usage:  o2s.dateAdd("unit", amount, base_date);
    //
    //    Units:  ms  - milliseconds      d - days
    //            s   - seconds           w - weeks
    //            i   - minutes           y - years
    //            h   - hours
    //
    //  Returns a new date object.
    // ***********************************************
    dateAdd   : function (unit, amt, date) {
      var diff = 0;
      switch (unit.toLowerCase()) {
        case "ms"   : { diff = amt * this.millisecond; break;};
        case "s"    : { diff = amt * this.second; break;};
        case "i"    : { diff = amt * this.minute; break;};
        case "h"    : { diff = amt * this.hour; break;};
        case "d"    : { diff = amt * this.day; break;};
        case "w"    : { diff = amt * this.week; break;};
        case "y"    : { diff = amt * this.year; break;};
        default     : { return 0; };
      };
      var date = o2s.parseDate(date);
      var mills = (date) ? date.getTime() + diff: diff;
      var nd = new Date();
      nd.setTime(mills);
      return nd;
    },
    
    // ***********************************************
    //  Formatting Functions
    //  
    //  These functions return a string with the corresponding date element.
    //  These are internal functions
    //
    //  Returns a string
    // ***********************************************
    d         :   function (date) { return date.getDate().toString(); },
    dd        :   function (date) { return this.pad(this.d(date)); },
    ddd       :   function (date) { return this.dayshort[date.getDay()]; },
    dddd      :   function (date) { return this.daylong[date.getDay()]; },
    j         :   function (date) { var y = date.getFullYear(); var ds = new Date(y, 0, 0); return Math.round(this.dateDiff("d", ds, date))  ; },   //jullian date
    m         :   function (date) { return date.getMonth() + 1; },
    mm        :   function (date) { return this.pad(date.getMonth() + 1); },
    mmm       :   function (date) { return this.monthshort[date.getMonth()]; },
    mmmm      :   function (date) { return this.monthlong[date.getMonth()]; },
    y         :   function (date) { return parseInt(date.getFullYear().toString().substr(2)); },
    yy        :   function (date) { return this.pad(this.y(date)); },
    yyyy      :   function (date) { return date.getFullYear(); },
    h         :   function (date) { return (date.getHours()>12?date.getHours()-12:date.getHours()); },
    hh        :   function (date) { return this.pad(this.h(date)); },
    H         :   function (date) { return date.getHours(); },
    HH        :   function (date) { return this.pad(this.H(date)); },
    i         :   function (date) { return date.getMinutes(); },
    ii        :   function (date) { return this.pad(this.i(date)); },
    s         :   function (date) { return date.getSeconds(); },
    ss        :   function (date) { return this.pad(this.s(date)); },
    a         :   function (date) { return (this.H(date)<12?"am":"pm"); },
    A         :   function (date) { return (this.H(date)<12?"AM":"PM"); },
    O         :   function (date) { var tz = (date.getTimezoneOffset() /60) * -1; return (tz<0?tz:"+"+tz); },
    short     :   function (date) { return this.m(date) + "/" + this.d(date) + "/" + this.yy(date); },
    medium    :   function (date) { return this.mmm(date) + " " + this.d(date) + ", " + this.yyyy(date); },
    long      :   function (date) { return this.mmmm(date) + " " + this.d(date) + ", " + this.yyyy(date); },
    shorttime :   function (date) { return this.short(date) + " " + this.HH(date) + ":" + this.ii(date); },
    mediumtime:   function (date) { return this.medium(date) + " " + this.HH(date) + ":" + this.ii(date); },
    longtime  :   function (date) { return this.long(date) + " " + this.hh(date) + ":" + this.ii(date) + ":" + this.ss(date) + " " + this.A(date); },
    iso8610   :   function (date) { return this.yyyy(date) + "-" + this.mm(date) + "-" + this.dd(date) + "T" + this.HH(date) + this.ii(date) + ":" + this.ss(date) + this.O(date); },
    yyyymmdd  :   function (date) { return this.yyyy(date) + "-" + this.mm(date) + "-" + this.dd(date); },
    
    
    // ***********************************************
    //   FORMAT
    //  
    //  Formats a date to an arbitrary format.
    //    Usage:  o2s.format(base_date, formatstring);
    //
    //    Example: o2s.format(new Date("1 Jan 2007", "dd.mmm.yyyy [HH:ii:ss]"));
    //                =>  "01.Jan.2007 [00:00:00]"
    //
    //    Any character in the string not in the following list is treated as a literal and placed into the output string directly.
    //    Available formatting options:
    //
    //      d     - day of month (not padded)       h     - hour, 12 hour clock (not padded)
    //      dd    - day of month (zero padded)      hh    - hour, 12 hour clock (zero padded)
    //      ddd   - day name, abbreviated           H     - hour, 24 hour clock (not padded)
    //      dddd  - day name, full                  HH    - hour, 24 hour clock (zero padded)
    //      m     - month number (not padded)       i     - minutes (not padded)
    //      mm    - month number (zero padded)      ii    - minutes (zero padded)
    //      mmm   - month name, abbreviated         s     - seconds (not padded)
    //      mmmm  - month name, full                ss    - seconds (zero padded)
    //      y     - two digit year (not padded)     a     - am / pm (lowercase)
    //      yy    - two digit year (zero padded)    A     - AM / PM (uppercase)
    //      yyyy  - four digit year                 O     - timze zone offset from GMT.  (that is a letter OH, not number zero)
    //      j     - ordinal day/julian date
    //      jjj   - ordinal day/julian date (same as "j")
    //      
    //    prebuilt formats
    //      short       - "m/d/yy"              
    //      medium      - "mmm d yyyy"          
    //      long        - "mmmm d yyyy"         
    //      shorttime   - "m/d/yy HH:ii"        
    //      mediumtime  - "mmm d yyyy HH:ii"    
    //      longtime    - "mmmm d yyyy hh:ii:ss A O"
    //      iso8610     - "yyyy-mm-ddTHH:ii:ss O"
    //      yyyymmdd    - "yyyy-mm-dd"
    //
    //  Returns a string
    // ***********************************************
    //format a date to an arbtrary format
    format    :   function (date, format) {
      if (!format) { format = "medium"; };   //make sure we have a format
      var output = "";

      if (!o2s.isDate(date)) { return date; };   //exit leaving the date item unchanged
      
      //start with the named formats
      switch (format) {
        case "short"      : { output += this.short(date); break;};
        case "medium"     : { output += this.medium(date); break;};
        case "long"       : { output += this.long(date); break;};
        case "shorttime"  : { output += this.shorttime(date); break;};
        case "mediumtime" : { output += this.mediumtime(date); break;};
        case "longtime"   : { output += this.longtime(date); break;};
        case "iso8610"    : { output += this.iso8610(date); break;};
        case "yyyymmdd"   : { output += this.yyyymmdd(date); break;};
        default           : {
          var tokens = this.getTokens(format);

          for (var x=0; x < tokens.length; x++) {
            var part = "";
            switch (tokens[x]) {
              case "d"          : { part += this.d(date); break;};
              case "dd"         : { part += this.dd(date); break;};
              case "ddd"        : { part += this.ddd(date); break;};
              case "dddd"       : { part += this.dddd(date); break;};
              case "m"          : { part += this.m(date); break;};
              case "mm"         : { part += this.mm(date); break;};
              case "mmm"        : { part += this.mmm(date); break;};
              case "mmmm"       : { part += this.mmmm(date); break;};
              case "y"          : { part += this.y(date); break;};
              case "yy"         : { part += this.yy(date); break;};
              case "yyyy"       : { part += this.yyyy(date); break;};
              case "h"          : { part += this.h(date); break;};
              case "hh"         : { part += this.hh(date); break;};
              case "H"          : { part += this.H(date); break;};
              case "HH"         : { part += this.H(date); break;};
              case "i"          : { part += this.i(date); break;};
              case "ii"         : { part += this.ii(date); break;};
              case "s"          : { part += this.s(date); break;};
              case "ss"         : { part += this.ss(date); break;};
              case "a"          : { part += this.a(date); break;};
              case "A"          : { part += this.A(date); break;};
              case "O"          : { part += this.O(date); break;};
              case "j": case "jjj": { part += this.j(date); break; };
              default           : { part += tokens[x]; break; };
        
            };
            output += part;
//            alert("token" + tokens[x] + "\noutput: " + output + "\npart: " + part);

          };
          break;
        }
      };
      return output;
    },
    
    // ***********************************************
    //  PARSE DATE
    //  
    //  This function attempts to get a date object from a string in an arbitrary format.
    //    Usage:  o2s.parseDate(datestring, formatstring);
    //  
    //    If the format string is omitted, the routine will attempt to find a date using
    //    common formats (in the commonformats[] array.
    //
    //    Format string takes the same arguments as the format routine above.
    //
    //    NOTE: a date string could match more than one format.  In this case, the first format
    //          found that works will be used.  If desired behaviour is not happening, 
    //          specify a format string to force the desired format.
    //
    //  Returns a date object if successful.  Otherwise returns null.
    // ***********************************************
    // Parse a date string if possible.
    // Based on Matt Kruse's parseString function in his date libarary:
    // http://www.mattkruse.com/javascript/date/
    // ***********************************************
    parseDate : function (val, format) {
      if (!val) { return null; }
      val = val.toString();
      
      //if format is not specified, create an array of general formats to try
      if (!format) {
        for (var i=0; i < this.commonformats.length; i++) {
          var d = this.parseDate(val, this.commonformats[i]);
          if (d != null) { 
            return d; 
          };
        };
        return null;
      };
      
      //subfunction to make sure a given value is an integer
      this.isInteger = function(val) {
        for (var i=0; i < val.length; i++) {
          if ("1234567890".indexOf(val.charAt(i))==-1) { 
            return false; 
          };
        };
        return true;
      };
      
      //subfunction to find a subset of the value, based on a format's limits (i.e. day of month cannot be more than two chars)
      this.getInt = function(str,i,minlength,maxlength) {
        for (var x=maxlength; x>=minlength; x--) {
          var token=str.substring(i,i+x);
          if (token.length < minlength) { 
            return null; 
          };
          if (this.isInteger(token)) { 
            return token; 
          };
        };
        return null;
      };
      
      //variables we'll need:
      var day = 0;
      var month = 0;
      var year = 0;
      var hour = 0;
      var minute = 0;
      var second = 0;
      var ampm;
      var min, max;
      var jullian = false;      //cheap out for the finaly touch ups.
      
      //tokenize the format so we can use it later:
      var tokens = this.getTokens(format);

      //check each token and get the corrsponding value, if possible
      idx=0;
      for (var x = 0; x < tokens.length; x++) {
        token = tokens[x];
        switch (token) {
          //numeric day
          case "d": case "dd": {
            day = this.getInt(val, idx, token.length, 2);
            
            if (day < 1 || day > 31 || day == null) { return null; }
            idx += day.length;
            break;
          };
          //text day
          case "ddd": case "dddd": {
            var names = (token == "ddd" ? this.dayshort : this.daylong);
            for (var check=0; check < names.length; check++) {
              if (val.substr(idx, names[check].length).toLowerCase() == names[check].toLowerCase()) {
                day = check;
                idx += names[check].length;
                break;
              };
            }; 
            break;
          };
          
          //jullian/ordinal day
          case "j": case "jjj": {
            var day = this.getInt(val, idx, token.length, 3);
            if (day < 1 || day > 366 || day == null) { return null; };
            idx += day.length;
            jullian = true;
            break;
          };
          
          //numeric month
          case "m": case "mm": {
            month = this.getInt(val, idx, token.length, 2);
            if (month < 1 || month > 12 || month == null) { return null; };
            idx += month.length;
            month = parseInt(month) - 1;
            break;
          };
          
          //text day
          case "mmm": case "mmmm": {
            var names = (token == "mmm") ? this.monthshort : this.monthlong;
            for (var check=0; check < names.length; check++) {
              if (val.substr(idx, names[check].length).toLowerCase() == names[check].toLowerCase()) {
                month = check;
                idx += names[check].length;
                break;
              };
            };
            break;
          };
          
          //years
          case "yyyy": case "yy": case "y": {
            max = 4;
            min = token.length;
            if (token == "y") { min = 2; max = 4; }
            year = this.getInt(val, idx, min, max);
            if (year == null) { return null; }
            idx += year.length;
            if (year.length == 2) {
              if (year > 70) { year = 1900 + parseInt(year); } else { year = 2000 + parseInt(year); }
            };
            break;
          };
          
          //hours
          case "HH": case "H": case "hh": case "h": {
            hour = this.getInt(val, idx, token.length, 2);
            var maxhour = 12;
            if (token == "HH" || token == "H") { maxhour = 23; };
            if (hour < 0 || hour > maxhour || hour == null) { return null; };
            idx += hour.length;
            break;
          };
          
          //minutes and seconds
          case "i": case "ii": case "s": case "ss": {
            minute = this.getInt(val, idx, token.length, 2);
            if (minute < 0 || minute > 59 || minute == null) { return null; };
            idx += minute.length;
            break;
          };
          
          //AM or PM
          case "a": case "A": {
            var ampm = val.substr(idx, 2).toUpperCase();
            if (ampm == "AM" || ampm == "PM") {
              idx += ampm.length;
            }
            else { 
              return null;
            };
            break;
          }
          
          default: {
            var check = val.substr(idx, token.length);
            if (check != token) { return null; }
            idx += token.length;
          };
        };
      };
    
      //final checks.
      // 1. make sure the day matches the number of days in the month
      if (!jullian) {
        if (month == 2) {
          if ( ( (year%4==0) && (year%100 != 0) ) || (year%400==0) ) { // leap year
            if (day > 29) { return null; };
          };
        }
        else {
          if (day < 1 || day > this.monthdays[month]) { return null; };
        };
      };
      // 2. make sure the hour reflects the am/pm setting
      if (parseInt(hour) < 12 && ampm == "PM") {
        hour = parseInt(hour) + 12;
      }
      else if (parseInt(hour) > 11 && ampm == "AM") {
        hour = parseInt(hour) - 12;
      };
      return new Date(year, month, day, hour, minute, second);
    },

    // ***********************************************
    //   IS DATE
    //  
    //  this method indicats if the item is a valid date.
    //    usage:    o2s.isDate(datestring, format);
    //
    //          datestring can be a date object, or a text string.
    //          format is optional, but can be used to explicitly indicate the desired format.
      //          See the format method above for formatting options.
    //
    //  Returns a true or false value
    // ***********************************************
    isDate : function (date, format) {
      d = o2s.parseDate(date, format);
      if (d != null) { return true; } else { return false; };
    },


    // ***********************************************
    //   DATE PICKER CALENDAR
    //  
    //  this method creates the calendar for the date picker.
    //  It should not be called directly.  Instead the showPopup() method should be used.
    //
    //  Returns a string
    // ***********************************************
    //  based on code found at http://jszen.blogspot.com/2007/03/how-to-build-simple-calendar-with.html
    // ***********************************************
    calendar : function (data) {
      showDate = ((!data || data.date == null) ? new Date() : data.date);
      //showDate is to be a date object.
      var showDate = (isNaN(showDate) || showDate == null) ? new Date() : showDate;
      var showYear = showDate.getFullYear();
      var showMonth = showDate.getMonth();
      var firstday = new Date(showYear, showMonth, 1).getDay();      
      var monthLength = this.monthdays[showMonth];
      //check for leapyear
      if (showMonth == 1) { // February only!
        if ((showYear % 4 == 0 && showYear % 100 != 0) || showYear % 400 == 0){
          monthLength = 29;
        };
      };
      
      var html = "<table class=\"" + this.classCal + "\" id=\"" + this.medium( showDate)+ "\">";
      html += "<tr>";
      html += "<th>&nbsp;</th>";
      html += "<th colspan=\"5\">";
      html += this.monthshort[showMonth] + "&nbsp;" + showYear;
      html += "</th>";
      html += "<th><a href=\"#\" id=\"o2sCalClose\">X</a></th>";
      html += "</tr>";
      html += "<tr>";
      html += "<td><a id=\"o2sCalPriorYear\" href=\"#\">" + this.popupTextLastYear + "</a></td>";
      html += "<td><a id=\"o2sCalPriorMonth\" href=\"#\">" + this.popupTextLastMonth + "</a></td>";
      html += "<td colspan=\"3\" style=\"text-align: center;\"><a href=\"#\" id=\"o2sCalToday\">" + this.popupTextToday + "</a></td>";
      html += "<td style=\"text-align: right;\"><a id=\"o2sCalNextMonth\" href=\"#\">" + this.popupTextNextMonth + "</a></td>";
      html += "<td style=\"text-align: right;\"><a id=\"o2sCalNextYear\" href=\"#\">" + this.popupTextNextYear + "</a></td>";
      html += "</tr>";
      html += "<tr class=\"" + this.classCalHeader + "\">";
      for (var i=0; i < this.dayshort.length; i++) {
        html += "<td class=\"" + this.classCalHeaderDay + "\">";
        html += this.dayletter[i];
        html += "</td>";
      };
      html += "</tr>";
      var curday = 1;
      while (curday <= monthLength) {
        html += "<tr>";
        for (var i=0; i < 7; i++) {
          if ((curday <= 1 && i < firstday) || curday > monthLength) {
            html += "<td class=\"" + this.classCalDay + "\" id=\"\">";
            html += "&nbsp;";
          }
          else {
            var dayclass = ((curday == showDate.getDate()) ? this.classCalDayTarget : this.classCalDay);
            html += "<td class=\"" + dayclass + "\" id=\"" + this.medium( new Date(showYear, showMonth, curday) ) + "\">";
            html += curday;
            curday++;
          };
          html += "</td>";
        };
        html += "</tr>";
      };
      if (o2sCalShowTime) {
        html += "<tr>"
        html += "<td>&nbsp;</td>";
        html += "<td colspan=\"2\" style=\"text-align:right;\"><select id=\"o2sCalTimeHour\">";
        for (var i=0; i < 24; i++) { 
          html += "<option value=\"" + this.pad(i) + "\">" + this.pad(i) + "</option>";
        };
        html += "</select> : </td>";
        html += "<td colspan=\"2\"><select id=\"o2sCalTimeHour\">";
        for (var i=0; i < 60; i++) { 
          html += "<option value=\"" + this.pad(i) + "\">" + this.pad(i) + "</option>";
        };
        html += "</select></td>";
        html += "<td>&nbsp;</td>";
        html += "</tr>";
      };
      html += "</table>";
      
      return html;
    },
    
    // ***********************************************
    //   CALENDAR CLOSE
    //  
    //  this method handles when the calendar should be closed.
    //  It should not be called directly.
    //
    // ***********************************************
    calendarClose : function () {
      if (o2sCalObject && $("#o2sCalendar").css("display") == "block") {
        $("#o2sCalendar").slideUp("fast", function () { $("#o2sCalendar").remove(); });
        o2sCalObject = false;
        return false;
      };
    },
    
    // ***********************************************
    //   CALENDAR EVENTS
    //  
    //  this method sets up the event handlers for the date picker calendar
    //  It should not be called directly.  
    //
    // ***********************************************
    calendarEvents : function (data) {
      if (!data) { data = {}; };
      if (!data.format) { data.format = "medium"; };
    
      //Assign event handlers
      //the close link
      $("#o2sCalClose").click(o2s.calendarClose);
      
      //handle if a click happens outside the calendar
//unstable
//      $(":not(#o2sCalendar)").click(o2s.calendarClose);

      //hover effects
      $("." + this.classCalDay).hover(
          function () {$(this).addClass(o2s.classCalHover); },
          function () { $(this).removeClass(o2s.classCalHover); }
      );
      

      //handle when day is clicked
      $("." + this.classCalDay).click( function() {
        if ($.trim($(this).text()).length > 0) {
          var d = new Date($(this).attr("id"));
          $(o2sCalObject).val(o2s.format(d, data.format));
          o2s.calendarClose();    //close the calendar
          return false;
        };
      });
      $("." + this.classCalDayTarget).click( function() {
        if ($.trim($(this).text()).length > 0) {
          var d = new Date($(this).attr("id"));
          $(o2sCalObject).val(o2s.format(d));
          $("#o2sCalClose").click();    //close the calendar
          return false;
        };
      });
      
      //Handle the year/month/today navigation links
      $("#o2sCalPriorYear").click( function () {
        var d = new Date ( $("." + o2s.classCal).attr("id") );
        d.setYear(d.getFullYear() - 1);
        $("#o2sCalendar").empty().append(o2s.calendar({date: d})); 
        o2s.calendarEvents(data);
        return false;
      });
      $("#o2sCalPriorMonth").click( function () { 
        var d = new Date ( $("." + o2s.classCal).attr("id") );
        d.setMonth(d.getMonth() - 1);
        $("#o2sCalendar").empty().append(o2s.calendar({date: d})); 
        o2s.calendarEvents(data);
        return false;
      });
      $("#o2sCalNextYear").click( function () {
        var d = new Date ( $("." + o2s.classCal).attr("id") );
        d.setYear(d.getFullYear() + 1);
        $("#o2sCalendar").empty().append(o2s.calendar({date: d})); 
        o2s.calendarEvents(data);
        return false;
      });
      $("#o2sCalNextMonth").click( function () { 
        var d = new Date ( $("." + o2s.classCal).attr("id") );
        d.setMonth(d.getMonth() + 1);
        $("#o2sCalendar").empty().append(o2s.calendar({date: d})); 
        o2s.calendarEvents(data);
        return false;
      });
      $("#o2sCalToday").click( function () {
        $("#o2sCalendar").empty().append(o2s.calendar({date: new Date()})); 
        o2s.calendarEvents(data);
        return false;
      });
    
    },
    
    // ***********************************************
    //   SHOW POPUP
    //  
    //  this method causes the date picker to become visible, and positioned properly.
    //    usage:    o2s.showPopup{options, object};
    //
    //      - object is the objec the date picker should be associated with and feed it's resulting date to.
    //      - options is an object that helps define how the date picker functions:
    //          options.date    - the default date to set as the current date in the calendar.
    //          options.focus   - true/false value - if true, the calendar will open when the object receives focus.
    //                            Otherwise, it will open when the object is clicked.
    //          options.parent  - The object to set the date value into.
    //                            Useful when the object to initiate the date picker is not a text field.
    //                            (i.e. activation icon, button, text, link, etc.)
    //
    // ***********************************************
    showPopup : function(data, obj) {
      if (!data) { data = {}; };
      if (data && data.parent) { obj = data.parent; };   //see if we need to override the object
    
      //abort if we are already showing the popup for the requested object
      if (obj == o2sCalObject) { return; };

      //now set the o2sCalObject  (global object to help with position/values)
      o2sCalObject = obj;
      
      var d = this.parseDate($(obj).val());
      if (d) { data.date = d; }
      $("body").append("<div id=\"o2sCalendar\" style=\"display: none;\">" + this.calendar(data) + "</div>");
      //make sure calendar is not already visible
      if ($("#o2sCalendar").css("display") == "block") { $("#o2sCalendar").hide(); }
      
      //position the calendar under the element in question
      $("#o2sCalendar").css({position: "absolute", top: obj.offsetTop + obj.offsetHeight, left: obj.offsetLeft });
      
      //show it
      $("#o2sCalendar").slideToggle("fast", function () {
        if ($(this).css("display") == "none") { $(this).remove(); }
      });
      
      //apply the event handlers
      this.calendarEvents(data);
    }

};


//*************************************
// jQuery wrapper functions to make life easy
//*************************************
jQuery.fn.datePicker = function (opts) {
  return this.each( function () {
      $(this).focus( function () { o2s.showPopup(opts,this); } );
  });  
};

jQuery.fn.dateDiff = function (unit, target) {
    var d = o2s.dateDiff(unit, $(this).val(), target);
    return d;    
};

jQuery.fn.dateAdd = function (unit, amt) {
    var d = o2s.dateAdd(unit, amt, $(this).val());
    return d;
};

jQuery.fn.dateFormat = function (format) {
  return this.each( function () {
    var d = o2s.parseDate($(this).val());
    $(this).val(o2s.format(d, format));
  });
};

jQuery.fn.parseDate = function (format) {
    var d = o2s.parseDate($(this).val());
    return d
};

jQuery.fn.isDate = function (format) {
    return o2s.isDate($(this).val(), format);
};











/*
 * jQuery Tooltip plugin 1.2
 *
 * http://bassistance.de/jquery-plugins/jquery-plugin-tooltip/
 * http://docs.jquery.com/Plugins/Tooltip
 *
 * Copyright (c) 2006 - 2008 Jörn Zaefferer
 *
 * $Id: jquery.tooltip.js 4569 2008-01-31 19:36:35Z joern.zaefferer $
 * 
 * Dual licensed under the MIT and GPL licenses:
 *   http://www.opensource.org/licenses/mit-license.php
 *   http://www.gnu.org/licenses/gpl.html
 */
 
;(function($) {
	
		// the tooltip element
	var helper = {},
		// the current tooltipped element
		current,
		// the title of the current element, used for restoring
		title,
		// timeout id for delayed tooltips
		tID,
		// IE 5.5 or 6
		IE = $.browser.msie && /MSIE\s(5\.5|6\.)/.test(navigator.userAgent),
		// flag for mouse tracking
		track = false;
	
	$.tooltip = {
		blocked: false,
		defaults: {
			delay: 200,
			showURL: true,
			extraClass: "",
			top: 15,
			left: 15,
			id: "tooltip"
		},
		block: function() {
			$.tooltip.blocked = !$.tooltip.blocked;
		}
	};
	
	$.fn.extend({
		tooltip: function(settings) {
			settings = $.extend({}, $.tooltip.defaults, settings);
			createHelper(settings);
			return this.each(function() {
					$.data(this, "tooltip-settings", settings);
					// copy tooltip into its own expando and remove the title
					this.tooltipText = this.title;
					$(this).removeAttr("title");
					// also remove alt attribute to prevent default tooltip in IE
					this.alt = "";
				})
				.hover(save, hide)
				.click(hide);
		},
		fixPNG: IE ? function() {
			return this.each(function () {
				var image = $(this).css('backgroundImage');
				if (image.match(/^url\(["']?(.*\.png)["']?\)$/i)) {
					image = RegExp.$1;
					$(this).css({
						'backgroundImage': 'none',
						'filter': "progid:DXImageTransform.Microsoft.AlphaImageLoader(enabled=true, sizingMethod=crop, src='" + image + "')"
					}).each(function () {
						var position = $(this).css('position');
						if (position != 'absolute' && position != 'relative')
							$(this).css('position', 'relative');
					});
				}
			});
		} : function() { return this; },
		unfixPNG: IE ? function() {
			return this.each(function () {
				$(this).css({'filter': '', backgroundImage: ''});
			});
		} : function() { return this; },
		hideWhenEmpty: function() {
			return this.each(function() {
				$(this)[ $(this).html() ? "show" : "hide" ]();
			});
		},
		url: function() {
			return this.attr('href') || this.attr('src');
		}
	});
	
	function createHelper(settings) {
		// there can be only one tooltip helper
		if( helper.parent )
			return;
		// create the helper, h3 for title, div for url
		helper.parent = $('<div id="' + settings.id + '"><h3></h3><div class="body"></div><div class="url"></div></div>')
			// add to document
			.appendTo(document.body)
			// hide it at first
			.hide();
			
		// apply bgiframe if available
		if ( $.fn.bgiframe )
			helper.parent.bgiframe();
		
		// save references to title and url elements
		helper.title = $('h3', helper.parent);
		helper.body = $('div.body', helper.parent);
		helper.url = $('div.url', helper.parent);
	}
	
	function settings(element) {
		return $.data(element, "tooltip-settings");
	}
	
	// main event handler to start showing tooltips
	function handle(event) {
		// show helper, either with timeout or on instant
		if( settings(this).delay )
			tID = setTimeout(show, settings(this).delay);
		else
			show();
		
		// if selected, update the helper position when the mouse moves
		track = !!settings(this).track;
		$(document.body).bind('mousemove', update);
			
		// update at least once
		update(event);
	}
	
	// save elements title before the tooltip is displayed
	function save() {
		// if this is the current source, or it has no title (occurs with click event), stop
		if ( $.tooltip.blocked || this == current || (!this.tooltipText && !settings(this).bodyHandler) )
			return;

		// save current
		current = this;
		title = this.tooltipText;
		
		if ( settings(this).bodyHandler ) {
			helper.title.hide();
			var bodyContent = settings(this).bodyHandler.call(this);
			if (bodyContent.nodeType || bodyContent.jquery) {
				helper.body.empty().append(bodyContent)
			} else {
				helper.body.html( bodyContent );
			}
			helper.body.show();
		} else if ( settings(this).showBody ) {
			var parts = title.split(settings(this).showBody);
			helper.title.html(parts.shift()).show();
			helper.body.empty();
			for(var i = 0, part; part = parts[i]; i++) {
				if(i > 0)
					helper.body.append("<br/>");
				helper.body.append(part);
			}
			helper.body.hideWhenEmpty();
		} else {
			helper.title.html(title).show();
			helper.body.hide();
		}
		
		// if element has href or src, add and show it, otherwise hide it
		if( settings(this).showURL && $(this).url() )
			helper.url.html( $(this).url().replace('http://', '') ).show();
		else 
			helper.url.hide();
		
		// add an optional class for this tip
		helper.parent.addClass(settings(this).extraClass);

		// fix PNG background for IE
		if (settings(this).fixPNG )
			helper.parent.fixPNG();
			
		handle.apply(this, arguments);
	}
	
	// delete timeout and show helper
	function show() {
		tID = null;
		helper.parent.show();
		update();
	}
	
	/**
	 * callback for mousemove
	 * updates the helper position
	 * removes itself when no current element
	 */
	function update(event)	{
		if($.tooltip.blocked)
			return;
		
		// stop updating when tracking is disabled and the tooltip is visible
		if ( !track && helper.parent.is(":visible")) {
			$(document.body).unbind('mousemove', update)
		}
		
		// if no current element is available, remove this listener
		if( current == null ) {
			$(document.body).unbind('mousemove', update);
			return;	
		}
		
		// remove position helper classes
		helper.parent.removeClass("viewport-right").removeClass("viewport-bottom");
		
		var left = helper.parent[0].offsetLeft;
		var top = helper.parent[0].offsetTop;
		if(event) {
			// position the helper 15 pixel to bottom right, starting from mouse position
			left = event.pageX + settings(current).left;
			top = event.pageY + settings(current).top;
			helper.parent.css({
				left: left + 'px',
				top: top + 'px'
			});
		}
		
		var v = viewport(),
			h = helper.parent[0];
		// check horizontal position
		if(v.x + v.cx < h.offsetLeft + h.offsetWidth) {
			left -= h.offsetWidth + 20 + settings(current).left;
			helper.parent.css({left: left + 'px'}).addClass("viewport-right");
		}
		// check vertical position
		if(v.y + v.cy < h.offsetTop + h.offsetHeight) {
			top -= h.offsetHeight + 20 + settings(current).top;
			helper.parent.css({top: top + 'px'}).addClass("viewport-bottom");
		}
	}
	
	function viewport() {
		return {
			x: $(window).scrollLeft(),
			y: $(window).scrollTop(),
			cx: $(window).width(),
			cy: $(window).height()
		};
	}
	
	// hide helper and restore added classes and the title
	function hide(event) {
		if($.tooltip.blocked)
			return;
		// clear timeout if possible
		if(tID)
			clearTimeout(tID);
		// no more current element
		current = null;
		
		helper.parent.hide().removeClass( settings(this).extraClass );
		
		if( settings(this).fixPNG )
			helper.parent.unfixPNG();
	}
	
	$.fn.Tooltip = $.fn.tooltip;
	
})(jQuery);
/* ================================================================== *\
   (C) Copyright 2005 by Secure Data Software, Inc.
   This file is part of Andromeda
   
   Andromeda is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.

   Andromeda is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with Andromeda; if not, write to the Free Software
   Foundation, Inc., 51 Franklin St, Fifth Floor,
   Boston, MA  02110-1301  USA 
   or visit http://www.gnu.org/licenses/gpl.html
\* ================================================================== */

/* ---------------------------------------------------- *\
   STRUCTURE OF THIS FILE
   
   This file contains the oldest javascript in 
   Andromeda.  Some code is required for b/w 
   compatibility, and other code was used only
   once or twice and is now dead.  
   
   Generally, anything that is documented with ROBODOC
   blocks is part of the permanent API, and anything
   not commented is deprecated and should not be
   used.
      
\* ---------------------------------------------------- */
      

/* ---------------------------------------------------- *\
   SECTION 1: X2 COMPATIBILITY.  
   
   Not deprecated outright, but discouraged. You
   should be coding x4 pages which have much 
   nicer functions for everything these functions do.
\* ---------------------------------------------------- */
function SetAndPost(varname,varvalue) {
	ob(varname).value = varvalue;
	formSubmit();
}
function SaveAndPost(varname,varvalue) {
   ob('gp_save').value=1;
	ob(varname).value = varvalue;
	formSubmit();
}
var ajaxTM=0;
function formPostAjax(stringparms) {
   var ajaxTMold=ajaxTM;
   ajaxTM=1;
   formPostString(stringparms);
   ajaxTM=ajaxTMold;
}
function formPostString(stringparms) {
   // Modified KFD 5/30/07 to switch to AJAX calls.  We now pass
   //   the string parms to formSubmit, where it uses them to 
   //   build a post string.
   //ob('Form1').action="index.php?"+stringparms;
   //formSubmit();
   formSubmit(stringparms);
}

function SetAction(gp_var,gp_action,varname,varvalue) {
	ob(gp_var).value = gp_action;
	SetAndPost(varname,varvalue);
}

function drillDown(dd_page,ddc,ddv) {
	drillDownSet(dd_page,ddc,ddv);
	formSubmit();
}

function drillDownSet(dd_page,ddc,ddv) {
	ob("dd_page").value   = dd_page;
	ob("dd_ddc").value    = ddc;
	ob("dd_ddv").value    = ddv;
}

function drillBack(gp_count) { 
	ob("dd_ddback").value=gp_count;
	formSubmit(); 
}
function serverFunctionCall(name,parms) {
   function_return_value=false;
   var str=''
   str = '?gp_function='+name;
   str+= '&gp_parms='+parms;
   andrax(str);
}

function byId(id) {
   return document.getElementById(id);
}
function ob(oname) {
  if (document.getElementById)
	  return document.getElementById(oname);
  else if (document.all) 
	  return document.all[name];
}
// Get the value of an object
function obv(oname) {
   if(ob(oname)) return ob(oname).value;
   else return '';
}

// Return keystroke like 'A' or '1'
function KeyEvent(e) {
   if(window.event)
      // IE
      return window.event.keyCode;  
   else
      // firefox
      return e.which;
}
// Compatibility
// Cross-browser implementation of element.addEventListener()
function addEventListener(element, type, expression,uc) {
    if(uc==null) { uc=false; }
    if (element.addEventListener) { // Standard
        element.addEventListener(type, expression, uc);
        return true;
    } else if (element.attachEvent) { // IE
        element.attachEvent('on' + type, expression);
        return true;
    } else return false;
}
// function contributed by Don Organ 10/29/07
function removeEventListener(el, eType, fn) {
    if (el.removeEventListener) {
        el.removeEventListener(eType, fn, false);
    }
    else if ( el.detachEvent ) {
        el.detachEvent( 'on' + eType, fn );
    } 
    else {
        return false;     
    }
}
// Compatibility
function eventTarget(e) {
    if(window.event) 
        return window.event.target;
    else 
        return e.currentTarget;
}

function bodyKeyPress(e) {
   keycode = KeyCode(e);
   
   // Handle function keys from f1 to f12
   if(keycode >= 112 && keycode <= 123) {
      var objnum = keycode - 111;
      var objname= 'object_for_f'+objnum.toString();
      //c*onsole.log(objname);
      obj = ob(objname);
      if(obj) {
         //c*onsole.log('going to onclick');
         obj.onclick();
         return false;
      }
   }
   
   if(keycode==13) {
      obj = ob('object_for_enter');
      if(obj) {
         obj.onclick();
         return false;
      }
   }
   
   /* Forward compatible to x4.js.  If that is not loaded,
      this array will not exist.  If it does exist, the code
      assumes the complete x4.js is loaded.
    */
   if(typeof(x4letters)!='undefined') {
       var objId='object_for_'+x4letters[e.charCode-97];
       if(byId(objId)) byId(objId).onclick();
   }
   
   return true;
   
   // END key         is 35
   // HOME key        is 36
   // PAGE DOWN   key is 34
   // PAGE UP     key is 33
   //alert(key);
}
// ------------------------------------------------------
// Universal handlers for events on user inputs
// ------------------------------------------------------
function inputOnFocus(obj) {
   /* read only controls don't do anything */
   //var lp = 'onfocus ' + obj.name;
   //c*onsole.log(lp);
   if(obj.readOnly) return;

   // Set the value now, so that we can detect changes
   // in values that need to call back to the server 
   //c*onsole.log(lp + ', getting value');
   obj.attributes.getNamedItem('x_value_focus').value=obj.value;
   
   // Set color on gaining focus
   //c*onsole.log(lp + ', calling focuscolor');
   focusColor(obj,true);
}
function inputOnBlur(obj) {
   /* read only controls don't do anything */
   //var lp = 'onfocus ' + obj.name;
   if(obj.readOnly) return true;
   
   // Check for changes and do the focus color thing
   //c*onsole.log(lp + ', calling changecheck');
   changeCheck(obj);   
   //c*onsole.log(lp + ', calling focuscolor');
   focusColor(obj,false);
   return true;
}
function inputClick(obj) {
   changeCheck(obj);
}
function inputOnKeyUp(e,obj) {
   keycode = KeyCode(e);
   // Check for F2 - F10 or END/Home
   if((keycode >= 113 && keycode <= 121) || (keycode >=33 && keycode <=40)) {
      bodyKeyPress(e);
      return false;
   }

   // read only controls don't do anything 
   if(obj.readOnly) return;
   
   // Check for open ajax dynamic list? not necessary, they take
   // over the keyboard automatically
   // UP ARROW    key is 38
   // DOWN ARROW  key is 40
   // RIGHT ARROW key is 39
   // LEFT ARROW  key is 37
   if(keycode == 38) {
      // handle the up arrow
   }
   else if(keycode == 40) {
      // handle the down arrow
   }
   else {
      fieldformat(obj,keycode);
      changeCheck(obj);
      return true;
   }
}

// KFD 8/8/07, project JS_KEYSTROKE, add lots of functionality
// for javascript
function fieldformat(obj,keycode) {
   var type_id = objAttValue(obj,'x_type_id');
   var objval  = obj.value;
   if(type_id=='ssn') {
      if(objval.length==3) {
         if(keycode!=8) {
            obj.value += '-';
         }
            
      }
      if(objval.length==6) {
         if(keycode!=8) {
            obj.value += '-';
         }
      }
   }
   if(type_id=='ph12') {
      if(objval.length==3) {
         if(keycode!=8) {
            obj.value += '-';
         }
      }
      if(objval.length==7) {
         if(keycode!=8) {
            obj.value += '-';
         }
      }
   }
   if(type_id=='date') {
      if(objval.length==6) {
         if(objval.indexOf('/')==-1) {
            if(objval.indexOf('-')==-1) {
               var month=objval.substr(0,2);
               var day  =objval.substr(2,2);
               var year =objval.substr(4,2);
               yearint = parseInt(year);
               if(yearint < 30) {
                  year = '20'+year
               }
               else {
                  year = '19'+year
               }
               obj.value=month+'/'+day+'/'+year;
            }
         }
      }
   }
}

// KFD, 5/25/07, Part of AJAX X3
// Revised 8/8/07 project JS_KEYSTROKE
function changeCheck(obj) {
   /* read only controls don't do anything */
   if(obj.readOnly) return;

   var x_value_original = objAttValue(obj,'x_value_original');
   var x_class_base     = objAttValue(obj,'x_class_base');
   var x_mode           = objAttValue(obj,'x_mode');
   if(x_mode!='ins' && x_mode!='search') {
       if(obj.attributes.getNamedItem('x_class_base')) {
           if(obj.value != x_value_original) {
               obj.attributes.getNamedItem('x_class_base').value='ins';
           }
           else {
               obj.attributes.getNamedItem('x_class_base').value='upd';
           }
       }
   }
   fieldColor(obj);
}

// KFD 5/25/07, Part of AJAX X3
// Revised 8/8/07 project JS_KEYSTROKE
// Change class Suffix to Selected
function focusColor(obj,selected) {
   if(obj.readOnly && selected) return;
   if(selected) {
      obj.attributes.getNamedItem('x_class_suffix').value='Selected';
   }
   else {
      obj.attributes.getNamedItem('x_class_suffix').value='';
   }
   fieldColor(obj);
}

// KFD 5/25/07, Part of AJAX X3
// Revised 8/8/07 project JS_KEYSTROKE
// Assign class to an item
function fieldColor(obj) {
   /* If it does not have the x_class_base, it ain't one of ours */
   if(!obj.attributes.getNamedItem('x_class_base')) return;
   
   obj.className='x3'
      +objAttValue(obj,'x_class_base')
      +objAttValue(obj,'x_class_suffix')
}

// ------------------------------------------------------
// KFD 10/8/07.  Backport.  We've started work on X4,
//    so only small fixes go here now.  This one lets
//    us do a two-column foreign key with dropdowns
// ------------------------------------------------------
function fetchSELECT(table,input,col1,col1val,col2,col2val) {
    var getString
        ='?ajxfSELECT='+table
        +'&pfx='+objAttValue(input,"nameprefix")
        +'&col1='+col1
        +'&col1val='+encodeURIComponent(col1val)
        +'&col2='+col2
        +'&col2val='+encodeURIComponent(col2val);
    andrax(getString);
}

function formSubmit(str) {
   // This is the branch that just submits
   if(typeof(ajaxTM)=='undefined') {
      if(str) {
         ob('Form1').action="index.php?"+str;
      }
      ob("Form1").submit();
      return;
   }
   if(ajaxTM==0) {
      if(str) {
         ob('Form1').action="index.php?"+str;
      }
      ob("Form1").submit();
      return;
   }
   
   // this is the ajax branch
   formSubmitajxBUFFER(str);
   return;
}
   
function formSubmitajxBUFFER(str) {
   if(str) {str='&'+str } else { str=''; }
   var postString= '?ajxBUFFER=1' + str;
   var f = ob('Form1');
   for(var no = 0; no < f.elements.length; no++ ) {
      e=f.elements[no];
      if(e.type=='hidden') {
         if(e.value) {
            postString+="&"+e.name+"=";
            postString+=encodeURIComponent(e.value);
         }
      }
      else {
         if(e.attributes.getNamedItem('x_value_original')) {
            xva = e.attributes.getNamedItem('x_value_original');
            if(e.tagName=='TEXTAREA') {
               postString+="&"+e.name+"=";
               postString+=encodeURIComponent(e.innerHTML);
            }
            else {
               postString+="&"+e.name+"=";
               postString+=encodeURIComponent(e.value);
            }
         }
         else {
            postString+="&"+e.name+"="+encodeURIComponent(e.value);
         }
      }
   }
   andrax(postString);
}

function fieldsReset() {
   var f = ob('Form1');
   //f.reset();
   var x = 'x'
   for(var no = 0; no < f.elements.length; no++ ) {
      if(f.elements[no].type!='hidden') {
         x=f.elements[no];
         if(x.attributes.getNamedItem('x_value_original')!=null) {
             x.value=x.attributes.getNamedItem('x_value_original').value;
             if(x.attributes.getNamedItem('x_mode').value=='upd') {
                 changeCheck(x);
             }
         }
      }
   }
}

function clearBoxes() {
   var f = ob('Form1');
   for(var no = 0; no < f.elements.length; no++ ) {
      obj=f.elements[no];
      if(obj.type=='hidden') continue;
      if(obj.type=='button') continue;
      //if(obj.attributes.getNamedItem('x_no_clear')) {
         if(obj.attributes.getNamedItem('x_no_clear').value=='Y') continue;
      //}
      f.elements[no].value='';
   }
}

// ------------------------------------------------------
// Popup a window and display something in that.
// ------------------------------------------------------
function Popup(url,caption){
	w = 770;
	h = 700;
	mytop = 100;
	myleft = 100;
	//settings="width=" + w + ",height=" + h + ",top=" + mytop + ",left=" + myleft + ", " +
	//	"scrollbars=yes,location=no,directories=no,status=no,resizable=yes";
	settings="width=" + w + ",height=" + h + ",top=" + mytop + ",left=" + myleft + ", " +
		"scrollbars=yes,resizable=yes";
	//win=window.open(url,caption,settings);
   //alert(5);
   // KFD 7/19/06, Using the caption fails on IE 6, go figure.
   //              not pursued at this time....
   //settings='';
	win=window.open(url,'Popup',settings);
   //win=window.open('http://www.novell.com','Testing','');
	win.focus();
}

// ------------------------------------------------------
// Popup a custom-size window and display something in that.
// ------------------------------------------------------
function PopupSized(url,caption,w,h,mytop,myleft){
	//w = 770;
	//h = 700;
	//mytop = 100;
	//myleft = 100;
	//settings="width=" + w + ",height=" + h + ",top=" + mytop + ",left=" + myleft + ", " +
	//	"scrollbars=yes,location=no,directories=no,status=no,resizable=yes";
	settings="width=" + w + ",height=" + h + ",top=" + mytop + ",left=" + myleft + ", " +
		"status=no,scrollbars=no,resizable=no,toolbars=no";
	win=window.open(url,caption,settings);
   //alert(5);
   // KFD 7/19/06, Using the caption fails on IE 6, go figure.
   //              not pursued at this time....
	//win=window.open(url,'Popup',settings);
   //win=window.open('http://www.novell.com','Testing','');
	win.focus();
}



// ------------------------------------------------------
// Routines added 4/9/07 for AJAX-ified interface
// ------------------------------------------------------
/**
name:FetchRow
parm:table_id
parm:pk_value

Executes an AJAX call to fetch a row to the browser for general use.

On the server the program [[index_hidden.php]] will handle this
call and return a 

-- This was an experimental function, and will likely disappear --
*/
function FetchRow(table_id,pk_ob) {
   andrax('?gp_fetchrow='+table_id+'&gp_pk='+encodeURI(ob(pk_ob).value))
}

function adl_visible() {
   if(ajax_optionDiv) {
      if(ajax_optionDiv.style.display!='none') return true;
   }
   if(ajax_optionDiv_iframe) {
      if(ajax_optionDiv_iframe.style.display!='none') return true;
   }
}

// DEPRECATED BY JS_KEYSTROKE but very much alive at the moment.
// Project JS_KEYSTROKE will revert to simply noticing that the
// value has changed, seeing that it has a "notify_server_on_change"
// flag, and sending the value back to the server for processing there.
// The server will then decide what to do about it.
//
function ajaxFetch(
    table_id_par
   ,name_prefix
   ,commapklist
   ,commafklist
   ,controls
   ,columns,obj) {

   // KFD 8/15/07.  This is a heartbreak.
   //     The line below checks for adl visibility.  The idea is not
   //     to call the ajax fetch if it is visible.  Without this line,
   //     the fetch is called twice when somebody uses the mouse to make
   //     a selection.  Worse, if somebody clicks somewhere outside of the
   //     the selected box, the ajax fetch fires.
   //
   //     However, if we uncomment it, then using TAB to exit a field does
   //     not work.  WORSE THAN THAT, this fact will be disguised if you have
   //     Firebug enabled.  Having firebug enabled causes it to work ok!
   //     Then the customer says, "hey, it doesn't work" and you pull your
   //     hair out wondering why.
   //
   //     Since we need the TAB key to work as an absolute must, we accept
   //     some strange behavior if people go clicking off in different
   //     places.
   //if(adl_visible()) return;
   
   // If the value did not change, do not do anything
   //var x_value_focus = obj.attributes.getNamedItem('x_value_focus').value
   //if(x_value_focus == obj.value) { return; }

   /* split up the pk list, make a list of controls and values */
   afklist=commafklist.split(',');
   vallist='';
   for(x=0; x<afklist.length; x++) {
      if(vallist.length!=0) {  vallist+=','; }
      ctlname=name_prefix+afklist[x];
      if(ob(ctlname)) {
         if(ob(ctlname).value=='') {
            return
         }
         else {
            vallist+=ob(ctlname).value
         }
      }
   }


   /* if we got this far, make the call */
   href='?ajxFETCH=1'
       +'&ajxtable='+table_id_par
       +'&ajxcol='+commapklist
       +'&ajxval='+vallist
       +'&ajxcontrols='+controls
       +'&ajxcolumns='+columns;
   //alert(href);
   andrax(href);
}

// ------------------------------------------------------
// This gives info on a row in a table
// ------------------------------------------------------
// Introduced w/x_table2
function Info2(table,field) {
var thevalue = ob(field).value;
	if (thevalue!="") {
      $href="?gp_page="+table+"&gp_pk="+thevalue;
      window.open($href);
		//Popup("?gp_out=info&gp_page="+table+"&gp_pk="+thevalue,"Info");
	}
	else {
		alert("Please select a value first!");
	}
}

// ------------------------------------------------------
// These are keyboard handling things
// ------------------------------------------------------

function doButton(e,compareto,obtoclick)  {
   // Obtain the keystroke.
   var key = KeyEvent(e);

   // if matches specified value, click the object
   if (key == compareto) {
      if(ob(obtoclick)) { ob(obtoclick).onclick(); }
   }
   return true;
}
/*  Not used yet
function doButtonActions(e,compareto,obtodo,funcs) {
   // Get the keystroke
   var key = KeyEvent(e);
   // If matches, do the events in order
   if(key==compareto) {
      afuncs=funcs.split(',');
      for(x=0; x<afuncs.length; x++) {
         eval( "ob('"+obtodo+"')."+afuncs[x] )
      }
   }      
}
*/



// ------------------------------------------------------
// Our super-simple AJAX library.  
//
// Originally copied whole from this site:
//
// http://rajshekhar.net/blog/archives/85-Rasmus-30-second-AJAX-Tutorial.html
//
// ...and then we modified it from there
// ------------------------------------------------------
function createRequestObject() {
    var ro;
    var browser = navigator.appName;
    if(browser == "Microsoft Internet Explorer"){
        ro = new ActiveXObject("Microsoft.XMLHTTP");
    }else{
        ro = new XMLHttpRequest();
    }
    return ro;
}

var http = createRequestObject();

function AjaxCol(objname,objvalue) {
   getString='?gp_ajaxcol=1';
   getString+='&gp_colname='+objname;
   getString+='&gp_colval='+objvalue;
   getString+='&gp_page='+ob('gp_page').value;
   if(objname!='appref') {
      getString+='&gp_appref='+ob('x2t_appref').value;  
   }
   andrax(getString);
}

function AjaxWriteVal(ajxc1table,ajxcol,ajxval,ajxskey,list) {
   getString ='?ajxc1table='+ajxc1table;
   getString+='&ajxcol='+ajxcol;
   getString+='&ajxval='+encodeURI(ajxval);
   getString+='&ajxskey='+ajxskey;
   getString+='&ajxlist='+list
   //alert(getString);
   andrax(getString);
}

// KFD 8/6/07.  A server "map-back" function.  Something in the client
//              invokes an ajax server function.  The class for this
//              page should include a function field_changed_<field> 
//              that will do what needs to be done here.
//              
function field_changed(page,field,value) {
   getString='?gp_page='+page
      +'&fwajax=field_changed'
      +'&ajx_field='+field
      +'&ajx_value='+encodeURIComponent(value)
   andrax(getString);
}

function andrax(getString,handler) {
    http.open('POST', 'index.php'+getString);
    if(handler==null) {
        http.onreadystatechange = handleResponse;
    }
    else {
        http.onreadystatechange = handler;
    }
    http.send(null);
}

function sndReq(action) {
    getString="?gp_page="+ob('gp_page').value;
    getString+="&gp_skey="+ob('gp_skey').value;
    getString+=action;
    andrax(getString);
}

function handleResponse() {
    if(http.readyState == 4){
        var response = http.responseText;
        var elements=new Array();
        
        var controls=new Array();

        // either multiples or only one
        if(response.indexOf('|-|') != -1) {
           elements=response.split('|-|');
           for(x=0; x<elements.length; x++) {
              controls = handleResponseOne(elements[x],controls);
           }
        }
        else {
           controls = handleResponseOne(response,controls);
        }
        
        if(controls) {
           for(var x=0;x<controls.length;x++) {
              changeCheck(ob(controls[x]));
              // This causes cascading fetches 
              if(ob(controls[x]).onblur)   { ob(controls[x]).onblur();  }
           }
        }
    }
}

function  handleResponseOne(one_element,controls) {
   //alert(one_element);
   //return;
   var update = new Array();
   //alert(one_element);
   if(one_element.indexOf('|') == -1) {
      if(one_element.length==0) return;
      alert('Bad Ajax Response: '+one_element);
   }
   else {
      update = one_element.split('|');
      //alert(update[0]);
      if(update[0]=='echo') {
         alert(update[1]);
      }
      else if(update[0]=='_focus') {
         ob(update[1]).focus();
      }
      else if(update[0]=='_prompt') {
         prompt(update[1],update[2]);
      }
      else if(update[0]=='_value') {
         if(ob(update[1])) {
            ob(update[1]).value=update[2];

            // save the name of the control, we will run the onchange
            // of all of them at the end.  We don't want to do it now
            // or we will have multiple ajax calls running at the same time.
            // That actually made firefox hang!
            controls.push(update[1]);
         }
         
      }
      else if(update[0]=='_script') {
         eval(update[1]);
      }
      else if(update[0]=='_redirect') {
         window.location=update[1];
      }
      else if(update[0]=='_title') {
         document.title=update[1];
      }
      else if(update[0]=='_alert') {
         if(ob('ql_right')) ob('ql_right').innerHTML=update[1];
         //alert(update[1]);
      }
      else {
         if(!ob(update[0])) {
            alert('Bad Object: '+update[0]);
            alert('Value: '+update[1]);
         }
         else {
            ob(update[0]).innerHTML=update[1];
         }
      }
   }
   return controls;
}

/****M* Javascript-API/Date-Extensions
*
* NAME
*   Date-Extensions
*
* FUNCTION
*   Javascript lacks a handful of useful date functions, 
*   such as returning the day of the week as a string.
*   These extensions to the Date object provide those
*   facilities.
*
******
*/

/****m* Date-Extensions/getDow
*
* NAME
*   Date.getDow
*
* FUNCTION
*   The Javascript method Date.getDow returns the day of
*   the week as a string.
*
*   If logical true is passed for the first parameter,
*   a 3-digit abbreviation is returned, using 'Thu' for
*   Thursday.
*
* INPUTS
*   boolean - If true, returns a 3-digit abbreviation
*
*
* SOURCE
*
*/
Date.prototype.getDow = function(makeItShort) {
    var days = ['Sunday','Monday','Tuesday','Wednesday'
        ,'Thursday','Friday','Saturday'
    ];
    var retval = days[this.getDay()];
    if(makeItShort) {
        return retval.slice(0,3);
    }
    return retval;
}
/******/


/****M* Javascript-API/String-Extensions
*
* NAME
*   String-Extensions
*
* FUNCTION
*   Javascript lacks a handful of useful string functions, 
*   such as padding and trimming, which we have added
*   to our standard library.
*
******
*/


/****m* String-Extensions/trim
*
* NAME
*   String.trim
*
* FUNCTION
*   The Javascript function trim removes leading and trailing
*   spaces from a string.
*
* EXAMPLE
*   Example usage:
*     var x = '  abc  ';
*     var y = x.trim();  // returns 'abc'
*
* SOURCE
*/
String.prototype.trim = function() {
	return this.replace(/^\s+|\s+$/g,"");
}
/******/

/****m* String-Extensions/ltrim
*
* NAME
*   String.ltrim
*
* FUNCTION
*   The Javascript function ltrim removes leading spaces
*   from a string (spaces on the left side).
*
* EXAMPLE
*   Example usage:
*     var x = '  abc  ';
*     var y = x.ltrim();  // returns 'abc  '
*
* SOURCE
*/
String.prototype.ltrim = function() {
	return this.replace(/^\s+/,"");
}
/******/

/****m* String-Extensions/rtrim
*
* NAME
*   String.rtrim
*
* FUNCTION
*   The Javascript function rtrim removes trailing spaces
*   from a string (spaces on the right side).
*
* EXAMPLE
*   Example usage:
*     var x = '  abc  ';
*     var y = x.ltrim();  // returns '  abc'
*
* SOURCE
*/
String.prototype.rtrim = function() {
	return this.replace(/\s+$/,"");
}
/******/


/****m* String-Extensions/pad
*
* NAME
*   String.pad
*
* FUNCTION
*   The Javascript function pad pads out a string to a given
*   length on either left or right with any character.
*
* INPUTS
*   int - the size of the resulting string
*
*   string - the string that should be added.  You can provide
*   a string of more than one character.  The resulting string
*   will be clipped to ensure it is returned at the requested
*   size.
*
*   character - either an 'L' or left padding or an 'R' for
*   right padding.
*
* EXAMPLE
*   Example usage:
*     var x = 'abc';
*     var y = x.pad(6,'0','L');  // returns '000abc';
*
* SOURCE
*/
String.prototype.pad = function(size,character,where) {
    var val = this;
    while(val.length < size) {
        if(where == 'L') 
            val = character + val;
        else
            val += character;
        if(val.length > size) {
            val = val.slice(0,size);
        }
    }
    return val;
}
/******/


// Taken from http://www.sourcesnippets.com/javascript-string-pad.html
var STR_PAD_LEFT = 1;
var STR_PAD_RIGHT = 2;
var STR_PAD_BOTH = 3;
 
String.prototype.strpad = function(str, len, pad, dir) {
 
    str = String(str);
	if (typeof(len) == "undefined") { var len = 0; }
	if (typeof(pad) == "undefined") { var pad = ' '; }
	if (typeof(dir) == "undefined") { var dir = STR_PAD_RIGHT; }
 
	if (len + 1 >= str.length) {
 
		switch (dir){
 
			case STR_PAD_LEFT:
				str = Array(len + 1 - str.length).join(pad) + str;
			break;
 
			case STR_PAD_BOTH:
				var right = Math.ceil((padlen = len - str.length) / 2);
				var left = padlen - right;
				str = Array(left+1).join(pad) + str + Array(right+1).join(pad);
			break;
 
			default:
				str = str + Array(len + 1 - str.length).join(pad);
			break;
 
		} // switch
 
	}
 
	return str;
 
}

String.prototype.pad = function(len, pad, dir) {
 
    str = this.toString();
	if (typeof(len) == "undefined") { var len = 0; }
	if (typeof(pad) == "undefined") { var pad = ' '; }
	if (typeof(dir) == "undefined") { var dir = STR_PAD_RIGHT; }
 
	if (len + 1 >= str.length) {
 
		switch (dir){
 
			case STR_PAD_LEFT:
				str = Array(len + 1 - str.length).join(pad) + str;
			break;
 
			case STR_PAD_BOTH:
				var right = Math.ceil((padlen = len - str.length) / 2);
				var left = padlen - right;
				str = Array(left+1).join(pad) + str + Array(right+1).join(pad);
			break;
 
			default:
				str = str + Array(len + 1 - str.length).join(pad);
			break;
 
		} // switch
 
	}
 
	return str;
 
}

/****m* String-Extensions/repeat
*
* NAME
*   String.repeat
*
* FUNCTION
*   The Javascript function repeat returns the input string
*   repeated any number of times
*
* INPUTS
*   * int - the number of times to repeat the string.
*
*
* EXAMPLE
*   Example usage:
*     alert( 'abc'.repeat(3));
*
* SOURCE
*/
String.prototype.repeat = function(count) {
    if(count==null) count = 1;
    retval = '';
    for(var x = 1; x<= count; x++) {
        retval+= this;
    }
    return retval;
}
/******/

/****m* String-Extensions/htmlDisplay
*
* NAME
*   String.htmlDisplay
*
* FUNCTION
*   The Javascript function htmlDisplay converts the HTML
*   characters ampersand '&amp;', less than '&lt;' and
*   greater-than '&gt;' to their HTML entities equivalents
*   for safe display.
*
*   The reverse function is htmlEdit.
*
* SOURCE
*/
String.prototype.htmlDisplay = function() {
    return this.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
}
/******/

/****m* String-Extensions/htmlEdit
*
* NAME
*   String.htmlEdit
*
* FUNCTION
*   The Javascript function htmlEdit removes HTML entities 
*   from a string and replaces them with literal characters,
*   suitable for editing in an input.  The characters replaced
*   are ampersand '&amp;', less-than '&lt;', greater-than '&gt;'
*   and non-breaking space '&amp;nbsp;'.
*
* SOURCE
*/
String.prototype.htmlEdit = function() {
    return this.replace(/&amp;/g,'&')
        .replace(/&lt;/g,'<')
        .replace(/&gt;/g,'>')
        .replace(/&nbsp;/g,' ');
}
/******/

/* ---------------------------------------------------- *\

   FIX BRAIN-DAMAGED INTERNET EXPLORER  
   
\* ---------------------------------------------------- */

if(!Array.indexOf){
    Array.prototype.indexOf = function(obj){
        for(var i=0; i<this.length; i++){
            if(this[i]==obj){
                return i;
            }
        }
        return -1;
    }
}


/* ---------------------------------------------------- *\

   SECTION 3: jQuery plugins and additions  
   
\* ---------------------------------------------------- */
/*
jQuery.extend(
  jQuery.expr[ ":" ], 
  { reallyvisible : "!(jQuery(a).is(':hidden') || jQuery(a).parents(':hidden').length)" }
);
*/

/*
jQuery.fn.isVisible = function(obj){
    var rv = true;
    if( !$(obj).is( ':visible' ) ) {
        console.log("object ",this);
        return $([]);
    }
    $(obj).parents().each(
        function(){
            if( !$(this).is(':visible') ) {
                console.log("not visible!  stopping ",this);
                return $([]);
            }
    });
    return obj;
}
*/
 
/*
* Plugin to track focus
*
* KFD 9/2/08, make conditional, this is safer
*/
if(typeof(jQuery)!='undefined') {
    jQuery.focusTrack = false;
    jQuery.focusTrackStack = [ ];
    jQuery.focusTrackPop = function() {
        if(jQuery.focusTrackStack.length > 0) {
            $( jQuery.focusTrack ).blur();
            jQuery.focusTrack = jQuery.focusTrackStack.pop(); 
        }    
    }
    jQuery.focusTrackBlur = function() {
        if(jQuery.focusTrackStack.length > 0) {
            $( jQuery.focusTrack ).blur();
        }    
    }
    jQuery.focusTrackRestore = function() {
        $(jQuery.focusTrack).focus();
    }
    jQuery.fn.focusTrack = function(newContext) {
        // If a new context, push the old back a layer     
        if(newContext) {
            if(jQuery.focusTrack) {
                $(jQuery.focusTrack).blur();
            }
            jQuery.focusTrackStack[jQuery.focusTrackStack.length]=jQuery.focusTrack;
            jQuery.focusTrack = false;
        }
        return this.each(function() {
            $(this).focus( function() {
                jQuery.focusTrack = this;
            })
        });
    }
    
    jQuery.getCSS = function( url, media, rel, title ) {
       jQuery( document.createElement('link') ).attr({
           href: url,
           media: media || 'screen',
           type: 'text/css',
           title: title || '',
           rel: rel || 'stylesheet'
       }).appendTo('head');
    };
}

var jqModalClose=function(hash) { hash.w.fadeOut(500, function() { hash.o.fadeOut(250);}); };
var jqModalOpen=function(hash) { hash.w.fadeIn(500);hash.o.fadeIn(500);};

/****O* Javascript-API/u
* NAME
*   u (Javascript General Utility Object)
*
* FUNCTION
*   Contains a variety of shortcut methods such as byId,
*   which makes code smaller and tighter.
*
*   Also contains several subobjects with specific areas
*   of utility.
*
*   The Javascript u object is present on all Andromeda
*   pages, you can access it from Javascript code on any
*   custom page you write.
*
* PORTABILITY
*   Requires no other Andromeda files or libraries, is
*   completely standalone.
*
******
*/
var u = {
    /****v* u/debugFlag
    *
    * NAME
    *    debugFlag
    *
    * FUNCTION 
    *    If this flag is false, messages sent to u.debug will be
    *    ignored and that function will return false.
    *
    *    If this flag is true, messages sent to u.debug will be 
    *    sent to console.log, if Firebug is installed and
    *    enabled.
    *
    * SEE ALSO
    *    u.debug
    *
    ******
    */
    debugFlag: false,
    debugStack: [ ],
    
    /****m* u/debugPush
    *
    * NAME
    *   u/debugPush
    *
    * FUNCTION
    *   The javascript method debugPush allows you to turn on 
    *   debugging and then later turn it back to its original
    *   setting (either on or off).  This is useful when you 
    *   do not want to turn debugging on globally, but just want
    *   to turn it on or off in a particular routine.
    *
    * INPUTS
    *   * boolean - new debugging setting (default: true)
    *
    * EXAMPLE
    *   A javascript example would be:
    *
    *      u.debugPush(true); // force debugging on
    *      u.debug("I want this message no matter what.");
    *      u.debugPop();      // return to prior setting
    *
    * SOURCE
    */
    debugPush: function(value) {
        if(value==null) value = true;
        this.debugStack.push(this.debugFlag);
        this.debugFlag = value;
    },
    /******/
    
    /****m* u/debugPop
    *
    * NAME
    *   u/debugPop
    *
    * FUNCTION
    *   The javascript method debugPop returns the u.debugFlag
    *   setting to whatever it was before the most recent
    *   call to u.debugPush.
    *
    *   If debugPop is called too many times (more than debugPush)
    *   was called, a call is made to u.error() but no javascript
    *   error actually occurs -- program execution will not fail.
    *
    * INPUTS
    *   none.
    *
    * EXAMPLE
    *   A javascript example would be:
    *
    *      u.debugPush(true); // force debugging on
    *      u.debug("I want this message no matter what.");
    *      u.debugPop();      // return to prior setting
    *
    * SOURCE
    */
    debugPop: function() {
        if(this.debugStack.length==0) {
            u.error("Call to debugPop with no prior call to debugPush");
        }
        else {
            this.debugFlag = this.debugStack.pop();
        }
    },
    /******/
    
    
    /****m* u/p
    * NAME
    *   p (Safe Object Property Access)
    *
    * FUNCTION
    *   The Javascript function u.p retrieves the 
    *   named property of an HTML or
    *   Javascript object, and returns a default value if
    *   the property does not exist.
    *
    *   This function resolves the answer by checking
    *   for the following in this order:
    *   *  An HTML attribute such as <td colspan="99">
    *   *  Any non-HTML attribute that may be have previously
    *      assigned in a command like "obj.zSpecialProperty = 5"
    *   *  A non-standard HTML attribute such
    *      as <td xColumnId="customer">
    *
    *   If all else fales, the parameter defValue is returned.
    *
    * INPUTS
    *   obj - The object whose property you need to access
    *   
    *   propName - The property whose value you are retrieving
    *   
    *   defValue - The value to return if the object has no 
    *   HTML attribute, no javascript-assigned property,
    *   and no HTML attribute accessible through
    *   HTMLElement.getAttribute().
    *
    * RESULT
    *   mixed - returns the value of the property, which can
    *   be any valid Javscript type.
    *
    * SOURCE
    */
    p: function(obj,varName,defValue) {
        if(typeof(obj)!='object') {
            return defValue;
        }
        
        // First try, maybe it is a direct property
        if(typeof(obj[varName])!='undefined') {
            return obj[varName];
        }
        // Second try, maybe it is an attribute
        if(obj.getAttribute) {
            if(obj.getAttribute(varName)!=null) {
                return obj.getAttribute(varName);
            }
        }
        // Give up, return the defvalue
        return defValue;
    },
    /******/
    
    /****m* u/byId
    * NAME
    *   u.byId 
    *
    * FUNCTION
    *   The Javascript method u.byId is a
    *   shortcut for javascript document.getElementById()
    *
    * RESULT
    *   HTMLElement - returns the same result as 
    *   document.getElementById
    *
    * SOURCE 
    */
    byId: function(id) {
        return document.getElementById(id );
    },
    /******/
    
    logIndent: 0,
    /****m* u/log
    * NAME
    *   u.log
    *
    * FUNCTION
    *   Sends a message to console.log
    *   if it exists.  If firebug is not installed or enabled
    *   then this function has no effect.  Compare to u.debug.
    *
    * RESULT
    *   message - If firebug is active and the message can
    *   be sent to the console, then the message itself is
    *   returned.  Otherwise returns false.
    *
    * SOURCE
    */
    log: function(message,logIndent) {
        if(logIndent==-1) this.logIndent--;
        message = ' '.repeat(this.logIndent*3)+message;
        if(logIndent ==1) this.logIndent++; 
        if(typeof(console)!='undefined') {
            console.log(message);
            return message;
        }
        return false;
    },
    /******/

    /****m* u/debug
    * NAME
    *   u.debug
    *
    * FUNCTION
    *   This Javascript method sends a message to console.log()
    *   if firebug is installed
    *   and the property u.debugFlag is true.  Compare
    *   to u.log which does not consider the value of u.debugFlag.
    *
    *   You can put commands to u.debug into your Javascript code,
    *   and then control whether or not they go to the console
    *   by setting and resetting the u.debugFlag.
    *
    * RESULT
    *   message - If firebug is present and enabled, and the flag
    *   u.debugFlag is true, returns the message back to the calling
    *   program.  Otherwise returns false.
    *
    * SOURCE
    */
    debug: function(message,logIndent) {
        if(this.debugFlag) {
            return this.log(message,logIndent);
        }
        return false;
    },
    /******/

    /****m* u/error
    * NAME
    *   u.error
    *
    * FUNCTION
    *   This Javascript method sends an error message to 
    *   console.log if firebug is installed.
    *
    *
    * RESULT
    *   Returns the original message if console.log is defined
    *   (that is, if Firebug is installed), otherwise returns false.
    *
    * SOURCE
    */
    error: function(message,logIndent) {
        if(typeof(console)!='undefined') {
            console.error(message);
            return message;
        }
        return false;
    },
    /******/
    
    /****m* u/uniqueId
    * NAME
    *   u.uniqueId
    *
    * FUNCTION
    *   Generates a random number between 1 and 1,000,000 that is
    *   not being used as the ID for any DOM object.  Useful for
    *   generating unique and content-free Id's for DOM objects.
    *
    * EXAMPLE
    *   Example usage would be:
    *
    *     var x = document.createElement('div');
    *     x.id = u.uniqueId();
    *
    * RESULT
    *   id - returns the id
    *
    * SOURCE
    */
    uniqueId: function() {
        var retval = 0;
        while( $('#'+retval).length > 1  || retval==0) {
            var retval=Math.floor(Math.random()*1000000);
        }
        return retval;
    },
    /******/
    
    /** NODOC **/
    /** DELETE **/    
    clone: function(obj,level) {
        if(level==null) level = 1;
        if(level==10) return { };
        var retval = { };
        for (var x in obj) {
            if(typeof(obj[x])=='object') {
                retval[x] = u.clone(obj[x],level+1);
            }
            else {
                retval[x] = obj[x];
            }
        }
        return retval;
    },
    
    /****O* u/bb
    * NAME
    *   u.bb
    *
    * FUNCTION
    *   "Bulletin Board" object that lets you "stick" values on it
    *   and "grab" them from elsewhere.
    *
    *   u.bb methods should be used instead of global variables
    *   because they allow you to avoid collisions with framework
    *   globals.  The framework uses the methods u.bb.vgfGet
    *   and u.bb.vgfSet, and your application should use u.bb.vgaGet
    *   and u.bb.vgaSet.
    *
    * EXAMPLE:
    *   Example usage:
    *      u.bb.vgaSet('myvar','value');
    *      var myvar = u.bb.vgaGet('myvar','acceptableDefault');
    *
    ******
    */
    bb: {
        /****v* bb/fwvars
        * NAME
        *   u.bb.fwvars
        *
        * FUNCTION
        *   Global bulletin board framework variables.  Not intended
        *   for direct access, manipulate at your own risk!
        ******
        */
        fwvars: { },
        /****v* bb/appvars
        * NAME
        *   u.bb.appvars
        *
        * FUNCTION
        *   Global bulletin board application variables.  Not intended
        *   for direct access, manipulate at your own risk!
        ******
        */
        appvars: { },
        /****m* bb/vgfSet
        * NAME
        *   u.bb.vgfSet
        *
        * FUNCTION
        *   Used by the Andromeda framework to save global variables
        *   for later use.  Application code should never use this,
        *   or you risk overwriting framework values and disrupting
        *   normal performance.
        *
        * INPUTS
        *   varName - The name of your variable
        *   varValue   - The value to store
        *
        * SOURCE
        */
        vgfSet: function(varName,value) {
            this.fwvars[varName] = value;
            //if(typeof(console)!='undefined') {
            //    console.log("vgfSet",varName,value);
            //}
        },
        /******/
        
        /****m* bb/vgfGet
        * NAME
        *   u.bb.vgfGet
        *
        * FUNCTION
        *   Retrieves a variable saved by the framework.
        *
        *   Unlike vfgSet, which application code should never call,
        *   it may be appropriate from time-to-time to call this
        *   function to find out what the framework is up to.
        *
        * INPUTS
        *   varName - the variable you wish to retrieve
        *   defValue - the value to return if the variable does not exist.
        *
        * SOURCE
        */
        vgfGet: function(varName,defValue) {
            var retval = u.p(this.fwvars,varName,defValue);
            //if(typeof(console)!='undefined') {
            //    console.log("vgfGet",varName,retval,defValue);
            //}
            return retval;
        },
        /******/

        /****m* bb/vgaSet
        * NAME
        *   u.bb.vgaSet
        *
        * FUNCTION
        *   Sets a global variable.  Globals saved with this method
        *   will not collide with any framework globals.
        *
        * INPUTS
        *   varName - the variable you wish to save
        *   varValue - the value to save
        *
        * SOURCE
        */
        vgaSet: function(varName,value) {
            this.appvars[varName] = value;
        },
        /******/
        
        
        /****m* bb/vgaGet
        * NAME
        *   u.bb.vgaGet
        *
        * FUNCTION
        *   Retrieves a global variable, or the default value
        *   if the global has not been set.
        *
        * INPUTS
        *   varName - the variable you wish to save
        *   defValue - the value to return if the global does
        *              not exist.
        *
        * SOURCE
        */
        vgaGet: function(varName,defValue) {
            return u.p(this.appvars,varName,defValue);
        }
        /******/
    },
    
    
    /****O* u/events
    *
    * NAME
    *   u.events
    *
    * FUNCTION
    *   The javascript object u.events implements the classic
    *   event listener and dispatcher pattern.
    *
    *   Objects can subscribe to events by name.  Other 
    *   objects can notify the events object when an event
    *   occurs, and it will in turn notify all of the subscribers.
    *
    * PORTABILITY
    *   The u.events object and its methods expect other u
    *   methods to be available, but do not have any other
    *   dependencies.
    *
    ******
    */
    events: {
        /****iv* events/subscribers
        *
        * NAME
        *   u.events.subscribers
        *
        * FUNCTION
        *   The javascript object u.events.subscribers is an object
        *   serving as an Associative Array.  Each entry in the array
        *   has a key that is the name of the event, and a value that
        *   is an array of object ids for the subscribers.  In JSON
        *   format the array might look like this:
        *
        *       u.events.subscribers = {  // example code only
        *           keyPress_Enter: { 
        *              gridEdit_regrules: 'gridEdit_regrules'
        *           }
        *           addRow_customers: {
        *              gridBrowse_customers: 'gridBrowse_customers'
        *           }
        *       }
        *
        *   This object is documented for completeness only, it is
        *   not intended for direct manipulation.  Use u.events.subscribe,
        *   u.events.unSubscribe, u.events.suppressByPrefix and 
        *   u.events.unSuppress to control event subscriptions.
        * 
        ******
        */
        subscribers: { },

        /****v* events/subStack
        *
        * NAME
        *   u.events.subStack
        *
        * FUNCTION
        *   The javascript object u.events.subStack is used by
        *   u.events as a stack. See u.events.suppressByPrefix
        *   and u.events.unSuppress.
        * 
        ******
        */
        subStack: [ ],
        
        /****m* events/subscribe
        *
        * NAME
        *   u.events.subscribe
        *
        * FUNCTION
        *   The Javascript method u.events.subscribe allows an
        *   object to subscribe to a named event.  That object will
        *   then be notified whenever the event fires.  See the
        *   method u.events.notify for more information on how
        *   the notification is handled.
        *
        * INPUTS
        *   eventName - Any string.  There is no validtion of the 
        *   eventName, so misspellings will result in your object 
        *   not being notified.
        *   object - The object itself.
        *   
        * NOTES
        *   The Andromeda framework fires the following events:
        *   * newRow_<table_name>, whenever any object has added a row
        *     to a back end table it fires newRow_<table_name>, passing
        *     the new row as the argument.
        *   * changeRow_<table_name>, whenever any object has modified
        *     a row it fires changeRow_<table_name>, passing the new
        *     version of the row as the argument.
        *   * deleteRow_<table_name>, whenever any object deletes a 
        *     row from the database, it fires deleteRow_<table_name>,
        *     passing in the skey of the deleted row.
        *   * keyPress, On Extended_Desktop pages all document level
        *     keystrokes are captured and handed first to the u.events
        *     object for dispatching.  Each is dispatched twice, the
        *     first time it is fired as a 'keyPress' event where the 
        *     x4.keyCode value is the argument.
        *   * keyPress_<keyCode>, On Extended Desktop pages all document
        *     level keystrokes are captured and handed first to the
        *     u.events object for dispatching.  Each is dispatched
        *     twice, the second time it is fired as a 'keyPress_<keyCode>'
        *     event with no arguments, such as 'keyPress_Enter' or
        *     'keyPress_F6'.
        *
        * RESULT
        *   No return value
        *
        * SEE ALSO
        *   u.events.notify
        *
        * SOURCE
        */
        subscribe: function(eventName,object) {
            // First determine if we have any listeners for this
            // event at all.  If not, make up the empty object
            if( u.p(this.subscribers,eventName,null)==null) {
                this.subscribers[eventName] = { };
            }
            var subs = this.subscribers[eventName];
            
            // Assign the listener by its ID.  This lets us prevent duplication
            // if the object is confused and registers itself twice.
            //
            var id = object.id;
            if(id == '' || id==null) {
                object.id = u.uniqueId(); 
                id = object.id;
            }
            if( u.p(subs,id,null)==null ) {
                subs[id] = id;
            }
        },
        /******/
        
        /****m* events/unSubscribe
        *
        * NAME
        *   u.events.unSubscribe
        *
        * FUNCTION
        *   The Javascript method u.events.unSubscribe allows an
        *   object to unscribe to an event that it has previously
        *   subscribed to with u.events.subscribe.
        *
        * SOURCE
        */
        unSubscribe: function(eventName,object) {
            var id = object.id;
            if( u.p(this.subscribers[eventName],id,null)!=null ) {
                delete this.subscribers[eventName][id];
            }
        },
        /******/

        /*m* events/getSubscribers
        *
        * NAME
        *   u.events.getSubscribers
        *
        * FUNCTION
        *   The Javascript method u.events.getSubscribes returns
        *   an array of subscribers to a particular event.
        *
        * NOTES
        *   Use this method to register "owners" or objects that
        *   have particular responsibility for a task.  For instance
        *   a grid that displays "patients" might register itself
        *   as a subscriber to "grid_patients".  Then another object
        *   that needs to know the owner can call
        *   u.events.getSubscribers('grid_patients') and find the
        *   object.
        *
        * RETURNS
        *   An array of zero or more object id's. 
        *
        * SOURCE
        */
        getSubscribers: function(eventName) {
            // This code works even if there are no subscribers
            var retval = [ ];
            var subscribers = u.p(this.subscribers,eventName,{ });
            for(var id in subscribers) {
                retval.push(id);
            }
            return retval;
        },
        /******/

        
        /****m* events/suppressByPrefix
        *
        * NAME
        *   u.events.suppressByPrefix
        *
        * FUNCTION
        *   The Javacript method u.event.suppressByPrefix suppresses
        *   event notification for one or more previously subscribed
        *   events.
        *
        *   The Framework uses this method when it creates a modal
        *   dialog to suppress all keyPress events, afterwhich it
        *   re-subscribes  to whatever keyPress events are appropriate
        *   to the particular dialog.
        *
        *   Events are suppressed by giving the event name or the
        *   beginning of a name.  To suppress all keyPress events
        *   call this with the argument 'keyPress', to suppress only
        *   one event you may call with 'keyPress_Enter'.  
        *
        * INPUTS
        *   String - the event name or prefix.
        *
        * RESULT
        *   Number - returns the count of events that were suppressed.
        *
        * SEE ALSO
        *   u.events.unSuppress
        *   u.events.subscribe (for a list of Framework events)
        *
        * SOURCE
        */
        suppressByPrefix: function(prefix) {
            this.subStack.push(u.clone(this.subscribers));
            var count = 0;
            for(var x in this.subscribers) {
                if (x.slice(0,prefix.length)==prefix) {
                    count++;
                    delete this.subscribers[x];
                }
            }
            return count;
        },
        /******/

        /****m* events/unSuppress
        *
        * NAME
        *   u.events.unSuppress
        *
        * FUNCTION
        *   This Javascript method reverses the effect of a previous
        *   call to u.events.suppressByPrefix, so that previously
        *   suppressed events are once again active.
        *
        *   If u.events.suppresByPrefix is called more than once,
        *   the u.events.unSuppress method always reverses the most
        *   recent call.  It is not possible to selectively unsuppress
        *   events or to unsuppress them in a different order than
        *   they were suppressed.
        *
        * INPUTS
        *   none
        *
        * RESULT
        *   true - Always returns true.
        *
        * SEE ALSO
        *   u.events.unSuppress
        *
        * SOURCE
        */
        unSuppress: function() {
            this.subscribers = this.subStack.pop();
            return true;
        },
        /******/
        
        
        /****m* events/notify
        *
        * NAME
        *   u.events.notify
        *
        * FUNCTION
        *   The Javascript method u.events.notify will notify all
        *   objects that have subscribed to an event by calling
        *   u.events.subscribe.
        *
        *   See below for examples on how to code up object listener
        *   methods that will receive the events.
        *
        *   If you want your application objects to notify other objects
        *   of its own events, call this function.
        *
        * INPUTS
        *   eventName, the name of the event
        *   mixed, a single argument.  If multiple arguments are required,
        *   pass an object that contains property:value assignments.
        *
        * RESULTS
        *   boolen - returns true if any listening object has reported
        *   that further handling of the event should stop.  This is
        *   particularly used by the framework to stop propagation of
        *   keystroke events that have been handled by some listener.
        *
        * SEE ALSO
        *   u.events.subscribe, for a list of Framework Events.
        *
        * EXAMPLE
        *    There are two ways to subscribe to events.  If your
        *    object has its own notify method, this method will 
        *    receive all events that the object is subscribed to,
        *    and it must dispatch them according to eventName.
        *
        *    If your object has a method that has the same name
        *    as the event, that method will be invoked.
        *
        *    If you mix both of these approaches you must be careful
        *    to handle each event in only one way, or it will fire
        *    twice.
        *
        *        var myObject = {
        *            notify: function(eventName,arguments) {
        *               // this function receives all events
        *               // and must dispatch them
        *               if(eventName=='keyPress_F10') {
        *                   this.doSomething();
        *               }
        *            },
        * 
        *            keyPress_Enter: function(arguments) {
        *                // do something when Enter is hit
        *                if(whatever) {
        *                    // tell the dispatcher the event
        *                    // was not handled
        *                    return false;
        *                }
        *                else {
        *                    // tell the dispatcher we handled the
        *                    // event.  Other subscribers will still
        *                    // get it, but for keystrokes the
        *                    // default behavior is suppressed.
        *                    return true;
        *               }
        *            }
        *        }
        *
        ******
        */
        notify: function(eventName,arguments) {
            x4.debug("in u.events.notify, eventname and arguments follow");
            x4.debug(eventName);
            x4.debug(arguments);
            // Find out if anybody is listening for this event
            var subscribers = u.p(this.subscribers,eventName,{ });
            
            this.zStop = false;
            for(var id in subscribers) {
                var subscriber = u.byId(id);
                if(subscriber==null) {
                    x4.debug("The subscriber is null!");
                    continue;
                }
                
                // First possibility is a generic nofity handler
                if(typeof(subscriber.notify)=='function') {
                    x4.debug("Dispatching to "+id+" generic NOTIFY method");
                    var x = subscriber.notify(eventName,arguments);
                    if(x) this.zStop = true;
                }
                // next possibility is a specific handler
                if(typeof(subscriber[eventName])=='function') {
                    x4.debug("Dispatching to "+id+" specific method "+eventName);
                    var x = subscriber[eventName](arguments);
                    if(x) this.zStop = true;
                }
            }
            x4.debug("Returning: =="+this.zStop+"==");
            return this.zStop;
        }
    }, 

    /****O* u/dialogs
    *
    * NAME
    *   u.dialogs
    *
    * FUNCTION
    *   The two Javascript dialogs u.dialogs.alert and
    *   u.dialogs.confirm replace the Javascript native functions
    *   alert() and confirm().
    *
    *   The third dialog, u.dialogs.pleaseWait, puts up a 
    *   suitable notice if your application must do something that
    *   will take more than 1-2 seconds.
    *
    *   They are all fully modal, respond to appropriate keystrokes
    *   like 'Y', 'N', 'Enter' and 'Esc', and maintain the same
    *   style as the rest of the template.
    *
    * PORTABILITY
    *   The u.dialogs object expects your HTML to contain two
    *   invisible (display:none) divs.  One is called "dialogoverlay"
    *   and the other is called "dialogbox".  These two divs are 
    *   provided by default on Andromeda templates.  If you make
    *   your own template and include androHTMLFoot.php at the bottom
    *   then your templates will also have these divs.
    *
    *   Making a page fully modal is difficult, because if an INPUT
    *   has focus it will be possible for the user to use the keyboard
    *   to navigate around.  Therefore the code in x4 checks
    *   the u.dialogs.currentDialog property, and disallow all activity
    *   if that property is not false.  If you make your own 
    *   custom pages that are not Extended Desktop pages, you must have
    *   your input's onkeyPress methods also check this property.
    *
    *   If this object is used outside of Andromeda, you must have
    *   the file phpWait.php in your public web root, otherwise the
    *   u.dialogs.confirm function will not work.
    *
    ******
    */
    dialogs: {
        /** NO DOC **/
        id: 'u_dialogs',
        answer: null,
        json: null,
        
        /****v* dialogs/currentDialog
        *
        * NAME
        *    u.events.currentDialog   
        *
        * FUNCTION
        *    This Javascript property will hold any of the value of:
        *    * false, no dialog is active
        *    * alert, an alert dialog is active
        *    * confirm, a confirm dialog is active
        *    * pleaseWait, a "Please Wait" box is being displayed
        *
        ******
        */
        currentDialog: false,
        
        /****v* dialogs/clear
        *
        * NAME
        *   u.events.clear
        *
        * FUNCTION
        *   The Javascript Method u.events.clear
        *   clears the current modal dialog.  
        *
        *   The two dialogs u.dialogs.alert and u.dialogs.confirm are
        *   cleared by user action.  But the u.dialogs.pleaseWait 
        *   dialog will remain on the screen until your application
        *   Javascript code clears it by calling this method.
        *
        * INPUTS
        *   ignore - This method accepts a paremeter that is useful
        *   only to the framework when managing a confirm dialog.
        *
        * SOURCE
        */
        clear: function(answer) {
            this.answer = answer;
            this.currentDialog = false;
            u.events.unSuppress();
            
            // KFD 12/20/08, also notify x6 if it exists
            if(typeof(x6events)!='undefined') {
                x6.dialogsAllow = false;
                x6events.enableEvents();
            }
            
            $('#dialogbox,#dialogoverlay').css('display','none');
        },
        /******/
        
        prepare: function(type) {
            // Tell the master what we are doing, 
            // and suppress all keystrokes except ENTER and ESC
            this.currentDialog = type;

            // Suppress all events 
            u.events.suppressByPrefix('keyPress');
            
            // Put in a trap to also tell x6 not to respond
            // to keyboard events.
            if(typeof(x6events)!='undefined') {
                x6.dialogsAllow = [ ];
                x6events.disableEvents();
            }

            // Get some basic heights and widths
            var wh = $(window).height();
            var ww = $(window).width();
            
            // Make complete assignment to the overlay
            $('#dialogoverlay')
                .css('position','absolute')
                .css('top',0)
                .css('left',0)
                .css('width' ,ww)
                .css('height',wh)
                .css('background-color','black')
                .css('opacity',0)
                .css('display','')
                .css('z-index',500);

            // Get height and width of the inner guy and center him
            var ch = $('#dialogbox').height();
            $('#dialogbox').css('width',300);
            cw = 300;
            
            // Center and otherwise prepare the box
            $('#dialogbox')
                .css('position','absolute')
                .css('top' , 300)
                .css('left', (ww - cw)/2)
                .css('opacity',0)
                .css('display','')
                .css('z-index',501)
                .addClass('dialog');
            u.byId('dialogbox').notify = function(eventName,args) {
                if(u.dialogs.currentDialog == 'alert') {
                    if(eventName == 'keyPress_Enter') {
                        u.dialogs.clear();
                        return true;
                    }
                    if(eventName == 'keyPress_Esc')  {
                        u.dialogs.clear();
                        return true;
                    }
                }
                if(u.dialogs.currentDialog == 'confirm') {
                    if(eventName == 'keyPress_Y') {
                        u.events.zStop = true;
                        u.dialogs.clear(true);
                        return true;
                    }
                    if(eventName == 'keyPress_N') {
                        u.events.zStop = true;
                        u.dialogs.clear(false);
                        return true;
                    }
                }
                return false;
            }
        },
        
        /****m* dialogs/alert
        *
        * NAME
        *   u.dialogs.alert
        *
        * FUNCTION
        *   The Javascript method u.dialogs.alert replaces the native
        *   Javascript alert() function with one that is stylistically
        *   consistent with the rest of the application.
        *
        *   Unlike Javascript's native alert() function, execution 
        *   continues after you call this function.  The user must
        *   clear it by clicking the OK button, hitting Enter, or
        *   hitting Esc.
        *
        *   When this alert is active, all keyboard events are
        *   suppressed except Enter and Esc.
        *
        * EXAMPLE
        *   Here is a usage example:
        *
        *       u.dialogs.alert("New data has been saved");
        *       // maybe do some other stuff while 
        *       // waiting for the user
        *       u.events.notify('myEventName',objParms);
        *
        ******
        */
        alert: function(msg) {
            alert(msg);
            return;
            this.prepare('alert');

            // Create the content for the dialog itself
            var html =
                "<h1>Message</h1>"
                +"<p>"+msg+"</p><br/>"
                +"<center>"
                +"<a href='javascript:u.dialogs.clear()'>OK</a>"
                +"</center>"
                +"<br/>"
                +"</div>";
                
            u.events.subscribe('keyPress_Enter',u.byId('dialogbox'));
            u.events.subscribe('keyPress_Esc'  ,u.byId('dialogbox'));

            u.byId('dialogbox').innerHTML=html;
            
            // Finally, display the dialog
            $('#dialogoverlay').css('opacity',0.4);
            $('#dialogbox').css(    'opacity',1);                
        },
        
        /****m* dialogs/confirm
        *
        * NAME
        *   u.dialogs.confirm
        *
        * FUNCTION
        *   The Javascript method u.dialogs.confirm replaces the native
        *   Javascript confirm() function with one that is stylistically
        *   consistent with the rest of the application.
        *
        *   Like Javascript's native confirm() function, execution 
        *   *stops* until the user answers the question.  This makes
        *   coding far easier because you do not have to code
        *   anonymous callback functions.
        *
        *   When this alert is active, all keyboard events are
        *   suppressed except 'Y' and 'N'.
        *
        * EXAMPLE
        *   Here is a usage example:
        *
        *       if(u.dialogs.confirm("Do you really want to delete?")) {
        *           // code to delete
        *       }
        *       else {
        *           u.events.debug("user chose not to delete");
        *       }
        *
        * PORTABILITY
        *    Javascript does not natively support an elegant way to
        *    pause execution.  For instance, it does not have a 
        *    "sleep" function that would allow a low-CPU idefinite
        *    loop to be executing while waiting for user input.
        *
        *    We could solve this by throwing caution to the wind and
        *    doing an indefinite loop anyway, which checks over and over
        *    to see if the user has responded, but this spikes CPU usage
        *    and is very bad form.
        *
        *    The technique used by u.dialogs.confirm is unusual, but it
        *    has the benefit of being extremely low on CPU power and
        *    extremely low on network bandwidth.  The approach contains
        *    an indefinite loop that makes a call to the program
        *    phpWait.php, which does a sleep for 1/4 second and returns.
        *    Even at four calls per second, the overall CPU and network
        *    bandwidth is practically zero.
        *
        *    Therefore, u.dialogs.confirm has a dependency that the
        *    php file phpWait.php be present in the web server's public
        *    root.  This is handled automatically by Andromeda, but you
        *    must provide such a file if you use this object in 
        *    a non-Andromeda application.
        *
        ******
        */
        confirm: function(msg,options) {
            return confirm(msg);
            
            this.prepare('confirm');
            u.events.subscribe('keyPress_Y'  ,u.byId('dialogbox'));
            u.events.subscribe('keyPress_N'  ,u.byId('dialogbox'));
            

            // Create the content for the dialog itself
            var html =
                "<h1>Please Confirm:</h1>"
                +"<p>"+msg+"</p><br/>"
                +"<center>"
                +"<a href='javascript:u.dialogs.clear(true)'>"
                +"&nbsp;&nbsp;Yes&nbsp;&nbsp;</a>"
                +"&nbsp;&nbsp;&nbsp;"
                +"<a href='javascript:u.dialogs.clear(false)'>"
                +"&nbsp;&nbsp;No&nbsp;&nbsp;</a>"
                +"</center>"
                +"<br/>"
                +"</div>";

            u.byId('dialogbox').innerHTML=html;
            
            
            // Finally, display the dialog
            $('#dialogoverlay').css('opacity',0.4);
            $('#dialogbox').css(    'opacity',1);
            
            // For this guy we need to make sure there
            // is a request object
            if(this.json!=null) {
                this.json.abort();
            }
            else {
                var browser = navigator.appName;
                if(browser == "Microsoft Internet Explorer"){
                    this.json = new ActiveXObject("Microsoft.XMLHTTP");
                }else{
                    this.json = new XMLHttpRequest();
                }
            }
            
            // The loop sends a request to the server,
            // which sleeps for 250ms, then comes back.
            // This gives us pretty good response w/o
            // chewing up the CPU
            while(true) {
                this.answer = null;
                this.json.open('POST' , 'phpWait.php', false);
                this.json.send(null);
                
                if(this.answer!=null) break;
            }
            return this.answer;
        },
        
        /****m* dialogs/pleaseWait
        *
        * NAME
        *   u.dialogs.pleaseWait
        *
        * FUNCTION
        *   The Javascript method u.dialogs.pleaseWait is not,
        *   strictly speaking, a dialog, because it does not require
        *   any user feedback, and in fact does not even allow it.
        *
        *   When you call u.dialogs.pleaseWait, a modal box pops up
        *   that is stylistically consistent with the overall template
        *   and which has an animated gif and the message "Please Wait".
        *
        *   Use this method when you are executing a long-running
        *   (greater than 2-3 seconds) process and you must let the
        *   user know the program is working on something.
        *
        *   The user cannot clear this display.  You must clear it
        *   yourself when work has been completed by calling
        *   u.dialogs.clear().
        *
        *  EXAMPLE
        *    Here is a usage example:
        *
        *         u.dialogs.pleaseWait();
        *         for(var x in rowsToSave()) {
        *            // some actions to save to server
        *         }
        *         u.dialogs.clear();
        *  
        ******
        */
        pleaseWait: function(msg) {
            if(msg==null) msg = "Please Wait...";
            this.prepare('pleaseWait');
            
            // Create the content for the dialog itself
            var html =
                "<center><br/>"
                +"<img src='clib/ajax-loader.gif'>"
                +"<br/><br/>"
                +msg+"<br/><br/>"
                +"</center>";

            u.byId('dialogbox').innerHTML=html;
            
            // Finally, display the dialog
            $('#dialogoverlay').css('opacity',0.4);
            $('#dialogbox').css(    'opacity',1);
        }
    },
    
    /* KFD 11/26/08
       EXPERIMENTAL
       Put here because there is already one in ua, where it
       really does not belong.
       Originally used by x6inputs.keydown to figure things out
    */
    metaKeys: {
        8: 'BackSpace',
        9: 'Tab',
        13: 'Enter',
        16: 'Shift',
        17: 'Ctrl',
        18: 'Alt',
        20: 'CapsLock',
        27: 'Esc',
        33: 'PageUp',
        34: 'PageDown',
        35: 'End',
        36: 'Home',
        37: 'LeftArrow',
        38: 'UpArrow',
        39: 'RightArrow',
        40: 'DownArrow',
        45: 'Insert',
        46: 'Delete',
        112: 'F1' ,
        113: 'F2' ,
        114: 'F3' ,
        115: 'F4' ,
        116: 'F5' ,
        117: 'F6' ,
        118: 'F7' ,
        119: 'F8' ,
        120: 'F9' ,
        121: 'F10',
        122: 'F11',
        123: 'F12'
    },
    keyLabel: function(e) {
        // if e.originalEvent is defined, this is a jQuery event.
        // jQuery events have charCode for non-meta keys, and they
        // shift the alphabet up by 32 characters for no good reason
        // at all.  
        if(e.originalEvent) {
            if(e.charCode >= 97 && e.charCode <= 122) {
                var x = e.charCode - 32;
            }
            else if(e.charCode != 0) {
                var x = e.charCode
            }
            else {
                var x = e.keyCode;
            }
        }
        else {
            var x = e.keyCode;
        }
        
        x4Keys = this.metaKeys;
    
        // If they hit one of the control keys, check for
        // Shift, Ctrl, or Alt
        var retval = '';
        if(typeof(x4Keys[x])!='undefined') {
            retval = x4Keys[x];
            if(e.ctrlKey)  retval = 'Ctrl'  + retval;
            if(e.altKey)   retval = 'Alt'   + retval;
            if(e.shiftKey) retval = 'Shift' + retval;
            return retval;
        }
        
        // If letters we look at shift key and return
        // upper or lower case
        if(x >= 65 && x <= 90) {
            if(e.shiftKey || e.ctrlKey || e.altKey) {
                var letters = 
                    [ 'A', 'B', 'C', 'D', 'E', 'F', 'G',
                      'H', 'I', 'J', 'K', 'L', 'M', 'N',
                      'O', 'P', 'Q', 'R', 'S', 'T', 'U',
                      'V', 'W', 'X', 'Y', 'Z' ];
            }
            else {
                var letters = 
                    [ 'a', 'b', 'c', 'd', 'e', 'f', 'g',
                      'h', 'i', 'j', 'k', 'l', 'm', 'n',
                      'o', 'p', 'q', 'r', 's', 't', 'u',
                      'v', 'w', 'x', 'y', 'z' ];
            }
            if(e.ctrlKey)  retval = 'Ctrl'  + retval;
            if(e.altKey)   retval = 'Alt'   + retval;
            var retval = letters[x - 65];
        }
        
        // Numbers or the corresponding codes go here
        if(x >= 48 && x <= 57) {
            if(e.shiftKey) {
                var numbers = [ ')','!','@','#','$','%','^','&','*','(' ];
            }
            else {
                var numbers = [ '0','1','2','3','4','5','6','7','8','9' ];
            }
            var retval = numbers[x - 48];
            return retval;
        }
        if(retval!='') {
            if(e.ctrlKey)  retval = 'Ctrl'  + retval;
            if(e.altKey)   retval = 'Alt'   + retval;
            if(e.shiftKey) retval = 'Shift' + retval;
            return retval;            
        }
        
        var lastChance = {
            192: '`',
            109: '-',
            61:  '=',
            219: '[',
            221: ']',
            220: '\\',
            188: ',',
            190: '.',
            191: '/',
            59:  ';',
            222: "'"
        }
        if(typeof(lastChance[x])!='undefined') {
            if(e.shiftKey) {
                var lastChance = {
                    192: '~',
                    109: '_',
                    61:  '+',
                    219: '{',
                    221: '}',
                    220: '|',
                    188: '<',
                    190: '>',
                    191: '?',
                    59:  ':',
                    222: '"'
                }
            }
            var retval = lastChance[x];
            return retval;
        }
        // otherwise put on any prefixes and return
        return retval;
    },
    
    keyIsNumeric: function(e) {
        var keyLabel = this.keyLabel(e);
        var numbers = [ '0','1','2','3','4','5','6','7','8','9' ];
        return numbers.indexOf(keyLabel)>=0;
    },
    
    keyIsMeta: function(e) {
        var code = e.keyCode || e.charCode;
        return typeof(this.metaKeys[code])!='undefined';
    }
    
}


/* ---------------------------------------------------- *\

   SECTION 6: Andromeda Code not part of au

   
\* ---------------------------------------------------- */
var aSelect = new Object();
aSelect.divWidth = 400;
aSelect.divHeight= 300;
aSelect.div      = false;
aSelect.iframe   = false;
aSelect.row      = false;
aSelect.hasFocus = false;

// Main routine called when a keystroke is hit on 
// the control that "hosts" the androSelect
function androSelect_onKeyUp(obj,strParms,e) {
    var kc = e.keyCode;
    if(e.ctrlKey || e.altKey) return;
    
    // KFD 7/11/08.
    // Prevent popup on shift, ctrl or alt.  Specifically
    // this is what alerted me to this.  If the user
    // does the following:
    //    hold down SHIFT
    //    hit TAB
    //    release TAB
    //    release SHIFT -- that's when this fires, we want
    //                   to prevent that
    var klabel = $a.label(e);
    //if(klabel=='Shift') return;
    if(klabel=='Alt')   return;
    if(klabel=='Ctrl')  return;
    
    // KFD 7/11/08.  If user has cleared the contents of
    //               the input, kill the box
    if(obj.value.trim()=='') {
        androSelect_hide();
        return;
    }

    // If TAB or ENTER, clear the box
    if(kc == 9 || kc == 13) { return true; }
    
    // If downarrow or uparrow....
    if(kc == 38 || kc == 40) {
        if(!androSelect_visible()) return;
        if(aSelect.div.firstChild.rows.length==0) return;

        if(!aSelect.row) { 
            var row = aSelect.div.firstChild.rows[0];
            var skey= objAttValue(row,'x_skey');
            androSelect_mo(row,skey);
            $('#androSelect').scrollTo(row);
            return;
        }
        
        var row = byId('as'+aSelect.row);
        if(kc==38) {
            var prev = objAttValue(row,'x_prev');
            if(prev!='') {
                var row = byId('as'+prev);
                androSelect_mo(row,prev);
                $('#androSelect').scrollTo(row);
            }
        }
        if(kc==40) {
            var next = objAttValue(row,'x_next');
            if(next!='') {
                var row = byId('as'+next);
                androSelect_mo(row,next);
                $('#androSelect').scrollTo(row);
            }
        }
        
        // No matter what, never proceed if it was up/down arrow
        return;
    }
    
    // This is used to track the last value we searched for
    if(typeof(obj.androSelect == 'undefined')) {
        obj.androSelect = '';
    }
    // Now test to see if the value has changed.  If not, return 
    if(obj.androSelect == obj.value) {
        return;
    }
    // From this point forward we are going to do a search,
    // because the user has changed the value of the input
    
    // If no DIV, set one up.
    if(!aSelect.div) {
        aSelect.div = document.createElement('DIV');
        aSelect.div.style.display  = 'none';
        aSelect.div.style.width    = aSelect.divWidth + "px";
        aSelect.div.style.height   = aSelect.divHeight+ "px";
        aSelect.div.style.position = 'absolute';
        aSelect.div.style.backgroundColor = 'white';
        aSelect.div.style.overflow = 'scroll';
        aSelect.div.style.border="1px solid black";
        //aSelect.div.className = 'androSelect';
        aSelect.div.id = 'androSelect';
        document.body.appendChild(aSelect.div);
        var x = document.createElement('TABLE');
        aSelect.div.appendChild(x);
    }
    // If it is invisible, position it and then make it visible
    if(aSelect.div.style.display=='none') {
        var position = $(obj).offset();
        var postop = position.top;
        var poslft = position.left;
        //var objpar = obj;
        //while((objpar = objpar.offsetParent) != null) {
        //    postop += objpar.offsetTop;
        //    poslft += objpar.offsetLeft;
        // }
        aSelect.div.style.top  = (postop + obj.offsetHeight +1) + "px";
        aSelect.div.style.left = poslft + "px";
        aSelect.div.style.display = 'block';
        aSelect.hasFocus = false;
        
        // As part of making visible, create an onclick
        // that will trap the event target and lose focus
        // if not the input object or the
        //addEventListener(document   ,'click',androSelect_documentClick);
    }
    
    // Tell it the current control it is working for
    aSelect.control = obj;
    aSelect.row = false;

    // KFD 7/21/08, add matches if present
    var matchurl = '' 
    var matchcols = $a.p(obj,'xMatches','');
    if(matchcols!='') {
        // WARNING!  x4 Assumed!
        var acols = matchcols.split(',');
        for(var cidx in acols) {
            var colname = acols[cidx];
            var colval  = $('[xcolumnid='+colname+']')[0].value.trim();
            matchurl+='&match_'+colname+"="+encodeURIComponent(colval);
        }
    }
    
    // Make up the URL and send the command
    var url = '?'+strParms+matchurl
        +'&gpv=2&gp_letters='+obj.value.replace(" ","+");         
    andrax(url,androSelect_handler);
}

function androSelect_handler() {
    // do default action
    handleResponse();
    
    if(aSelect.div.firstChild) {
        var table = aSelect.div.firstChild;
        if(table.rows.length > 0) { 
            table.rows[0].onmouseover();
            aSelect.hasFocus = false;
        }
    }    
}

function androSelect_onKeyDown(e) {
    var kc = e.keyCode;

    // If TAB or ENTER, clear the box
    if(kc == 9 || kc == 13) { 
        if(!androSelect_visible()) return true;
        
        if(aSelect.div.firstChild.rows.length==0) {
            androSelect_hide();
           return true;
        }
        
        if(aSelect.row) {
            var row = byId('as'+aSelect.row);
            var pk  = objAttValue(row,'x_value');
            aSelect.control.value = pk;
            u.events.notify('assigned_'+aSelect.control.id,pk);
        }
        androSelect_hide();
        return true;
    }
}


// Make the div go away.  Actually choosing a value
// is done elsewhere
function androSelect_hide() {
    if(aSelect.div) {
        aSelect.div.innerHTML = ''
        aSelect.div.style.display = 'none';
    }
}

// Make the div go away.  Actually choosing a value
// is done elsewhere
function androSelect_onBlur() {
    if(aSelect.hasFocus) {
        return;
    } 
    else {
        androSelect_hide();
    }
}


function androSelect_visible() {
    if(aSelect.div == false) return false;
    if(aSelect.div.style.display=='none') return false;
    
    return true;
}

// Main purpose is to see if user clicked anywhere except
// on current control or the div.  If they did, hide it
// w/o making a choice.
function androSelect_documentClick(e) {
    androSelect_hide();
    return false;
}

// User is rolling over a row
function androSelect_mo(tr,skey) {
    if(byId('as'+aSelect.row)) {
        byId('as'+aSelect.row).className = '';
    }
    aSelect.row = skey;
    tr.className = 'lightgray';
    aSelect.hasFocus = true;   
}
function androSelect_moout(tr,skey) {
    aSelect.hasFocus = false;   
}

// User clicked on a row
function androSelect_click(value,suppress_focus) {
    aSelect.control.value = value;
    u.events.notify('assigned_'+aSelect.control.id,value);
    androSelect_hide();
    if(suppress_focus==null) {
        aSelect.control.focus();
    }
    return false;
}


/****O* Javascript-API/ua
*
* NAME
*   ua
*
* FUNCTION
*   The Javascript ua object provides Andromeda-specific 
*   utilities.  This object is used to communicate with the
*   web server, and it also contains parsed results that
*   have been received from the server.
*
*   Compare ua to the Javascript u object, which
*   contains general purpose utilities that do not depend
*   upon or expect other Andromeda objects to be present.
*
*   The Javascript ua object is present on all HTML sent to
*   the browser and can be used on any custom page you write.
*
* PORTABILITY
*   The Javascript ua object is not meant to be used outside
*   of Andromeda.  Specifically it is hardcoded to expect
*   an Andromeda web-server.  
*
*   The ua object also depends on the u object, and it expects
*   jQuery to be available.
******
*/
window.a = window.ua = window.$a = {
    /****v* ua/data
    *
    * NAME
    *   ua.data
    *
    * FUNCTION
    *   The javascript object ua.data contains data that has been
    *   sent to the browser by the web server.  Such data can
    *   be sent in a normal HTML page or in a JSON call.
    *
    *   The data that is sent by PHP is JSON encoded, so it can 
    *   conceivably contain numeric arrays, associative arrays, objects,
    *   and even objects with javascript code.
    *
    * NOTES
    *   The Andromeda framework uses this mechanism in its own code,
    *   so your application code should not send data with the
    *   following names:
    *
    *   * row - used by the Framework for sending back individual rows
    *   * dd.* - used by the Framework for sending data dictionaries
    *   * fetch - used to return FETCH values when the user changes
    *     a foreign key during editing.
    *   * init
    *   * x4Mode - reserved for b/w compatibility
    *   * x4Focus - reserved for b/w compatiblity
    *   * returnto - specifies where the page should go when the user
    *     exits.  No value means go back to menu, "exit" means close
    *     the tab.
    *
    * EXAMPLE
    *   If you have PHP code that must return a set of rows
    *   the the browser, you can use this command:
    *
    *       <?php
    *       $sql = "Select * from states";
    *       $rows = sql_allRows($sql);
    *       x4Data('states',$rows);
    *       ?>
    *
    *   And then in script you will find this data here:
    *
    *       <script>
    *       var states = ua.data.states;
    *       for(var idx in states) {
    *           .....
    *       }
    *       </script>
    *******
    */
    data: { 
       dd: {} 
    },
     
    /** NO DOC **/
    /** DEPRECATED **/
    returnto: '',
    
    /** NO DOC **/
    /** DEPRECATED **/
    /** Was moved to u, because it is independent of Andromeda **/
    bb: {
        vars: { },
        stick: function(varName,value) {
            this.vars[varName] = value;
        },
        grab: function(varName,defValue) {
            return $a.p(this.vars,varName,defValue);
        }
    },
    
    /** NO DOC **/
    /** DEPRECATED **/
    openWindow: function(url) {
        $a.window = window.open(url);
    },
     
    /** NO DOC **/
    /** DEPRECATED **/
    /** Was moved to u, because it is independent of Andromeda **/
    dialogs: {
        alert: function(msg) {
            alert(msg);
        },
        confirm: function(msg) {
            return confirm(msg);
        },
        alertx: function($msg, $title) {
           if ( $title == null ) {
               $title = 'Alert';
           }
           
           // Center the modal dialog horizontally
           var $winwid = $(window).width();
           var $boxwid = 300;
           if($winwid < 300) { $boxwid = $winwid; }
           var $boxleft= parseInt(($winwid - $boxwid)/2);
           $(".jqmWindow").css("width",$boxwid+"px").css("left",$boxleft);
           
           $('#jqmTitle').html($title);
           $('#jqmMessage').html('<p>' + $msg  
               +'</p><br/><p align="center">'
               +'<input type="button" name="OK" value="OK" class="jqmClose" '
               +'   style="font-weight:bold;text-align:center;'
               +'cursor:pointer;" /></p>');
           $('#jqmModal').jqm({modal:true,overlay:75,onHide:jqModalClose,onShow:jqModalOpen}).jqmShow();
        },
        confirm: function(msg) {
            return confirm(msg);
        }
    }, 
    
    /*
     * Sub object for doing things on a detail form
     * like fetch values, recalculate and so forth
     *
     */
    forms: {
        fetch: function(table,column,value,obj,inp) {
            // KFD 9/7/08   If there is an androSelect visible,
            //              don't do this.
            if(aSelect) {
                if(aSelect.hasFocus && aSelect.div.style.display != 'none') {
                    return;
                }
            }
            // KFD 9/7/08   Hack.  Make sure he goes away if we
            //              are fetching.
            androSelect_hide();
            
            
            // KFD 7/30/08, detect if this value matches
            //              a "pre" value that was sent in,
            //              and if so, fire no matter what
            //              otherwise fire only on change
            var go = false;
            var column = u.p(inp,'xColumnId');
            if(typeof(a.data.init)!='undefined') {
                if(typeof(a.data.init[column])!='undefined') {
                    delete a.data.init[column];
                    go = true;
                }
            }
            if(! go) {
                var valold = u.p(inp,'xValue','');
                if(value.trim() == valold.trim()) return;
            }
            
            // MAJOR BUG: KFD 8/22/08.  This line is evil.  It causes
            //       the browser to think the value was not changed, and
            //       so the browser does not try to save it, and you 
            //       can figure out the awful calls you get after that.
            //inp.xValue = value;
            
            $a.json.init('x4Action','fetch');
            $a.json.reportErrors = false;
            $a.json.addParm('x4Page',table);
            $a.json.addParm('column',column);
            $a.json.addParm('value',value);
            if($a.json.execute()) {
                $a.json.process();
                for(var idx in $a.data.fetch) {
                    var retval = $a.data.fetch[idx];
                    var inputs = $(obj).find("[xcolumnid="+idx+"]");
                    if(inputs.length > 0) {
                        inputs[0].value = retval;
                    }
                }
            }
        }
    },

    /****M* Javascript-API/AJAX
    *
    * NAME
    *   AJAX
    *
    * FUNCTION
    *   In Andromeda, all Ajax requests are handled by the
    *   ua.json object.  We do not use the term AJAX because
    *   it is not a correct technical description.  We use
    *   term "JSON", as in 'make a JSON call to the server'.
    *
    *   Specifically, Andromeda PHP pages return JSON data
    *   instead of XML (hence no "X" in AJA"X"), and many calls
    *   are actually synchronous (hence no "A"synchronous
    *   in AJAX).
    *
    *   For a complete reference, see $a.json.
    ******
    */
    
    /****O* ua/json
    *
    * NAME
    *   ua.json
    *
    * FUNCTION
    *   The Javascript object ua.json is used to send any request
    *   to the web server, either for a new complete page or for
    *   data and HTML fragments.
    *   Examples include requesting a single row from a table,
    *   requesting search results, fetching HTML fragments, or
    *   popping up a new window.
    *
    *   Andromeda uses the term JSON instead of AJAX because 
    *   the term AJAX does not describe how Andromeda works.
    *   Specifically, Andromeda PHP pages return JSON data
    *   instead of XML (hence no "X" in AJA"X"), and many calls
    *   are actually synchronous (hence no "A" in "A"JAX).
    *
    *   The ua.json object is always present on all pages, and
    *   you can use it in Javascript code on any custom page.
    *
    * NOTES
    *   Andromeda handles all of the values returned in the
    *   request automatically.  On custom pages you do not have
    *   to code a response handler because Andromeda handles
    *   the response for you.
    *
    *   The PHP code that handles a JSON request sends data back
    *   by using the routines x4Debug, x4Data, x4HTML, x4Script,
    *   x4Error.  While the complete PHP-API documentation on
    *   those functions will give you most of what you need, we
    *   must note here how the returned data is handled:
    *   * x4Debug - ignored, but you can examine the results in
    *     firebug.
    *   * x4Error - any errors sent back by this function are reported
    *     to the user and ua.json.execute returns false.
    *   * x4HTML - calls to this function in PHP code provide an 
    *     element Id and a fragment of HTML.  The HTML replaces the
    *     innerHTML of the specific item.
    *   * x4Data - calls to this function in PHP code provide a name
    *     and some data value (including arrays and objects).  The
    *     result can be examined when the call completes in ua.data.<name>.
    *   * x4Script - provides script that should execute on the browser
    *     when the call returns.
    *
    * EXAMPLE
    *
    *   The basic usage of ua.json is to initialize a call 
    *   with ua.json.init, and then to add parameters with
    *   ua.json.addParm, and finally to execute and process the
    *   call with ua.json.execute and ua.json.process.
    *
    *   There are also special-purpose methods like ua.json.inputs
    *   that will take all of the inputs inside of an object and
    *   add them to the request.
    *
    *   You can also use the function ua.json.windowLocation to
    *   execute the call as a new page request, and ua.json.newWindow
    *   to execute the call as a new page request in a tab.
    *  
    *      <script>
    *      // Initialize the call
    *      ua.json.init('x4Page','myCustomPage'); 
    *      // Name the server-side PHP method to call
    *      ua.json.addParm('x4Action','getSomething'); 
    *      // Add some parms
    *      ua.json.addParm('parm1','value');
    *      ua.json.addParm('parm2','value');
    *      // Execute and process in one step.  Note that this
    *      // is synchronous, there is no need for a callback
    *      // function.
    *      ua.json.execute(true);
    *     
    *      for(var x in $a.data.returnedStuff) {
    *        ....
    *      }
    *      </script>
    *
    *  This call requires an Extended-Desktop page to be defined
    *  in PHP that will service the request.  A super-simple example
    *  is here, more information is provided in the
    *  Extended-Desktop documentation.
    *
    *      <?php
    *      # This is file application/x4MyCustomPage.php
    *      class x4MyCustomPage extends androX4 {
    *          # this function handles the call given above
    *          function getSomething() {
    *               $parm1 = gp('parm1');
    *               $parm2 = gp('parm2');
    *               $sql = "Select blah blah blah";
    *               $rows = SQL_AllRows($sql);
    *               x4Data('returnedStuff',$rows
    *          }
    *      }
    *      ?>
    *
    *   Sometimes you make a call that returns replacement HTML
    *   for a single object.  In this case your PHP code supplies
    *   the HTML by calling x4HTML with the value of '*MAIN*' for
    *   the first parameter, as in x4HTML('*MAIN*',$html);
    *   Such a call is handled this way in script:
    *
    *      <script>
    *      ua.json.init('x4Page','myCustomPage');
    *
    *      // We need the conditional in case the server returns
    *      // an error and we should not replace the html
    *      if(ua.json.execute()) {
    *         ua.json.process('nameofItemToReplace');
    *      }
    *      </script>
    *
    ******
    */
    json: {
        callString: '',
        http:       false,
        active:     false,
        jdata:      { },
        data:       { dd: {} },
        requests:   { },
        parms:      { },
        reportErrors: true,
        x4Page:     '',
        x4Action:   '',
        explicitParms: '',
        hadErrors: false,
        
        /****m* json/init
        *
        * NAME
        *   ua.json.init
        *
        * FUNCTION
        *   The Javascript method ua.json.init initiates a new 
        *   JSON request.
        *
        *   Optionally you can pass two inputs and eliminate one
        *   call to ua.json.addParm.
        *
        * INPUTS
        *   string - if provided, a parameter name
        *   mixed - if provided, the value for the parameter
        *
        * EXAMPLE
        *   Here are two examples for initiating a JSON request
        *
        *      <script>
        *      // The short way
        *      ua.json.init('x4Page','myCustomPage');
        * 
        *      // Passing w/o parameters requires at least one
        *      // call to ua.json.addParm.
        *      ua.json.init();
        *      ua.json.addParm('x4Page','myCustomPage');
        *      </script>
        *
        * SOURCE
        */
        init: function(name,value) {
            this.x4Page     = '';
            this.x4Action   = '';
            this.callString = '';
            this.parms      = { };
            this.reportErrors=true;
            this.explicitParms= '';
            if(name!=null) {
                this.addParm(name,value);
            }
        },
        /******/

        /****** json/addParm
        *
        * NAME
        *   ua.json.addParm
        *
        * FUNCTION
        *   The Javascript method ua.json.addParm adds one parameter
        *   to a JSON call previously initiated with ua.json.init.
        *
        * INPUTS
        *   string - required, a parameter name
        *   mixed - required, the value for the parameter
        *
        * EXAMPLE
        *   Here are two examples for initiating a JSON request
        *
        *      <script>
        *      ua.json.init();
        *      // Name the server-side page to call
        *      ua.json.addParm('x4Page','myCustomPage');
        *      // Name the server-side method to call
        *      ua.json.addParm('x4Action','fetchSomething');
        *      </script>
        *
        * SOURCE
        */
        addParm: function(name,value) {
            this.parms[name] = value;
            if(name=='x4Page')   this.x4Page = value;
            if(name=='x4Action') this.x4Action = value;
        },
        /******/
        
        makeString: function() {
            if(this.explicitParms!='') {
                return this.explicitParms;
            }
            var list = [ ];
            for(var x in this.parms) {
                list[list.length] = x + "=" +encodeURIComponent(this.parms[x]);
            }
            return list.join('&');
        },
        //addValue: function(name,value) {
        //    if(this.callString!='') this.callString+="&";
        //    this.callString += 'x4c_' + name + '=' + encodeURIComponent(value);
        //},
        
        /****** json/inputs
        *
        * NAME
        *   ua.json.inputs
        *
        * FUNCTION
        *   The Javascript method ua.json.inputs adds inputs to
        *   a JSON call previously initiated with ua.json.init.
        *
        *   This method accepts an object as its parameter, and
        *   will add every input that is a child (at any level)
        *   of that object.
        *
        *   This method uses the "id" property of the input to
        *   name the parameter, not the "name" property.  Andromeda
        *   makes no use of the "name" property.
        *
        *   This method is equivalent to use ua.json.addParm
        *   for each of the desired inputs.
        *
        *   Checkboxes receive special treatment.  If the box is 
        *   checked a value of 'Y' is sent, and if the box is not
        *   checked a value of 'N' is sent.
        *
        *   The name of each parameter is normally the Id of the
        *   input.  If the inputs were generated by Andromeda
        *   on an Extended-Desktop page, they will have the names
        *   'x4inp_<tableId>_<columnId>.  
        *
        * INPUTS
        *   object - optional, the object to recurse.  You must
        *   pass the object itself, not its Id.  If no object is
        *   passed the Extended-Desktop top-level object x4Top
        *   is used, which means you get every input on the page,
        *   whether or not it is visible or
        *
        *   direct - a special flag that says to name the parameters
        *   'x4c_<columnId>'.  This is required when you are sending
        *   Direct-Database-Access calls.
        *
        *
        * SOURCE
        */
        inputs: function(obj,direct) {
            if(direct==null) direct=false;
            if(obj==null) {
                if(u.byId('x4Top')!=null) {
                    obj = u.byId('x4Top');
                }
                else {
                    obj = $('.x6main')[0];
                }
            }
            if(typeof(obj)=='string') {
                if(obj.indexOf('input')==-1) {
                    var jqObjects = $(obj).find(':input');
                }
                else {
                    var jqObjects = $(obj);
                }
            }
            else {
                var jqObjects = $(obj).find(":input");
            }
            jqObjects.each( function() {
                if(direct) 
                    var id = 'x4c_'+u.p(this,'xColumnId');
                else
                    var id = this.id;
                    
                
                if(this.type=='checkbox') {
                    if(this.checked) {
                        $a.json.addParm(id,'Y');
                    }
                    else {
                        $a.json.addParm(id,'N');
                    }
                }
                else {
                    if(typeof(x6)=='undefined') {
                        if(this.value!='') {
                            $a.json.addParm(id,this.value);
                        }
                    }
                    else {
                        var zOrig = u.p(this,'zOriginalValue','').trim();
                        if(this.value.trim()!=zOrig) {
                            $a.json.addParm(id,this.value);
                        }
                    }
                }
            });
        },
        /******/
        
        /****** json/serialize
        *
        * NAME
        *   ua.json.serialize
        *
        * FUNCTION
        *   The Javascript method ua.json.serialize takes a
        *   Javascript Object or Array and serializes it and
        *   adds the values to a JSON request previously
        *   initialized with ua.json.init.
        *
        *   This method accepts an object as its parameter.
        *
        *   When you call this function, the parameters sent
        *   back take the form of an associative array.
        *
        * INPUTS
        *   prefix - The base name of the parameter
        *
        *   object - the object to serialize.
        *
        * EXAMPLE
        *   Consider the following object that is serialized
        *
        *      <script>
        *      var x = {
        *         parm1: [ 1, 2, 3],
        *         parm2: 'hello',
        *         parm3: {
        *             x: 5,
        *             y: 10,
        *         }
        *      ua.json.init('x4Page','myCustomPage');
        *      ua.json.addParm('x4Action','serialHandler');
        *      ua.json.serialize('example',x);
        *      <script>
        *
        *   Then on the server, you can grab the "example" parameter
        *   and you will get the following associative array:
        *
        *      <?php
        *      # this is file x4myCustomPage.php
        *      class x4myCustomPage extends androX4 {
        * 
        *          # this handles the 'x4Action' specified above
        *          function serialHandler() {
        *              $example = gp('example');
        *            
        *              # ...the following code shows how 
        *              #    the values that are in x4
        *              $example['parm1'][0] = 1;
        *              $example['parm1'][1] = 2;
        *              $example['parm1'][2] = 3;
        *              $example['parm2'] = 'hello';
        *              $example['parm3']['x'] = 5;
        *              $example['parm3']['y'] = 10;
        *          }
        *      }
        *      ?>
        *
        * SOURCE
        */
        serialize: function(prefix,obj) {
            for(var x in obj) {
                if(typeof(obj[x])=='object') {
                    this.serialize(prefix+'['+x+']',obj[x]);
                }
                else {
                    this.addParm(prefix+'['+x+']',obj[x]);
                }
            }
        },
        /******/
        
        /****** json/windowLocation
        *
        * NAME
        *   ua.json.windowLocation
        *
        * FUNCTION
        *   The Javascript method ua.json.windowLocation takes a
        *   JSON request and executes it as a page request.
        *
        * EXAMPLE
        *   The following example loads a new page
        *
        *      <script>
        *      ua.json.init('x4Page','calendar');
        *      ua.json.windowLocation();
        *      </script>
        *
        * SOURCE
        */
        windowLocation: function() {
            var entireGet = 'index.php?'+this.makeString()
            window.location = entireGet;
        },
        /******/
        
        /****** json/newWindow
        *
        * NAME
        *   ua.json.newWindow
        *
        * FUNCTION
        *   The Javascript method ua.json.newWindow takes a
        *   JSON request and executes it as a page request, popping
        *   the result up in a new tab or window.
        *
        *   When the user exits the resulting tab or window, it
        *   will close.  
        *
        * EXAMPLE
        *   The following example loads a new page
        *
        *      <script>
        *      ua.json.init('x4Page','calendar');
        *      ua.json.newWindow();
        *      </script>
        *
        * SOURCE
        */
        newWindow: function() {
            var entireGet = 'index.php?'+this.makeString()+'&x4Return=exit';
            $a.openWindow(entireGet);
        },
        /******/

        /****** json/executeAsync
        *
        * NAME
        *   ua.json.executeAsync
        *
        * FUNCTION
        *   By default Andromeda sends JSON requests synchronously,
        *   which is more appropriate for business database applications
        *   than asynchronous requests.
        *
        *   There are however some times when you do not want the user
        *   to wait, and so you can make asynchronous calls. 
        *
        *   Andromeda does not make use of response handlers, see the
        *   above section on ua.json for more details.
        *
        * SOURCE
        */
        executeAsync: function() {
            this.execute(true,true);
        },
        /******/
        
        /****** json/execute
        *
        * NAME
        *   ua.json.execute
        *
        * FUNCTION
        *   The Javascript method ua.json.execute sends a request to
        *   the server that has been initialized with ua.json.init
        *   and has received parameters with any of ua.json.addParm,
        *   ua.json.inputs and ua.json.serialize.
        *
        *   In normal usage, you call this routine and check for
        *   a return value of true.  If the routine returns true
        *   you call ua.json.process to process the returned
        *   results.
        *
        * RESULTS
        *   This routine returns true if the server reports no 
        *   errors.
        *
        *   If the server reports errors, they are displayed to the
        *   user using u.dialogs.alert, and this routine returns
        *   false.
        *
        *******
        */
        execute: function(autoProcess,async) {
            this.hadErrors = false;
            if(async==null) async = false;
            if(autoProcess==null) autoProcess=false;
            
            // Create an object
            var browser = navigator.appName;
            if(browser == "Microsoft Internet Explorer"){
                // KFD 11/24
                var http = new ActiveXObject("Microsoft.XMLHTTP");
            }
            else {
                // KFD 11/24
                var http = new XMLHttpRequest();
            }
            // KFD 7/8/08, When the user is clicking on
            //             search boxes, they can click faster
            //             than we can get answers, so if
            //             we notice we are running an action
            //             that is already in progress, we
            //             cancel the earlier action.
            var key = this.x4Page + this.x4Action;
            if( typeof(this.requests[key])!='undefined') {
                this.requests[key].abort();
            }
            this.requests[key] = http;
            
            // If async, we have to do it a little differently
            // KFD 11/24, did nothing yet for async
            if(async) {
                http.onreadystatechange = function() {
                    if(this.readyState!=4) return;
                    $a.json.processPre(this,key,false);
                    $a.json.process();
                }
            }
            
            // Execute the call
            var entireGet = 'index.php?json=1&'+this.makeString();
            http.open('POST' , entireGet, async);
            http.send(null);

            // KFD 11/24A
            this.active = false;
            
            
            // An asynchronous call now exits, but a
            // synchronous call continues            
            if (async) return;
            else return this.processPre(http,key,autoProcess);
            
        },
        
        processPre: function(http,key,autoProcess) {
            // Attempt to evaluate the JSON
            try {
                eval('this.jdata = '+http.responseText);
            }
            catch(e) { 
                $a.dialogs.alert("Could not process server response!");
                if(u.byId('x6Log')) {
                    u.byId('x6Log').innerHTML = http.responseText;
                    u.byId('x6Log').style.display='block';
                }
                return false;
            }
            
            // KFD 7/8/08, additional housekeeping, throw away
            //             references to the object  
            delete this.requests[key];
            delete http;

            // If there were server errors, report those
            if(this.jdata.error.length>0 && this.reportErrors) {
                this.hadErrors = true;
                $a.dialogs.alert(this.jdata.error.join("\n\n"));
                return false;
            }
            if(this.jdata.notice.length>0 && this.reportErrors) {
                $a.dialogs.alert(this.jdata.notice.join("\n\n"));
            }
            
            if(autoProcess) {
                this.process();
            }
            
            return true;
        },
        
        /****** json/process
        *
        * NAME
        *   ua.json.process
        *
        * FUNCTION
        *   The Javascript method ua.json.execute is the final
        *   step in sending and receiving JSON requests.  This
        *   routine does the following:
        *   * Any HTML sent back via PHP x4HTML replaces the 
        *     innerHTML of the named items (actually item Ids are used).
        *   * Any script sent back via PHP x4Script is executed.
        *   * Any data sent back via PHP x4Data is placed into
        *     ua.data.
        *
        * EXAMPLE
        *   This example shows how you can retrieve table data and
        *   then process it:
        *
        *      <script>
        *      ua.json.init('x4Page','myCustomPage');
        *      ua.json.addParm('x4Action','getStates');
        *      // ua.json.execute will return false on errors
        *      if(ua.json.execute()) {
        *         // ua.json.process puts everything in its place...
        *         ua.json.process();
        *         // ...so that we can handle the returned data
        *         for (var idx in ua.data.states) {
        *            // do something
        *         }
        *      }
        *      <script>
        *
        *   This code requires the following PHP code on the server:
        *
        *      <?php
        *      # this is file application/x4myCustomPage.php
        *      class x4myCustomPage extends androX4 {
        *          function getStates() {
        *              $states = SQL("Select * from states");
        *              x4Data('states',$states);
        *          }
        *      }
        *
        *******
        */
        process: function(divMain) {
            for(var x in this.jdata.html) {
                if(x=='*MAIN*') {
                    $('#'+divMain).html(this.jdata.html[x]);
                }
                else {
                    var obj = u.byId(x);
                    if(obj) {
                        if (obj.tagName =='INPUT') {
                            obj.value = this.jdata.html[x];
                        }
                        else {
                            obj.innerHTML = this.jdata.html[x];
                        }
                    }
                }
            }
            
            // Execute any script that was provided
            for(var x in this.jdata.script) {
                eval(this.jdata.script[x]); 
            }
            
            return true;
        }
    },

    /** NO DOC **/
    /** DEPRECATED **/
    /** Moved into u **/
    byId: function(id) {
        return document.getElementById(id );
    },
    value: function(id) {
        return $a.byId(id).value;
    },

    /** NO DOC **/
    /** DEPRECATED **/
    /** Moved into u **/
    // Retrieve an object's property, creating it if not
    // there and assigning it the default    
    aProp: function(obj,propname,defvalue) {
        if(typeof(obj)!='object') {
            return defvalue;
        }
        
        // First try, maybe it is a direct property
        if(typeof(obj[propname])!='undefined') {
            return obj[propname];
        }
        // Second try, maybe it is an attribute
        if(obj.getAttribute) {
            if(obj.getAttribute(propname)!=null) {
                return obj.getAttribute(propname);
            }
        }
        // Give up, return the defvalue
        return defvalue;
    },
    /** NO DOC **/
    /** DEPRECATED **/
    /** Moved into u **/
    p: function(obj,propname,defvalue) {
        return this.aProp(obj,propname,defvalue);
    },
    
    /*
     * Create a tab loop 
     */
    tabLoopInit: function(jqo) {
        if( $(jqo).find(":input:not([@readonly])").length == 0) return;
        
        // Assign first to shift back to the last
        $(jqo).find(":input:not([@readonly]):first")[0].tabPrev =
            '#' + $(jqo).find(":input:not([@readonly]):last")[0].id;
        $(jqo).find(":input:not([@readonly]):first").keypress( function(event) {
            var label = $a.keyLabel(event);
            if(label=='ShiftTab') {
                $(event.currentTarget.tabPrev).focus();
                event.preventDefault();
            }
        });
    
        // Assign last to shift to the first
        $(jqo).find(":input:not([@readonly]):last")[0].tabNext =
            '#' + $(jqo).find(":input:not([@readonly]):first")[0].id;
        $(jqo).find(":input:not([@readonly]):last").keypress( function(event) {
            var label = $a.keyLabel(event);
            // A tab key on the last element will loop around    
            if(label=='Tab') {
                $(event.currentTarget.tabNext).focus();
                event.preventDefault();
            }
        });
    
        // TAB LOOP: Put focus on first non-readonly element
        $(jqo).find(':input:not([@readonly]):first').focus();      
    },

    keyLabel: function(e) {
        var x = e.keyCode;
        
        var x4Keys = { };
        x4Keys['8']  = 'BackSpace';
        x4Keys['9']  = 'Tab';
        x4Keys['13'] = 'Enter';
        x4Keys['16'] = '';   // actually Shift, but prefix will take care of it
        x4Keys['17'] = '';   // actually Ctrl,  but prefix will take care of it
        x4Keys['18'] = '';   // actually Alt,   but prefix will take care of it
        x4Keys['20'] = 'CapsLock';
        x4Keys['27'] = 'Esc';
        x4Keys['33'] = 'PageUp';
        x4Keys['34'] = 'PageDown';
        x4Keys['35'] = 'End';
        x4Keys['36'] = 'Home';
        x4Keys['37'] = 'LeftArrow';
        x4Keys['38'] = 'UpArrow';
        x4Keys['39'] = 'RightArrow';
        x4Keys['40'] = 'DownArrow';
        x4Keys['45'] = 'Insert';
        x4Keys['46'] = 'Delete';
        x4Keys['112']= 'F1' ;
        x4Keys['113']= 'F2' ;
        x4Keys['114']= 'F3' ;
        x4Keys['115']= 'F4' ;
        x4Keys['116']= 'F5' ;
        x4Keys['117']= 'F6' ;
        x4Keys['118']= 'F7' ;
        x4Keys['119']= 'F8' ;
        x4Keys['120']= 'F9' ;
        x4Keys['121']= 'F10';
        x4Keys['122']= 'F11';
        x4Keys['123']= 'F12';
    
        // If they did not hit a key we know about, return empty
        if(typeof(x4Keys[x])=='undefined') return '';
    
        // otherwise put on any prefixes and return
        var prefix = '';
        if(e.ctrlKey)  prefix = 'Ctrl';
        if(e.altKey)   prefix += 'Alt';
        if(e.shiftKey) prefix += 'Shift';
        
        var retval = prefix + x4Keys[x];
        return retval;
    },   
    
    charLetter: function(charcode) {
        var letters = 
            [ 'a', 'b', 'c', 'd', 'e', 'f', 'g',
              'h', 'i', 'j', 'k', 'l', 'm', 'n',
              'o', 'p', 'q', 'r', 's', 't', 'u',
              'v', 'w', 'x', 'y', 'z' ];
        if(charcode >= 65 && charcode <= 90) {
            return letters[charcode - 65];
        }
    },
    charNumber: function(charcode) {
        var numbers = [ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' ];
        if(charcode >= 48 && charcode <= 57) {
            return numbers[charcode - 48];
        }
    },
    
    /**
      * A comprehensive function that returns letters, numbers, labels
      * and so forth, like CtrlPageDown, ShiftR etc.
      *
      */
    label: function(e) {
        // KFD 11/26/08, nifty fix to recognize letters etc
        var x = e.keyCode || e.charCode;
        
        var x4Keys = { };
        x4Keys['8']  = 'BackSpace';
        x4Keys['9']  = 'Tab';
        x4Keys['13'] = 'Enter';
        //x4Keys['16'] = '';   // actually Shift, but prefix will take care of it
        //x4Keys['17'] = '';   // actually Ctrl,  but prefix will take care of it
        //x4Keys['18'] = '';   // actually Alt,   but prefix will take care of it
        // KFD Added these three lines, so that keyup/keydown are tracked
        x4Keys['16'] = 'Shift';
        x4Keys['17'] = 'Ctrl';
        x4Keys['18'] = 'Alt';
        x4Keys['20'] = 'CapsLock';
        x4Keys['27'] = 'Esc';
        x4Keys['33'] = 'PageUp';
        x4Keys['34'] = 'PageDown';
        x4Keys['35'] = 'End';
        x4Keys['36'] = 'Home';
        x4Keys['37'] = 'LeftArrow';
        x4Keys['38'] = 'UpArrow';
        x4Keys['39'] = 'RightArrow';
        x4Keys['40'] = 'DownArrow';
        x4Keys['45'] = 'Insert';
        x4Keys['46'] = 'Delete';
        x4Keys['112']= 'F1' ;
        x4Keys['113']= 'F2' ;
        x4Keys['114']= 'F3' ;
        x4Keys['115']= 'F4' ;
        x4Keys['116']= 'F5' ;
        x4Keys['117']= 'F6' ;
        x4Keys['118']= 'F7' ;
        x4Keys['119']= 'F8' ;
        x4Keys['120']= 'F9' ;
        x4Keys['121']= 'F10';
        x4Keys['122']= 'F11';
        x4Keys['123']= 'F12';
    
        // If they hit one of the control keys, check for
        // Shift, Ctrl, or Alt
        var retval = '';
        if(typeof(x4Keys[x])!='undefined') {
            retval = x4Keys[x];
            if(e.ctrlKey)  retval = 'Ctrl'  + retval;
            // KFD 8/4/08, this never worked, removed.
            if(e.altKey)   retval = 'Alt'   + retval;
            if(e.shiftKey) retval = 'Shift' + retval;
        }
        else {
            var letters = 
                [ 'A', 'B', 'C', 'D', 'E', 'F', 'G',
                  'H', 'I', 'J', 'K', 'L', 'M', 'N',
                  'O', 'P', 'Q', 'R', 'S', 'T', 'U',
                  'V', 'W', 'X', 'Y', 'Z' ];
            var numbers = [ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' ];
            if(x >= 65 && x <= 90) {
                retval = letters[x - 65];
            }
            else if(x >= 97 && x <= 121) {
                retval = letters[x - 97];
            }
            else if(x >= 48 && x <= 57) {
                retval = numbers[x - 48];
            }
        }
    
        // otherwise put on any prefixes and return
        
        return retval;
    }
}

function showHide( id ) {
    var tag = '#' + id;
    $(tag).slideToggle();
}


/* ----------------------------------------------------- *\
   EXPERIMENTAL, json constructor
\* ----------------------------------------------------- */
function androJSON(parm,value) {
    
    this.callString = '';
    this.http       =  false,
    this.active     =false,
    this.jdata      ={ },
    this.data       ={ dd: {} },
    this.requests   ={ },
    this.parms      ={ },
    this.reportErrors= true,
    this.x4Page     ='',
    this.x4Action   ='',
    this.explicitParms= '',
    this.hadErrors= false,

    /****m* json/addParm
    *
    * NAME
    *   ua.json.addParm
    *
    * FUNCTION
    *   The Javascript method ua.json.addParm adds one parameter
    *   to a JSON call previously initiated with ua.json.init.
    *
    * INPUTS
    *   string - required, a parameter name
    *   mixed - required, the value for the parameter
    *
    * EXAMPLE
    *   Here are two examples for initiating a JSON request
    *
    *      <script>
    *      ua.json.init();
    *      // Name the server-side page to call
    *      ua.json.addParm('x4Page','myCustomPage');
    *      // Name the server-side method to call
    *      ua.json.addParm('x4Action','fetchSomething');
    *      </script>
    *
    * SOURCE
    */
    this.addParm = function(name,value) {
        this.parms[name] = value;
        if(name=='x4Page')   this.x4Page = value;
        if(name=='x4Action') this.x4Action = value;
    }
    /******/
    
    // Original init code
    this.x4Page     = '';
    this.x4Action   = '';
    this.callString = '';
    this.parms      = { };
    this.reportErrors=true;
    this.explicitParms= '';
    if(parm!=null) {
        this.addParm(parm,value);
    }
    // Create an object
    var browser = navigator.appName;
    if(browser == "Microsoft Internet Explorer"){
        // KFD 11/24
        this.http = new ActiveXObject("Microsoft.XMLHTTP");
    }
    else {
        // KFD 11/24
        this.http = new XMLHttpRequest();
    }
    
    this.makeString = function() {
        if(this.explicitParms!='') {
            return this.explicitParms;
        }
        var list = [ ];
        for(var x in this.parms) {
            list[list.length] = x + "=" +encodeURIComponent(this.parms[x]);
        }
        return list.join('&');
    }
    //addValue: function(name,value) {
    //    if(this.callString!='') this.callString+="&";
    //    this.callString += 'x4c_' + name + '=' + encodeURIComponent(value);
    //},
    
    /****m* json/inputs
    *
    * NAME
    *   ua.json.inputs
    *
    * FUNCTION
    *   The Javascript method ua.json.inputs adds inputs to
    *   a JSON call previously initiated with ua.json.init.
    *
    *   This method accepts an object as its parameter, and
    *   will add every input that is a child (at any level)
    *   of that object.
    *
    *   This method uses the "id" property of the input to
    *   name the parameter, not the "name" property.  Andromeda
    *   makes no use of the "name" property.
    *
    *   This method is equivalent to use ua.json.addParm
    *   for each of the desired inputs.
    *
    *   Checkboxes receive special treatment.  If the box is 
    *   checked a value of 'Y' is sent, and if the box is not
    *   checked a value of 'N' is sent.
    *
    *   The name of each parameter is normally the Id of the
    *   input.  If the inputs were generated by Andromeda
    *   on an Extended-Desktop page, they will have the names
    *   'x4inp_<tableId>_<columnId>.  
    *
    * INPUTS
    *   object - optional, the object to recurse.  You must
    *   pass the object itself, not its Id.  If no object is
    *   passed the Extended-Desktop top-level object x4Top
    *   is used, which means you get every input on the page,
    *   whether or not it is visible or
    *
    *   direct - a special flag that says to name the parameters
    *   'x4c_<columnId>'.  This is required when you are sending
    *   Direct-Database-Access calls.
    *
    *
    * SOURCE
    */
    this.inputs= function(obj,direct) {
        if(direct==null) direct=false;
        if(obj==null) {
            obj = $a.byId('x4Top');
        }
        if(typeof(obj)=='string') {
            var jqObjects = $(obj);
        }
        else {
            var jqObjects = $(obj).find(":input");
        }
        jqObjects.each( function() {
                if(direct) 
                    var id = 'x4c_'+u.p(this,'xColumnId');
                else
                    var id = this.id;
                    
                
                if(this.type=='checkbox') {
                    if(this.checked) {
                        $a.json.addParm(id,'Y');
                    }
                    else {
                        $a.json.addParm(id,'N');
                    }
                }
                else {
                    if(this.value!='') {
                        $a.json.addParm(id,this.value);
                    }
                }
        });
    }
    /******/
    
    /****m* json/serialize
    *
    * NAME
    *   ua.json.serialize
    *
    * FUNCTION
    *   The Javascript method ua.json.serialize takes a
    *   Javascript Object or Array and serializes it and
    *   adds the values to a JSON request previously
    *   initialized with ua.json.init.
    *
    *   This method accepts an object as its parameter.
    *
    *   When you call this function, the parameters sent
    *   back take the form of an associative array.
    *
    * INPUTS
    *   prefix - The base name of the parameter
    *
    *   object - the object to serialize.
    *
    * EXAMPLE
    *   Consider the following object that is serialized
    *
    *      <script>
    *      var x = {
    *         parm1: [ 1, 2, 3],
    *         parm2: 'hello',
    *         parm3: {
    *             x: 5,
    *             y: 10,
    *         }
    *      ua.json.init('x4Page','myCustomPage');
    *      ua.json.addParm('x4Action','serialHandler');
    *      ua.json.serialize('example',x);
    *      <script>
    *
    *   Then on the server, you can grab the "example" parameter
    *   and you will get the following associative array:
    *
    *      <?php
    *      # this is file x4myCustomPage.php
    *      class x4myCustomPage extends androX4 {
    * 
    *          # this handles the 'x4Action' specified above
    *          function serialHandler() {
    *              $example = gp('example');
    *            
    *              # ...the following code shows how 
    *              #    the values that are in x4
    *              $example['parm1'][0] = 1;
    *              $example['parm1'][1] = 2;
    *              $example['parm1'][2] = 3;
    *              $example['parm2'] = 'hello';
    *              $example['parm3']['x'] = 5;
    *              $example['parm3']['y'] = 10;
    *          }
    *      }
    *      ?>
    *
    * SOURCE
    */
    this.serialize = function(prefix,obj) {
        for(var x in obj) {
            if(typeof(obj[x])=='object') {
                this.serialize(prefix+'['+x+']',obj[x]);
            }
            else {
                this.addParm(prefix+'['+x+']',obj[x]);
            }
        }
    }
    /******/
    
    /****m* json/windowLocation
    *
    * NAME
    *   ua.json.windowLocation
    *
    * FUNCTION
    *   The Javascript method ua.json.windowLocation takes a
    *   JSON request and executes it as a page request.
    *
    * EXAMPLE
    *   The following example loads a new page
    *
    *      <script>
    *      ua.json.init('x4Page','calendar');
    *      ua.json.windowLocation();
    *      </script>
    *
    * SOURCE
    */
    this.windowLocation = function() {
        var entireGet = 'index.php?'+this.makeString()
        window.location = entireGet;
    }
    /******/
    
    /****m* json/newWindow
    *
    * NAME
    *   ua.json.newWindow
    *
    * FUNCTION
    *   The Javascript method ua.json.newWindow takes a
    *   JSON request and executes it as a page request, popping
    *   the result up in a new tab or window.
    *
    *   When the user exits the resulting tab or window, it
    *   will close.  
    *
    * EXAMPLE
    *   The following example loads a new page
    *
    *      <script>
    *      ua.json.init('x4Page','calendar');
    *      ua.json.newWindow();
    *      </script>
    *
    * SOURCE
    */
    this.newWindow = function() {
        var entireGet = 'index.php?'+this.makeString()+'&x4Return=exit';
        $a.openWindow(entireGet);
    }
    /******/

    /****m* json/executeAsync
    *
    * NAME
    *   ua.json.executeAsync
    *
    * FUNCTION
    *   By default Andromeda sends JSON requests synchronously,
    *   which is more appropriate for business database applications
    *   than asynchronous requests.
    *
    *   There are however some times when you do not want the user
    *   to wait, and so you can make asynchronous calls. 
    *
    *   Andromeda does not make use of response handlers, see the
    *   above section on ua.json for more details.
    *
    * SOURCE
    */
    this.executeAsync = function() {
        this.execute(true,true);
    }
    /******/
    
    /****m* json/execute
    *
    * NAME
    *   ua.json.execute
    *
    * FUNCTION
    *   The Javascript method ua.json.execute sends a request to
    *   the server that has been initialized with ua.json.init
    *   and has received parameters with any of ua.json.addParm,
    *   ua.json.inputs and ua.json.serialize.
    *
    *   In normal usage, you call this routine and check for
    *   a return value of true.  If the routine returns true
    *   you call ua.json.process to process the returned
    *   results.
    *
    * RESULTS
    *   This routine returns true if the server reports no 
    *   errors.
    *
    *   If the server reports errors, they are displayed to the
    *   user using u.dialogs.alert, and this routine returns
    *   false.
    *
    *******
    */
    this.execute = function(autoProcess,async,returnString) {
        this.hadErrors = false;
        if(async==null) async = false;
        if(autoProcess==null) autoProcess=false;
        
        // If async, we have to do it a little differently
        // KFD 11/24, did nothing yet for async
        if(async) {
            http.onreadystatechange = function() {
                if(this.readyState!=4) return;
                $a.json.processPre(this,key,false);
                $a.json.process();
            }
        }
        
        // Execute the call
        var entireGet = 'index.php?json=1&'+this.makeString();
        this.http.open('POST' , entireGet, async);
        this.http.send(null);
        
        // An asynchronous call now exits, but a
        // synchronous call continues            
        if (async) return;
        else if(returnString) return this.http.responseText;
        else return this.processPre(autoProcess);
        
    }
    
    this.processPre = function(autoProcess) {
        // Attempt to evaluate the JSON
        try {
            eval('this.jdata = '+this.http.responseText);
        }
        catch(e) { 
            $a.dialogs.alert("Could not process server response!");
            if(typeof(x4)!='undefined') {
                x4.debug(this.http.responseText);
            }
            if(u.byId('x6Log')) {
                u.byId('x6Log').innerHTML = http.responseText;
                u.byId('x6Log').style.display='block';
            }
            this.http = false;
            return false;
        }
        
        // KFD 7/8/08, additional housekeeping, throw away
        //             references to the object
        this.http = false;

        // If there were server errors, report those
        if(this.jdata.error.length>0 && this.reportErrors) {
            this.hadErrors = true;
            $a.dialogs.alert(this.jdata.error.join("\n\n"));
            return false;
        }
        if(this.jdata.notice.length>0 && this.reportErrors) {
            $a.dialogs.alert(this.jdata.notice.join("\n\n"));
        }
        
        if(autoProcess) {
            this.process();
        }
        
        return true;
    }
    
    /****m* json/process
    *
    * NAME
    *   ua.json.process
    *
    * FUNCTION
    *   The Javascript method ua.json.execute is the final
    *   step in sending and receiving JSON requests.  This
    *   routine does the following:
    *   * Any HTML sent back via PHP x4HTML replaces the 
    *     innerHTML of the named items (actually item Ids are used).
    *   * Any script sent back via PHP x4Script is executed.
    *   * Any data sent back via PHP x4Data is placed into
    *     ua.data.
    *
    * EXAMPLE
    *   This example shows how you can retrieve table data and
    *   then process it:
    *
    *      <script>
    *      ua.json.init('x4Page','myCustomPage');
    *      ua.json.addParm('x4Action','getStates');
    *      // ua.json.execute will return false on errors
    *      if(ua.json.execute()) {
    *         // ua.json.process puts everything in its place...
    *         ua.json.process();
    *         // ...so that we can handle the returned data
    *         for (var idx in ua.data.states) {
    *            // do something
    *         }
    *      }
    *      <script>
    *
    *   This code requires the following PHP code on the server:
    *
    *      <?php
    *      # this is file application/x4myCustomPage.php
    *      class x4myCustomPage extends androX4 {
    *          function getStates() {
    *              $states = SQL("Select * from states");
    *              x4Data('states',$states);
    *          }
    *      }
    *
    *******
    */
    this.process = function(divMain) {
        for(var x in this.jdata.html) {
            if(x=='*MAIN*') {
                $('#'+divMain).html(this.jdata.html[x]);
            }
            else {
                var obj = u.byId(x);
                if(obj) {
                    if (obj.tagName =='INPUT') {
                        obj.value = this.jdata.html[x];
                    }
                    else {
                        obj.innerHTML = this.jdata.html[x];
                    }
                }
            }
        }
        
        // Execute any script that was provided
        for(var x in this.jdata.script) {
            eval(this.jdata.script[x]); 
        }
        
        return true;
    }
}



/* ---------------------------------------------------- *\
   SECTION 8: SUSPECTED DEAD CODE  
   
   We want to move everything here
   to androLibDeprecated.js
\* ---------------------------------------------------- */
var androIE = false;
if(    navigator.userAgent.indexOf('MSIE') >=0 
    && navigator.userAgent.indexOf('Opera')<0) {
    androIE = true;
}


function u3SS(stringparms) {
   ob('u3string').value=stringparms;
   formSubmit();
}

/* ================================================================== *\
   (C) Copyright 2005 by Secure Data Software, Inc.
   This file is part of Andromeda
   
   Andromeda is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.

   Andromeda is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with Andromeda; if not, write to the Free Software
   Foundation, Inc., 51 Franklin St, Fifth Floor,
   Boston, MA  02110-1301  USA 
   or visit http://www.gnu.org/licenses/gpl.html
\* ================================================================== */

/*
 * This file contains routines that go almost back to the
 * beginning of the Andromed epoch (July 1, 2004).  To 
 * avoid loading this file, set your configuration 
 * variable "deprecated" to "N"
 *
 */

// return keycode, this is the one you
// need to catch function keys, arrows keys and 
// so forth.
function KeyCode(e) {
   if(window.event)
      // IE
      return window.event.keyCode;  
   else
      // firefox
      return e.keyCode;
}
// Return the value of a named attribute, empty
// string if it does not exist
function obAttValue(objname,attname) {
   var obj=ob(objname);
   return objAttValue(obj,attname);
}

function objAttValue(obj,attname) {
   if(!obj) {
      //alert('Reference to unknown object: '+objname);
      return '';
   }
   else {
      att = obj.attributes.getNamedItem(attname);
      if(!att) {  
         //alert('Reference to unknown attribute: '+objanem+'.'+attname);
         return '';
      }
      else {
         return att.value;
      }           
   }
}

// //(C) www.dhtmlgoodies.com, September 2005

var languageCode='en';var todayStringFormat='[todayString] [dayString]. [day]. [monthString] [year]';var pathToImages='clib/dhtmlgoodies_calendar_images/';var calendar_offsetTop=0;var calendar_offsetLeft=0;var calendarDiv=false;var MSIE=false;var Opera=false;if(navigator.userAgent.indexOf('MSIE')>=0&&navigator.userAgent.indexOf('Opera')<0)MSIE=true;if(navigator.userAgent.indexOf('Opera')>=0)Opera=true;switch(languageCode){case"en":var monthArray=['January','February','March','April','May','June','July','August','September','October','November','December'];var monthArrayShort=['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];var dayArray=['Mon','Tue','Wed','Thu','Fri','Sat','Sun'];var weekString='Week';var todayString='Today is';break;case"ge":var monthArray=['Januar','Februar','M�rz','April','Mai','Juni','Juli','August','September','Oktober','November','Dezember'];var monthArrayShort=['Jan','Feb','Mar','Apr','Mai','Jun','Jul','Aug','Sep','Okt','Nov','Dez'];var dayArray=['Mon','Die','Mit','Don','Fre','Sam','Son'];var weekString='Woche';var todayString='Heute';break;case"no":var monthArray=['Januar','Februar','Mars','April','Mai','Juni','Juli','August','September','Oktober','November','Desember'];var monthArrayShort=['Jan','Feb','Mar','Apr','Mai','Jun','Jul','Aug','Sep','Okt','Nov','Des'];var dayArray=['Man','Tir','Ons','Tor','Fre','L&oslash;r','S&oslash;n'];var weekString='Uke';var todayString='Dagen i dag er';break;case"nl":var monthArray=['Januari','Februari','Maart','April','Mei','Juni','Juli','Augustus','September','Oktober','November','December'];var monthArrayShort=['Jan','Feb','Mar','Apr','Mei','Jun','Jul','Aug','Sep','Okt','Nov','Dec'];var dayArray=['Ma','Di','Wo','Do','Vr','Za','Zo'];var weekString='Week';var todayString='Vandaag';break;case"es":var monthArray=['Enero','Febrero','Marzo','April','Mayo','Junio','Julio','Agosto','Septiembre','Octubre','Noviembre','Diciembre'];var monthArrayShort=['Ene','Feb','Mar','Abr','May','Jun','Jul','Ago','Sep','Oct','Nov','Dic'];var dayArray=['Lun','Mar','Mie','Jue','Vie','Sab','Dom'];var weekString='Semana';var todayString='Hoy es';break;case"pt-br":var monthArray=['Janeiro','Fevereiro','Mar&ccedil;o','Abril','Maio','Junho','Julho','Agosto','Setembro','Outubro','Novembro','Dezembro'];var monthArrayShort=['Jan','Fev','Mar','Abr','Mai','Jun','Jul','Ago','Set','Out','Nov','Dez'];var dayArray=['Seg','Ter','Qua','Qui','Sex','S&aacute;b','Dom'];var weekString='Sem.';var todayString='Hoje &eacute;';break;case"fr":var monthArray=['Janvier','F�vrier','Mars','Avril','Mai','Juin','Juillet','Ao�t','Septembre','Octobre','Novembre','D�cembre'];var monthArrayShort=['Jan','Fev','Mar','Avr','Mai','Jun','Jul','Aou','Sep','Oct','Nov','Dec'];var dayArray=['Lun','Mar','Mer','Jeu','Ven','Sam','Dim'];var weekString='Sem';var todayString="Aujourd'hui";break;case"ru":var monthArray=['������','�������','����','������','���','����','����','������','��������','�������','������','�������'];var monthArrayShort=['���','���','���','���','���','���','���','���','���','���','���','���'];var dayArray=['��','��','��','��','��','��','��'];var weekString='#';var todayString='�������';break;case"da":var monthArray=['januar','februar','marts','april','maj','juni','juli','august','september','oktober','november','december'];var monthArrayShort=['jan','feb','mar','apr','maj','jun','jul','aug','sep','okt','nov','dec'];var dayArray=['man','tirs','ons','tors','fre','l�r','s�n']
var weekString='Uge';var todayString='I dag er den';break;case"hu":var monthArray=['Január','Február','Március','�?prilis','Május','Június','Július','Augusztus','Szeptember','Október','November','December'];var monthArrayShort=['Jan','Feb','Márc','�?pr','Máj','Jún','Júl','Aug','Szep','Okt','Nov','Dec'];var dayArray=['Hé','Ke','Sze','Cs','Pé','Szo','Vas'];var weekString='Hét';var todayString='Mai nap';break;case"it":var monthArray=['Gennaio','Febbraio','Marzo','Aprile','Maggio','Giugno','Luglio','Agosto','Settembre','Ottobre','Novembre','Dicembre'];var monthArrayShort=['Gen','Feb','Mar','Apr','Mag','Giu','Lugl','Ago','Set','Ott','Nov','Dic'];var dayArray=['Lun',';Mar','Mer','Gio','Ven','Sab','Dom'];var weekString='Settimana';var todayString='Oggi &egrave; il';break;}
var daysInMonthArray=[31,28,31,30,31,30,31,31,30,31,30,31];var currentMonth;var currentYear;var calendarContentDiv;var returnDateTo;var returnFormat;var activeSelectBoxMonth;var activeSelectBoxYear;var iframeObj=false;var returnDateToYear;var returnDateToMonth;var returnDateToDay;var inputYear;var inputMonth;var inputDay;var selectBoxHighlightColor='#D60808';var selectBoxRolloverBgColor='#E2EBED';function cancelCalendarEvent()
{return false;}
function isLeapYear(inputYear)
{if(inputYear%400==0||(inputYear%4==0&&inputYear%100!=0))return true;return false;}
var activeSelectBoxMonth=false;function highlightMonthYear()
{if(activeSelectBoxMonth)activeSelectBoxMonth.className='';if(this.className=='monthYearActive'){this.className='';}else{this.className='monthYearActive';activeSelectBoxMonth=this;}}
function showMonthDropDown()
{if(document.getElementById('monthDropDown').style.display=='block'){document.getElementById('monthDropDown').style.display='none';}else{document.getElementById('monthDropDown').style.display='block';document.getElementById('yearDropDown').style.display='none';}}
function showYearDropDown()
{if(document.getElementById('yearDropDown').style.display=='block'){document.getElementById('yearDropDown').style.display='none';}else{document.getElementById('yearDropDown').style.display='block';document.getElementById('monthDropDown').style.display='none';}}
function selectMonth()
{document.getElementById('calendar_month_txt').innerHTML=this.innerHTML
currentMonth=this.id.replace(/[^\d]/g,'');document.getElementById('monthDropDown').style.display='none';for(var no=0;no<monthArray.length;no++){document.getElementById('monthDiv_'+no).style.color='';}
this.style.color=selectBoxHighlightColor;activeSelectBoxMonth=this;writeCalendarContent();}
function selectYear()
{document.getElementById('calendar_year_txt').innerHTML=this.innerHTML
currentYear=this.innerHTML.replace(/[^\d]/g,'');document.getElementById('yearDropDown').style.display='none';if(activeSelectBoxYear){activeSelectBoxYear.style.color='';}
activeSelectBoxYear=this;this.style.color=selectBoxHighlightColor;writeCalendarContent();}
function switchMonth()
{if(this.src.indexOf('left')>=0){currentMonth=currentMonth-1;;if(currentMonth<0){currentMonth=11;currentYear=currentYear-1;}}else{currentMonth=currentMonth+1;;if(currentMonth>11){currentMonth=0;currentYear=currentYear/1+1;}}
writeCalendarContent();}
function createMonthDiv(){var div=document.createElement('DIV');div.className='monthYearPicker';div.id='monthPicker';for(var no=0;no<monthArray.length;no++){var subDiv=document.createElement('DIV');subDiv.innerHTML=monthArray[no];subDiv.onmouseover=highlightMonthYear;subDiv.onmouseout=highlightMonthYear;subDiv.onclick=selectMonth;subDiv.id='monthDiv_'+no;subDiv.style.width='56px';subDiv.onselectstart=cancelCalendarEvent;div.appendChild(subDiv);if(currentMonth&&currentMonth==no){subDiv.style.color=selectBoxHighlightColor;activeSelectBoxMonth=subDiv;}}
return div;}
function changeSelectBoxYear()
{var yearItems=this.parentNode.getElementsByTagName('DIV');if(this.innerHTML.indexOf('-')>=0){var startYear=yearItems[1].innerHTML/1-1;if(activeSelectBoxYear){activeSelectBoxYear.style.color='';}}else{var startYear=yearItems[1].innerHTML/1+1;if(activeSelectBoxYear){activeSelectBoxYear.style.color='';}}
for(var no=1;no<yearItems.length-1;no++){yearItems[no].innerHTML=startYear+no-1;yearItems[no].id='yearDiv'+(startYear/1+no/1-1);}
if(activeSelectBoxYear){activeSelectBoxYear.style.color='';if(document.getElementById('yearDiv'+currentYear)){activeSelectBoxYear=document.getElementById('yearDiv'+currentYear);activeSelectBoxYear.style.color=selectBoxHighlightColor;;}}}
function updateYearDiv()
{var div=document.getElementById('yearDropDown');var yearItems=div.getElementsByTagName('DIV');for(var no=1;no<yearItems.length-1;no++){yearItems[no].innerHTML=currentYear/1-6+no;if(currentYear==(currentYear/1-6+no)){yearItems[no].style.color=selectBoxHighlightColor;activeSelectBoxYear=yearItems[no];}else{yearItems[no].style.color='';}}}
function updateMonthDiv()
{for(no=0;no<12;no++){document.getElementById('monthDiv_'+no).style.color='';}
document.getElementById('monthDiv_'+currentMonth).style.color=selectBoxHighlightColor;activeSelectBoxMonth=document.getElementById('monthDiv_'+currentMonth);}
function createYearDiv()
{if(!document.getElementById('yearDropDown')){var div=document.createElement('DIV');div.className='monthYearPicker';}else{var div=document.getElementById('yearDropDown');var subDivs=div.getElementsByTagName('DIV');for(var no=0;no<subDivs.length;no++){subDivs[no].parentNode.removeChild(subDivs[no]);}}
var d=new Date();if(currentYear){d.setFullYear(currentYear);}
var startYear=d.getFullYear()/1-5;var subDiv=document.createElement('DIV');subDiv.innerHTML='&nbsp;&nbsp;- ';subDiv.onclick=changeSelectBoxYear;subDiv.onmouseover=highlightMonthYear;subDiv.onmouseout=highlightMonthYear;subDiv.onselectstart=cancelCalendarEvent;div.appendChild(subDiv);for(var no=startYear;no<(startYear+10);no++){var subDiv=document.createElement('DIV');subDiv.innerHTML=no;subDiv.onmouseover=highlightMonthYear;subDiv.onmouseout=highlightMonthYear;subDiv.onclick=selectYear;subDiv.id='yearDiv'+no;subDiv.onselectstart=cancelCalendarEvent;div.appendChild(subDiv);if(currentYear&&currentYear==no){subDiv.style.color=selectBoxHighlightColor;activeSelectBoxYear=subDiv;}}
var subDiv=document.createElement('DIV');subDiv.innerHTML='&nbsp;&nbsp;+ ';subDiv.onclick=changeSelectBoxYear;subDiv.onmouseover=highlightMonthYear;subDiv.onmouseout=highlightMonthYear;subDiv.onselectstart=cancelCalendarEvent;div.appendChild(subDiv);return div;}
function highlightSelect()
{if(this.className=='selectBox'){this.className='selectBoxOver';this.getElementsByTagName('IMG')[0].src=pathToImages+'down_over.gif';}else{this.className='selectBox';this.getElementsByTagName('IMG')[0].src=pathToImages+'down.gif';}}
function highlightArrow()
{if(this.src.indexOf('over')>=0){if(this.src.indexOf('left')>=0)this.src=pathToImages+'left.gif';if(this.src.indexOf('right')>=0)this.src=pathToImages+'right.gif';}else{if(this.src.indexOf('left')>=0)this.src=pathToImages+'left_over.gif';if(this.src.indexOf('right')>=0)this.src=pathToImages+'right_over.gif';}}
function highlightClose()
{if(this.src.indexOf('over')>=0){this.src=pathToImages+'close.gif';}else{this.src=pathToImages+'close_over.gif';}}
function closeCalendar(){document.getElementById('yearDropDown').style.display='none';document.getElementById('monthDropDown').style.display='none';calendarDiv.style.display='none';if(iframeObj)iframeObj.style.display='none';if(activeSelectBoxMonth)activeSelectBoxMonth.className='';if(activeSelectBoxYear)activeSelectBoxYear.className='';}
function writeTopBar()
{var topBar=document.createElement('DIV');topBar.className='topBar';topBar.id='topBar';calendarDiv.appendChild(topBar);var leftDiv=document.createElement('DIV');leftDiv.style.marginRight='1px';var img=document.createElement('IMG');img.src=pathToImages+'left.gif';img.onmouseover=highlightArrow;img.onclick=switchMonth;img.onmouseout=highlightArrow;leftDiv.appendChild(img);topBar.appendChild(leftDiv);if(Opera)leftDiv.style.width='16px';var rightDiv=document.createElement('DIV');rightDiv.style.marginRight='1px';var img=document.createElement('IMG');img.src=pathToImages+'right.gif';img.onclick=switchMonth;img.onmouseover=highlightArrow;img.onmouseout=highlightArrow;rightDiv.appendChild(img);if(Opera)rightDiv.style.width='16px';topBar.appendChild(rightDiv);var monthDiv=document.createElement('DIV');monthDiv.id='monthSelect';monthDiv.onmouseover=highlightSelect;monthDiv.onmouseout=highlightSelect;monthDiv.onclick=showMonthDropDown;var span=document.createElement('SPAN');span.innerHTML=monthArray[currentMonth];span.id='calendar_month_txt';monthDiv.appendChild(span);var img=document.createElement('IMG');img.src=pathToImages+'down.gif';img.style.position='absolute';img.style.right='0px';monthDiv.appendChild(img);monthDiv.className='selectBox';if(Opera){img.style.cssText='float:right;position:relative';img.style.position='relative';img.style.styleFloat='right';}
topBar.appendChild(monthDiv);var monthPicker=createMonthDiv();monthPicker.style.left='37px';monthPicker.style.top=monthDiv.offsetTop+monthDiv.offsetHeight+1+'px';monthPicker.style.width='60px';monthPicker.id='monthDropDown';calendarDiv.appendChild(monthPicker);var yearDiv=document.createElement('DIV');yearDiv.onmouseover=highlightSelect;yearDiv.onmouseout=highlightSelect;yearDiv.onclick=showYearDropDown;var span=document.createElement('SPAN');span.innerHTML=currentYear;span.id='calendar_year_txt';yearDiv.appendChild(span);topBar.appendChild(yearDiv);var img=document.createElement('IMG');img.src=pathToImages+'down.gif';yearDiv.appendChild(img);yearDiv.className='selectBox';if(Opera){yearDiv.style.width='50px';img.style.cssText='float:right';img.style.position='relative';img.style.styleFloat='right';}
var yearPicker=createYearDiv();yearPicker.style.left='113px';yearPicker.style.top=monthDiv.offsetTop+monthDiv.offsetHeight+1+'px';yearPicker.style.width='35px';yearPicker.id='yearDropDown';calendarDiv.appendChild(yearPicker);var img=document.createElement('IMG');img.src=pathToImages+'close.gif';img.style.styleFloat='right';img.onmouseover=highlightClose;img.onmouseout=highlightClose;img.onclick=closeCalendar;topBar.appendChild(img);if(!document.all){img.style.position='absolute';img.style.right='2px';}}
function writeCalendarContent()
{var calendarContentDivExists=true;if(!calendarContentDiv){calendarContentDiv=document.createElement('DIV');calendarDiv.appendChild(calendarContentDiv);calendarContentDivExists=false;}
currentMonth=currentMonth/1;var d=new Date();d.setFullYear(currentYear);d.setDate(1);d.setMonth(currentMonth);var dayStartOfMonth=d.getDay();if(dayStartOfMonth==0)dayStartOfMonth=7;dayStartOfMonth--;document.getElementById('calendar_year_txt').innerHTML=currentYear;document.getElementById('calendar_month_txt').innerHTML=monthArray[currentMonth];var existingTable=calendarContentDiv.getElementsByTagName('TABLE');if(existingTable.length>0){calendarContentDiv.removeChild(existingTable[0]);}
var calTable=document.createElement('TABLE');calTable.cellSpacing='0';calendarContentDiv.appendChild(calTable);var calTBody=document.createElement('TBODY');calTable.appendChild(calTBody);var row=calTBody.insertRow(-1);var cell=row.insertCell(-1);cell.innerHTML=weekString;cell.style.backgroundColor=selectBoxRolloverBgColor;for(var no=0;no<dayArray.length;no++){var cell=row.insertCell(-1);cell.innerHTML=dayArray[no];}
var row=calTBody.insertRow(-1);var cell=row.insertCell(-1);cell.style.backgroundColor=selectBoxRolloverBgColor;var week=getWeek(currentYear,currentMonth,1);cell.innerHTML=week;for(var no=0;no<dayStartOfMonth;no++){var cell=row.insertCell(-1);cell.innerHTML='&nbsp;';}
var colCounter=dayStartOfMonth;var daysInMonth=daysInMonthArray[currentMonth];if(daysInMonth==28){if(isLeapYear(currentYear))daysInMonth=29;}
for(var no=1;no<=daysInMonth;no++){d.setDate(no-1);if(colCounter>0&&colCounter%7==0){var row=calTBody.insertRow(-1);var cell=row.insertCell(-1);var week=getWeek(currentYear,currentMonth,no);cell.innerHTML=week;cell.style.backgroundColor=selectBoxRolloverBgColor;}
var cell=row.insertCell(-1);if(currentYear==inputYear&&currentMonth==inputMonth&&no==inputDay){cell.className='activeDay';}
cell.innerHTML=no;cell.onclick=pickDate;colCounter++;}
if(!document.all){if(calendarContentDiv.offsetHeight)
document.getElementById('topBar').style.top=calendarContentDiv.offsetHeight+document.getElementById('topBar').offsetHeight-1+'px';else{document.getElementById('topBar').style.top='';document.getElementById('topBar').style.bottom='0px';}}
if(iframeObj){if(!calendarContentDivExists)setTimeout('resizeIframe()',350);else setTimeout('resizeIframe()',10);}}
function resizeIframe()
{iframeObj.style.width=calendarDiv.offsetWidth+'px';iframeObj.style.height=calendarDiv.offsetHeight+'px';}
function pickTodaysDate()
{var d=new Date();currentMonth=d.getMonth();currentYear=d.getFullYear();pickDate(false,d.getDate());}
function pickDate(e,inputDay)
{var month=currentMonth/1+1;if(month<10)month='0'+month;var day;if(!inputDay&&this)day=this.innerHTML;else day=inputDay;if(day/1<10)day='0'+day;if(returnFormat){returnFormat=returnFormat.replace('dd',day);returnFormat=returnFormat.replace('mm',month);returnFormat=returnFormat.replace('yyyy',currentYear);returnDateTo.value=returnFormat;if(returnDateTo.onchange){returnDateTo.onchange();}}else{for(var no=0;no<returnDateToYear.options.length;no++){if(returnDateToYear.options[no].value==currentYear){returnDateToYear.selectedIndex=no;break;}}
for(var no=0;no<returnDateToMonth.options.length;no++){if(returnDateToMonth.options[no].value==month){returnDateToMonth.selectedIndex=no;break;}}
for(var no=0;no<returnDateToDay.options.length;no++){if(returnDateToDay.options[no].value==day){returnDateToDay.selectedIndex=no;break;}}}
closeCalendar();}
function getWeek(year,month,day){day=day/1;year=year/1;month=month/1+1;var a=Math.floor((14-(month))/12);var y=year+4800-a;var m=(month)+(12*a)-3;var jd=day+Math.floor(((153*m)+2)/5)+
(365*y)+Math.floor(y/4)-Math.floor(y/100)+
Math.floor(y/400)-32045;var d4=(jd+31741-(jd%7))%146097%36524%1461;var L=Math.floor(d4/1460);var d1=((d4-L)%365)+L;NumberOfWeek=Math.floor(d1/7)+1;return NumberOfWeek;}
function writeBottomBar()
{var d=new Date();var topBar=document.createElement('DIV');topBar.id='bottomBar';topBar.onclick=pickTodaysDate;topBar.style.cursor='pointer';topBar.className='todaysDate';var day=d.getDay();if(day==0)day=7;day--;var bottomString=todayStringFormat;bottomString=bottomString.replace('[monthString]',monthArrayShort[d.getMonth()]);bottomString=bottomString.replace('[day]',d.getDate());bottomString=bottomString.replace('[year]',d.getFullYear());bottomString=bottomString.replace('[dayString]',dayArray[day].toLowerCase());bottomString=bottomString.replace('[todayString]',todayString);topBar.innerHTML=todayString+': '+d.getDate()+'. '+monthArrayShort[d.getMonth()]+', '+d.getFullYear();topBar.innerHTML=bottomString;calendarDiv.appendChild(topBar);}
function getTopPos(inputObj)
{var returnValue=inputObj.offsetTop+inputObj.offsetHeight;while((inputObj=inputObj.offsetParent)!=null)returnValue+=inputObj.offsetTop;return returnValue+calendar_offsetTop;}
function getleftPos(inputObj)
{var returnValue=inputObj.offsetLeft;while((inputObj=inputObj.offsetParent)!=null)returnValue+=inputObj.offsetLeft;return returnValue+calendar_offsetLeft;}
function positionCalendar(inputObj)
{calendarDiv.style.left=getleftPos(inputObj)+'px';calendarDiv.style.top=getTopPos(inputObj)+'px';if(iframeObj){iframeObj.style.left=calendarDiv.style.left;iframeObj.style.top=calendarDiv.style.top;}}
function initCalendar()
{if(MSIE){iframeObj=document.createElement('IFRAME');iframeObj.style.position='absolute';iframeObj.border='0px';iframeObj.style.border='0px';iframeObj.style.backgroundColor='#FF0000';document.body.appendChild(iframeObj);}
calendarDiv=document.createElement('DIV');calendarDiv.id='calendarDiv';calendarDiv.style.zIndex=1000;document.body.appendChild(calendarDiv);writeBottomBar();writeTopBar();if(!currentYear){var d=new Date();currentMonth=d.getMonth();currentYear=d.getFullYear();}
writeCalendarContent();}
function displayCalendar(inputField,format,buttonObj)
{if(inputField.value.length==format.length){var monthPos=format.indexOf('mm');currentMonth=inputField.value.substr(monthPos,2)/1-1;var yearPos=format.indexOf('yyyy');currentYear=inputField.value.substr(yearPos,4);var dayPos=format.indexOf('dd');tmpDay=inputField.value.substr(dayPos,2);}else{var d=new Date();currentMonth=d.getMonth();currentYear=d.getFullYear();tmpDay=d.getDate();}
inputYear=currentYear;inputMonth=currentMonth;inputDay=tmpDay/1;if(!calendarDiv){initCalendar();}else{if(calendarDiv.style.display=='block'){closeCalendar();return false;}
writeCalendarContent();}
returnFormat=format;returnDateTo=inputField;positionCalendar(buttonObj);calendarDiv.style.visibility='visible';calendarDiv.style.display='block';if(iframeObj){iframeObj.style.display='';iframeObj.style.height='140px';iframeObj.style.width='195px';}
updateYearDiv();updateMonthDiv();}
function displayCalendarSelectBox(yearInput,monthInput,dayInput,buttonObj)
{currentMonth=monthInput.options[monthInput.selectedIndex].value/1-1;currentYear=yearInput.options[yearInput.selectedIndex].value;inputYear=yearInput.options[yearInput.selectedIndex].value;inputMonth=monthInput.options[monthInput.selectedIndex].value/1-1;inputDay=dayInput.options[dayInput.selectedIndex].value/1;if(!calendarDiv){initCalendar();}else{writeCalendarContent();}
returnDateToYear=yearInput;returnDateToMonth=monthInput;returnDateToDay=dayInput;returnFormat=false;returnDateTo=false;positionCalendar(buttonObj);calendarDiv.style.visibility='visible';calendarDiv.style.display='block';if(iframeObj){iframeObj.style.display='';iframeObj.style.height=calendarDiv.offsetHeight+'px';iframeObj.style.width=calendarDiv.offsetWidth+'px';}
updateYearDiv();updateMonthDiv();}