From f2a26474ba0893a4d2edd1c4b1dc443afc787a70 Mon Sep 17 00:00:00 2001 From: Valentin Kuznetsov Date: Tue, 24 Jun 2014 11:29:25 -0400 Subject: [PATCH] New code for ReqMgr web UI including changes for underlying WMCore --- src/html/ReqMgr/img/cms_logo.jpg | Bin 0 -> 11891 bytes src/html/ReqMgr/javascript/ajax_utils.js | 11 + src/html/ReqMgr/javascript/prototype.js | 7036 +++++++++++++++++ src/html/ReqMgr/javascript/utils.js | 86 +- src/html/ReqMgr/style/kube.min.css | 1 + src/html/ReqMgr/style/kube301-less/LICENSE | 21 + src/html/ReqMgr/style/kube301-less/README.md | 51 + .../ReqMgr/style/kube301-less/css/.DS_Store | Bin 0 -> 6148 bytes .../ReqMgr/style/kube301-less/css/kube.css | 1876 +++++ .../style/kube301-less/css/kube.min.css | 1 + src/html/ReqMgr/style/kube301-less/index.html | 14 + .../style/kube301-less/less/blocks.less | 71 + .../style/kube301-less/less/buttons.less | 170 + .../ReqMgr/style/kube301-less/less/forms.less | 209 + .../ReqMgr/style/kube301-less/less/grid.less | 262 + .../style/kube301-less/less/helpers.less | 92 + .../ReqMgr/style/kube301-less/less/kube.less | 20 + .../style/kube301-less/less/mixins.less | 185 + .../style/kube301-less/less/navigation.less | 244 + .../style/kube301-less/less/normalize.less | 225 + .../style/kube301-less/less/prettyprint.less | 69 + .../style/kube301-less/less/tables.less | 43 + .../style/kube301-less/less/typography.less | 324 + .../style/kube301-less/less/variables.less | 59 + src/html/ReqMgr/style/main.css | 88 + src/html/ReqMgr/templates/admin.tmpl | 190 + src/html/ReqMgr/templates/apis.tmpl | 36 + src/html/ReqMgr/templates/approve.tmpl | 78 + src/html/ReqMgr/templates/assign.tmpl | 337 + src/html/ReqMgr/templates/confirm.tmpl | 29 + src/html/ReqMgr/templates/create.tmpl | 92 + src/html/ReqMgr/templates/error.tmpl | 6 + src/html/ReqMgr/templates/generic.tmpl | 6 + src/html/ReqMgr/templates/json/dataproc.tmpl | 30 + src/html/ReqMgr/templates/json/mc.tmpl | 23 + src/html/ReqMgr/templates/json/redigi.tmpl | 37 + src/html/ReqMgr/templates/json/rereco.tmpl | 29 + src/html/ReqMgr/templates/json/resub.tmpl | 18 + src/html/ReqMgr/templates/json/storeres.tmpl | 21 + src/html/ReqMgr/templates/main.tmpl | 32 + src/html/ReqMgr/templates/menu.tmpl | 15 + src/html/ReqMgr/templates/requests.tmpl | 35 + src/html/ReqMgr/templates/search.tmpl | 22 + src/html/ReqMgr/templates/validate.tmpl | 9 + src/html/ReqMgr/templates/workflow.tmpl | 34 + src/python/WMCore/Cache/WMConfigCache.py | 55 + src/python/WMCore/Lexicon.py | 5 +- src/python/WMCore/ReqMgr/Service/Request.py | 8 +- .../WMCore/ReqMgr/Tools/WorkflowTools.py | 608 ++ src/python/WMCore/ReqMgr/Tools/__init__.py | 0 src/python/WMCore/ReqMgr/Tools/cms.py | 172 + .../WMCore/ReqMgr/Tools/reqMgrClient.py | 339 + src/python/WMCore/ReqMgr/Utils/__init__.py | 0 .../ReqMgr/Utils/jsonwrapper/.__init__.py.un~ | Bin 0 -> 5269 bytes .../ReqMgr/Utils/jsonwrapper/__init__.py | 172 + .../ReqMgr/Utils/jsonwrapper/__init__.py~ | 138 + src/python/WMCore/ReqMgr/Utils/url_utils.py | 144 + src/python/WMCore/ReqMgr/Web/ReqMgrService.py | 622 ++ src/python/WMCore/ReqMgr/Web/__init__.py | 0 src/python/WMCore/ReqMgr/Web/tools.py | 185 + src/python/WMCore/ReqMgr/Web/utils.py | 162 + src/python/WMCore/WMSpec/StdSpecs/StdBase.py | 55 +- src/python/WMCore/WMSpec/WMWorkload.py | 23 +- src/python/WMCore/WebTools/Root.py | 2 +- 64 files changed, 14904 insertions(+), 23 deletions(-) create mode 100644 src/html/ReqMgr/img/cms_logo.jpg create mode 100644 src/html/ReqMgr/javascript/ajax_utils.js create mode 100644 src/html/ReqMgr/javascript/prototype.js create mode 100644 src/html/ReqMgr/style/kube.min.css create mode 100644 src/html/ReqMgr/style/kube301-less/LICENSE create mode 100644 src/html/ReqMgr/style/kube301-less/README.md create mode 100644 src/html/ReqMgr/style/kube301-less/css/.DS_Store create mode 100644 src/html/ReqMgr/style/kube301-less/css/kube.css create mode 100644 src/html/ReqMgr/style/kube301-less/css/kube.min.css create mode 100644 src/html/ReqMgr/style/kube301-less/index.html create mode 100644 src/html/ReqMgr/style/kube301-less/less/blocks.less create mode 100644 src/html/ReqMgr/style/kube301-less/less/buttons.less create mode 100644 src/html/ReqMgr/style/kube301-less/less/forms.less create mode 100644 src/html/ReqMgr/style/kube301-less/less/grid.less create mode 100644 src/html/ReqMgr/style/kube301-less/less/helpers.less create mode 100644 src/html/ReqMgr/style/kube301-less/less/kube.less create mode 100644 src/html/ReqMgr/style/kube301-less/less/mixins.less create mode 100644 src/html/ReqMgr/style/kube301-less/less/navigation.less create mode 100644 src/html/ReqMgr/style/kube301-less/less/normalize.less create mode 100644 src/html/ReqMgr/style/kube301-less/less/prettyprint.less create mode 100644 src/html/ReqMgr/style/kube301-less/less/tables.less create mode 100644 src/html/ReqMgr/style/kube301-less/less/typography.less create mode 100644 src/html/ReqMgr/style/kube301-less/less/variables.less create mode 100644 src/html/ReqMgr/style/main.css create mode 100644 src/html/ReqMgr/templates/admin.tmpl create mode 100644 src/html/ReqMgr/templates/apis.tmpl create mode 100644 src/html/ReqMgr/templates/approve.tmpl create mode 100644 src/html/ReqMgr/templates/assign.tmpl create mode 100644 src/html/ReqMgr/templates/confirm.tmpl create mode 100644 src/html/ReqMgr/templates/create.tmpl create mode 100644 src/html/ReqMgr/templates/error.tmpl create mode 100644 src/html/ReqMgr/templates/generic.tmpl create mode 100644 src/html/ReqMgr/templates/json/dataproc.tmpl create mode 100644 src/html/ReqMgr/templates/json/mc.tmpl create mode 100644 src/html/ReqMgr/templates/json/redigi.tmpl create mode 100644 src/html/ReqMgr/templates/json/rereco.tmpl create mode 100644 src/html/ReqMgr/templates/json/resub.tmpl create mode 100644 src/html/ReqMgr/templates/json/storeres.tmpl create mode 100644 src/html/ReqMgr/templates/main.tmpl create mode 100644 src/html/ReqMgr/templates/menu.tmpl create mode 100644 src/html/ReqMgr/templates/requests.tmpl create mode 100644 src/html/ReqMgr/templates/search.tmpl create mode 100644 src/html/ReqMgr/templates/validate.tmpl create mode 100644 src/html/ReqMgr/templates/workflow.tmpl create mode 100644 src/python/WMCore/ReqMgr/Tools/WorkflowTools.py create mode 100644 src/python/WMCore/ReqMgr/Tools/__init__.py create mode 100644 src/python/WMCore/ReqMgr/Tools/cms.py create mode 100644 src/python/WMCore/ReqMgr/Tools/reqMgrClient.py create mode 100644 src/python/WMCore/ReqMgr/Utils/__init__.py create mode 100644 src/python/WMCore/ReqMgr/Utils/jsonwrapper/.__init__.py.un~ create mode 100644 src/python/WMCore/ReqMgr/Utils/jsonwrapper/__init__.py create mode 100644 src/python/WMCore/ReqMgr/Utils/jsonwrapper/__init__.py~ create mode 100644 src/python/WMCore/ReqMgr/Utils/url_utils.py create mode 100644 src/python/WMCore/ReqMgr/Web/ReqMgrService.py create mode 100644 src/python/WMCore/ReqMgr/Web/__init__.py create mode 100644 src/python/WMCore/ReqMgr/Web/tools.py create mode 100644 src/python/WMCore/ReqMgr/Web/utils.py diff --git a/src/html/ReqMgr/img/cms_logo.jpg b/src/html/ReqMgr/img/cms_logo.jpg new file mode 100644 index 0000000000000000000000000000000000000000..8769ce2770a837dd6cb6d9c1c481c7db10e2256e GIT binary patch literal 11891 zcmbt)XH?V8^KTFp0RicqsDN~kCdEj8iV{IdAoL=60O?W!B0+&Dy`yxg4?>6mLhn+9 z1bFC5?~u@>Ct$pTr<$q*8?yx zFaRE(cfc75@b(eP%^3hNGz7>30DvoiOAK59#&e3{yaO170GIwJ4FDK02>&+?XOR9c zo(lj#lpEmUfALtKkN>uF&HqaO*LdOY3;)e=|8K_sP5;Y!HVe@G*Pj2c_OFuvF9rV! zpM3>zUIE+(+-GFq1YF=`VB};tBLaZuNG|;^-T#{VZ)3Q?c=6I@rYp=Wtmh1M*8mq7 z7#S~IWW03g;<+Ojg3rGLE^=Ptx}kjkGPkik(@i9g%DWE*SHvGwb@0N!QzTR!d_$R8 z`1l0`g(PoDNz2Hpso&Ai)Y8^{_(%`(Sl__J)Xdz%(#qQLxs$VttDC#uOaE5^sKD1@ z;SrHh(J`^fDXD4c8JQomJ{1;Wim{(@CDk>xb@dI6P0gJIVpn(1KfQe;qhsR}lT*_( zB=Yjg>e~8`jZNzA-tYZ`L)xFCe{nGY82{U4|3_TsPGGoj@gn0zrhjoUTnP9VIOoMn zHCOf92c+J%4#iI=*&;a2KG&`(CV=>ESc z{xXBRu-V-q54J%KEojlaI$zUYGnmU6-&VzL(>gMmeF-ifi=84p4ULCX+JB0GYr=(I zmXK}T$c2=0Gm(cfP1dHLN}y$%Zz>{R?u5?pB=EbHfuT^oF51cbb?IiC4hbw6*^3jhtteSh#!HdOF#|fd2ku%csl4 zofCP$_GnC}iuPs+L6d?JpOiimn>=tk$)>B4vm0)Akh}$huF$LsHZ5ut*LQ#0RBQ|9 z0*P!l+l zdngTH(%N=&L`zxB)S(!?QIG6&(`Ch9EKtKPa6r~kpUU?a2nC(grO_GNdibx2FMf8D z^B!Zy$3h`J_%v`edV54qWRIdmidlDLzfb94lC^sL*L`y*@vGBH5@R_zx#ZUMV4jo) zNZXg4&wm_7lVoGvVe{V?(@M9{{rliy*y**!E9h^fq(wUS6q*|Qn?t+5`uM}~@UnO8 zsTA$eF+&i~L=f8>v9#q1J{s#16TzE#D}!025*1)f&4_sUj~D16PSf?#+K1vPyI$;| zG_%L&K{r%TSA#3!I6sV%ip&%P7DmUH*!%nu74f>69AH)eVTkyQ>(j7U%hlK0cm{d6 z^9r3a9iksLNQaO!5-}{apayNS$eThbw43zWR9)ZF%vxOPa+wKo#(N=2l+hMhC%*1qir4CorVIrm z*zwcSDA|N8vmHV`WxhDB)LkPB+Eh-z@uj{t>FHzzwk=!#3pFo-9aHRHfAp;N@Ajrf zt8(E#`9u&n2F0RfZrtNTbi~CAAoF0f*bFGtWa`H6ac``aP(r-5I+0v?EJZ|6GJmT<7#pc26DtvG~ z!`|H`^)||OsL1T89zUbmB!Se5fyknqqWxuq9q=q(YE`_2y)0?V#-mu4pwDD-pRGa^ z`ROcchvmSe3Mog@l~g@2sf~HUmNZxhVKWfO8C=2{7oOxtbT9MWY-41!m?8U23EGq5 zDlE8vTAlj(|Auq}+n!LK2L+!*>Tm^V)K8cYPOoQ^I69KJXd1R3B+_T~sTq+$QU!<~ z0otJtsvpk;Z69j;uaxqP%s_&?977@THuGlyU!U$9ODaR6-F^A@1e^pk9pE{EtC1BM z_PDVqxkvbQ`Lnm|Bc+?s5A!m{DOI_Y={!!7lN9_~IaQ8RFV(wWu#*QX z)ROG3nkp&KpY}*INZ+v$A>O_05>KG&ARr8O*jiH;)Uph&X*eSd!>rl$s2h}Ml3MmN z#KoKN8kkfEOfo_pcb~FFLB1=1oqn=*_~~B87ke}_(w2#5fNNeL*m4E4HSB7CKq&;X z@#?jCt{<*G*SmO&Z*QP}+d6NZ#B_!_gKX>fdASYHndHcws}P8I@SJ?aML-Db!&FN zlH%U+8MMP28CCMfO|@2H7|Z3&dgi(UnT74U{{c2f(a z7s^)o<| z8@9^kzE->OPo`VIEo~M-G1%c?(YcJIqk8Q+tA1V~pLB~b>`!GLq}FHg6)^2pQ&; zGiSE)C+nahR3lH3V)4@heT=Z7-)*D$*`q0)lS}nUls%%kH4|NaG3lu8Fh-%}5!urb z{tcvFy7{X4(ohCw+S+PE=vQf}@K3%)SOb+mZncK;v*`U?i1>0lug`+vSCp670M)W= zGDfw9oGukc3%Eg;=vfP#@Q4Z{81+{VNWxsq%ef5j-al=-OR!=6xIX_?CH{$%{Jk0FFgggF2IX290t`%#q+ zrp-#nZ~WB!Pc$htFB`b_p9{VvMaR_EI=5+zKhu@I-LM3!4H zS1htQHc32mr9mQX&YRWB>Q-9n$-X#!x^xUQden82b;`V@fv(Zmrhc|W5uSqGY21{= z6M1zYyWw$)-_kwy<-Rvt_dKseFwfN7i!c9S+snPecPgFq^EtWg8o6aXyk_`PqX)R3 zW}-2u`GzB|7ll>r-3!uVl;|Fo>YTudI5XYte8Q{o z8UtbqLC5k`j_B%^SHS{n`)-cDwhik2GLEWp&H^PqrXEg>pW!B5eLUQ`7J8{}&1qHK zzAIb)?Euss0oi}2@NZGQzMG3w6-sNr$L%6?>sqkt_QC6s3+*TyH|y~KXk+t=^+VCm zXokmzrwKaZ;~lFxkW80TE*DgtUOWz2EEGm+BztHphedI(Oy$@;3oVe>yNNWTg{JEZ zLO4K=yh|0WIZt0KFBUMx2Z=-YWB&1(_7+`ol_B4vd5#g*%Hy5P4MF+9Z7G{;8`9&s zpmyQ7l!LaRT>f6|nq|*l*ISYX>T(sYty;au$^J}p%{AA(p8C8fo%qo~Z^l74nsf&E zkB=su-Q!*7nw2w{HOGn8qUOF-^R&KJxl#TVUy^ONn1OylC(Q3o5f3G$!p=u zCfR?OL^B+!bDq8612aZ{b+)vwlb$3vx;glujuaKNM$-AY1AUAaG*T#P5RyMsnQ_y;RM*cGW=y`?|EET(@c$u&F-)L_B*2HLtQqE zh#Kw62p=r*(TM!$B&J0b^Ejkk)w$Iw1|(g9nqWXZ@iisB2PoN>!C9qOn-w*;U!=*Z zZII?s6X;1ht`mXV(+w;nx(D?*x&$Tj8{IjHljjSJ`3e&xbHq0GiFIO+8c0B9GA0V2 zw~SPm1&PVUV)QA$AVR2kJJ~wx%)q~9%YJh8dGsI0ZPX=%9F0vc(n8l}?%ug`*(v*! z^L(q_;M+4~iVV7*Aw4NYI0(@2-OR|RYEW-gknedWthQs%r!nGXql;=6GKtRxRjx*r z(+J08Bb^oFB88dt8wXeDs-Xw98_z$o30lc=h7ukAT9D#|7vfnsDM3C*kTEJ_)NT?B zU6T|f6}||ub=>_efBH9fY4WHhh zyz}ZOj|$3x9JrqgF{~L;@cO9(>p7w;q^zKx9%>G_*`vZ{;T@}jdc04BbEqKfz~|LJ zcIj`RJ=r$)rq#{3una|BE0`cmwLLrRA1#3OJ;PRX;o&dast&dRyKjx z&R-f{*!w^;9&*SeOlst3$RWs6_V|+qt3lXHFa;v@mX0c+TxO`U4tZ^`hWX zf2+#@SlQ>JIn!9?|5QvU9V~2~C?#yc5OxO8&N?dG8$Et_l3cOvg?3t;uAX7tx*Jyt zdy8N)e`BdwkWQZOAuO_Is1`xj1OY5pQkfk|f|pehdvD0~!h#W0;Uvkpg4jY4h%ogb zrMy+%OxAzHV#tprRZ8Ey^FgkG9~Q2d^Vt+9l#?^IIrvQX*y4KbRVXA?UH+ciNWYIy zavH)@2woV8G3@wW{0k%YNDFssX%()}btmcXx$4XFUs(j6OV8%3dL%D9zPOm`rV&15 zD(xe@BndTC*NFanl9ha39n={dQdEs!Y%q$trw*GjNnPjvf_1}M%r>+S%esGwPr-1w zej=&gDk?CXHi`93k|;G$$sW<}zMT{oWKD@8P%#J;fhaT>_0~K2vFA-{*yY*Jnx!T^NA1hs5RR_q< zE-b?-iO8zZia#mGFFls3Y#4X+=vS&j?bwa3CqL%eBTQ(g^7EnDfA>nTBz)f7D5dK? zUQlThrAYoh&~9Bk9{jo>=hAM_l|!P5$52Erb+o7_6sA(^o)+FCxQN}H7*IxmbZ!FJt(8~tmzCJMaoeQ&m)7R=s$dD6!UU$oJ)?O6r&?k=*Nfy) zw%C~t;UEARQFe)30t0bjDj>Cc<>D#>H7v>%*<|+z@H{Xuse~$+9-P$GlNhIS=xo;IgRv9VR(a8CDb_N1AKSLYw@v8x*JX)wq|Y z8(E1$jm3UiU}cCZ#@O(~i&`-O#@^+>(j>n9CMnd0$&nGfHtx;9$aD}uYDDeAgq;Sp zn(o7myR}^8Q*czqlp%s9~A^zZo$G!;qHk*#jm80rg);6 zd&}l9gyf>5gev%*J%|GT7+!)fK0g&eJn@+JqKpH;O1;&J`-bZgOlFEZZb_e6`5CG*KQhT0xg;P3ly! z70h$1171DJf>4SJ#d}43Yiy=F{74^HP=`<6kTx*R&bijGUevmDiv7gv?9_0??a z@ay!N%W=RHP)>hxH67AermP;MR;Cb4apqwJedtRw%?)XF$>V^CAV}OW%>e}(DHaNP z)G3(f*ePPTZ~F`s%+gd2iN=NIiL=HRXxURusgwkHGZAw>%nLqA)7+=>f584Q8q@6Q z$f^Hc+erQ%;S6vg=}-ROzVOqhka^%PxMg@+qcene68#)F4P|p7G>c(4PjW(-awlIK z=5`THhAI67m3v)v1U=`5EOT~hrUXAH8(!qYoJ!Wd>eGBl@>SNa$jkcaE&_tzQo5X` zClK!jD#tfE*>4}T+Z39IVa=MLMUqf|5nF3xtU46iQ|6_?7>_ey$RtzL3*+jAEUg=3 z{n7%*^BYSZl&u1NLAB%*cjbeizCvTvB2Rbj5}ThcrJeBMGpeEIRH9}~Jx(cnQak%e z(Z;DjUEtp>)b-dFkJpp0qXQBrSpH}tyqZ0biI|F_kNoO$ar03&Msw>CxN z-k?uX*lr6Uf;Os(*Am1)~stnq0EA`0nX52(7Tyn+D_{sB+r zN6*)zX(+T6N-l93dh@Gu4y3zj2q;VG7srdVtB?mm5c@gn%0pD6cuJ5=hoBz+W}0SC zEOG=|&Lmv`HSQplI4FNi3#tyc=ihS)-+wvbpGmhM;dRh6++_dQXI5Zhu)(k44&cQQ z(I?lzUf)B6-6!2DraIiX7|%cmHt`H#A~OC7y6J?jqCpC%vH}`*ll8bP0j>&4@a~y4VSA1ql*jK#632zTh6GAORO2G6h$l$VtjqS1 zZk*h6TiuV8l`78CxDJwxwqW`N%b8l8s+j(@&r32!-B1qB&>YN@j6E@>f9M)yP_?Dd zf*@r_przalInC?MCpSU1ZQ#i5R6R-;7bVz&5mCD|lkm0*kU2}aoLa==rUwEa)rU?U zYervYzscN3Y@0Ae{}jYKkJ0smakRp&X0xA#y>2+SDq2z(e{VucNM|Ej*1mb6chM7?q6g3 z=!MR+c#PRlgsc7Smw9~ke5PaNCFd0%-XQxU^OIXt^~R*mxy88Krvg3;kFp)B2WAOL zSA0P=1VvUP!Sv+>Uparc+o~pglEH*S+cMT?s79pItaAUy7+$FTOWldxros(Uw%r#G zR=icGB_WXQWGC9cKPKSzGhxC^P(YppRei4GL5b@G|4vhQ%CLL(Dxz%}!GmIbi+dmf z%QuSDF8f5$VrS7T8@)FWI98y`mhN-Pp7zvS5wHK?rHpAQ3y)i$QBp%}d=6Ni683|% z3`rQL@&ZS-co#FAOo6W!ROQyS4*y=m!gX7&_PS85^( zk5Z~Cw9~&o8p}F6?Wwr$!9Vi*r_Rx&=}3!$pJdtuKA?13D<3*Z50DH$Y<|k+x~JkU zCQ7-S_C_b)rR*U{m)vS%KJoL#%U0Gsd46_eRJ`DX4e?amiMLUSumB`}A8dR=koR{o zxi_o|Qnvzq(O7P-IvA5FlYhRsq#0<2(~EfRvDI#AjD_a^Rt)Sp~tjs_V4w7H2PfnGfxjoe1h~FC)Il>=fR7f$kD~aNHJ#0t+^`e zka0>BHLec5{wV~GrZ%7cb%4s-I|Fo-o&i$8t#~k)&U(sxq8l;+zKY-w+${Wo1g`{Qjf z9@Y|i<3wC@A@U9JkKSEb(l*pAdCe*j5!N#nsljBI`+|RxG9`qHjpMhA z@b-`g`H6JDtaN`_Ic58@^0%jXI-gzA_npoV3F;wnyC>o{%IYs#fP}xQm?BxEfyuhf zDQ#uS7EZAL;q=|#F#?~zuKu~KK@yW#8quFF$Bp2Q!|Sq!A7r9!X;vpq)b}3E?b$pa|7W0m^tI#s&PT|sDlp!~4P8Ni}3;Ld>bX!s9@#^F15 zGnU)8xN@yc`e@)=SPRy7@s<5*L|v0j5|Vi z30cJcQZOMNF4iJC?kO3KmZ`N~p6xilcnXqQlD;|GqS%}wQ#+#6a(>JtMGC zGpd#zhk8XEam~L(7HE|RWQZd`7)-b=(mGSO97NDLG)E9=L3CILpGsp}R&){B<2AS1 z5kO@SZSA9p&I0sD{sNB3CJok=X)!B(2PtSOlDA|{VB6nyI6c>Vtz=My$rAQ&_6mrYn#jeVTIJoWiB@*>q}U*zpOk1q|r__m*N$y5wi%$ zwjx5CYb~BSwcXDkXuz4EA(XoLToXku6dZ|{WX6re<8UUi@#0d)W;)kNf%A$Cq(Aza zLp+r&#@&5tt=Ya&wH-RQ(p;PR>_!TGv2Y{z+qvpPi$iG6mK*-0d~V6s^|%(l@_a70 z;W8NR7Upl1dWAo$aGZyNv9}Eg8v)|RY4TNX+!w4H=N32QOz8CT-cLFRq+T{#J_B5# z<})n&C2B8$8VCkWo~*XV2DD8vzdzph~QX|6j}kz)pD#H z=MJw@4F`v|WrQdYI-c%CqWVKy07oE4)p7+(x}rt`t^X}@F0f1U#jI3;?y($O#2LV* zX8Twc15acr%cm`xL=UnSiB{gEJbsT~JBh znd|1O^|$I(V^jr~m0zY=!c9sowo)B_d`~Tr9u4zvAB6Gfd^puXkt;vC#E3~!rMR7v zb6KolOiK>CiR_&Y?xrh*C7&S9&PMq!62)oeKS(!VQn?Q|zZdBFi)?mP5ADIm%GSS( zS_k59w&wNq+i=YqVo>3;MPcUc-&m@eo4)<-rxlL;HU69lMrAMX#OW6$ZMW)7>=^YB zb~ta470-*>f_z4#4lC$gw)E$qC-5R(9hDw9PCIZyT$V5vk2R6*$FiVaTeT=fb(=-# z8^@2tFBUF)zfGfG>Z61DQ0*}kWhy$h+B!$SJA`kE;(N3nF|DVgX+0+wN-hg3qKs)` z?-mHrwh>obz>}IG2~Sms#1x*@QNpzee>XTKeL2s z!WtwDhH~y;1HCB>e~@?+QATbN@e|`8uEZPomZq+89`dVOr(g_<$bY;}@bj_;*Sj@mhJ^le3_ct{(;uPPjzKN~XMixo8Q>PWBxp)!!`3tqx+J|5PPKnQPnRBV?aJsR{0KESng_^ z@RMV4aeVgbX8C>g8$F~S^5>5Vgk|v}PgPpf5#?lM%ei2wxZ)B5Ke(38>$o(F2GZ41 zh6KI3x_}5lu7iZ6nPv7c+9k51$}SoBSNY=PL5^1? zD%0%sGRB;)npr0)B_N!l3FtgjJ7y;_$RyuEJZf2JjxBW!RrX5J3KBIXqQnYE#qO5I z95fDum_5h~&UKWOmRb;t zveZ)>NvYFN@7*qhsva08h$m(w*eRLdgx*sfi5??qR(*6QVt=l2gh6U{o0GPG;Fi*B zt}wIUvZ9=I_lvYCV&u!cu7rNICOx-g3AWQ~s2Fm4vl{lTwn-NGG<8k;0rt=2XX^-`X5@gW}E%(R>eiz$&@0V+W7Mb&dy`}oA z)G0w0Y0u}Z49RjN*M6=%sk>htkX5l2zv^I|Vvoz+Jnjs!l6yk^*)9_Z83eKtanM1$ zbPz)X>9t>NF`aqHuYIp;EQ4<*vWC)pK>vt<)Ww=Me+dY5R_*l_#l)hwhNu7ZH2wlp z3BV4zsxAXb%rBP$^^C@Mfw(hPElZ~66d{Qn%L`8uI-E6`N$&Qt$1Vu=f@fs=I4ra8 zD8_gwf3IPrKM}=!gHjKIb)R0KA;``^1#*0GI#8m^8RXa5_*yfkXDibw(|$phxErp_ zW1F+QO&P;!Gn-`jL`cHA6>A$SmK*EG>xMbE>Md9$EVQ4kE=ILz6>ciu-d$j`>nE*8 z(gg#{W_p3S-T}p%N0@^TZ#+Tu4M#7iZwpT{E0%PmqP5Q7aF8>iLFg2dQs({}Lap`F_MxA(z6%P<4N7H=DKTEjssRsORT`CI{+fbXWC%&nVWbHY>)ejz z!9e4FzxHdI1gQiAXs$ua-Aq(ej?2`GY~JCRl7fV<&&R5aaC)&3A+P=3p(AGi?`}p4 zcwP_Xi5*&4n3z$EU6`1dnb0KF)z-f=pP3oqNt~IuvK%=hP!R8R(!-EW@-1WMknO*1 zY5PU4-xc2JG$ntBFP)oBm)Ria1~g4bO%;_&j8=L zkgM&x%3sk_Ui|-Fx$1-fMbN}{wO{~vdF2eyeg>$$6f(>~o;U+kc$|lAk;ds}EBfF7 zX-3C)gLf^Eu#?ol_0uvcWJ4=}9!CKmFh!o5aQpWD8G!aX>a;E84DfImVN8;ur#C7c rUmO7M^SSgR&j9q4aqzYh>$!E0SGJDE=(nW-e{*DP?d|P{olXA_qO3&M literal 0 HcmV?d00001 diff --git a/src/html/ReqMgr/javascript/ajax_utils.js b/src/html/ReqMgr/javascript/ajax_utils.js new file mode 100644 index 0000000000..b0ae04c39c --- /dev/null +++ b/src/html/ReqMgr/javascript/ajax_utils.js @@ -0,0 +1,11 @@ +function ajaxApproveRequests(base, method, ids) { + // base is a URL base, e.g. https://cmsweb.cern.ch + // method is request method, e.g. /request + // ids are request ids which needs to be approved + new Ajax.Updater('response', base+'/'+method, + { method: 'get' , + parameters : {'ids': ids}, + onException: function() {return;}, + onComplete : function() {return;} + }); +} diff --git a/src/html/ReqMgr/javascript/prototype.js b/src/html/ReqMgr/javascript/prototype.js new file mode 100644 index 0000000000..37dd39ac2f --- /dev/null +++ b/src/html/ReqMgr/javascript/prototype.js @@ -0,0 +1,7036 @@ +/* Prototype JavaScript framework, version 1.7.1 + * (c) 2005-2010 Sam Stephenson + * + * Prototype is freely distributable under the terms of an MIT-style license. + * For details, see the Prototype web site: http://www.prototypejs.org/ + * + *--------------------------------------------------------------------------*/ + +var Prototype = { + + Version: '1.7.1', + + Browser: (function(){ + var ua = navigator.userAgent; + var isOpera = Object.prototype.toString.call(window.opera) == '[object Opera]'; + return { + IE: !!window.attachEvent && !isOpera, + Opera: isOpera, + WebKit: ua.indexOf('AppleWebKit/') > -1, + Gecko: ua.indexOf('Gecko') > -1 && ua.indexOf('KHTML') === -1, + MobileSafari: /Apple.*Mobile/.test(ua) + } + })(), + + BrowserFeatures: { + XPath: !!document.evaluate, + + SelectorsAPI: !!document.querySelector, + + ElementExtensions: (function() { + var constructor = window.Element || window.HTMLElement; + return !!(constructor && constructor.prototype); + })(), + SpecificElementExtensions: (function() { + if (typeof window.HTMLDivElement !== 'undefined') + return true; + + var div = document.createElement('div'), + form = document.createElement('form'), + isSupported = false; + + if (div['__proto__'] && (div['__proto__'] !== form['__proto__'])) { + isSupported = true; + } + + div = form = null; + + return isSupported; + })() + }, + + ScriptFragment: ']*>([\\S\\s]*?)<\/script\\s*>', + JSONFilter: /^\/\*-secure-([\s\S]*)\*\/\s*$/, + + emptyFunction: function() { }, + + K: function(x) { return x } +}; + +if (Prototype.Browser.MobileSafari) + Prototype.BrowserFeatures.SpecificElementExtensions = false; +/* Based on Alex Arnell's inheritance implementation. */ + +var Class = (function() { + + var IS_DONTENUM_BUGGY = (function(){ + for (var p in { toString: 1 }) { + if (p === 'toString') return false; + } + return true; + })(); + + function subclass() {}; + function create() { + var parent = null, properties = $A(arguments); + if (Object.isFunction(properties[0])) + parent = properties.shift(); + + function klass() { + this.initialize.apply(this, arguments); + } + + Object.extend(klass, Class.Methods); + klass.superclass = parent; + klass.subclasses = []; + + if (parent) { + subclass.prototype = parent.prototype; + klass.prototype = new subclass; + parent.subclasses.push(klass); + } + + for (var i = 0, length = properties.length; i < length; i++) + klass.addMethods(properties[i]); + + if (!klass.prototype.initialize) + klass.prototype.initialize = Prototype.emptyFunction; + + klass.prototype.constructor = klass; + return klass; + } + + function addMethods(source) { + var ancestor = this.superclass && this.superclass.prototype, + properties = Object.keys(source); + + if (IS_DONTENUM_BUGGY) { + if (source.toString != Object.prototype.toString) + properties.push("toString"); + if (source.valueOf != Object.prototype.valueOf) + properties.push("valueOf"); + } + + for (var i = 0, length = properties.length; i < length; i++) { + var property = properties[i], value = source[property]; + if (ancestor && Object.isFunction(value) && + value.argumentNames()[0] == "$super") { + var method = value; + value = (function(m) { + return function() { return ancestor[m].apply(this, arguments); }; + })(property).wrap(method); + + value.valueOf = (function(method) { + return function() { return method.valueOf.call(method); }; + })(method); + + value.toString = (function(method) { + return function() { return method.toString.call(method); }; + })(method); + } + this.prototype[property] = value; + } + + return this; + } + + return { + create: create, + Methods: { + addMethods: addMethods + } + }; +})(); +(function() { + + var _toString = Object.prototype.toString, + _hasOwnProperty = Object.prototype.hasOwnProperty, + NULL_TYPE = 'Null', + UNDEFINED_TYPE = 'Undefined', + BOOLEAN_TYPE = 'Boolean', + NUMBER_TYPE = 'Number', + STRING_TYPE = 'String', + OBJECT_TYPE = 'Object', + FUNCTION_CLASS = '[object Function]', + BOOLEAN_CLASS = '[object Boolean]', + NUMBER_CLASS = '[object Number]', + STRING_CLASS = '[object String]', + ARRAY_CLASS = '[object Array]', + DATE_CLASS = '[object Date]', + NATIVE_JSON_STRINGIFY_SUPPORT = window.JSON && + typeof JSON.stringify === 'function' && + JSON.stringify(0) === '0' && + typeof JSON.stringify(Prototype.K) === 'undefined'; + + + + var DONT_ENUMS = ['toString', 'toLocaleString', 'valueOf', + 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable', 'constructor']; + + var IS_DONTENUM_BUGGY = (function(){ + for (var p in { toString: 1 }) { + if (p === 'toString') return false; + } + return true; + })(); + + function Type(o) { + switch(o) { + case null: return NULL_TYPE; + case (void 0): return UNDEFINED_TYPE; + } + var type = typeof o; + switch(type) { + case 'boolean': return BOOLEAN_TYPE; + case 'number': return NUMBER_TYPE; + case 'string': return STRING_TYPE; + } + return OBJECT_TYPE; + } + + function extend(destination, source) { + for (var property in source) + destination[property] = source[property]; + return destination; + } + + function inspect(object) { + try { + if (isUndefined(object)) return 'undefined'; + if (object === null) return 'null'; + return object.inspect ? object.inspect() : String(object); + } catch (e) { + if (e instanceof RangeError) return '...'; + throw e; + } + } + + function toJSON(value) { + return Str('', { '': value }, []); + } + + function Str(key, holder, stack) { + var value = holder[key]; + if (Type(value) === OBJECT_TYPE && typeof value.toJSON === 'function') { + value = value.toJSON(key); + } + + var _class = _toString.call(value); + + switch (_class) { + case NUMBER_CLASS: + case BOOLEAN_CLASS: + case STRING_CLASS: + value = value.valueOf(); + } + + switch (value) { + case null: return 'null'; + case true: return 'true'; + case false: return 'false'; + } + + var type = typeof value; + switch (type) { + case 'string': + return value.inspect(true); + case 'number': + return isFinite(value) ? String(value) : 'null'; + case 'object': + + for (var i = 0, length = stack.length; i < length; i++) { + if (stack[i] === value) { + throw new TypeError("Cyclic reference to '" + value + "' in object"); + } + } + stack.push(value); + + var partial = []; + if (_class === ARRAY_CLASS) { + for (var i = 0, length = value.length; i < length; i++) { + var str = Str(i, value, stack); + partial.push(typeof str === 'undefined' ? 'null' : str); + } + partial = '[' + partial.join(',') + ']'; + } else { + var keys = Object.keys(value); + for (var i = 0, length = keys.length; i < length; i++) { + var key = keys[i], str = Str(key, value, stack); + if (typeof str !== "undefined") { + partial.push(key.inspect(true)+ ':' + str); + } + } + partial = '{' + partial.join(',') + '}'; + } + stack.pop(); + return partial; + } + } + + function stringify(object) { + return JSON.stringify(object); + } + + function toQueryString(object) { + return $H(object).toQueryString(); + } + + function toHTML(object) { + return object && object.toHTML ? object.toHTML() : String.interpret(object); + } + + function keys(object) { + if (Type(object) !== OBJECT_TYPE) { throw new TypeError(); } + var results = []; + for (var property in object) { + if (_hasOwnProperty.call(object, property)) + results.push(property); + } + + if (IS_DONTENUM_BUGGY) { + for (var i = 0; property = DONT_ENUMS[i]; i++) { + if (_hasOwnProperty.call(object, property)) + results.push(property); + } + } + + return results; + } + + function values(object) { + var results = []; + for (var property in object) + results.push(object[property]); + return results; + } + + function clone(object) { + return extend({ }, object); + } + + function isElement(object) { + return !!(object && object.nodeType == 1); + } + + function isArray(object) { + return _toString.call(object) === ARRAY_CLASS; + } + + var hasNativeIsArray = (typeof Array.isArray == 'function') + && Array.isArray([]) && !Array.isArray({}); + + if (hasNativeIsArray) { + isArray = Array.isArray; + } + + function isHash(object) { + return object instanceof Hash; + } + + function isFunction(object) { + return _toString.call(object) === FUNCTION_CLASS; + } + + function isString(object) { + return _toString.call(object) === STRING_CLASS; + } + + function isNumber(object) { + return _toString.call(object) === NUMBER_CLASS; + } + + function isDate(object) { + return _toString.call(object) === DATE_CLASS; + } + + function isUndefined(object) { + return typeof object === "undefined"; + } + + extend(Object, { + extend: extend, + inspect: inspect, + toJSON: NATIVE_JSON_STRINGIFY_SUPPORT ? stringify : toJSON, + toQueryString: toQueryString, + toHTML: toHTML, + keys: Object.keys || keys, + values: values, + clone: clone, + isElement: isElement, + isArray: isArray, + isHash: isHash, + isFunction: isFunction, + isString: isString, + isNumber: isNumber, + isDate: isDate, + isUndefined: isUndefined + }); +})(); +Object.extend(Function.prototype, (function() { + var slice = Array.prototype.slice; + + function update(array, args) { + var arrayLength = array.length, length = args.length; + while (length--) array[arrayLength + length] = args[length]; + return array; + } + + function merge(array, args) { + array = slice.call(array, 0); + return update(array, args); + } + + function argumentNames() { + var names = this.toString().match(/^[\s\(]*function[^(]*\(([^)]*)\)/)[1] + .replace(/\/\/.*?[\r\n]|\/\*(?:.|[\r\n])*?\*\//g, '') + .replace(/\s+/g, '').split(','); + return names.length == 1 && !names[0] ? [] : names; + } + + + function bind(context) { + if (arguments.length < 2 && Object.isUndefined(arguments[0])) + return this; + + if (!Object.isFunction(this)) + throw new TypeError("The object is not callable."); + + var nop = function() {}; + var __method = this, args = slice.call(arguments, 1); + + var bound = function() { + var a = merge(args, arguments), c = context; + var c = this instanceof bound ? this : context; + return __method.apply(c, a); + }; + + nop.prototype = this.prototype; + bound.prototype = new nop(); + + return bound; + } + + function bindAsEventListener(context) { + var __method = this, args = slice.call(arguments, 1); + return function(event) { + var a = update([event || window.event], args); + return __method.apply(context, a); + } + } + + function curry() { + if (!arguments.length) return this; + var __method = this, args = slice.call(arguments, 0); + return function() { + var a = merge(args, arguments); + return __method.apply(this, a); + } + } + + function delay(timeout) { + var __method = this, args = slice.call(arguments, 1); + timeout = timeout * 1000; + return window.setTimeout(function() { + return __method.apply(__method, args); + }, timeout); + } + + function defer() { + var args = update([0.01], arguments); + return this.delay.apply(this, args); + } + + function wrap(wrapper) { + var __method = this; + return function() { + var a = update([__method.bind(this)], arguments); + return wrapper.apply(this, a); + } + } + + function methodize() { + if (this._methodized) return this._methodized; + var __method = this; + return this._methodized = function() { + var a = update([this], arguments); + return __method.apply(null, a); + }; + } + + var extensions = { + argumentNames: argumentNames, + bindAsEventListener: bindAsEventListener, + curry: curry, + delay: delay, + defer: defer, + wrap: wrap, + methodize: methodize + }; + + if (!Function.prototype.bind) + extensions.bind = bind; + + return extensions; +})()); + + + +(function(proto) { + + + function toISOString() { + return this.getUTCFullYear() + '-' + + (this.getUTCMonth() + 1).toPaddedString(2) + '-' + + this.getUTCDate().toPaddedString(2) + 'T' + + this.getUTCHours().toPaddedString(2) + ':' + + this.getUTCMinutes().toPaddedString(2) + ':' + + this.getUTCSeconds().toPaddedString(2) + 'Z'; + } + + + function toJSON() { + return this.toISOString(); + } + + if (!proto.toISOString) proto.toISOString = toISOString; + if (!proto.toJSON) proto.toJSON = toJSON; + +})(Date.prototype); + + +RegExp.prototype.match = RegExp.prototype.test; + +RegExp.escape = function(str) { + return String(str).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1'); +}; +var PeriodicalExecuter = Class.create({ + initialize: function(callback, frequency) { + this.callback = callback; + this.frequency = frequency; + this.currentlyExecuting = false; + + this.registerCallback(); + }, + + registerCallback: function() { + this.timer = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000); + }, + + execute: function() { + this.callback(this); + }, + + stop: function() { + if (!this.timer) return; + clearInterval(this.timer); + this.timer = null; + }, + + onTimerEvent: function() { + if (!this.currentlyExecuting) { + try { + this.currentlyExecuting = true; + this.execute(); + this.currentlyExecuting = false; + } catch(e) { + this.currentlyExecuting = false; + throw e; + } + } + } +}); +Object.extend(String, { + interpret: function(value) { + return value == null ? '' : String(value); + }, + specialChar: { + '\b': '\\b', + '\t': '\\t', + '\n': '\\n', + '\f': '\\f', + '\r': '\\r', + '\\': '\\\\' + } +}); + +Object.extend(String.prototype, (function() { + var NATIVE_JSON_PARSE_SUPPORT = window.JSON && + typeof JSON.parse === 'function' && + JSON.parse('{"test": true}').test; + + function prepareReplacement(replacement) { + if (Object.isFunction(replacement)) return replacement; + var template = new Template(replacement); + return function(match) { return template.evaluate(match) }; + } + + function gsub(pattern, replacement) { + var result = '', source = this, match; + replacement = prepareReplacement(replacement); + + if (Object.isString(pattern)) + pattern = RegExp.escape(pattern); + + if (!(pattern.length || pattern.source)) { + replacement = replacement(''); + return replacement + source.split('').join(replacement) + replacement; + } + + while (source.length > 0) { + if (match = source.match(pattern)) { + result += source.slice(0, match.index); + result += String.interpret(replacement(match)); + source = source.slice(match.index + match[0].length); + } else { + result += source, source = ''; + } + } + return result; + } + + function sub(pattern, replacement, count) { + replacement = prepareReplacement(replacement); + count = Object.isUndefined(count) ? 1 : count; + + return this.gsub(pattern, function(match) { + if (--count < 0) return match[0]; + return replacement(match); + }); + } + + function scan(pattern, iterator) { + this.gsub(pattern, iterator); + return String(this); + } + + function truncate(length, truncation) { + length = length || 30; + truncation = Object.isUndefined(truncation) ? '...' : truncation; + return this.length > length ? + this.slice(0, length - truncation.length) + truncation : String(this); + } + + function strip() { + return this.replace(/^\s+/, '').replace(/\s+$/, ''); + } + + function stripTags() { + return this.replace(/<\w+(\s+("[^"]*"|'[^']*'|[^>])+)?>|<\/\w+>/gi, ''); + } + + function stripScripts() { + return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), ''); + } + + function extractScripts() { + var matchAll = new RegExp(Prototype.ScriptFragment, 'img'), + matchOne = new RegExp(Prototype.ScriptFragment, 'im'); + return (this.match(matchAll) || []).map(function(scriptTag) { + return (scriptTag.match(matchOne) || ['', ''])[1]; + }); + } + + function evalScripts() { + return this.extractScripts().map(function(script) { return eval(script); }); + } + + function escapeHTML() { + return this.replace(/&/g,'&').replace(//g,'>'); + } + + function unescapeHTML() { + return this.stripTags().replace(/</g,'<').replace(/>/g,'>').replace(/&/g,'&'); + } + + + function toQueryParams(separator) { + var match = this.strip().match(/([^?#]*)(#.*)?$/); + if (!match) return { }; + + return match[1].split(separator || '&').inject({ }, function(hash, pair) { + if ((pair = pair.split('='))[0]) { + var key = decodeURIComponent(pair.shift()), + value = pair.length > 1 ? pair.join('=') : pair[0]; + + if (value != undefined) value = decodeURIComponent(value); + + if (key in hash) { + if (!Object.isArray(hash[key])) hash[key] = [hash[key]]; + hash[key].push(value); + } + else hash[key] = value; + } + return hash; + }); + } + + function toArray() { + return this.split(''); + } + + function succ() { + return this.slice(0, this.length - 1) + + String.fromCharCode(this.charCodeAt(this.length - 1) + 1); + } + + function times(count) { + return count < 1 ? '' : new Array(count + 1).join(this); + } + + function camelize() { + return this.replace(/-+(.)?/g, function(match, chr) { + return chr ? chr.toUpperCase() : ''; + }); + } + + function capitalize() { + return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase(); + } + + function underscore() { + return this.replace(/::/g, '/') + .replace(/([A-Z]+)([A-Z][a-z])/g, '$1_$2') + .replace(/([a-z\d])([A-Z])/g, '$1_$2') + .replace(/-/g, '_') + .toLowerCase(); + } + + function dasherize() { + return this.replace(/_/g, '-'); + } + + function inspect(useDoubleQuotes) { + var escapedString = this.replace(/[\x00-\x1f\\]/g, function(character) { + if (character in String.specialChar) { + return String.specialChar[character]; + } + return '\\u00' + character.charCodeAt().toPaddedString(2, 16); + }); + if (useDoubleQuotes) return '"' + escapedString.replace(/"/g, '\\"') + '"'; + return "'" + escapedString.replace(/'/g, '\\\'') + "'"; + } + + function unfilterJSON(filter) { + return this.replace(filter || Prototype.JSONFilter, '$1'); + } + + function isJSON() { + var str = this; + if (str.blank()) return false; + str = str.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@'); + str = str.replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']'); + str = str.replace(/(?:^|:|,)(?:\s*\[)+/g, ''); + return (/^[\],:{}\s]*$/).test(str); + } + + function evalJSON(sanitize) { + var json = this.unfilterJSON(), + cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g; + if (cx.test(json)) { + json = json.replace(cx, function (a) { + return '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); + }); + } + try { + if (!sanitize || json.isJSON()) return eval('(' + json + ')'); + } catch (e) { } + throw new SyntaxError('Badly formed JSON string: ' + this.inspect()); + } + + function parseJSON() { + var json = this.unfilterJSON(); + return JSON.parse(json); + } + + function include(pattern) { + return this.indexOf(pattern) > -1; + } + + function startsWith(pattern) { + return this.lastIndexOf(pattern, 0) === 0; + } + + function endsWith(pattern) { + var d = this.length - pattern.length; + return d >= 0 && this.indexOf(pattern, d) === d; + } + + function empty() { + return this == ''; + } + + function blank() { + return /^\s*$/.test(this); + } + + function interpolate(object, pattern) { + return new Template(this, pattern).evaluate(object); + } + + return { + gsub: gsub, + sub: sub, + scan: scan, + truncate: truncate, + strip: String.prototype.trim || strip, + stripTags: stripTags, + stripScripts: stripScripts, + extractScripts: extractScripts, + evalScripts: evalScripts, + escapeHTML: escapeHTML, + unescapeHTML: unescapeHTML, + toQueryParams: toQueryParams, + parseQuery: toQueryParams, + toArray: toArray, + succ: succ, + times: times, + camelize: camelize, + capitalize: capitalize, + underscore: underscore, + dasherize: dasherize, + inspect: inspect, + unfilterJSON: unfilterJSON, + isJSON: isJSON, + evalJSON: NATIVE_JSON_PARSE_SUPPORT ? parseJSON : evalJSON, + include: include, + startsWith: startsWith, + endsWith: endsWith, + empty: empty, + blank: blank, + interpolate: interpolate + }; +})()); + +var Template = Class.create({ + initialize: function(template, pattern) { + this.template = template.toString(); + this.pattern = pattern || Template.Pattern; + }, + + evaluate: function(object) { + if (object && Object.isFunction(object.toTemplateReplacements)) + object = object.toTemplateReplacements(); + + return this.template.gsub(this.pattern, function(match) { + if (object == null) return (match[1] + ''); + + var before = match[1] || ''; + if (before == '\\') return match[2]; + + var ctx = object, expr = match[3], + pattern = /^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/; + + match = pattern.exec(expr); + if (match == null) return before; + + while (match != null) { + var comp = match[1].startsWith('[') ? match[2].replace(/\\\\]/g, ']') : match[1]; + ctx = ctx[comp]; + if (null == ctx || '' == match[3]) break; + expr = expr.substring('[' == match[3] ? match[1].length : match[0].length); + match = pattern.exec(expr); + } + + return before + String.interpret(ctx); + }); + } +}); +Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/; + +var $break = { }; + +var Enumerable = (function() { + function each(iterator, context) { + try { + this._each(iterator, context); + } catch (e) { + if (e != $break) throw e; + } + return this; + } + + function eachSlice(number, iterator, context) { + var index = -number, slices = [], array = this.toArray(); + if (number < 1) return array; + while ((index += number) < array.length) + slices.push(array.slice(index, index+number)); + return slices.collect(iterator, context); + } + + function all(iterator, context) { + iterator = iterator || Prototype.K; + var result = true; + this.each(function(value, index) { + result = result && !!iterator.call(context, value, index, this); + if (!result) throw $break; + }, this); + return result; + } + + function any(iterator, context) { + iterator = iterator || Prototype.K; + var result = false; + this.each(function(value, index) { + if (result = !!iterator.call(context, value, index, this)) + throw $break; + }, this); + return result; + } + + function collect(iterator, context) { + iterator = iterator || Prototype.K; + var results = []; + this.each(function(value, index) { + results.push(iterator.call(context, value, index, this)); + }, this); + return results; + } + + function detect(iterator, context) { + var result; + this.each(function(value, index) { + if (iterator.call(context, value, index, this)) { + result = value; + throw $break; + } + }, this); + return result; + } + + function findAll(iterator, context) { + var results = []; + this.each(function(value, index) { + if (iterator.call(context, value, index, this)) + results.push(value); + }, this); + return results; + } + + function grep(filter, iterator, context) { + iterator = iterator || Prototype.K; + var results = []; + + if (Object.isString(filter)) + filter = new RegExp(RegExp.escape(filter)); + + this.each(function(value, index) { + if (filter.match(value)) + results.push(iterator.call(context, value, index, this)); + }, this); + return results; + } + + function include(object) { + if (Object.isFunction(this.indexOf)) + if (this.indexOf(object) != -1) return true; + + var found = false; + this.each(function(value) { + if (value == object) { + found = true; + throw $break; + } + }); + return found; + } + + function inGroupsOf(number, fillWith) { + fillWith = Object.isUndefined(fillWith) ? null : fillWith; + return this.eachSlice(number, function(slice) { + while(slice.length < number) slice.push(fillWith); + return slice; + }); + } + + function inject(memo, iterator, context) { + this.each(function(value, index) { + memo = iterator.call(context, memo, value, index, this); + }, this); + return memo; + } + + function invoke(method) { + var args = $A(arguments).slice(1); + return this.map(function(value) { + return value[method].apply(value, args); + }); + } + + function max(iterator, context) { + iterator = iterator || Prototype.K; + var result; + this.each(function(value, index) { + value = iterator.call(context, value, index, this); + if (result == null || value >= result) + result = value; + }, this); + return result; + } + + function min(iterator, context) { + iterator = iterator || Prototype.K; + var result; + this.each(function(value, index) { + value = iterator.call(context, value, index, this); + if (result == null || value < result) + result = value; + }, this); + return result; + } + + function partition(iterator, context) { + iterator = iterator || Prototype.K; + var trues = [], falses = []; + this.each(function(value, index) { + (iterator.call(context, value, index, this) ? + trues : falses).push(value); + }, this); + return [trues, falses]; + } + + function pluck(property) { + var results = []; + this.each(function(value) { + results.push(value[property]); + }); + return results; + } + + function reject(iterator, context) { + var results = []; + this.each(function(value, index) { + if (!iterator.call(context, value, index, this)) + results.push(value); + }, this); + return results; + } + + function sortBy(iterator, context) { + return this.map(function(value, index) { + return { + value: value, + criteria: iterator.call(context, value, index, this) + }; + }, this).sort(function(left, right) { + var a = left.criteria, b = right.criteria; + return a < b ? -1 : a > b ? 1 : 0; + }).pluck('value'); + } + + function toArray() { + return this.map(); + } + + function zip() { + var iterator = Prototype.K, args = $A(arguments); + if (Object.isFunction(args.last())) + iterator = args.pop(); + + var collections = [this].concat(args).map($A); + return this.map(function(value, index) { + return iterator(collections.pluck(index)); + }); + } + + function size() { + return this.toArray().length; + } + + function inspect() { + return '#'; + } + + + + + + + + + + return { + each: each, + eachSlice: eachSlice, + all: all, + every: all, + any: any, + some: any, + collect: collect, + map: collect, + detect: detect, + findAll: findAll, + select: findAll, + filter: findAll, + grep: grep, + include: include, + member: include, + inGroupsOf: inGroupsOf, + inject: inject, + invoke: invoke, + max: max, + min: min, + partition: partition, + pluck: pluck, + reject: reject, + sortBy: sortBy, + toArray: toArray, + entries: toArray, + zip: zip, + size: size, + inspect: inspect, + find: detect + }; +})(); + +function $A(iterable) { + if (!iterable) return []; + if ('toArray' in Object(iterable)) return iterable.toArray(); + var length = iterable.length || 0, results = new Array(length); + while (length--) results[length] = iterable[length]; + return results; +} + + +function $w(string) { + if (!Object.isString(string)) return []; + string = string.strip(); + return string ? string.split(/\s+/) : []; +} + +Array.from = $A; + + +(function() { + var arrayProto = Array.prototype, + slice = arrayProto.slice, + _each = arrayProto.forEach; // use native browser JS 1.6 implementation if available + + function each(iterator, context) { + for (var i = 0, length = this.length >>> 0; i < length; i++) { + if (i in this) iterator.call(context, this[i], i, this); + } + } + if (!_each) _each = each; + + function clear() { + this.length = 0; + return this; + } + + function first() { + return this[0]; + } + + function last() { + return this[this.length - 1]; + } + + function compact() { + return this.select(function(value) { + return value != null; + }); + } + + function flatten() { + return this.inject([], function(array, value) { + if (Object.isArray(value)) + return array.concat(value.flatten()); + array.push(value); + return array; + }); + } + + function without() { + var values = slice.call(arguments, 0); + return this.select(function(value) { + return !values.include(value); + }); + } + + function reverse(inline) { + return (inline === false ? this.toArray() : this)._reverse(); + } + + function uniq(sorted) { + return this.inject([], function(array, value, index) { + if (0 == index || (sorted ? array.last() != value : !array.include(value))) + array.push(value); + return array; + }); + } + + function intersect(array) { + return this.uniq().findAll(function(item) { + return array.indexOf(item) !== -1; + }); + } + + + function clone() { + return slice.call(this, 0); + } + + function size() { + return this.length; + } + + function inspect() { + return '[' + this.map(Object.inspect).join(', ') + ']'; + } + + function indexOf(item, i) { + if (this == null) throw new TypeError(); + + var array = Object(this), length = array.length >>> 0; + if (length === 0) return -1; + + i = Number(i); + if (isNaN(i)) { + i = 0; + } else if (i !== 0 && isFinite(i)) { + i = (i > 0 ? 1 : -1) * Math.floor(Math.abs(i)); + } + + if (i > length) return -1; + + var k = i >= 0 ? i : Math.max(length - Math.abs(i), 0); + for (; k < length; k++) + if (k in array && array[k] === item) return k; + return -1; + } + + + function lastIndexOf(item, i) { + if (this == null) throw new TypeError(); + + var array = Object(this), length = array.length >>> 0; + if (length === 0) return -1; + + if (!Object.isUndefined(i)) { + i = Number(i); + if (isNaN(i)) { + i = 0; + } else if (i !== 0 && isFinite(i)) { + i = (i > 0 ? 1 : -1) * Math.floor(Math.abs(i)); + } + } else { + i = length; + } + + var k = i >= 0 ? Math.min(i, length - 1) : + length - Math.abs(i); + + for (; k >= 0; k--) + if (k in array && array[k] === item) return k; + return -1; + } + + function concat(_) { + var array = [], items = slice.call(arguments, 0), item, n = 0; + items.unshift(this); + for (var i = 0, length = items.length; i < length; i++) { + item = items[i]; + if (Object.isArray(item) && !('callee' in item)) { + for (var j = 0, arrayLength = item.length; j < arrayLength; j++) { + if (j in item) array[n] = item[j]; + n++; + } + } else { + array[n++] = item; + } + } + array.length = n; + return array; + } + + + function wrapNative(method) { + return function() { + if (arguments.length === 0) { + return method.call(this, Prototype.K); + } else if (arguments[0] === undefined) { + var args = slice.call(arguments, 1); + args.unshift(Prototype.K); + return method.apply(this, args); + } else { + return method.apply(this, arguments); + } + }; + } + + + function map(iterator) { + if (this == null) throw new TypeError(); + iterator = iterator || Prototype.K; + + var object = Object(this); + var results = [], context = arguments[1], n = 0; + + for (var i = 0, length = object.length >>> 0; i < length; i++) { + if (i in object) { + results[n] = iterator.call(context, object[i], i, object); + } + n++; + } + results.length = n; + return results; + } + + if (arrayProto.map) { + map = wrapNative(Array.prototype.map); + } + + function filter(iterator) { + if (this == null || !Object.isFunction(iterator)) + throw new TypeError(); + + var object = Object(this); + var results = [], context = arguments[1], value; + + for (var i = 0, length = object.length >>> 0; i < length; i++) { + if (i in object) { + value = object[i]; + if (iterator.call(context, value, i, object)) { + results.push(value); + } + } + } + return results; + } + + if (arrayProto.filter) { + filter = Array.prototype.filter; + } + + function some(iterator) { + if (this == null) throw new TypeError(); + iterator = iterator || Prototype.K; + var context = arguments[1]; + + var object = Object(this); + for (var i = 0, length = object.length >>> 0; i < length; i++) { + if (i in object && iterator.call(context, object[i], i, object)) { + return true; + } + } + + return false; + } + + if (arrayProto.some) { + var some = wrapNative(Array.prototype.some); + } + + + function every(iterator) { + if (this == null) throw new TypeError(); + iterator = iterator || Prototype.K; + var context = arguments[1]; + + var object = Object(this); + for (var i = 0, length = object.length >>> 0; i < length; i++) { + if (i in object && !iterator.call(context, object[i], i, object)) { + return false; + } + } + + return true; + } + + if (arrayProto.every) { + var every = wrapNative(Array.prototype.every); + } + + var _reduce = arrayProto.reduce; + function inject(memo, iterator) { + iterator = iterator || Prototype.K; + var context = arguments[2]; + return _reduce.call(this, iterator.bind(context), memo); + } + + if (!arrayProto.reduce) { + var inject = Enumerable.inject; + } + + Object.extend(arrayProto, Enumerable); + + if (!arrayProto._reverse) + arrayProto._reverse = arrayProto.reverse; + + Object.extend(arrayProto, { + _each: _each, + + map: map, + collect: map, + select: filter, + filter: filter, + findAll: filter, + some: some, + any: some, + every: every, + all: every, + inject: inject, + + clear: clear, + first: first, + last: last, + compact: compact, + flatten: flatten, + without: without, + reverse: reverse, + uniq: uniq, + intersect: intersect, + clone: clone, + toArray: clone, + size: size, + inspect: inspect + }); + + var CONCAT_ARGUMENTS_BUGGY = (function() { + return [].concat(arguments)[0][0] !== 1; + })(1,2); + + if (CONCAT_ARGUMENTS_BUGGY) arrayProto.concat = concat; + + if (!arrayProto.indexOf) arrayProto.indexOf = indexOf; + if (!arrayProto.lastIndexOf) arrayProto.lastIndexOf = lastIndexOf; +})(); +function $H(object) { + return new Hash(object); +}; + +var Hash = Class.create(Enumerable, (function() { + function initialize(object) { + this._object = Object.isHash(object) ? object.toObject() : Object.clone(object); + } + + + function _each(iterator, context) { + for (var key in this._object) { + var value = this._object[key], pair = [key, value]; + pair.key = key; + pair.value = value; + iterator.call(context, pair); + } + } + + function set(key, value) { + return this._object[key] = value; + } + + function get(key) { + if (this._object[key] !== Object.prototype[key]) + return this._object[key]; + } + + function unset(key) { + var value = this._object[key]; + delete this._object[key]; + return value; + } + + function toObject() { + return Object.clone(this._object); + } + + + + function keys() { + return this.pluck('key'); + } + + function values() { + return this.pluck('value'); + } + + function index(value) { + var match = this.detect(function(pair) { + return pair.value === value; + }); + return match && match.key; + } + + function merge(object) { + return this.clone().update(object); + } + + function update(object) { + return new Hash(object).inject(this, function(result, pair) { + result.set(pair.key, pair.value); + return result; + }); + } + + function toQueryPair(key, value) { + if (Object.isUndefined(value)) return key; + + var value = String.interpret(value); + + value = value.gsub(/(\r)?\n/, '\r\n'); + value = encodeURIComponent(value); + value = value.gsub(/%20/, '+'); + return key + '=' + value; + } + + function toQueryString() { + return this.inject([], function(results, pair) { + var key = encodeURIComponent(pair.key), values = pair.value; + + if (values && typeof values == 'object') { + if (Object.isArray(values)) { + var queryValues = []; + for (var i = 0, len = values.length, value; i < len; i++) { + value = values[i]; + queryValues.push(toQueryPair(key, value)); + } + return results.concat(queryValues); + } + } else results.push(toQueryPair(key, values)); + return results; + }).join('&'); + } + + function inspect() { + return '#'; + } + + function clone() { + return new Hash(this); + } + + return { + initialize: initialize, + _each: _each, + set: set, + get: get, + unset: unset, + toObject: toObject, + toTemplateReplacements: toObject, + keys: keys, + values: values, + index: index, + merge: merge, + update: update, + toQueryString: toQueryString, + inspect: inspect, + toJSON: toObject, + clone: clone + }; +})()); + +Hash.from = $H; +Object.extend(Number.prototype, (function() { + function toColorPart() { + return this.toPaddedString(2, 16); + } + + function succ() { + return this + 1; + } + + function times(iterator, context) { + $R(0, this, true).each(iterator, context); + return this; + } + + function toPaddedString(length, radix) { + var string = this.toString(radix || 10); + return '0'.times(length - string.length) + string; + } + + function abs() { + return Math.abs(this); + } + + function round() { + return Math.round(this); + } + + function ceil() { + return Math.ceil(this); + } + + function floor() { + return Math.floor(this); + } + + return { + toColorPart: toColorPart, + succ: succ, + times: times, + toPaddedString: toPaddedString, + abs: abs, + round: round, + ceil: ceil, + floor: floor + }; +})()); + +function $R(start, end, exclusive) { + return new ObjectRange(start, end, exclusive); +} + +var ObjectRange = Class.create(Enumerable, (function() { + function initialize(start, end, exclusive) { + this.start = start; + this.end = end; + this.exclusive = exclusive; + } + + function _each(iterator, context) { + var value = this.start; + while (this.include(value)) { + iterator.call(context, value); + value = value.succ(); + } + } + + function include(value) { + if (value < this.start) + return false; + if (this.exclusive) + return value < this.end; + return value <= this.end; + } + + return { + initialize: initialize, + _each: _each, + include: include + }; +})()); + + + +var Abstract = { }; + + +var Try = { + these: function() { + var returnValue; + + for (var i = 0, length = arguments.length; i < length; i++) { + var lambda = arguments[i]; + try { + returnValue = lambda(); + break; + } catch (e) { } + } + + return returnValue; + } +}; + +var Ajax = { + getTransport: function() { + return Try.these( + function() {return new XMLHttpRequest()}, + function() {return new ActiveXObject('Msxml2.XMLHTTP')}, + function() {return new ActiveXObject('Microsoft.XMLHTTP')} + ) || false; + }, + + activeRequestCount: 0 +}; + +Ajax.Responders = { + responders: [], + + _each: function(iterator, context) { + this.responders._each(iterator, context); + }, + + register: function(responder) { + if (!this.include(responder)) + this.responders.push(responder); + }, + + unregister: function(responder) { + this.responders = this.responders.without(responder); + }, + + dispatch: function(callback, request, transport, json) { + this.each(function(responder) { + if (Object.isFunction(responder[callback])) { + try { + responder[callback].apply(responder, [request, transport, json]); + } catch (e) { } + } + }); + } +}; + +Object.extend(Ajax.Responders, Enumerable); + +Ajax.Responders.register({ + onCreate: function() { Ajax.activeRequestCount++ }, + onComplete: function() { Ajax.activeRequestCount-- } +}); +Ajax.Base = Class.create({ + initialize: function(options) { + this.options = { + method: 'post', + asynchronous: true, + contentType: 'application/x-www-form-urlencoded', + encoding: 'UTF-8', + parameters: '', + evalJSON: true, + evalJS: true + }; + Object.extend(this.options, options || { }); + + this.options.method = this.options.method.toLowerCase(); + + if (Object.isHash(this.options.parameters)) + this.options.parameters = this.options.parameters.toObject(); + } +}); +Ajax.Request = Class.create(Ajax.Base, { + _complete: false, + + initialize: function($super, url, options) { + $super(options); + this.transport = Ajax.getTransport(); + this.request(url); + }, + + request: function(url) { + this.url = url; + this.method = this.options.method; + var params = Object.isString(this.options.parameters) ? + this.options.parameters : + Object.toQueryString(this.options.parameters); + + if (!['get', 'post'].include(this.method)) { + params += (params ? '&' : '') + "_method=" + this.method; + this.method = 'post'; + } + + if (params && this.method === 'get') { + this.url += (this.url.include('?') ? '&' : '?') + params; + } + + this.parameters = params.toQueryParams(); + + try { + var response = new Ajax.Response(this); + if (this.options.onCreate) this.options.onCreate(response); + Ajax.Responders.dispatch('onCreate', this, response); + + this.transport.open(this.method.toUpperCase(), this.url, + this.options.asynchronous); + + if (this.options.asynchronous) this.respondToReadyState.bind(this).defer(1); + + this.transport.onreadystatechange = this.onStateChange.bind(this); + this.setRequestHeaders(); + + this.body = this.method == 'post' ? (this.options.postBody || params) : null; + this.transport.send(this.body); + + /* Force Firefox to handle ready state 4 for synchronous requests */ + if (!this.options.asynchronous && this.transport.overrideMimeType) + this.onStateChange(); + + } + catch (e) { + this.dispatchException(e); + } + }, + + onStateChange: function() { + var readyState = this.transport.readyState; + if (readyState > 1 && !((readyState == 4) && this._complete)) + this.respondToReadyState(this.transport.readyState); + }, + + setRequestHeaders: function() { + var headers = { + 'X-Requested-With': 'XMLHttpRequest', + 'X-Prototype-Version': Prototype.Version, + 'Accept': 'text/javascript, text/html, application/xml, text/xml, */*' + }; + + if (this.method == 'post') { + headers['Content-type'] = this.options.contentType + + (this.options.encoding ? '; charset=' + this.options.encoding : ''); + + /* Force "Connection: close" for older Mozilla browsers to work + * around a bug where XMLHttpRequest sends an incorrect + * Content-length header. See Mozilla Bugzilla #246651. + */ + if (this.transport.overrideMimeType && + (navigator.userAgent.match(/Gecko\/(\d{4})/) || [0,2005])[1] < 2005) + headers['Connection'] = 'close'; + } + + if (typeof this.options.requestHeaders == 'object') { + var extras = this.options.requestHeaders; + + if (Object.isFunction(extras.push)) + for (var i = 0, length = extras.length; i < length; i += 2) + headers[extras[i]] = extras[i+1]; + else + $H(extras).each(function(pair) { headers[pair.key] = pair.value }); + } + + for (var name in headers) + this.transport.setRequestHeader(name, headers[name]); + }, + + success: function() { + var status = this.getStatus(); + return !status || (status >= 200 && status < 300) || status == 304; + }, + + getStatus: function() { + try { + if (this.transport.status === 1223) return 204; + return this.transport.status || 0; + } catch (e) { return 0 } + }, + + respondToReadyState: function(readyState) { + var state = Ajax.Request.Events[readyState], response = new Ajax.Response(this); + + if (state == 'Complete') { + try { + this._complete = true; + (this.options['on' + response.status] + || this.options['on' + (this.success() ? 'Success' : 'Failure')] + || Prototype.emptyFunction)(response, response.headerJSON); + } catch (e) { + this.dispatchException(e); + } + + var contentType = response.getHeader('Content-type'); + if (this.options.evalJS == 'force' + || (this.options.evalJS && this.isSameOrigin() && contentType + && contentType.match(/^\s*(text|application)\/(x-)?(java|ecma)script(;.*)?\s*$/i))) + this.evalResponse(); + } + + try { + (this.options['on' + state] || Prototype.emptyFunction)(response, response.headerJSON); + Ajax.Responders.dispatch('on' + state, this, response, response.headerJSON); + } catch (e) { + this.dispatchException(e); + } + + if (state == 'Complete') { + this.transport.onreadystatechange = Prototype.emptyFunction; + } + }, + + isSameOrigin: function() { + var m = this.url.match(/^\s*https?:\/\/[^\/]*/); + return !m || (m[0] == '#{protocol}//#{domain}#{port}'.interpolate({ + protocol: location.protocol, + domain: document.domain, + port: location.port ? ':' + location.port : '' + })); + }, + + getHeader: function(name) { + try { + return this.transport.getResponseHeader(name) || null; + } catch (e) { return null; } + }, + + evalResponse: function() { + try { + return eval((this.transport.responseText || '').unfilterJSON()); + } catch (e) { + this.dispatchException(e); + } + }, + + dispatchException: function(exception) { + (this.options.onException || Prototype.emptyFunction)(this, exception); + Ajax.Responders.dispatch('onException', this, exception); + } +}); + +Ajax.Request.Events = + ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete']; + + + + + + + + +Ajax.Response = Class.create({ + initialize: function(request){ + this.request = request; + var transport = this.transport = request.transport, + readyState = this.readyState = transport.readyState; + + if ((readyState > 2 && !Prototype.Browser.IE) || readyState == 4) { + this.status = this.getStatus(); + this.statusText = this.getStatusText(); + this.responseText = String.interpret(transport.responseText); + this.headerJSON = this._getHeaderJSON(); + } + + if (readyState == 4) { + var xml = transport.responseXML; + this.responseXML = Object.isUndefined(xml) ? null : xml; + this.responseJSON = this._getResponseJSON(); + } + }, + + status: 0, + + statusText: '', + + getStatus: Ajax.Request.prototype.getStatus, + + getStatusText: function() { + try { + return this.transport.statusText || ''; + } catch (e) { return '' } + }, + + getHeader: Ajax.Request.prototype.getHeader, + + getAllHeaders: function() { + try { + return this.getAllResponseHeaders(); + } catch (e) { return null } + }, + + getResponseHeader: function(name) { + return this.transport.getResponseHeader(name); + }, + + getAllResponseHeaders: function() { + return this.transport.getAllResponseHeaders(); + }, + + _getHeaderJSON: function() { + var json = this.getHeader('X-JSON'); + if (!json) return null; + + try { + json = decodeURIComponent(escape(json)); + } catch(e) { + } + + try { + return json.evalJSON(this.request.options.sanitizeJSON || + !this.request.isSameOrigin()); + } catch (e) { + this.request.dispatchException(e); + } + }, + + _getResponseJSON: function() { + var options = this.request.options; + if (!options.evalJSON || (options.evalJSON != 'force' && + !(this.getHeader('Content-type') || '').include('application/json')) || + this.responseText.blank()) + return null; + try { + return this.responseText.evalJSON(options.sanitizeJSON || + !this.request.isSameOrigin()); + } catch (e) { + this.request.dispatchException(e); + } + } +}); + +Ajax.Updater = Class.create(Ajax.Request, { + initialize: function($super, container, url, options) { + this.container = { + success: (container.success || container), + failure: (container.failure || (container.success ? null : container)) + }; + + options = Object.clone(options); + var onComplete = options.onComplete; + options.onComplete = (function(response, json) { + this.updateContent(response.responseText); + if (Object.isFunction(onComplete)) onComplete(response, json); + }).bind(this); + + $super(url, options); + }, + + updateContent: function(responseText) { + var receiver = this.container[this.success() ? 'success' : 'failure'], + options = this.options; + + if (!options.evalScripts) responseText = responseText.stripScripts(); + + if (receiver = $(receiver)) { + if (options.insertion) { + if (Object.isString(options.insertion)) { + var insertion = { }; insertion[options.insertion] = responseText; + receiver.insert(insertion); + } + else options.insertion(receiver, responseText); + } + else receiver.update(responseText); + } + } +}); + +Ajax.PeriodicalUpdater = Class.create(Ajax.Base, { + initialize: function($super, container, url, options) { + $super(options); + this.onComplete = this.options.onComplete; + + this.frequency = (this.options.frequency || 2); + this.decay = (this.options.decay || 1); + + this.updater = { }; + this.container = container; + this.url = url; + + this.start(); + }, + + start: function() { + this.options.onComplete = this.updateComplete.bind(this); + this.onTimerEvent(); + }, + + stop: function() { + this.updater.options.onComplete = undefined; + clearTimeout(this.timer); + (this.onComplete || Prototype.emptyFunction).apply(this, arguments); + }, + + updateComplete: function(response) { + if (this.options.decay) { + this.decay = (response.responseText == this.lastText ? + this.decay * this.options.decay : 1); + + this.lastText = response.responseText; + } + this.timer = this.onTimerEvent.bind(this).delay(this.decay * this.frequency); + }, + + onTimerEvent: function() { + this.updater = new Ajax.Updater(this.container, this.url, this.options); + } +}); + +(function(GLOBAL) { + + var UNDEFINED; + var SLICE = Array.prototype.slice; + + var DIV = document.createElement('div'); + + + function $(element) { + if (arguments.length > 1) { + for (var i = 0, elements = [], length = arguments.length; i < length; i++) + elements.push($(arguments[i])); + return elements; + } + + if (Object.isString(element)) + element = document.getElementById(element); + return Element.extend(element); + } + + GLOBAL.$ = $; + + + if (!GLOBAL.Node) GLOBAL.Node = {}; + + if (!GLOBAL.Node.ELEMENT_NODE) { + Object.extend(GLOBAL.Node, { + ELEMENT_NODE: 1, + ATTRIBUTE_NODE: 2, + TEXT_NODE: 3, + CDATA_SECTION_NODE: 4, + ENTITY_REFERENCE_NODE: 5, + ENTITY_NODE: 6, + PROCESSING_INSTRUCTION_NODE: 7, + COMMENT_NODE: 8, + DOCUMENT_NODE: 9, + DOCUMENT_TYPE_NODE: 10, + DOCUMENT_FRAGMENT_NODE: 11, + NOTATION_NODE: 12 + }); + } + + var ELEMENT_CACHE = {}; + + function shouldUseCreationCache(tagName, attributes) { + if (tagName === 'select') return false; + if ('type' in attributes) return false; + return true; + } + + var HAS_EXTENDED_CREATE_ELEMENT_SYNTAX = (function(){ + try { + var el = document.createElement(''); + return el.tagName.toLowerCase() === 'input' && el.name === 'x'; + } + catch(err) { + return false; + } + })(); + + + var oldElement = GLOBAL.Element; + function Element(tagName, attributes) { + attributes = attributes || {}; + tagName = tagName.toLowerCase(); + + if (HAS_EXTENDED_CREATE_ELEMENT_SYNTAX && attributes.name) { + tagName = '<' + tagName + ' name="' + attributes.name + '">'; + delete attributes.name; + return Element.writeAttribute(document.createElement(tagName), attributes); + } + + if (!ELEMENT_CACHE[tagName]) + ELEMENT_CACHE[tagName] = Element.extend(document.createElement(tagName)); + + var node = shouldUseCreationCache(tagName, attributes) ? + ELEMENT_CACHE[tagName].cloneNode(false) : document.createElement(tagName); + + return Element.writeAttribute(node, attributes); + } + + GLOBAL.Element = Element; + + Object.extend(GLOBAL.Element, oldElement || {}); + if (oldElement) GLOBAL.Element.prototype = oldElement.prototype; + + Element.Methods = { ByTag: {}, Simulated: {} }; + + var methods = {}; + + var INSPECT_ATTRIBUTES = { id: 'id', className: 'class' }; + function inspect(element) { + element = $(element); + var result = '<' + element.tagName.toLowerCase(); + + var attribute, value; + for (var property in INSPECT_ATTRIBUTES) { + attribute = INSPECT_ATTRIBUTES[property]; + value = (element[property] || '').toString(); + if (value) result += ' ' + attribute + '=' + value.inspect(true); + } + + return result + '>'; + } + + methods.inspect = inspect; + + + function visible(element) { + return $(element).style.display !== 'none'; + } + + function toggle(element, bool) { + element = $(element); + if (Object.isUndefined(bool)) + bool = !Element.visible(element); + Element[bool ? 'show' : 'hide'](element); + + return element; + } + + function hide(element) { + element = $(element); + element.style.display = 'none'; + return element; + } + + function show(element) { + element = $(element); + element.style.display = ''; + return element; + } + + + Object.extend(methods, { + visible: visible, + toggle: toggle, + hide: hide, + show: show + }); + + + function remove(element) { + element = $(element); + element.parentNode.removeChild(element); + return element; + } + + var SELECT_ELEMENT_INNERHTML_BUGGY = (function(){ + var el = document.createElement("select"), + isBuggy = true; + el.innerHTML = ""; + if (el.options && el.options[0]) { + isBuggy = el.options[0].nodeName.toUpperCase() !== "OPTION"; + } + el = null; + return isBuggy; + })(); + + var TABLE_ELEMENT_INNERHTML_BUGGY = (function(){ + try { + var el = document.createElement("table"); + if (el && el.tBodies) { + el.innerHTML = "test"; + var isBuggy = typeof el.tBodies[0] == "undefined"; + el = null; + return isBuggy; + } + } catch (e) { + return true; + } + })(); + + var LINK_ELEMENT_INNERHTML_BUGGY = (function() { + try { + var el = document.createElement('div'); + el.innerHTML = ""; + var isBuggy = (el.childNodes.length === 0); + el = null; + return isBuggy; + } catch(e) { + return true; + } + })(); + + var ANY_INNERHTML_BUGGY = SELECT_ELEMENT_INNERHTML_BUGGY || + TABLE_ELEMENT_INNERHTML_BUGGY || LINK_ELEMENT_INNERHTML_BUGGY; + + var SCRIPT_ELEMENT_REJECTS_TEXTNODE_APPENDING = (function () { + var s = document.createElement("script"), + isBuggy = false; + try { + s.appendChild(document.createTextNode("")); + isBuggy = !s.firstChild || + s.firstChild && s.firstChild.nodeType !== 3; + } catch (e) { + isBuggy = true; + } + s = null; + return isBuggy; + })(); + + function update(element, content) { + element = $(element); + + var descendants = element.getElementsByTagName('*'), + i = descendants.length; + while (i--) purgeElement(descendants[i]); + + if (content && content.toElement) + content = content.toElement(); + + if (Object.isElement(content)) + return element.update().insert(content); + + + content = Object.toHTML(content); + var tagName = element.tagName.toUpperCase(); + + if (tagName === 'SCRIPT' && SCRIPT_ELEMENT_REJECTS_TEXTNODE_APPENDING) { + element.text = content; + return element; + } + + if (ANY_INNERHTML_BUGGY) { + if (tagName in INSERTION_TRANSLATIONS.tags) { + while (element.firstChild) + element.removeChild(element.firstChild); + + var nodes = getContentFromAnonymousElement(tagName, content.stripScripts()); + for (var i = 0, node; node = nodes[i]; i++) + element.appendChild(node); + + } else if (LINK_ELEMENT_INNERHTML_BUGGY && Object.isString(content) && content.indexOf(' -1) { + while (element.firstChild) + element.removeChild(element.firstChild); + + var nodes = getContentFromAnonymousElement(tagName, + content.stripScripts(), true); + + for (var i = 0, node; node = nodes[i]; i++) + element.appendChild(node); + } else { + element.innerHTML = content.stripScripts(); + } + } else { + element.innerHTML = content.stripScripts(); + } + + content.evalScripts.bind(content).defer(); + return element; + } + + function replace(element, content) { + element = $(element); + + if (content && content.toElement) { + content = content.toElement(); + } else if (!Object.isElement(content)) { + content = Object.toHTML(content); + var range = element.ownerDocument.createRange(); + range.selectNode(element); + content.evalScripts.bind(content).defer(); + content = range.createContextualFragment(content.stripScripts()); + } + + element.parentNode.replaceChild(content, element); + return element; + } + + var INSERTION_TRANSLATIONS = { + before: function(element, node) { + element.parentNode.insertBefore(node, element); + }, + top: function(element, node) { + element.insertBefore(node, element.firstChild); + }, + bottom: function(element, node) { + element.appendChild(node); + }, + after: function(element, node) { + element.parentNode.insertBefore(node, element.nextSibling); + }, + + tags: { + TABLE: ['', '
', 1], + TBODY: ['', '
', 2], + TR: ['', '
', 3], + TD: ['
', '
', 4], + SELECT: ['', 1] + } + }; + + var tags = INSERTION_TRANSLATIONS.tags; + + Object.extend(tags, { + THEAD: tags.TBODY, + TFOOT: tags.TBODY, + TH: tags.TD + }); + + function replace_IE(element, content) { + element = $(element); + if (content && content.toElement) + content = content.toElement(); + if (Object.isElement(content)) { + element.parentNode.replaceChild(content, element); + return element; + } + + content = Object.toHTML(content); + var parent = element.parentNode, tagName = parent.tagName.toUpperCase(); + + if (tagName in INSERTION_TRANSLATIONS.tags) { + var nextSibling = Element.next(element); + var fragments = getContentFromAnonymousElement( + tagName, content.stripScripts()); + + parent.removeChild(element); + + var iterator; + if (nextSibling) + iterator = function(node) { parent.insertBefore(node, nextSibling) }; + else + iterator = function(node) { parent.appendChild(node); } + + fragments.each(iterator); + } else { + element.outerHTML = content.stripScripts(); + } + + content.evalScripts.bind(content).defer(); + return element; + } + + if ('outerHTML' in document.documentElement) + replace = replace_IE; + + function isContent(content) { + if (Object.isUndefined(content) || content === null) return false; + + if (Object.isString(content) || Object.isNumber(content)) return true; + if (Object.isElement(content)) return true; + if (content.toElement || content.toHTML) return true; + + return false; + } + + function insertContentAt(element, content, position) { + position = position.toLowerCase(); + var method = INSERTION_TRANSLATIONS[position]; + + if (content && content.toElement) content = content.toElement(); + if (Object.isElement(content)) { + method(element, content); + return element; + } + + content = Object.toHTML(content); + var tagName = ((position === 'before' || position === 'after') ? + element.parentNode : element).tagName.toUpperCase(); + + var childNodes = getContentFromAnonymousElement(tagName, content.stripScripts()); + + if (position === 'top' || position === 'after') childNodes.reverse(); + + for (var i = 0, node; node = childNodes[i]; i++) + method(element, node); + + content.evalScripts.bind(content).defer(); + } + + function insert(element, insertions) { + element = $(element); + + if (isContent(insertions)) + insertions = { bottom: insertions }; + + for (var position in insertions) + insertContentAt(element, insertions[position], position); + + return element; + } + + function wrap(element, wrapper, attributes) { + element = $(element); + + if (Object.isElement(wrapper)) { + $(wrapper).writeAttribute(attributes || {}); + } else if (Object.isString(wrapper)) { + wrapper = new Element(wrapper, attributes); + } else { + wrapper = new Element('div', wrapper); + } + + if (element.parentNode) + element.parentNode.replaceChild(wrapper, element); + + wrapper.appendChild(element); + + return wrapper; + } + + function cleanWhitespace(element) { + element = $(element); + var node = element.firstChild; + + while (node) { + var nextNode = node.nextSibling; + if (node.nodeType === Node.TEXT_NODE && !/\S/.test(node.nodeValue)) + element.removeChild(node); + node = nextNode; + } + return element; + } + + function empty(element) { + return $(element).innerHTML.blank(); + } + + function getContentFromAnonymousElement(tagName, html, force) { + var t = INSERTION_TRANSLATIONS.tags[tagName], div = DIV; + + var workaround = !!t; + if (!workaround && force) { + workaround = true; + t = ['', '', 0]; + } + + if (workaround) { + div.innerHTML = ' ' + t[0] + html + t[1]; + div.removeChild(div.firstChild); + for (var i = t[2]; i--; ) + div = div.firstChild; + } else { + div.innerHTML = html; + } + + return $A(div.childNodes); + } + + function clone(element, deep) { + if (!(element = $(element))) return; + var clone = element.cloneNode(deep); + if (!HAS_UNIQUE_ID_PROPERTY) { + clone._prototypeUID = UNDEFINED; + if (deep) { + var descendants = Element.select(clone, '*'), + i = descendants.length; + while (i--) + descendants[i]._prototypeUID = UNDEFINED; + } + } + return Element.extend(clone); + } + + function purgeElement(element) { + var uid = getUniqueElementID(element); + if (uid) { + Element.stopObserving(element); + if (!HAS_UNIQUE_ID_PROPERTY) + element._prototypeUID = UNDEFINED; + delete Element.Storage[uid]; + } + } + + function purgeCollection(elements) { + var i = elements.length; + while (i--) + purgeElement(elements[i]); + } + + function purgeCollection_IE(elements) { + var i = elements.length, element, uid; + while (i--) { + element = elements[i]; + uid = getUniqueElementID(element); + delete Element.Storage[uid]; + delete Event.cache[uid]; + } + } + + if (HAS_UNIQUE_ID_PROPERTY) { + purgeCollection = purgeCollection_IE; + } + + + function purge(element) { + if (!(element = $(element))) return; + purgeElement(element); + + var descendants = element.getElementsByTagName('*'), + i = descendants.length; + + while (i--) purgeElement(descendants[i]); + + return null; + } + + Object.extend(methods, { + remove: remove, + update: update, + replace: replace, + insert: insert, + wrap: wrap, + cleanWhitespace: cleanWhitespace, + empty: empty, + clone: clone, + purge: purge + }); + + + + function recursivelyCollect(element, property, maximumLength) { + element = $(element); + maximumLength = maximumLength || -1; + var elements = []; + + while (element = element[property]) { + if (element.nodeType === Node.ELEMENT_NODE) + elements.push(Element.extend(element)); + + if (elements.length === maximumLength) break; + } + + return elements; + } + + + function ancestors(element) { + return recursivelyCollect(element, 'parentNode'); + } + + function descendants(element) { + return Element.select(element, '*'); + } + + function firstDescendant(element) { + element = $(element).firstChild; + while (element && element.nodeType !== Node.ELEMENT_NODE) + element = element.nextSibling; + + return $(element); + } + + function immediateDescendants(element) { + var results = [], child = $(element).firstChild; + + while (child) { + if (child.nodeType === Node.ELEMENT_NODE) + results.push(Element.extend(child)); + + child = child.nextSibling; + } + + return results; + } + + function previousSiblings(element) { + return recursivelyCollect(element, 'previousSibling'); + } + + function nextSiblings(element) { + return recursivelyCollect(element, 'nextSibling'); + } + + function siblings(element) { + element = $(element); + var previous = previousSiblings(element), + next = nextSiblings(element); + return previous.reverse().concat(next); + } + + function match(element, selector) { + element = $(element); + + if (Object.isString(selector)) + return Prototype.Selector.match(element, selector); + + return selector.match(element); + } + + + function _recursivelyFind(element, property, expression, index) { + element = $(element), expression = expression || 0, index = index || 0; + if (Object.isNumber(expression)) { + index = expression, expression = null; + } + + while (element = element[property]) { + if (element.nodeType !== 1) continue; + if (expression && !Prototype.Selector.match(element, expression)) + continue; + if (--index >= 0) continue; + + return Element.extend(element); + } + } + + + function up(element, expression, index) { + element = $(element); + + if (arguments.length === 1) return $(element.parentNode); + return _recursivelyFind(element, 'parentNode', expression, index); + } + + function down(element, expression, index) { + element = $(element), expression = expression || 0, index = index || 0; + + if (Object.isNumber(expression)) + index = expression, expression = '*'; + + var node = Prototype.Selector.select(expression, element)[index]; + return Element.extend(node); + } + + function previous(element, expression, index) { + return _recursivelyFind(element, 'previousSibling', expression, index); + } + + function next(element, expression, index) { + return _recursivelyFind(element, 'nextSibling', expression, index); + } + + function select(element) { + element = $(element); + var expressions = SLICE.call(arguments, 1).join(', '); + return Prototype.Selector.select(expressions, element); + } + + function adjacent(element) { + element = $(element); + var expressions = SLICE.call(arguments, 1).join(', '); + var siblings = Element.siblings(element), results = []; + for (var i = 0, sibling; sibling = siblings[i]; i++) { + if (Prototype.Selector.match(sibling, expressions)) + results.push(sibling); + } + + return results; + } + + function descendantOf_DOM(element, ancestor) { + element = $(element), ancestor = $(ancestor); + while (element = element.parentNode) + if (element === ancestor) return true; + return false; + } + + function descendantOf_contains(element, ancestor) { + element = $(element), ancestor = $(ancestor); + if (!ancestor.contains) return descendantOf_DOM(element, ancestor); + return ancestor.contains(element) && ancestor !== element; + } + + function descendantOf_compareDocumentPosition(element, ancestor) { + element = $(element), ancestor = $(ancestor); + return (element.compareDocumentPosition(ancestor) & 8) === 8; + } + + var descendantOf; + if (DIV.compareDocumentPosition) { + descendantOf = descendantOf_compareDocumentPosition; + } else if (DIV.contains) { + descendantOf = descendantOf_contains; + } else { + descendantOf = descendantOf_DOM; + } + + + Object.extend(methods, { + recursivelyCollect: recursivelyCollect, + ancestors: ancestors, + descendants: descendants, + firstDescendant: firstDescendant, + immediateDescendants: immediateDescendants, + previousSiblings: previousSiblings, + nextSiblings: nextSiblings, + siblings: siblings, + match: match, + up: up, + down: down, + previous: previous, + next: next, + select: select, + adjacent: adjacent, + descendantOf: descendantOf, + + getElementsBySelector: select, + + childElements: immediateDescendants + }); + + + var idCounter = 1; + function identify(element) { + element = $(element); + var id = Element.readAttribute(element, 'id'); + if (id) return id; + + do { id = 'anonymous_element_' + idCounter++ } while ($(id)); + + Element.writeAttribute(element, 'id', id); + return id; + } + + + function readAttribute(element, name) { + return $(element).getAttribute(name); + } + + function readAttribute_IE(element, name) { + element = $(element); + + var table = ATTRIBUTE_TRANSLATIONS.read; + if (table.values[name]) + return table.values[name](element, name); + + if (table.names[name]) name = table.names[name]; + + if (name.include(':')) { + if (!element.attributes || !element.attributes[name]) return null; + return element.attributes[name].value; + } + + return element.getAttribute(name); + } + + function readAttribute_Opera(element, name) { + if (name === 'title') return element.title; + return element.getAttribute(name); + } + + var PROBLEMATIC_ATTRIBUTE_READING = (function() { + DIV.setAttribute('onclick', Prototype.emptyFunction); + var value = DIV.getAttribute('onclick'); + var isFunction = (typeof value === 'function'); + DIV.removeAttribute('onclick'); + return isFunction; + })(); + + if (PROBLEMATIC_ATTRIBUTE_READING) { + readAttribute = readAttribute_IE; + } else if (Prototype.Browser.Opera) { + readAttribute = readAttribute_Opera; + } + + + function writeAttribute(element, name, value) { + element = $(element); + var attributes = {}, table = ATTRIBUTE_TRANSLATIONS.write; + + if (typeof name === 'object') { + attributes = name; + } else { + attributes[name] = Object.isUndefined(value) ? true : value; + } + + for (var attr in attributes) { + name = table.names[attr] || attr; + value = attributes[attr]; + if (table.values[attr]) + name = table.values[attr](element, value); + if (value === false || value === null) + element.removeAttribute(name); + else if (value === true) + element.setAttribute(name, name); + else element.setAttribute(name, value); + } + + return element; + } + + function hasAttribute(element, attribute) { + attribute = ATTRIBUTE_TRANSLATIONS.has[attribute] || attribute; + var node = $(element).getAttributeNode(attribute); + return !!(node && node.specified); + } + + GLOBAL.Element.Methods.Simulated.hasAttribute = hasAttribute; + + function classNames(element) { + return new Element.ClassNames(element); + } + + var regExpCache = {}; + function getRegExpForClassName(className) { + if (regExpCache[className]) return regExpCache[className]; + + var re = new RegExp("(^|\\s+)" + className + "(\\s+|$)"); + regExpCache[className] = re; + return re; + } + + function hasClassName(element, className) { + if (!(element = $(element))) return; + + var elementClassName = element.className; + + if (elementClassName.length === 0) return false; + if (elementClassName === className) return true; + + return getRegExpForClassName(className).test(elementClassName); + } + + function addClassName(element, className) { + if (!(element = $(element))) return; + + if (!hasClassName(element, className)) + element.className += (element.className ? ' ' : '') + className; + + return element; + } + + function removeClassName(element, className) { + if (!(element = $(element))) return; + + element.className = element.className.replace( + getRegExpForClassName(className), ' ').strip(); + + return element; + } + + function toggleClassName(element, className, bool) { + if (!(element = $(element))) return; + + if (Object.isUndefined(bool)) + bool = !hasClassName(element, className); + + var method = Element[bool ? 'addClassName' : 'removeClassName']; + return method(element, className); + } + + var ATTRIBUTE_TRANSLATIONS = {}; + + var classProp = 'className', forProp = 'for'; + + DIV.setAttribute(classProp, 'x'); + if (DIV.className !== 'x') { + DIV.setAttribute('class', 'x'); + if (DIV.className === 'x') + classProp = 'class'; + } + + var LABEL = document.createElement('label'); + LABEL.setAttribute(forProp, 'x'); + if (LABEL.htmlFor !== 'x') { + LABEL.setAttribute('htmlFor', 'x'); + if (LABEL.htmlFor === 'x') + forProp = 'htmlFor'; + } + LABEL = null; + + function _getAttr(element, attribute) { + return element.getAttribute(attribute); + } + + function _getAttr2(element, attribute) { + return element.getAttribute(attribute, 2); + } + + function _getAttrNode(element, attribute) { + var node = element.getAttributeNode(attribute); + return node ? node.value : ''; + } + + function _getFlag(element, attribute) { + return $(element).hasAttribute(attribute) ? attribute : null; + } + + DIV.onclick = Prototype.emptyFunction; + var onclickValue = DIV.getAttribute('onclick'); + + var _getEv; + + if (String(onclickValue).indexOf('{') > -1) { + _getEv = function(element, attribute) { + var value = element.getAttribute(attribute); + if (!value) return null; + value = value.toString(); + value = value.split('{')[1]; + value = value.split('}')[0]; + return value.strip(); + }; + } + else if (onclickValue === '') { + _getEv = function(element, attribute) { + var value = element.getAttribute(attribute); + if (!value) return null; + return value.strip(); + }; + } + + ATTRIBUTE_TRANSLATIONS.read = { + names: { + 'class': classProp, + 'className': classProp, + 'for': forProp, + 'htmlFor': forProp + }, + + values: { + style: function(element) { + return element.style.cssText.toLowerCase(); + }, + title: function(element) { + return element.title; + } + } + }; + + ATTRIBUTE_TRANSLATIONS.write = { + names: { + className: 'class', + htmlFor: 'for', + cellpadding: 'cellPadding', + cellspacing: 'cellSpacing' + }, + + values: { + checked: function(element, value) { + element.checked = !!value; + }, + + style: function(element, value) { + element.style.cssText = value ? value : ''; + } + } + }; + + ATTRIBUTE_TRANSLATIONS.has = { names: {} }; + + Object.extend(ATTRIBUTE_TRANSLATIONS.write.names, + ATTRIBUTE_TRANSLATIONS.read.names); + + var CAMEL_CASED_ATTRIBUTE_NAMES = $w('colSpan rowSpan vAlign dateTime ' + + 'accessKey tabIndex encType maxLength readOnly longDesc frameBorder'); + + for (var i = 0, attr; attr = CAMEL_CASED_ATTRIBUTE_NAMES[i]; i++) { + ATTRIBUTE_TRANSLATIONS.write.names[attr.toLowerCase()] = attr; + ATTRIBUTE_TRANSLATIONS.has.names[attr.toLowerCase()] = attr; + } + + Object.extend(ATTRIBUTE_TRANSLATIONS.read.values, { + href: _getAttr2, + src: _getAttr2, + type: _getAttr, + action: _getAttrNode, + disabled: _getFlag, + checked: _getFlag, + readonly: _getFlag, + multiple: _getFlag, + onload: _getEv, + onunload: _getEv, + onclick: _getEv, + ondblclick: _getEv, + onmousedown: _getEv, + onmouseup: _getEv, + onmouseover: _getEv, + onmousemove: _getEv, + onmouseout: _getEv, + onfocus: _getEv, + onblur: _getEv, + onkeypress: _getEv, + onkeydown: _getEv, + onkeyup: _getEv, + onsubmit: _getEv, + onreset: _getEv, + onselect: _getEv, + onchange: _getEv + }); + + + Object.extend(methods, { + identify: identify, + readAttribute: readAttribute, + writeAttribute: writeAttribute, + classNames: classNames, + hasClassName: hasClassName, + addClassName: addClassName, + removeClassName: removeClassName, + toggleClassName: toggleClassName + }); + + + function normalizeStyleName(style) { + if (style === 'float' || style === 'styleFloat') + return 'cssFloat'; + return style.camelize(); + } + + function normalizeStyleName_IE(style) { + if (style === 'float' || style === 'cssFloat') + return 'styleFloat'; + return style.camelize(); + } + + function setStyle(element, styles) { + element = $(element); + var elementStyle = element.style, match; + + if (Object.isString(styles)) { + elementStyle.cssText += ';' + styles; + if (styles.include('opacity')) { + var opacity = styles.match(/opacity:\s*(\d?\.?\d*)/)[1]; + Element.setOpacity(element, opacity); + } + return element; + } + + for (var property in styles) { + if (property === 'opacity') { + Element.setOpacity(element, styles[property]); + } else { + var value = styles[property]; + if (property === 'float' || property === 'cssFloat') { + property = Object.isUndefined(elementStyle.styleFloat) ? + 'cssFloat' : 'styleFloat'; + } + elementStyle[property] = value; + } + } + + return element; + } + + + function getStyle(element, style) { + element = $(element); + style = normalizeStyleName(style); + + var value = element.style[style]; + if (!value || value === 'auto') { + var css = document.defaultView.getComputedStyle(element, null); + value = css ? css[style] : null; + } + + if (style === 'opacity') return value ? parseFloat(value) : 1.0; + return value === 'auto' ? null : value; + } + + function getStyle_Opera(element, style) { + switch (style) { + case 'height': case 'width': + if (!Element.visible(element)) return null; + + var dim = parseInt(getStyle(element, style), 10); + + if (dim !== element['offset' + style.capitalize()]) + return dim + 'px'; + + return Element.measure(element, style); + + default: return getStyle(element, style); + } + } + + function getStyle_IE(element, style) { + element = $(element); + style = normalizeStyleName_IE(style); + + var value = element.style[style]; + if (!value && element.currentStyle) { + value = element.currentStyle[style]; + } + + if (style === 'opacity' && !STANDARD_CSS_OPACITY_SUPPORTED) + return getOpacity_IE(element); + + if (value === 'auto') { + if ((style === 'width' || style === 'height') && Element.visible(element)) + return Element.measure(element, style) + 'px'; + return null; + } + + return value; + } + + function stripAlphaFromFilter_IE(filter) { + return (filter || '').replace(/alpha\([^\)]*\)/gi, ''); + } + + function hasLayout_IE(element) { + if (!element.currentStyle.hasLayout) + element.style.zoom = 1; + return element; + } + + var STANDARD_CSS_OPACITY_SUPPORTED = (function() { + DIV.style.cssText = "opacity:.55"; + return /^0.55/.test(DIV.style.opacity); + })(); + + function setOpacity(element, value) { + element = $(element); + if (value == 1 || value === '') value = ''; + else if (value < 0.00001) value = 0; + element.style.opacity = value; + return element; + } + + function setOpacity_IE(element, value) { + if (STANDARD_CSS_OPACITY_SUPPORTED) + return setOpacity(element, value); + + element = hasLayout_IE($(element)); + var filter = Element.getStyle(element, 'filter'), + style = element.style; + + if (value == 1 || value === '') { + filter = stripAlphaFromFilter_IE(filter); + if (filter) style.filter = filter; + else style.removeAttribute('filter'); + return element; + } + + if (value < 0.00001) value = 0; + + style.filter = stripAlphaFromFilter_IE(filter) + + 'alpha(opacity=' + (value * 100) + ')'; + + return element; + } + + + function getOpacity(element) { + return Element.getStyle(element, 'opacity'); + } + + function getOpacity_IE(element) { + if (STANDARD_CSS_OPACITY_SUPPORTED) + return getOpacity(element); + + var filter = Element.getStyle(element, 'filter'); + if (filter.length === 0) return 1.0; + var match = (filter || '').match(/alpha\(opacity=(.*)\)/); + if (match[1]) return parseFloat(match[1]) / 100; + return 1.0; + } + + + Object.extend(methods, { + setStyle: setStyle, + getStyle: getStyle, + setOpacity: setOpacity, + getOpacity: getOpacity + }); + + if ('styleFloat' in DIV.style) { + methods.getStyle = getStyle_IE; + methods.setOpacity = setOpacity_IE; + methods.getOpacity = getOpacity_IE; + } + + var UID = 0; + + GLOBAL.Element.Storage = { UID: 1 }; + + function getUniqueElementID(element) { + if (element === window) return 0; + + if (typeof element._prototypeUID === 'undefined') + element._prototypeUID = Element.Storage.UID++; + return element._prototypeUID; + } + + function getUniqueElementID_IE(element) { + if (element === window) return 0; + if (element == document) return 1; + return element.uniqueID; + } + + var HAS_UNIQUE_ID_PROPERTY = ('uniqueID' in DIV); + if (HAS_UNIQUE_ID_PROPERTY) + getUniqueElementID = getUniqueElementID_IE; + + function getStorage(element) { + if (!(element = $(element))) return; + + var uid = getUniqueElementID(element); + + if (!Element.Storage[uid]) + Element.Storage[uid] = $H(); + + return Element.Storage[uid]; + } + + function store(element, key, value) { + if (!(element = $(element))) return; + var storage = getStorage(element); + if (arguments.length === 2) { + storage.update(key); + } else { + storage.set(key, value); + } + return element; + } + + function retrieve(element, key, defaultValue) { + if (!(element = $(element))) return; + var storage = getStorage(element), value = storage.get(key); + + if (Object.isUndefined(value)) { + storage.set(key, defaultValue); + value = defaultValue; + } + + return value; + } + + + Object.extend(methods, { + getStorage: getStorage, + store: store, + retrieve: retrieve + }); + + + var Methods = {}, ByTag = Element.Methods.ByTag, + F = Prototype.BrowserFeatures; + + if (!F.ElementExtensions && ('__proto__' in DIV)) { + GLOBAL.HTMLElement = {}; + GLOBAL.HTMLElement.prototype = DIV['__proto__']; + F.ElementExtensions = true; + } + + function checkElementPrototypeDeficiency(tagName) { + if (typeof window.Element === 'undefined') return false; + var proto = window.Element.prototype; + if (proto) { + var id = '_' + (Math.random() + '').slice(2), + el = document.createElement(tagName); + proto[id] = 'x'; + var isBuggy = (el[id] !== 'x'); + delete proto[id]; + el = null; + return isBuggy; + } + + return false; + } + + var HTMLOBJECTELEMENT_PROTOTYPE_BUGGY = + checkElementPrototypeDeficiency('object'); + + function extendElementWith(element, methods) { + for (var property in methods) { + var value = methods[property]; + if (Object.isFunction(value) && !(property in element)) + element[property] = value.methodize(); + } + } + + var EXTENDED = {}; + function elementIsExtended(element) { + var uid = getUniqueElementID(element); + return (uid in EXTENDED); + } + + function extend(element) { + if (!element || elementIsExtended(element)) return element; + if (element.nodeType !== Node.ELEMENT_NODE || element == window) + return element; + + var methods = Object.clone(Methods), + tagName = element.tagName.toUpperCase(); + + if (ByTag[tagName]) Object.extend(methods, ByTag[tagName]); + + extendElementWith(element, methods); + EXTENDED[getUniqueElementID(element)] = true; + return element; + } + + function extend_IE8(element) { + if (!element || elementIsExtended(element)) return element; + + var t = element.tagName; + if (t && (/^(?:object|applet|embed)$/i.test(t))) { + extendElementWith(element, Element.Methods); + extendElementWith(element, Element.Methods.Simulated); + extendElementWith(element, Element.Methods.ByTag[t.toUpperCase()]); + } + + return element; + } + + if (F.SpecificElementExtensions) { + extend = HTMLOBJECTELEMENT_PROTOTYPE_BUGGY ? extend_IE8 : Prototype.K; + } + + function addMethodsToTagName(tagName, methods) { + tagName = tagName.toUpperCase(); + if (!ByTag[tagName]) ByTag[tagName] = {}; + Object.extend(ByTag[tagName], methods); + } + + function mergeMethods(destination, methods, onlyIfAbsent) { + if (Object.isUndefined(onlyIfAbsent)) onlyIfAbsent = false; + for (var property in methods) { + var value = methods[property]; + if (!Object.isFunction(value)) continue; + if (!onlyIfAbsent || !(property in destination)) + destination[property] = value.methodize(); + } + } + + function findDOMClass(tagName) { + var klass; + var trans = { + "OPTGROUP": "OptGroup", "TEXTAREA": "TextArea", "P": "Paragraph", + "FIELDSET": "FieldSet", "UL": "UList", "OL": "OList", "DL": "DList", + "DIR": "Directory", "H1": "Heading", "H2": "Heading", "H3": "Heading", + "H4": "Heading", "H5": "Heading", "H6": "Heading", "Q": "Quote", + "INS": "Mod", "DEL": "Mod", "A": "Anchor", "IMG": "Image", "CAPTION": + "TableCaption", "COL": "TableCol", "COLGROUP": "TableCol", "THEAD": + "TableSection", "TFOOT": "TableSection", "TBODY": "TableSection", "TR": + "TableRow", "TH": "TableCell", "TD": "TableCell", "FRAMESET": + "FrameSet", "IFRAME": "IFrame" + }; + if (trans[tagName]) klass = 'HTML' + trans[tagName] + 'Element'; + if (window[klass]) return window[klass]; + klass = 'HTML' + tagName + 'Element'; + if (window[klass]) return window[klass]; + klass = 'HTML' + tagName.capitalize() + 'Element'; + if (window[klass]) return window[klass]; + + var element = document.createElement(tagName), + proto = element['__proto__'] || element.constructor.prototype; + + element = null; + return proto; + } + + function addMethods(methods) { + if (arguments.length === 0) addFormMethods(); + + if (arguments.length === 2) { + var tagName = methods; + methods = arguments[1]; + } + + if (!tagName) { + Object.extend(Element.Methods, methods || {}); + } else { + if (Object.isArray(tagName)) { + for (var i = 0, tag; tag = tagName[i]; i++) + addMethodsToTagName(tag, methods); + } else { + addMethodsToTagName(tagName, methods); + } + } + + var ELEMENT_PROTOTYPE = window.HTMLElement ? HTMLElement.prototype : + Element.prototype; + + if (F.ElementExtensions) { + mergeMethods(ELEMENT_PROTOTYPE, Element.Methods); + mergeMethods(ELEMENT_PROTOTYPE, Element.Methods.Simulated, true); + } + + if (F.SpecificElementExtensions) { + for (var tag in Element.Methods.ByTag) { + var klass = findDOMClass(tag); + if (Object.isUndefined(klass)) continue; + mergeMethods(klass.prototype, ByTag[tag]); + } + } + + Object.extend(Element, Element.Methods); + Object.extend(Element, Element.Methods.Simulated); + delete Element.ByTag; + delete Element.Simulated; + + Element.extend.refresh(); + + ELEMENT_CACHE = {}; + } + + Object.extend(GLOBAL.Element, { + extend: extend, + addMethods: addMethods + }); + + if (extend === Prototype.K) { + GLOBAL.Element.extend.refresh = Prototype.emptyFunction; + } else { + GLOBAL.Element.extend.refresh = function() { + if (Prototype.BrowserFeatures.ElementExtensions) return; + Object.extend(Methods, Element.Methods); + Object.extend(Methods, Element.Methods.Simulated); + + EXTENDED = {}; + }; + } + + function addFormMethods() { + Object.extend(Form, Form.Methods); + Object.extend(Form.Element, Form.Element.Methods); + Object.extend(Element.Methods.ByTag, { + "FORM": Object.clone(Form.Methods), + "INPUT": Object.clone(Form.Element.Methods), + "SELECT": Object.clone(Form.Element.Methods), + "TEXTAREA": Object.clone(Form.Element.Methods), + "BUTTON": Object.clone(Form.Element.Methods) + }); + } + + Element.addMethods(methods); + +})(this); +(function() { + + function toDecimal(pctString) { + var match = pctString.match(/^(\d+)%?$/i); + if (!match) return null; + return (Number(match[1]) / 100); + } + + function getRawStyle(element, style) { + element = $(element); + + var value = element.style[style]; + if (!value || value === 'auto') { + var css = document.defaultView.getComputedStyle(element, null); + value = css ? css[style] : null; + } + + if (style === 'opacity') return value ? parseFloat(value) : 1.0; + return value === 'auto' ? null : value; + } + + function getRawStyle_IE(element, style) { + var value = element.style[style]; + if (!value && element.currentStyle) { + value = element.currentStyle[style]; + } + return value; + } + + function getContentWidth(element, context) { + var boxWidth = element.offsetWidth; + + var bl = getPixelValue(element, 'borderLeftWidth', context) || 0; + var br = getPixelValue(element, 'borderRightWidth', context) || 0; + var pl = getPixelValue(element, 'paddingLeft', context) || 0; + var pr = getPixelValue(element, 'paddingRight', context) || 0; + + return boxWidth - bl - br - pl - pr; + } + + if ('currentStyle' in document.documentElement) { + getRawStyle = getRawStyle_IE; + } + + + function getPixelValue(value, property, context) { + var element = null; + if (Object.isElement(value)) { + element = value; + value = getRawStyle(element, property); + } + + if (value === null || Object.isUndefined(value)) { + return null; + } + + if ((/^(?:-)?\d+(\.\d+)?(px)?$/i).test(value)) { + return window.parseFloat(value); + } + + var isPercentage = value.include('%'), isViewport = (context === document.viewport); + + if (/\d/.test(value) && element && element.runtimeStyle && !(isPercentage && isViewport)) { + var style = element.style.left, rStyle = element.runtimeStyle.left; + element.runtimeStyle.left = element.currentStyle.left; + element.style.left = value || 0; + value = element.style.pixelLeft; + element.style.left = style; + element.runtimeStyle.left = rStyle; + + return value; + } + + if (element && isPercentage) { + context = context || element.parentNode; + var decimal = toDecimal(value), whole = null; + + var isHorizontal = property.include('left') || property.include('right') || + property.include('width'); + + var isVertical = property.include('top') || property.include('bottom') || + property.include('height'); + + if (context === document.viewport) { + if (isHorizontal) { + whole = document.viewport.getWidth(); + } else if (isVertical) { + whole = document.viewport.getHeight(); + } + } else { + if (isHorizontal) { + whole = $(context).measure('width'); + } else if (isVertical) { + whole = $(context).measure('height'); + } + } + + return (whole === null) ? 0 : whole * decimal; + } + + return 0; + } + + function toCSSPixels(number) { + if (Object.isString(number) && number.endsWith('px')) + return number; + return number + 'px'; + } + + function isDisplayed(element) { + while (element && element.parentNode) { + var display = element.getStyle('display'); + if (display === 'none') { + return false; + } + element = $(element.parentNode); + } + return true; + } + + var hasLayout = Prototype.K; + if ('currentStyle' in document.documentElement) { + hasLayout = function(element) { + if (!element.currentStyle.hasLayout) { + element.style.zoom = 1; + } + return element; + }; + } + + function cssNameFor(key) { + if (key.include('border')) key = key + '-width'; + return key.camelize(); + } + + Element.Layout = Class.create(Hash, { + initialize: function($super, element, preCompute) { + $super(); + this.element = $(element); + + Element.Layout.PROPERTIES.each( function(property) { + this._set(property, null); + }, this); + + if (preCompute) { + this._preComputing = true; + this._begin(); + Element.Layout.PROPERTIES.each( this._compute, this ); + this._end(); + this._preComputing = false; + } + }, + + _set: function(property, value) { + return Hash.prototype.set.call(this, property, value); + }, + + set: function(property, value) { + throw "Properties of Element.Layout are read-only."; + }, + + get: function($super, property) { + var value = $super(property); + return value === null ? this._compute(property) : value; + }, + + _begin: function() { + if (this._isPrepared()) return; + + var element = this.element; + if (isDisplayed(element)) { + this._setPrepared(true); + return; + } + + + var originalStyles = { + position: element.style.position || '', + width: element.style.width || '', + visibility: element.style.visibility || '', + display: element.style.display || '' + }; + + element.store('prototype_original_styles', originalStyles); + + var position = getRawStyle(element, 'position'), width = element.offsetWidth; + + if (width === 0 || width === null) { + element.style.display = 'block'; + width = element.offsetWidth; + } + + var context = (position === 'fixed') ? document.viewport : + element.parentNode; + + var tempStyles = { + visibility: 'hidden', + display: 'block' + }; + + if (position !== 'fixed') tempStyles.position = 'absolute'; + + element.setStyle(tempStyles); + + var positionedWidth = element.offsetWidth, newWidth; + if (width && (positionedWidth === width)) { + newWidth = getContentWidth(element, context); + } else if (position === 'absolute' || position === 'fixed') { + newWidth = getContentWidth(element, context); + } else { + var parent = element.parentNode, pLayout = $(parent).getLayout(); + + newWidth = pLayout.get('width') - + this.get('margin-left') - + this.get('border-left') - + this.get('padding-left') - + this.get('padding-right') - + this.get('border-right') - + this.get('margin-right'); + } + + element.setStyle({ width: newWidth + 'px' }); + + this._setPrepared(true); + }, + + _end: function() { + var element = this.element; + var originalStyles = element.retrieve('prototype_original_styles'); + element.store('prototype_original_styles', null); + element.setStyle(originalStyles); + this._setPrepared(false); + }, + + _compute: function(property) { + var COMPUTATIONS = Element.Layout.COMPUTATIONS; + if (!(property in COMPUTATIONS)) { + throw "Property not found."; + } + + return this._set(property, COMPUTATIONS[property].call(this, this.element)); + }, + + _isPrepared: function() { + return this.element.retrieve('prototype_element_layout_prepared', false); + }, + + _setPrepared: function(bool) { + return this.element.store('prototype_element_layout_prepared', bool); + }, + + toObject: function() { + var args = $A(arguments); + var keys = (args.length === 0) ? Element.Layout.PROPERTIES : + args.join(' ').split(' '); + var obj = {}; + keys.each( function(key) { + if (!Element.Layout.PROPERTIES.include(key)) return; + var value = this.get(key); + if (value != null) obj[key] = value; + }, this); + return obj; + }, + + toHash: function() { + var obj = this.toObject.apply(this, arguments); + return new Hash(obj); + }, + + toCSS: function() { + var args = $A(arguments); + var keys = (args.length === 0) ? Element.Layout.PROPERTIES : + args.join(' ').split(' '); + var css = {}; + + keys.each( function(key) { + if (!Element.Layout.PROPERTIES.include(key)) return; + if (Element.Layout.COMPOSITE_PROPERTIES.include(key)) return; + + var value = this.get(key); + if (value != null) css[cssNameFor(key)] = value + 'px'; + }, this); + return css; + }, + + inspect: function() { + return "#"; + } + }); + + Object.extend(Element.Layout, { + PROPERTIES: $w('height width top left right bottom border-left border-right border-top border-bottom padding-left padding-right padding-top padding-bottom margin-top margin-bottom margin-left margin-right padding-box-width padding-box-height border-box-width border-box-height margin-box-width margin-box-height'), + + COMPOSITE_PROPERTIES: $w('padding-box-width padding-box-height margin-box-width margin-box-height border-box-width border-box-height'), + + COMPUTATIONS: { + 'height': function(element) { + if (!this._preComputing) this._begin(); + + var bHeight = this.get('border-box-height'); + if (bHeight <= 0) { + if (!this._preComputing) this._end(); + return 0; + } + + var bTop = this.get('border-top'), + bBottom = this.get('border-bottom'); + + var pTop = this.get('padding-top'), + pBottom = this.get('padding-bottom'); + + if (!this._preComputing) this._end(); + + return bHeight - bTop - bBottom - pTop - pBottom; + }, + + 'width': function(element) { + if (!this._preComputing) this._begin(); + + var bWidth = this.get('border-box-width'); + if (bWidth <= 0) { + if (!this._preComputing) this._end(); + return 0; + } + + var bLeft = this.get('border-left'), + bRight = this.get('border-right'); + + var pLeft = this.get('padding-left'), + pRight = this.get('padding-right'); + + if (!this._preComputing) this._end(); + return bWidth - bLeft - bRight - pLeft - pRight; + }, + + 'padding-box-height': function(element) { + var height = this.get('height'), + pTop = this.get('padding-top'), + pBottom = this.get('padding-bottom'); + + return height + pTop + pBottom; + }, + + 'padding-box-width': function(element) { + var width = this.get('width'), + pLeft = this.get('padding-left'), + pRight = this.get('padding-right'); + + return width + pLeft + pRight; + }, + + 'border-box-height': function(element) { + if (!this._preComputing) this._begin(); + var height = element.offsetHeight; + if (!this._preComputing) this._end(); + return height; + }, + + 'border-box-width': function(element) { + if (!this._preComputing) this._begin(); + var width = element.offsetWidth; + if (!this._preComputing) this._end(); + return width; + }, + + 'margin-box-height': function(element) { + var bHeight = this.get('border-box-height'), + mTop = this.get('margin-top'), + mBottom = this.get('margin-bottom'); + + if (bHeight <= 0) return 0; + + return bHeight + mTop + mBottom; + }, + + 'margin-box-width': function(element) { + var bWidth = this.get('border-box-width'), + mLeft = this.get('margin-left'), + mRight = this.get('margin-right'); + + if (bWidth <= 0) return 0; + + return bWidth + mLeft + mRight; + }, + + 'top': function(element) { + var offset = element.positionedOffset(); + return offset.top; + }, + + 'bottom': function(element) { + var offset = element.positionedOffset(), + parent = element.getOffsetParent(), + pHeight = parent.measure('height'); + + var mHeight = this.get('border-box-height'); + + return pHeight - mHeight - offset.top; + }, + + 'left': function(element) { + var offset = element.positionedOffset(); + return offset.left; + }, + + 'right': function(element) { + var offset = element.positionedOffset(), + parent = element.getOffsetParent(), + pWidth = parent.measure('width'); + + var mWidth = this.get('border-box-width'); + + return pWidth - mWidth - offset.left; + }, + + 'padding-top': function(element) { + return getPixelValue(element, 'paddingTop'); + }, + + 'padding-bottom': function(element) { + return getPixelValue(element, 'paddingBottom'); + }, + + 'padding-left': function(element) { + return getPixelValue(element, 'paddingLeft'); + }, + + 'padding-right': function(element) { + return getPixelValue(element, 'paddingRight'); + }, + + 'border-top': function(element) { + return getPixelValue(element, 'borderTopWidth'); + }, + + 'border-bottom': function(element) { + return getPixelValue(element, 'borderBottomWidth'); + }, + + 'border-left': function(element) { + return getPixelValue(element, 'borderLeftWidth'); + }, + + 'border-right': function(element) { + return getPixelValue(element, 'borderRightWidth'); + }, + + 'margin-top': function(element) { + return getPixelValue(element, 'marginTop'); + }, + + 'margin-bottom': function(element) { + return getPixelValue(element, 'marginBottom'); + }, + + 'margin-left': function(element) { + return getPixelValue(element, 'marginLeft'); + }, + + 'margin-right': function(element) { + return getPixelValue(element, 'marginRight'); + } + } + }); + + if ('getBoundingClientRect' in document.documentElement) { + Object.extend(Element.Layout.COMPUTATIONS, { + 'right': function(element) { + var parent = hasLayout(element.getOffsetParent()); + var rect = element.getBoundingClientRect(), + pRect = parent.getBoundingClientRect(); + + return (pRect.right - rect.right).round(); + }, + + 'bottom': function(element) { + var parent = hasLayout(element.getOffsetParent()); + var rect = element.getBoundingClientRect(), + pRect = parent.getBoundingClientRect(); + + return (pRect.bottom - rect.bottom).round(); + } + }); + } + + Element.Offset = Class.create({ + initialize: function(left, top) { + this.left = left.round(); + this.top = top.round(); + + this[0] = this.left; + this[1] = this.top; + }, + + relativeTo: function(offset) { + return new Element.Offset( + this.left - offset.left, + this.top - offset.top + ); + }, + + inspect: function() { + return "#".interpolate(this); + }, + + toString: function() { + return "[#{left}, #{top}]".interpolate(this); + }, + + toArray: function() { + return [this.left, this.top]; + } + }); + + function getLayout(element, preCompute) { + return new Element.Layout(element, preCompute); + } + + function measure(element, property) { + return $(element).getLayout().get(property); + } + + function getHeight(element) { + return Element.getDimensions(element).height; + } + + function getWidth(element) { + return Element.getDimensions(element).width; + } + + function getDimensions(element) { + element = $(element); + var display = Element.getStyle(element, 'display'); + + if (display && display !== 'none') { + return { width: element.offsetWidth, height: element.offsetHeight }; + } + + var style = element.style; + var originalStyles = { + visibility: style.visibility, + position: style.position, + display: style.display + }; + + var newStyles = { + visibility: 'hidden', + display: 'block' + }; + + if (originalStyles.position !== 'fixed') + newStyles.position = 'absolute'; + + Element.setStyle(element, newStyles); + + var dimensions = { + width: element.offsetWidth, + height: element.offsetHeight + }; + + Element.setStyle(element, originalStyles); + + return dimensions; + } + + function getOffsetParent(element) { + element = $(element); + + if (isDocument(element) || isDetached(element) || isBody(element) || isHtml(element)) + return $(document.body); + + var isInline = (Element.getStyle(element, 'display') === 'inline'); + if (!isInline && element.offsetParent) return $(element.offsetParent); + + while ((element = element.parentNode) && element !== document.body) { + if (Element.getStyle(element, 'position') !== 'static') { + return isHtml(element) ? $(document.body) : $(element); + } + } + + return $(document.body); + } + + + function cumulativeOffset(element) { + element = $(element); + var valueT = 0, valueL = 0; + if (element.parentNode) { + do { + valueT += element.offsetTop || 0; + valueL += element.offsetLeft || 0; + element = element.offsetParent; + } while (element); + } + return new Element.Offset(valueL, valueT); + } + + function positionedOffset(element) { + element = $(element); + + var layout = element.getLayout(); + + var valueT = 0, valueL = 0; + do { + valueT += element.offsetTop || 0; + valueL += element.offsetLeft || 0; + element = element.offsetParent; + if (element) { + if (isBody(element)) break; + var p = Element.getStyle(element, 'position'); + if (p !== 'static') break; + } + } while (element); + + valueL -= layout.get('margin-top'); + valueT -= layout.get('margin-left'); + + return new Element.Offset(valueL, valueT); + } + + function cumulativeScrollOffset(element) { + var valueT = 0, valueL = 0; + do { + valueT += element.scrollTop || 0; + valueL += element.scrollLeft || 0; + element = element.parentNode; + } while (element); + return new Element.Offset(valueL, valueT); + } + + function viewportOffset(forElement) { + var valueT = 0, valueL = 0, docBody = document.body; + + var element = $(forElement); + do { + valueT += element.offsetTop || 0; + valueL += element.offsetLeft || 0; + if (element.offsetParent == docBody && + Element.getStyle(element, 'position') == 'absolute') break; + } while (element = element.offsetParent); + + element = forElement; + do { + if (element != docBody) { + valueT -= element.scrollTop || 0; + valueL -= element.scrollLeft || 0; + } + } while (element = element.parentNode); + return new Element.Offset(valueL, valueT); + } + + function absolutize(element) { + element = $(element); + + if (Element.getStyle(element, 'position') === 'absolute') { + return element; + } + + var offsetParent = getOffsetParent(element); + var eOffset = element.viewportOffset(), + pOffset = offsetParent.viewportOffset(); + + var offset = eOffset.relativeTo(pOffset); + var layout = element.getLayout(); + + element.store('prototype_absolutize_original_styles', { + left: element.getStyle('left'), + top: element.getStyle('top'), + width: element.getStyle('width'), + height: element.getStyle('height') + }); + + element.setStyle({ + position: 'absolute', + top: offset.top + 'px', + left: offset.left + 'px', + width: layout.get('width') + 'px', + height: layout.get('height') + 'px' + }); + + return element; + } + + function relativize(element) { + element = $(element); + if (Element.getStyle(element, 'position') === 'relative') { + return element; + } + + var originalStyles = + element.retrieve('prototype_absolutize_original_styles'); + + if (originalStyles) element.setStyle(originalStyles); + return element; + } + + + function scrollTo(element) { + element = $(element); + var pos = Element.cumulativeOffset(element); + window.scrollTo(pos.left, pos.top); + return element; + } + + + function makePositioned(element) { + element = $(element); + var position = Element.getStyle(element, 'position'), styles = {}; + if (position === 'static' || !position) { + styles.position = 'relative'; + if (Prototype.Browser.Opera) { + styles.top = 0; + styles.left = 0; + } + Element.setStyle(element, styles); + Element.store(element, 'prototype_made_positioned', true); + } + return element; + } + + function undoPositioned(element) { + element = $(element); + var storage = Element.getStorage(element), + madePositioned = storage.get('prototype_made_positioned'); + + if (madePositioned) { + storage.unset('prototype_made_positioned'); + Element.setStyle(element, { + position: '', + top: '', + bottom: '', + left: '', + right: '' + }); + } + return element; + } + + function makeClipping(element) { + element = $(element); + + var storage = Element.getStorage(element), + madeClipping = storage.get('prototype_made_clipping'); + + if (Object.isUndefined(madeClipping)) { + var overflow = Element.getStyle(element, 'overflow'); + storage.set('prototype_made_clipping', overflow); + if (overflow !== 'hidden') + element.style.overflow = 'hidden'; + } + + return element; + } + + function undoClipping(element) { + element = $(element); + var storage = Element.getStorage(element), + overflow = storage.get('prototype_made_clipping'); + + if (!Object.isUndefined(overflow)) { + storage.unset('prototype_made_clipping'); + element.style.overflow = overflow || ''; + } + + return element; + } + + function clonePosition(element, source, options) { + options = Object.extend({ + setLeft: true, + setTop: true, + setWidth: true, + setHeight: true, + offsetTop: 0, + offsetLeft: 0 + }, options || {}); + + source = $(source); + element = $(element); + var p, delta, layout, styles = {}; + + if (options.setLeft || options.setTop) { + p = Element.viewportOffset(source); + delta = [0, 0]; + if (Element.getStyle(element, 'position') === 'absolute') { + var parent = Element.getOffsetParent(element); + if (parent !== document.body) delta = Element.viewportOffset(parent); + } + } + + if (options.setWidth || options.setHeight) { + layout = Element.getLayout(source); + } + + if (options.setLeft) + styles.left = (p[0] - delta[0] + options.offsetLeft) + 'px'; + if (options.setTop) + styles.top = (p[1] - delta[1] + options.offsetTop) + 'px'; + + if (options.setWidth) + styles.width = layout.get('border-box-width') + 'px'; + if (options.setHeight) + styles.height = layout.get('border-box-height') + 'px'; + + return Element.setStyle(element, styles); + } + + + if (Prototype.Browser.IE) { + getOffsetParent = getOffsetParent.wrap( + function(proceed, element) { + element = $(element); + + if (isDocument(element) || isDetached(element) || isBody(element) || isHtml(element)) + return $(document.body); + + var position = element.getStyle('position'); + if (position !== 'static') return proceed(element); + + element.setStyle({ position: 'relative' }); + var value = proceed(element); + element.setStyle({ position: position }); + return value; + } + ); + + positionedOffset = positionedOffset.wrap(function(proceed, element) { + element = $(element); + if (!element.parentNode) return new Element.Offset(0, 0); + var position = element.getStyle('position'); + if (position !== 'static') return proceed(element); + + var offsetParent = element.getOffsetParent(); + if (offsetParent && offsetParent.getStyle('position') === 'fixed') + hasLayout(offsetParent); + + element.setStyle({ position: 'relative' }); + var value = proceed(element); + element.setStyle({ position: position }); + return value; + }); + } else if (Prototype.Browser.Webkit) { + cumulativeOffset = function(element) { + element = $(element); + var valueT = 0, valueL = 0; + do { + valueT += element.offsetTop || 0; + valueL += element.offsetLeft || 0; + if (element.offsetParent == document.body) { + if (Element.getStyle(element, 'position') == 'absolute') break; + } + + element = element.offsetParent; + } while (element); + + return new Element.Offset(valueL, valueT); + }; + } + + + Element.addMethods({ + getLayout: getLayout, + measure: measure, + getWidth: getWidth, + getHeight: getHeight, + getDimensions: getDimensions, + getOffsetParent: getOffsetParent, + cumulativeOffset: cumulativeOffset, + positionedOffset: positionedOffset, + cumulativeScrollOffset: cumulativeScrollOffset, + viewportOffset: viewportOffset, + absolutize: absolutize, + relativize: relativize, + scrollTo: scrollTo, + makePositioned: makePositioned, + undoPositioned: undoPositioned, + makeClipping: makeClipping, + undoClipping: undoClipping, + clonePosition: clonePosition + }); + + function isBody(element) { + return element.nodeName.toUpperCase() === 'BODY'; + } + + function isHtml(element) { + return element.nodeName.toUpperCase() === 'HTML'; + } + + function isDocument(element) { + return element.nodeType === Node.DOCUMENT_NODE; + } + + function isDetached(element) { + return element !== document.body && + !Element.descendantOf(element, document.body); + } + + if ('getBoundingClientRect' in document.documentElement) { + Element.addMethods({ + viewportOffset: function(element) { + element = $(element); + if (isDetached(element)) return new Element.Offset(0, 0); + + var rect = element.getBoundingClientRect(), + docEl = document.documentElement; + return new Element.Offset(rect.left - docEl.clientLeft, + rect.top - docEl.clientTop); + } + }); + } + + +})(); + +(function() { + + var IS_OLD_OPERA = Prototype.Browser.Opera && + (window.parseFloat(window.opera.version()) < 9.5); + var ROOT = null; + function getRootElement() { + if (ROOT) return ROOT; + ROOT = IS_OLD_OPERA ? document.body : document.documentElement; + return ROOT; + } + + function getDimensions() { + return { width: this.getWidth(), height: this.getHeight() }; + } + + function getWidth() { + return getRootElement().clientWidth; + } + + function getHeight() { + return getRootElement().clientHeight; + } + + function getScrollOffsets() { + var x = window.pageXOffset || document.documentElement.scrollLeft || + document.body.scrollLeft; + var y = window.pageYOffset || document.documentElement.scrollTop || + document.body.scrollTop; + + return new Element.Offset(x, y); + } + + document.viewport = { + getDimensions: getDimensions, + getWidth: getWidth, + getHeight: getHeight, + getScrollOffsets: getScrollOffsets + }; + +})(); +window.$$ = function() { + var expression = $A(arguments).join(', '); + return Prototype.Selector.select(expression, document); +}; + +Prototype.Selector = (function() { + + function select() { + throw new Error('Method "Prototype.Selector.select" must be defined.'); + } + + function match() { + throw new Error('Method "Prototype.Selector.match" must be defined.'); + } + + function find(elements, expression, index) { + index = index || 0; + var match = Prototype.Selector.match, length = elements.length, matchIndex = 0, i; + + for (i = 0; i < length; i++) { + if (match(elements[i], expression) && index == matchIndex++) { + return Element.extend(elements[i]); + } + } + } + + function extendElements(elements) { + for (var i = 0, length = elements.length; i < length; i++) { + Element.extend(elements[i]); + } + return elements; + } + + + var K = Prototype.K; + + return { + select: select, + match: match, + find: find, + extendElements: (Element.extend === K) ? K : extendElements, + extendElement: Element.extend + }; +})(); +/*! + * Sizzle CSS Selector Engine + * Copyright 2011, The Dojo Foundation + * Released under the MIT, BSD, and GPL Licenses. + * More information: http://sizzlejs.com/ + */ +(function(){ + +var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g, + done = 0, + toString = Object.prototype.toString, + hasDuplicate = false, + baseHasDuplicate = true, + rBackslash = /\\/g, + rNonWord = /\W/; + +[0, 0].sort(function() { + baseHasDuplicate = false; + return 0; +}); + +var Sizzle = function( selector, context, results, seed ) { + results = results || []; + context = context || document; + + var origContext = context; + + if ( context.nodeType !== 1 && context.nodeType !== 9 ) { + return []; + } + + if ( !selector || typeof selector !== "string" ) { + return results; + } + + var m, set, checkSet, extra, ret, cur, pop, i, + prune = true, + contextXML = Sizzle.isXML( context ), + parts = [], + soFar = selector; + + do { + chunker.exec( "" ); + m = chunker.exec( soFar ); + + if ( m ) { + soFar = m[3]; + + parts.push( m[1] ); + + if ( m[2] ) { + extra = m[3]; + break; + } + } + } while ( m ); + + if ( parts.length > 1 && origPOS.exec( selector ) ) { + + if ( parts.length === 2 && Expr.relative[ parts[0] ] ) { + set = posProcess( parts[0] + parts[1], context ); + + } else { + set = Expr.relative[ parts[0] ] ? + [ context ] : + Sizzle( parts.shift(), context ); + + while ( parts.length ) { + selector = parts.shift(); + + if ( Expr.relative[ selector ] ) { + selector += parts.shift(); + } + + set = posProcess( selector, set ); + } + } + + } else { + if ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML && + Expr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) { + + ret = Sizzle.find( parts.shift(), context, contextXML ); + context = ret.expr ? + Sizzle.filter( ret.expr, ret.set )[0] : + ret.set[0]; + } + + if ( context ) { + ret = seed ? + { expr: parts.pop(), set: makeArray(seed) } : + Sizzle.find( parts.pop(), parts.length === 1 && (parts[0] === "~" || parts[0] === "+") && context.parentNode ? context.parentNode : context, contextXML ); + + set = ret.expr ? + Sizzle.filter( ret.expr, ret.set ) : + ret.set; + + if ( parts.length > 0 ) { + checkSet = makeArray( set ); + + } else { + prune = false; + } + + while ( parts.length ) { + cur = parts.pop(); + pop = cur; + + if ( !Expr.relative[ cur ] ) { + cur = ""; + } else { + pop = parts.pop(); + } + + if ( pop == null ) { + pop = context; + } + + Expr.relative[ cur ]( checkSet, pop, contextXML ); + } + + } else { + checkSet = parts = []; + } + } + + if ( !checkSet ) { + checkSet = set; + } + + if ( !checkSet ) { + Sizzle.error( cur || selector ); + } + + if ( toString.call(checkSet) === "[object Array]" ) { + if ( !prune ) { + results.push.apply( results, checkSet ); + + } else if ( context && context.nodeType === 1 ) { + for ( i = 0; checkSet[i] != null; i++ ) { + if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && Sizzle.contains(context, checkSet[i])) ) { + results.push( set[i] ); + } + } + + } else { + for ( i = 0; checkSet[i] != null; i++ ) { + if ( checkSet[i] && checkSet[i].nodeType === 1 ) { + results.push( set[i] ); + } + } + } + + } else { + makeArray( checkSet, results ); + } + + if ( extra ) { + Sizzle( extra, origContext, results, seed ); + Sizzle.uniqueSort( results ); + } + + return results; +}; + +Sizzle.uniqueSort = function( results ) { + if ( sortOrder ) { + hasDuplicate = baseHasDuplicate; + results.sort( sortOrder ); + + if ( hasDuplicate ) { + for ( var i = 1; i < results.length; i++ ) { + if ( results[i] === results[ i - 1 ] ) { + results.splice( i--, 1 ); + } + } + } + } + + return results; +}; + +Sizzle.matches = function( expr, set ) { + return Sizzle( expr, null, null, set ); +}; + +Sizzle.matchesSelector = function( node, expr ) { + return Sizzle( expr, null, null, [node] ).length > 0; +}; + +Sizzle.find = function( expr, context, isXML ) { + var set; + + if ( !expr ) { + return []; + } + + for ( var i = 0, l = Expr.order.length; i < l; i++ ) { + var match, + type = Expr.order[i]; + + if ( (match = Expr.leftMatch[ type ].exec( expr )) ) { + var left = match[1]; + match.splice( 1, 1 ); + + if ( left.substr( left.length - 1 ) !== "\\" ) { + match[1] = (match[1] || "").replace( rBackslash, "" ); + set = Expr.find[ type ]( match, context, isXML ); + + if ( set != null ) { + expr = expr.replace( Expr.match[ type ], "" ); + break; + } + } + } + } + + if ( !set ) { + set = typeof context.getElementsByTagName !== "undefined" ? + context.getElementsByTagName( "*" ) : + []; + } + + return { set: set, expr: expr }; +}; + +Sizzle.filter = function( expr, set, inplace, not ) { + var match, anyFound, + old = expr, + result = [], + curLoop = set, + isXMLFilter = set && set[0] && Sizzle.isXML( set[0] ); + + while ( expr && set.length ) { + for ( var type in Expr.filter ) { + if ( (match = Expr.leftMatch[ type ].exec( expr )) != null && match[2] ) { + var found, item, + filter = Expr.filter[ type ], + left = match[1]; + + anyFound = false; + + match.splice(1,1); + + if ( left.substr( left.length - 1 ) === "\\" ) { + continue; + } + + if ( curLoop === result ) { + result = []; + } + + if ( Expr.preFilter[ type ] ) { + match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter ); + + if ( !match ) { + anyFound = found = true; + + } else if ( match === true ) { + continue; + } + } + + if ( match ) { + for ( var i = 0; (item = curLoop[i]) != null; i++ ) { + if ( item ) { + found = filter( item, match, i, curLoop ); + var pass = not ^ !!found; + + if ( inplace && found != null ) { + if ( pass ) { + anyFound = true; + + } else { + curLoop[i] = false; + } + + } else if ( pass ) { + result.push( item ); + anyFound = true; + } + } + } + } + + if ( found !== undefined ) { + if ( !inplace ) { + curLoop = result; + } + + expr = expr.replace( Expr.match[ type ], "" ); + + if ( !anyFound ) { + return []; + } + + break; + } + } + } + + if ( expr === old ) { + if ( anyFound == null ) { + Sizzle.error( expr ); + + } else { + break; + } + } + + old = expr; + } + + return curLoop; +}; + +Sizzle.error = function( msg ) { + throw "Syntax error, unrecognized expression: " + msg; +}; + +var Expr = Sizzle.selectors = { + order: [ "ID", "NAME", "TAG" ], + + match: { + ID: /#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/, + CLASS: /\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/, + NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/, + ATTR: /\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(?:(['"])(.*?)\3|(#?(?:[\w\u00c0-\uFFFF\-]|\\.)*)|)|)\s*\]/, + TAG: /^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/, + CHILD: /:(only|nth|last|first)-child(?:\(\s*(even|odd|(?:[+\-]?\d+|(?:[+\-]?\d*)?n\s*(?:[+\-]\s*\d+)?))\s*\))?/, + POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/, + PSEUDO: /:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/ + }, + + leftMatch: {}, + + attrMap: { + "class": "className", + "for": "htmlFor" + }, + + attrHandle: { + href: function( elem ) { + return elem.getAttribute( "href" ); + }, + type: function( elem ) { + return elem.getAttribute( "type" ); + } + }, + + relative: { + "+": function(checkSet, part){ + var isPartStr = typeof part === "string", + isTag = isPartStr && !rNonWord.test( part ), + isPartStrNotTag = isPartStr && !isTag; + + if ( isTag ) { + part = part.toLowerCase(); + } + + for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) { + if ( (elem = checkSet[i]) ) { + while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {} + + checkSet[i] = isPartStrNotTag || elem && elem.nodeName.toLowerCase() === part ? + elem || false : + elem === part; + } + } + + if ( isPartStrNotTag ) { + Sizzle.filter( part, checkSet, true ); + } + }, + + ">": function( checkSet, part ) { + var elem, + isPartStr = typeof part === "string", + i = 0, + l = checkSet.length; + + if ( isPartStr && !rNonWord.test( part ) ) { + part = part.toLowerCase(); + + for ( ; i < l; i++ ) { + elem = checkSet[i]; + + if ( elem ) { + var parent = elem.parentNode; + checkSet[i] = parent.nodeName.toLowerCase() === part ? parent : false; + } + } + + } else { + for ( ; i < l; i++ ) { + elem = checkSet[i]; + + if ( elem ) { + checkSet[i] = isPartStr ? + elem.parentNode : + elem.parentNode === part; + } + } + + if ( isPartStr ) { + Sizzle.filter( part, checkSet, true ); + } + } + }, + + "": function(checkSet, part, isXML){ + var nodeCheck, + doneName = done++, + checkFn = dirCheck; + + if ( typeof part === "string" && !rNonWord.test( part ) ) { + part = part.toLowerCase(); + nodeCheck = part; + checkFn = dirNodeCheck; + } + + checkFn( "parentNode", part, doneName, checkSet, nodeCheck, isXML ); + }, + + "~": function( checkSet, part, isXML ) { + var nodeCheck, + doneName = done++, + checkFn = dirCheck; + + if ( typeof part === "string" && !rNonWord.test( part ) ) { + part = part.toLowerCase(); + nodeCheck = part; + checkFn = dirNodeCheck; + } + + checkFn( "previousSibling", part, doneName, checkSet, nodeCheck, isXML ); + } + }, + + find: { + ID: function( match, context, isXML ) { + if ( typeof context.getElementById !== "undefined" && !isXML ) { + var m = context.getElementById(match[1]); + return m && m.parentNode ? [m] : []; + } + }, + + NAME: function( match, context ) { + if ( typeof context.getElementsByName !== "undefined" ) { + var ret = [], + results = context.getElementsByName( match[1] ); + + for ( var i = 0, l = results.length; i < l; i++ ) { + if ( results[i].getAttribute("name") === match[1] ) { + ret.push( results[i] ); + } + } + + return ret.length === 0 ? null : ret; + } + }, + + TAG: function( match, context ) { + if ( typeof context.getElementsByTagName !== "undefined" ) { + return context.getElementsByTagName( match[1] ); + } + } + }, + preFilter: { + CLASS: function( match, curLoop, inplace, result, not, isXML ) { + match = " " + match[1].replace( rBackslash, "" ) + " "; + + if ( isXML ) { + return match; + } + + for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) { + if ( elem ) { + if ( not ^ (elem.className && (" " + elem.className + " ").replace(/[\t\n\r]/g, " ").indexOf(match) >= 0) ) { + if ( !inplace ) { + result.push( elem ); + } + + } else if ( inplace ) { + curLoop[i] = false; + } + } + } + + return false; + }, + + ID: function( match ) { + return match[1].replace( rBackslash, "" ); + }, + + TAG: function( match, curLoop ) { + return match[1].replace( rBackslash, "" ).toLowerCase(); + }, + + CHILD: function( match ) { + if ( match[1] === "nth" ) { + if ( !match[2] ) { + Sizzle.error( match[0] ); + } + + match[2] = match[2].replace(/^\+|\s*/g, ''); + + var test = /(-?)(\d*)(?:n([+\-]?\d*))?/.exec( + match[2] === "even" && "2n" || match[2] === "odd" && "2n+1" || + !/\D/.test( match[2] ) && "0n+" + match[2] || match[2]); + + match[2] = (test[1] + (test[2] || 1)) - 0; + match[3] = test[3] - 0; + } + else if ( match[2] ) { + Sizzle.error( match[0] ); + } + + match[0] = done++; + + return match; + }, + + ATTR: function( match, curLoop, inplace, result, not, isXML ) { + var name = match[1] = match[1].replace( rBackslash, "" ); + + if ( !isXML && Expr.attrMap[name] ) { + match[1] = Expr.attrMap[name]; + } + + match[4] = ( match[4] || match[5] || "" ).replace( rBackslash, "" ); + + if ( match[2] === "~=" ) { + match[4] = " " + match[4] + " "; + } + + return match; + }, + + PSEUDO: function( match, curLoop, inplace, result, not ) { + if ( match[1] === "not" ) { + if ( ( chunker.exec(match[3]) || "" ).length > 1 || /^\w/.test(match[3]) ) { + match[3] = Sizzle(match[3], null, null, curLoop); + + } else { + var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not); + + if ( !inplace ) { + result.push.apply( result, ret ); + } + + return false; + } + + } else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) { + return true; + } + + return match; + }, + + POS: function( match ) { + match.unshift( true ); + + return match; + } + }, + + filters: { + enabled: function( elem ) { + return elem.disabled === false && elem.type !== "hidden"; + }, + + disabled: function( elem ) { + return elem.disabled === true; + }, + + checked: function( elem ) { + return elem.checked === true; + }, + + selected: function( elem ) { + if ( elem.parentNode ) { + elem.parentNode.selectedIndex; + } + + return elem.selected === true; + }, + + parent: function( elem ) { + return !!elem.firstChild; + }, + + empty: function( elem ) { + return !elem.firstChild; + }, + + has: function( elem, i, match ) { + return !!Sizzle( match[3], elem ).length; + }, + + header: function( elem ) { + return (/h\d/i).test( elem.nodeName ); + }, + + text: function( elem ) { + var attr = elem.getAttribute( "type" ), type = elem.type; + return elem.nodeName.toLowerCase() === "input" && "text" === type && ( attr === type || attr === null ); + }, + + radio: function( elem ) { + return elem.nodeName.toLowerCase() === "input" && "radio" === elem.type; + }, + + checkbox: function( elem ) { + return elem.nodeName.toLowerCase() === "input" && "checkbox" === elem.type; + }, + + file: function( elem ) { + return elem.nodeName.toLowerCase() === "input" && "file" === elem.type; + }, + + password: function( elem ) { + return elem.nodeName.toLowerCase() === "input" && "password" === elem.type; + }, + + submit: function( elem ) { + var name = elem.nodeName.toLowerCase(); + return (name === "input" || name === "button") && "submit" === elem.type; + }, + + image: function( elem ) { + return elem.nodeName.toLowerCase() === "input" && "image" === elem.type; + }, + + reset: function( elem ) { + var name = elem.nodeName.toLowerCase(); + return (name === "input" || name === "button") && "reset" === elem.type; + }, + + button: function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && "button" === elem.type || name === "button"; + }, + + input: function( elem ) { + return (/input|select|textarea|button/i).test( elem.nodeName ); + }, + + focus: function( elem ) { + return elem === elem.ownerDocument.activeElement; + } + }, + setFilters: { + first: function( elem, i ) { + return i === 0; + }, + + last: function( elem, i, match, array ) { + return i === array.length - 1; + }, + + even: function( elem, i ) { + return i % 2 === 0; + }, + + odd: function( elem, i ) { + return i % 2 === 1; + }, + + lt: function( elem, i, match ) { + return i < match[3] - 0; + }, + + gt: function( elem, i, match ) { + return i > match[3] - 0; + }, + + nth: function( elem, i, match ) { + return match[3] - 0 === i; + }, + + eq: function( elem, i, match ) { + return match[3] - 0 === i; + } + }, + filter: { + PSEUDO: function( elem, match, i, array ) { + var name = match[1], + filter = Expr.filters[ name ]; + + if ( filter ) { + return filter( elem, i, match, array ); + + } else if ( name === "contains" ) { + return (elem.textContent || elem.innerText || Sizzle.getText([ elem ]) || "").indexOf(match[3]) >= 0; + + } else if ( name === "not" ) { + var not = match[3]; + + for ( var j = 0, l = not.length; j < l; j++ ) { + if ( not[j] === elem ) { + return false; + } + } + + return true; + + } else { + Sizzle.error( name ); + } + }, + + CHILD: function( elem, match ) { + var type = match[1], + node = elem; + + switch ( type ) { + case "only": + case "first": + while ( (node = node.previousSibling) ) { + if ( node.nodeType === 1 ) { + return false; + } + } + + if ( type === "first" ) { + return true; + } + + node = elem; + + case "last": + while ( (node = node.nextSibling) ) { + if ( node.nodeType === 1 ) { + return false; + } + } + + return true; + + case "nth": + var first = match[2], + last = match[3]; + + if ( first === 1 && last === 0 ) { + return true; + } + + var doneName = match[0], + parent = elem.parentNode; + + if ( parent && (parent.sizcache !== doneName || !elem.nodeIndex) ) { + var count = 0; + + for ( node = parent.firstChild; node; node = node.nextSibling ) { + if ( node.nodeType === 1 ) { + node.nodeIndex = ++count; + } + } + + parent.sizcache = doneName; + } + + var diff = elem.nodeIndex - last; + + if ( first === 0 ) { + return diff === 0; + + } else { + return ( diff % first === 0 && diff / first >= 0 ); + } + } + }, + + ID: function( elem, match ) { + return elem.nodeType === 1 && elem.getAttribute("id") === match; + }, + + TAG: function( elem, match ) { + return (match === "*" && elem.nodeType === 1) || elem.nodeName.toLowerCase() === match; + }, + + CLASS: function( elem, match ) { + return (" " + (elem.className || elem.getAttribute("class")) + " ") + .indexOf( match ) > -1; + }, + + ATTR: function( elem, match ) { + var name = match[1], + result = Expr.attrHandle[ name ] ? + Expr.attrHandle[ name ]( elem ) : + elem[ name ] != null ? + elem[ name ] : + elem.getAttribute( name ), + value = result + "", + type = match[2], + check = match[4]; + + return result == null ? + type === "!=" : + type === "=" ? + value === check : + type === "*=" ? + value.indexOf(check) >= 0 : + type === "~=" ? + (" " + value + " ").indexOf(check) >= 0 : + !check ? + value && result !== false : + type === "!=" ? + value !== check : + type === "^=" ? + value.indexOf(check) === 0 : + type === "$=" ? + value.substr(value.length - check.length) === check : + type === "|=" ? + value === check || value.substr(0, check.length + 1) === check + "-" : + false; + }, + + POS: function( elem, match, i, array ) { + var name = match[2], + filter = Expr.setFilters[ name ]; + + if ( filter ) { + return filter( elem, i, match, array ); + } + } + } +}; + +var origPOS = Expr.match.POS, + fescape = function(all, num){ + return "\\" + (num - 0 + 1); + }; + +for ( var type in Expr.match ) { + Expr.match[ type ] = new RegExp( Expr.match[ type ].source + (/(?![^\[]*\])(?![^\(]*\))/.source) ); + Expr.leftMatch[ type ] = new RegExp( /(^(?:.|\r|\n)*?)/.source + Expr.match[ type ].source.replace(/\\(\d+)/g, fescape) ); +} + +var makeArray = function( array, results ) { + array = Array.prototype.slice.call( array, 0 ); + + if ( results ) { + results.push.apply( results, array ); + return results; + } + + return array; +}; + +try { + Array.prototype.slice.call( document.documentElement.childNodes, 0 )[0].nodeType; + +} catch( e ) { + makeArray = function( array, results ) { + var i = 0, + ret = results || []; + + if ( toString.call(array) === "[object Array]" ) { + Array.prototype.push.apply( ret, array ); + + } else { + if ( typeof array.length === "number" ) { + for ( var l = array.length; i < l; i++ ) { + ret.push( array[i] ); + } + + } else { + for ( ; array[i]; i++ ) { + ret.push( array[i] ); + } + } + } + + return ret; + }; +} + +var sortOrder, siblingCheck; + +if ( document.documentElement.compareDocumentPosition ) { + sortOrder = function( a, b ) { + if ( a === b ) { + hasDuplicate = true; + return 0; + } + + if ( !a.compareDocumentPosition || !b.compareDocumentPosition ) { + return a.compareDocumentPosition ? -1 : 1; + } + + return a.compareDocumentPosition(b) & 4 ? -1 : 1; + }; + +} else { + sortOrder = function( a, b ) { + if ( a === b ) { + hasDuplicate = true; + return 0; + + } else if ( a.sourceIndex && b.sourceIndex ) { + return a.sourceIndex - b.sourceIndex; + } + + var al, bl, + ap = [], + bp = [], + aup = a.parentNode, + bup = b.parentNode, + cur = aup; + + if ( aup === bup ) { + return siblingCheck( a, b ); + + } else if ( !aup ) { + return -1; + + } else if ( !bup ) { + return 1; + } + + while ( cur ) { + ap.unshift( cur ); + cur = cur.parentNode; + } + + cur = bup; + + while ( cur ) { + bp.unshift( cur ); + cur = cur.parentNode; + } + + al = ap.length; + bl = bp.length; + + for ( var i = 0; i < al && i < bl; i++ ) { + if ( ap[i] !== bp[i] ) { + return siblingCheck( ap[i], bp[i] ); + } + } + + return i === al ? + siblingCheck( a, bp[i], -1 ) : + siblingCheck( ap[i], b, 1 ); + }; + + siblingCheck = function( a, b, ret ) { + if ( a === b ) { + return ret; + } + + var cur = a.nextSibling; + + while ( cur ) { + if ( cur === b ) { + return -1; + } + + cur = cur.nextSibling; + } + + return 1; + }; +} + +Sizzle.getText = function( elems ) { + var ret = "", elem; + + for ( var i = 0; elems[i]; i++ ) { + elem = elems[i]; + + if ( elem.nodeType === 3 || elem.nodeType === 4 ) { + ret += elem.nodeValue; + + } else if ( elem.nodeType !== 8 ) { + ret += Sizzle.getText( elem.childNodes ); + } + } + + return ret; +}; + +(function(){ + var form = document.createElement("div"), + id = "script" + (new Date()).getTime(), + root = document.documentElement; + + form.innerHTML = ""; + + root.insertBefore( form, root.firstChild ); + + if ( document.getElementById( id ) ) { + Expr.find.ID = function( match, context, isXML ) { + if ( typeof context.getElementById !== "undefined" && !isXML ) { + var m = context.getElementById(match[1]); + + return m ? + m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ? + [m] : + undefined : + []; + } + }; + + Expr.filter.ID = function( elem, match ) { + var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id"); + + return elem.nodeType === 1 && node && node.nodeValue === match; + }; + } + + root.removeChild( form ); + + root = form = null; +})(); + +(function(){ + + var div = document.createElement("div"); + div.appendChild( document.createComment("") ); + + if ( div.getElementsByTagName("*").length > 0 ) { + Expr.find.TAG = function( match, context ) { + var results = context.getElementsByTagName( match[1] ); + + if ( match[1] === "*" ) { + var tmp = []; + + for ( var i = 0; results[i]; i++ ) { + if ( results[i].nodeType === 1 ) { + tmp.push( results[i] ); + } + } + + results = tmp; + } + + return results; + }; + } + + div.innerHTML = ""; + + if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" && + div.firstChild.getAttribute("href") !== "#" ) { + + Expr.attrHandle.href = function( elem ) { + return elem.getAttribute( "href", 2 ); + }; + } + + div = null; +})(); + +if ( document.querySelectorAll ) { + (function(){ + var oldSizzle = Sizzle, + div = document.createElement("div"), + id = "__sizzle__"; + + div.innerHTML = "

"; + + if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) { + return; + } + + Sizzle = function( query, context, extra, seed ) { + context = context || document; + + if ( !seed && !Sizzle.isXML(context) ) { + var match = /^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec( query ); + + if ( match && (context.nodeType === 1 || context.nodeType === 9) ) { + if ( match[1] ) { + return makeArray( context.getElementsByTagName( query ), extra ); + + } else if ( match[2] && Expr.find.CLASS && context.getElementsByClassName ) { + return makeArray( context.getElementsByClassName( match[2] ), extra ); + } + } + + if ( context.nodeType === 9 ) { + if ( query === "body" && context.body ) { + return makeArray( [ context.body ], extra ); + + } else if ( match && match[3] ) { + var elem = context.getElementById( match[3] ); + + if ( elem && elem.parentNode ) { + if ( elem.id === match[3] ) { + return makeArray( [ elem ], extra ); + } + + } else { + return makeArray( [], extra ); + } + } + + try { + return makeArray( context.querySelectorAll(query), extra ); + } catch(qsaError) {} + + } else if ( context.nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) { + var oldContext = context, + old = context.getAttribute( "id" ), + nid = old || id, + hasParent = context.parentNode, + relativeHierarchySelector = /^\s*[+~]/.test( query ); + + if ( !old ) { + context.setAttribute( "id", nid ); + } else { + nid = nid.replace( /'/g, "\\$&" ); + } + if ( relativeHierarchySelector && hasParent ) { + context = context.parentNode; + } + + try { + if ( !relativeHierarchySelector || hasParent ) { + return makeArray( context.querySelectorAll( "[id='" + nid + "'] " + query ), extra ); + } + + } catch(pseudoError) { + } finally { + if ( !old ) { + oldContext.removeAttribute( "id" ); + } + } + } + } + + return oldSizzle(query, context, extra, seed); + }; + + for ( var prop in oldSizzle ) { + Sizzle[ prop ] = oldSizzle[ prop ]; + } + + div = null; + })(); +} + +(function(){ + var html = document.documentElement, + matches = html.matchesSelector || html.mozMatchesSelector || html.webkitMatchesSelector || html.msMatchesSelector; + + if ( matches ) { + var disconnectedMatch = !matches.call( document.createElement( "div" ), "div" ), + pseudoWorks = false; + + try { + matches.call( document.documentElement, "[test!='']:sizzle" ); + + } catch( pseudoError ) { + pseudoWorks = true; + } + + Sizzle.matchesSelector = function( node, expr ) { + expr = expr.replace(/\=\s*([^'"\]]*)\s*\]/g, "='$1']"); + + if ( !Sizzle.isXML( node ) ) { + try { + if ( pseudoWorks || !Expr.match.PSEUDO.test( expr ) && !/!=/.test( expr ) ) { + var ret = matches.call( node, expr ); + + if ( ret || !disconnectedMatch || + node.document && node.document.nodeType !== 11 ) { + return ret; + } + } + } catch(e) {} + } + + return Sizzle(expr, null, null, [node]).length > 0; + }; + } +})(); + +(function(){ + var div = document.createElement("div"); + + div.innerHTML = "
"; + + if ( !div.getElementsByClassName || div.getElementsByClassName("e").length === 0 ) { + return; + } + + div.lastChild.className = "e"; + + if ( div.getElementsByClassName("e").length === 1 ) { + return; + } + + Expr.order.splice(1, 0, "CLASS"); + Expr.find.CLASS = function( match, context, isXML ) { + if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) { + return context.getElementsByClassName(match[1]); + } + }; + + div = null; +})(); + +function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) { + for ( var i = 0, l = checkSet.length; i < l; i++ ) { + var elem = checkSet[i]; + + if ( elem ) { + var match = false; + + elem = elem[dir]; + + while ( elem ) { + if ( elem.sizcache === doneName ) { + match = checkSet[elem.sizset]; + break; + } + + if ( elem.nodeType === 1 && !isXML ){ + elem.sizcache = doneName; + elem.sizset = i; + } + + if ( elem.nodeName.toLowerCase() === cur ) { + match = elem; + break; + } + + elem = elem[dir]; + } + + checkSet[i] = match; + } + } +} + +function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) { + for ( var i = 0, l = checkSet.length; i < l; i++ ) { + var elem = checkSet[i]; + + if ( elem ) { + var match = false; + + elem = elem[dir]; + + while ( elem ) { + if ( elem.sizcache === doneName ) { + match = checkSet[elem.sizset]; + break; + } + + if ( elem.nodeType === 1 ) { + if ( !isXML ) { + elem.sizcache = doneName; + elem.sizset = i; + } + + if ( typeof cur !== "string" ) { + if ( elem === cur ) { + match = true; + break; + } + + } else if ( Sizzle.filter( cur, [elem] ).length > 0 ) { + match = elem; + break; + } + } + + elem = elem[dir]; + } + + checkSet[i] = match; + } + } +} + +if ( document.documentElement.contains ) { + Sizzle.contains = function( a, b ) { + return a !== b && (a.contains ? a.contains(b) : true); + }; + +} else if ( document.documentElement.compareDocumentPosition ) { + Sizzle.contains = function( a, b ) { + return !!(a.compareDocumentPosition(b) & 16); + }; + +} else { + Sizzle.contains = function() { + return false; + }; +} + +Sizzle.isXML = function( elem ) { + var documentElement = (elem ? elem.ownerDocument || elem : 0).documentElement; + + return documentElement ? documentElement.nodeName !== "HTML" : false; +}; + +var posProcess = function( selector, context ) { + var match, + tmpSet = [], + later = "", + root = context.nodeType ? [context] : context; + + while ( (match = Expr.match.PSEUDO.exec( selector )) ) { + later += match[0]; + selector = selector.replace( Expr.match.PSEUDO, "" ); + } + + selector = Expr.relative[selector] ? selector + "*" : selector; + + for ( var i = 0, l = root.length; i < l; i++ ) { + Sizzle( selector, root[i], tmpSet ); + } + + return Sizzle.filter( later, tmpSet ); +}; + + +window.Sizzle = Sizzle; + +})(); + +Prototype._original_property = window.Sizzle; + +;(function(engine) { + var extendElements = Prototype.Selector.extendElements; + + function select(selector, scope) { + return extendElements(engine(selector, scope || document)); + } + + function match(element, selector) { + return engine.matches(selector, [element]).length == 1; + } + + Prototype.Selector.engine = engine; + Prototype.Selector.select = select; + Prototype.Selector.match = match; +})(Sizzle); + +window.Sizzle = Prototype._original_property; +delete Prototype._original_property; + +var Form = { + reset: function(form) { + form = $(form); + form.reset(); + return form; + }, + + serializeElements: function(elements, options) { + if (typeof options != 'object') options = { hash: !!options }; + else if (Object.isUndefined(options.hash)) options.hash = true; + var key, value, submitted = false, submit = options.submit, accumulator, initial; + + if (options.hash) { + initial = {}; + accumulator = function(result, key, value) { + if (key in result) { + if (!Object.isArray(result[key])) result[key] = [result[key]]; + result[key].push(value); + } else result[key] = value; + return result; + }; + } else { + initial = ''; + accumulator = function(result, key, value) { + value = value.gsub(/(\r)?\n/, '\r\n'); + value = encodeURIComponent(value); + value = value.gsub(/%20/, '+'); + return result + (result ? '&' : '') + encodeURIComponent(key) + '=' + value; + } + } + + return elements.inject(initial, function(result, element) { + if (!element.disabled && element.name) { + key = element.name; value = $(element).getValue(); + if (value != null && element.type != 'file' && (element.type != 'submit' || (!submitted && + submit !== false && (!submit || key == submit) && (submitted = true)))) { + result = accumulator(result, key, value); + } + } + return result; + }); + } +}; + +Form.Methods = { + serialize: function(form, options) { + return Form.serializeElements(Form.getElements(form), options); + }, + + + getElements: function(form) { + var elements = $(form).getElementsByTagName('*'); + var element, results = [], serializers = Form.Element.Serializers; + + for (var i = 0; element = elements[i]; i++) { + if (serializers[element.tagName.toLowerCase()]) + results.push(Element.extend(element)); + } + return results; + }, + + getInputs: function(form, typeName, name) { + form = $(form); + var inputs = form.getElementsByTagName('input'); + + if (!typeName && !name) return $A(inputs).map(Element.extend); + + for (var i = 0, matchingInputs = [], length = inputs.length; i < length; i++) { + var input = inputs[i]; + if ((typeName && input.type != typeName) || (name && input.name != name)) + continue; + matchingInputs.push(Element.extend(input)); + } + + return matchingInputs; + }, + + disable: function(form) { + form = $(form); + Form.getElements(form).invoke('disable'); + return form; + }, + + enable: function(form) { + form = $(form); + Form.getElements(form).invoke('enable'); + return form; + }, + + findFirstElement: function(form) { + var elements = $(form).getElements().findAll(function(element) { + return 'hidden' != element.type && !element.disabled; + }); + var firstByIndex = elements.findAll(function(element) { + return element.hasAttribute('tabIndex') && element.tabIndex >= 0; + }).sortBy(function(element) { return element.tabIndex }).first(); + + return firstByIndex ? firstByIndex : elements.find(function(element) { + return /^(?:input|select|textarea)$/i.test(element.tagName); + }); + }, + + focusFirstElement: function(form) { + form = $(form); + var element = form.findFirstElement(); + if (element) element.activate(); + return form; + }, + + request: function(form, options) { + form = $(form), options = Object.clone(options || { }); + + var params = options.parameters, action = form.readAttribute('action') || ''; + if (action.blank()) action = window.location.href; + options.parameters = form.serialize(true); + + if (params) { + if (Object.isString(params)) params = params.toQueryParams(); + Object.extend(options.parameters, params); + } + + if (form.hasAttribute('method') && !options.method) + options.method = form.method; + + return new Ajax.Request(action, options); + } +}; + +/*--------------------------------------------------------------------------*/ + + +Form.Element = { + focus: function(element) { + $(element).focus(); + return element; + }, + + select: function(element) { + $(element).select(); + return element; + } +}; + +Form.Element.Methods = { + + serialize: function(element) { + element = $(element); + if (!element.disabled && element.name) { + var value = element.getValue(); + if (value != undefined) { + var pair = { }; + pair[element.name] = value; + return Object.toQueryString(pair); + } + } + return ''; + }, + + getValue: function(element) { + element = $(element); + var method = element.tagName.toLowerCase(); + return Form.Element.Serializers[method](element); + }, + + setValue: function(element, value) { + element = $(element); + var method = element.tagName.toLowerCase(); + Form.Element.Serializers[method](element, value); + return element; + }, + + clear: function(element) { + $(element).value = ''; + return element; + }, + + present: function(element) { + return $(element).value != ''; + }, + + activate: function(element) { + element = $(element); + try { + element.focus(); + if (element.select && (element.tagName.toLowerCase() != 'input' || + !(/^(?:button|reset|submit)$/i.test(element.type)))) + element.select(); + } catch (e) { } + return element; + }, + + disable: function(element) { + element = $(element); + element.disabled = true; + return element; + }, + + enable: function(element) { + element = $(element); + element.disabled = false; + return element; + } +}; + +/*--------------------------------------------------------------------------*/ + +var Field = Form.Element; + +var $F = Form.Element.Methods.getValue; + +/*--------------------------------------------------------------------------*/ + +Form.Element.Serializers = (function() { + function input(element, value) { + switch (element.type.toLowerCase()) { + case 'checkbox': + case 'radio': + return inputSelector(element, value); + default: + return valueSelector(element, value); + } + } + + function inputSelector(element, value) { + if (Object.isUndefined(value)) + return element.checked ? element.value : null; + else element.checked = !!value; + } + + function valueSelector(element, value) { + if (Object.isUndefined(value)) return element.value; + else element.value = value; + } + + function select(element, value) { + if (Object.isUndefined(value)) + return (element.type === 'select-one' ? selectOne : selectMany)(element); + + var opt, currentValue, single = !Object.isArray(value); + for (var i = 0, length = element.length; i < length; i++) { + opt = element.options[i]; + currentValue = this.optionValue(opt); + if (single) { + if (currentValue == value) { + opt.selected = true; + return; + } + } + else opt.selected = value.include(currentValue); + } + } + + function selectOne(element) { + var index = element.selectedIndex; + return index >= 0 ? optionValue(element.options[index]) : null; + } + + function selectMany(element) { + var values, length = element.length; + if (!length) return null; + + for (var i = 0, values = []; i < length; i++) { + var opt = element.options[i]; + if (opt.selected) values.push(optionValue(opt)); + } + return values; + } + + function optionValue(opt) { + return Element.hasAttribute(opt, 'value') ? opt.value : opt.text; + } + + return { + input: input, + inputSelector: inputSelector, + textarea: valueSelector, + select: select, + selectOne: selectOne, + selectMany: selectMany, + optionValue: optionValue, + button: valueSelector + }; +})(); + +/*--------------------------------------------------------------------------*/ + + +Abstract.TimedObserver = Class.create(PeriodicalExecuter, { + initialize: function($super, element, frequency, callback) { + $super(callback, frequency); + this.element = $(element); + this.lastValue = this.getValue(); + }, + + execute: function() { + var value = this.getValue(); + if (Object.isString(this.lastValue) && Object.isString(value) ? + this.lastValue != value : String(this.lastValue) != String(value)) { + this.callback(this.element, value); + this.lastValue = value; + } + } +}); + +Form.Element.Observer = Class.create(Abstract.TimedObserver, { + getValue: function() { + return Form.Element.getValue(this.element); + } +}); + +Form.Observer = Class.create(Abstract.TimedObserver, { + getValue: function() { + return Form.serialize(this.element); + } +}); + +/*--------------------------------------------------------------------------*/ + +Abstract.EventObserver = Class.create({ + initialize: function(element, callback) { + this.element = $(element); + this.callback = callback; + + this.lastValue = this.getValue(); + if (this.element.tagName.toLowerCase() == 'form') + this.registerFormCallbacks(); + else + this.registerCallback(this.element); + }, + + onElementEvent: function() { + var value = this.getValue(); + if (this.lastValue != value) { + this.callback(this.element, value); + this.lastValue = value; + } + }, + + registerFormCallbacks: function() { + Form.getElements(this.element).each(this.registerCallback, this); + }, + + registerCallback: function(element) { + if (element.type) { + switch (element.type.toLowerCase()) { + case 'checkbox': + case 'radio': + Event.observe(element, 'click', this.onElementEvent.bind(this)); + break; + default: + Event.observe(element, 'change', this.onElementEvent.bind(this)); + break; + } + } + } +}); + +Form.Element.EventObserver = Class.create(Abstract.EventObserver, { + getValue: function() { + return Form.Element.getValue(this.element); + } +}); + +Form.EventObserver = Class.create(Abstract.EventObserver, { + getValue: function() { + return Form.serialize(this.element); + } +}); +(function(GLOBAL) { + var DIV = document.createElement('div'); + var docEl = document.documentElement; + var MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED = 'onmouseenter' in docEl + && 'onmouseleave' in docEl; + + var Event = { + KEY_BACKSPACE: 8, + KEY_TAB: 9, + KEY_RETURN: 13, + KEY_ESC: 27, + KEY_LEFT: 37, + KEY_UP: 38, + KEY_RIGHT: 39, + KEY_DOWN: 40, + KEY_DELETE: 46, + KEY_HOME: 36, + KEY_END: 35, + KEY_PAGEUP: 33, + KEY_PAGEDOWN: 34, + KEY_INSERT: 45 + }; + + + var isIELegacyEvent = function(event) { return false; }; + + if (window.attachEvent) { + if (window.addEventListener) { + isIELegacyEvent = function(event) { + return !(event instanceof window.Event); + }; + } else { + isIELegacyEvent = function(event) { return true; }; + } + } + + var _isButton; + + function _isButtonForDOMEvents(event, code) { + return event.which ? (event.which === code + 1) : (event.button === code); + } + + var legacyButtonMap = { 0: 1, 1: 4, 2: 2 }; + function _isButtonForLegacyEvents(event, code) { + return event.button === legacyButtonMap[code]; + } + + function _isButtonForWebKit(event, code) { + switch (code) { + case 0: return event.which == 1 && !event.metaKey; + case 1: return event.which == 2 || (event.which == 1 && event.metaKey); + case 2: return event.which == 3; + default: return false; + } + } + + if (window.attachEvent) { + if (!window.addEventListener) { + _isButton = _isButtonForLegacyEvents; + } else { + _isButton = function(event, code) { + return isIELegacyEvent(event) ? _isButtonForLegacyEvents(event, code) : + _isButtonForDOMEvents(event, code); + } + } + } else if (Prototype.Browser.WebKit) { + _isButton = _isButtonForWebKit; + } else { + _isButton = _isButtonForDOMEvents; + } + + function isLeftClick(event) { return _isButton(event, 0) } + + function isMiddleClick(event) { return _isButton(event, 1) } + + function isRightClick(event) { return _isButton(event, 2) } + + function element(event) { + return Element.extend(_element(event)); + } + + function _element(event) { + event = Event.extend(event); + + var node = event.target, type = event.type, + currentTarget = event.currentTarget; + + if (currentTarget && currentTarget.tagName) { + if (type === 'load' || type === 'error' || + (type === 'click' && currentTarget.tagName.toLowerCase() === 'input' + && currentTarget.type === 'radio')) + node = currentTarget; + } + + if (node.nodeType == Node.TEXT_NODE) + node = node.parentNode; + + return Element.extend(node); + } + + function findElement(event, expression) { + var element = _element(event), match = Prototype.Selector.match; + if (!expression) return Element.extend(element); + while (element) { + if (Object.isElement(element) && match(element, expression)) + return Element.extend(element); + element = element.parentNode; + } + } + + function pointer(event) { + return { x: pointerX(event), y: pointerY(event) }; + } + + function pointerX(event) { + var docElement = document.documentElement, + body = document.body || { scrollLeft: 0 }; + + return event.pageX || (event.clientX + + (docElement.scrollLeft || body.scrollLeft) - + (docElement.clientLeft || 0)); + } + + function pointerY(event) { + var docElement = document.documentElement, + body = document.body || { scrollTop: 0 }; + + return event.pageY || (event.clientY + + (docElement.scrollTop || body.scrollTop) - + (docElement.clientTop || 0)); + } + + + function stop(event) { + Event.extend(event); + event.preventDefault(); + event.stopPropagation(); + + event.stopped = true; + } + + + Event.Methods = { + isLeftClick: isLeftClick, + isMiddleClick: isMiddleClick, + isRightClick: isRightClick, + + element: element, + findElement: findElement, + + pointer: pointer, + pointerX: pointerX, + pointerY: pointerY, + + stop: stop + }; + + var methods = Object.keys(Event.Methods).inject({ }, function(m, name) { + m[name] = Event.Methods[name].methodize(); + return m; + }); + + if (window.attachEvent) { + function _relatedTarget(event) { + var element; + switch (event.type) { + case 'mouseover': + case 'mouseenter': + element = event.fromElement; + break; + case 'mouseout': + case 'mouseleave': + element = event.toElement; + break; + default: + return null; + } + return Element.extend(element); + } + + var additionalMethods = { + stopPropagation: function() { this.cancelBubble = true }, + preventDefault: function() { this.returnValue = false }, + inspect: function() { return '[object Event]' } + }; + + Event.extend = function(event, element) { + if (!event) return false; + + if (!isIELegacyEvent(event)) return event; + + if (event._extendedByPrototype) return event; + event._extendedByPrototype = Prototype.emptyFunction; + + var pointer = Event.pointer(event); + + Object.extend(event, { + target: event.srcElement || element, + relatedTarget: _relatedTarget(event), + pageX: pointer.x, + pageY: pointer.y + }); + + Object.extend(event, methods); + Object.extend(event, additionalMethods); + + return event; + }; + } else { + Event.extend = Prototype.K; + } + + if (window.addEventListener) { + Event.prototype = window.Event.prototype || document.createEvent('HTMLEvents').__proto__; + Object.extend(Event.prototype, methods); + } + + var EVENT_TRANSLATIONS = { + mouseenter: 'mouseover', + mouseleave: 'mouseout' + }; + + function getDOMEventName(eventName) { + return EVENT_TRANSLATIONS[eventName] || eventName; + } + + if (MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED) + getDOMEventName = Prototype.K; + + function getUniqueElementID(element) { + if (element === window) return 0; + + if (typeof element._prototypeUID === 'undefined') + element._prototypeUID = Element.Storage.UID++; + return element._prototypeUID; + } + + function getUniqueElementID_IE(element) { + if (element === window) return 0; + if (element == document) return 1; + return element.uniqueID; + } + + if ('uniqueID' in DIV) + getUniqueElementID = getUniqueElementID_IE; + + function isCustomEvent(eventName) { + return eventName.include(':'); + } + + Event._isCustomEvent = isCustomEvent; + + function getRegistryForElement(element, uid) { + var CACHE = GLOBAL.Event.cache; + if (Object.isUndefined(uid)) + uid = getUniqueElementID(element); + if (!CACHE[uid]) CACHE[uid] = { element: element }; + return CACHE[uid]; + } + + function destroyRegistryForElement(element, uid) { + if (Object.isUndefined(uid)) + uid = getUniqueElementID(element); + delete GLOBAL.Event.cache[uid]; + } + + + function register(element, eventName, handler) { + var registry = getRegistryForElement(element); + if (!registry[eventName]) registry[eventName] = []; + var entries = registry[eventName]; + + var i = entries.length; + while (i--) + if (entries[i].handler === handler) return null; + + var uid = getUniqueElementID(element); + var responder = GLOBAL.Event._createResponder(uid, eventName, handler); + var entry = { + responder: responder, + handler: handler + }; + + entries.push(entry); + return entry; + } + + function unregister(element, eventName, handler) { + var registry = getRegistryForElement(element); + var entries = registry[eventName]; + if (!entries) return; + + var i = entries.length, entry; + while (i--) { + if (entries[i].handler === handler) { + entry = entries[i]; + break; + } + } + + if (!entry) return; + + var index = entries.indexOf(entry); + entries.splice(index, 1); + + return entry; + } + + + function observe(element, eventName, handler) { + element = $(element); + var entry = register(element, eventName, handler); + + if (entry === null) return element; + + var responder = entry.responder; + if (isCustomEvent(eventName)) + observeCustomEvent(element, eventName, responder); + else + observeStandardEvent(element, eventName, responder); + + return element; + } + + function observeStandardEvent(element, eventName, responder) { + var actualEventName = getDOMEventName(eventName); + if (element.addEventListener) { + element.addEventListener(actualEventName, responder, false); + } else { + element.attachEvent('on' + actualEventName, responder); + } + } + + function observeCustomEvent(element, eventName, responder) { + if (element.addEventListener) { + element.addEventListener('dataavailable', responder, false); + } else { + element.attachEvent('ondataavailable', responder); + element.attachEvent('onlosecapture', responder); + } + } + + function stopObserving(element, eventName, handler) { + element = $(element); + var handlerGiven = !Object.isUndefined(handler), + eventNameGiven = !Object.isUndefined(eventName); + + if (!eventNameGiven && !handlerGiven) { + stopObservingElement(element); + return element; + } + + if (!handlerGiven) { + stopObservingEventName(element, eventName); + return element; + } + + var entry = unregister(element, eventName, handler); + + if (!entry) return element; + removeEvent(element, eventName, entry.responder); + return element; + } + + function stopObservingStandardEvent(element, eventName, responder) { + var actualEventName = getDOMEventName(eventName); + if (element.removeEventListener) { + element.removeEventListener(actualEventName, responder, false); + } else { + element.detachEvent('on' + actualEventName, responder); + } + } + + function stopObservingCustomEvent(element, eventName, responder) { + if (element.removeEventListener) { + element.removeEventListener('dataavailable', responder, false); + } else { + element.detachEvent('ondataavailable', responder); + element.detachEvent('onlosecapture', responder); + } + } + + + + function stopObservingElement(element) { + var uid = getUniqueElementID(element), + registry = getRegistryForElement(element, uid); + + destroyRegistryForElement(element, uid); + + var entries, i; + for (var eventName in registry) { + if (eventName === 'element') continue; + + entries = registry[eventName]; + i = entries.length; + while (i--) + removeEvent(element, eventName, entries[i].responder); + } + } + + function stopObservingEventName(element, eventName) { + var registry = getRegistryForElement(element); + var entries = registry[eventName]; + if (!entries) return; + delete registry[eventName]; + + var i = entries.length; + while (i--) + removeEvent(element, eventName, entries[i].responder); + } + + + function removeEvent(element, eventName, handler) { + if (isCustomEvent(eventName)) + stopObservingCustomEvent(element, eventName, handler); + else + stopObservingStandardEvent(element, eventName, handler); + } + + + + function getFireTarget(element) { + if (element !== document) return element; + if (document.createEvent && !element.dispatchEvent) + return document.documentElement; + return element; + } + + function fire(element, eventName, memo, bubble) { + element = getFireTarget($(element)); + if (Object.isUndefined(bubble)) bubble = true; + memo = memo || {}; + + var event = fireEvent(element, eventName, memo, bubble); + return Event.extend(event); + } + + function fireEvent_DOM(element, eventName, memo, bubble) { + var event = document.createEvent('HTMLEvents'); + event.initEvent('dataavailable', bubble, true); + + event.eventName = eventName; + event.memo = memo; + + element.dispatchEvent(event); + return event; + } + + function fireEvent_IE(element, eventName, memo, bubble) { + var event = document.createEventObject(); + event.eventType = bubble ? 'ondataavailable' : 'onlosecapture'; + + event.eventName = eventName; + event.memo = memo; + + element.fireEvent(event.eventType, event); + return event; + } + + var fireEvent = document.createEvent ? fireEvent_DOM : fireEvent_IE; + + + + Event.Handler = Class.create({ + initialize: function(element, eventName, selector, callback) { + this.element = $(element); + this.eventName = eventName; + this.selector = selector; + this.callback = callback; + this.handler = this.handleEvent.bind(this); + }, + + + start: function() { + Event.observe(this.element, this.eventName, this.handler); + return this; + }, + + stop: function() { + Event.stopObserving(this.element, this.eventName, this.handler); + return this; + }, + + handleEvent: function(event) { + var element = Event.findElement(event, this.selector); + if (element) this.callback.call(this.element, event, element); + } + }); + + function on(element, eventName, selector, callback) { + element = $(element); + if (Object.isFunction(selector) && Object.isUndefined(callback)) { + callback = selector, selector = null; + } + + return new Event.Handler(element, eventName, selector, callback).start(); + } + + Object.extend(Event, Event.Methods); + + Object.extend(Event, { + fire: fire, + observe: observe, + stopObserving: stopObserving, + on: on + }); + + Element.addMethods({ + fire: fire, + + observe: observe, + + stopObserving: stopObserving, + + on: on + }); + + Object.extend(document, { + fire: fire.methodize(), + + observe: observe.methodize(), + + stopObserving: stopObserving.methodize(), + + on: on.methodize(), + + loaded: false + }); + + if (GLOBAL.Event) Object.extend(window.Event, Event); + else GLOBAL.Event = Event; + + GLOBAL.Event.cache = {}; + + function destroyCache_IE() { + GLOBAL.Event.cache = null; + } + + if (window.attachEvent) + window.attachEvent('onunload', destroyCache_IE); + + DIV = null; + docEl = null; +})(this); + +(function(GLOBAL) { + /* Code for creating leak-free event responders is based on work by + John-David Dalton. */ + + var docEl = document.documentElement; + var MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED = 'onmouseenter' in docEl + && 'onmouseleave' in docEl; + + function isSimulatedMouseEnterLeaveEvent(eventName) { + return !MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED && + (eventName === 'mouseenter' || eventName === 'mouseleave'); + } + + function createResponder(uid, eventName, handler) { + if (Event._isCustomEvent(eventName)) + return createResponderForCustomEvent(uid, eventName, handler); + if (isSimulatedMouseEnterLeaveEvent(eventName)) + return createMouseEnterLeaveResponder(uid, eventName, handler); + + return function(event) { + var cacheEntry = Event.cache[uid]; + var element = cacheEntry.element; + + Event.extend(event, element); + handler.call(element, event); + }; + } + + function createResponderForCustomEvent(uid, eventName, handler) { + return function(event) { + var cacheEntry = Event.cache[uid], element = cacheEntry.element; + + if (Object.isUndefined(event.eventName)) + return false; + + if (event.eventName !== eventName) + return false; + + Event.extend(event, element); + handler.call(element, event); + }; + } + + function createMouseEnterLeaveResponder(uid, eventName, handler) { + return function(event) { + var cacheEntry = Event.cache[uid], element = cacheEntry.element; + + Event.extend(event, element); + var parent = event.relatedTarget; + + while (parent && parent !== element) { + try { parent = parent.parentNode; } + catch(e) { parent = element; } + } + + if (parent === element) return; + handler.call(element, event); + } + } + + GLOBAL.Event._createResponder = createResponder; + docEl = null; +})(this); + +(function(GLOBAL) { + /* Support for the DOMContentLoaded event is based on work by Dan Webb, + Matthias Miller, Dean Edwards, John Resig, and Diego Perini. */ + + var TIMER; + + function fireContentLoadedEvent() { + if (document.loaded) return; + if (TIMER) window.clearTimeout(TIMER); + document.loaded = true; + document.fire('dom:loaded'); + } + + function checkReadyState() { + if (document.readyState === 'complete') { + document.detachEvent('onreadystatechange', checkReadyState); + fireContentLoadedEvent(); + } + } + + function pollDoScroll() { + try { + document.documentElement.doScroll('left'); + } catch (e) { + TIMER = pollDoScroll.defer(); + return; + } + + fireContentLoadedEvent(); + } + + if (document.addEventListener) { + document.addEventListener('DOMContentLoaded', fireContentLoadedEvent, false); + } else { + document.attachEvent('onreadystatechange', checkReadyState); + if (window == top) TIMER = pollDoScroll.defer(); + } + + Event.observe(window, 'load', fireContentLoadedEvent); +})(this); + + +Element.addMethods(); +/*------------------------------- DEPRECATED -------------------------------*/ + +Hash.toQueryString = Object.toQueryString; + +var Toggle = { display: Element.toggle }; + +Element.Methods.childOf = Element.Methods.descendantOf; + +var Insertion = { + Before: function(element, content) { + return Element.insert(element, {before:content}); + }, + + Top: function(element, content) { + return Element.insert(element, {top:content}); + }, + + Bottom: function(element, content) { + return Element.insert(element, {bottom:content}); + }, + + After: function(element, content) { + return Element.insert(element, {after:content}); + } +}; + +var $continue = new Error('"throw $continue" is deprecated, use "return" instead'); + +var Position = { + includeScrollOffsets: false, + + prepare: function() { + this.deltaX = window.pageXOffset + || document.documentElement.scrollLeft + || document.body.scrollLeft + || 0; + this.deltaY = window.pageYOffset + || document.documentElement.scrollTop + || document.body.scrollTop + || 0; + }, + + within: function(element, x, y) { + if (this.includeScrollOffsets) + return this.withinIncludingScrolloffsets(element, x, y); + this.xcomp = x; + this.ycomp = y; + this.offset = Element.cumulativeOffset(element); + + return (y >= this.offset[1] && + y < this.offset[1] + element.offsetHeight && + x >= this.offset[0] && + x < this.offset[0] + element.offsetWidth); + }, + + withinIncludingScrolloffsets: function(element, x, y) { + var offsetcache = Element.cumulativeScrollOffset(element); + + this.xcomp = x + offsetcache[0] - this.deltaX; + this.ycomp = y + offsetcache[1] - this.deltaY; + this.offset = Element.cumulativeOffset(element); + + return (this.ycomp >= this.offset[1] && + this.ycomp < this.offset[1] + element.offsetHeight && + this.xcomp >= this.offset[0] && + this.xcomp < this.offset[0] + element.offsetWidth); + }, + + overlap: function(mode, element) { + if (!mode) return 0; + if (mode == 'vertical') + return ((this.offset[1] + element.offsetHeight) - this.ycomp) / + element.offsetHeight; + if (mode == 'horizontal') + return ((this.offset[0] + element.offsetWidth) - this.xcomp) / + element.offsetWidth; + }, + + + cumulativeOffset: Element.Methods.cumulativeOffset, + + positionedOffset: Element.Methods.positionedOffset, + + absolutize: function(element) { + Position.prepare(); + return Element.absolutize(element); + }, + + relativize: function(element) { + Position.prepare(); + return Element.relativize(element); + }, + + realOffset: Element.Methods.cumulativeScrollOffset, + + offsetParent: Element.Methods.getOffsetParent, + + page: Element.Methods.viewportOffset, + + clone: function(source, target, options) { + options = options || { }; + return Element.clonePosition(target, source, options); + } +}; + +/*--------------------------------------------------------------------------*/ + +if (!document.getElementsByClassName) document.getElementsByClassName = function(instanceMethods){ + function iter(name) { + return name.blank() ? null : "[contains(concat(' ', @class, ' '), ' " + name + " ')]"; + } + + instanceMethods.getElementsByClassName = Prototype.BrowserFeatures.XPath ? + function(element, className) { + className = className.toString().strip(); + var cond = /\s/.test(className) ? $w(className).map(iter).join('') : iter(className); + return cond ? document._getElementsByXPath('.//*' + cond, element) : []; + } : function(element, className) { + className = className.toString().strip(); + var elements = [], classNames = (/\s/.test(className) ? $w(className) : null); + if (!classNames && !className) return elements; + + var nodes = $(element).getElementsByTagName('*'); + className = ' ' + className + ' '; + + for (var i = 0, child, cn; child = nodes[i]; i++) { + if (child.className && (cn = ' ' + child.className + ' ') && (cn.include(className) || + (classNames && classNames.all(function(name) { + return !name.toString().blank() && cn.include(' ' + name + ' '); + })))) + elements.push(Element.extend(child)); + } + return elements; + }; + + return function(className, parentElement) { + return $(parentElement || document.body).getElementsByClassName(className); + }; +}(Element.Methods); + +/*--------------------------------------------------------------------------*/ + +Element.ClassNames = Class.create(); +Element.ClassNames.prototype = { + initialize: function(element) { + this.element = $(element); + }, + + _each: function(iterator, context) { + this.element.className.split(/\s+/).select(function(name) { + return name.length > 0; + })._each(iterator, context); + }, + + set: function(className) { + this.element.className = className; + }, + + add: function(classNameToAdd) { + if (this.include(classNameToAdd)) return; + this.set($A(this).concat(classNameToAdd).join(' ')); + }, + + remove: function(classNameToRemove) { + if (!this.include(classNameToRemove)) return; + this.set($A(this).without(classNameToRemove).join(' ')); + }, + + toString: function() { + return $A(this).join(' '); + } +}; + +Object.extend(Element.ClassNames.prototype, Enumerable); + +/*--------------------------------------------------------------------------*/ + +(function() { + window.Selector = Class.create({ + initialize: function(expression) { + this.expression = expression.strip(); + }, + + findElements: function(rootElement) { + return Prototype.Selector.select(this.expression, rootElement); + }, + + match: function(element) { + return Prototype.Selector.match(element, this.expression); + }, + + toString: function() { + return this.expression; + }, + + inspect: function() { + return "#"; + } + }); + + Object.extend(Selector, { + matchElements: function(elements, expression) { + var match = Prototype.Selector.match, + results = []; + + for (var i = 0, length = elements.length; i < length; i++) { + var element = elements[i]; + if (match(element, expression)) { + results.push(Element.extend(element)); + } + } + return results; + }, + + findElement: function(elements, expression, index) { + index = index || 0; + var matchIndex = 0, element; + for (var i = 0, length = elements.length; i < length; i++) { + element = elements[i]; + if (Prototype.Selector.match(element, expression) && index === matchIndex++) { + return Element.extend(element); + } + } + }, + + findChildElements: function(element, expressions) { + var selector = expressions.toArray().join(', '); + return Prototype.Selector.select(selector, element || document); + } + }); +})(); diff --git a/src/html/ReqMgr/javascript/utils.js b/src/html/ReqMgr/javascript/utils.js index af9d76e6cc..4452f88271 100644 --- a/src/html/ReqMgr/javascript/utils.js +++ b/src/html/ReqMgr/javascript/utils.js @@ -171,4 +171,88 @@ var utils = } -} // utils \ No newline at end of file +} // utils + +/* + * ReqMgr specific utilities + * Author: Valentin Kuznetsov + */ +function getTagValue(tag) +{ + return document.getElementById(tag).value; +} +function updateTag(tag, val) { + var id = document.getElementById(tag); + if (id) { + id.value=val; + } +} +function ClearTag(tag) { + var id=document.getElementById(tag); + if (id) { + id.innerHTML=""; + } +} +function HideTag(tag) { + var id=document.getElementById(tag); + if (id) { + id.className="hide"; + } +} +function ShowTag(tag) { + var id=document.getElementById(tag); + if (id) { + id.className="show"; + } +} +function FlipTag(tag) { + var id=document.getElementById(tag); + if (id) { + if (id.className == "show") { + id.className="hide"; + } else { + id.className="show"; + } + } +} +function ToggleTag(tag, link_tag) { + var id=document.getElementById(tag); + if (id) { + if (id.className == "show") { + id.className="hide"; + } else { + id.className="show"; + } + } + var lid=document.getElementById(link_tag); + if (lid) { + if (lid.innerHTML == "show") { + lid.innerHTML="hide"; + } else { + lid.innerHTML="show"; + } + } +} +function load(url) { + window.location.href=url; +} +function reload() { + load(window.location.href); +} +// Select all checkboxes +function CheckAll(bx) { + var cbs = document.getElementsByTagName('input'); + for(var i=0; i < cbs.length; i++) { + if(cbs[i].type == 'checkbox') { + cbs[i].checked = bx.checked; + } + } +} +// change menu items +function ChangeMenuItem(cls, tag) { + var items = document.getElementsByClassName(cls); + for (var i = 0; i < items.length; i++ ) { + items[i].className=cls; + } + tag.parentNode.className = cls+" active underline" +} diff --git a/src/html/ReqMgr/style/kube.min.css b/src/html/ReqMgr/style/kube.min.css new file mode 100644 index 0000000000..b31deb6490 --- /dev/null +++ b/src/html/ReqMgr/style/kube.min.css @@ -0,0 +1 @@ +.vertical-align{position:relative;top:50%;-webkit-transform:translateY(-50%);-ms-transform:translateY(-50%);transform:translateY(-50%)}*,*:before,*:after{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}html,body,div,span,object,iframe,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td,article,aside,canvas,details,embed,figure,figcaption,footer,header,hgroup,menu,nav,output,ruby,section,summary,time,mark,audio,video,h1,h2,h3,h4,h5,h6{margin:0;padding:0;border:0;outline:0;font-size:100%;vertical-align:baseline;background:transparent}a:active,a:hover{outline:0}button,input{line-height:normal}button,select{text-transform:none}article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,summary{display:block}audio,canvas,video{display:inline-block}audio:not([controls]){display:none;height:0}blockquote,q{quotes:none}blockquote p:before,blockquote p:after,q:before,q:after{content:'';content:none}table{border-collapse:collapse;border-spacing:0}caption,th,td{text-align:left;vertical-align:top;font-weight:normal}thead th,thead td{font-weight:bold;vertical-align:bottom}a img,th img,td img{vertical-align:top}button,input,select,textarea{margin:0}textarea{overflow:auto;vertical-align:top;resize:vertical}button{width:auto;overflow:visible}input[type=button],input[type=submit],button{cursor:pointer}input[type="radio"],input[type="checkbox"]{font-size:110%;position:relative;top:-1px}input[type="search"]{-webkit-appearance:textfield}input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}hr{display:block;height:1px;border:0;border-top:1px solid #ddd}img,video{max-width:100%;height:auto}img{-ms-interpolation-mode:bicubic}audio{width:100%}body{background:#fff;color:#333;font-size:14px;line-height:1.6em;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif}a{color:#3570c3}a:focus,a:hover{color:#d70a16}.title,h1,h2,h3,h4,h5,h6{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-weight:bold;color:#222;text-rendering:optimizeLegibility}h1{font-size:36px;line-height:1.111em;margin-bottom:.25em}h2{font-size:30px;line-height:1.111em;margin-bottom:.5em}h3{font-size:24px;line-height:1.333em;margin-bottom:.5em}h4{font-size:18px;line-height:1.5em;margin-bottom:.333em}h5{font-size:14px;line-height:1.6em;margin-bottom:.25em}h6{font-size:12px;text-transform:uppercase;line-height:1.6em;margin-bottom:.25em}hgroup h1:first-child,hgroup h2:first-child,hgroup h3:first-child,hgroup h4:first-child,hgroup h5:first-child{margin-bottom:0}h1.subheading,h2.subheading,h3.subheading,h4.subheading,h5.subheading{font-weight:300}h1.subheading{font-size:30px}h2.subheading{font-size:24px}h3.subheading{font-size:18px}h4.subheading{font-size:14px}h5.subheading{font-size:12px}.lead{font-size:18px;font-weight:300;line-height:1.4em;margin-bottom:.75em}p,ul,ol,dl,dd,dt,blockquote,td,th{line-height:1.6em}ul,ol,ul ul,ol ol,ul ol,ol ul{margin:0 0 0 1.6em}ol ol li{list-style-type:lower-alpha}ol ol ol li{list-style-type:lower-roman}p,ul,ol,dl,blockquote,hr,pre,table,form,fieldset,figure,address{margin-bottom:1.6em}address{font-style:normal}dl dt{font-weight:500}dd{margin-left:1em}blockquote{font-size:16px;font-style:italic;position:relative;padding-left:1.6em;margin-left:0;border-left:2px solid #dbdada}blockquote p{margin-bottom:.5em}blockquote small,cite{color:#777;font-style:italic}small,blockquote cite{font-size:12px;line-height:1}del{text-decoration:line-through}abbr[title],dfn[title]{border-bottom:1px dotted #000;cursor:help}strong,b{font-weight:bold}em,i{font-style:italic}sub,sup{font-size:10px;line-height:0;position:relative}sup{top:-0.5em}sub{bottom:-0.25em}figcaption{font-size:12px;font-style:italic}ins,mark{background-color:#fddc43;color:#0f0f0f;text-decoration:none}pre,code,kbd,samp{font-size:90%;font-family:Menlo,Monaco,Consolas,"Courier New",monospace}pre{font-size:90%;line-height:1.45em;color:#333;background:#f6f6f6;padding:1.6em;overflow:auto}code{padding:3px 3px 1px 3px;display:inline-block;line-height:1;background:#f6f6f6;border:1px solid #ddd}pre code{font-size:100%;border:0;padding:0;background:0;line-height:1.6em}kbd{padding:2px 6px 1px 6px;line-height:1;display:inline-block;border-radius:.3em;box-shadow:0 1px 0 rgba(0,0,0,0.2),0 0 0 1px #fff inset;background-color:#fafafa;border:1px solid #ccc;color:#333;font-weight:normal;white-space:nowrap}button:active,button:focus{outline:0}input[type="text"],input[type="password"],input[type="email"],input[type="url"],input[type="phone"],input[type="tel"],input[type="number"],input[type="datetime"],input[type="date"],input[type="search"],input[type="datetime-local"],textarea,select[multiple="multiple"]{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;line-height:1;font-size:14px;border-radius:0;background:#fff;box-shadow:none;border:1px solid #bbbcc0;outline:0;padding:7px 5px;position:relative;z-index:2;-webkit-appearance:none}input[type="text"],input[type="password"],input[type="email"],input[type="url"],input[type="phone"],input[type="tel"],input[type="number"],input[type="datetime"],input[type="date"],input[type="search"],input[type="datetime-local"]{height:2.3em}input[type="range"]{position:relative;top:3px}select[multiple="multiple"],textarea{line-height:1.35em}fieldset{padding:1.6em;margin-bottom:1.6em;border:1px solid #dbdada}legend{font-weight:bold;padding:0 1em;margin-left:-1em}tfoot th,tfoot td{background-color:#f4f4f4}th,td{border-bottom:1px solid #eee;padding:8px 10px}table caption{text-transform:uppercase;padding:0 1em;color:#777;font-size:12px}.units-container:after,.units-row:after{content:"";display:table;clear:both}.units-container{padding-top:1px;margin-top:-1px}.units-row{margin-bottom:1.6em}.width-100,.unit-100{width:100%}.width-90,.unit-90{width:90%}.width-80,.unit-80{width:80%}.width-75,.unit-75{width:75%}.width-70,.unit-70{width:70%}.width-66,.unit-66{width:66.6%}.width-65,.unit-65{width:65%}.width-60,.unit-60{width:60%}.width-50,.unit-50{width:50%}.width-40,.unit-40{width:40%}.width-35,.unit-35{width:35%}.width-33,.unit-33{width:33.3%}.width-30,.unit-30{width:30%}.width-25,.unit-25{width:25%}.width-20,.unit-20{width:20%}.width-10,.unit-10{width:10%}input.width-100,input.unit-100{width:98.6%}textarea.width-100,textarea.unit-100{width:98.8%}select.width-100,select.unit-100{width:99.4%}.units-row .unit-90,.units-row .unit-80,.units-row .unit-75,.units-row .unit-70,.units-row .unit-66,.units-row .unit-65,.units-row .unit-60,.units-row .unit-50,.units-row .unit-40,.units-row .unit-35,.units-row .unit-33,.units-row .unit-30,.units-row .unit-25,.units-row .unit-20,.units-row .unit-10{float:left;margin-left:3%}.units-row .unit-90:first-child,.units-row .unit-80:first-child,.units-row .unit-75:first-child,.units-row .unit-70:first-child,.units-row .unit-66:first-child,.units-row .unit-65:first-child,.units-row .unit-60:first-child,.units-row .unit-50:first-child,.units-row .unit-40:first-child,.units-row .unit-35:first-child,.units-row .unit-33:first-child,.units-row .unit-30:first-child,.units-row .unit-25:first-child,.units-row .unit-20:first-child,.units-row .unit-10:first-child{margin-left:0}.units-row .unit-90{width:89.7%}.units-row .unit-80{width:79.4%}.units-row .unit-75{width:74.25%}.units-row .unit-70{width:69.1%}.units-row .unit-66{width:65.66666666666666%}.units-row .unit-65{width:65.66666666666666%}.units-row .unit-60{width:58.800000000000004%}.units-row .unit-50{width:48.5%}.units-row .unit-40{width:38.2%}.units-row .unit-35{width:31.333333333333332%}.units-row .unit-33{width:31.333333333333332%}.units-row .unit-30{width:27.9%}.units-row .unit-25{width:22.75%}.units-row .unit-20{width:17.6%}.units-row .unit-10{width:7.3%}.unit-push-90,.unit-push-80,.unit-push-75,.unit-push-70,.unit-push-66,.unit-push-65,.unit-push-60,.unit-push-50,.unit-push-40,.unit-push-35,.unit-push-33,.unit-push-30,.unit-push-25,.unit-push-20,.unit-push-10{position:relative}.unit-push-90{left:92.7%}.unit-push-80{left:82.4%}.unit-push-75{left:77.25%}.unit-push-70{left:72.1%}.unit-push-66{left:68.66666666666666%}.unit-push-65{left:68.66666666666666%}.unit-push-60{left:61.800000000000004%}.unit-push-50{left:51.5%}.unit-push-40{left:41.2%}.unit-push-35{left:34.33333333333333%}.unit-push-33{left:34.33333333333333%}.unit-push-30{left:30.9%}.unit-push-25{left:25.75%}.unit-push-20{left:20.6%}.unit-push-10{left:10.3%}.units-row .unit-push-right{float:right}.centered,.unit-centered{float:none!important;margin:0 auto!important}.unit-padding{padding:1.6em}.units-padding .unit-100,.units-padding .unit-90,.units-padding .unit-80,.units-padding .unit-75,.units-padding .unit-70,.units-padding .unit-66,.units-padding .unit-65,.units-padding .unit-60,.units-padding .unit-50,.units-padding .unit-40,.units-padding .unit-35,.units-padding .unit-33,.units-padding .unit-30,.units-padding .unit-25,.units-padding .unit-20,.units-padding .unit-10{padding:1.6em}.units-split .unit-90,.units-split .unit-80,.units-split .unit-75,.units-split .unit-70,.units-split .unit-66,.units-split .unit-65,.units-split .unit-60,.units-split .unit-50,.units-split .unit-40,.units-split .unit-35,.units-split .unit-33,.units-split .unit-30,.units-split .unit-25,.units-split .unit-20,.units-split .unit-10{margin-left:0}.units-split .unit-90{width:90%}.units-split .unit-80{width:80%}.units-split .unit-75{width:75%}.units-split .unit-70{width:70%}.units-split .unit-66{width:66.6%}.units-split .unit-65{width:65%}.units-split .unit-60{width:60%}.units-split .unit-50{width:50%}.units-split .unit-40{width:40%}.units-split .unit-35{width:35%}.units-split .unit-33{width:33.3%}.units-split .unit-30{width:30%}.units-split .unit-25{width:25%}.units-split .unit-20{width:20%}.units-split .unit-10{width:10%}@media only screen and (max-width:767px){.mobile-width-100{width:100%}.units-row .unit-90,.units-row .unit-80,.units-row .unit-75,.units-row .unit-70,.units-row .unit-66,.units-row .unit-65,.units-row .unit-60,.units-row .unit-50,.units-row .unit-40,.units-row .unit-35,.units-row .unit-33,.units-row .unit-30,.units-row .unit-25,.units-row .unit-20,.units-row .unit-10{width:100%;float:none;margin-left:0;margin-bottom:1.6em}.unit-push-90,.unit-push-80,.unit-push-75,.unit-push-70,.unit-push-66,.unit-push-65,.unit-push-60,.unit-push-50,.unit-push-40,.unit-push-35,.unit-push-33,.unit-push-30,.unit-push-25,.unit-push-20,.unit-push-10{left:0}.units-row .unit-push-right{float:none}.units-mobile-50 .unit-90,.units-mobile-50 .unit-80,.units-mobile-50 .unit-75,.units-mobile-50 .unit-70,.units-mobile-50 .unit-66,.units-mobile-50 .unit-65,.units-mobile-50 .unit-60,.units-mobile-50 .unit-40,.units-mobile-50 .unit-30,.units-mobile-50 .unit-35,.units-mobile-50 .unit-33,.units-mobile-50 .unit-25,.units-mobile-50 .unit-20,.units-mobile-50 .unit-10{float:left;margin-left:3%;width:48.5%}.units-mobile-50 .unit-90:first-child,.units-mobile-50 .unit-80:first-child,.units-mobile-50 .unit-75:first-child,.units-mobile-50 .unit-70:first-child,.units-mobile-50 .unit-66:first-child,.units-mobile-50 .unit-65:first-child,.units-mobile-50 .unit-60:first-child,.units-mobile-50 .unit-40:first-child,.units-mobile-50 .unit-35:first-child,.units-mobile-50 .unit-30:first-child,.units-mobile-50 .unit-33:first-child,.units-mobile-50 .unit-25:first-child,.units-mobile-50 .unit-20:first-child,.units-mobile-50 .unit-10:first-child{margin-left:0}}.blocks-2,.blocks-3,.blocks-4,.blocks-5,.blocks-6{padding-left:0;list-style:none;margin-left:-3%}.blocks-2:after,.blocks-3:after,.blocks-4:after,.blocks-5:after,.blocks-6:after{content:"";display:table;clear:both}.blocks-2 li,.blocks-3 li,.blocks-4 li,.blocks-5 li,.blocks-6 li{height:auto;float:left;margin-bottom:1.6em;margin-left:3%}.blocks-2>li{width:47%}.blocks-3>li{width:30.333333333333332%}.blocks-4>li{width:22%}.blocks-5>li{width:17%}.blocks-6>li{width:13.666666666666666%}.block-first{clear:both}@media only screen and (max-width:767px){.blocks-2,.blocks-3,.blocks-4,.blocks-5,.blocks-6{margin-left:0;margin-bottom:1.6em}.blocks-2>li,.blocks-3>li,.blocks-4>li,.blocks-5>li,.blocks-6>li{float:none;margin-left:0;width:100%}.blocks-mobile-50>li,.blocks-mobile-33>li{float:left;margin-left:3%}.blocks-mobile-33,.blocks-mobile-50{margin-left:-3%}.blocks-mobile-50>li{width:47%}.blocks-mobile-33>li{width:30.333333333333332%}}.navbar{margin-bottom:1.6em}.navbar:after{content:"";display:table;clear:both}.navbar-left{float:left}.navbar-right{float:right}.navbar ul{list-style:none;margin:0}.navbar ul:after{content:"";display:table;clear:both}.navbar ul li{float:left;margin-right:1.6em}.navbar-right ul li{margin-right:0;margin-left:1.6em}.navbar ul li a,.navbar ul li span{display:block;text-decoration:none}.navbar ul li a:hover{color:#d70a16}.navbar ul li.active a,.navbar ul li span{text-decoration:none;cursor:text;color:rgba(0,0,0,0.4)}.fullwidth ul{width:100%}.fullwidth li{float:none!important;margin:0;display:table-cell;width:1%;text-align:center}.fullwidth li a,.fullwidth li span{display:block}.nav{margin-bottom:1.6em}.nav ul{list-style:none;margin:0}.nav ul li ul{margin-left:2em;font-size:.95em}.nav ul li a,.nav ul li span{display:block;padding:5px 0}.nav ul li ul li a,.nav ul li ul li span{padding:4px 0}.nav ul li a{text-decoration:none}.nav ul li a:hover{color:#d70a16;text-decoration:underline}.nav ul li.active a,.nav ul li span{text-decoration:none;cursor:text;color:rgba(0,0,0,0.4)}.nav-stroked li{border-bottom:1px solid #eee}.nav-stroked li:last-child{border-bottom:0}.nav-stacked ul{border:1px solid #eee;border-bottom:0}.nav-stacked ul li{border-bottom:1px solid #eee}.nav-stacked ul li a,.nav-stacked ul li span{padding:5px 10px}.nav-stacked ul li a:hover{background-color:#f5f5f5}.nav-stats li{position:relative}.nav-stats li a,.nav-stats li span{padding-right:50px}.nav-stats sup{position:absolute;top:50%;right:0;color:rgba(0,0,0,0.4)}.nav-stats.nav-stacked sup{right:.5em}.breadcrumbs{margin-bottom:1.6em}.breadcrumbs:after{content:"";display:table;clear:both}.breadcrumbs ul{font-size:.9em;color:rgba(0,0,0,0.4);list-style:none;margin:0}.breadcrumbs ul:after{content:"";display:table;clear:both}.breadcrumbs ul li{float:left;margin-right:3px}.breadcrumbs li+li:before{content:" > ";color:#aaa;font-size:12px;margin:0 3px;position:relative;top:-1px}.breadcrumbs-sections li+li:before{content:" | ";top:0}.breadcrumbs-path li+li:before{content:" / ";top:0}.breadcrumbs ul li a{color:#0f0f0f;text-decoration:none}.breadcrumbs ul li.active a{text-decoration:none;cursor:text;color:rgba(0,0,0,0.4)}.breadcrumbs ul li a:hover{color:#0f0f0f;text-decoration:underline}.pagination{position:relative;left:-9px;margin-left:0;list-style:none}.pagination:after{content:"";display:table;clear:both}.pagination li{float:left;margin-right:2px}.pagination li a,.pagination li span{display:block;padding:7px 9px;line-height:1;border-radius:3px;color:#0f0f0f;text-decoration:none}.pagination span{border:1px solid #ddd}.pagination li a:focus,.pagination li a:hover{text-decoration:none;background-color:#0f0f0f;color:#fff}@media only screen and (max-width:767px){.navbar-left,.navbar-right,.navbar ul li,.navbar-right ul li,.subnav-right ul,.subnav ul li,.subnav-right ul li{float:none;text-align:center}.navbar ul li,.navbar-right ul li,.subnav ul li,.subnav-right ul li{margin-left:0;margin-right:0}.fullwidth ul{width:auto}.fullwidth li{display:block;width:auto}}table.table-bordered td,table.table-bordered th{border:1px solid #ddd}table.table-simple td,table.table-simple th{border:0;padding-left:0}table.table-flat td,table.table-flat th{border:0;padding:0}table.table-striped tbody tr:nth-child(odd) td{background-color:#f8f8f8}table.table-hovered tbody tr:hover td{background-color:#f4f4f4}.table-container{width:100%;overflow:auto;margin-bottom:1.6em}.table-container table{margin-bottom:0}.table-container::-webkit-scrollbar{-webkit-appearance:none;width:14px;height:14px}.table-container::-webkit-scrollbar-thumb{border-radius:8px;border:3px solid #fff;background-color:rgba(0,0,0,0.3)}.forms label{display:block;margin-bottom:1.6em}.forms fieldset{padding-bottom:.5em;border-radius:4px}.forms input[type="text"],.forms input[type="password"],.forms input[type="email"],.forms input[type="url"],.forms input[type="phone"],.forms input[type="tel"],.forms input[type="number"],.forms input[type="datetime"],.forms input[type="date"],.forms input[type="search"],.forms input[type="range"],.forms input[type="file"],.forms input[type="datetime-local"],.forms textarea,.forms select{display:block}.forms-inline input[type="text"],.forms-inline-list input[type="text"],.forms-inline input[type="password"],.forms-inline-list input[type="password"],.forms-inline input[type="email"],.forms-inline-list input[type="email"],.forms-inline input[type="url"],.forms-inline-list input[type="url"],.forms-inline input[type="phone"],.forms-inline-list input[type="phone"],.forms-inline input[type="tel"],.forms-inline-list input[type="tel"],.forms-inline input[type="number"],.forms-inline-list input[type="number"],.forms-inline input[type="datetime"],.forms-inline-list input[type="datetime"],.forms-inline input[type="date"],.forms-inline-list input[type="date"],.forms-inline input[type="search"],.forms-inline-list input[type="search"],.forms-inline input[type="range"],.forms-inline-list input[type="range"],.forms-inline input[type="file"],.forms-inline-list input[type="file"],.forms-inline input[type="datetime-local"],.forms-inline-list input[type="datetime-local"],.forms-inline textarea,.forms-inline-list textarea,.forms-inline select,.forms-inline-list select{display:inline-block}.forms-list,.forms-inline-list{margin:0;padding:0;margin-bottom:1.6em;list-style:none}.forms-list label,.forms-inline-list li,.forms-inline-list li label{display:inline-block;margin-bottom:0}.forms-inline-list li label{margin-right:1.6em}.forms-inline-list li{margin-bottom:3px}.forms-list li{margin-bottom:6px}.forms-desc{margin-top:4px;color:rgba(0,0,0,0.4);font-size:12px;line-height:1.4em}input[type="radio"],input[type="checkbox"]{position:relative;top:-1px}input[type="text"],input[type="password"],input[type="email"],input[type="url"],input[type="phone"],input[type="tel"],input[type="number"],input[type="datetime"],input[type="date"],input[type="search"],input[type="datetime-local"],textarea{-moz-transition:border ease .5s;transition:border ease .5s}input[type="range"]{position:relative;top:3px}textarea{line-height:1.4em}select{margin-bottom:0!important}.error,.success{font-weight:normal;font-size:12px}input.input-error,textarea.input-error,select.input-error,.input-error{border-color:#d70a16;box-shadow:0 0 0 2px rgba(215,10,22,0.3),0 1px 2px rgba(0,0,0,0.2) inset}input.input-success,textarea.input-success,select.input-success,.input-success{border-color:#159776;box-shadow:0 0 0 2px rgba(21,151,118,0.3),0 1px 2px rgba(0,0,0,0.2) inset}input.input-gray,textarea.input-gray,select.input-gray,.input-gray{border-color:#ccc;box-shadow:0 0 0 2px rgba(204,204,204,0.3),0 1px 2px rgba(0,0,0,0.2) inset}input:focus,textarea:focus{outline:0;border-color:#5ca9e4;box-shadow:0 0 0 2px rgba(70,161,231,0.3),0 1px 2px rgba(0,0,0,0.2) inset}input.input-search,input[type="search"]{padding-right:10px;padding-left:10px;margin-bottom:0;border-radius:15px}.input-groups{display:table!important}.input-groups input{width:100%}.input-groups input,.input-groups .input-append,.input-groups .input-prepend,.input-groups .btn-append{display:table-cell!important}.input-groups .btn-append,.input-groups .input-append,.input-groups .input-prepend{width:1%;vertical-align:middle}.input-groups .input-append,.input-groups .input-prepend{background-color:#eee;border:1px solid #ccc;margin:0;padding:0 7px;color:rgba(0,0,0,0.5);line-height:1;font-size:12px;white-space:nowrap}.input-groups .input-prepend{border-right:0}.input-groups .input-append{position:relative;z-index:1;border-left:none}.input-groups .btn-append .btn{display:block;height:auto;border-radius:0 4px 4px 0}@media only screen and (max-width:767px){.forms-list label{display:inline-block}}.btn{display:inline-block;vertical-align:top;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;font-weight:400;line-height:1;text-align:center;text-decoration:none;color:#333;margin:0;border:0;border-radius:3px;box-shadow:none;cursor:pointer;background:#eeeded;padding:9px 20px;height:2.3em}.btn:hover{color:rgba(0,0,0,0.5);background:#dbdada}input[type="submit"].btn{-webkit-appearance:none}.btn-small{font-size:11px;padding:7px 16px}.btn-outline{background:0;border:1px solid #ddd;padding:8px 19px}.btn-outline.btn-small{padding:6px 15px}.btn-blue,.btn-red,.btn-green,.btn-black{color:#fff}.btn-blue:hover,.btn-red:hover,.btn-green:hover,.btn-black:hover{color:rgba(255,255,255,0.7)}.btn-white{background:#fff}.btn-white:hover{background:#f2f2f2}.btn-white.btn-outline{background:0;border-color:rgba(255,255,255,0.85);color:rgba(255,255,255,0.85)}.btn-white.btn-outline:hover{color:rgba(0,0,0,0.9);background:#fff}.btn-blue{background:#3d58a8}.btn-blue:hover{background:#2f4483}.btn-red{background:#d70a16}.btn-red:hover{background:#a60811}.btn-green{background:#159776}.btn-green:hover{background:#0f6a53}.btn-yellow{background:#fddc43;color:#000}.btn-yellow:hover{background:#e7be02}.btn-black{background:#000}.btn-black:hover{background:#333}.btn-active,.btn[disabled],.btn-disabled{border:0;background:0;background-color:#dbdada;color:rgba(0,0,0,0.5)}.btn-active:hover,.btn[disabled]:hover,.btn-disabled:hover{color:rgba(0,0,0,0.5)}.btn-active{box-shadow:0 1px 3px rgba(0,0,0,0.4) inset}.btn[disabled],.btn-disabled{cursor:default;box-shadow:none}.btn-blue.btn-active,.btn-red.btn-active,.btn-green.btn-active,.btn-black.btn-active{box-shadow:0 1px 3px rgba(0,0,0,0.7) inset}.btn-blue.btn-active,.btn-red.btn-active,.btn-green.btn-active,.btn-black.btn-active,.btn-blue.btn-disabled,.btn-red.btn-disabled,.btn-green.btn-disabled,.btn-black.btn-disabled,.btn-blue[disabled],.btn-red[disabled],.btn-green[disabled],.btn-black[disabled]{color:rgba(255,255,255,0.6)}.btn-blue.btn-active:hover,.btn-red.btn-active:hover,.btn-green.btn-active:hover,.btn-black.btn-active:hover,.btn-blue.btn-disabled:hover,.btn-red.btn-disabled:hover,.btn-green.btn-disabled:hover,.btn-black.btn-disabled:hover,.btn-blue[disabled]:hover,.btn-red[disabled]:hover,.btn-green[disabled]:hover,.btn-black[disabled]:hover{color:rgba(255,255,255,0.6)}.btn-white.btn-active,.btn-white.btn-disabled,.btn-white[disabled]{background:#f2f2f2}.btn-blue.btn-active,.btn-blue.btn-disabled,.btn-blue[disabled]{background:#2f4483}.btn-red.btn-active,.btn-red.btn-disabled,.btn-red[disabled]{background:#a60811}.btn-green.btn-active,.btn-green.btn-disabled,.btn-green[disabled]{background:#0f6a53}.btn-yellow.btn-active,.btn-yellow.btn-disabled,.btn-yellow[disabled]{background:#e7be02}.btn-black.btn-active,.btn-black.btn-disabled,.btn-black[disabled]{background:#4d4d4d}.group:after{content:"";display:table;clear:both}.small{font-size:12px}.nowrap,.nowrap td{white-space:nowrap}.req,.required{font-weight:normal;color:#d70a16}.color-gray{color:#777}.color-gray-light{color:#999}.color-black{color:#000}.color-red,.error{color:#d70a16}.color-green,.success{color:#159776}.text-centered{text-align:center}.text-right{text-align:right}.last{margin-right:0!important}.pause{margin-bottom:.8em!important}.end{margin-bottom:0!important}.normal{font-weight:normal}.bold{font-weight:bold}.italic{font-style:italic}.left{float:left}.right{float:right}.video-wrapper{height:0;padding-bottom:56.25%;position:relative;margin-bottom:1.6em}.video-wrapper iframe,.video-wrapper object,.video-wrapper embed{position:absolute;top:0;left:0;width:100%;height:100%}@media only screen and (max-width:767px){.left,.right{float:none}}.str{color:#d14}.kwd{color:#333}.com{color:#998}.typ{color:#458}.lit{color:#458}.pun{color:#888}.opn{color:#333}.clo{color:#333}.tag{color:#367ac3}.atn{color:#51a7c9}.atv{color:#709c1a}.dec{color:#666}.var{color:teal}.fun{color:#900} \ No newline at end of file diff --git a/src/html/ReqMgr/style/kube301-less/LICENSE b/src/html/ReqMgr/style/kube301-less/LICENSE new file mode 100644 index 0000000000..1f688168d4 --- /dev/null +++ b/src/html/ReqMgr/style/kube301-less/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2011-2014 Twitter, Inc + +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. \ No newline at end of file diff --git a/src/html/ReqMgr/style/kube301-less/README.md b/src/html/ReqMgr/style/kube301-less/README.md new file mode 100644 index 0000000000..8b4b7c3c78 --- /dev/null +++ b/src/html/ReqMgr/style/kube301-less/README.md @@ -0,0 +1,51 @@ +Kube CSS-Framework +==== + +Kube is a minimalistic CSS-framework for professional developers and designers. + +Here’s your most advanced and flexible CSS-framework. Take a look. Kube doesn’t force you into specific styling, Kube doesn’t require rewriting its code; it leaves all of the important decisions to you and your creative genius. Think about Kube as if it was your most reliable helper, who would do your routine job with a smile on his face, so that you can actually concentrate on something big and wonderful. + +##[Documentation and examples](http://imperavi.com/kube/) + +- [Details](http://imperavi.com/kube/) +- [Typography](http://imperavi.com/kube/typography/) +- [Grid](http://imperavi.com/kube/grid/) +- [Blocks](http://imperavi.com/kube/blocks/) +- [Tables](http://imperavi.com/kube/tables/) +- [Forms](http://imperavi.com/kube/forms/) +- [Navigation](http://imperavi.com/kube/navigation/) +- [Buttons](http://imperavi.com/kube/buttons/) +- [Helpers](http://imperavi.com/kube/helpers/) +- [Mixins](http://imperavi.com/kube/mixins/) + +####Works in every modern browser + +Kube works in all modern browsers on desktops and mobile devices: + +- Latest Chrome +- Latest Firefox +- Latest Safari +- Latest Opera +- IE9+ +- and mobile browsers + +####License + +Kube licensed under [MIT](http://opensource.org/licenses/MIT). Kube Framework is absolutely free for personal or commercial use. + +####License FAQ + +Q: Can I use Kube in my open-source project?
+A: Yes, you can! + +Q: Can I fork Kube on Github for any needs whatsoever?
+A: Sure! + +Q: Can I redistribute my modification of Kube?
+A: Yes, you can! + +Q: Can I sell Kube or my modification of Kube?
+A: No problem, go ahead! + +Q: Can I rename Kube to Mube and distribute it as if it was my product?
+A: Well yes, if you absolutely have to… We don't appreciate such things though. \ No newline at end of file diff --git a/src/html/ReqMgr/style/kube301-less/css/.DS_Store b/src/html/ReqMgr/style/kube301-less/css/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..5008ddfcf53c02e82d7eee2e57c38e5672ef89f6 GIT binary patch literal 6148 zcmeH~Jr2S!425mzP>H1@V-^m;4Wg<&0T*E43hX&L&p$$qDprKhvt+--jT7}7np#A3 zem<@ulZcFPQ@L2!n>{z**++&mCkOWA81W14cNZlEfg7;MkzE(HCqgga^y>{tEnwC%0;vJ&^%eQ zLs35+`xjp>T0 li { + width: 47%; +} +.blocks-3 > li { + width: 30.333333333333332%; +} +.blocks-4 > li { + width: 22%; +} +.blocks-5 > li { + width: 17%; +} +.blocks-6 > li { + width: 13.666666666666666%; +} +.block-first { + clear: both; +} +/* =Responsive +-----------------------------------------------------------------------------*/ +@media only screen and (max-width: 767px) { + .blocks-2, + .blocks-3, + .blocks-4, + .blocks-5, + .blocks-6 { + margin-left: 0; + margin-bottom: 1.6em; + } + .blocks-2 > li, + .blocks-3 > li, + .blocks-4 > li, + .blocks-5 > li, + .blocks-6 > li { + float: none; + margin-left: 0; + width: 100%; + } + .blocks-mobile-50 > li, + .blocks-mobile-33 > li { + float: left; + margin-left: 3%; + } + .blocks-mobile-33, + .blocks-mobile-50 { + margin-left: -3%; + } + .blocks-mobile-50 > li { + width: 47%; + } + .blocks-mobile-33 > li { + width: 30.333333333333332%; + } +} +/* =Navs +-----------------------------------------------------------------------------*/ +/* NavBar */ +.navbar { + margin-bottom: 1.6em; +} +.navbar:after { + content: ""; + display: table; + clear: both; +} +.navbar-left { + float: left; +} +.navbar-right { + float: right; +} +.navbar ul { + list-style: none; + margin: 0; +} +.navbar ul:after { + content: ""; + display: table; + clear: both; +} +.navbar ul li { + float: left; + margin-right: 1.6em; +} +.navbar-right ul li { + margin-right: 0; + margin-left: 1.6em; +} +.navbar ul li a, +.navbar ul li span { + display: block; + text-decoration: none; +} +.navbar ul li a:hover { + color: #d70a16; +} +.navbar ul li.active a, +.navbar ul li span { + text-decoration: none; + cursor: text; + color: rgba(0, 0, 0, 0.4); +} +/* Fullwidth */ +.fullwidth ul { + width: 100%; +} +.fullwidth li { + float: none !important; + margin: 0; + display: table-cell; + width: 1%; + text-align: center; +} +.fullwidth li a, +.fullwidth li span { + display: block; +} +/* Vertical */ +.nav { + margin-bottom: 1.6em; +} +.nav ul { + list-style: none; + margin: 0; +} +.nav ul li ul { + margin-left: 2em; + font-size: .95em; +} +.nav ul li a, +.nav ul li span { + display: block; + padding: 5px 0; +} +.nav ul li ul li a, +.nav ul li ul li span { + padding: 4px 0; +} +.nav ul li a { + text-decoration: none; +} +.nav ul li a:hover { + color: #d70a16; + text-decoration: underline; +} +.nav ul li.active a, +.nav ul li span { + text-decoration: none; + cursor: text; + color: rgba(0, 0, 0, 0.4); +} +/* Stroked */ +.nav-stroked li { + border-bottom: 1px solid #eee; +} +.nav-stroked li:last-child { + border-bottom: none; +} +/* Stacked */ +.nav-stacked ul { + border: 1px solid #eee; + border-bottom: 0; +} +.nav-stacked ul li { + border-bottom: 1px solid #eee; +} +.nav-stacked ul li a, +.nav-stacked ul li span { + padding: 5px 10px; +} +.nav-stacked ul li a:hover { + background-color: #f5f5f5; +} +/* Stats */ +.nav-stats li { + position: relative; +} +.nav-stats li a, +.nav-stats li span { + padding-right: 50px; +} +.nav-stats sup { + position: absolute; + top: 50%; + right: 0; + color: rgba(0, 0, 0, 0.4); +} +.nav-stats.nav-stacked sup { + right: .5em; +} +/* Breadcrumbs */ +.breadcrumbs { + margin-bottom: 1.6em; +} +.breadcrumbs:after { + content: ""; + display: table; + clear: both; +} +.breadcrumbs ul { + font-size: .9em; + color: rgba(0, 0, 0, 0.4); + list-style: none; + margin: 0; +} +.breadcrumbs ul:after { + content: ""; + display: table; + clear: both; +} +.breadcrumbs ul li { + float: left; + margin-right: 3px; +} +.breadcrumbs li + li:before { + content: " > "; + color: #aaa; + font-size: 12px; + margin: 0 3px; + position: relative; + top: -1px; +} +.breadcrumbs-sections li + li:before { + content: " | "; + top: 0; +} +.breadcrumbs-path li + li:before { + content: " / "; + top: 0; +} +.breadcrumbs ul li a { + color: #0f0f0f; + text-decoration: none; +} +.breadcrumbs ul li.active a { + text-decoration: none; + cursor: text; + color: rgba(0, 0, 0, 0.4); +} +.breadcrumbs ul li a:hover { + color: #0f0f0f; + text-decoration: underline; +} +/* =Pagination +-----------------------------------------------------------------------------*/ +.pagination { + position: relative; + left: -9px; + margin-left: 0; + list-style: none; +} +.pagination:after { + content: ""; + display: table; + clear: both; +} +.pagination li { + float: left; + margin-right: 2px; +} +.pagination li a, +.pagination li span { + display: block; + padding: 7px 9px; + line-height: 1; + border-radius: 3px; + color: #0f0f0f; + text-decoration: none; +} +.pagination span { + border: 1px solid #ddd; +} +.pagination li a:focus, +.pagination li a:hover { + text-decoration: none; + background-color: #0f0f0f; + color: #fff; +} +/* =Responsive +-----------------------------------------------------------------------------*/ +@media only screen and (max-width: 767px) { + .navbar-left, + .navbar-right, + .navbar ul li, + .navbar-right ul li, + .subnav-right ul, + .subnav ul li, + .subnav-right ul li { + float: none; + text-align: center; + } + .navbar ul li, + .navbar-right ul li, + .subnav ul li, + .subnav-right ul li { + margin-left: 0; + margin-right: 0; + } + .fullwidth ul { + width: auto; + } + .fullwidth li { + display: block; + width: auto; + } +} +/* =Tables +-----------------------------------------------------------------------------*/ +table.table-bordered td, +table.table-bordered th { + border: 1px solid #ddd; +} +table.table-simple td, +table.table-simple th { + border: none; + padding-left: 0; +} +table.table-flat td, +table.table-flat th { + border: none; + padding: 0; +} +table.table-striped tbody tr:nth-child(odd) td { + background-color: #f8f8f8; +} +table.table-hovered tbody tr:hover td { + background-color: #f4f4f4; +} +/* Responsive Tables */ +.table-container { + width: 100%; + overflow: auto; + margin-bottom: 1.6em; +} +.table-container table { + margin-bottom: 0; +} +.table-container::-webkit-scrollbar { + -webkit-appearance: none; + width: 14px; + height: 14px; +} +.table-container::-webkit-scrollbar-thumb { + border-radius: 8px; + border: 3px solid #fff; + background-color: rgba(0, 0, 0, 0.3); +} +/* =Forms +-----------------------------------------------------------------------------*/ +.forms label { + display: block; + margin-bottom: 1.6em; +} +.forms fieldset { + padding-bottom: .5em; + border-radius: 4px; +} +.forms input[type="text"], +.forms input[type="password"], +.forms input[type="email"], +.forms input[type="url"], +.forms input[type="phone"], +.forms input[type="tel"], +.forms input[type="number"], +.forms input[type="datetime"], +.forms input[type="date"], +.forms input[type="search"], +.forms input[type="range"], +.forms input[type="file"], +.forms input[type="datetime-local"], +.forms textarea, +.forms select { + display: block; +} +.forms-inline input[type="text"], +.forms-inline-list input[type="text"], +.forms-inline input[type="password"], +.forms-inline-list input[type="password"], +.forms-inline input[type="email"], +.forms-inline-list input[type="email"], +.forms-inline input[type="url"], +.forms-inline-list input[type="url"], +.forms-inline input[type="phone"], +.forms-inline-list input[type="phone"], +.forms-inline input[type="tel"], +.forms-inline-list input[type="tel"], +.forms-inline input[type="number"], +.forms-inline-list input[type="number"], +.forms-inline input[type="datetime"], +.forms-inline-list input[type="datetime"], +.forms-inline input[type="date"], +.forms-inline-list input[type="date"], +.forms-inline input[type="search"], +.forms-inline-list input[type="search"], +.forms-inline input[type="range"], +.forms-inline-list input[type="range"], +.forms-inline input[type="file"], +.forms-inline-list input[type="file"], +.forms-inline input[type="datetime-local"], +.forms-inline-list input[type="datetime-local"], +.forms-inline textarea, +.forms-inline-list textarea, +.forms-inline select, +.forms-inline-list select { + display: inline-block; +} +.forms-list, +.forms-inline-list { + margin: 0; + padding: 0; + margin-bottom: 1.6em; + list-style: none; +} +.forms-list label, +.forms-inline-list li, +.forms-inline-list li label { + display: inline-block; + margin-bottom: 0; +} +.forms-inline-list li label { + margin-right: 1.6em; +} +.forms-inline-list li { + margin-bottom: 3px; +} +.forms-list li { + margin-bottom: 6px; +} +.forms-desc { + margin-top: 4px; + color: rgba(0, 0, 0, 0.4); + font-size: 12px; + line-height: 1.4em; +} +input[type="radio"], +input[type="checkbox"] { + position: relative; + top: -1px; +} +input[type="text"], +input[type="password"], +input[type="email"], +input[type="url"], +input[type="phone"], +input[type="tel"], +input[type="number"], +input[type="datetime"], +input[type="date"], +input[type="search"], +input[type="datetime-local"], +textarea { + -moz-transition: border ease 0.5s; + transition: border ease 0.5s; +} +input[type="range"] { + position: relative; + top: 3px; +} +textarea { + line-height: 1.4em; +} +select { + margin-bottom: 0 !important; +} +/* States */ +.error, +.success { + font-weight: normal; + font-size: 12px; +} +input.input-error, +textarea.input-error, +select.input-error, +.input-error { + border-color: #d70a16; + box-shadow: 0 0 0 2px rgba(215, 10, 22, 0.3), 0 1px 2px rgba(0, 0, 0, 0.2) inset; +} +input.input-success, +textarea.input-success, +select.input-success, +.input-success { + border-color: #159776; + box-shadow: 0 0 0 2px rgba(21, 151, 118, 0.3), 0 1px 2px rgba(0, 0, 0, 0.2) inset; +} +input.input-gray, +textarea.input-gray, +select.input-gray, +.input-gray { + border-color: #ccc; + box-shadow: 0 0 0 2px rgba(204, 204, 204, 0.3), 0 1px 2px rgba(0, 0, 0, 0.2) inset; +} +input:focus, +textarea:focus { + outline: none; + border-color: #5ca9e4; + box-shadow: 0 0 0 2px rgba(70, 161, 231, 0.3), 0 1px 2px rgba(0, 0, 0, 0.2) inset; +} +input.input-search, +input[type="search"] { + padding-right: 10px; + padding-left: 10px; + margin-bottom: 0; + border-radius: 15px; +} +/* Append & Prepend */ +.input-groups { + display: table !important; +} +.input-groups input { + width: 100%; +} +.input-groups input, +.input-groups .input-append, +.input-groups .input-prepend, +.input-groups .btn-append { + display: table-cell !important; +} +.input-groups .btn-append, +.input-groups .input-append, +.input-groups .input-prepend { + width: 1%; + vertical-align: middle; +} +.input-groups .input-append, +.input-groups .input-prepend { + background-color: #eee; + border: 1px solid #ccc; + margin: 0; + padding: 0 7px; + color: rgba(0, 0, 0, 0.5); + line-height: 1; + font-size: 12px; + white-space: nowrap; +} +.input-groups .input-prepend { + border-right: none; +} +.input-groups .input-append { + position: relative; + z-index: 1; + border-left: none; +} +.input-groups .btn-append .btn { + display: block; + height: auto; + border-radius: 0 4px 4px 0; +} +/* =Mobile (Portrait) +-----------------------------------------------------------------------------*/ +@media only screen and (max-width: 767px) { + .forms-list label { + display: inline-block; + } +} +/* =Buttons +-----------------------------------------------------------------------------*/ +.btn { + display: inline-block; + vertical-align: top; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 14px; + font-weight: 400; + line-height: 1; + text-align: center; + text-decoration: none; + color: #333333; + margin: 0; + border: none; + border-radius: 3px; + box-shadow: none; + cursor: pointer; + background: #eeeded; + padding: 9px 20px; + height: 2.3em; +} +.btn:hover { + color: rgba(0, 0, 0, 0.5); + background: #dbdada; +} +input[type="submit"].btn { + -webkit-appearance: none; +} +.btn-small { + font-size: 11px; + padding: 7px 16px; +} +.btn-outline { + background: none; + border: 1px solid #ddd; + padding: 8px 19px; +} +.btn-outline.btn-small { + padding: 6px 15px; +} +.btn-blue, +.btn-red, +.btn-green, +.btn-black { + color: #fff; +} +.btn-blue:hover, +.btn-red:hover, +.btn-green:hover, +.btn-black:hover { + color: rgba(255, 255, 255, 0.7); +} +.btn-white { + background: #fff; +} +.btn-white:hover { + background: #f2f2f2; +} +.btn-white.btn-outline { + background: none; + border-color: rgba(255, 255, 255, 0.85); + color: rgba(255, 255, 255, 0.85); +} +.btn-white.btn-outline:hover { + color: rgba(0, 0, 0, 0.9); + background: #fff; +} +.btn-blue { + background: #3d58a8; +} +.btn-blue:hover { + background: #2f4483; +} +.btn-red { + background: #d70a16; +} +.btn-red:hover { + background: #a60811; +} +.btn-green { + background: #159776; +} +.btn-green:hover { + background: #0f6a53; +} +.btn-yellow { + background: #fddc43; + color: #000; +} +.btn-yellow:hover { + background: #e7be02; +} +.btn-black { + background: #000; +} +.btn-black:hover { + background: #333333; +} +.btn-active, +.btn[disabled], +.btn-disabled { + border: none; + background: none; + background-color: #dbdada; + color: rgba(0, 0, 0, 0.5); +} +.btn-active:hover, +.btn[disabled]:hover, +.btn-disabled:hover { + color: rgba(0, 0, 0, 0.5); +} +.btn-active { + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.4) inset; +} +.btn[disabled], +.btn-disabled { + cursor: default; + box-shadow: none; +} +.btn-blue.btn-active, +.btn-red.btn-active, +.btn-green.btn-active, +.btn-black.btn-active { + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.7) inset; +} +.btn-blue.btn-active, +.btn-red.btn-active, +.btn-green.btn-active, +.btn-black.btn-active, +.btn-blue.btn-disabled, +.btn-red.btn-disabled, +.btn-green.btn-disabled, +.btn-black.btn-disabled, +.btn-blue[disabled], +.btn-red[disabled], +.btn-green[disabled], +.btn-black[disabled] { + color: rgba(255, 255, 255, 0.6); +} +.btn-blue.btn-active:hover, +.btn-red.btn-active:hover, +.btn-green.btn-active:hover, +.btn-black.btn-active:hover, +.btn-blue.btn-disabled:hover, +.btn-red.btn-disabled:hover, +.btn-green.btn-disabled:hover, +.btn-black.btn-disabled:hover, +.btn-blue[disabled]:hover, +.btn-red[disabled]:hover, +.btn-green[disabled]:hover, +.btn-black[disabled]:hover { + color: rgba(255, 255, 255, 0.6); +} +.btn-white.btn-active, +.btn-white.btn-disabled, +.btn-white[disabled] { + background: #f2f2f2; +} +.btn-blue.btn-active, +.btn-blue.btn-disabled, +.btn-blue[disabled] { + background: #2f4483; +} +.btn-red.btn-active, +.btn-red.btn-disabled, +.btn-red[disabled] { + background: #a60811; +} +.btn-green.btn-active, +.btn-green.btn-disabled, +.btn-green[disabled] { + background: #0f6a53; +} +.btn-yellow.btn-active, +.btn-yellow.btn-disabled, +.btn-yellow[disabled] { + background: #e7be02; +} +.btn-black.btn-active, +.btn-black.btn-disabled, +.btn-black[disabled] { + background: #4d4d4d; +} +/* =Helpers +-----------------------------------------------------------------------------*/ +.group:after { + content: ""; + display: table; + clear: both; +} +.small { + font-size: 12px; +} +.nowrap, +.nowrap td { + white-space: nowrap; +} +.req, +.required { + font-weight: normal; + color: #d70a16; +} +.color-gray { + color: #777; +} +.color-gray-light { + color: #999; +} +.color-black { + color: #000; +} +.color-red, +.error { + color: #d70a16; +} +.color-green, +.success { + color: #159776; +} +.text-centered { + text-align: center; +} +.text-right { + text-align: right; +} +.last { + margin-right: 0 !important; +} +.pause { + margin-bottom: 0.8em !important; +} +.end { + margin-bottom: 0 !important; +} +.normal { + font-weight: normal; +} +.bold { + font-weight: bold; +} +.italic { + font-style: italic; +} +.left { + float: left; +} +.right { + float: right; +} +/* Responsive embedded objects */ +.video-wrapper { + height: 0; + padding-bottom: 56.25%; + position: relative; + margin-bottom: 1.6em; +} +.video-wrapper iframe, +.video-wrapper object, +.video-wrapper embed { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; +} +/* =Responsive +-----------------------------------------------------------------------------*/ +@media only screen and (max-width: 767px) { + .left, + .right { + float: none; + } +} +/* string content */ +.str { + color: #dd1144; +} +/* a keyword */ +.kwd { + color: #333333; +} +/* a comment */ +.com { + color: #999988; +} +/* a type name */ +.typ { + color: #445588; +} +/* a literal value */ +.lit { + color: #445588; +} +/* punctuation */ +.pun { + color: #888; +} +/* lisp open bracket */ +.opn { + color: #333333; +} +/* lisp close bracket */ +.clo { + color: #333333; +} +/* a markup tag name */ +.tag { + color: #367ac3; +} +/* a markup attribute name */ +.atn { + color: #51a7c9; +} +/* a markup attribute value */ +.atv { + color: #709c1a; +} +/* a declaration */ +.dec { + color: #666; +} +/* a variable name */ +.var { + color: teal; +} +/* a function name */ +.fun { + color: #990000; +} diff --git a/src/html/ReqMgr/style/kube301-less/css/kube.min.css b/src/html/ReqMgr/style/kube301-less/css/kube.min.css new file mode 100644 index 0000000000..b31deb6490 --- /dev/null +++ b/src/html/ReqMgr/style/kube301-less/css/kube.min.css @@ -0,0 +1 @@ +.vertical-align{position:relative;top:50%;-webkit-transform:translateY(-50%);-ms-transform:translateY(-50%);transform:translateY(-50%)}*,*:before,*:after{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}html,body,div,span,object,iframe,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td,article,aside,canvas,details,embed,figure,figcaption,footer,header,hgroup,menu,nav,output,ruby,section,summary,time,mark,audio,video,h1,h2,h3,h4,h5,h6{margin:0;padding:0;border:0;outline:0;font-size:100%;vertical-align:baseline;background:transparent}a:active,a:hover{outline:0}button,input{line-height:normal}button,select{text-transform:none}article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,summary{display:block}audio,canvas,video{display:inline-block}audio:not([controls]){display:none;height:0}blockquote,q{quotes:none}blockquote p:before,blockquote p:after,q:before,q:after{content:'';content:none}table{border-collapse:collapse;border-spacing:0}caption,th,td{text-align:left;vertical-align:top;font-weight:normal}thead th,thead td{font-weight:bold;vertical-align:bottom}a img,th img,td img{vertical-align:top}button,input,select,textarea{margin:0}textarea{overflow:auto;vertical-align:top;resize:vertical}button{width:auto;overflow:visible}input[type=button],input[type=submit],button{cursor:pointer}input[type="radio"],input[type="checkbox"]{font-size:110%;position:relative;top:-1px}input[type="search"]{-webkit-appearance:textfield}input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}hr{display:block;height:1px;border:0;border-top:1px solid #ddd}img,video{max-width:100%;height:auto}img{-ms-interpolation-mode:bicubic}audio{width:100%}body{background:#fff;color:#333;font-size:14px;line-height:1.6em;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif}a{color:#3570c3}a:focus,a:hover{color:#d70a16}.title,h1,h2,h3,h4,h5,h6{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-weight:bold;color:#222;text-rendering:optimizeLegibility}h1{font-size:36px;line-height:1.111em;margin-bottom:.25em}h2{font-size:30px;line-height:1.111em;margin-bottom:.5em}h3{font-size:24px;line-height:1.333em;margin-bottom:.5em}h4{font-size:18px;line-height:1.5em;margin-bottom:.333em}h5{font-size:14px;line-height:1.6em;margin-bottom:.25em}h6{font-size:12px;text-transform:uppercase;line-height:1.6em;margin-bottom:.25em}hgroup h1:first-child,hgroup h2:first-child,hgroup h3:first-child,hgroup h4:first-child,hgroup h5:first-child{margin-bottom:0}h1.subheading,h2.subheading,h3.subheading,h4.subheading,h5.subheading{font-weight:300}h1.subheading{font-size:30px}h2.subheading{font-size:24px}h3.subheading{font-size:18px}h4.subheading{font-size:14px}h5.subheading{font-size:12px}.lead{font-size:18px;font-weight:300;line-height:1.4em;margin-bottom:.75em}p,ul,ol,dl,dd,dt,blockquote,td,th{line-height:1.6em}ul,ol,ul ul,ol ol,ul ol,ol ul{margin:0 0 0 1.6em}ol ol li{list-style-type:lower-alpha}ol ol ol li{list-style-type:lower-roman}p,ul,ol,dl,blockquote,hr,pre,table,form,fieldset,figure,address{margin-bottom:1.6em}address{font-style:normal}dl dt{font-weight:500}dd{margin-left:1em}blockquote{font-size:16px;font-style:italic;position:relative;padding-left:1.6em;margin-left:0;border-left:2px solid #dbdada}blockquote p{margin-bottom:.5em}blockquote small,cite{color:#777;font-style:italic}small,blockquote cite{font-size:12px;line-height:1}del{text-decoration:line-through}abbr[title],dfn[title]{border-bottom:1px dotted #000;cursor:help}strong,b{font-weight:bold}em,i{font-style:italic}sub,sup{font-size:10px;line-height:0;position:relative}sup{top:-0.5em}sub{bottom:-0.25em}figcaption{font-size:12px;font-style:italic}ins,mark{background-color:#fddc43;color:#0f0f0f;text-decoration:none}pre,code,kbd,samp{font-size:90%;font-family:Menlo,Monaco,Consolas,"Courier New",monospace}pre{font-size:90%;line-height:1.45em;color:#333;background:#f6f6f6;padding:1.6em;overflow:auto}code{padding:3px 3px 1px 3px;display:inline-block;line-height:1;background:#f6f6f6;border:1px solid #ddd}pre code{font-size:100%;border:0;padding:0;background:0;line-height:1.6em}kbd{padding:2px 6px 1px 6px;line-height:1;display:inline-block;border-radius:.3em;box-shadow:0 1px 0 rgba(0,0,0,0.2),0 0 0 1px #fff inset;background-color:#fafafa;border:1px solid #ccc;color:#333;font-weight:normal;white-space:nowrap}button:active,button:focus{outline:0}input[type="text"],input[type="password"],input[type="email"],input[type="url"],input[type="phone"],input[type="tel"],input[type="number"],input[type="datetime"],input[type="date"],input[type="search"],input[type="datetime-local"],textarea,select[multiple="multiple"]{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;line-height:1;font-size:14px;border-radius:0;background:#fff;box-shadow:none;border:1px solid #bbbcc0;outline:0;padding:7px 5px;position:relative;z-index:2;-webkit-appearance:none}input[type="text"],input[type="password"],input[type="email"],input[type="url"],input[type="phone"],input[type="tel"],input[type="number"],input[type="datetime"],input[type="date"],input[type="search"],input[type="datetime-local"]{height:2.3em}input[type="range"]{position:relative;top:3px}select[multiple="multiple"],textarea{line-height:1.35em}fieldset{padding:1.6em;margin-bottom:1.6em;border:1px solid #dbdada}legend{font-weight:bold;padding:0 1em;margin-left:-1em}tfoot th,tfoot td{background-color:#f4f4f4}th,td{border-bottom:1px solid #eee;padding:8px 10px}table caption{text-transform:uppercase;padding:0 1em;color:#777;font-size:12px}.units-container:after,.units-row:after{content:"";display:table;clear:both}.units-container{padding-top:1px;margin-top:-1px}.units-row{margin-bottom:1.6em}.width-100,.unit-100{width:100%}.width-90,.unit-90{width:90%}.width-80,.unit-80{width:80%}.width-75,.unit-75{width:75%}.width-70,.unit-70{width:70%}.width-66,.unit-66{width:66.6%}.width-65,.unit-65{width:65%}.width-60,.unit-60{width:60%}.width-50,.unit-50{width:50%}.width-40,.unit-40{width:40%}.width-35,.unit-35{width:35%}.width-33,.unit-33{width:33.3%}.width-30,.unit-30{width:30%}.width-25,.unit-25{width:25%}.width-20,.unit-20{width:20%}.width-10,.unit-10{width:10%}input.width-100,input.unit-100{width:98.6%}textarea.width-100,textarea.unit-100{width:98.8%}select.width-100,select.unit-100{width:99.4%}.units-row .unit-90,.units-row .unit-80,.units-row .unit-75,.units-row .unit-70,.units-row .unit-66,.units-row .unit-65,.units-row .unit-60,.units-row .unit-50,.units-row .unit-40,.units-row .unit-35,.units-row .unit-33,.units-row .unit-30,.units-row .unit-25,.units-row .unit-20,.units-row .unit-10{float:left;margin-left:3%}.units-row .unit-90:first-child,.units-row .unit-80:first-child,.units-row .unit-75:first-child,.units-row .unit-70:first-child,.units-row .unit-66:first-child,.units-row .unit-65:first-child,.units-row .unit-60:first-child,.units-row .unit-50:first-child,.units-row .unit-40:first-child,.units-row .unit-35:first-child,.units-row .unit-33:first-child,.units-row .unit-30:first-child,.units-row .unit-25:first-child,.units-row .unit-20:first-child,.units-row .unit-10:first-child{margin-left:0}.units-row .unit-90{width:89.7%}.units-row .unit-80{width:79.4%}.units-row .unit-75{width:74.25%}.units-row .unit-70{width:69.1%}.units-row .unit-66{width:65.66666666666666%}.units-row .unit-65{width:65.66666666666666%}.units-row .unit-60{width:58.800000000000004%}.units-row .unit-50{width:48.5%}.units-row .unit-40{width:38.2%}.units-row .unit-35{width:31.333333333333332%}.units-row .unit-33{width:31.333333333333332%}.units-row .unit-30{width:27.9%}.units-row .unit-25{width:22.75%}.units-row .unit-20{width:17.6%}.units-row .unit-10{width:7.3%}.unit-push-90,.unit-push-80,.unit-push-75,.unit-push-70,.unit-push-66,.unit-push-65,.unit-push-60,.unit-push-50,.unit-push-40,.unit-push-35,.unit-push-33,.unit-push-30,.unit-push-25,.unit-push-20,.unit-push-10{position:relative}.unit-push-90{left:92.7%}.unit-push-80{left:82.4%}.unit-push-75{left:77.25%}.unit-push-70{left:72.1%}.unit-push-66{left:68.66666666666666%}.unit-push-65{left:68.66666666666666%}.unit-push-60{left:61.800000000000004%}.unit-push-50{left:51.5%}.unit-push-40{left:41.2%}.unit-push-35{left:34.33333333333333%}.unit-push-33{left:34.33333333333333%}.unit-push-30{left:30.9%}.unit-push-25{left:25.75%}.unit-push-20{left:20.6%}.unit-push-10{left:10.3%}.units-row .unit-push-right{float:right}.centered,.unit-centered{float:none!important;margin:0 auto!important}.unit-padding{padding:1.6em}.units-padding .unit-100,.units-padding .unit-90,.units-padding .unit-80,.units-padding .unit-75,.units-padding .unit-70,.units-padding .unit-66,.units-padding .unit-65,.units-padding .unit-60,.units-padding .unit-50,.units-padding .unit-40,.units-padding .unit-35,.units-padding .unit-33,.units-padding .unit-30,.units-padding .unit-25,.units-padding .unit-20,.units-padding .unit-10{padding:1.6em}.units-split .unit-90,.units-split .unit-80,.units-split .unit-75,.units-split .unit-70,.units-split .unit-66,.units-split .unit-65,.units-split .unit-60,.units-split .unit-50,.units-split .unit-40,.units-split .unit-35,.units-split .unit-33,.units-split .unit-30,.units-split .unit-25,.units-split .unit-20,.units-split .unit-10{margin-left:0}.units-split .unit-90{width:90%}.units-split .unit-80{width:80%}.units-split .unit-75{width:75%}.units-split .unit-70{width:70%}.units-split .unit-66{width:66.6%}.units-split .unit-65{width:65%}.units-split .unit-60{width:60%}.units-split .unit-50{width:50%}.units-split .unit-40{width:40%}.units-split .unit-35{width:35%}.units-split .unit-33{width:33.3%}.units-split .unit-30{width:30%}.units-split .unit-25{width:25%}.units-split .unit-20{width:20%}.units-split .unit-10{width:10%}@media only screen and (max-width:767px){.mobile-width-100{width:100%}.units-row .unit-90,.units-row .unit-80,.units-row .unit-75,.units-row .unit-70,.units-row .unit-66,.units-row .unit-65,.units-row .unit-60,.units-row .unit-50,.units-row .unit-40,.units-row .unit-35,.units-row .unit-33,.units-row .unit-30,.units-row .unit-25,.units-row .unit-20,.units-row .unit-10{width:100%;float:none;margin-left:0;margin-bottom:1.6em}.unit-push-90,.unit-push-80,.unit-push-75,.unit-push-70,.unit-push-66,.unit-push-65,.unit-push-60,.unit-push-50,.unit-push-40,.unit-push-35,.unit-push-33,.unit-push-30,.unit-push-25,.unit-push-20,.unit-push-10{left:0}.units-row .unit-push-right{float:none}.units-mobile-50 .unit-90,.units-mobile-50 .unit-80,.units-mobile-50 .unit-75,.units-mobile-50 .unit-70,.units-mobile-50 .unit-66,.units-mobile-50 .unit-65,.units-mobile-50 .unit-60,.units-mobile-50 .unit-40,.units-mobile-50 .unit-30,.units-mobile-50 .unit-35,.units-mobile-50 .unit-33,.units-mobile-50 .unit-25,.units-mobile-50 .unit-20,.units-mobile-50 .unit-10{float:left;margin-left:3%;width:48.5%}.units-mobile-50 .unit-90:first-child,.units-mobile-50 .unit-80:first-child,.units-mobile-50 .unit-75:first-child,.units-mobile-50 .unit-70:first-child,.units-mobile-50 .unit-66:first-child,.units-mobile-50 .unit-65:first-child,.units-mobile-50 .unit-60:first-child,.units-mobile-50 .unit-40:first-child,.units-mobile-50 .unit-35:first-child,.units-mobile-50 .unit-30:first-child,.units-mobile-50 .unit-33:first-child,.units-mobile-50 .unit-25:first-child,.units-mobile-50 .unit-20:first-child,.units-mobile-50 .unit-10:first-child{margin-left:0}}.blocks-2,.blocks-3,.blocks-4,.blocks-5,.blocks-6{padding-left:0;list-style:none;margin-left:-3%}.blocks-2:after,.blocks-3:after,.blocks-4:after,.blocks-5:after,.blocks-6:after{content:"";display:table;clear:both}.blocks-2 li,.blocks-3 li,.blocks-4 li,.blocks-5 li,.blocks-6 li{height:auto;float:left;margin-bottom:1.6em;margin-left:3%}.blocks-2>li{width:47%}.blocks-3>li{width:30.333333333333332%}.blocks-4>li{width:22%}.blocks-5>li{width:17%}.blocks-6>li{width:13.666666666666666%}.block-first{clear:both}@media only screen and (max-width:767px){.blocks-2,.blocks-3,.blocks-4,.blocks-5,.blocks-6{margin-left:0;margin-bottom:1.6em}.blocks-2>li,.blocks-3>li,.blocks-4>li,.blocks-5>li,.blocks-6>li{float:none;margin-left:0;width:100%}.blocks-mobile-50>li,.blocks-mobile-33>li{float:left;margin-left:3%}.blocks-mobile-33,.blocks-mobile-50{margin-left:-3%}.blocks-mobile-50>li{width:47%}.blocks-mobile-33>li{width:30.333333333333332%}}.navbar{margin-bottom:1.6em}.navbar:after{content:"";display:table;clear:both}.navbar-left{float:left}.navbar-right{float:right}.navbar ul{list-style:none;margin:0}.navbar ul:after{content:"";display:table;clear:both}.navbar ul li{float:left;margin-right:1.6em}.navbar-right ul li{margin-right:0;margin-left:1.6em}.navbar ul li a,.navbar ul li span{display:block;text-decoration:none}.navbar ul li a:hover{color:#d70a16}.navbar ul li.active a,.navbar ul li span{text-decoration:none;cursor:text;color:rgba(0,0,0,0.4)}.fullwidth ul{width:100%}.fullwidth li{float:none!important;margin:0;display:table-cell;width:1%;text-align:center}.fullwidth li a,.fullwidth li span{display:block}.nav{margin-bottom:1.6em}.nav ul{list-style:none;margin:0}.nav ul li ul{margin-left:2em;font-size:.95em}.nav ul li a,.nav ul li span{display:block;padding:5px 0}.nav ul li ul li a,.nav ul li ul li span{padding:4px 0}.nav ul li a{text-decoration:none}.nav ul li a:hover{color:#d70a16;text-decoration:underline}.nav ul li.active a,.nav ul li span{text-decoration:none;cursor:text;color:rgba(0,0,0,0.4)}.nav-stroked li{border-bottom:1px solid #eee}.nav-stroked li:last-child{border-bottom:0}.nav-stacked ul{border:1px solid #eee;border-bottom:0}.nav-stacked ul li{border-bottom:1px solid #eee}.nav-stacked ul li a,.nav-stacked ul li span{padding:5px 10px}.nav-stacked ul li a:hover{background-color:#f5f5f5}.nav-stats li{position:relative}.nav-stats li a,.nav-stats li span{padding-right:50px}.nav-stats sup{position:absolute;top:50%;right:0;color:rgba(0,0,0,0.4)}.nav-stats.nav-stacked sup{right:.5em}.breadcrumbs{margin-bottom:1.6em}.breadcrumbs:after{content:"";display:table;clear:both}.breadcrumbs ul{font-size:.9em;color:rgba(0,0,0,0.4);list-style:none;margin:0}.breadcrumbs ul:after{content:"";display:table;clear:both}.breadcrumbs ul li{float:left;margin-right:3px}.breadcrumbs li+li:before{content:" > ";color:#aaa;font-size:12px;margin:0 3px;position:relative;top:-1px}.breadcrumbs-sections li+li:before{content:" | ";top:0}.breadcrumbs-path li+li:before{content:" / ";top:0}.breadcrumbs ul li a{color:#0f0f0f;text-decoration:none}.breadcrumbs ul li.active a{text-decoration:none;cursor:text;color:rgba(0,0,0,0.4)}.breadcrumbs ul li a:hover{color:#0f0f0f;text-decoration:underline}.pagination{position:relative;left:-9px;margin-left:0;list-style:none}.pagination:after{content:"";display:table;clear:both}.pagination li{float:left;margin-right:2px}.pagination li a,.pagination li span{display:block;padding:7px 9px;line-height:1;border-radius:3px;color:#0f0f0f;text-decoration:none}.pagination span{border:1px solid #ddd}.pagination li a:focus,.pagination li a:hover{text-decoration:none;background-color:#0f0f0f;color:#fff}@media only screen and (max-width:767px){.navbar-left,.navbar-right,.navbar ul li,.navbar-right ul li,.subnav-right ul,.subnav ul li,.subnav-right ul li{float:none;text-align:center}.navbar ul li,.navbar-right ul li,.subnav ul li,.subnav-right ul li{margin-left:0;margin-right:0}.fullwidth ul{width:auto}.fullwidth li{display:block;width:auto}}table.table-bordered td,table.table-bordered th{border:1px solid #ddd}table.table-simple td,table.table-simple th{border:0;padding-left:0}table.table-flat td,table.table-flat th{border:0;padding:0}table.table-striped tbody tr:nth-child(odd) td{background-color:#f8f8f8}table.table-hovered tbody tr:hover td{background-color:#f4f4f4}.table-container{width:100%;overflow:auto;margin-bottom:1.6em}.table-container table{margin-bottom:0}.table-container::-webkit-scrollbar{-webkit-appearance:none;width:14px;height:14px}.table-container::-webkit-scrollbar-thumb{border-radius:8px;border:3px solid #fff;background-color:rgba(0,0,0,0.3)}.forms label{display:block;margin-bottom:1.6em}.forms fieldset{padding-bottom:.5em;border-radius:4px}.forms input[type="text"],.forms input[type="password"],.forms input[type="email"],.forms input[type="url"],.forms input[type="phone"],.forms input[type="tel"],.forms input[type="number"],.forms input[type="datetime"],.forms input[type="date"],.forms input[type="search"],.forms input[type="range"],.forms input[type="file"],.forms input[type="datetime-local"],.forms textarea,.forms select{display:block}.forms-inline input[type="text"],.forms-inline-list input[type="text"],.forms-inline input[type="password"],.forms-inline-list input[type="password"],.forms-inline input[type="email"],.forms-inline-list input[type="email"],.forms-inline input[type="url"],.forms-inline-list input[type="url"],.forms-inline input[type="phone"],.forms-inline-list input[type="phone"],.forms-inline input[type="tel"],.forms-inline-list input[type="tel"],.forms-inline input[type="number"],.forms-inline-list input[type="number"],.forms-inline input[type="datetime"],.forms-inline-list input[type="datetime"],.forms-inline input[type="date"],.forms-inline-list input[type="date"],.forms-inline input[type="search"],.forms-inline-list input[type="search"],.forms-inline input[type="range"],.forms-inline-list input[type="range"],.forms-inline input[type="file"],.forms-inline-list input[type="file"],.forms-inline input[type="datetime-local"],.forms-inline-list input[type="datetime-local"],.forms-inline textarea,.forms-inline-list textarea,.forms-inline select,.forms-inline-list select{display:inline-block}.forms-list,.forms-inline-list{margin:0;padding:0;margin-bottom:1.6em;list-style:none}.forms-list label,.forms-inline-list li,.forms-inline-list li label{display:inline-block;margin-bottom:0}.forms-inline-list li label{margin-right:1.6em}.forms-inline-list li{margin-bottom:3px}.forms-list li{margin-bottom:6px}.forms-desc{margin-top:4px;color:rgba(0,0,0,0.4);font-size:12px;line-height:1.4em}input[type="radio"],input[type="checkbox"]{position:relative;top:-1px}input[type="text"],input[type="password"],input[type="email"],input[type="url"],input[type="phone"],input[type="tel"],input[type="number"],input[type="datetime"],input[type="date"],input[type="search"],input[type="datetime-local"],textarea{-moz-transition:border ease .5s;transition:border ease .5s}input[type="range"]{position:relative;top:3px}textarea{line-height:1.4em}select{margin-bottom:0!important}.error,.success{font-weight:normal;font-size:12px}input.input-error,textarea.input-error,select.input-error,.input-error{border-color:#d70a16;box-shadow:0 0 0 2px rgba(215,10,22,0.3),0 1px 2px rgba(0,0,0,0.2) inset}input.input-success,textarea.input-success,select.input-success,.input-success{border-color:#159776;box-shadow:0 0 0 2px rgba(21,151,118,0.3),0 1px 2px rgba(0,0,0,0.2) inset}input.input-gray,textarea.input-gray,select.input-gray,.input-gray{border-color:#ccc;box-shadow:0 0 0 2px rgba(204,204,204,0.3),0 1px 2px rgba(0,0,0,0.2) inset}input:focus,textarea:focus{outline:0;border-color:#5ca9e4;box-shadow:0 0 0 2px rgba(70,161,231,0.3),0 1px 2px rgba(0,0,0,0.2) inset}input.input-search,input[type="search"]{padding-right:10px;padding-left:10px;margin-bottom:0;border-radius:15px}.input-groups{display:table!important}.input-groups input{width:100%}.input-groups input,.input-groups .input-append,.input-groups .input-prepend,.input-groups .btn-append{display:table-cell!important}.input-groups .btn-append,.input-groups .input-append,.input-groups .input-prepend{width:1%;vertical-align:middle}.input-groups .input-append,.input-groups .input-prepend{background-color:#eee;border:1px solid #ccc;margin:0;padding:0 7px;color:rgba(0,0,0,0.5);line-height:1;font-size:12px;white-space:nowrap}.input-groups .input-prepend{border-right:0}.input-groups .input-append{position:relative;z-index:1;border-left:none}.input-groups .btn-append .btn{display:block;height:auto;border-radius:0 4px 4px 0}@media only screen and (max-width:767px){.forms-list label{display:inline-block}}.btn{display:inline-block;vertical-align:top;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;font-weight:400;line-height:1;text-align:center;text-decoration:none;color:#333;margin:0;border:0;border-radius:3px;box-shadow:none;cursor:pointer;background:#eeeded;padding:9px 20px;height:2.3em}.btn:hover{color:rgba(0,0,0,0.5);background:#dbdada}input[type="submit"].btn{-webkit-appearance:none}.btn-small{font-size:11px;padding:7px 16px}.btn-outline{background:0;border:1px solid #ddd;padding:8px 19px}.btn-outline.btn-small{padding:6px 15px}.btn-blue,.btn-red,.btn-green,.btn-black{color:#fff}.btn-blue:hover,.btn-red:hover,.btn-green:hover,.btn-black:hover{color:rgba(255,255,255,0.7)}.btn-white{background:#fff}.btn-white:hover{background:#f2f2f2}.btn-white.btn-outline{background:0;border-color:rgba(255,255,255,0.85);color:rgba(255,255,255,0.85)}.btn-white.btn-outline:hover{color:rgba(0,0,0,0.9);background:#fff}.btn-blue{background:#3d58a8}.btn-blue:hover{background:#2f4483}.btn-red{background:#d70a16}.btn-red:hover{background:#a60811}.btn-green{background:#159776}.btn-green:hover{background:#0f6a53}.btn-yellow{background:#fddc43;color:#000}.btn-yellow:hover{background:#e7be02}.btn-black{background:#000}.btn-black:hover{background:#333}.btn-active,.btn[disabled],.btn-disabled{border:0;background:0;background-color:#dbdada;color:rgba(0,0,0,0.5)}.btn-active:hover,.btn[disabled]:hover,.btn-disabled:hover{color:rgba(0,0,0,0.5)}.btn-active{box-shadow:0 1px 3px rgba(0,0,0,0.4) inset}.btn[disabled],.btn-disabled{cursor:default;box-shadow:none}.btn-blue.btn-active,.btn-red.btn-active,.btn-green.btn-active,.btn-black.btn-active{box-shadow:0 1px 3px rgba(0,0,0,0.7) inset}.btn-blue.btn-active,.btn-red.btn-active,.btn-green.btn-active,.btn-black.btn-active,.btn-blue.btn-disabled,.btn-red.btn-disabled,.btn-green.btn-disabled,.btn-black.btn-disabled,.btn-blue[disabled],.btn-red[disabled],.btn-green[disabled],.btn-black[disabled]{color:rgba(255,255,255,0.6)}.btn-blue.btn-active:hover,.btn-red.btn-active:hover,.btn-green.btn-active:hover,.btn-black.btn-active:hover,.btn-blue.btn-disabled:hover,.btn-red.btn-disabled:hover,.btn-green.btn-disabled:hover,.btn-black.btn-disabled:hover,.btn-blue[disabled]:hover,.btn-red[disabled]:hover,.btn-green[disabled]:hover,.btn-black[disabled]:hover{color:rgba(255,255,255,0.6)}.btn-white.btn-active,.btn-white.btn-disabled,.btn-white[disabled]{background:#f2f2f2}.btn-blue.btn-active,.btn-blue.btn-disabled,.btn-blue[disabled]{background:#2f4483}.btn-red.btn-active,.btn-red.btn-disabled,.btn-red[disabled]{background:#a60811}.btn-green.btn-active,.btn-green.btn-disabled,.btn-green[disabled]{background:#0f6a53}.btn-yellow.btn-active,.btn-yellow.btn-disabled,.btn-yellow[disabled]{background:#e7be02}.btn-black.btn-active,.btn-black.btn-disabled,.btn-black[disabled]{background:#4d4d4d}.group:after{content:"";display:table;clear:both}.small{font-size:12px}.nowrap,.nowrap td{white-space:nowrap}.req,.required{font-weight:normal;color:#d70a16}.color-gray{color:#777}.color-gray-light{color:#999}.color-black{color:#000}.color-red,.error{color:#d70a16}.color-green,.success{color:#159776}.text-centered{text-align:center}.text-right{text-align:right}.last{margin-right:0!important}.pause{margin-bottom:.8em!important}.end{margin-bottom:0!important}.normal{font-weight:normal}.bold{font-weight:bold}.italic{font-style:italic}.left{float:left}.right{float:right}.video-wrapper{height:0;padding-bottom:56.25%;position:relative;margin-bottom:1.6em}.video-wrapper iframe,.video-wrapper object,.video-wrapper embed{position:absolute;top:0;left:0;width:100%;height:100%}@media only screen and (max-width:767px){.left,.right{float:none}}.str{color:#d14}.kwd{color:#333}.com{color:#998}.typ{color:#458}.lit{color:#458}.pun{color:#888}.opn{color:#333}.clo{color:#333}.tag{color:#367ac3}.atn{color:#51a7c9}.atv{color:#709c1a}.dec{color:#666}.var{color:teal}.fun{color:#900} \ No newline at end of file diff --git a/src/html/ReqMgr/style/kube301-less/index.html b/src/html/ReqMgr/style/kube301-less/index.html new file mode 100644 index 0000000000..ac44e9a651 --- /dev/null +++ b/src/html/ReqMgr/style/kube301-less/index.html @@ -0,0 +1,14 @@ + + + + Kube + + + + + + + +

Hello, world!

+ + \ No newline at end of file diff --git a/src/html/ReqMgr/style/kube301-less/less/blocks.less b/src/html/ReqMgr/style/kube301-less/less/blocks.less new file mode 100644 index 0000000000..4fed59da06 --- /dev/null +++ b/src/html/ReqMgr/style/kube301-less/less/blocks.less @@ -0,0 +1,71 @@ +/* =Blocks +-----------------------------------------------------------------------------*/ +.blocks-2, +.blocks-3, +.blocks-4, +.blocks-5, +.blocks-6 { + padding-left: 0; + list-style: none; + margin-left: -@gridGutterWidth / (@gridWidth / 100) * 1%; + .clearfix; + & li { + height: auto; + float: left; + margin-bottom: @baseLine / @em; + margin-left: @gridGutterWidth / (@gridWidth / 100) * 1%; + } +} + +.blocks-2 > li { .block(2); } +.blocks-3 > li { .block(3); } +.blocks-4 > li { .block(4); } +.blocks-5 > li { .block(5); } +.blocks-6 > li { .block(6); } + +.block-first { + clear: both; +} + +/* =Responsive +-----------------------------------------------------------------------------*/ +@media only screen and (max-width: 767px) { + + .blocks-2, + .blocks-3, + .blocks-4, + .blocks-5, + .blocks-6 { + margin-left: 0; + margin-bottom: @baseLine/@em; + } + + .blocks-2 > li, + .blocks-3 > li, + .blocks-4 > li, + .blocks-5 > li, + .blocks-6 > li { + float: none; + margin-left: 0; + width: 100%; + } + + .blocks-mobile-50 > li, + .blocks-mobile-33 > li { + float: left; + margin-left: @gridGutterWidth/(@gridWidth/100)*1%; + } + .blocks-mobile-33, + .blocks-mobile-50 { + margin-left: -@gridGutterWidth/(@gridWidth/100)*1%; + } + + .blocks-mobile-50 > li { + .block(2); + } + .blocks-mobile-33 > li { + .block(3); + } + + +} \ No newline at end of file diff --git a/src/html/ReqMgr/style/kube301-less/less/buttons.less b/src/html/ReqMgr/style/kube301-less/less/buttons.less new file mode 100644 index 0000000000..273752724a --- /dev/null +++ b/src/html/ReqMgr/style/kube301-less/less/buttons.less @@ -0,0 +1,170 @@ +/* =Buttons +-----------------------------------------------------------------------------*/ +.btn { + display: inline-block; + vertical-align: top; + + font-family: @controlsFontFamily; + font-size: @baseFontSize + 0px; + font-weight: 400; + + line-height: 1; + + text-align: center; + text-decoration: none; + color: @colorBody; + + margin: 0; + border: none; + border-radius: 3px; + box-shadow: none; + + cursor: pointer; + + background: #eeeded; + padding: 9px 20px; + height: 2.3em; + + &:hover { + color: rgba(0, 0, 0, .5); + background: #dbdada; + } +} +input[type="submit"].btn { + -webkit-appearance: none; +} +.btn-small { + font-size: @buttonsSmallFontSize; + padding: 7px 16px; +} +.btn-outline { + background: none; + border: 1px solid #ddd; + padding: 8px 19px; + &.btn-small { + padding: 6px 15px; + } +} + + +.btn-blue, +.btn-red, +.btn-green, +.btn-black { + color: #fff; + &:hover { + color: rgba(255, 255, 255, .7); + } +} +.btn-white { + background: #fff; + &:hover { background: darken(#fff, 5%); } +} +.btn-white.btn-outline { + background: none; + border-color: rgba(255, 255, 255, .85); + color: rgba(255, 255, 255, .85); + &:hover { + color: rgba(0, 0, 0, .9); + background: #fff; + } +} +.btn-blue { + background: @colorBlue; + &:hover { background: darken(@colorBlue, 10%); } +} +.btn-red { + background: @colorRed; + &:hover { background: darken(@colorRed, 10%); } +} +.btn-green { + background: @colorGreen; + &:hover { background: darken(@colorGreen, 10%); } +} +.btn-yellow { + background: @colorYellow; + color: #000; + &:hover { + background: darken(@colorYellow, 17%); + } +} +.btn-black { + background: #000; + &:hover { background: lighten(#000, 20%); } +} + +.btn-active, +.btn[disabled], +.btn-disabled { + border: none; + background: none; + background-color: #dbdada; + color: rgba(0, 0, 0, .5); + &:hover { + color: rgba(0, 0, 0, .5); + } +} +.btn-active { + box-shadow: 0 1px 3px rgba(0, 0, 0, .4) inset; +} +.btn[disabled], +.btn-disabled { + cursor: default; + box-shadow: none; +} + +.btn-blue.btn-active, +.btn-red.btn-active, +.btn-green.btn-active, +.btn-black.btn-active { + box-shadow: 0 1px 3px rgba(0, 0, 0, .7) inset; +} + +.btn-blue.btn-active, +.btn-red.btn-active, +.btn-green.btn-active, +.btn-black.btn-active, +.btn-blue.btn-disabled, +.btn-red.btn-disabled, +.btn-green.btn-disabled, +.btn-black.btn-disabled, +.btn-blue[disabled], +.btn-red[disabled], +.btn-green[disabled], +.btn-black[disabled] { + color: rgba(255, 255, 255, .6); + &:hover { + color: rgba(255, 255, 255, .6); + } +} + +.btn-white.btn-active, +.btn-white.btn-disabled, +.btn-white[disabled] { + background: darken(#fff, 5%); +} +.btn-blue.btn-active, +.btn-blue.btn-disabled, +.btn-blue[disabled] { + background: darken(@colorBlue, 10%); +} +.btn-red.btn-active, +.btn-red.btn-disabled, +.btn-red[disabled] { + background: darken(@colorRed, 10%); +} +.btn-green.btn-active, +.btn-green.btn-disabled, +.btn-green[disabled] { + background: darken(@colorGreen, 10%); +} +.btn-yellow.btn-active, +.btn-yellow.btn-disabled, +.btn-yellow[disabled] { + background: darken(@colorYellow, 17%); +} +.btn-black.btn-active, +.btn-black.btn-disabled, +.btn-black[disabled] { + background: lighten(#000, 30%); +} \ No newline at end of file diff --git a/src/html/ReqMgr/style/kube301-less/less/forms.less b/src/html/ReqMgr/style/kube301-less/less/forms.less new file mode 100644 index 0000000000..71f2a3e0e7 --- /dev/null +++ b/src/html/ReqMgr/style/kube301-less/less/forms.less @@ -0,0 +1,209 @@ +/* =Forms +-----------------------------------------------------------------------------*/ +.forms { + & label { + display: block; + margin-bottom: @fieldsBottom; + } + + & fieldset { + padding-bottom: .5em; + border-radius: 4px; + } + + & input[type="text"], + & input[type="password"], + & input[type="email"], + & input[type="url"], + & input[type="phone"], + & input[type="tel"], + & input[type="number"], + & input[type="datetime"], + & input[type="date"], + & input[type="search"], + & input[type="range"], + & input[type="file"], + & input[type="datetime-local"], + & textarea, + & select { + display: block; + } +} +.forms-inline, +.forms-inline-list { + & input[type="text"], + & input[type="password"], + & input[type="email"], + & input[type="url"], + & input[type="phone"], + & input[type="tel"], + & input[type="number"], + & input[type="datetime"], + & input[type="date"], + & input[type="search"], + & input[type="range"], + & input[type="file"], + & input[type="datetime-local"], + & textarea, + & select { + display: inline-block; + } +} + +.forms-list, +.forms-inline-list { + margin: 0; + padding: 0; + margin-bottom: @fieldsBottom; + list-style: none; +} +.forms-list label, +.forms-inline-list li, +.forms-inline-list li label { + display: inline-block; + margin-bottom: 0; +} +.forms-inline-list li label { + margin-right: @fieldsBottom; +} +.forms-inline-list li { + margin-bottom: 3px; +} +.forms-list li { + margin-bottom: 6px; +} +.forms-desc { + margin-top: 4px; + color: rgba(0, 0, 0, .4); + font-size: @smallFontSize; + line-height: 1.4em; +} + + +input[type="radio"], +input[type="checkbox"] { + position: relative; + top: -1px; +} +input[type="text"], +input[type="password"], +input[type="email"], +input[type="url"], +input[type="phone"], +input[type="tel"], +input[type="number"], +input[type="datetime"], +input[type="date"], +input[type="search"], +input[type="datetime-local"], +textarea { + .transition(border ease .5s); +} +input[type="range"] { + position: relative; + top: 3px; +} +textarea { + line-height: 1.4em; +} +select { + margin-bottom: 0 !important; +} + +/* States */ +.error, +.success { + font-weight: normal; + font-size: @smallFontSize; +} +input.input-error, +textarea.input-error, +select.input-error, +.input-error { + border-color: @colorRed; + box-shadow: 0 0 0 2px rgba(red(@colorRed), green(@colorRed), blue(@colorRed), .3),0 1px 2px rgba(0, 0, 0, .2) inset; +} +input.input-success, +textarea.input-success, +select.input-success, +.input-success { + border-color: @colorGreen; + box-shadow: 0 0 0 2px rgba(red(@colorGreen), green(@colorGreen), blue(@colorGreen), .3),0 1px 2px rgba(0, 0, 0, .2) inset; + +} +input.input-gray, +textarea.input-gray, +select.input-gray, +.input-gray { + border-color: #ccc; + box-shadow: 0 0 0 2px rgba(204, 204, 204, .3),0 1px 2px rgba(0, 0, 0, .2) inset; +} + +input:focus, +textarea:focus { + outline: none; + border-color: #5ca9e4; + box-shadow: 0 0 0 2px rgba(70, 161, 231, .3), 0 1px 2px rgba(0, 0, 0, .2) inset; +} + +input.input-search, +input[type="search"] { + padding-right: 10px; + padding-left: 10px; + margin-bottom: 0; + border-radius: 15px; +} + +/* Append & Prepend */ +.input-groups { + display: table !important; +} +.input-groups input { + width: 100%; +} +.input-groups input, +.input-groups .input-append, +.input-groups .input-prepend, +.input-groups .btn-append { + display: table-cell !important; +} +.input-groups .btn-append, +.input-groups .input-append, +.input-groups .input-prepend { + width: 1%; + vertical-align: middle; +} +.input-groups .input-append, +.input-groups .input-prepend { + background-color: #eee; + border: 1px solid #ccc; + margin: 0; + padding: 0 7px; + color: rgba(0, 0, 0, .5); + line-height: 1; + font-size: @smallFontSize; + white-space: nowrap; +} +.input-groups .input-prepend { + border-right: none; +} +.input-groups .input-append { + position: relative; + z-index: 1; + border-left: none; +} +.input-groups .btn-append .btn { + display: block; + height: auto; + border-radius: 0 4px 4px 0; +} + +/* =Mobile (Portrait) +-----------------------------------------------------------------------------*/ +@media only screen and (max-width: 767px) { + + .forms-list label { + display: inline-block; + } + +} \ No newline at end of file diff --git a/src/html/ReqMgr/style/kube301-less/less/grid.less b/src/html/ReqMgr/style/kube301-less/less/grid.less new file mode 100644 index 0000000000..3886792e0f --- /dev/null +++ b/src/html/ReqMgr/style/kube301-less/less/grid.less @@ -0,0 +1,262 @@ +/* =Grid +-----------------------------------------------------------------------------*/ +.units-container, +.units-row { + .clearfix; +} +.units-container { /* it's the trick to not collapse padding-top in the child element */ + padding-top: 1px; + margin-top: -1px; +} +.units-row { + margin-bottom: @baseLine / @em; +} + +.width-100, .unit-100 { width: 100%; } +.width-90, .unit-90 { width: 90%; } +.width-80, .unit-80 { width: 80%; } +.width-75, .unit-75 { width: 75%; } +.width-70, .unit-70 { width: 70%; } +.width-66, .unit-66 { width: 66.6%; } +.width-65, .unit-65 { width: 65%; } +.width-60, .unit-60 { width: 60%; } +.width-50, .unit-50 { width: 50%; } +.width-40, .unit-40 { width: 40%; } +.width-35, .unit-35 { width: 35%; } +.width-33, .unit-33 { width: 33.3%; } +.width-30, .unit-30 { width: 30%; } +.width-25, .unit-25 { width: 25%; } +.width-20, .unit-20 { width: 20%; } +.width-10, .unit-10 { width: 10%; } + +input.width-100, +input.unit-100 { width: 98.6%; } +textarea.width-100, +textarea.unit-100 { width: 98.8%; } +select.width-100, +select.unit-100 { width: 99.4%; } + +.units-row .unit-90, +.units-row .unit-80, +.units-row .unit-75, +.units-row .unit-70, +.units-row .unit-66, +.units-row .unit-65, +.units-row .unit-60, +.units-row .unit-50, +.units-row .unit-40, +.units-row .unit-35, +.units-row .unit-33, +.units-row .unit-30, +.units-row .unit-25, +.units-row .unit-20, +.units-row .unit-10 { + float: left; + margin-left: @gridGutterWidth/(@gridWidth/100)*1%; + &:first-child { + margin-left: 0; + } +} +.units-row .unit-90 { .columns(9, 10); } +.units-row .unit-80 { .columns(4, 5); } +.units-row .unit-75 { .columns(3, 4); } +.units-row .unit-70 { .columns(7, 10); } +.units-row .unit-66 { .columns(2, 3); } +.units-row .unit-65 { .columns(2, 3); } +.units-row .unit-60 { .columns(3, 5); } +.units-row .unit-50 { .column(2); } +.units-row .unit-40 { .columns(2, 5); } +.units-row .unit-35 { .column(3); } +.units-row .unit-33 { .column(3); } +.units-row .unit-30 { .columns(3, 10); } +.units-row .unit-25 { .column(4); } +.units-row .unit-20 { .column(5); } +.units-row .unit-10 { .columns(1, 10); } + +.unit-push-90, +.unit-push-80, +.unit-push-75, +.unit-push-70, +.unit-push-66, +.unit-push-65, +.unit-push-60, +.unit-push-50, +.unit-push-40, +.unit-push-35, +.unit-push-33, +.unit-push-30, +.unit-push-25, +.unit-push-20, +.unit-push-10 { + position: relative; +} +.unit-push-90 { .columns-push(9, 10); } +.unit-push-80 { .columns-push(4, 5); } +.unit-push-75 { .columns-push(3, 4); } +.unit-push-70 { .columns-push(7, 10); } +.unit-push-66 { .columns-push(2, 3); } +.unit-push-65 { .columns-push(2, 3); } +.unit-push-60 { .columns-push(3, 5); } +.unit-push-50 { .column-push(2); } +.unit-push-40 { .columns-push(2, 5); } +.unit-push-35 { .column-push(3); } +.unit-push-33 { .column-push(3); } +.unit-push-30 { .columns-push(3, 10); } +.unit-push-25 { .column-push(4); } +.unit-push-20 { .column-push(5); } +.unit-push-10 { .columns-push(1, 10); } + +.units-row .unit-push-right { + float: right; +} + +.centered, +.unit-centered { + float: none !important; + margin: 0 auto !important; +} + +.unit-padding { + padding: @baseLine / @em; +} +.units-padding .unit-100, +.units-padding .unit-90, +.units-padding .unit-80, +.units-padding .unit-75, +.units-padding .unit-70, +.units-padding .unit-66, +.units-padding .unit-65, +.units-padding .unit-60, +.units-padding .unit-50, +.units-padding .unit-40, +.units-padding .unit-35, +.units-padding .unit-33, +.units-padding .unit-30, +.units-padding .unit-25, +.units-padding .unit-20, +.units-padding .unit-10 { + padding: @baseLine / @em; +} + +.units-split .unit-90, +.units-split .unit-80, +.units-split .unit-75, +.units-split .unit-70, +.units-split .unit-66, +.units-split .unit-65, +.units-split .unit-60, +.units-split .unit-50, +.units-split .unit-40, +.units-split .unit-35, +.units-split .unit-33, +.units-split .unit-30, +.units-split .unit-25, +.units-split .unit-20, +.units-split .unit-10 { + margin-left: 0; +} +.units-split .unit-90 { width: 90%; } +.units-split .unit-80 { width: 80%; } +.units-split .unit-75 { width: 75%; } +.units-split .unit-70 { width: 70%; } +.units-split .unit-66 { width: 66.6%; } +.units-split .unit-65 { width: 65%; } +.units-split .unit-60 { width: 60%; } +.units-split .unit-50 { width: 50%; } +.units-split .unit-40 { width: 40%; } +.units-split .unit-35 { width: 35%; } +.units-split .unit-33 { width: 33.3%; } +.units-split .unit-30 { width: 30%; } +.units-split .unit-25 { width: 25%; } +.units-split .unit-20 { width: 20%; } +.units-split .unit-10 { width: 10%; } + + +/* =Responsive +-----------------------------------------------------------------------------*/ +@media only screen and (max-width: 767px) { + + .mobile-width-100 { + width: 100%; + } + + .units-row .unit-90, + .units-row .unit-80, + .units-row .unit-75, + .units-row .unit-70, + .units-row .unit-66, + .units-row .unit-65, + .units-row .unit-60, + .units-row .unit-50, + .units-row .unit-40, + .units-row .unit-35, + .units-row .unit-33, + .units-row .unit-30, + .units-row .unit-25, + .units-row .unit-20, + .units-row .unit-10 { + width: 100%; + float: none; + margin-left: 0; + margin-bottom: @baseLine/@em; + } + + .unit-push-90, + .unit-push-80, + .unit-push-75, + .unit-push-70, + .unit-push-66, + .unit-push-65, + .unit-push-60, + .unit-push-50, + .unit-push-40, + .unit-push-35, + .unit-push-33, + .unit-push-30, + .unit-push-25, + .unit-push-20, + .unit-push-10 { + left: 0; + } + + .units-row .unit-push-right { + float: none; + } + + .units-mobile-50 .unit-90, + .units-mobile-50 .unit-80, + .units-mobile-50 .unit-75, + .units-mobile-50 .unit-70, + .units-mobile-50 .unit-66, + .units-mobile-50 .unit-65, + .units-mobile-50 .unit-60, + .units-mobile-50 .unit-40, + .units-mobile-50 .unit-30, + .units-mobile-50 .unit-35, + .units-mobile-50 .unit-33, + .units-mobile-50 .unit-25, + .units-mobile-50 .unit-20, + .units-mobile-50 .unit-10 { + float: left; + margin-left: @gridGutterWidth/(@gridWidth/100)*1%; + .column(2); + } + .units-mobile-50 .unit-90:first-child, + .units-mobile-50 .unit-80:first-child, + .units-mobile-50 .unit-75:first-child, + .units-mobile-50 .unit-70:first-child, + .units-mobile-50 .unit-66:first-child, + .units-mobile-50 .unit-65:first-child, + .units-mobile-50 .unit-60:first-child, + .units-mobile-50 .unit-40:first-child, + .units-mobile-50 .unit-35:first-child, + .units-mobile-50 .unit-30:first-child, + .units-mobile-50 .unit-33:first-child, + .units-mobile-50 .unit-25:first-child, + .units-mobile-50 .unit-20:first-child, + .units-mobile-50 .unit-10:first-child { + margin-left: 0; + } + + +} diff --git a/src/html/ReqMgr/style/kube301-less/less/helpers.less b/src/html/ReqMgr/style/kube301-less/less/helpers.less new file mode 100644 index 0000000000..41e3de3c00 --- /dev/null +++ b/src/html/ReqMgr/style/kube301-less/less/helpers.less @@ -0,0 +1,92 @@ +/* =Helpers +-----------------------------------------------------------------------------*/ +.group { + .clearfix; +} +.small { + font-size: @smallFontSize; +} +.nowrap, +.nowrap td { + white-space: nowrap; +} +.req, +.required { + font-weight: normal; + color: @colorRed; +} +.color-gray { + color: #777; +} +.color-gray-light { + color: #999; +} +.color-black { + color: #000; +} +.color-red, +.error { + color: @colorRed; +} +.color-green, +.success { + color: @colorGreen; +} +.text-centered { + text-align: center; +} +.text-right { + text-align: right; +} +.last { + margin-right: 0 !important; +} +.pause { + margin-bottom: @baseLineHeight/2 + 0em !important; +} +.end { + margin-bottom: 0 !important; +} +.normal { + font-weight: normal; +} +.bold { + font-weight: bold; +} +.italic { + font-style: italic; +} +.left { + float: left; +} +.right { + float: right; +} + + +/* Responsive embedded objects */ +.video-wrapper { + height: 0; + padding-bottom: 56.25%; // ratio 16:9 + position: relative; + margin-bottom: @baseLineInEms; + & iframe, + & object, + & embed { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + } +} + + +/* =Responsive +-----------------------------------------------------------------------------*/ +@media only screen and (max-width: 767px) { + .left, + .right { + float: none; + } +} diff --git a/src/html/ReqMgr/style/kube301-less/less/kube.less b/src/html/ReqMgr/style/kube301-less/less/kube.less new file mode 100644 index 0000000000..caf140752a --- /dev/null +++ b/src/html/ReqMgr/style/kube301-less/less/kube.less @@ -0,0 +1,20 @@ +@import "mixins.less"; +@import "variables.less"; + +@import "normalize.less"; +@import "typography.less"; + +@import "grid.less"; +@import "blocks.less"; + +@import "navigation.less"; + +@import "tables.less"; +@import "forms.less"; + +@import "buttons.less"; + +@import "helpers.less"; +@import "prettyprint.less"; + + diff --git a/src/html/ReqMgr/style/kube301-less/less/mixins.less b/src/html/ReqMgr/style/kube301-less/less/mixins.less new file mode 100644 index 0000000000..ad54675c38 --- /dev/null +++ b/src/html/ReqMgr/style/kube301-less/less/mixins.less @@ -0,0 +1,185 @@ +.clearfix() { + &:after { + content: ""; + display: table; + clear: both; + } + +} +.btn-color(@color, @darken: 10%) { + background: @color; + &:hover { + background: darken(@color, @darken); + } +} +.opacity(@opacity: 100) { + filter: e(%("alpha(opacity=%d)", @opacity)); + -moz-opacity: @opacity / 100; + opacity: @opacity / 100; +} +.selection(@r, @g, @b) { + ::selection { background: rgb(@r, @g, @b); } + ::-moz-selection { background: rgb(@r, @g, @b); } + img::selection { background: transparent; } + img::-moz-selection { background: transparent; } +} +.box-sizing(@box-model) { + -webkit-box-sizing: @box-model; + -moz-box-sizing: @box-model; + box-sizing: @box-model; +} +.vertical-align { + position: relative; + top: 50%; + -webkit-transform: translateY(-50%); + -ms-transform: translateY(-50%); + transform: translateY(-50%); +} +.gradient(@start: #555, @end: #333) { + background-color: mix(@start, @end, 60%); + background-image: -moz-linear-gradient(top, @start, @end); // FF 3.6+ + background-image: -ms-linear-gradient(top, @start, @end); // IE10 + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(@start), to(@end)); // Safari 4+, Chrome 2+ + background-image: -webkit-linear-gradient(top, @start, @end); // Safari 5.1+, Chrome 10+ + background-image: -o-linear-gradient(top, @start, @end); // Opera 11.10 + background-image: linear-gradient(top, @start, @end); // The standard + filter: e(%("progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)",@start,@end)); // IE9 and down +} +.border-top-radius(@radius) { + border-top-right-radius: @radius; + border-top-left-radius: @radius; +} +.border-right-radius(@radius) { + border-bottom-right-radius: @radius; + border-top-right-radius: @radius; +} +.border-bottom-radius(@radius) { + border-bottom-right-radius: @radius; + border-bottom-left-radius: @radius; +} +.border-left-radius(@radius) { + border-bottom-left-radius: @radius; + border-top-left-radius: @radius; +} +.bordered(@top-color: #eee, @right-color: #eee, @bottom-color: #eee, @left-color: #eee) { + border-top: 1px solid @top-color; + border-right: 1px solid @right-color; + border-bottom: 1px solid @bottom-color; + border-left: 1px solid @left-color; +} +.retina-image(@path, @w: auto, @h: auto) { + background-image: url(@path); + @at2x_path: ~'@{path}.replace(/\.\w+$/, function(match) { return "@2x" + match; })'; + + @media + only screen and (-webkit-min-device-pixel-ratio: 2), + only screen and ( min--moz-device-pixel-ratio: 2), + only screen and ( -o-min-device-pixel-ratio: 2/1), + only screen and ( min-device-pixel-ratio: 2), + only screen and ( min-resolution: 192dpi), + only screen and ( min-resolution: 2dppx) { + background-image: url(@at2x_path); + background-size: @w @h; + } +} +.transition(@transition: all linear .2s) { + -moz-transition: @transition; + transition: @transition; +} +.blur(@radius) { + -webkit-filter: blur(@radius); + -moz-filter: blur(@radius); + -ms-filter: blur(@radius); + filter: blur(@radius); +} +.arrow-top(@color: #fff, @size: 5px, @left: 5px) { + &:after { + bottom: 100%; + border: solid transparent; + content: " "; + height: 0; + width: 0; + position: absolute; + pointer-events: none; + border-bottom-color: @color; + border-width: @size; + left: @left; + } +} +.arrow-bottom(@color: #fff, @size: 5px, @left: 5px) { + &:after { + top: 100%; + border: solid transparent; + content: " "; + height: 0; + width: 0; + position: absolute; + pointer-events: none; + border-top-color: @color; + border-width: @size; + left: @left; + } +} +.arrow-left(@color: #fff, @size: 5px, @top: 5px) { + &:after { + right: 100%; + border: solid transparent; + content: " "; + height: 0; + width: 0; + position: absolute; + pointer-events: none; + border-right-color: @color; + border-width: @size; + top: @top; + } +} +.arrow-right(@color: #fff, @size: 5px, @top: 5px) { + &:after { + left: 100%; + border: solid transparent; + content: " "; + height: 0; + width: 0; + position: absolute; + pointer-events: none; + border-left-color: @color; + border-width: @size; + top: @top; + } +} +.close(@r: 0, @g: 0, @b: 0) { + padding: 4px 6px; + line-height: 1; + font-size: 18px; + cursor: pointer; + color: rgba(@r, @g, @b, 1); + text-decoration: none; + .opacity(50); + &:before { + content: '\00D7'; + } + &:hover { + .opacity(100); + } +} +// Grid +.block(@num) { + width: ((@gridWidth - (@gridGutterWidth * @num)) / @num / (@gridWidth / 100)) * 1%; +} + +.column(@num) { + width: ((@gridWidth - (@gridGutterWidth * (@num - 1))) / @num / (@gridWidth / 100)) * 1%; +} + +.columns(@factor, @num) { + width: (((@gridWidth - (@gridGutterWidth * (@num - 1))) / @num / (@gridWidth / 100)) * @factor) + ((@gridGutterWidth / (@gridWidth / 100)) * (@factor - 1)) * 1%; +} + +.column-push(@num) { + left: (((@gridWidth - (@gridGutterWidth * (@num - 1))) / @num / (@gridWidth / 100)) + @gridGutterWidth/(@gridWidth/100)) * 1%; +} + +.columns-push(@factor, @num) { + left: ((((@gridWidth - (@gridGutterWidth * (@num - 1))) / @num / (@gridWidth / 100)) * @factor) + ((@gridGutterWidth / (@gridWidth / 100)) * (@factor - 1)) + @gridGutterWidth/(@gridWidth/100)) * 1%; +} diff --git a/src/html/ReqMgr/style/kube301-less/less/navigation.less b/src/html/ReqMgr/style/kube301-less/less/navigation.less new file mode 100644 index 0000000000..e127786b5c --- /dev/null +++ b/src/html/ReqMgr/style/kube301-less/less/navigation.less @@ -0,0 +1,244 @@ +/* =Navs +-----------------------------------------------------------------------------*/ + +/* NavBar */ +.navbar { + margin-bottom: @baseLineInEms; + .clearfix; +} +.navbar-left { + float: left; +} +.navbar-right { + float: right; +} +.navbar ul { + list-style: none; + margin: 0; + .clearfix; +} +.navbar ul li { + float: left; + margin-right: @baseLineInEms; +} +.navbar-right ul li { + margin-right: 0; + margin-left: @baseLineInEms; +} +.navbar ul li a, +.navbar ul li span { + display: block; + text-decoration: none; +} +.navbar ul li a:hover { + color: @colorLinkHover; +} +.navbar ul li.active a, +.navbar ul li span { + text-decoration: none; + cursor: text; + color: rgba(0, 0, 0, .4); +} + + +/* Fullwidth */ +.fullwidth ul { + width: 100%; +} +.fullwidth li { + float: none !important; + margin: 0; + display: table-cell; + width: 1%; + text-align: center; +} +.fullwidth li a, +.fullwidth li span { + display: block; +} + + +/* Vertical */ +.nav { + margin-bottom: @baseLineInEms; +} +.nav ul { + list-style: none; + margin: 0; +} +.nav ul li ul { + margin-left: 2em; + font-size: .95em; +} +.nav ul li a, +.nav ul li span { + display: block; + padding: 5px 0; +} +.nav ul li ul li a, +.nav ul li ul li span { + padding: 4px 0; +} +.nav ul li a { + text-decoration: none; +} +.nav ul li a:hover { + color: @colorLinkHover; + text-decoration: underline; +} +.nav ul li.active a, +.nav ul li span { + text-decoration: none; + cursor: text; + color: rgba(0, 0, 0, .4); +} + +/* Stroked */ +.nav-stroked li { + border-bottom: 1px solid #eee; +} +.nav-stroked li:last-child { + border-bottom: none; +} + +/* Stacked */ +.nav-stacked ul { + border: 1px solid #eee; + border-bottom: 0; +} +.nav-stacked ul li { + border-bottom: 1px solid #eee; +} +.nav-stacked ul li a, +.nav-stacked ul li span { + padding: 5px 10px; +} +.nav-stacked ul li a:hover { + background-color: #f5f5f5; +} + +/* Stats */ +.nav-stats li { + position: relative; +} +.nav-stats li a, +.nav-stats li span { + padding-right: 50px; +} +.nav-stats sup { + position: absolute; + top: 50%; + right: 0; + color: rgba(0, 0, 0, .4); +} +.nav-stats.nav-stacked sup { + right: .5em; +} + + +/* Breadcrumbs */ +.breadcrumbs { + margin-bottom: @baseLineInEms; + .clearfix; +} +.breadcrumbs ul { + font-size: .9em; + color: rgba(0, 0, 0, .4); + list-style: none; + margin: 0; + .clearfix; +} +.breadcrumbs ul li { + float: left; + margin-right: 3px; +} +.breadcrumbs li + li:before { + content: " > "; + color: #aaa; + font-size: 12px; + margin: 0 3px; + position: relative; + top: -1px; +} +.breadcrumbs-sections li + li:before { + content: " | "; + top: 0; +} +.breadcrumbs-path li + li:before { + content: " / "; + top: 0; +} +.breadcrumbs ul li a { + color: @colorBlack; + text-decoration: none; +} +.breadcrumbs ul li.active a { + text-decoration: none; + cursor: text; + color: rgba(0, 0, 0, .4); +} +.breadcrumbs ul li a:hover { + color: @colorBlack; + text-decoration: underline; +} + +/* =Pagination +-----------------------------------------------------------------------------*/ +.pagination { + position: relative; + left: -9px; + margin-left: 0; + list-style: none; + .clearfix; +} +.pagination li { + float: left; + margin-right: 2px; +} +.pagination li a, +.pagination li span { + display: block; + padding: 7px 9px; + line-height: 1; + border-radius: 3px; + color: @colorBlack; + text-decoration: none; +} +.pagination span { + border: 1px solid #ddd; +} +.pagination li a:focus, +.pagination li a:hover { + text-decoration: none; + background-color: @colorBlack; + color: #fff; +} + +/* =Responsive +-----------------------------------------------------------------------------*/ +@media only screen and (max-width: 767px) { + .navbar-left, + .navbar-right, + .navbar ul li, + .navbar-right ul li, + .subnav-right ul, + .subnav ul li, + .subnav-right ul li { + float: none; + text-align: center; + } + .navbar ul li, + .navbar-right ul li, + .subnav ul li, + .subnav-right ul li { + margin-left: 0; + margin-right: 0; + } + .fullwidth ul { + width: auto; + } + .fullwidth li { + display: block; + width: auto; + } +} \ No newline at end of file diff --git a/src/html/ReqMgr/style/kube301-less/less/normalize.less b/src/html/ReqMgr/style/kube301-less/less/normalize.less new file mode 100644 index 0000000000..374bc5d7db --- /dev/null +++ b/src/html/ReqMgr/style/kube301-less/less/normalize.less @@ -0,0 +1,225 @@ +/* =Normalize +-----------------------------------------------------------------------------*/ +*, +*:before, +*:after { + .box-sizing(border-box); +} +html { + font-family: sans-serif; + -ms-text-size-adjust: 100%; + -webkit-text-size-adjust: 100%; +} +html, +body, +div, +span, +object, +iframe, +p, +blockquote, +pre, +a, +abbr, +acronym, +address, +big, +cite, +code, +del, +dfn, +em, +img, +ins, +kbd, +q, +s, +samp, +small, +strike, +strong, +sub, +sup, +tt, +var, +b, +u, +i, +center, +dl, +dt, +dd, +ol, +ul, +li, +fieldset, +form, +label, +legend, +table, +caption, +tbody, +tfoot, +thead, +tr, +th, +td, +article, +aside, +canvas, +details, +embed, +figure, +figcaption, +footer, +header, +hgroup, +menu, +nav, +output, +ruby, +section, +summary, +time, +mark, +audio, +video, +h1, +h2, +h3, +h4, +h5, +h6 { + margin: 0; + padding: 0; + border: 0; + outline: 0; + font-size: 100%; + vertical-align: baseline; + background: transparent; +} +a:active, +a:hover { + outline: 0; +} +button, +input { + line-height: normal; +} +button, +select { + text-transform: none; +} +/* Structure tags */ +article, +aside, +details, +figcaption, +figure, +footer, +header, +hgroup, +main, +nav, +section, +summary { + display: block; +} +audio, +canvas, +video { + display: inline-block; +} +audio:not([controls]) { + display: none; + height: 0; +} +/* To prevent some browsers from inserting quotes on q and p */ +blockquote, +q { + quotes: none; +} +blockquote p:before, +blockquote p:after, +q:before, +q:after { + content: ''; + content: none; +} +/* Table reset */ +table { + border-collapse: collapse; + border-spacing: 0; +} +caption, +th, +td { + text-align: left; + vertical-align: top; + font-weight: normal; +} +thead th, +thead td { + font-weight: bold; + vertical-align: bottom; +} +/* Image inside */ +a img, +th img, +td img { + vertical-align: top; +} +button, +input, +select, +textarea { + margin: 0; +} +textarea { + overflow: auto; + vertical-align: top; + resize: vertical; +} +/* Normalize buttons in IE */ +button { + width: auto; + overflow: visible; +} +/* Hand cursor on clickable controls */ +input[type=button], +input[type=submit], +button { + cursor: pointer; +} +/* Trick with vertical align for radio and checkbox */ +input[type="radio"], +input[type="checkbox"] { + font-size: 110%; + position: relative; + top: -1px; +} +/* Normalize search fields */ +input[type="search"] { + -webkit-appearance: textfield; +} +input[type="search"]::-webkit-search-decoration { + -webkit-appearance: none; +} +hr { + display: block; + height: 1px; + border: 0; + border-top: 1px solid #ddd; +} +/* Responsive media */ +img, +video { + max-width: 100%; + height: auto; +} +img { + -ms-interpolation-mode: bicubic; +} +audio { + width: 100%; +} diff --git a/src/html/ReqMgr/style/kube301-less/less/prettyprint.less b/src/html/ReqMgr/style/kube301-less/less/prettyprint.less new file mode 100644 index 0000000000..466d06f15e --- /dev/null +++ b/src/html/ReqMgr/style/kube301-less/less/prettyprint.less @@ -0,0 +1,69 @@ +/* string content */ +.str { + color: #dd1144; +} + +/* a keyword */ +.kwd { + color: #333333; +} + +/* a comment */ +.com { + color: #999988; +} + +/* a type name */ +.typ { + color: #445588; +} + +/* a literal value */ +.lit { + color: #445588; +} + +/* punctuation */ +.pun { + color: #888; +} + +/* lisp open bracket */ +.opn { + color: #333333; +} + +/* lisp close bracket */ +.clo { + color: #333333; +} + +/* a markup tag name */ +.tag { + color: #367ac3; +} + +/* a markup attribute name */ +.atn { + color: #51a7c9; +} + +/* a markup attribute value */ +.atv { + color: #709c1a; +} + +/* a declaration */ +.dec { + color: #666; +} + +/* a variable name */ +.var { + color: teal; +} + +/* a function name */ +.fun { + color: #990000; +} \ No newline at end of file diff --git a/src/html/ReqMgr/style/kube301-less/less/tables.less b/src/html/ReqMgr/style/kube301-less/less/tables.less new file mode 100644 index 0000000000..381c574aee --- /dev/null +++ b/src/html/ReqMgr/style/kube301-less/less/tables.less @@ -0,0 +1,43 @@ +/* =Tables +-----------------------------------------------------------------------------*/ +table.table-bordered td, +table.table-bordered th { + border: 1px solid #ddd; +} +table.table-simple td, +table.table-simple th { + border: none; + padding-left: 0; +} +table.table-flat td, +table.table-flat th { + border: none; + padding: 0; +} +table.table-striped tbody tr:nth-child(odd) td { + background-color: #f8f8f8; +} +table.table-hovered tbody tr:hover td { + background-color: #f4f4f4; +} + +/* Responsive Tables */ +.table-container { + width: 100%; + overflow: auto; + margin-bottom: @baseLineInEms; + & table { + margin-bottom: 0; + } + &::-webkit-scrollbar { + -webkit-appearance: none; + width: 14px; + height: 14px; + } + &::-webkit-scrollbar-thumb { + border-radius: 8px; + border: 3px solid #fff; + background-color: rgba(0, 0, 0, .3); + } +} + diff --git a/src/html/ReqMgr/style/kube301-less/less/typography.less b/src/html/ReqMgr/style/kube301-less/less/typography.less new file mode 100644 index 0000000000..0ef44e749d --- /dev/null +++ b/src/html/ReqMgr/style/kube301-less/less/typography.less @@ -0,0 +1,324 @@ +/* =Typography +-----------------------------------------------------------------------------*/ +body { + background: @backgroundBody; + color: @colorBody; + font-size: @baseFontSize + 0px; + line-height: @baseLineInEms; + font-family: @baseFontFamily; +} +a { + color: @colorLink; + &:focus, + &:hover { + color: @colorLinkHover; + } +} +.title, +h1, +h2, +h3, +h4, +h5, +h6 { + font-family: @headingsFontFamily; + font-weight: bold; + color: @colorHeadings; + text-rendering: optimizeLegibility; +} +h1 { + font-size: @h1FontSize; + line-height: 1.111em; + margin-bottom: .25em; +} +h2 { + font-size: @h2FontSize; + line-height: 1.111em; + margin-bottom: .5em; +} +h3 { + font-size: @h3FontSize; + line-height: 1.333em; + margin-bottom: .5em; +} +h4 { + font-size: @h4FontSize; + line-height: 1.5em; + margin-bottom: .333em; +} +h5 { + font-size: @h5FontSize; + line-height: @baseLineInEms; + margin-bottom: .25em; +} +h6 { + font-size: @h6FontSize; + text-transform: uppercase; + line-height: @baseLineInEms; + margin-bottom: .25em; +} + +/* =Subheading +-----------------------------------------------------------------------------*/ +hgroup h1:first-child, +hgroup h2:first-child, +hgroup h3:first-child, +hgroup h4:first-child, +hgroup h5:first-child, { + margin-bottom: 0; +} +h1.subheading, +h2.subheading, +h3.subheading, +h4.subheading, +h5.subheading { + font-weight: 300; +} +h1.subheading { + font-size: @h1SubheadingFontSize; +} +h2.subheading { + font-size: @h2SubheadingFontSize; +} +h3.subheading { + font-size: @h3SubheadingFontSize; +} +h4.subheading { + font-size: @h4SubheadingFontSize; +} +h5.subheading { + font-size: @h5SubheadingFontSize; +} + +/* =Lead +-----------------------------------------------------------------------------*/ +.lead { + font-size: @leadFontSize; + font-weight: 300; + line-height: 1.4em; + margin-bottom: .75em; +} +p, +ul, +ol, +dl, +dd, +dt, +blockquote, +td, +th { + line-height: @baseLineInEms; +} +ul, +ol, +ul ul, +ol ol, +ul ol, +ol ul { + margin: 0 0 0 @baseLineInEms; +} +ol ol li { + list-style-type: lower-alpha; +} +ol ol ol li { + list-style-type: lower-roman; +} +p, +ul, +ol, +dl, +blockquote, +hr, +pre, +table, +form, +fieldset, +figure, +address { + margin-bottom: @baseLineInEms; +} +address { + font-style: normal; +} +dl dt { + font-weight: 500; +} +dd { + margin-left: 1em; +} +blockquote { + font-size: @quoteFontSize; + font-style: italic; + position: relative; + padding-left: @baseLineInEms; + margin-left: 0; + border-left: 2px solid #dbdada; + & p { + margin-bottom: .5em; + } +} +blockquote small, +cite { + color: #777; + font-style: italic; +} +small, +blockquote cite { + font-size: @smallFontSize; + line-height: 1; +} +del { + text-decoration: line-through; +} +abbr[title], +dfn[title] { + border-bottom: 1px dotted #000; + cursor: help; +} +strong, +b { + font-weight: bold; +} +em, +i { + font-style: italic; +} +sub, +sup { + font-size: @superSmallFontSize; + line-height: 0; + position: relative; +} +sup { + top: -0.5em; +} +sub { + bottom: -0.25em; +} +figcaption { + font-size: @smallFontSize; + font-style: italic; +} +ins, +mark { + background-color: @colorYellow; + color: @colorBlack; + text-decoration: none; +} +pre, +code, +kbd, +samp { + font-size: 90%; + font-family: @codeFontFamily; +} +pre { + font-size: 90%; + line-height: 1.45em; + color: @colorBody; + background: #f6f6f6; + padding: @baseLineInEms; + overflow: auto; +} +code { + padding: 3px 3px 1px 3px; + display: inline-block; + line-height: 1; + background: #f6f6f6; + border: 1px solid #ddd; +} +pre code { + font-size: 100%; + border: none; + padding: 0; + background: none; + line-height: @baseLineInEms; +} +kbd { + padding: 2px 6px 1px 6px; + line-height: 1; + display: inline-block; + border-radius: .3em; + box-shadow: 0 1px 0 rgba(0, 0, 0, 0.2), 0 0 0 1px white inset; + background-color: #fafafa; + border: 1px solid #ccc; + color: @colorBody; + font-weight: normal; + white-space: nowrap; +} +button:active, +button:focus { + outline: none; +} +input[type="text"], +input[type="password"], +input[type="email"], +input[type="url"], +input[type="phone"], +input[type="tel"], +input[type="number"], +input[type="datetime"], +input[type="date"], +input[type="search"], +input[type="datetime-local"], +textarea, +select[multiple="multiple"] { + font-family: @controlsFontFamily; + line-height: 1; + font-size: @baseFontSize + 0px; + border-radius: 0; + background: #fff; + box-shadow: none; + border: 1px solid #bbbcc0; + outline: none; + padding: 7px 5px; + position: relative; + z-index: 2; + -webkit-appearance: none; +} +input[type="text"], +input[type="password"], +input[type="email"], +input[type="url"], +input[type="phone"], +input[type="tel"], +input[type="number"], +input[type="datetime"], +input[type="date"], +input[type="search"], +input[type="datetime-local"] { + height: 2.3em; +} +input[type="range"] { + position: relative; + top: 3px; +} +select[multiple="multiple"], +textarea { + line-height: 1.35em; +} +fieldset { + padding: @baseLineInEms; + margin-bottom: @baseLineInEms; + border: 1px solid #dbdada; +} +legend { + font-weight: bold; + padding: 0 1em; + margin-left: -1em; +} +tfoot th, +tfoot td { + background-color: #f4f4f4; +} +th, +td { + border-bottom: 1px solid #eee; + padding: 8px 10px; +} +table caption { + text-transform: uppercase; + padding: 0 1em; + color: #777; + font-size: @smallFontSize; +} \ No newline at end of file diff --git a/src/html/ReqMgr/style/kube301-less/less/variables.less b/src/html/ReqMgr/style/kube301-less/less/variables.less new file mode 100644 index 0000000000..b57f1fc21b --- /dev/null +++ b/src/html/ReqMgr/style/kube301-less/less/variables.less @@ -0,0 +1,59 @@ +// Font +@baseFontFamily: "Helvetica Neue", Helvetica, Arial, sans-serif; +@headingsFontFamily: @baseFontFamily; +@controlsFontFamily: @baseFontFamily; +@codeFontFamily: Menlo, Monaco, Consolas, "Courier New", monospace; + +// Base color +@backgroundBody: #fff; +@colorBody: #333; +@colorHeadings: #222; + +// Grid +@gridWidth: 1000; +@gridGutterWidth: 30; + +// Type +@baseFontSize: 14; +@baseLineHeight: 1.6; +@baseLine: @baseFontSize * @baseLineHeight; +@baseLineInEms: @baseLine / @em; + +// Heading +@h1FontSize: @baseFontSize + 22px; // 36px +@h2FontSize: @baseFontSize + 16px; // 30px +@h3FontSize: @baseFontSize + 10px; // 24px +@h4FontSize: @baseFontSize + 4px; // 18px +@h5FontSize: @baseFontSize + 0px; // 14px +@h6FontSize: @baseFontSize - 2px; // 12px + +@h1SubheadingFontSize: @baseFontSize + 16px; // 30px +@h2SubheadingFontSize: @baseFontSize + 10px; // 24px +@h3SubheadingFontSize: @baseFontSize + 4px; // 18px +@h4SubheadingFontSize: @baseFontSize + 0px; // 14px +@h5SubheadingFontSize: @baseFontSize - 2px; // 12px + +// Font size +@superSmallFontSize: @baseFontSize - 4px; // 10px +@smallFontSize: @baseFontSize - 2px; // 12px +@leadFontSize: @baseFontSize + 4px; // 18px +@bigFontSize: @baseFontSize + 4px; // 18px +@quoteFontSize: @baseFontSize + 2px; // 16px +@buttonsSmallFontSize: @baseFontSize - 3px; // 11px + +// Colors +@colorBlack: #0f0f0f; +@colorBlue: #3d58a8; +@colorRed: #d70a16; +@colorGreen: #159776; +@colorYellow: #fddc43; + +// LinkColor +@colorLink: #3570c3; +@colorLinkHover: @colorRed; + +// Forms +@fieldsBottom: 1.6em; + +// Util +@em: @baseFontSize * 1em; // outputting ems, e.g. 14 / @em \ No newline at end of file diff --git a/src/html/ReqMgr/style/main.css b/src/html/ReqMgr/style/main.css new file mode 100644 index 0000000000..3f835a5f78 --- /dev/null +++ b/src/html/ReqMgr/style/main.css @@ -0,0 +1,88 @@ +.main-menu-item { + font-size: 16px; + color: #7C81B2; +} +.header { + background-color: #CCCCCC; + padding-left: 20px; + padding-right: 20px; + padding-top: 10px; + border-bottom: 1px solid #000000; + font: "Lucida Grande","Lucida Sans Unicode","Lucida Sans",Helvetica,Arial,sans-serif; + font-weight: bold; + color: #808080; +} +.shadow { + -webkit-box-shadow: 10px 10px 20px #666666; + -moz-box-shadow: 10px 10px 20px #666666; + -o-box-shadow: 10px 10px 20px #666666; + box-shadow: 10px 10px 20px #666666; +} +.all-side-shadow { + -webkit-box-shadow: 2px 2px 10px 10px #666; + -moz-box-shadow: 2px 2px 10px 10px #666; + -o-box-shadow: 2px 2px 10px 10px #666; + box-shadow: 2px 2px 10px 10px #666; +} +.borders { + -webkit-border-radius: 40px; + -khtml-border-radius: 40px; + -moz-border-radius: 40px; + border-radius: 40px; +} + +.editor { + width:100%; + height:400px; + font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', 'Consolas', 'source-code-pro', monospace; + font-weight: bold; + font-size: 1.2em; + color: rgb(64, 64, 64); + position: relative; +/* overflow: hidden;*/ +/* resize: none;*/ +/* white-space: nowrap;*/ +} +.height-200 { + height:200px; +} + +// http://css-tricks.com/snippets/css/glowing-blue-input-highlights/ +input[type=text], textarea { + -webkit-appearance: textarea; + -webkit-transition: all 0.30s ease-in-out; + -moz-transition: all 0.30s ease-in-out; + -ms-transition: all 0.30s ease-in-out; + -o-transition: all 0.30s ease-in-out; + outline: none; + padding: 3px 0px 3px 3px; + margin: 5px 1px 3px 0px; + border: 1px solid #DDDDDD; +} + +input[type=text]:focus, textarea:focus { + box-shadow: 0 0 5px #333333; + border: 1px solid #808080; + outline-color: #333333; + background-color: #F5F5F5; +} +.underline {border-bottom: 3px solid #808080;} +.hide { + display: none; +} +.show { + display: block; + margin-top:6px; + border-top-width:1px; +} +.ticket { + color: #000000; + background-color: #F5C669; + border-width: 1px 1px 1px 1px; + padding: 3px 3px 3px 3px; + border-style: dashed; + border-color: #000; + cursor: pointer; +} +tr:nth-child(even){ background:#F4F4F4; } +th { background: #B3B3B3; color: white; font-weight: bold; } diff --git a/src/html/ReqMgr/templates/admin.tmpl b/src/html/ReqMgr/templates/admin.tmpl new file mode 100644 index 0000000000..63709c3b54 --- /dev/null +++ b/src/html/ReqMgr/templates/admin.tmpl @@ -0,0 +1,190 @@ +
+ + +
+
+ +
+ +

Team management

+
+
+ + + +

+ +

+
+
+ + + + +

User management

+
+
+
+

+ + + + +

+
+
+ The roles will be assigned according with user DN registered in SiteDB +
+
+
+
+
+
+

+ + + + +

+
+
+ Upon this request given user name will be deleted from ReqMgr +
+
+
+ +
+ + + +
+ +

Existing accounts

+
+ + + + + + + + + + + + + + + + + + + + + +
UsersRoles
User 1Role 1
User 2Role 2
User 3Role 3
+
+ +
+ + + +
+ +

Existing teams

+
+Team 1: some description +
+ +
+ +
+Team 2: some description +
+ +
+ +
+Team 3: some description +
+ +
+ +
+ + diff --git a/src/html/ReqMgr/templates/apis.tmpl b/src/html/ReqMgr/templates/apis.tmpl new file mode 100644 index 0000000000..d6f785a180 --- /dev/null +++ b/src/html/ReqMgr/templates/apis.tmpl @@ -0,0 +1,36 @@ +

ReqMgr web UI mock-up

+
+This is a prototype web UI interface. All menus are click-able +but their actions are fakes. Please take a few minutes to +explore and provide your feedback. Thank you. + +
+
+ +
ReqMgr APIs
+
+This section will contain information about public APIs. We believe that +web UI will fully utilize those APIs. + + +
+ +
ReqMgr scripts
+
+This section will provide information about various scripts used by data-ops. +We think that sharing this information in one common place is beneficials for +everyone. + + diff --git a/src/html/ReqMgr/templates/approve.tmpl b/src/html/ReqMgr/templates/approve.tmpl new file mode 100644 index 0000000000..26440cda59 --- /dev/null +++ b/src/html/ReqMgr/templates/approve.tmpl @@ -0,0 +1,78 @@ +#from WMCore.ReqMgr.Web.utils import gen_color +#from WMCore.ReqMgr.Tools.cms import next_status + +

Approve interface

+
+ +##
+## +
+ + +
+ +
+#set ids=[] +#for rdict in $requests +#set rstat=$rdict['RequestStatus'] +#set keys=$rdict.keys() +#silent $keys.sort() +#silent $keys.remove('RequestName') ## we want RequestName to be first in a list +#for key in ['RequestName']+$keys +#set val=$rdict[$key] +#if $key=="RequestDate" +#set val="%s"%$val +#elif $key=="RequestName" +#set id=$val +#silent $ids.append($id) +#set val='%s'%($base,$val,$val) +#end if +#if $key=="RequestStatus" + $key.capitalize(): + #set color=$gen_color($rstat) + $val +#elif $key=="RequestName" +
+ + +
+#else + $key.capitalize(): + $val +
+#end if +#end for +Request seen by data-ops on 02/02/02. +
+#end for +##
+ diff --git a/src/html/ReqMgr/templates/assign.tmpl b/src/html/ReqMgr/templates/assign.tmpl new file mode 100644 index 0000000000..1110a41149 --- /dev/null +++ b/src/html/ReqMgr/templates/assign.tmpl @@ -0,0 +1,337 @@ +
+ + +
+
+ +
+
+ + +
+ + +
+ +
+ + + + + + + + + + + + + #for doc in $requests + #set id=$doc['RequestName'] + + + + + + + + + #end for + +
+ + Select + RequestsUsersGroupsDatesStatus
+ + $id$doc['Requestor']$doc['Group']$doc['RequestDate']$doc['RequestStatus']
+
+
+ +
+

Assign settings

+ +
Site lists
+
    +
  • Whitelist
    + +
  • +
  • Blacklist
    + +
  • +
+ +
+ +
PhEDEx subscription
+
    +
  • Custodial sites
    + +
  • +
  • Non-Custodial sites
    + +
  • +
  • Auto-Approve subscription sites
    + +
  • +
+ +
+ +
Misc Options
+
+ + +#* + + + + + + + + + + + + + + + + + + + +*# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +#* + + + + +*# + + + + + + + + + + + + + + + + + + +
CMSSW version + +
SCRAM architecture + +
Global Tag
Campaign
Subscription Priority + +
Custodial Subscription Type + +
Merged LFN Base + +
Unmerged LFN Base + +
Min Merge Size
Max Merge Size
Max Merge Events
Memory limit RSS (KiBytes)
Memory limit VSS (KiBytes)
Timeout (Seconds)
GracePeriod (Seconds)
Block close timeout (Seconds)
Block close max number of files
Block close max number of events
Block close max block size (Bytes)
Acquisition Era:
Processing Version:
Processing String:
Dashboard Activity: + +
Group name + + +
+
+ + + +
+ +#* +
+
+ +
+
+*# + + + diff --git a/src/html/ReqMgr/templates/confirm.tmpl b/src/html/ReqMgr/templates/confirm.tmpl new file mode 100644 index 0000000000..00a5d52899 --- /dev/null +++ b/src/html/ReqMgr/templates/confirm.tmpl @@ -0,0 +1,29 @@ +#import time +
Confirmation
+
+
+#if isinstance($ticket, list) +#for t in $ticket +Your request  $t  +has been created on $time.ctime() +
+#end for +#else +Your request  $ticket  +has been created on $time.ctime() +#end if +
+
+
+
Further actions
+
    +#if not isinstance($ticket, list) +
  • +Check status of this request +
  • +#end if +
  • +Check status of all requests +
  • +
+
diff --git a/src/html/ReqMgr/templates/create.tmpl b/src/html/ReqMgr/templates/create.tmpl new file mode 100644 index 0000000000..3a34e2ac7c --- /dev/null +++ b/src/html/ReqMgr/templates/create.tmpl @@ -0,0 +1,92 @@ +

Create request interface

+
+
+ + +
+ +
+ +
+ +Please use the following table to fill out template form. If you need to extend it +fill free to switch to JSON format and edit JSON directly. + +$table +

+

+ +

+
+
+
+
+ +
+

+ +

+
+
+
+ +
+ +

Create series of JSON requests

+You can place your python snippet below to either update given JSON +template or generate out of it multiple JSON objects. To do so, please +use jsondict object as your starting point. +
+ + +
+

+ +

+
+ + diff --git a/src/html/ReqMgr/templates/error.tmpl b/src/html/ReqMgr/templates/error.tmpl new file mode 100644 index 0000000000..cd11f2a368 --- /dev/null +++ b/src/html/ReqMgr/templates/error.tmpl @@ -0,0 +1,6 @@ +

Error page

+
+Something bad happens with your request and you end-up on this page. +Here is description of the error:
+ +$content diff --git a/src/html/ReqMgr/templates/generic.tmpl b/src/html/ReqMgr/templates/generic.tmpl new file mode 100644 index 0000000000..5933c99c95 --- /dev/null +++ b/src/html/ReqMgr/templates/generic.tmpl @@ -0,0 +1,6 @@ +
+ $menu +
+
$content
+
+
diff --git a/src/html/ReqMgr/templates/json/dataproc.tmpl b/src/html/ReqMgr/templates/json/dataproc.tmpl new file mode 100644 index 0000000000..0a7ac1f03d --- /dev/null +++ b/src/html/ReqMgr/templates/json/dataproc.tmpl @@ -0,0 +1,30 @@ +{ +"RequestString":"", +"PrepID":"", +"User":$user, +"Group":$groups, +"RequestPriority":1, +"CMSSWVersion":$releases, +"ScramArch": $arch, +"IncludeParents": [true, false], +"Campaign":"", +"MultiCore":"", +"Scenario":$scenarios, +"GlobalTag":"", +"EnableHarvesting": false, +"DQMConfigCacheID":"", +"DQMSequences":"", +"DQMUploadURL":$dqm_urls, +"InputDataset":"", +"OpenRunningTimeout":0, +"DBS":$dbs_urls, +"RunWhitelist":"", +"RunBlacklist":"", +"BlockWhitelist":"", +"BlockBlacklist":"", +"TimePerEvent":12, +"Memory":2300, +"SizePerEvent": 512, +"RequestType": "DataProcessing" +} + diff --git a/src/html/ReqMgr/templates/json/mc.tmpl b/src/html/ReqMgr/templates/json/mc.tmpl new file mode 100644 index 0000000000..dded8a5f7a --- /dev/null +++ b/src/html/ReqMgr/templates/json/mc.tmpl @@ -0,0 +1,23 @@ +{ +"RequestString":"", +"PrepID":"", +"User":$user, +"Group":$groups, +"RequestPriority":1, +"CMSSWVersion":$releases, +"ScramArch": $arch, +"RequestNumEvents": 1000, +"FilterEfficiency": 1.0, +"TotalTime": 32400, +"TimePerEvent":12, +"Memory":2300, +"SizePerEvent": 512, +"FirstEvent": 1, +"Scenario":$scenarios, +"GlobalTag":"", +"PrimaryDataset":"", +"DataPileup":"", +"MCPileup":"", +"RequestType": "MonteCarlo" +} + diff --git a/src/html/ReqMgr/templates/json/redigi.tmpl b/src/html/ReqMgr/templates/json/redigi.tmpl new file mode 100644 index 0000000000..ab7c585011 --- /dev/null +++ b/src/html/ReqMgr/templates/json/redigi.tmpl @@ -0,0 +1,37 @@ +{ +"RequestString":"", +"PrepID":"", +"User":$user, +"Group":$groups, +"RequestPriority":1, +"CMSSWVersion":$releases, +"ScramArch": $arch, +"IncludeParents": [true, false], +"Campaign":"", +"InputDataset":"", +"OpenRunningTimeout":0, +"DBS":$dbs_urls, +"EnableHarvesting": false, +"DQMConfigCacheID":"", +"DQMSequences":"", +"DQMUploadURL":$dqm_urls, +"StepOneConfigCacheID":"", +"StepOneOutputModuleName":"", +"StepTwoConfigCacheID":"", +"StepTwoOutputModuleName":"", +"StepThreeConfigCacheID":"", +"KeepStepOneOutput": [true, false], +"KeepStepTwoOutput": [true, false], +"GlobalTag":"", +"DataPileup":"", +"MCPileup":"", +"RunWhitelist":"", +"RunBlacklist":"", +"BlockWhitelist":"", +"BlockBlacklist":"", +"TimePerEvent":12, +"Memory":2300, +"SizePerEvent": 512, +"RequestType": "DataProcessing" +} + diff --git a/src/html/ReqMgr/templates/json/rereco.tmpl b/src/html/ReqMgr/templates/json/rereco.tmpl new file mode 100644 index 0000000000..1e98b1a632 --- /dev/null +++ b/src/html/ReqMgr/templates/json/rereco.tmpl @@ -0,0 +1,29 @@ +{ +"RequestString":"", +"PrepID":"", +"User":$user, +"Group":$groups, +"RequestPriority":1, +"CMSSWVersion":$releases, +"ScramArch": $arch, +"IncludeParents": [true, false], +"Campaign": "", +"Scenario":$scenarios, +"GlobalTag":"", +"EnableHarvesting": false, +"DQMConfigCacheID":"", +"DQMSequences":"", +"DQMUploadURL":$dqm_urls, +"InputDataset":"", +"OpenRunningTimeout":0, +"DBS":$dbs_urls, +"RunWhitelist":"", +"RunBlacklist":"", +"BlockWhitelist":"", +"BlockBlacklist":"", +"TimePerEvent":12, +"Memory":2300, +"SizePerEvent": 512, +"RequestType": "ReReco" +} + diff --git a/src/html/ReqMgr/templates/json/resub.tmpl b/src/html/ReqMgr/templates/json/resub.tmpl new file mode 100644 index 0000000000..134986f364 --- /dev/null +++ b/src/html/ReqMgr/templates/json/resub.tmpl @@ -0,0 +1,18 @@ +{ +"RequestString":"", +"PrepID":"", +"User":$user, +"Group":$groups, +"RequestPriority":1, +"OriginalRequestName": "", +"InitialTaskPath":"", +"ACDCServer":"", +"ACDCDatabase":"", +"IgnoredOutputModules": "", +"CollectionName":"", +"TimePerEvent":12, +"Memory":2300, +"SizePerEvent": 512, +"RequestType": "Resubmission" +} + diff --git a/src/html/ReqMgr/templates/json/storeres.tmpl b/src/html/ReqMgr/templates/json/storeres.tmpl new file mode 100644 index 0000000000..068ea4014c --- /dev/null +++ b/src/html/ReqMgr/templates/json/storeres.tmpl @@ -0,0 +1,21 @@ +{ +"RequestString":"", +"PrepID":"", +"User":$user, +"Group":$groups, +"RequestPriority":1, +"AcquisitionEra":"", +"ProcessingVersion":"", +"GlobalTag":"", +"CmsPath":"", +"CMSSWVersion":$releases, +"ScramArch": $arch, +"InputDataset":"", +"OpenRunningTimeout":"", +"DBS":$dbs_urls, +"TimePerEvent":12, +"Memory":2300, +"SizePerEvent": 512, +"RequestType": "StoreResults" +} + diff --git a/src/html/ReqMgr/templates/main.tmpl b/src/html/ReqMgr/templates/main.tmpl new file mode 100644 index 0000000000..63ff229816 --- /dev/null +++ b/src/html/ReqMgr/templates/main.tmpl @@ -0,0 +1,32 @@ + + + + ReqMgr Prototype + + + + + + + +
+ + +
+
+ + $content + + + diff --git a/src/html/ReqMgr/templates/menu.tmpl b/src/html/ReqMgr/templates/menu.tmpl new file mode 100644 index 0000000000..4fd0d43c99 --- /dev/null +++ b/src/html/ReqMgr/templates/menu.tmpl @@ -0,0 +1,15 @@ +
+
+ +
diff --git a/src/html/ReqMgr/templates/requests.tmpl b/src/html/ReqMgr/templates/requests.tmpl new file mode 100644 index 0000000000..0346717662 --- /dev/null +++ b/src/html/ReqMgr/templates/requests.tmpl @@ -0,0 +1,35 @@ +

Requests

+
+
+ + +
+
+ +#for rdict in $requests +
+ +
+
+#end for diff --git a/src/html/ReqMgr/templates/search.tmpl b/src/html/ReqMgr/templates/search.tmpl new file mode 100644 index 0000000000..4ebb29affc --- /dev/null +++ b/src/html/ReqMgr/templates/search.tmpl @@ -0,0 +1,22 @@ +

Search interface

+
+DB statistics: N-docs, Number TB, last update: 20140303 + +
+This form will provide search capabilities. For example you can enter +the following dataset +
+/RelValSingleMuPt10/CMSSW_6_2_0-PRE_ST62_V8-v1/GEN-SIM-RECO +
+to see how it works. + +
+ +
+ +
$content
diff --git a/src/html/ReqMgr/templates/validate.tmpl b/src/html/ReqMgr/templates/validate.tmpl new file mode 100644 index 0000000000..851004086b --- /dev/null +++ b/src/html/ReqMgr/templates/validate.tmpl @@ -0,0 +1,9 @@ +

Validate interface

+
+Paste your JSON into provided box: +
+ + +
+ +
diff --git a/src/html/ReqMgr/templates/workflow.tmpl b/src/html/ReqMgr/templates/workflow.tmpl new file mode 100644 index 0000000000..9522211b0a --- /dev/null +++ b/src/html/ReqMgr/templates/workflow.tmpl @@ -0,0 +1,34 @@ +
+
+ + +
+
+ +
+
+ + + +
+ +  + +  + +
+
+ +
+ diff --git a/src/python/WMCore/Cache/WMConfigCache.py b/src/python/WMCore/Cache/WMConfigCache.py index 6ccb0faf59..6b1707ec92 100644 --- a/src/python/WMCore/Cache/WMConfigCache.py +++ b/src/python/WMCore/Cache/WMConfigCache.py @@ -36,8 +36,52 @@ class ConfigCacheException(WMException): """ +class Singleton(type): + """Implementation of Singleton class""" + _instances = {} + def __call__(cls, *args, **kwargs): + if cls not in cls._instances: + cls._instances[cls] = \ + super(Singleton, cls).__call__(*args, **kwargs) + return cls._instances[cls] +class DocumentCache(object): + """DocumentCache holds config ids. Use this class as singleton""" + __metaclass__ = Singleton + def __init__(self, database): + super(DocumentCache, self).__init__() + self.cache = {} + self.database = database + def __getitem__(self, configID): + """ + Internal get method to fetch document based on provided ID + """ + if configID not in self.cache: + self.prefetch([configID]) + return self.cache[configID] + + def cleanup(self, ids): + """ + _cleanup_ + + Clean-up given ids from local cache + """ + for rid in ids: + if rid in self.cache: + del self.cache[rid] + + def prefetch(self, keys): + """ + _fetch_ + + Pre-fetch given list of documents from CouchDB + """ + options = {'include_docs':True} + result = self.database.allDocs(options=options, keys=keys) + for row in result['rows']: + doc = row['doc'] + self.cache[doc['_id']] = doc class ConfigCache(WMObject): """ @@ -62,6 +106,9 @@ def __init__(self, dbURL, couchDBName = None, id = None, rev = None, usePYCurl = logging.error(msg) raise ConfigCacheException(message = msg) + # local cache + self.docs_cache = DocumentCache(self.database) + # UserGroup variables self.group = None self.owner = None @@ -212,6 +259,14 @@ def saveAttachment(self, name, attachment): return + def loadDocument(self, configID): + """ + _loadDocumentID_ + + Load a document from the document cache given its couchID + """ + self.document = self.docs_cache.cache[configID] + def loadByID(self, configID): """ _loadByID_ diff --git a/src/python/WMCore/Lexicon.py b/src/python/WMCore/Lexicon.py index 96f70698ec..7869ccac02 100644 --- a/src/python/WMCore/Lexicon.py +++ b/src/python/WMCore/Lexicon.py @@ -294,8 +294,11 @@ def validateUrl(candidate): regex_url = r'%s(%s|%s|%s|%s)%s%s' % (protocol, domain, localhost, ipv4, ipv6, port, path) return check(regex_url, candidate) +REGEX_CACHE = {} def check(regexp, candidate): - assert re.compile(regexp).match(candidate) != None , \ + if regexp not in REGEX_CACHE: + REGEX_CACHE[regexp] = re.compile(regexp) + assert REGEX_CACHE[regexp].match(candidate) != None, \ "'%s' does not match regular expression %s" % (candidate, regexp) return True diff --git a/src/python/WMCore/ReqMgr/Service/Request.py b/src/python/WMCore/ReqMgr/Service/Request.py index ca37993554..5bc6a3dfb2 100644 --- a/src/python/WMCore/ReqMgr/Service/Request.py +++ b/src/python/WMCore/ReqMgr/Service/Request.py @@ -247,7 +247,7 @@ def _get_couch_view(self, couchdb, couchapp, view, options, keys): if not options: options = {} options.setdefault("include_docs", True) - if type(keys) == str: + if isinstance(keys, basestring): keys = [keys] result = couchdb.loadView(couchapp, view, options, keys) @@ -261,9 +261,9 @@ def _get_couch_view(self, couchdb, couchapp, view, options, keys): #TODO move this out of this class def filterCouchInfo(self, couchInfo): - del couchInfo["_rev"] - del couchInfo["_id"] - del couchInfo["_attachments"] + for key in ['_rev', '_attachments']: + if key in couchInfo: + del couchInfo[key] def get_reqmgr_view(self, view, options, keys): return self._get_couch_view(self.reqmgr_db, "ReqMgr", view, diff --git a/src/python/WMCore/ReqMgr/Tools/WorkflowTools.py b/src/python/WMCore/ReqMgr/Tools/WorkflowTools.py new file mode 100644 index 0000000000..cd7aeac6f7 --- /dev/null +++ b/src/python/WMCore/ReqMgr/Tools/WorkflowTools.py @@ -0,0 +1,608 @@ +#!/usr/bin/env python +#-*- coding: utf-8 -*- +#pylint: disable= +""" +File : WorkflowManager.py +Author : Valentin Kuznetsov +Description: Workflow management tools +""" + +# system modules +import os +import re +import json +import httplib + +# ReqMgr modules +from ReqMgr.tools.reqMgrClient import WorkflowManager + +# DBS modules +import dbs3Client as dbs3 +from dbs.apis.dbsClient import DbsApi + +# DBS3 helper functions +DBS3 = r'https://cmsweb.cern.ch/dbs/prod/global/DBSReader' +def getDatasets(dataset_pattern): + "Return list of dataset for given dataset pattern" + dbsapi = DbsApi(url=DBS3, verifypeer=False) + reply = dbsapi.listDatasets(dataset=dataset_pattern, dataset_access_type='*') + return reply + +def getDatasetStatus(dataset): + "Return dataset status" + dbsapi = DbsApi(url=DBS3, verifypeer=False) + reply = dbsapi.listDatasets(dataset=dataset, dataset_access_type='*', detail=True) + return reply[0]['dataset_access_type'] + +def getWorkload(url, workflow): + "Return workload list" + conn = httplib.HTTPSConnection(url, + cert_file = os.getenv('X509_USER_PROXY'), + key_file = os.getenv('X509_USER_PROXY')) + r1=conn.request("GET",'/reqmgr/view/showWorkload?requestName='+workflow) + r2=conn.getresponse() + workload=r2.read() + return workload.split('\n') + +class WorkflowDataOpsMgr(WorkflowManager): + def __init__(self, workflow, **kwds): + """ + Extend WorkflowManager and assign data-ops attributes for + given workflow. The order of calls does matter !!! + """ + self.kwds = kwds + self.url = self.get('url', 'cmsweb.cern.ch') + WorkflowManager.__init__(self, workflow, self.url) + self.workload = getWorkload(self.url, workflow) + self.cacheID = self.winfo.get('StepOneConfigCacheID', '') + self.config = getConfig(self.url, self.cacheID) + self.pileup_dataset = self._pileup_dataset() + self.priority = self._priority() + self.era = self.get('era', 'Summer12') + self.lfn = self.get('lfn', '/store/mc') + self.special_name = self.get('specialName', '') + self.max_rss = self.get('maxRSS', 2300000) + self.max_vsize = self.get('maxVSize', 4100000000) + self.input_dataset = '' + self.pileup_scenario = '' + self.global_tag = self.get('globalTag', '') + self.campaign = self.get('campaign', '') + self.max_merge_events = self.get('maxMergeEvents', 50000) + self.activity = self.get('activity', 'reprocessing') + self.restrict = self.get('restrict', 'None') + self.site_use = self.get('site', None) + self.site_cust = self.get('site_cust', None) + self.xrootd = self.get('xrootd', 0) + self.ext_tag = self.get('ext', '') + self.team = self.get('team', '') + + # perform various initialization + self._init() + + # custom settings + # Construct processed dataset version + if self.pileup_scenario: + self.pileup_scenario = self.pileup_scenario+'_' + + specialprocstring = kwds.get('specialName', '') + if specialprocstring: + self.special_name = specialprocstring + '_' + + # ProcessingString + inprocstring = kwds.get('procstring', '') + if inprocstring: + self.procstring = inprocstring + else: + self.procstring = self.special_name + self.pileup_scenario +\ + self.global_tag + self.ext_tag + + # ProcessingVersion + inprocversion = kwds.get('procversion', '') + if inprocversion: + self.procversion = inprocversion + else: + self.procversion = self.dataset_version(self.era, self.procstring) + + def dataset_version(self, era, partialProcVersion): + versionNum = 1 + outputs = self.output_datasets + for output in outputs: + bits = output.split('/') + outputCheck = '/'+bits[1]+'/'+era+'-'+partialProcVersion+'*/'+bits[len(bits)-1] + + datasets = getDatasets(outputCheck) + for dataset in datasets: + datasetName = dataset['dataset'] + matchObj = re.match(r".*-v(\d+)/.*", datasetName) + if matchObj: + currentVersionNum = int(matchObj.group(1)) + if versionNum <= currentVersionNum: + versionNum=versionNum+1 + + return versionNum + + ### private methods + def _init(self): + "Perform initialization and cross-checks" + self.input_dataset = self._input_dataset() + self.global_tag = self._global_tag() + self.ext_tag = self._ext_tag() + self.campaign = self._campaign() + self.era, self.lfn, self.special_name = self._era_lfn_name() + self.pileup_scenario = self._pileup_scenario() + self.max_rss = self._max_rss() + self.max_merge_events = self._max_merge_events() + self.team = self._team() + self.site_use, self.site_cust = self._sites() + + # Checks attributes + checklist = [(self.era, ''), (self.lfn, ''), (self.pileup_scenario, 'Unknown')] + for att, val in checklist: + if att == val: + raise Exception('ERROR: %s == "%s"' % (att, val)) + + # Check status of input dataset + inputDatasetStatus = getDatasetStatus(self.input_dataset) + if inputDatasetStatus != 'VALID' and inputDatasetStatus != 'PRODUCTION': + raise Exception('ERROR: Input dataset is not PRODUCTION or VALID, status=%s' % inputDatasetStatus) + + def get(self, key, default=''): + "Get extension tag" + val = self.kwds.get(key) + if not val: + val = default + return val + + def _ext_tag(self): + "Get extension tag" + if self.ext_tag: + ext_tag = '_ext' + self.ext_tag + else: + ext_tag = '' + return ext_tag + + def _global_tag(self): + "Extract required part of global tag from workflow info" + return self.winfo.get('GlobalTag', '').split('::')[0] + + def _campaign(self): + "Return campaign from workflow info" + return self.winfo.get('Campaign', '') + + def _max_rss(self): + "Return maxRSS" + max_rss = self.max_rss + if ('HiFall11' in self.workflow or 'HiFall13DR53X' in self.workflow) and \ + 'IN2P3' in self.site_use: + max_rss = 4000000 + return max_rss + + def _max_merge_events(self): + "Return max number of merge events" + if 'DR61SLHCx' in self.workflow: + return 5000 + return self.max_merge_events + + def _input_dataset(self): + "Return input dataset of workflow" + dataset = self.winfo.get('InputDataset', '') + if not dataset: + raise Exception("Error: no input dataset found for %s" % self.workflow) + return dataset + + def _era_lfn_name(self): + """ + Return era/lfn/name for given workflow, so far we have hard-coded cases, + later it should be stored persistently and we should have APIs: get/put + to fetch/store/update this info in DB. + """ + workflow = self.workflow + campaign = self.campaign + era = 'Summer12' + lfn = '/store/mc' + specialName = '' + + # Set era, lfn and campaign-dependent part of name if necessary + if 'Summer12_DR51X' in workflow: + era = 'Summer12' + lfn = '/store/mc' + + if 'Summer12_DR52X' in workflow: + era = 'Summer12' + lfn = '/store/mc' + + if 'Summer12_DR53X' in workflow or ('Summer12' in workflow and 'DR53X' in workflow): + era = 'Summer12_DR53X' + lfn = '/store/mc' + + #this is incorrect for HiFall11 workflows, but is changed further down + if 'Fall11_R' in workflow or 'Fall11R' in workflow: + era = 'Fall11' + lfn = '/store/mc' + + if 'Summer13dr53X' in workflow: + era = 'Summer13dr53X' + lfn = '/store/mc' + + if 'Summer11dr53X' in workflow: + era = 'Summer11dr53X' + lfn = '/store/mc' + + if 'Fall11_HLTMuonia' in workflow: + era = 'Fall11' + lfn = '/store/mc' + specialName = 'HLTMuonia_' + + if 'Summer11_R' in workflow: + era = 'Summer11' + lfn = '/store/mc' + + if 'LowPU2010_DR42' in workflow or 'LowPU2010DR42' in workflow: + era = 'Summer12' + lfn = '/store/mc' + specialName = 'LowPU2010_DR42_' + + if 'UpgradeL1TDR_DR6X' in workflow: + era = 'Summer12' + lfn = '/store/mc' + + if 'HiWinter13' in self.input_dataset: + era = 'HiWinter13' + lfn = '/store/himc' + + if 'Spring14dr' in workflow: + era = 'Spring14dr' + lfn = '/store/mc' + if '_castor_' in workflow: + specialName = 'castor_' + + if 'Winter13' in workflow and 'DR53X' in workflow: + era = 'HiWinter13' + lfn = '/store/himc' + + if 'Summer11LegDR' in campaign: + era = 'Summer11LegDR' + lfn = '/store/mc' + + if 'UpgradePhase1Age' in campaign: + era = 'Summer13' + lfn = '/store/mc' + specialName = campaign + '_' + + if campaign == 'UpgradePhase2LB4PS_2013_DR61SLHCx': + era = 'Summer13' + lfn = '/store/mc' + specialName = campaign + '_' + + if campaign == 'UpgradePhase2BE_2013_DR61SLHCx': + era = 'Summer13' + lfn = '/store/mc' + specialName = campaign + '_' + + if campaign == 'UpgradePhase2LB6PS_2013_DR61SLHCx': + era = 'Summer13' + lfn = '/store/mc' + specialName = campaign + '_' + + if campaign == 'UpgradePhase1Age0DES_DR61SLHCx': + era = 'Summer13' + lfn = '/store/mc' + specialName = campaign + '_' + + if campaign == 'UpgradePhase1Age0START_DR61SLHCx': + era = 'Summer13' + lfn = '/store/mc' + specialName = campaign + '_' + + if campaign == 'UpgradePhase1Age3H_DR61SLHCx': + era = 'Summer13' + lfn = '/store/mc' + specialName = campaign + '_' + + if campaign == 'UpgradePhase1Age5H_DR61SLHCx': + era = 'Summer13' + lfn = '/store/mc' + specialName = campaign + '_' + + if campaign == 'UpgradePhase1Age1K_DR61SLHCx': + era = 'Summer13' + lfn = '/store/mc' + specialName = campaign + '_' + + if campaign == 'UpgradePhase1Age3K_DR61SLHCx': + era = 'Summer13' + lfn = '/store/mc' + specialName = campaign + '_' + + #change back to old campaign names for UpgradePhase1 + if 'UpgradePhase1Age' in campaign and 'dr61SLHCx' in specialName: + specialName = specialName.replace("dr61SLHCx","_DR61SLHCx") + if 'dr61SLHCx' in specialName: + print 'WARNING: using new campaign name format' + + if campaign == 'HiFall11_DR44X' or campaign == 'HiFall11DR44': + era = 'HiFall11' + lfn = '/store/himc' + specialName = 'HiFall11_DR44X' + '_' + + if campaign == 'HiFall13DR53X': + era = 'HiFall13DR53X' + lfn = '/store/himc' + + if campaign == 'UpgFall13d': + era = campaign + lfn = '/store/mc' + + if campaign == 'Fall13dr': + era = campaign + lfn = '/store/mc' + if '_castor_tsg_' in workflow: + specialName = 'castor_tsg_' + elif '_castor_' in workflow: + specialName = 'castor_' + elif '_tsg_' in workflow: + specialName = 'tsg_' + elif '__' in workflow: + specialName = '' + else: + print 'ERROR: unexpected special name string in workflow name' + sys.exit(0) + + # Handle NewG4Phys + if campaign == 'Summer12DR53X' and 'NewG4Phys' in workflow: + specialName = 'NewG4Phys_' + + # Handle Ext30 + if campaign == 'Summer12DR53X' and 'Ext30' in workflow: + specialName = 'Ext30_' + + # Handle BS2011 + if campaign == 'LowPU2010DR42' and 'BS2011' in workflow: + specialName = 'LowPU2010_DR42_BS2011_' + + return era, lfn, specialName + + def _pileup_scenario(self): + """ + Return pileup scenario name based on given workflow + Code should be replaced with persistent store. + """ + workflow = self.workflow + campaign = self.campaign + pileupDataset = self._pileup_dataset() + if pileupDataset != 'None': + [subscribedOurSite, subscribedOtherSite] = checkAcceptedSubscriptionRequest(self.url, pileupDataset, siteSE) + if not subscribedOurSite: + print 'ERROR: pileup dataset not subscribed/approved to required Disk endpoint' + sys.exit(0) + + # Determine pileup scenario + # - Fall11_R2 & Fall11_R4 don't add pileup so extract pileup scenario from input + pileupScenario = '' + pileupScenario = getPileupScenario(self.winfo, self.config) + if campaign == 'Summer12_DR53X_RD': + pileupScenario = 'PU_RD1' + if pileupScenario == 'Unknown' and 'MinBias' in pileupDataset and 'LowPU2010DR42' not in workflow: + print 'ERROR: unable to determine pileup scenario' + sys.exit(0) + elif 'Fall11_R2' in workflow or 'Fall11_R4' in workflow or 'Fall11R2' in workflow or 'Fall11R4' in workflow: + matchObj = re.match(r".*Fall11-(.*)_START.*", inputDataset) + if matchObj: + pileupScenario = matchObj.group(1) + else: + pileupScenario == 'Unknown' + elif pileupScenario == 'Unknown' and 'MinBias' not in pileupDataset: + pileupScenario = 'NoPileUp' + + if pileupScenario == 'Unknown': + pileupScenario = '' + + if 'LowPU2010_DR42' in workflow or 'LowPU2010DR42' in workflow: + pileupScenario = 'PU_S0' + if 'HiWinter13' in workflow and 'DR53X' in workflow: + pileupScenario = '' + if 'pAWinter13' in workflow and 'DR53X' in workflow: + pileupScenario = 'pa' # not actually the pileup scenario of course + if 'ppWinter13' in workflow and 'DR53X' in workflow: + pileupScenario = 'pp' # not actually the pileup scenario of course + return pileupScenario + + def _pileup_dataset(self): + pileupDataset = 'None' + for line in self.workload: + if 'request.schema.MCPileup' in line: + pileupDataset = line[line.find("'")+1:line.find("'",line.find("'")+1)] + return pileupDataset + + + def _priority(self): + priority = -1 + for line in self.workload: + if 'request.schema.RequestPriority' in line: + priority = line[line.find("=")+1:line.find("
1: + siteUse = findCustodialLocation(self.url, self.input_dataset) + if siteUse == 'None': + raise Exception('ERROR: No custodial site found for dataset=%s' % self.input_dataset) + siteUse = siteUse[:-4] + + # Set the custodial location if necessary + if not self.site_use or self.site_use != 'T2_US': + if not self.site_cust: + siteCust = siteUse + else: + siteCust = self.site_cust + + # Check if input dataset subscribed to disk endpoint + if 'T2_CH_CERN' in siteUse: + siteSE = 'T2_CH_CERN' + else: + siteSE = siteUse + '_Disk' + subscribedOurSite, subscribedOtherSite = \ + checkAcceptedSubscriptionRequest(self.url, self.input_dataset, siteSE) + if not subscribedOurSite and not self.xrootd and 'Fall11R2' not in workflow: + raise Exception('ERROR: input dataset not subscribed/approved to required Disk endpoint') + if self.xrootd and not subscribedOtherSite: + raise Exception('ERROR: input dataset not subscribed/approved to any Disk endpoint') + if siteUse not in sites and options.site != 'T2_US' and \ + siteUse != ['T2_CH_CERN_AI', 'T2_CH_CERN_HLT', 'T2_CH_CERN']: + raise Exception('ERROR: invalid site=%s' % siteUse) + + if not siteCust: + raise Exception('ERROR: A custodial site must be specified') + + return siteUse, siteCust + +def getScenario(ps): + pss = 'Unknown' + + if ps == 'SimGeneral.MixingModule.mix_E8TeV_AVE_16_BX_25ns_cfi': + pss = 'PU140Bx25' + if ps == 'SimGeneral.MixingModule.mix_2012_Summer_50ns_PoissonOOTPU_cfi': + pss = 'PU_S10' + if ps == 'SimGeneral.MixingModule.mix_E7TeV_Fall2011_Reprocess_50ns_PoissonOOTPU_cfi': + pss = 'PU_S6' + if ps == 'SimGeneral.MixingModule.mix_E8TeV_AVE_10_BX_25ns_300ns_spread_cfi': + pss = 'PU10bx25' + if ps == 'SimGeneral.MixingModule.mix_E8TeV_AVE_10_BX_50ns_300ns_spread_cfi': + pss = 'PU10bx50' + if ps == 'SimGeneral.MixingModule.mix_2011_FinalDist_OOTPU_cfi': + pss = 'PU_S13' + if ps == 'SimGeneral.MixingModule.mix_fromDB_cfi': + pss = 'PU_RD1' + if ps == 'SimGeneral.MixingModule.mix_2012C_Profile_PoissonOOTPU_cfi': + pss = 'PU2012CExt' + if ps == 'SimGeneral.MixingModule.mixNoPU_cfi': + pss = 'NoPileUp' + if ps == 'SimGeneral.MixingModule.mix_POISSON_average_cfi': + pss = 'PU' + if ps == 'SimGeneral.MixingModule.mix_CSA14_50ns_PoissonOOTPU_cfi': + pss = 'PU_S14' + + return pss + +def getPileupScenario(winfo, config): + "Get pileup scanario for given workflow dict and configuration" + workflow = winfo['RequestName'] + pileup, meanPileUp, bunchSpacing, cmdLineOptions = getPileup(config) + scenario = getScenario(pileup) + if scenario == 'PU140Bx25' and meanPileUp != 'Unknown': + scenario = 'PU' + meanPileUp + 'bx25' + if scenario == 'PU140bx25' and 'Upgrade' in workflow: + scenario = 'PU140Bx25' + if scenario == 'PU': + scenario = 'PU' + meanPileUp + 'bx' + bunchSpacing + if meanPileUp == 'None' or bunchSpacing == 'None': + print 'ERROR: unexpected pileup settings in config' + sys.exit(0) + if scenario == 'PU_RD1' and cmdLineOptions != 'None': + if '--runsAndWeightsForMC [(190482,0.924) , (194270,4.811), (200466,7.21), (207214,7.631)]' in cmdLineOptions: + scenario = 'PU_RD2' + return scenario + +def getPileup(config): + "Helper function used in getPileupScenario" + pu = 'Unknown' + vmeanpu = 'None' + bx = 'None' + cmdLineOptions = 'None' + lines = config.split('\n') + for line in lines: + if 'process.load' and 'MixingModule' in line: + pu = line[line.find("'")+1:line.find("'",line.find("'")+1)] + if 'process.mix.input.nbPileupEvents.averageNumber' in line: + meanpu = line[line.find("(")+1:line.find(")")].split('.', 1) + vmeanpu = meanpu[0] + if 'process.mix.bunchspace' in line: + bx = line[line.find("(")+1:line.find(")")] + if 'with command line options' in line: + cmdLineOptions = line + return pu, vmeanpu, bx, cmdLineOptions + +def getConfig(url, cacheID): + "Helper function to get configuration for given cacheID" + conn = httplib.HTTPSConnection(url, + cert_file = os.getenv('X509_USER_PROXY'), + key_file = os.getenv('X509_USER_PROXY')) + conn.request("GET",'/couchdb/reqmgr_config_cache/'+cacheID+'/configFile') + config = conn.getresponse().read() + return config + +def findCustodialLocation(url, dataset): + "Helper function to find custodial location for given dataset" + conn = httplib.HTTPSConnection(url, cert_file = os.getenv('X509_USER_PROXY'), key_file = os.getenv('X509_USER_PROXY')) + r1=conn.request("GET",'/phedex/datasvc/json/prod/blockreplicas?dataset='+dataset) + r2=conn.getresponse() + result = json.loads(r2.read()) + request=result['phedex'] + if 'block' not in request.keys(): + return "No Site" + if len(request['block'])==0: + return "No Site" + for replica in request['block'][0]['replica']: + if replica['custodial']=="y" and replica['node']!="T0_CH_CERN_MSS": + return replica['node'] + return "None" + +def checkAcceptedSubscriptionRequest(url, dataset, site): + "Helper function" + conn = httplib.HTTPSConnection(url, + cert_file = os.getenv('X509_USER_PROXY'), + key_file = os.getenv('X509_USER_PROXY')) + conn.request("GET",'/phedex/datasvc/json/prod/requestlist?dataset='+dataset+'&type=xfer') + resp = conn.getresponse() + result = json.load(resp) + requests=result['phedex'] + if 'request' not in requests.keys(): + return [False, False] + ourNode = False + otherNode = False + for request in result['phedex']['request']: + for node in request['node']: + if node['name']==site and node['decision']=='approved': + ourNode = True + elif 'Disk' in node['name'] and node['decision']=='approved': + otherNode = True + return ourNode, otherNode + diff --git a/src/python/WMCore/ReqMgr/Tools/__init__.py b/src/python/WMCore/ReqMgr/Tools/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/python/WMCore/ReqMgr/Tools/cms.py b/src/python/WMCore/ReqMgr/Tools/cms.py new file mode 100644 index 0000000000..5eaf29d4a8 --- /dev/null +++ b/src/python/WMCore/ReqMgr/Tools/cms.py @@ -0,0 +1,172 @@ +from WMCore.RequestManager.RequestDB.Settings.RequestStatus import StatusList, NextStatus +from WMCore.Services.SiteDB.SiteDB import SiteDBJSON + +def next_status(status=None): + "Return next ReqMgr status for given status" + if status: + if status in NextStatus: + return NextStatus[status] + else: + return 'N/A' + return StatusList + +def sites(): + "Return known CMS site list from SiteDB" + try: + # Download a list of all the sites from SiteDB, uses v2 API. + sitedb = SiteDBJSON() + sites = sitedb.getAllCMSNames() + sites.sort() + except Exception as exc: + msg = "ERROR: Could not retrieve sites from SiteDB, reason: %s" % str(exc) + raise Exception(msg) + return sites + +def site_white_list(): + "site white list, default all T1" + t1_sites = [s for s in sites() if s.startswith('T1_')] + return t1_sites + +def site_black_list(): + "site black list, default all T3" + t3_sites = [s for s in sites() if s.startswith('T3_')] + return t3_sites + +def cust_sites(): + "Default custodial sites" + return ['T1_US_FNAL', 'T1_CH_CERN'] + +def non_cust_sites(): + "Default non-custodial sites, all T2" + t2_sites = [s for s in sites() if s.startswith('T2_')] + return t2_sites + +def auto_approve_sites(): + "Default custodial sites" + return ['T1_US_FNAL', 'T1_CH_CERN'] + +def lfn_bases(): + "Return LFN Base list" + storeResultLFNBase = [ + "/store/backfill/1", + "/store/backfill/2", + "/store/data", + "/store/mc", + "/store/generator", + "/store/relval", + "/store/hidata", + "/store/himc", + "/store/results/analysisops", + "/store/results/b_physics", + "/store/results/b_tagging", + "/store/results/b2g", + "/store/results/e_gamma_ecal", + "/store/results/ewk", + "/store/results/exotica", + "/store/results/forward", + "/store/results/heavy_ions", + "/store/results/higgs", + "/store/results/jets_met_hcal", + "/store/results/muon", + "/store/results/qcd", + "/store/results/susy", + "/store/results/tau_pflow", + "/store/results/top", + "/store/results/tracker_dpg", + "/store/results/tracker_pog", + "/store/results/trigger"] + return storeResultLFNBase + +def lfn_unmerged_bases(): + "Return list of LFN unmerged bases" + out = ["/store/data", "/store/temp"] + return out + +def web_ui_names(): + "Return dict of web UI JSON naming conventions" + maps = {"InputDataset": "Input Dataset", + "IncludeParents": "Include Parents", + "PrepID": "Optional Prep ID String", + "BlockBlacklist": "Block black list", + "RequestPriority": "Request Priority", + "TimePerEvent": "Time per event (Seconds)", + "RunWhitelist": "Run white list", + "BlockWhitelist": "Block white list", + "OpenRunningTimeout": "Open Running Timeout", + "DQLUploadURL": "DQM URL", + "DqmSequences": "DQM Sequences", + "SizePerEvent": "Size per event (KBytes)", + "ScramArch": "Architecture", + "EnableHarvesting": "Enable DQM Harvesting", + "DQMConfigCacheID": "DQM Config CacheID", + "Memory": "Memory (MBytes)", + "RunBlacklist": "Run black list", + "RequestString": "Optional Request ID String", + "CMSSWVersion": "Software Releases", + "DQMUploadURL":"DQM URL", + "DQMSequences": "DQM sequences", + "DataPileup": "Data Pileup", + "FilterEfficiency": "Filter Efficiency", + "GlobalTag": "Global tag", + "MCPileup": "MonteCarlo Pileup", + "PrimaryDataset": "Parimary dataset", + "Acquisitionera": "Acquisition era", + "CmsPath": "CMS path", + "DBS": "DBS urls", + "ProcessingVersion": "Processing version", + "RequestType": "Request type", + "ACDCDatabase": "ACDC database", + "ACDCServer": "ACDC server", + "CollectionName": "Collection name", + "IgnoredOutputModules": "Ignored output modules", + "InitialTaskPath": "Initial task path", + "KeepStepOneOutput": "Keep step one output", + "KeepStepTwoOutput": "Keep step two output", + "StepOneConfigCacheID": "Step one config cache id", + "StepOneOutputModuleName": "Step one output module name", + "StepThreeConfigCacheID": "Step three config cache id", + "StepTwoConfigCacheID": "Step two config cache id", + "StepTwoOutputModuleName": "Step two output module name", + } + return maps + +def dqm_urls(): + "Return list of DQM urls" + urls = [ + "https://cmsweb.cern.ch/dqm/dev", + "https://cmsweb.cern.ch/dqm/offline", + "https://cmsweb.cern.ch/dqm/relval", + "https://cmsweb-testbed.cern.ch/dqm/dev", + "https://cmsweb-testbed.cern.ch/dqm/offline", + "https://cmsweb-testbed.cern.ch/dqm/relval", + ] + return urls + +def dbs_urls(): + "Return list of DBS urls" + urls = [] + base = "https://cmsweb.cern.ch/dbs/prod/global/DBSReader/" + for inst in ["prod", "phys01", "phys02", "phys03"]: + urls.append(base.replace("prod", inst)) + return urls + +def releases(): + "Return list of CMSSW releases" + releases=["CMSSW_7_0_0", "CMSSW_6_8_1"] + return releases + +def architectures(): + "Return list of CMSSW architectures" + arch=["slc5_amd64_gcc472", "slc5_ad4_gcc481"] + return arch + +def scenarios(): + "Return list of scenarios" + slist = ["pp", "cosmics", "hcalnzs", "preprodmc", "prodmc"] + return slist + +def cms_groups(): + "Return list of CMS data-ops groups" + groups = ["DATAOPS"] + return groups + diff --git a/src/python/WMCore/ReqMgr/Tools/reqMgrClient.py b/src/python/WMCore/ReqMgr/Tools/reqMgrClient.py new file mode 100644 index 0000000000..defa0eaa54 --- /dev/null +++ b/src/python/WMCore/ReqMgr/Tools/reqMgrClient.py @@ -0,0 +1,339 @@ +#!/usr/bin/env python +""" + This client encapsulates several basic queries to request manager. + This uses ReqMgr rest api through HTTP + url parameter is normally 'cmsweb.cern.ch' +""" + +# system modules +import os +import re +import sys +import json +import urllib +import urllib2 +import httplib + +# default headers for PUT and POST methods +HEADERS={"Content-type": "application/x-www-form-urlencoded","Accept": "text/plain"} + +def requestManagerGet(url, request, retries=4): + """ + Queries ReqMgr through a HTTP GET method + in every request manager query + url: the instance used, i.e. url='cmsweb.cern.ch' + request: the request suffix url + retries: number of retries + """ + cert = os.getenv('X509_USER_PROXY') + ckey = os.getenv('X509_USER_PROXY') + for _ in range(retries): + conn = httplib.HTTPSConnection(url, cert_file=cert, key_file=ckey) + conn.request("GET", request) + resp = conn.getresponse() + request = json.load(resp) + if 'exception' not in request: + return request + raise Exception('Maximum queries to ReqMgr exceeded') + +def requestManagerPost(url, request, params, head = HEADERS): + """ + Performs some operation on ReqMgr through + an HTTP POST method. + url: the instance used, i.e. url='cmsweb.cern.ch' + request: the request suffix url for the POST method + params: a dict with the POST parameters + """ + conn = httplib.HTTPSConnection(url, cert_file = os.getenv('X509_USER_PROXY'), + key_file = os.getenv('X509_USER_PROXY')) + headers = head + encodedParams = urllib.urlencode(params) + conn.request("POST", request, encodedParams, headers) + response = conn.getresponse() + data = response.read() + conn.close() + return data + +def requestManagerPut(url, request, params, head = HEADERS): + """ + Performs some operation on ReqMgr through + an HTTP PUT method. + url: the instance used, i.e. url='cmsweb.cern.ch' + request: the request suffix url for the POST method + params: a dict with the PUT parameters + head: optional headers param. If not given it takes default value (HEADERS) + """ + conn = httplib.HTTPSConnection(url, cert_file = os.getenv('X509_USER_PROXY'), + key_file = os.getenv('X509_USER_PROXY')) + headers = head + encodedParams = urllib.urlencode(params) + conn.request("PUT", request, encodedParams, headers) + response = conn.getresponse() + data = response.read() + conn.close() + return data + +class WorkflowManager(object): + def __init__(self, workflow, url='cmsweb.cern.ch'): + "Request workflow information from ReqMgr and provide APIs for its attributes" + self.workflow = workflow + self.winfo = requestManagerGet(url,'/reqmgr/reqMgr/request?requestName='+workflow) + self.output_datasets = requestManagerGet(url,'/reqmgr/reqMgr/outputDatasetsByRequestName?requestName='+workflow) + + def request_status(self): + """Retrieves workflow status""" + return self.winfo.get('RequestStatus', '') + + def request_type(self): + return self.winfo.get('RequestType', '') + + def request_priority(self): + return self.winfo.get('RequestPriority', 0) + + def run_whitelist(self): + return self.winfo.get('RunWhiteList', []) + + def block_whitelist(self): + return self.winfo.get('BlockWhiteList', []) + + def input_dataset(self): + return self.winfo.get('InputDataset', '') + + def team(self): + """Retrieves the team on which the wf is assigned""" + return self.winfo.get('teams', ['NoTeam'])[0] + + def output_datasets(self): + """returns the output datasets for a given workfow""" + return self.output_datasets + + def input_events(self): + """ + Gets the inputs events of a given workflow + depending of the kind of workflow + """ + request = self.winfo + requestType = request['RequestType'] + #if request is montecarlo or Step0, the numer of + #input events is by the requsted events + if requestType == 'MonteCarlo' or requestType == 'LHEStepZero': + if 'RequestNumEvents' in request: + if request['RequestNumEvents']>0: + return request['RequestNumEvents'] + if 'RequestSizeEvents' in request: + return request['RequestSizeEvents'] + else: + return 0 + if requestType == 'TaskChain': + return handleTaskChain(request) + + #if request is not montecarlo, then we need to check the size + #of input datasets + #This loops fixes the white and blacklists in the workflow + #information, + for listitem in ["RunWhitelist", "RunBlacklist", + "BlockWhitelist", "BlockBlacklist"]: + if listitem in request: + #if empty + if request[listitem]=='[]' or request[listitem]=='': + request[listitem]=[] + #if there is not a list but some elements it creates a list + if type(request[listitem]) is not list: + # if doesn't contain "[" is a single block + if '[' not in request[listitem]: + #wrap in a list + request[listitem] = [request[listitem]] + #else parse a list + else: + request[listitem]= eval(request[listitem]) + #if not, an empty list will do + else: + request[listitem]=[] + + inputDataSet=request['InputDataset'] + + #it the request is rereco, we valiate white/black lists + if requestType=='ReReco': + # if there is block whte list, count only the selected block + if request['BlockWhitelist']: + events = dbs3.getEventCountDataSetBlockList(inputDataSet,request['BlockWhitelist']) + # if there is block black list, substract them from the total + if request['BlockBlacklist']: + events = (dbs3.getEventCountDataSet(inputDataSet) - + dbs3.getEventCountDataSet(inputDataSet,request['BlockBlacklist'])) + return events + # same if a run whitelist + if request['RunWhitelist']: + events = dbs3.getEventCountDataSetRunList(inputDataSet, request['RunWhitelist']) + return events + # otherwize, the full lumi count + else: + events = dbs3.getEventCountDataset(inputDataSet) + return events + + events = dbs3.getEventCountDataSet(inputDataSet) + # if black list, subsctract them + if request['BlockBlacklist']: + events=events-dbs3.getEventCountDataSetBlockList(inputDataSet, request['BlockBlacklist']) + # if white list, only the ones in the whitelist. + if request['RunWhitelist']: + events=dbs3.getEventCountDataSetRunList(inputDataSet, request['RunWhitelist']) + # if white list of blocks + if request['BlockWhitelist']: + events=dbs3.getEventCountDataSetBlockList(inputDataSet, request['BlockWhitelist']) + + if 'FilterEfficiency' in request: + return float(request['FilterEfficiency'])*events + else: + return events + +def getOutputEvents(dataset): + """ + Gets the output events depending on the type + if the request + """ + return dbs3.getEventCountDataSet(dataset) + +def closeOutWorkflow(url, workflowname): + """ + Closes out a workflow by changing the state to closed-out + This does not care about cascade workflows + """ + params = {"requestName" : workflowname,"status" : "closed-out"} + data = requestManagerPut(url,"/reqmgr/reqMgr/request", params) + return data + +def closeOutWorkflowCascade(url, workflowname): + """ + Closes out a workflow, it will search for any Resubmission requests + for which the given request is a parent and announce them too. + """ + params = {"requestName" : workflowname, "cascade" : True} + data = requestManagerPost(url,"/reqmgr/reqMgr/closeout", params) + return data + +def announceWorkflow(url, workflowname): + """ + Sets a workflow state to announced + This does not care about cascade workflows + """ + params = {"requestName" : workflowname,"status" : "announced"} + data = requestManagerPut(url,"/reqmgr/reqMgr/request", params) + return data + +def announceWorkflowCascade(url, workflowname): + """ + Sets a workflow state to announced, it will search for any Resubmission requests + for which the given request is a parent and announce them too. + """ + params = {"requestName" : workflowname, "cascade" : True} + data = requestManagerPost(url,"/reqmgr/reqMgr/announce", params) + return data + + +def setWorkflowApproved(url, workflowname): + """ + Sets a workflow state to assignment-approved + """ + params = {"requestName" : workflowname,"status" : "assignment-approved"} + data = requestManagerPut(url,"/reqmgr/reqMgr/request", params) + return data + +def setWorkflowRunning(url, workflowname): + """ + Sets a workflow state to running + """ + params = {"requestName" : workflowname,"status" : "running"} + data = requestManagerPut(url,"/reqmgr/reqMgr/request", params) + return data + +def rejectWorkflow(url, workflowname): + """ + Sets a workflow state to rejected + """ + params = {"requestName" : workflowname,"status" : "rejected"} + data = requestManagerPut(url,"/reqmgr/reqMgr/request", params) + return data + +def abortWorkflow(url, workflowname): + """ + Sets a workflow state to aborted + """ + params = {"requestName" : workflowname,"status" : "aborted"} + data = requestManagerPut(url,"/reqmgr/reqMgr/request", params) + return data + +def cloneWorkflow(url, workflowname): + """ + This clones a request + """ + headers={"Content-Length": 0} + params = {} + data = requestManagerPut(url,"/reqmgr/reqMgr/clone/", params, headers) + return data + +def submitWorkflow(url, schema): + """ + This submits a workflow into the ReqMgr, can be used for cloning + and resubmitting workflows + url: the instance ued, i.e. 'cmsweb.cern.ch' + schema: A dictionary with the parameters needed to create + the workflow + + """ + data = requestManagerPost(url,"/reqmgr/create/makeSchema", schema) + return data + +def handleTaskChain(request): + # Check if it's MC from scratch + if 'RequestNumEvents' in request['Task1']: + if request['Task1']['RequestNumEvents'] is not None: + return request['Task1']['RequestNumEvents'] + + blockWhitelist = blockBlacklist = runWhitelist = runBlacklist = [] + if 'InputDataset' in request['Task1']: + inputDataSet=request['Task1']['InputDataset'] + if 'BlockWhitelist' in request['Task1']: + blockWhitelist=request['Task1']['BlockWhitelist'] + if 'BlockBlacklist' in request['Task1']: + blockBlacklist=request['Task1']['BlockBlacklist'] + if 'RunWhitelist' in request['Task1']: + runWhitelist=request['Task1']['RunWhitelist'] + if 'RunBlacklist' in request['Task1']: + runBlacklist=request['Task1']['RunBlacklist'] + + if blockWhitelist: + return dbs3.getEventCountDataSetBlockList(inputDataSet,blockWhitelist) + if blockBlacklist: + return dbs3.getEventCountDataset(inputDataSet) - dbs3.getEventCountDataSetBlockList(inputDataSet,blockBlacklist) + if runWhitelist: + return dbs3.getEventCountDataSetRunList(inputDataSet, runWhitelist) + else: + return dbs3.getEventCountDataset(inputDataSet) + +### TODO: implement multi white/black list +# if len(blockWhitelist)>0 and len(runWhitelist)>0: +# print "Hey, you have block and run white list :-D" +# return getRunLumiCountDatasetBlockList(inputDataSet,BlockWhitelist) +# elif len(blockWhitelist)>0 and len(runWhitelist)==0: +# print "Hey, you have block white list but NOT run white list :-D" +# elif len(blockWhitelist)==0 and len(runWhitelist)>0: +# print "Hey, you have NO block white list but you do have run white list :-D" +# return getRunLumiCountDatasetList(inputDataSet, runWhitelist) +# elif len(blockWhitelist)==0 and len(runWhitelist)==0: +# print "Hey, you have NO block and run white list :-D" +# +# if len(BlockBlacklist)>0 and len(runBlacklist)>0: +# print "Hey, you have block and run black list :-(" +# return getRunLumiCountDataset(inputDataSet)-getRunLumiCountDatasetBlockList(inputDataSet,BlockBlacklist) +# elif len(BlockBlacklist)>0 and len(runBlacklist)==0: +# print "Hey, you have block black list but NOT run black list :-(" +# elif len(BlockBlacklist)==0 and len(runBlacklist)>0: +# print "Hey, you have NO block black list but you do have run black list :-(" +# elif len(BlockBlacklist)==0 and len(runBlacklist)==0: +# print "Hey, you have NO block and run black list :-(" + + + + + diff --git a/src/python/WMCore/ReqMgr/Utils/__init__.py b/src/python/WMCore/ReqMgr/Utils/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/python/WMCore/ReqMgr/Utils/jsonwrapper/.__init__.py.un~ b/src/python/WMCore/ReqMgr/Utils/jsonwrapper/.__init__.py.un~ new file mode 100644 index 0000000000000000000000000000000000000000..0f98b716813248917077b70c4b2d8059089abcbd GIT binary patch literal 5269 zcmeI0%WD%+7{$jL1F_b3)VdmiI2o*gBn?ed6ki(!7hPDFA&_dvnn}n^TT1An_zwuS zK9TOb>7U`og$OR)2qJFW=}zi7GxzG9G{llHyL;fwedW$1-*3Ljojd0=w(hyNnqQ5P z#>c|UyN%85_hSdm(O)w^esAq`E?@ifIayDoo;Xjx7>4l{E}BBl1JhhwskI+^X3edO zYHp$CRTt-1vzFIv`>c7EUBR?v_$+)JJ`Yo)FzZ2kc6VnYaR9BPQT>2|IuRDZkrR)? z3AT12gCEw4DFUmvi3El;NM+oN`Y?Rgmcx<0!TZl8qU=r8x z7=%~_c$ZXo3_X!NhokT~atz*tB|O49#kLSZ5`Q3_c`_0MmWQFd9AkN`etj@Uue43M_$v-)t4GAFWb)B6Or!6&05{d_WYnNl9hZ<^Xx)oPwOYG zjL0yh%2=(dyl<+!8TvEuOE{XW97)43V41A4k6jc&9u8_a=wy|V7_ej{Woq!hOV$Z5 zs3TeLsPIx4I0C9wJSxCv|j(nhzwJz z@W`ZBR^AWb9SRq(3_V{m-oR0K9O3eoSS38!$1aK>mqn=`sf_@i5l`Yk^wP0D{K11)#1;LgKgN+376Udot zGi>XH5Tg*Mz}D`^_G(CWFWZ0nMEiiOCNF;tffMp|HZ){LAx;5uxUY~5|5v*o%{TrJ P*>v2#TZN_dZ`*$Wb{Zfp literal 0 HcmV?d00001 diff --git a/src/python/WMCore/ReqMgr/Utils/jsonwrapper/__init__.py b/src/python/WMCore/ReqMgr/Utils/jsonwrapper/__init__.py new file mode 100644 index 0000000000..97115ee84e --- /dev/null +++ b/src/python/WMCore/ReqMgr/Utils/jsonwrapper/__init__.py @@ -0,0 +1,172 @@ +#!/usr/bin/env python +#-*- coding: ISO-8859-1 -*- + +""" +JSON wrapper around different JSON python implementations. +We use simplejson (json), cjson and yajl JSON implementation. + +NOTE: different JSON implementation handle floats in different way +Here are few examples + + +..doctest:: + + r1={"ts":time.time()} + print r1 + {'ts': 1374255843.891289} + +Python json: + +..doctest:: + + print json.dumps(r1), json.loads(json.dumps(r1)) + {"ts": 1374255843.891289} {u'ts': 1374255843.891289} + +CJSON: + +..doctest:: + print cjson.encode(r1), cjson.decode(cjson.encode(r1)) + {"ts": 1374255843.89} {'ts': 1374255843.89} + +YAJL: + +..doctest:: + + print yajl.dumps(r1), yajl.loads(yajl.dumps(r1)) + {"ts":1.37426e+09} {u'ts': 1374260000.0} + +Therefore when records contains timestamp it is ADVISED to round it to integer. +Then json/cjson implementations will agreee on input/output, while yajl will +still differ (for that reason we can't use yajl). +""" + +__author__ = "Valentin Kuznetsov " + +MODULE = None + +try: + import yajl + MODULE = "yajl" +except: + pass + +try: + import cjson + MODULE = "cjson" +except: + pass + +import json +if not MODULE: # use default JSON module + MODULE = "json" + +#print "### DAS uses %s JSON module" % MODULE + +def loads(idict, **kwargs): + """ + Based on default MODULE invoke appropriate JSON decoding API call + """ + if MODULE == 'json': + return json.loads(idict, **kwargs) + elif MODULE == 'cjson': + return cjson.decode(idict) + elif MODULE == 'yajl': + try: # yajl.loads("123") will fail + res = yajl.loads(idict) + except: # fall back into default python JSON + res = json.loads(idict, **kwargs) + return res + else: + raise Exception("Not support JSON module: %s" % MODULE) + +def load(source): + """ + Use json.load for back-ward compatibility, since cjson doesn't + provide this method. The load method works on file-descriptor + objects. + """ + if MODULE == 'json': + return json.load(source) + elif MODULE == 'cjson': + data = source.read() + return cjson.decode(data) + elif MODULE == 'yajl': + return yajl.load(source) + else: + raise Exception("Not support JSON module: %s" % MODULE) + +def dumps(idict, **kwargs): + """ + Based on default MODULE invoke appropriate JSON encoding API call + """ + if MODULE == 'json': + return json.dumps(idict, **kwargs) + elif MODULE == 'cjson': + return cjson.encode(idict) + elif MODULE == 'yajl': + return yajl.dumps(idict) + else: + raise Exception("JSON module %s is not supported" % MODULE) + +def dump(doc, source): + """ + Use json.dump for back-ward compatibility, since cjson doesn't + provide this method. The dump method works on file-descriptor + objects. + """ + if MODULE == 'json': + return json.dump(doc, source) + elif MODULE == 'cjson': + stj = cjson.encode(doc) + return source.write(stj) + elif MODULE == 'yajl': + return yajl.dump(doc, source) + else: + raise Exception("JSON module %s is not supported" % MODULE) + +class JSONEncoder(object): + """ + JSONEncoder wrapper + """ + def __init__(self, **kwargs): + self.encoder = json.JSONEncoder(**kwargs) + if kwargs and 'sort_keys' in kwargs: + self.module = 'default' + else: + self.module = MODULE + + def encode(self, idict): + """Decode JSON method""" + if self.module == 'cjson': + return cjson.encode(idict) + elif self.module == 'yajl': + return yajl.Encoder().encode(idict) + return self.encoder.encode(idict) + + def iterencode(self, idict): + "Encode input dict" + return self.encoder.iterencode(idict) + +class JSONDecoder(object): + """ + JSONDecoder wrapper + """ + def __init__(self, **kwargs): + self.decoder = json.JSONDecoder(**kwargs) + if kwargs: + self.module = 'default' + else: + self.module = MODULE + + def decode(self, istring): + """Decode JSON method""" + if MODULE == 'cjson': + return cjson.decode(istring) + elif MODULE == 'yajl': + return yajl.Decoder().decode(istring) + return self.decoder.decode(istring) + + def raw_decode(self, istring): + "Decode given string" + return self.decoder.raw_decode(istring) + diff --git a/src/python/WMCore/ReqMgr/Utils/jsonwrapper/__init__.py~ b/src/python/WMCore/ReqMgr/Utils/jsonwrapper/__init__.py~ new file mode 100644 index 0000000000..9c9cba0caa --- /dev/null +++ b/src/python/WMCore/ReqMgr/Utils/jsonwrapper/__init__.py~ @@ -0,0 +1,138 @@ +#!/usr/bin/env python +#-*- coding: ISO-8859-1 -*- + +""" +JSON wrapper around different JSON python implementations. +We use simplejson (json), cjson and yajl JSON implementation. +""" + +__author__ = "Valentin Kuznetsov " + +MODULE = None + +try: + import yajl + MODULE = "yajl" +except: + pass + +try: + import cjson + MODULE = "cjson" +except: + pass + +import json +if not MODULE: # use default JSON module + MODULE = "json" + +print "DAS uses %s JSON module" % MODULE + +def loads(idict, **kwargs): + """ + Based on default MODULE invoke appropriate JSON decoding API call + """ + if MODULE == 'json': + return json.loads(idict, **kwargs) + elif MODULE == 'cjson': + return cjson.decode(idict) + elif MODULE == 'yajl': + try: # yajl.loads("123") will fail + res = yajl.loads(idict) + except: # fall back into default python JSON + res = json.loads(idict, **kwargs) + return res + else: + raise Exception("Not support JSON module: %s" % MODULE) + +def load(source): + """ + Use json.load for back-ward compatibility, since cjson doesn't + provide this method. The load method works on file-descriptor + objects. + """ + if MODULE == 'json': + return json.load(source) + elif MODULE == 'cjson': + data = source.read() + return cjson.decode(data) + elif MODULE == 'yajl': + return yajl.load(source) + else: + raise Exception("Not support JSON module: %s" % MODULE) + +def dumps(idict, **kwargs): + """ + Based on default MODULE invoke appropriate JSON encoding API call + """ + if MODULE == 'json': + return json.dumps(idict, **kwargs) + elif MODULE == 'cjson': + return cjson.encode(idict) + elif MODULE == 'yajl': + return yajl.dumps(idict) + else: + raise Exception("JSON module %s is not supported" % MODULE) + +def dump(doc, source): + """ + Use json.dump for back-ward compatibility, since cjson doesn't + provide this method. The dump method works on file-descriptor + objects. + """ + if MODULE == 'json': + return json.dump(doc, source) + elif MODULE == 'cjson': + stj = cjson.encode(doc) + return source.write(stj) + elif MODULE == 'yajl': + return yajl.dump(doc, source) + else: + raise Exception("JSON module %s is not supported" % MODULE) + +class JSONEncoder(object): + """ + JSONEncoder wrapper + """ + def __init__(self, **kwargs): + self.encoder = json.JSONEncoder(**kwargs) + if kwargs and kwargs.has_key('sort_keys'): + self.module = 'default' + else: + self.module = MODULE + + def encode(self, idict): + """Decode JSON method""" + if self.module == 'cjson': + return cjson.encode(idict) + elif self.module == 'yajl': + return yajl.Encoder().encode(idict) + return self.encoder.encode(idict) + + def iterencode(self, idict): + "Encode input dict" + return self.encoder.iterencode(idict) + +class JSONDecoder(object): + """ + JSONDecoder wrapper + """ + def __init__(self, **kwargs): + self.decoder = json.JSONDecoder(**kwargs) + if kwargs: + self.module = 'default' + else: + self.module = MODULE + + def decode(self, istring): + """Decode JSON method""" + if MODULE == 'cjson': + return cjson.decode(istring) + elif MODULE == 'yajl': + return yajl.Decoder().decode(istring) + return self.decoder.decode(istring) + + def raw_decode(self, istring): + "Decode given string" + return self.decoder.raw_decode(istring) + diff --git a/src/python/WMCore/ReqMgr/Utils/url_utils.py b/src/python/WMCore/ReqMgr/Utils/url_utils.py new file mode 100644 index 0000000000..886c87567b --- /dev/null +++ b/src/python/WMCore/ReqMgr/Utils/url_utils.py @@ -0,0 +1,144 @@ +#!/usr/bin/env python +#-*- coding: utf-8 -*- +#pylint: disable= +""" +File : url_utils.py +Author : Valentin Kuznetsov +Description: +""" + +# system modules +import os +import time +import urllib +import urllib2 +import httplib +import WMCore.ReqMgr.Utils.jsonwrapper as json + +def get_key_cert(): + """ + Get user key/certificate + """ + key = None + cert = None + globus_key = os.path.join(os.environ['HOME'], '.globus/userkey.pem') + globus_cert = os.path.join(os.environ['HOME'], '.globus/usercert.pem') + if os.path.isfile(globus_key): + key = globus_key + if os.path.isfile(globus_cert): + cert = globus_cert + + # First presendence to HOST Certificate, RARE + if 'X509_HOST_CERT' in os.environ: + cert = os.environ['X509_HOST_CERT'] + key = os.environ['X509_HOST_KEY'] + + # Second preference to User Proxy, very common + elif 'X509_USER_PROXY' in os.environ: + cert = os.environ['X509_USER_PROXY'] + key = cert + + # Third preference to User Cert/Proxy combinition + elif 'X509_USER_CERT' in os.environ: + cert = os.environ['X509_USER_CERT'] + key = os.environ['X509_USER_KEY'] + + # Worst case, look for cert at default location /tmp/x509up_u$uid + elif not key or not cert: + uid = os.getuid() + cert = '/tmp/x509up_u'+str(uid) + key = cert + + if not os.path.exists(cert): + raise Exception("Certificate PEM file %s not found" % key) + if not os.path.exists(key): + raise Exception("Key PEM file %s not found" % key) + + return key, cert + +def disable_urllib2Proxy(): + """ + Setup once and forever urllib2 proxy, see + http://kember.net/articles/obscure-python-urllib2-proxy-gotcha + """ + proxy_support = urllib2.ProxyHandler({}) + opener = urllib2.build_opener(proxy_support) + urllib2.install_opener(opener) + +class HTTPSClientAuthHandler(urllib2.HTTPSHandler): + """ + Simple HTTPS client authentication class based on provided + key/ca information + """ + def __init__(self, key=None, cert=None, level=0): + if level > 1: + urllib2.HTTPSHandler.__init__(self, debuglevel=1) + else: + urllib2.HTTPSHandler.__init__(self) + self.key = key + self.cert = cert + + def https_open(self, req): + """Open request method""" + #Rather than pass in a reference to a connection class, we pass in + # a reference to a function which, for all intents and purposes, + # will behave as a constructor + return self.do_open(self.get_connection, req) + + def get_connection(self, host, timeout=300): + """Connection method""" + if self.key: + return httplib.HTTPSConnection(host, key_file=self.key, + cert_file=self.cert) + return httplib.HTTPSConnection(host) + +def getdata(url, params, headers=None, post=None, verbose=False): + """ + Invoke URL call and retrieve data from data-service based + on provided URL and set of parameters. Use post=True to + invoke POST request. + """ + encoded_data = urllib.urlencode(params) + if not post: + url = url + '?' + encoded_data + if not headers: + headers = {} + if verbose: + print '+++ getdata, url=%s, headers=%s' % (url, headers) + req = urllib2.Request(url) + for key, val in headers.iteritems(): + req.add_header(key, val) + if verbose > 1: + handler = urllib2.HTTPHandler(debuglevel=1) + opener = urllib2.build_opener(handler) + urllib2.install_opener(opener) + ckey, cert = get_key_cert() + handler = HTTPSClientAuthHandler(ckey, cert, verbose) + opener = urllib2.build_opener(handler) + urllib2.install_opener(opener) + try: + if post: + data = urllib2.urlopen(req, encoded_data) + else: + data = urllib2.urlopen(req) + info = data.info() + code = data.getcode() + if verbose > 1: + print "+++ response code:", code + print "+++ response info\n", info + data = json.load(data) + except urllib2.HTTPError as httperror: + msg = 'HTTPError, url=%s, args=%s, headers=%s' \ + % (url, params, headers) + data = {'error': 'Unable to contact %s' % url , 'reason': msg} + try: + data.update({'httperror':extract_http_error(httperror.read())}) + except Exception as exp: + data.update({'httperror': None}) + data = json.dumps(data) + except Exception as exp: + msg = 'HTTPError, url=%s, args=%s, headers=%s, error=%s' \ + % (url, params, headers, str(exp)) + data = {'error': 'Unable to contact %s' % url, 'reason': msg} + data = json.dumps(data) + return data diff --git a/src/python/WMCore/ReqMgr/Web/ReqMgrService.py b/src/python/WMCore/ReqMgr/Web/ReqMgrService.py new file mode 100644 index 0000000000..6352dfb8a9 --- /dev/null +++ b/src/python/WMCore/ReqMgr/Web/ReqMgrService.py @@ -0,0 +1,622 @@ +#!/usr/bin/env python +#-*- coding: ISO-8859-1 -*- + +""" +web server. +""" + +__author__ = "Valentin Kuznetsov" + +# system modules +import os +import sys +import time +import json +import pprint +try: + import cStringIO as StringIO +except: + import StringIO + +# cherrypy modules +import cherrypy +from cherrypy import expose, response, tools +from cherrypy.lib.static import serve_file +from cherrypy import config as cherryconf + +# ReqMgrSrv modules +from WMCore.ReqMgr.Web.tools import exposecss, exposejs, exposejson, TemplatedPage +from WMCore.ReqMgr.Web.utils import json2table, genid, checkargs, tstamp +from WMCore.ReqMgr.Utils.url_utils import getdata +from WMCore.ReqMgr.Tools.cms import dqm_urls, dbs_urls, releases, architectures +from WMCore.ReqMgr.Tools.cms import scenarios, cms_groups +from WMCore.ReqMgr.Tools.cms import web_ui_names, next_status, sites +from WMCore.ReqMgr.Tools.cms import lfn_bases, lfn_unmerged_bases +from WMCore.ReqMgr.Tools.cms import site_white_list, site_black_list +from WMCore.ReqMgr.Tools.cms import cust_sites, non_cust_sites, auto_approve_sites + +# WMCore modules +from WMCore.WMSpec.WMWorkloadTools import loadSpecByType +from WMCore.WMSpec.WMWorkload import WMWorkloadHelper +from WMCore.ReqMgr.Service.Auxiliary import Info, Group, Team, Software +from WMCore.ReqMgr.Service.Request import Request +from WMCore.ReqMgr.Service.RestApiHub import RestApiHub +from WMCore.REST.Main import RESTMain +from WMCore.REST.Auth import authz_fake + +def set_headers(itype, size=0): + """ + Set response header Content-type (itype) and Content-Length (size). + """ + if size > 0: + response.headers['Content-Length'] = size + response.headers['Content-Type'] = itype + response.headers['Expires'] = 'Sat, 14 Oct 2027 00:59:30 GMT' + +def set_no_cache_flags(): + "Set cherrypy flags to prevent caching" + cherrypy.response.headers['Cache-Control'] = 'no-cache' + cherrypy.response.headers['Pragma'] = 'no-cache' + cherrypy.response.headers['Expires'] = 'Sat, 01 Dec 2001 00:00:00 GMT' + +def set_cache_flags(): + "Set cherrypy flags to prevent caching" + headers = cherrypy.response.headers + for key in ['Cache-Control', 'Pragma']: + if key in headers: + del headers[key] + +def minify(content): + """ + Remove whitespace in provided content. + """ + content = content.replace('\n', ' ') + content = content.replace('\t', ' ') + content = content.replace(' ', ' ') + content = content.replace(' ', ' ') + return content + +def menus(active='search'): + "Return dict of menus" + items = ['admin', 'assign', 'approve', 'create', 'requests'] + mdict = dict(zip(items, ['']*len(items))) + mdict[active] = 'active' + return mdict + +def request_attr(doc, attrs=None): + "Return request attributes/values in separate document" + if not attrs: + attrs = ['RequestName', 'Requestdate', 'Inputdataset', \ + 'Prepid', 'Group', 'Requestor', 'RequestDate', \ + 'RequestStatus'] + rdict = {} + for key in attrs: + if key in doc: + if key=='RequestDate': + tval = doc[key] + if isinstance(tval, list): + while len(tval) < 9: + tval.append(0) + gmt = time.gmtime(time.mktime(tval)) + rdict[key] = time.strftime("%Y-%m-%d %H:%M:%S GMT", gmt) + else: + rdictp[key] = tval + else: + rdict[key] = doc[key] + return rdict + +class ActionMgr(object): + def __init__(self, reqmgr): + "Action manager" + self.reqmgr = reqmgr + self.cache = {} # cache which keeps actions, TODO make it persistent + self.specs = {} + + def create(self, req): + """ + Create action: + create new request and send it to Requset Manager via POST method + """ + self.add_request('create', req) + if isinstance(req, dict): + docs = [req] + elif isinstnace(req, list): + docs = req + else: + raise Exception('Unsupported request type') + for jsondata in docs: + data = StringIO.StringIO(json.dumps(jsondata)) + cherrypy.request.body = data + print "\n### CALL reqmgr.post()" +# self.reqmgr.post() + + def validate(self, req): + """ + Validate action: + """ + self.add_request('validate', req) + + def approve(self, req): + """ + Approve action + should get list of requests to approve via Request::get(status) + and change request status from assign-approve to assigned + """ + self.add_request('approve', req) + status = req.get('status', '') + if status == 'assign-approve': + new_status = 'assigned' + docs = self.get_requests(req) + spec = loadSpecByType(docs[0]["RequestType"]) + workloads = spec.factoryWorkloadConstruction4docs(docs) + for workload in workloads: + self.reqmgr.put(workload, {'RequestStatus': new_status}) + + def assign(self, req): + """ + Assign action + should get list of requests to assign via Request::get(status) + and change request status from new to assigned + """ + self.add_request('assign', req) + new_status = 'assign-approve' + docs = self.get_requests(req) + statuses = list(set(d['RequestStatus'] for d in docs)) + if len(statuses) == 1 and statuses[0] == 'new': + spec = loadSpecByType(docs[0]["RequestType"]) + workloads = spec.factoryWorkloadConstruction4docs(docs) + for workload in workloads: + try: + workload.updateArguments(req) + self.reqmgr.put(workload, {'RequestStatus': new_status}) + except Exception as exp: + print "Fail to upgrade/put workload document" + print str(exp) +# workload.saveCouch(self.reqmgr.config.couch_host, self.reqmgr.config.couch_reqmgr_db) + + def add_request(self, action, req): + """ + Add request to internal cache + """ + print "\n### %s\n%s" % (action.upper(), pprint.pformat(req)) + + def get_requests(self, doc): + "Extract requests from given documents" + docs = [] + for key in doc.keys(): + if key.startswith('request'): + rid = key.split('request-')[-1] + if rid != 'all': + docs.append(self.reqmgr.reqmgr_db.document(rid)) + del doc[key] + return docs + + def actions(self): + "Return list of actions from the cache" + return self.cache + +class ReqMgrService(TemplatedPage): + """ + Request Manager web service class + """ + def __init__(self, config=None): + print "\n### Configuration:" + print config + self.base = config.base + if config and not isinstance(config, dict): + web_config = config.dictionary_() + if not config: + web_config = {'base': self.base} + pprint.pprint(web_config) + TemplatedPage.__init__(self, web_config) + imgdir = os.environ.get('RM_IMAGESPATH', os.getcwd()+'/images') + self.imgdir = web_config.get('imgdir', imgdir) + cssdir = os.environ.get('RM_CSSPATH', os.getcwd()+'/css') + self.cssdir = web_config.get('cssdir', cssdir) + jsdir = os.environ.get('RM_JSPATH', os.getcwd()+'/js') + self.jsdir = web_config.get('jsdir', jsdir) + yuidir = os.environ.get('YUI_ROOT', os.getcwd()+'/yui') + self.yuidir = web_config.get('yuidir', yuidir) + + # To be filled at run time + self.cssmap = {} + self.jsmap = {} + self.imgmap = {} + self.yuimap = {} + + # Update CherryPy configuration + mime_types = ['text/css'] + mime_types += ['application/javascript', 'text/javascript', + 'application/x-javascript', 'text/x-javascript'] + cherryconf.update({'tools.encode.on': True, + 'tools.gzip.on': True, + 'tools.gzip.mime_types': mime_types, + }) + self._cache = {} + + # TODO: + # for time being use development environment + # this part will be removed in production when we enable + # proper authentication + cherrypy.server.environment = 'development' + cherrypy.server.socket_host = '127.0.0.1' + authz_fake() + + # initialize rest API + statedir = '/tmp' + app = RESTMain(config, statedir) # REST application + mount = '/rest' # mount point for cherrypy service + api = RestApiHub(app, config.reqmgr, mount) + self.reqmgr = Request(app, api, config.reqmgr, mount=mount+'/reqmgr') + + # admin helpers + self.admin_info = Info(app, api, config.reqmgr, mount=mount+'/info') + self.admin_group = Group(app, api, config.reqmgr, mount=mount+'/group') + self.admin_team = Team(app, api, config.reqmgr, mount=mount+'/team') + + # action manager (will be replaced with appropriate class + self.actionmgr = ActionMgr(self.reqmgr) + + def user(self): + """ + Return user name associated with this instance. + This method should implement fetching user parameters through passed DN + """ + return 'testuser' + + def user_dn(self): + """ + Return user DN. + This method should implement fetching user DN + """ + return '/CN/bla/foo/' + + def abs_page(self, tmpl, content): + """generate abstract page""" + menu = self.templatepage('menu', menus=menus(tmpl)) + if tmpl == 'main': + body = self.templatepage('generic', menu=menu, content=content) + page = self.templatepage('main', content=body, user=self.user()) + else: + body = self.templatepage(tmpl, menu=menu, content=content) + page = self.templatepage('main', content=body, user=self.user()) + return page + + def page(self, content): + """ + Provide page wrapped with top/bottom templates. + """ + return self.templatepage('main', content=content) + + def error(self, content): + "Generate common error page" + content = self.templatepage('error', content=content) + return self.abs_page('generic', content) + + @expose + def index(self, **kwds): + """Main page""" + apis = {} + scripts = {} + for idx in range(5): + key = 'api_%s' % idx + val = '%s description' % key + apis[key] = val + skey = 'script_%s' % idx + sval = '%s description' % skey + scripts[skey] = sval + content = self.templatepage('apis', apis=apis, scripts=scripts) + return self.abs_page('generic', content) + + ### Admin actions ### + + @expose + def admin(self, **kwds): + """admin page""" + print "\n### ADMIN PAGE" + authz_fake() + rows = self.admin_info.get() + print "rows", [r for r in rows] + + content = self.templatepage('admin') + return self.abs_page('generic', content) + + @expose + def add_user(self, **kwds): + """add_user action""" + rid = genid(kwds) + content = self.templatepage('confirm', ticket=rid, user=self.user()) + return self.abs_page('generic', content) + + @expose + def add_group(self, **kwds): + """add_group action""" + rows = self.admin_group.get() + print "\n### GROUPS", [r for r in rows] + rid = genid(kwds) + content = self.templatepage('confirm', ticket=rid, user=self.user()) + return self.abs_page('generic', content) + + @expose + def add_team(self, **kwds): + """add_team action""" + rows = self.admin_team.get() + print "\n### TEAMS", kwds, [r for r in rows] + print "request to add", kwds + rid = genid(kwds) + content = self.templatepage('confirm', ticket=rid, user=self.user()) + return self.abs_page('generic', content) + + ### Request actions ### + + @expose + @checkargs(['status']) + def assign(self, **kwds): + """assign page""" + if not kwds: + kwds = {'status': 'new'} + docs = [] + attrs = ['RequestName', 'RequestDate', 'Group', 'Requestor', 'RequestStatus'] + for rid in self.reqmgr.get(**kwds): + if isinstance(rid, basestring): + doc = self.doc(rid) + docs.append(request_attr(doc, attrs)) + elif isinstance(rid, dict): + for key, val in rid.items(): + docs.append(request_attr(val, attrs)) + else: + raise Exception('Wrong rid=%s' % rid) + content = self.templatepage('assign', + site_white_list=site_white_list(), + site_black_list=site_black_list(), + cust_sites=cust_sites(), non_cust_sites=non_cust_sites(), + auto_approve_sites=auto_approve_sites(), + user=self.user(), user_dn=self.user_dn(), requests=docs, + cmssw_versions=releases(), scram_arch=architectures(), + sites=sites(), lfn_bases=lfn_bases(), + lfn_unmerged_bases=lfn_unmerged_bases()) + return self.abs_page('generic', content) + + @expose + @checkargs(['status']) + def approve(self, **kwds): + """ + Approve page: get list of request associated with user DN. + Fetch their status list from ReqMgr and display if requests + were seen by data-ops. + """ + if not kwds: + kwds = {'status': 'assign-approve'} + kwds.update({'_nostale':True}) + docs = [] + attrs = ['RequestName', 'RequestDate', 'Group', 'Requestor', 'RequestStatus'] + for rid in self.reqmgr.get(**kwds): + if isinstance(rid, basestring): + doc = self.doc(rid) + docs.append(request_attr(doc, attrs)) + elif isinstance(rid, dict): + for key, val in rid.items(): + docs.append(request_attr(val, attrs)) + else: + raise Exception('Wrong rid=%s' % rid) + content = self.templatepage('approve', requests=docs, date=tstamp()) + return self.abs_page('generic', content) + + @expose + def ajax_approve(self, ids, **kwds): + """ + AJAX approve action. It creates request dictionary and pass it to + action manager approve method. + """ + req = {} + if isinstance(ids, list): + for rid in ids: + req['request-%s'%rid] = 'on' + elif isinstance(ids, basestring): + req['request-%s'%ids] = 'on' + else: + raise NotImplemented + req['status'] = 'assign-approve' + self.actionmgr.approve(req) + + @expose + def create(self, **kwds): + """create page""" + req_form = kwds.get('form', 'rereco') + fname = 'json/%s' % str(req_form) + # request form + jsondata = self.templatepage(fname, + user=json.dumps(self.user()), + groups=json.dumps(cms_groups()), + releases=json.dumps(releases()), + arch=json.dumps(architectures()), + scenarios=json.dumps(scenarios()), + dqm_urls=json.dumps(dqm_urls()), + dbs_urls=json.dumps(dbs_urls()), + ) + try: + jsondata = json.loads(jsondata) + except Exception as exp: + msg = '
Fail to load JSON for %s workflow
\n' % req_form + msg += '
Error: %s
\n' % str(exp) + msg += '
JSON: %s
' % jsondata + return self.error(msg) + + # create templatized page out of provided forms + content = self.templatepage('create', table=json2table(jsondata, web_ui_names()), + jsondata=json.dumps(jsondata, indent=2), name=req_form) + return self.abs_page('generic', content) + + @expose + def confirm_action(self, **kwds): + """ + Confirm action method is called from web UI forms. It grabs input parameters + and passed them to Action manager. + """ + print "\n### confirm_action", kwds + try: + action = kwds.pop('action') + getattr(self.actionmgr, action)(kwds) + rid = genid(kwds) + print "called action", action, rid + content = self.templatepage('confirm', ticket=rid, user=self.user()) + return self.abs_page('generic', content) + except: + msg = '
No action specified
' + self.error(msg) + + @expose + def generate_objs(self, **kwargs): + """create page interface: generate objects from givem JSON template""" + jsondict = json.loads(kwargs.get('jsondict')) + code = kwargs.get('code') + if code.find('def genobjs(jsondict)') == -1: + return self.error("Improper python snippet, your code should start with def genobjs(jsondict) function") + exec(code) # code snippet must starts with genobjs function + objs = genobjs(jsondict) + rids = [] + for iobj in objs: + print "### generate JSON" + print iobj + rids.append(genid(iobj)) + content = self.templatepage('confirm', ticket=rids, user=self.user()) + return self.abs_page('generic', content) + + @exposejson + def fetch(self, rid): + "Fetch document for given id" + return self.reqmgr.reqmgr_db.document(rid) + + def doc(self, rid): + "Fetch document for given id" + return self.reqmgr.reqmgr_db.document(rid) + + @expose + def requests(self, **kwds): + """Check status of requests""" + if not kwds: + kwds = {'status':'acquired'} + results = self.reqmgr.get(**kwds) + requests = [] + for rid in results: + if isinstance(rid, basestring): + doc = self.doc(rid) + elif isinstance(rid, dict): + doc = rid[rid.keys()[0]] # new reqmgr2 returns {'rid':{doc}} + else: + raise Exception('Wrong rid=%s' % rid) + requests.append(request_attr(doc)) + content = self.templatepage('requests', requests=requests) + return self.abs_page('generic', content) + + @expose + def request(self, **kwargs): + "Get data example and expose it as json" + dataset = kwargs.get('uinput', '') + if not dataset: + return {'error':'no input dataset'} + url = 'https://cmsweb.cern.ch/reqmgr/rest/outputdataset/%s' % dataset + params = {} + headers = {'Accept': 'application/json;text/json'} + wdata = getdata(url, params) + wdict = dict(date=time.ctime(), team='Team-A', status='Running', ID=genid(wdata)) + winfo = self.templatepage('workflow', wdict=wdict, + dataset=dataset, code=pprint.pformat(wdata)) + content = self.templatepage('search', content=winfo) + return self.abs_page('generic', content) + + ### Aux methods ### + + @expose + def images(self, *args, **kwargs): + """ + Serve static images. + """ + args = list(args) + self.check_scripts(args, self.imgmap, self.imgdir) + mime_types = ['*/*', 'image/gif', 'image/png', + 'image/jpg', 'image/jpeg'] + accepts = cherrypy.request.headers.elements('Accept') + for accept in accepts: + if accept.value in mime_types and len(args) == 1 \ + and args[0] in self.imgmap: + image = self.imgmap[args[0]] + # use image extension to pass correct content type + ctype = 'image/%s' % image.split('.')[-1] + cherrypy.response.headers['Content-type'] = ctype + return serve_file(image, content_type=ctype) + + def serve(self, kwds, imap, idir, datatype='', minimize=False): + "Serve files for high level APIs (yui/css/js)" + args = [] + for key, val in kwds.items(): + if key == 'f': # we only look-up files from given kwds dict + if isinstance(val, list): + args += val + else: + args.append(val) + scripts = self.check_scripts(args, imap, idir) + return self.serve_files(args, scripts, imap, datatype, minimize) + + @exposecss + @tools.gzip() + def css(self, **kwargs): + """ + Serve provided CSS files. They can be passed as + f=file1.css&f=file2.css + """ + resource = kwargs.get('resource', 'css') + if resource == 'css': + return self.serve(kwargs, self.cssmap, self.cssdir, 'css', True) + elif resource == 'yui': + return self.serve(kwargs, self.yuimap, self.yuidir) + + @exposejs + @tools.gzip() + def js(self, **kwargs): + """ + Serve provided JS scripts. They can be passed as + f=file1.js&f=file2.js with optional resource parameter + to speficy type of JS files, e.g. resource=yui. + """ + resource = kwargs.get('resource', 'js') + if resource == 'js': + return self.serve(kwargs, self.jsmap, self.jsdir) + elif resource == 'yui': + return self.serve(kwargs, self.yuimap, self.yuidir) + + def serve_files(self, args, scripts, resource, datatype='', minimize=False): + """ + Return asked set of files for JS, YUI, CSS. + """ + idx = "-".join(scripts) + if idx not in self._cache.keys(): + data = '' + if datatype == 'css': + data = '@CHARSET "UTF-8";' + for script in args: + path = os.path.join(sys.path[0], resource[script]) + path = os.path.normpath(path) + ifile = open(path) + data = "\n".join ([data, ifile.read().\ + replace('@CHARSET "UTF-8";', '')]) + ifile.close() + if datatype == 'css': + set_headers("text/css") + if minimize: + self._cache[idx] = minify(data) + else: + self._cache[idx] = data + return self._cache[idx] + + def check_scripts(self, scripts, resource, path): + """ + Check a script is known to the resource map + and that the script actually exists + """ + for script in scripts: + if script not in resource.keys(): + spath = os.path.normpath(os.path.join(path, script)) + if os.path.isfile(spath): + resource.update({script: spath}) + return scripts diff --git a/src/python/WMCore/ReqMgr/Web/__init__.py b/src/python/WMCore/ReqMgr/Web/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/python/WMCore/ReqMgr/Web/tools.py b/src/python/WMCore/ReqMgr/Web/tools.py new file mode 100644 index 0000000000..04d80f76d5 --- /dev/null +++ b/src/python/WMCore/ReqMgr/Web/tools.py @@ -0,0 +1,185 @@ +#!/usr/bin/env python +#-*- coding: ISO-8859-1 -*- + +""" +Web tools. +""" + +__revision__ = "$Id: tools.py,v 1.5 2010/04/07 18:19:31 valya Exp $" +__author__ = "Valentin Kuznetsov" +__email__ = "vkuznet@gmail.com" + +# system modules +import os +import types +import logging + +from datetime import datetime, timedelta +from time import mktime +from wsgiref.handlers import format_date_time + +# cherrypy modules +import cherrypy +from cherrypy import log as cplog +from cherrypy import expose + +# cheetag modules +from Cheetah.Template import Template +from Cheetah import Version + +import WMCore.ReqMgr.Utils.jsonwrapper as json +from json import JSONEncoder + +class Page(object): + """ + __Page__ + + Page is a base class that holds a configuration + """ + def __init__(self): + self.name = "Page" + + def warning(self, msg): + """Define warning log""" + if msg: + self.log(msg, logging.WARNING) + + def exception(self, msg): + """Define exception log""" + if msg: + self.log(msg, logging.ERROR) + + def debug(self, msg): + """Define debug log""" + if msg: + self.log(msg, logging.DEBUG) + + def info(self, msg): + """Define info log""" + if msg: + self.log(msg, logging.INFO) + + def log(self, msg, severity): + """Define log level""" + if type(msg) != str: + msg = str(msg) + if msg: + cplog(msg, context=self.name, + severity=severity, traceback=False) + +class TemplatedPage(Page): + """ + TemplatedPage is a class that provides simple Cheetah templating + """ + def __init__(self, config): + Page.__init__(self) + tmpldir = os.environ.get('RM_TMPLPATH', os.getcwd()+'/templates') + self.templatedir = config.get('tmpldir', tmpldir) + self.name = "TemplatedPage" + self.base = config.get('base', '') + verbose = config.get('verbose', 0) + if verbose: + self.info("Templates are located in: %s" % self.templatedir) + self.info("Using Cheetah version: %s" % Version) + + def templatepage(self, ifile=None, *args, **kwargs): + """ + Template page method. + """ + search_list = [{'base':self.base}] + if len(args) > 0: + search_list.append(args) + if len(kwargs) > 0: + search_list.append(kwargs) + templatefile = os.path.join(self.templatedir, ifile + '.tmpl') + if os.path.exists(templatefile): + # little workaround to fix '#include' + search_list.append({'templatedir': self.templatedir}) + template = Template(file=templatefile, searchList=search_list) + return template.respond() + + else: + self.warning("%s not found at %s" % (ifile, self.templatedir)) + return "Template %s not known" % ifile + +def exposetext (func): + """CherryPy expose Text decorator""" + @expose + def wrapper (self, *args, **kwds): + """Decorator wrapper""" + data = func (self, *args, **kwds) + cherrypy.response.headers['Content-Type'] = "text/plain" + return data + return wrapper + +def jsonstreamer(func): + """JSON streamer decorator""" + def wrapper (self, *args, **kwds): + """Decorator wrapper""" + cherrypy.response.headers['Content-Type'] = "application/json" + func._cp_config = {'response.stream': True} + head, data = func (self, *args, **kwds) + yield json.dumps(head)[:-1] # do not yield } + yield ', "data": [' + if isinstance(data, dict): + for chunk in JSONEncoder().iterencode(data): + yield chunk + elif isinstance(data, list) or isinstance(data, types.GeneratorType): + sep = '' + for rec in data: + if sep: + yield sep + for chunk in JSONEncoder().iterencode(rec): + yield chunk + if not sep: + sep = ', ' + else: + msg = 'jsonstreamer, improper data type %s' % type(data) + raise Exception(msg) + yield ']}' + return wrapper + +def exposejson (func): + """CherryPy expose JSON decorator""" + @expose + def wrapper (self, *args, **kwds): + """Decorator wrapper""" + encoder = JSONEncoder() + data = func (self, *args, **kwds) + cherrypy.response.headers['Content-Type'] = "text/json" + try: + jsondata = encoder.encode(data) + return jsondata + except: + Exception("Fail to JSONtify obj '%s' type '%s'" \ + % (data, type(data))) + return wrapper + +def exposejs (func): + """CherryPy expose JavaScript decorator""" + @expose + def wrapper (self, *args, **kwds): + """Decorator wrapper""" + data = func (self, *args, **kwds) + cherrypy.response.headers['Content-Type'] = "text/javascript" + return data + return wrapper + +def exposecss (func): + """CherryPy expose CSS decorator""" + @expose + def wrapper (self, *args, **kwds): + """Decorator wrapper""" + data = func (self, *args, **kwds) + cherrypy.response.headers['Content-Type'] = "text/css" + return data + return wrapper + +def make_timestamp(seconds=0): + """Create timestamp""" + then = datetime.now() + timedelta(seconds=seconds) + return mktime(then.timetuple()) + +def make_rfc_timestamp(seconds=0): + """Create RFC timestamp""" + return format_date_time(make_timestamp(seconds)) diff --git a/src/python/WMCore/ReqMgr/Web/utils.py b/src/python/WMCore/ReqMgr/Web/utils.py new file mode 100644 index 0000000000..ed6399eeb0 --- /dev/null +++ b/src/python/WMCore/ReqMgr/Web/utils.py @@ -0,0 +1,162 @@ +#!/usr/bin/env python +#-*- coding: utf-8 -*- +#pylint: disable= +""" +File : utils.py +Author : Valentin Kuznetsov +Description: +""" + +# system modules +import cgi +import json +import time +import hashlib +import cherrypy + +def tstamp(): + "Generic time stamp" + return time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime()) + +def gen_color(val): + "Generate unique color code for given string value" + keyhash = hashlib.md5() + keyhash.update(val) + col = '#%s' % keyhash.hexdigest()[:6] + return col + +def quote(data): + """ + Sanitize the data using cgi.escape. + """ + if isinstance(data, int) or isinstance(data, float): + res = data + elif isinstance(data, dict): + res = data + elif isinstance(data, list): + res = data + elif isinstance(data, long) or isinstance(data, int) or\ + isinstance(data, float): + res = data + else: + try: + if data: + res = cgi.escape(data, quote=True) + else: + res = "" + except Exception as exc: + print_exc(exc) + print "Unable to cgi.escape(%s, quote=True)" % data + res = "" + return res + +def json2table(jsondata, web_ui_map): + """ + Convert input json dict into HTML table based on assumtion that + input json is in a simple key:value form. + """ + table = """\n""" + table += "\n" + keys = sorted(jsondata.keys()) + for key in keys: + val = jsondata[key] + if isinstance(val, list): + sel = "" + val = sel + elif isinstance(val, basestring): + if len(val) < 80: + val = '' % (key, val) + else: + val = '' % (key, val) + else: + val = '' % (key, val) + if key in web_ui_map: + kname = web_ui_map[key] + else: + kname = key.capitalize().replace('_', ' ') + table += "\n" % (kname, val) + table += "
FieldValue
%s%s
" + return table + +def genid(kwds): + "Generate id for given field" + if isinstance(kwds, dict): + record = dict(kwds) + data = json.JSONEncoder(sort_keys=True).encode(record) + else: + data = str(kwds) + keyhash = hashlib.md5() + keyhash.update(data) + return keyhash.hexdigest() + +def checkarg(kwds, arg): + """Check arg in a dict that it has str/unicode type""" + data = kwds.get(arg, None) + cond = data and (isinstance(data, str) or isinstance(data, unicode)) + return cond + +def checkargs(supported): + """ + Decorator to check arguments in provided supported list + """ + def wrap(func): + """Wrap input function""" + + def require_string(val): + """Check that provided input is a string""" + if not (isinstance(val, str) or isinstance(val, unicode)): + code = web_code('Invalid input') + raise HTTPError(500, 'DAS error, code=%s' % code) + + def wrapped_f(self, *args, **kwds): + """Wrap function arguments""" + # check request headers. For methods POST/PUT + # we need to read request body to get parameters + if cherrypy.request.method == 'POST' or\ + cherrypy.request.method == 'PUT': + try: + body = cherrypy.request.body.read() + except: + body = None + if args and kwds: + code = web_code('Misleading request') + raise HTTPError(500, 'error, code=%s' % code) + if body: + jsondict = json.loads(body, encoding='latin-1') + else: + jsondict = kwds + for key, val in jsondict.iteritems(): + kwds[str(key)] = str(val) + + if not kwds: + if args: + kwds = args[-1] + keys = [] + if not isinstance(kwds, dict): + code = web_code('Unsupported kwds') + raise HTTPError(500, 'error, code=%s' % code) + if kwds: + keys = [i for i in kwds.keys() if i not in supported] + if keys: + code = web_code('Unsupported key') + raise HTTPError(500, 'error, code=%s' % code) + if checkarg(kwds, 'status'): + if kwds['status'] not in \ + ['new', 'assigned']: + code = web_code('Unsupported view') + raise HTTPError(500, 'error, code=%s' % code) + data = func (self, *args, **kwds) + return data + wrapped_f.__doc__ = func.__doc__ + wrapped_f.__name__ = func.__name__ + wrapped_f.exposed = True + return wrapped_f + wrap.exposed = True + return wrap + diff --git a/src/python/WMCore/WMSpec/StdSpecs/StdBase.py b/src/python/WMCore/WMSpec/StdSpecs/StdBase.py index 6eb5705f0a..869a18e2b8 100644 --- a/src/python/WMCore/WMSpec/StdSpecs/StdBase.py +++ b/src/python/WMCore/WMSpec/StdSpecs/StdBase.py @@ -54,6 +54,7 @@ def __init__(self): self.workloadName = None self.multicoreNCores = None self.schema = None + self.config_cache = {} return @@ -99,8 +100,12 @@ def determineOutputModules(self, scenarioFunc = None, scenarioArgs = None, outputModules = {} if configDoc != None and configDoc != "": url = configCacheUrl or couchURL - configCache = ConfigCache(url, couchDBName) - configCache.loadByID(configDoc) + if (url, couchDBName) in self.config_cache: + configCache = self.config_cache[(url, couchDBName)] + else: + configCache = ConfigCache(url, couchDBName) + self.config_cache[(url, couchDBName)] = configCache + configCache.loadDocument(configDoc) outputModules = configCache.getOutputModuleInfo() else: if 'outputs' in scenarioArgs and scenarioFunc in [ "promptReco", "expressProcessing", "repack" ]: @@ -701,6 +706,41 @@ def validateWorkload(self, workload): """ pass + def factoryWorkloadConstruction4docs(self, docs): + """ + _factoryWorkloadConstruction_ + + Build workloads from given list of of request documents. + Provided list of docs should have similar parameters, such as + request type, couch url/db, etc. + """ + if len(set([d['RequestType'] for d in docs])) != 1: + raise Exception('Provided list of docs has different request type') + ids = set() + for doc in docs: + for key, val in doc.iteritems(): + if key.endswith('ConfigCacheID'): + ids.add(val) + ids = list(ids) + couchURL = docs[0]['CouchURL'] + couchDBName = docs[0]['CouchDBName'] + if (couchURL, couchDBName) in self.config_cache: + configCache = self.config_cache[(couchURL, couchDBName)] + else: + configCache = ConfigCache(dbURL=couchURL, couchDBName=couchDBName) + self.config_cache[(couchURL, couchDBName)] = configCache + configCache.docs_cache.prefetch(ids) + workloads = [] + for doc in docs: + workloadName = doc['RequestName'] + self.masterValidation(schema=doc) + self.validateSchema(schema=doc) + workload = self.__call__(workloadName=workloadName, arguments=doc) + self.validateWorkload(workload) + workloads.append(workload) + configCache.docs_cache.cleanup(ids) + return workloads + def factoryWorkloadConstruction(self, workloadName, arguments): """ _factoryWorkloadConstruction_ @@ -758,10 +798,15 @@ def validateConfigCacheExists(self, configID, couchURL, couchDBName, if configID == '' or configID == ' ': self.raiseValidationException(msg = "ConfigCacheID is invalid and cannot be loaded") - configCache = ConfigCache(dbURL = couchURL, couchDBName = couchDBName, - id = configID) + if (couchURL, couchDBName) in self.config_cache: + configCache = self.config_cache[(couchURL, couchDBName)] + configCache.document['_id'] = configID + else: + configCache = ConfigCache(dbURL = couchURL, couchDBName = couchDBName, + id = configID) + self.config_cache[(couchURL, couchDBName)] = configCache try: - configCache.loadByID(configID = configID) + configCache.loadDocument(configID = configID) except ConfigCacheException: self.raiseValidationException(msg = "Failure to load ConfigCache while validating workload") diff --git a/src/python/WMCore/WMSpec/WMWorkload.py b/src/python/WMCore/WMSpec/WMWorkload.py index 506d4593ed..bb1fc070dc 100644 --- a/src/python/WMCore/WMSpec/WMWorkload.py +++ b/src/python/WMCore/WMSpec/WMWorkload.py @@ -1649,11 +1649,11 @@ def _checkKeys(self, kwargs, keys): if all keys exist return True if partial keys exsit raise Exception """ - if type(keys) == str: + if isinstance(keys, basestring): keys = [keys] validKey = 0 for key in keys: - if kwargs.has_key(key): + if key in kwargs: validKey += 1 if validKey == 0: return False @@ -1678,16 +1678,8 @@ def updateArguments(self, kwargs, wildcardSites = {}): self.setSiteWildcardsLists(siteWhitelist = kwargs["SiteWhitelist"], siteBlacklist = kwargs["SiteBlacklist"], wildcardDict = wildcardSites) - # Set ProcessingVersion and AcquisitionEra, which could be json encoded dicts - if self._checkKeys(kwargs, "ProcessingVersion"): - self.setProcessingVersion(kwargs["ProcessingVersion"]) - if self._checkKeys(kwargs, "AcquisitionEra"): - self.setAcquisitionEra(kwargs["AcquisitionEra"]) - if self._checkKeys(kwargs, "ProcessingString"): - self.setProcessingString(kwargs["ProcessingString"]) - #FIXME not validated - if self._checkKeys(kwargs, ["MergedLFNBase", "MergedLFNBase"]): + if self._checkKeys(kwargs, ["MergedLFNBase", "UnmergedLFNBase"]): self.setLFNBase(kwargs["MergedLFNBase"], kwargs["UnmergedLFNBase"]) if self._checkKeys(kwargs, ["MinMergeSize", "MaxMergeSize", "MaxMergeEvents"]): @@ -1695,6 +1687,15 @@ def updateArguments(self, kwargs, wildcardSites = {}): int(kwargs["MaxMergeSize"]), int(kwargs["MaxMergeEvents"])) + # Set ProcessingVersion and AcquisitionEra, which could be json encoded dicts + # it should be processed once LFNBase are set + if self._checkKeys(kwargs, "ProcessingVersion"): + self.setProcessingVersion(kwargs["ProcessingVersion"]) + if self._checkKeys(kwargs, "AcquisitionEra"): + self.setAcquisitionEra(kwargs["AcquisitionEra"]) + if self._checkKeys(kwargs, "ProcessingString"): + self.setProcessingString(kwargs["ProcessingString"]) + if self._checkKeys(kwargs, ["MaxRSS", "MaxVSize", "SoftTimeout", "GracePeriod"]): self.setupPerformanceMonitoring(int(kwargs["MaxRSS"]), int(kwargs["MaxVSize"]), diff --git a/src/python/WMCore/WebTools/Root.py b/src/python/WMCore/WebTools/Root.py index 68f04665f9..7798765e8f 100644 --- a/src/python/WMCore/WebTools/Root.py +++ b/src/python/WMCore/WebTools/Root.py @@ -88,7 +88,7 @@ def access(self): 'H': self.host, 'h': remote.name or remote.ip, 'r': request.request_line, - 's': response.status.split(" ", 1)[0], + 's': response.status, 'b': outheaders.get('Content-Length', '') or "-", 'T': (time.time() - request.start_time)*1e6, 'AS': inheaders.get("CMS-Auth-Status", "-"),