From 292860d6eb57d68dd7edc036e0e4915b9b887e1d Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Tue, 13 Oct 2015 11:03:51 +0800 Subject: [PATCH 01/25] =?UTF-8?q?=E8=8B=B1=E6=96=87=E5=AD=97=E4=BD=93?= =?UTF-8?q?=E9=83=BD=E4=BF=AE=E6=94=B9=E4=B8=BA=20source=20code=20pro?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../EOT/SourceCodePro-Regular.eot | Bin 0 -> 83790 bytes .../OTF/SourceCodePro-Regular.otf | Bin 0 -> 140088 bytes .../TTF/SourceCodePro-Regular.ttf | Bin 0 -> 197644 bytes .../WOFF/OTF/SourceCodePro-Regular.otf.woff | Bin 0 -> 89024 bytes .../WOFF/TTF/SourceCodePro-Regular.ttf.woff | Bin 0 -> 89604 bytes .../WOFF2/OTF/SourceCodePro-Regular.otf.woff2 | Bin 0 -> 76256 bytes .../WOFF2/TTF/SourceCodePro-Regular.ttf.woff2 | Bin 0 -> 64948 bytes static/src/css/global.css | 22 ++++++++++++++---- 8 files changed, 18 insertions(+), 4 deletions(-) create mode 100755 static/src/css/fonts/source_code_pro/EOT/SourceCodePro-Regular.eot create mode 100755 static/src/css/fonts/source_code_pro/OTF/SourceCodePro-Regular.otf create mode 100755 static/src/css/fonts/source_code_pro/TTF/SourceCodePro-Regular.ttf create mode 100755 static/src/css/fonts/source_code_pro/WOFF/OTF/SourceCodePro-Regular.otf.woff create mode 100755 static/src/css/fonts/source_code_pro/WOFF/TTF/SourceCodePro-Regular.ttf.woff create mode 100755 static/src/css/fonts/source_code_pro/WOFF2/OTF/SourceCodePro-Regular.otf.woff2 create mode 100755 static/src/css/fonts/source_code_pro/WOFF2/TTF/SourceCodePro-Regular.ttf.woff2 diff --git a/static/src/css/fonts/source_code_pro/EOT/SourceCodePro-Regular.eot b/static/src/css/fonts/source_code_pro/EOT/SourceCodePro-Regular.eot new file mode 100755 index 0000000000000000000000000000000000000000..09e94730dbb7fdedbfccd07705ba6db7e606a189 GIT binary patch literal 83790 zcmaf(Wl$VZx3vc!+}#FucOPJY!QFjucLF51JA-?$;2t1oaCg_>5IjJFB_X+a-@5hH z{rB~$uCw<#&so*|r@OjqX(%B8$P^I(i2nvM002P5L_tSFMnXhHMgXKC{1X6bT2F`o z5R&LW;D3+z2mq`9l((GYp8qTTpNR&b3-AE=0lWb=06PE(Apftk{col9PxJUk;sA91 z^$!0SSAg|D4FK>382nSb0X_hye}wzLUHkxU055>=-zfpm`sWDxSN})(&yWB({-b>V z`TvLY|9@-T08xP8zg4$?t}g%l{=?Y++vWPt74~08p?{q)K;R$6`)>vS{{Pns0LbYo zY5kwu`)`Sa$dijelaD}CjDS8vG<%*j`PS88%E`yiHzt7D=b}U~R8W)qY(5o&xF!y9 z!mMUtsH{#;dT=MCP81boQJ>f!bys3-Pb|YH4QDXesa?rsRHU_~D6T;;9tq5&rmX3* z8QdXNLK&PRG3>soj9%_x?mP7N#O~$@4Y8CqTS&z)QL%o!WcnKUl`k^zvAHF`f~iw2 zj%#WC2p#>@k%z)uLqZ_ScxIbUrvDv%`@Y6gO_N}akooLJlEM4KFhv_t7Y%L8fw?UV zAjSN=-=3HprWg?0MaRbKFTQgRrFcH%XAzmH%O1CLy13n* zX*pFRh0%v{6}$>|{5eP!xC6*^)Cl7pgiVOTi6P@Ju+ke_7?N8U7G>H5`g!ey5-y4D z?3?4Hf~Z@45FicX8{XfYEd*WZ?T5$ZcLUyuyKC%LtFZdG%OD!VMmT}B*eqTNu|B6d zS6c`Pp|qZVYL+!8`)QUypHoznX1@Kmg+caby4}^cfMrELK(`M5utZkfGxd%y>@>qG0&s= zwpmwE-~f6q;}Vh7b`D&>hDU1AY{vm%Azs5z8gOpK_diHxL{1cSa#e`oH} z!xpT`EcaKx?4u>)>1x|c*Q$Avi(q50sQfm=WU9PS*U23_4k5-~`?Dp$%ubs3Q%Knd zmsyowxB5tIDoGC42j!Nrz)!7o=$e~FN&k8}=PLi}Foj~{pW&c`l@gNjB3*FT5dPU(8x)zkHAuzgq zajG4kMuR6m(YeoC81Ut)YRjZYJO=ie%&B0-S0DJ(?n5F5eWU;|?3dlRo#^+#ftxWb zB2b}k5JHd?cddQmk^P?8MC-(L;nY4->aDRi9G&$SHKw7*i| zdTR_IJPq@mN}uS;PV)qw^k{7*Yi&mS5fD**JQZ(!d|?}1cG~U7?zm$*;3$^+iZ2BE zvomhsP072A%N5W;B?}6;RIp}9mWSb0-M1um~qBT#eajaX*m-Q6U$6(QFu|1do&@;|0>N=X5 zhbJIf_|}`)sy)b9MxxL2Muj&t!V^g0=FCvplq(=uRhsq%3>{O?xkGCZvQIMhj3{*m zB&*SSw9WhcisW|Z(=V6j5fG9_gp$KNGuSw@Y%FvpTddKw4Dl8;Frbq;Z))&~V`EE5$mq+v1BsYNDB^A zf8J8@ak7>Cg`^!C;Rx_6z^{h>kX@YK)7JTywfM(j4QuTeBldYeFSOaAAdndExNpG1 zf}F|$TfS3?*UvRoGL`h&1<J&cjPvRb&xTl*;}kJIlD>xV|fn}q6i1Q zDF0@kOi((+Hs;IN$DB46H!8fn@fGu%MY2tlU-5N|WUm$wU*g8g1}e^NTyI9+n-cnh z=tu&4Z!y*6%b}YJfpcj$R4JZ4N{^H%QA|F_-t8xbePyo_=Xt{YOrE{ovK1%vv1_rz zDfim}(KZh+d~XQOnv(~#P<^U-12LdJ()xP|_eC?yK}Q`{<%MidVvj#7vg3PmFE9n< zdl{mX`t8(=%TV}Zl^wVxk*!zoy_7P>2`0f}2SSKQQ}v)sVt+`@BH_maDK>?*Xgbrv zNONU@anPrNa^|uKh&k<+ep>VUfkew0d~jl7yyZ>^`xfBmyW3uxU@E2ORVV_MuEUPZ zllI}w_t{{OMECYg?#nWa8k%;vB_WiscS;NT%JPjNWKk&5o5lo%)5Fb+|6}+0xTF4c zACt)0mX2yB;WssIa4Bxi;1hlu{;)D*lthAs!234xe#cCC3d%1&A{c5br)YVaq!Gwg z5oZ$M@$-UXRxIM8L3pi%hKXHI97mSR0vypIG;eAAkU1JzC>D;;`hb}a-qi1> zY1@+2?W0c!s`L88n+(VcKFE z#?bgIi#_|7Mucj?Vy+x5DgsIFJfC+E%k2fDYeRyvqo7_1!qL?Gdjf&m7AbGAF>Ubb zFWbm7!7u%@hfB#g{$EV0t3uSd485lXPIltMm&f8O1r@c2_vCh@33MAIKyQ1upCDs9 ze1utcq3ZV^K#M{UY*OoCKPQv3EN*QX)U+Y^0Efj4Ub#gpQ@cJ$k1Wl&LjI0BROUXeh1||u>RW4Srd65t>fwaX$tYn9dw*-&P6NC8 zqOKnYaMkvD)M9bdC({m4+Q9yo-85p5S=skp*9>-`1}Uk-jjqO|=;~F`Cs&OYi6Vnbyh@`s z2(1hy;6zg-vJdXO`d0_ZtbCl2lM`$8?XN{ce00A;1Mosw%rE-Q@Facm6y#N0ZufJk zAmH9~x_GD~v3naB3p%S2{mB z#F7iEFXp?CDD0<9#Gs3-AR@X)L}V!pK`|AL-{hrJddm#t3xlSK|h>>2PoTVt%LCOv83O!&z>_ZUJQLNhk z5+U=K*xOHqdHi<5mxc8mNl9N)(;QF2;0BYTd#t;@$7Thcj?b81nxfjo31VPa_0feo zjAlL1sO;{VI%-wj+YR+V%H2!HF{9@Zlb6YGol|Tcf$I~T=lLZH@qB0 zbPg1*5^q5g;!SC%Y`sChjYm{xM%T`hYJsDQd1!wYehK|DJyYY5q#gbdlGL&5>S(KQ zhZ70ZQZ0ZYCaDLq-V(?vRaSO?@Jh`%<&IJNf$$Q;8|%`35TnQT4MXaD?GK;VQuF7# zN}Nvwk}ZR21Jv`O1>QuIe?@-_=Xn!IQp?;S2uI;@MpR3Aid_<=6EIQBf*G@5>hdF@ zq^DPL{}?ym=ElF+%kQ2BV!L=s!5yXZNb4XZ)y-IVF@@ef?jW@tJ*3irk3z zH|*Z!mVdlt`YgLxEj^!>|05R~tnr|Q)>8yod}@StGPkM+F$foxH?*%6 zkEm}@IYm*#$td=`#f1>)9!QtgIWrei?qfCf$A2>}1M2}om`P@s;T0n0PlGyYa}d%N z9mo+Wt)UV5P9-{L@~rc!BY(~!u%;=>Dca`TBseXy936;-Nr*_SN@9Q!?~^ zDQ5q+{YLYaek*_0p(PXeViz+yF^g}6`9ZF1F;P-9qc&MtY5gaL&lW?L3LiBKc6_UW zkFJNOeJOK1<5>sOC|R+Xj;|XjZPQ&r$tntjIB=lkbRAdPn^+WHsPIb(q&Q_EWo=xX9Ux;#aTxP0CAycfTEBj-8ij%%GnDO$N;LML)hy~w^yzP zz(efh%NaI2&(>Hpkqjq~=8Zgjm+;d&jO&evl}LIBD+c}rC|0K~wq9;bzi8h`MH5rT zXCccL&1Al^F<2cRRFQ$AWREa+;>_KS$L$Yit#^bAz>n(*ou+qa~ zBW9yZ(Z!=bA##8BMRdN(2@>-=lNDIWm;K?Ae&Xc*Trbu;u-34at1i&wK`NW+>B8mJzeLW3a7@6)RcubGZz_JK%S-DTT+PD$k66k(ksG ze%k$cVDc$^T}dsLn*4@AV*rEn2qU+I%UhhY0g4oMn6-6~7*XNKo^+SHZnDT0wxvn8M+{;EhW#vfH zkRBIaRPwte=UuIFeov`iLe-9pV6DTmRByI$H#9|)=Jzg5CpGAo?(<`ALZrOlAQk1n zM{H!th+{ckzQMXXBz8MF_lr-DA=;ntlzTWqA%hJ!lL=2b#!jDv6bdwD81|jFj+Qf{g?y zdzKI94S~2*onSA5L)iiaB^h^4V%W*`CCZoT4gr?0QG(npbXf_)7r3^htwgJW*z186 z7=3kjcKiOBvAU5Tp`1kMi%@a`Gb5L;l4!y_=&4l?JxrGlwhpd!yPrlWpN|-0?{k$S z+ka^@ByI1CM(d4FvvBgAe!!aF3!hVlbevjqBo$_KABCu=XXL|C&||fYL{{u#s7NT3nj7^FH?OL4{3mU^nbk(UgMdfKE@Q zyZJx*^h1A%uqB=?D0o_r7tKk8&nIo1I1`4SCTc$mD`3NHwG8Yk&e1xOQ_OrTN*xtcFo)PyGKPYYc8R~zi?a)^ozYn4A~~^zfk&~!gcA*s;^OrY zF;}%H66i~qG!g(Oa6GbnI0DBx9`KVEzzGt7Oe`J%Sfd+25yt_rEkSxtJ}{#F|ZMh!>SlsO4 zC>6Y*%;6{kLZB=i49F-F4n$wU3tI~VVv+$~!~xr&za0s0ugjc%IVJ*In)UZwij?w? zr8-*buPbdQeyA0pl`{r`HNPs32rtn~IO1t+AyY}i(1?^OFgG%p-Xq|v^8K=dD}H(6 zrYi7xwA5MP3VjI*m&6c8sJ};hz{Fd>l=N=XcAI9MLbFgl)QP@LjX9;v577%)Xw+4# zC1%b74^_O!;`xZy6hmGy5szFDrs(8&wtxnCU1!wF90YVv!Y~{~h4#(asKODn;j!n^ zN!{IaLHpj=?!BgA9G8l@$|DN`AF>8sbAPAdA?UTD{UB|w)hSFv;cO)5@lTh59;u~w zd7&6T@aG2$$+x(M04H|&Y=nfKOPf&dy zMNmNYbGA203K4Z6Nc0K+sN~kx3G4&{iBSdcUEn(Yvi`z-ejnSPR(B#jkj#%J%!I_a zBx}8u7m9?JQ&>Pd*dt3H(mfUVnG6y;flid7n6a^%jz-7B?^&74r7_v@wGS7gGlmw3 zaCF4Gd{9OESe$?GDKaxHvF1!4ECnpnWtNIHyze zBVx@ck4$le6VwDaQz&+zD(gr+j6jBEasq^*HB>zdDt058VBqb48y}s@C`E( zg4oD9ZGI64WcR$_*m4jn4dCSASG?ov-#``t0>S zY^MTrV3=o>fcCtdyJo&Sj(27~%Ad{5iI^_2awcIb z7rMUj)TpqNIGhisChiM`Ed8+&964O9l?9u8HbWci-VHDOghiI(d^xLr&0hBIModD~ z-R{}>5%H`DfA>c2)YO~u!;9pxyc$}y%8H~8KfDv(3a?^MI2gpjtwhdNUO${ItQ}rJ zbdwS6%~$6VIqpZ^IQ2`UUbSrQ2K36`;;qO*^=KVe)GC0i9 z@+(V-jFab>E5Q-~wh{5RRg54+N8Kr&Nuzgd|9&fEZj%@>Nv3yaC8~20>sx4CHgR>I z?Ri8k`}oYt1*lzlXsuXOsx;ttzq^5Pg(!Tpk`k|`cFD`ZuR6)KuXG%^=Gb5top};Z zcDlINF)1u>eFg9(l$4NHCS|@91az`v0#2LJv^DjPz9I;N^BL7I6iyr;@aw2&95*OBBXmx zHV3MY#%t@`S%d}pk%+%U?Lqlc>fIlvJQY)O<EpFTPIcZxi&;%t4Dr9?2Vk6M zo7TmsV!iV*9WD(Pz#a^%=G8_%3bO|oVW^=3I?--e7pz~FwHf*kei^8}Xro7m;kI}# ze%(;3Q|?l21YIWgC{NJD&>tY_5I%%66+ZN|3X?B4Ab)3p8y%MU!jN=;wDf!tgnBwU z7>}!TPiu1oLzp@t&Bw?n$nnE`!eMj8e#n4s_+W~l^&bu#@7HjHceK{#7QdqF4oR>E zN(EkHg5!Tx9dFP7g?k_nyf49g=hsq3As%{AzzMT)wh!|$MRnrC8&x#)0^ZX7VciumGkmL15PBZt zl*`;6EgE2i%&Iqv%^%;wu4dZsxVAsjP<}#(7o^zXexlMt_(c4-p_4+Y@-m`*b{)L@NzaaG zb3FLrW74I(1C2lGoS+JJ-bGaQ%)L*?%&+vvnb&(ae$>!8W@_df<_#v-S50cy;!pzD z@W79P$w0e`wOS@G-N8Z+v>>6H+ zXUZVGFRW-$AgoB5-=PTMcTI}0-6S8WY30RNwY7pv*m}SWCYCa};CZOq44lcrim&9Z z$q4M5WOLfBf`9^BYputL0X}tj->9%+HM(o^2vaNN##61O`O(TQ;gVIQ?1QI+b%2rv zCbg&z{?gD7UxERnGd*wdTljkZi2uHO%33W>O0HM2k$VBfk{Ox9*CZS`3)y?Z#N~p* zdLmghSc?w(nI54atTQRd`;+_dCZQs;gczJV!h^ITY&??7% ztJU@B_+`l4&)uaT9wCpP`iv-1MUbp=T-spf_DTP~!Io7WEy@K!tPR)#l^=qab=0Yb z6*{GEy-RE?5ur??_jCGu%ub{N;~auy3U?ouS43reU3HR=!|tki~vL4$+{b9Rmg>pVSKd^x_$Jawfo*;SJ>blOxB8f9YyFWq=q%Kh%6R)uv~z`CH@=LkBNW%EzTO-PsK5rvq4m!QcAsi$W_xeKZie6J6UVoFiO|Ck zW?*f)DL`Hzbl=ZXBvfBkCKeQe7SV@M<4=txuvvxejyRM0HT*Xw0jVWmV!#@0{gEIfm=cPO+8%T*XX zDQo)k?N)QgumMUDReoq$))@e&1ap*uF$@{OVKD6Nihw$t#UE`~fa4I&1`Q}Y;6&I$ ze331(7-SylfdijxS-n76z_ZZ$LFw&J9pZQKUbgx%L`zgi_)8n5Z4rqQRsM`IkJ_lo zAI5r_@X4FG5*VaWgF0w@T&~RQ+#q-w`7qR~qaC*PFq#s-!W`t+tY2wX@ zy>e_|q4KVvcj_^^qv3G^?>2G&YsVhR`i&Ne)|wWHPV@1x?L1d3n#TosqOMZi(2~k)xy~&K9iAnc^r606iFGkEdLErf-qYPG{E`%cl{@ASotdMpXq_R_Ki*REJ#m1>SrPM%6%(9im=C1=SbF3UGyDIM(3;d+- z$%@XJ!HUQx&f3wTc?9GInx2bC z`LWE@WxJos97Ragko|jSQ8JcZ-OQg42|WyX30w5X1~}x`tqf#ck+@pwho6 zNt2_@8W94xFaou3R<5xoThJeds^2gy&BC;yP3B~iblNjS%FHR)Iur=Je?vTL;TKpx zW8dRjZ;(78jsN&zUerqGG|RdWl{Usw`&|5n{=z>bKRCNHD@=f{N`mr}oaV=G31$&n zUp?36Hs-ZAzu!R;WVI-eI#t~SpzuqE07#1*Ht)iV>>#tweB4eJq`<|xq~1>C4tQHABD}8)6x`|3bNa?~(JNyV%7nJ^!-? zqcE8zvc>UJFoh#E4YdrWE53Ip!*9Y|k&|HJ=XM)O3la|{(vEw`7D7>%Z{s{W!$+v{ z3B8>= z#bP|0CB?y?A`lt*diOuBxc)rdE!lTKGURL>%M^EHXv}<8(#yX*2%ma34qig$v79DiizW~B$*ukX$#cuX63BF z=d?T&zPHk<1M=}6XRpd3sIEI#r;LD!lEG*M2rO-g>H|WKfQKlA(N26_5VLaFB`U(l zsdOj-Nhm4iE{tRd)TxerZ;^ z&J-?pja?mukVZfo1u@ca2@Bp3Z=HWeCDOf;Zzgm^4@>+Ba=k#vL`MtsKouc5!c$bXM%dTHd+p=q{6dS91K0 z=K2tuLFMMt6J0!Y;Icm)P0fC-DbSw3g1RGED}wp37ghkO|E0<0>} zdc7|6u`$uIcy;;O!N|^@(2Pe{0^#MaN+5J51Ult99XdG&GW7U~hB|6;#Rm9fveizT z&jejeX+dr5GBCSG+lr1M0aF67J2oQkTPF>Dvf9-~=i*Ft*-i0ak^~671 z$W(PWl}?ah5!_4v$G(fj!ZJmTQNjPGAPfh+F@jc{JI>WTfJrtsZG%h{cU{vv1#s9) zjkjDVt|5NQoG)gRJFy{{?eK{Csg$%rz!w>ACM7F2l_(QI%nU3~CD4 zF)2m)qh9cDN#S`z4W(@+iiKw2yStHQdr&K{D`Xog*<{v2DzK^UbnI(InJry?V zc-*Z4y!)t+`u4xhOs5OxMn->Cp)vF3Z`~AM6z`k_wwV%|`{F_9bsmv)tPU}D_L4t` z`=RYmMl8f%rMhm(&XtR(b_8L7Qw%W$%9!*R>!wp9zO|AY%f(`9rLRpmVS0U748Afh z;_5?x$IL+H>X{__21Q86At+94O^x(Vo#VH(Tjpjfc>gK2CZ{og0hv+Dm(Yby{)VS% z2j75pe7Gi~dr4u;F7hB#@KYy*!v{Oc@TIRQv(gg14ZSfTkf14Qh+~1f`M$y{K2rak zf47)+Il_L{BkCW86y`KejCQ_raHz}&G{nlT2D-Um7vBNr+N$qu>6pa|UJ_B-O2v4B z`cou!-cr1pgpk8i76wucQV4lc-KKOA?lD!Qaul)O=w>YoZ7F&pzQC07SA8}Y)tu>q z%lf>SW8N4ijDlCgWj_cwC@uU=&BwS`T9#2=NKP zru@zP;U-8mN!4ZA4BeD+Ka8vbW?0gdjF>X?#LBE?N6Q4zaup8Dv&H%yVfycg zL{Xs;l0Br!e~c;QWR$3IMnT4%u4dz`{V;AK?dGpQ!Ud;us%eC0_j^O*i7c3cog|De z`p71};01%$qZ2J973HZAfsKrUTTX^4EG|O#88Q31r}mC!5{R@;1CabT2?@PSo{p`_dba3P$U_F9L6BB>A`SiK z2(MziF<4vNFI0{LrUVx%R0+`V<)qZfXk~_^k^R_}Ie=!Gz{p4fSAR=3b^Zu+c1joJ zEO_J{u4sHugFdiul%h#ry!}%xD$Vx4?|yer%_>H7Za0`YWDxAPY3$gPBW>$%sT6W72bE0w=(1UHg!gxY zGprKg(|z%3DP{Zc-Y(niYAi7c_IA{ zGiLeDEMr!Tl(<*{25kL74F_2h27RWLbVBrEb(A&>m|m8}u-PxYXI{DQyO^@9j#)e; zA*#s^Bq0)zWLq3WF2QApR*X*T7sT@aPDP2vFoo*~Y6rT_oUQl=Dq-4d42gad=1|06 zp|;W+NJ;oB!$x=O0!=nAy?zA5}W&}^Dr@Dso1Un87Z zq5)Wyq%kd<0pKZIm|zgi#uol3!?IRM*p7%02((NWqppoGPXUWZgv*(IursmG-9HTLyM@*Tv%5M>GC%w-@TOo>i-<$;ICzO^F z5+0Bq2w;fhJJA!Bi~?%yKH9Opg2weo0rWhuY*i`I%lGuy;SvnL19WLTBMp%8gX>(E z@pn-h_TTw|y#tCB%m!r>3YsGL&SR2T-bGKL^SxH8eu!2qC@kTX zhD}atB;l9x#P~<)C-aHt?#n>5({kzXes)u`H8HVE)H~+}BIfV$F9Z^}ck>^bkg@(; zlfno(LH}7+EROUADy9T#S61=w-qQggz!BgB%US69Y|AMQn5)k(7#+2e@(N;XYEogM zhB@K+zRym+ZYf0>96k2#vFL2nRGiK;9Jus{JmuVrBOOzvROm?$qChv+7*->7Gh|y zo4>-=Wq;C^Y%!g|KuNS#MNN8&HTtRLkQCo{S65XK${TQr@>Cc@)}r9(mlW=WX-nlH zk2Zg#3t;a7(~W2xuNr<{Wzrk-9zr|hyZj)&9sHzqbE9bo)(k1|A2CkbDB>e*)mJsU zX(ONQDSi80p}6y3vBQXrA)pne*oOD-yT=H}faw1qLkVvLzx}8RxiYZ0q==dlMx@u< zZyl)HReTgJ`ZlnVHal%Am265 z15Z^2?g6>^0WnX=CN(=kMr{O2bhK5KJ)%z!efbgVAY#lT3~Fc9kc{?nwK@-MlnKhb zRgZ7Ym9-7)j;lT0N0K1At7~mO4Ko#_D*DPm@dGkCg5_DqRy%f6m=K^vC?!UbIl()Op=f&* zRgVXH)aiK%)w-;a`d4H{w`#BnCh_P)!Y#V74W2%*1X*pD{T8xuBp9m56zK}89%@LcGbzb zcF-uWcjU+}vSkV~wRH%3*ct!0{zj8CW()5=cGGoDJDi$YiB@tSyCsr~*s^fJef^Vw zjk@=FF{USu^KN#(4EJIEMnPca3DnPiG;%+keNPI}yh|}58_pKjcz%K5f5+}6y7Wc- zO;=l$0&{9AyX+_A#8-&YTUu;YRqdIQ1zgTtv(1$-x!m8!Wqv=RWZ51t*vDi(*T?u3%Bv^b8Hy*A7T^(25Q<*@h}Z0X zcvZz;eX_5zF7*c!NN04mja+$K_!GMzeryReea6pCLdRD*S7OdUAMitOyYCVe(2Lrc z6Rfk`#g*(Q;f;l&&&sSBfhIbuxfNsZt_I8y5!1N~xy*I68?2RO5y zYMa0)jE%(69C&;G8c?V0Uvl@g9_!u|m%^CCpyo3oWc!$c%HQ$11Q{iH23HFM{G-3F zx)CR?($hX8MO4%FV?V*2W=W{Pt`v$@6`DP4F84B- zHv|=fMk% z22DoqLzF36hN zli~V{7n6p9#a~ReDHkP&E26Gj1~N2N_@UdFh@zvwAF=2Qvo;>X0hE*DG5Wcy2X8=f zZ4cZK1yRPG;|00x4P<4iyeF!d3_7<((BL>``9#3Kbm-7GvfSfoRB3^N_z>(33Gz)e zjfoAY0M_8l^tPMM`3i}*}wjO`1A4}O2%gmIHrpv~03YCwxpLw^IK6j9gvO0T+PL6V_q;dce*U)EW zv0)}Ly3<7Nyh|vbs36>S>OJOt3T5^PvC)hJRDxx zB;|cvYF33Yds0o@z&ShbDuUb!&x|XzbEORllylscptl(~N7~}{XVG{dXGE8 zzn`&nvx~`Rp*Ls(?2S^awll3OtHf^>M zjeSQ*F8PzNu51KI1QmB?;@u`|g@_!J8*&+R-~kCHh~HTSQEpW)w~sk2J?s$jh~L*q z^D6#cbHTJUXV1cX%4F!z35*bu?&`}U+R;UJdI()TH)5i%W=dx$;y~k7JWs^`OpM#~ z$WAZo`J5GPg|vDiYbtckR}ONcIyzRxWIN#U zqMXPNI7LSAh}MaUG(?pOPH7-^ub29kNSkUG;rwW(1Wtq5#QV(`Q1}h@>i~d%_#P~M zfhXU1XvEa+r>N9hrr1N*Q>pu#4eCzdQet3TC~gaO&L_%L zQ0;Cvya6j}H&qDtdWfz16DUbp69}B7DdjTvUTSqyOcsdOZls`}T(gE{?^^y87&WtC z*g;y_5kEWdQOl{Al+q8sB0?OUBN@tCx@dH*r{9S61XaRK@B0nuyQSXNOlI{rz^$Uo z%o1g6zFV1rkT};LI?gfktpJ~weB=f~!it2yl}A^XeGZ!+iHfLd6VTR@qenu_yjx@6YQ2b7E3 zUs+UPk1*ay=17i>{%IXMi`Ra8sF!C%>v$5k6srh1a7dlJLoZaYa3Li}t>G@&=N>w) z5#2eFCtIdx+KrnMKCUV_Zf{PZq3W4y_0qZOYcQ7%^mS5;r)_u~)kKP>HyZ4}y8F@l1Fx7GZJLASI+5;v88`N07;bK?e0(4saN$>BsNA^x%32hG z0JkBIi(~+QF1p6M)0&^h*MsUpgn2R5*F*3|#?oz3YkO+%zWJjg?GQ zZVY996Ba{~q0PNUwjmuF!t1$*;oU&?VSz`1@Ct8`Q}dv zO*$ts;pMf_=cX*8%vA&S`5MHAl!(|tiIWPX4`J3VZzM;jx&~xQByMRBVx&F<3EnY3 z1&d;D6S%H?MVWAr4RqGa z4^jul{uz`7X|Y<4(`zk1i3F!>dC2PViI8l_DN`C{a^<}Q71qAl{g*t zU9WuT#*EVC>9XmmbmV<*@yYuba1aAB2VbEacw&ZGSqw{zeK+IToa3uE60N$EWg41m ziNJ+Kp|RiuG#ju#zZn=D*f_`^7R7Ef;+Tf;g?*|cPjQUe0-}ai1$WPfBkv;K+tn1isDT_5yJeJ>95R}znHbeNb|Rl z3UaRRUpP?WSCNDe5lRUMF5H#ESMg}jus0=fsH7xu2!lj%h(MmybsGy3?u39V8%2ao zCo(jY^S3#R!MgiA#50%z{*N|@msM|$K1c=evL%1DrhfCiE&(Sbpw`o8aPDtGMB5!N zFRaGpV^wx`5T@VWLKPE@Qs+c2(K7RXdDutlq!7=wkyMH+twd}juDj3y!?;k=cL)Jy z-!57{&k{VjFK+O+->;ZzHF&p4k0m7h$Y3%=Iz^iB>~`^##9t?$Z$M+cqI=&dV8rO( zjzVM)82J{|iETtT(Z#n;z;jSn1x6X47Z;t&P@fvHS-X z3-%QUABu_;QxN-&^m`qYcp!N0kmIH~LC=*3+5?ObP*M?ng9;_9XqRlDJI-vK1my#@ z1A@_{ZRKkz`Cc}P>Qaxt$CNo6fT|g$guRk9XU5RZMP$nq2YFdXl+gK{n$@%djwon) z*T;?o;hWe$-MdEBJfysmVwj{ROOwq6$&;A7ImMV%bW9_jv(@UOuatF=7oXYYlOUC?OB#_AT7}APLCSF#! z_y`6bySCgev1BXCB}lLRn2Kls4kO9RY&j|=D#;oeBjb_240m_~;S79~;7ybQQmreLV~N3~#84m7bEzoUpY4a{ zKTv})vc>~Dj63WRJ*^(JYi+!BI+Oytr%)g-Uz{ET_wO(6+mYHAiq3&HqORm^j_#{8 zXK1LJRTs-~&9_F-#0%z|c|ZJ#?zNNJ26S|1r)mTiIb@a)eY3#zvnq;Z!c3tsQR2_KR0pq*};B>t^b_D^B?1Rs3tT2Al2AbA-$$vTwmJcY^f8g zr)mD0Qf$@RFeN3_dtX(**ARbVIemVN`sH=IJ(nE(i=`@C>xV8)N(+ z6|FBa2S2i9ySHMOX6N5D{;g=3UA6fWVpY(swDV&7wkBui_n?T4`^$^yMaP%#ClMFX zb1$ts4^ZyFTF5HHwvP|>s;EaJx-vITRd)8`G^lDVM%r~yL)~zQy zsh)@HE1zAs^fUX5=O1UpZNk@9kdPPRh8$ zzUtLcU9k3N50(@zi92DqHut~F{v_B>|33gcK*PW0x2BiwS-mN;drxIU(p9-^P8P6R zmb$X5qMWrW%7Mi>YqyoH$vc*_Nl?6}Emu;!nJZG1(<{^!##|{@=&my4<}|8<;cEkgb9_}kb0oW{vLg~P6ETd~-^?*wvLMtJ7vVu^(Fhc$aIl*|cn4v(>LU`=O zHVI54tU`1(TIltV4wGnv?>Ii-__FO|O_dvwkVC+@viG5K5J7YV5wI>Sv~Rs{K=3b2 ze{p3WeaF4~;CdeWizwmt8u#Dz=nK9qt*6-|-u~yVUih--zWdzu>7NhtuZeMG=mO=; zCCT@$B&-g*cRcu0ke+yw#WZ-AE=6&W8sz}Fw1S>Kj!@9`&Ed@;r+=vgBt3FoM_Ch{ z{K3fgek7}xSIeEfb`l1)EQ5v|)vUp&V{Rm~cGm{Ga|{H^)R%12%T}Wo&1+H}yl-~r z$B+?f$$6c5%f*`1%ZY4Nv{8;eIL8h=Ib5b+OmX{$H(1}`OHVK1=aRbWTYu>-i&w+Q z>2waZzBsL(*Ta-?c-?F9hb&wc<&&!VsZ!Rt(dQKr zDnhC4OD`FAiO!By%r!p-;=gmc+pjW%Fb`a&V=xBK%Ov;Orb~5)9TOnL7Hh8BpS5XbNNa z(Z<0t2 zYT<+95ij=$aMkp$>@=wnQsJIqXqoen;5c|aff%A-do zNwj(kI{!?a1k|E~AA`18*Sy%aC zW;JBi>aPbUG2Cd;t|mKvuLC=WVhmTcPr-`331%a(<$2I0J`~_fQXLkJQ-{NHs$eDR z4&u|tNgQH(@`(pkspF(Cojv(v!>s|SYOv999rxQ4f$Q;q4F5%z*dI*w&BqDV8${lpCP?3M0Gho21Mih{pmzwyCH%Yw0;tP z^tmFtF#;H~y}|p^!^K|*<9BNq2!8aj(OF4*%x{Xmi{6(aE6*!>S>Uhou{!mJpDTBa zaa%I6I@=CFR_IbiI>zo)mGMk~hwnp*DF6`7CnT_dg>Hh_Q36)zbHy^ECi)yvOtAsb z)e%Bqju9#A1c(sQ5#T`#K@3CajL3p0jj;f&)|`IB*gO6M_;t5n>P|J}6=wozRqqRLR{5$YO9#;0O$<6Sy72 zD#UIDkTSjk5x@>XC!igi0=N(?BXBFglMof|0LTcmyKsj?1WysR6Yx)o8)0MvTM@Pk za6KqShKK{15wsBCIjH+V@&j!Tv;x3`0rr6$4n986CP3e#?Et_ILO#%81D=hv7yE%e zEE_@if%+R{I{;=N;bGb z1KJl{8UV>+>w=rx;_HUjy12UF@b*9xTp)YYiLMH93Sw)5Wx&xjfJ6v*Ibg>7l6bYS zBE8K6)(cQXERQ~;eZxFX*b(9lgVqYsy2gAp3cv>VdZ34LN0d5)XW1k@P`~aj9;j&c zO9!d|{@Mc74L;+1Q@6_(xl}Dczx!N;mXmdVlf^~zzOXGoO8Y8>s1aW7;blZvqov`i z=7Jyh3=2>$KI$QA1CO$V9YBls)(21H>Z2N#IDJ6?wx!%_Ft>^#?1C*X3`EjR70Z;trr*Io3D` z<`n*Qpftm8&iDqHY&rgb(+!U?Gz~Dk^OJ$55FKrCjHq z``T5f`J(-$SzpXe_LX7IYp-cm;pb!aEdp1JKWSI6=R5Y5Y5rmVX;%=|i1w9iEq|Wr zSK-$2?v-o(XrAd;d)FB5m1zBBPH9&H)-q>Gx(>9ibb$5LGo%##Xng2^KDyR)K^Lxf zoe+Di4bF&p^|*7Q9`)97q6s~BjW__BiRC!a3(mg=G(!JfOBx`xtQy9MN_CPkq5&;+ zJZOTeT~8Vz_pZr}5GLzN-$VrZ{Ws76X14|O12s8-8AQ=4C&k3eIb+f=vj4HNxaj!0ki)W4(jb&``|KnNUHn&$X?k%1J*z`8f z38p#-yfXew=ekW=5CBTBc-U_BQ1+BSzy*Q!+I!J0&wC zp5q3lWOK}7bj*y4k6kk(BJq>cGCOuY^vsXpj5?WdzcKixTx>jPw9Ad6+(vnEpf>3f z%a7LGE_rdVEz2jD8YQvR^5etXLQgI-t+G7w<6zrD&n`BVu*CA?30tC1E-@Xnyz-+$ zTT0I=I{Rt)h%ssK^Ur`DI5Mw^x=_d24Uk zWkwa-|H~>dqTEhdQQ>WhGRlmLZM`h0{TA6}l^tT=jB=yLTNRE}YzuR-%8%)6KOCsR zmg{4a9TMLRa$_%BSjMI@0^jyEG1V=SBU2b5ZmAlWUXe~D#P05X>9n{eltx6qD5$e~` z)X@elEFDb|?$&v<(JZVTTTKFj#HF;*Z)iK4O#yy@%-U!OR0xfxg3CZaZ8Q?v3O3V0 zZ=rl`Gy(bw2Gc-yp@?lX7m5Q0(!rac3O1Gq9SZ|#U{O#rnM()ahGmqn38-vYO9t8v zmQum-Le|Py3Ft(bO9GaLiIlK;p?_s8910KyQotQU9?DoJ=mLwSgUtwn>0k>`2G>gh zy$fpTV7;MxT`Ul~7nRb%xj~a%EEakK66s)=&B1u95`}0?Blc4Acs%q=Ml>@VZD9bT2EU zfbByBx=25C6)U8I{X>qrNDA~DE2M+}Kt)|71&SBNQb9JMXH_H`N)QE7KxI(u6p#vNWzk6i<|NG& zkVfbw(MbWIq9Q3E9%O%!Nd_K4GKx4bau}$ifPuG0ia0QmDrlpDFCh#)91Lk1W6{8A zh#@^37zqSJ(ZC`|l=L#?BJO%P4q0>2!E=#qJsbuSp@*Y`%b)&cS!qS2U(ZIUNe&ay ztAa)F^lJ8y03MBRaxf>OR8)-F=+w_6TzWMUNUt7^YqA_?qgwoqVW`)aAa<3AB^u;s zjYOn*8iP?N2P0f+CG=FB3oNwlvIi2w`85FMY9;1C4MM;Y-r#DDM7g*GV^J?+1l-h1 z*?=2{;w4L9JWWKPOb)@QmrnsN4Zunez(N{{NU#ZE>$u3geuxD|~UPSQYzmSamEFz;I!7!WC+9dUk*nO}K=X1xF4rj_ClYmk9Sw zz$|ToJ(`@%#xQc00ZLcKM{qBTsP?ll=SK=vECT0Pek=mym^~H&aBMM!fK($4>0lJt zU~()1$FR|s0Z6PY<$zSH2r*z3CSZLm0_fOsO8}-;5CXs~8HOCN3teC^NCiQ#Kav4V z3?U?dRC5P%KrIc2BPn2CYsKoRAQwQuY)A!0Fh;XW-@;P@mUSTSp0lh!Vrv<`MjvdN zLD*9R+blk3*g1&+qB8?qG>~^}z*f$n6gyZySq{P2;|ps(T=s@APLcwEWiuIO+yw^- zd+RRHbv(kz&eR>vFoZMH2Tx2Xu8bVJ~pDg5_qO%OS zC$2q0Y?7R*^&1jT?PWuZljx^#ks2yj#I~r)l6twSAL`qEWU2=wp10)&0#97pgV86g zeM9Jz*Px)Zlh;(Lu$0ZOIz~ZsAbAn+5 zoW!Bd0Juz|c)_C{f(YaFnd?99cE#EF{9zBmFo}T}Lg0)@#zu=|7`wAphKq_-brRs_ z*_Br5N4t&|Tq9SQWVn3j1kOs}}O2A4&o4XBdjvWBHFA_v++%F~tu?=;Ca+CS7b zt~G;Nj>Q6gOIbL8vYaUkDjM0Lvex>=WC`IQ!m&v$;Tac6$KmQSNiYQ&IGcb$(8H_H zDDWek0oCQV-A}nD=yVJ}#!HWL507^FZjM>HXBgbWAX0o-7rr^INL;oim8_9qS|uPg z$S7?w8g8I&umREF9GJi^DMIe>&&Bdb$B~JlHBax9zV3+`Kx30IHaswGz(0zL*UIRC z2B=7Zqo}PW8&CC8Kr_++&{KiJ+vuo`&9UB)%c4LWh$!q#!Em>fO@eINzs+LWQLHpx zZ^CFHB{DvXCXsfOz0i}J7$SunRGP&wTEroBu_6nZwAS;uTWlRB@%q;k-80fM_JPS} zce_%A0iHb&#uRlr2u;Ms-Pkfqh*1HjtCH2TiwV`)8*|o{jt=iXoFEzopOHr$_B=c| z1B82RjgoQ<6g!n>7GI|%YAYHT@`SpM&+O+s2ot0N3iWE!^Rt|>w%HLah{(|L`l?vC ztJ}2FVVCVn=4$VO10s{tMLhwW;zK(#9zG?|M~H43ocV*VeT>eo{Ld7?<9xSSbg9{N zv5*kkx;$SuH5$BQF4s;8$%xmW$Z)dM)84!UnUo+Fed0IX9yTzLG2d(#8vd{lxl+=p z6UGo5KFK$+g&@n0QolF|8;a_DDFFnEU`<}?Z8$tWy)Y$qK%^oW{M|?!@wkVsm|%ttzys(bjil@kZ>(A%h>GUM4X7M1u`%X! zK)sG+=MgSDy1+Fb9R3kUrjJ&cNNe$_x7`kHJ;mTLXS2&0=N(n9B#=5qq}L{t$e4>LbXg!hKy_hk`_P<{ZS-&zHVRrkjTVu+fCqz-Y|VsX|isI z8K#oG4^LA1v-=iwbJjX2(7}2bYfXLB6mb(HR4ye6H;kmxK*maSI!{PUK>Qq|D97<> zrx_>zCV*r%7s6N>1vicBAUpb^uMt>jtIjI)hABpU7OxW|7nMO@U`$kRB59+Zt9C#TON(^j15Omwvg=)P2nN!SZn$z_- zWjn4Za7fhx^k_@1u-Ro*jEf$m{^#%r25QP-45BNLX#VgK2P%?KxYZCAf*Q4qLsp43 zPZ1|Le#?knby(@Kg~Y@`L>7t=h%J}Z^g>2BEeM@kQHbQLBk7L{OpyjchHW9x`h+N) zi5{_rjqq03W6x4MRzKiwS(&dw{&3}GVMo^ut5p0+O?8^sadk3vjR$PZ&dQ2(y0=bT zk2$Im8S&d~V^6Rvt<|&)ozXv57!T`5D6I4vrT`KBNoW=zpeWl>)GzEj2#H2O@9iuO86zXrAa7b`P(&mw49Ax@Nm{n8sEO0^y5 zg4!m9Y%TWU{n0s3%CID-GJLJ4O2;qcgzjPNK7IOOanvoW&%oLi3Qo)-k5w9aQ1u_gNTD;VS+IHyg zS}6&K7X!QA{ynut@QS~%Z?Vh_1WvfrNyl~fFz9HaZL?nWNRyu zf4{ino!u0YI)iqsY-Rwi+n{N09RYr!SytD;lQ11gu z&rGwiRKqg5>2jqR_7QqG@FeI<{;qHi$rpg+*BHlL`Xl9(gn4} z?~AuBwu^$`9;RsZplU%Y^vUR@fl(@Jdyg-N6NVv?RY;bgQ-!SU6AXGl-%BIBR#7aV zZo5|WK&5fJl>s9T=?X!`&9>AeA#pK?MgowaC=Vy3OL>~(Wfbe7UfVM=5KSlqmW@8X zS|)s!F2OKlkeMH&I!i$EvPMeiq8%#(xS$P2C%X!9x0=cc<_Rt0m7az(T%S6ur$#d^ z(Wz($4{Hdg@C8O9N*CG&x(%{^?J8vInm-(SBUU}Pe63I+JE;fO2fy^|*-exSb1OA8 zc}N?~%R`Y@{6d>)T63s~mlxY9vi?%pu|}_RJHSiaOHvQ+7A#Bo7Z}30-co~+h1BUZ zBx8TELQ@)^fNm~Dl)h0~a+H-Cg4O^)n!fB)x=8`J3xGl(&~IvO#Z;^lt5+x@@spKf zJ(tA)LhmtHB4BPQtLr4M9H7ILBim9%4VzkNFLb1_knFfra7rqZNkJ48T1X#PnFwQo z;7qz8+$pMSDsvd#>m=pH_#W_65DuvCONvV}ln#u~%gFP6&^T!6$|u>daRNG^_nrI0l>@i4APNzdrcNnWQ zJoB4efXGZ-LEDQA??e$gKGu)6;X}|m*r#+}y6KR!$Ld67t z`z$(~kRr<%pn;=C!w(@<0b}AG5(ff>zqb`rb=hi&#-LwPX)X{J;Ht_9aJMgb=-f!9 ze+s1qr0%9AEeb~6LhO^LoKjFggdeL*Nu2(YRg%j`OQp$CqE3@npbG{Uj-|3%Mbw0Q z^$$$Ta1=z#HhET|oKvH;_$wsFO0#I%Ne@A=`GGjpiok^NGbXa(NZ2HcP_;pK(q-uO z&TIu3X`yD(gt}PJC}1A8Ho7*m!{@#KZg5zc%%Ih(g)&3fb4af}qICXtkoZ=8IE0aE z+3F`nO+*^(nBKwc7z%e!1^cmy%#w1i&8&%!sl*3fJW5}0)@imjtas!>#o78(HEcjk zg)D1noQ!2aPOm_(%WFenkkEEZlKw%-#;U0#FBGRllbx+#Sqs5UZlWt=IASyb`A8P< z$8ukk)p)lVY4EZ@lBDo>1~!q4qINc2EWuxF(RZ^4vVSq(CjUJyCUYP^buhUFN&ym_ zFCK%Y_6GzDnX+PQt4J_Nw-f>r94uTe6$6*w9x-)U2U#A|ayB+(9fAr*vQ_vqiZcm= z+b-d?nK}ufRSx_t7R~E2z%Pzqc>i&1D(`i&1Jf1>aYV8COrV$%;9hgrY9KS?WT1eU zzYU*`3_8Gb5hpdeYHmS}p)6+!sy( zvhR`@hO5;~GbPd4t;wREBEYJGC>Eu9wAZ8Svw^C)ZV1PDs4GDy0YD^u6jT_h)}YV_ zh+!0eM;1#{9l^cyP+)i-T>;>Gri2&37BwQfw0_dP>+~$M&BX{wz%Dakiy5Ubbj)94 z9-P+CWOvWpg3Gxa8T*hN>O+wINx5jw2X*}2;NHWGzVa-$AmfLP(Cp>~5IayE@ZO8% z4Ci!HI7qH=HX;L51o^!Tbn9hg9-2aku?mnnY42MK52b30pzT?yFm=N;2(B?ikdp5-Jy`N5Sdlak$UeDcb^3u*v%wd{QiqvE6i) z5RzORaX7L(T{Ixa=BQD3@iE&D{zQTSw(lt*{~=6!|A=-P8jdysMO{fV_Dqi(lmRKt zb^!#=xvJu53LFLaxecWpPT(jA)PWXAIJ-h09NMKMhc%Goj2|qRX4l>i6}!JV+MiF5 z!Jj_ya+G$4%xuZd(biKK2_Ov6lZ$V?d8Yn(D=v;YP@{f1d5tF0m9QL(8%Aujm4=Ug z<)yujGT&&JTE{r=v^`~=*bwRkm&U7H3uh+$Os~1(O1w6u`skTy2J%`1ZZMfrN5|jx zF@6Szn%s6;<4ah~vXR_-jHczd06#qW?g&M6@fQGqj>b+;L6{Ng{xWo=Ypm=TdTlxx z1=LWc`$tG5yj=(hC!sQFAT`E>U?jX-9uR&6y{)t@kR$$1xq_a$1zYJZ+RGbJ0CS;P ztHeLwl<}*kaM5L>>qUxC8RR&-J)!W~9LAo|=bk*xWJu_yc@|+i93^iNp(%JfW()2` zVmKiIpdTseFC6$`0cD|_W_-2=(g>KkM{EL5pz$b;z(0y`2~I4HOQwH0 zc~@{G^IBWcP*(C$gh*5j-~*W^B>ul^+O;mpupi`ONeG> zGtaMppArnVM4$u0gL5O8re-Wf2S^c^1O`;2hD!(n5bj|&OZ}MAN2EGOop8;ULH!0B z{bkR{LB%m1aEVxd#g`rFq)B@^#4Qdx_47akf0Fcf9mSYPuScVLlUrcDrS<4{a={$O zF5NUR4P%xK-*WPMZeb=tF3f?ZXpWzUnKbtj0pAV=Z;ire?M%h*2&bVimVZAeg7L2rN6o!DCGa>~k@2`OxDSf8lMQfFg#4 z;6H%7dw6|<=K&aKU<6DgerYA7)F(*~uJkM{0V2Ra)tl&=TZ!YVo9*D^1KgN~g#Y_+ zK>jUyAjyB`w7Ic$-pOsQNiAatWKh{L0=ea$CV5vkRLHA5jb zLPft|NhO~w0UoHunaTWlASXyb#VvBAhexe+9c6izh>$cYp}JPJ>( zU}4-U8DYQ>aWz9M`1>4{PRl_88x%nO0Pl#nN9_N6FnEAi+uCU-1~7l(0XXw{@ySDq zp?Ni?0y?lDf&4sf5VT@D3`MI^4qPQLF9Nk0EJiX%${;j+@&c{GM_f636WE}dwsO`2 z>WL&K1L4*{eMCTcKZj!g4~Jru1I7}^=Sbi}W`J)X=SpUTa$)(<81e^T93k9$^i3;) zO_LAe+x%{5W8D*sNBI&^N<&5{ex-@fxgQhL1fzBy@+^`rj)r)WN}!25yDuKn9b=I8~Z7sshL^~~5zNl5`=1gxj zRn(m|?kpUBmdB4oDo~mq4$)2ntCk$(LVSf&q}Vsuq-%OKNf#afK@yq)i#e}I+WCbb zT`CJfn^pX_g;WaA)XtpvMpMCmNw}GPcpn=fsT%)QLI~ zjFL1yndu@mVk%P*50L}|L-EL%1Jqg}0wdbuQAwXZ;*IVe6SCq;54sS1iD35;eH=EP z>%v4x=u|o47LQ~t8~u^TbLlm74Dr}w?EzE6$&m!?gXf2*cR)IUbFs(T+o@q-P^=18$v|pjeR;yIH_+pH?cFt@}mZi=3&0oVKU>rpE}L;4CJIQv3c&5Hix@j$!H$-@HL1 z`!y!_z~hqNu03o3yp)l_~Zx_+UulST$A+jzX^I$tZL zf+gQZqri5PlC}|uh;DK72|jh<`N5?tpT`S`j}|9zgm|}NC&Hs=#4ue*4_exE37}tY z=O2fWb{R0|-t7-r0J^J@N-7a2@!aE5G;br54IU;jMJ5UlNyCZ(K?OQ7h+Jzk22$dH znAK(qbuDXfljVJ{7+tFj46ED(^=@_(ZO93nJ`$I0*^{tOcMOxC|5HOZc!#D=lJ z9xG?>5>e$wL}}+qDB9t4xq)=9OqZxHTniYIb-lC0+#9Lv{dtvcNWHr%R zh(dAM6}rZqr8&TlBOW*iQVzt8jmUdIbDh+x1HQDRgn~jTS4L74 z)eEV4+dT4Eg}A>ZKOneWBnY_-tM|jJgRZbZB2}TGDUfVV(?7!CsK`f$ydOGUiXWvK z(Pb&~_60E>c19+*;7+c(jc=$mFb2?FZ%!{6Ez_Pb!HxQcEO!pCkG3)3Ovw0G^xXQZIW$TKP-8L-HVpFG>`h*O%`R zB>|-6390sxPZ!+QyFnI{W#Q3s7hg%24vZ})S?)c7aLr31Nun>5Z??DsrlV;!LMO7y z3Y~cabT&N$j@<45B-)(wSB^jv0oHg%BV++NX(EpK#iLW_NSCzfe}E|>%nCjml~sp! ziAA-yfMQYtT$1W^5RfVmKEsM#eH}{c3HlJFgRnv+WA!;B%8=`@=@)6kuY;rviQ+K0 zz4v19D9lNE$Vn1FbPtEQ2j+ni%+E;3g{+b^bgB`Z;XEf2vdRskZN6JqWl%cT>{K&?_q9pF{f3L(1mtJE~Kw96FHEr65SgDh` zHkWipdeaVr_L5JTfZ!a#0kH+NLRj9O4KRsp*pUGK6HArnD-mJ10F0 za5I}Bl`ZXSeaFDsjt@_f?c|WKT$B@RPF*DSvtBD(07|(t&54}#ZV&M0=*9{z@lo_VM8tYcb#il}?6{P%K?~trigrta#kA~%_fv9pl?Efq(^3+lh2!rIrB%i6u z^#YPifJe<#DEY4u7)_m(3hS?1k~bJh8;k-L@Q)*j&B_MT{1f=f6GC023KAoiCBVU; z?lU3Y!8Cgqjw1rm?u(NT<0t0KTSJk$kpqo70za{4ZApYVvqBAToC|@`5ugn(Sip2Z z290r8_ZOn{p-P_S`WP&xd2B=?$#N3)-8D5O#i)V6~cq{C>Dp|FiP7mV5O_Mep5 zBsR)rv(#ezb3wmBZH+mlkr*5dml*=3&jUEd=BeO?Cq8h^RiDqpl%`>eOEim|CN?+0 z;V_a+$1l(;nQkIylBTM@!}P#P`MH628WnInEQ#bW0GCqBU%Y<`4p7WkCXDnxl8nV7 zsh$c^xsioHJSeDJiy*cZG2m_;!y@W1fQvI?mYZifMF8i@WEyz&CzrNNU}2QZ$G?w% zMm<6;=gMXoa7i){J@1*H&W&)6r9Lyjqah1JJ^x7*jUj540h>;H(#(8kI&Kw-%orE= zf!2$M$kJkIef+7N%$fixN>GQ%GJk%>0c1FDH$SATsih~0$}h70((pa-=1-JI1jr#G z7!%bm06cvlu?y3{N2)R<&uS89p51tDnsSDY>lR3rgX045NbC`4VM!7WQv=f#5$ZSa zmnHQnjm08VzHN!>jEPT_H4_RurUW!iSfMs#Z(Xz@4beF(%m!Np^IQ`DBB{wvpci5Q z5hThB@}{PP$v7p-n0N>^cau!mutBIyzO6vUra0wAz#~^!XjI!VqW&ZVggRnqlxflh z5N`-woWd#q>e3$>1s)7?5{;}h5*gAEES2_V2^R~7RJenVaQ=0|Lvk0EQ}YZV^q4?F z9E1lh7(?Q-TCwc%fqso@hck+Zt2#x)S&sm;Tr^Gjihu?`ckrJ~G=2XX8?QhHq4DFO z+6A+gdf!;v!%uEw$q2V46kD1-tf|5`U8_(5Q?Q$sr%A-uSfZdn!Xy$IPchXeC?Em? zFwBT|_NhCli5f8V9V92=Ndz4T5IAIwWmmiUwFUc7?Uaygr)!>Sg@>0VSJ9DLge%S$ zEknZDk<*+^y_;FuLQ(;x(?p4-C^8+Zs0WCnc$|nfqXI_Kzk0Wv zl;fmz41-QKTV`T#B>E9WQj~KM)Z-j$L zpAyZVJL;KldZ`bDz391k(~|oWQ`(iV>J{DRMh-L&7Z!4HTH4 z1TZDY5_5+jGVE!=!@CJJ!UGMrn!fL>2X|77N z(@_3NG=%ku(wC_y==xJkJfwLhaEy^@rUilnRn7s?t$P|&)Qst%_8Z7dW%zVL_Zv|N z1q&T7K?p9EZOS3_cWb1)94ot-OOkRmF+6)TGGf}LadyyXDv7hmk>?VMWX38eE^6Gy zN&J#z0|IKt_3(3%K<)s%b^s;-!qCLQ=n8K|aeVTBwrK~!(}C4mILZZDT#`W%@ZLl# zmD5=*C=@>F-iR5uZBL>GdA%1j-il&ievC$zFcTg;y5XMX1crDiXwb}1_~fJp&NKT%Gy@*E zOgHzC&S^Y~8JVs0$4#LsM;Izfx!x9vqeGksQkDt2N;<}o^X;Q@6lf9a8mytFaAu*W zss5)m4TI03tEg^I_W~D38OE(=(4$HtV0N}%v=gUPM>RXf%u_#6no~4>T=>G2XPIae zj(IWs#-ykp_t@%X2Z0KmHM~6x!W20^hJ3?+=g>EqdH!Y}6p4IkOg;UrF>pf6EBYsx z;ixU7B?(mGK#&n~>_Q!fc5NkT&b0z@V8DWTOf=<#ns85HN@ktVOeCo%9;zCyTGTA5 zyI1<--PEDGB(lx-L>56Vip;-6A)`fxfaoP2Dy*q!fJDxoOW9=VC?q+eSj~z8J4}LB z9u_2)7G2R1k<*lVno;249x{UqB>yW1GP3g~1S^i}@=V!frk&T4V#~HB##b7Clj|x$ z^7I5@YF1dY*&Fj&KWNf^wXAlf7wA)LZydM)m`TkGQW#Dx3g=ysHBYM~zGf(mjaEr* z)+%X?RL&d!z~$Q_4)_%iFxA?xf#i=htxiVa9dB9aD6o^I*K***m6Ed(bTn{ z?d*;XpeN_!g{*ipA@q;n33 zZyJ`?ldtqR3p8%Ol9DX|32bQ4b9F4D=xo@IQf%4{jwC2l=4&@*)XhYm0NJuqJc+gp z>}KrR)RmzgS#Gjj2p|j6jI&*zshd&6#9}q{kHqLAT0ImHKnu{oren8gDA>CS{lkc` z0|4eNwy7zZeQ4m3SpK~k=cURkxeqhcy-hl5%aOCF2{Qx-xH+NP(2C8 zftZE|`|0{Em@R00RUyCUPUPd00I7Bn| znKc&e`FPlDN;wZdL`nr*EP~+Mw^ms5^qj+bHxTJS`zF(>)Lp&=v72 z45_BDM99kzn21u44&qvanrvNo_Uu!1!!Cq=RY_sV2!^!CLNB3lTxT zlSyJze&VIml|g9~RsS#)?*iL*W|v=6gSK0*qSr8BBHc=`!CpG`zIgp?8Ve#zACx-0 zMpxWcW|^GSHELNaWdEo~NV^AOE7Zb3_@OuTa}tz(!YB0Ja8V^n!?LvNZ$ejRNC|Fa z-$7bnCY2UxK@1A=8sTjit^k-%myx99Gre~XUo_u+Y5zb+W@_C zwJ`yn4Y97sB_KMdwobG{dKZxN{O53|Jw)^%qBwv7=h{{s)FN;tW3Ak_-dG!tUDRV*a(hVGc_PP zNrSZP6^G{JSZ=&{Jtc7^H-SDo>{$2Ar$hvh&l*0~BJ>^pfv*XAEqEjvELE+|^(FnG zuICRxZs;iN(<=$@%tt6iw}40_;-&{spY4N3=Ne_B+igeL=x`bFR62JCx(((O?wL<+#f zWpN_dz!82JGCoqUW(0+Y2KvyZG6JRG36;eufUXYdHz95^ps3Q6LA$?ub_z2xP@f%U za;I69zhMnt0O~P{&=Ca8K=2?mj5^F1du3FL9JKBp2>5w1&aWqM0L#+pz8peO;!L3= zBy#zUbU=b38rtLVNNLHTPkEp)-ch;?0*#St7378(+<}}9JYcFBcH@Oe`2lTC3&>G# ziU%hT0uRXUv)H->^ss=ht9Y*OAAOWo=D=@V8isd_?@`Zz7J)wdkx`c0u#E_LIXNsCpF@N?T3;WaO33A3EZ_dxmUUAMf*VsTtB>_8 zl&mF-xeW7CiFwi47nj2NU<&6s>==}kHdM7?Wub<8v^Z@MC|HcVwozobK`Dgsonl16 zv{8A^f-A zoEiX*mHGg<;5-6?lz`fiG*VPXA-f4K9*W46UW`^p7^>lN7ZO`bx*&7~(t%L6LLj<1 zm%CYci#MuqdOfNnIST%ZSSJd@^J)qYPkl&#y4N z&7t8Zx!G|WC?FbT@$sa^&|5lr9?)&#=*XeE*&`9Y53!}`zc_wtpt5H1kVL?V`Z45! zPK6L)Iu!PQ2RF?n^T6s4@yfP z=Z*T&j!>_I9)+wr3k~8*%bI_R^4cTNNQxQA5zJ%i>CFCWB9SyX?i{(QG!DuqUKmjF#uWguh0f! z_N*YiFqe`!EVyAixR0%cBUX5Yobv9csKA*U2+xm#`T2RA0YQ30CNzO}RU;bZ|E$M9 z!UH=T5y>9B7I;H1iJ(=j6Zv6k|BhxK8o-pklva(Ti_0|v+|X)BMknmhUVCCi@UMd+ zx9JnY zFSd%1BYd@W9yEygIU^r%rjCKx4q2^F47#nhhLgr^z`W3s62irvEd@zdD*D#Y#xh0T zGYN}53SUT1n&)d@>C<6HNT*q)j&IK5w@fgI+$2xM8eabgjmHz=uf_0jq0~%*g(A$J zclF3zozD9Vv-UM5H==T8JBv40XPOMOsfwh#{U9QqO%1yE)qh=$=WCgv=bhQtYr&ak9Zn z;NtKNq``-vxT`V7L71yV6ALsWlew9YFi{95t&-|jaym|fNvKTgjV37xN-0F*9K7H}njcsRcW!Jo`pg(9OJF108B&bk?k|3d+M+y;y zY@jp1hTF*V#k=e9T1HMF`XLW*v+!u}>>`zNT&Ct!tqk^J{LCRyzade)oVPL68GmnYzi1usP6ShfLzTorol|25iDkV7!;>NUGJ zy2#Fv7UJeP<0gMyR7m@G!Q0flCju`QFgNf#Mf*hlf1wrMI2avQo*z(6##;Qrd-x)Ya^NZVwRf#B|zH0BTH{n zaY3$&)=YM4JW;P#4|Vju&UD6Tk>ZB!i1Ly3lb)692o12&CWUB}mG@o1OgDR2*fj;7g5 z88~7LoEMWmZFK!DAgL~|Gmo6WFfr>`c8cu+G#o!*yd6P321e;F|C-ndM{g#}X9rO- zqy|gKh5L)Z_|*TFs*Rvh(g}E$2!NC|WFp8<%>0ItK=C*2GGOH@nOqg0D%&le z?tDSbJ{owa0i+eZ2AbW$kY$J-79xaT)DVmhfG{B;wY)A4d z4}YJ*WZ=1f2qv{Bue`ooeo)UM2nD_kWuikP4Ld=&vk5wk8f6iwarWM${y&02rBThe zZ4$D0o51L08%=gSc1K%usnM!aa^l`Fi6&1TLA4j~2PXHzZK6l#nki;@t$=(Kp4|3P zSQ~&G$8ZKpZq~QCDv8buy&J4)V&a_@d#6>K7M_I{#OmTu!K|%e%E|)V#>hU$d<+2V z9Co{(k>IgKrHryr-j)?P+jJc~zFS>~iC zI=~SGy~t=^&fMzN%&p2DPB!fUu`Ah$Fbv3ALazRn(s<5zBev5T>jX)?D_|4OpMz6a znM#HwZ>i2nJH^^!s5hGP>$m&+8oDSLs0*zE7TvgeuePIqIli6_P|4cF3pq}My5#ka z3RzPoAGUaR3i?6iUX3mb0VW1eFTU4lWMSi=Gh9*zQJ3bf^(l69r)C}(wrXvLo5}J&vObTzoYMu!w>}ozr z4WSf!I-|rnB2ppRJe>9fO|#^m3~{nyD%}ooaGUK`&P1x?WN~Vy+HzCfn5{l(u%az6 zKs=~W6~F@uF|e+(EbG%kVAG+u$AR%B-Jli{PI9x zQP-k$i9C+xIN1!r!M>l>jbIo!Pw>eRFpY<=L@m*))o8{ZNF%>M$vmfY1Rny2do)fQ zzvj3maQh_s`(cjtMg!{JAw0PysZ z)sl2ST80fBsCLuFwP*z|sUK~)He*hTg~6uEbSf?lH0f!ZuG$*?r$Mcer+R*->>}l}YQ~i=Aa|U$_;K(3*8X4{nMmL|SCYpSl#$Zl72TeTb z_#27q5l`zbXQ&M%lWom+jEDnTyx4e=ne6aT{bSxDIrmYf0!{Vy)XH%rz{MG2Nd`mE zWc$i2!D$HKH2`7ddDaw?`T=Z9l2~aV0y3D27vzM8vXOZj1Qo3xNSVz;Ru~; z`*9xbsp5)yv3M(ufi~CSbCgYn7rd*mXhNGP%KmK+RHRtQjaZ9`u^X9D#9D#M*zSSr zP{fPW2GB_n45o6|wraWwz{EB@#tlmhdPk%x8b{W;@M zD?-JfV5h1-^GgYdW1}?5fl>Gg!7$wz6%C3>aZ+3uBSr%;$YYJpfOR@}XtmU*)yyI2f|A9Nloth#i%J@`96_lfmWIGzo7j2) zUj<4s{u9~OttC1G=?*JV6%eBqFu<#V{?%RifGv{)lGqdP1I7>*{_%3aQ!oTYo^1$L z4RFY2c)=BL<2|87mpV}}dU(c<{#*sr)pKJRP|YcawsIng-%wBwxtHO)hJz}gYeDLR z%&-pG6C2RvN?AxP-8K|7dMts8>#!w}=EC{p3id5d?p2WzM1W&V&p2EpH&tYlm`{oM z5|GG{?lnMEN6J+PH9*UUA`0Y2EE$5wf6L706@eAS80s(|ipHN!a0F=LlE9#gD0h@t zMCawGuMG7C;UmI|nqaI<;&5>pW_uDykkfEcUQr)IsYED=1O+gQ)RQ?uJ(efmiuZzs zRBGxd$?LM^5!1@>NMQDie9djQoV7B@j8~J|A*9 ztWj{HHmvoFq%m<-G6UpB=!j^TAF^Z;7mi>0 zdIjk$0(t*;{PAFc5}k>1!iax7hUWp5x6kBFkC~pD#w^)p*~6Fs!8ty6^Z8YN!VQ4! zC~RFuu-bEAR4lyCX+MQ9n{1LyPmI)6F3?y*1>ZE{Wta!gYV#eSs>>MROBR26@TZCh z3^RU(RUh*T;<)tOZCZO4v^*RbSakR|bK=1-HZ6{F}y8^@-}1=-brbKmj=o- zGdz3q@?u}C8E4VXuzv1j)8^^1OOYd#%Xm2B%m|x|1V+A0ID{XA7_8UdANkmfyvm4V z)Z?4;1IYf&cYdS)6sxD-CD#9>BXI4=wbQ4(b>@_KWOYLuOZh+4{NUPMVS(=sE5T$L zBm?>?m(A?#ky3Bj#Gd5PT_HrJ;y%LwC-R<<3&xf6B=BG1!75k$(GP4th5Cb$qGyoc zpzikW+Un*{m2oHkTd;Q4!lvuNDLkaDfE2JwYK=s9&YDSJ#q_PC|3C&LzSN|N(hemU zG9}^l0MVf@Fr}W}$J90M9NCB}=4B!f;v8Jzh_*qMt7+zBOg4m3G7uusHCMA1qRY1y=ef_lg+) z&LI`DIBq!`VjrYJNFn0PwXbPuol0)wTa#f3@YVU&Lulm^NGJ1@h9z@m8vco$Q`W4k z{dudJesf<*pFylu&ux~NYQly{v_X@>v2_+{{XbO8Z7QxZmPm&#u$uxlmtx4dH-XR} zt)yB>0-TntyoB4{LeRm4{W<;n?>Ve9529xEZV|WnkNu?_mzo>x4mtQuBgc(7z! zxYLnx!(x+~?zM4^E@_$lNlP!~&yj2_IclA5FKcE}BvG*+dhF~A0<_V<{uIGfw*~tt znIJq>*UVVjqT7@kziM4@_@n=>N`}Utp*tEM{^84!Y9GWfswpoFXj3z>?WT9jsm_iq8O&sP;wtBuQ@oo^)u>ABpWtfU`qwUG7#XJd4^c&PgciOSCsj-OIV=(%L!jqOcO}vGa0NDJwf-bbv zQPapR53)}J$Et+O#y+GpY{Xt4H4}1T5_qlZm+q8faK=_s@7x=>{&0_ z>^_16qq`NTvACcm2a|-@O|^JaRqv_Xuh73O^j)}X@n6}aE$ID{=}nuk%=k_W^&)=O z{bsYnK9$?78=1t4vQwjLw;GK;9YYtpYYu}HFm@9%=0?TH17-V8fkvJ6OUgB}LAoi9 zjCn|T9e6;cN+u?j01N=W$O?^`#_gP{KJm|5JQ_mYA-WKU8R2K4O|uQH5Ko60FZ--B z^4>OGG;dXl#J!0wtqa&>YzD=H+$bDrH>*J?C%jSrbdu}A4`M)5Cf1~Fr``;ghM z0#X|*oC*=E6j5Y&V0!2)5ziRnTfV>1CN~GBwHdnE|7D)_^3wB3va8@y{7*xU+CM;yC zh=!_SXh_F&0CO!IYKVs&PABx@aTOmish44egvx4-4E<4Bwq76VqAR{z!8v5iV8auz zt%WZ$7(Y{GKvz9aYGzGAJ=0{%iYMtMsj>OUX%xbL)5^=k;2inI7+qcM*M%KVr=tlI zz@+uUHO2>-@%yz2(3h%#(Tgm@tjoQ#_fm7-ZDA*Q@-?$vOXfa8psuUU$ ztSb1`!YBWIgE#FFizT9t_dpt?0M!QzoTVBrEw7f{Al~yOV8N{N9S*ME#!oSqHF1qR z?3hJIi0hL`s1VbiLBiKyDG38KTUGn|x5(J_l*} z#mxQf-r$%!My<9n{OZ<1WuJMv%Nf0>(*YwlYH#jMKB_2JD;eL0u;SN-VHLrVTTsd>&Ty3qA9<^9ZI!|r~hR}wfQ?%!)P`&SE3 z2Cd;0+5041_5+hM)vh_#uMay84eIYsujr10oPVSaGTw&ljxvlB9W@Pt%EQYinCt?A zy$eu&>;n$>u4Y#6E!bv5hUWYO@`7WpL_!MThZ4%|4hDqcw2_BNzSfpri&K4ELM0J3 z6oq_O%;E|q4VmDMar_=uQ!;b5EzJ>YDLCQpNs*3 zZ$rlXgI#V4U)Y%|Kd1lZ{BWf%F zghN&bay5F)scf}(#P6V;9JQ03pvGgFqRqLc37q|vY4fyMe10p$C-ocfujhh2PBWSc z9MfF+2wRaKn=e5GOkfj8M9H0~*|coa`$*ZjYAD&x$8lex%Q|g^2*Z@++->UIj0~XmZZ%;p0I1AJX%|{Liwk8W(5}Pd8!>|B zBbB%ViS+5*D#UmmvPu<196|$etOT_TwC_E|t_7oB6K#8asiBZk9GD0~ODq>6M}TRc zO(g{i|Axq9EthCOY*C=7qY>GFI|mCn2-jImnRlzG%EOglEVnOt_H8ieFJYQL=mo|t>Pyh zvEn3(M4h$~E1R_BDAPUF_9@N5x?+LI!6Gm<;M#=50uI3F+ejIi_c|9$28~t0awtNv zurLBH33-PA^g*Ji*c1ZW*&t123q0LHXxS>-VvC63v~N426zkZSgEf@1`S>XRcS z9K6yI9>XTwTDXj}-Q+`7*(xTk2_m!3Bf!(Z#a2D7U@$p~fx{%LhAxd7rw7gC^c1S; zw1G>v9}NAHLldP$psfmk(~M2I001RbnujIq&1ETpTx!MSZ&mc>fT_J_EI(d5QU=sn zt!RhBJ;=vtr`>!Z(EUU)d#K6lRudp)UdRm2aU}4awphoxNQ%lK69PoQ^YK`XpEzLd00K@>zS!a`cQM49*&s0u9xA63HWOG%|8+HBwad43 zHE^`c>AqdAODa=yzayzdu%JwF1Bs^ELIm~ct|XDF@&hLT(j^Wwuv?H=EClUCc#9lT z(`SEqtJFsqWs_zXwwpZZ-O5MQv~9~Er_p}2=R7$+%A(-*(Koi!-o(+}!uEMT9x5b% z%4JH9N!M3UBtrlZe3Jw6&;{J@UoBnrZVoB4b#136=^h`@JmJQQU#B2ZJemqSVVrWT z)!xhfA`7Gmg!UU~(~rrghz(4Qb$Kp{ei|tOft({&MsD*kF`nlY&jS1&3f4(1_$ym? z`s$p(?ahW&Ts${1(xPT8webUBZm)~__E|MqOuwf*>C>K+85D8PngJLr5`|Dnw^PHu zf~)|#!h9x&#mpQoGs+)Rk}!4>0Z^zq7A&F;#9hq52Y#6vg@zg5Gp3YPbT%-uBOk}n zN)iK+67##5HMbXwuok&w5*4bZN-EQ`z^oz!{cdh92^ydTdDE#P?%GD=S);;-Z32Xw z3<%{ULW6mwtPXYTtf*9Ct0GfA&j1jvqki0qeYw!tM6{-rC1W&O1Vx*{1!}0X=C>%} zTub$5$cM-xAZG$TJ+_;XlE1`e%|mkGngy@W6r~g&5KI?xxtEvC2!qG@HX=YsZ}uLb z5K-!#UOiUMT%o5?MZQ?=+y`Tq(WI*NYk{EZEg?Nl*YU$Jl?p~IsZeIhQ=AvbDywIu z)PHF}5vRM_LXnchtW}LiEBlXE-^Zzbef}&^SX^a>iq=R=l?<6(DpXk?Bo=Jg<`4XZ zbN-}~2pFwx$|l%+k`Bo=GpGqzXno%2^`@Qq(p_w_S3Z%L+FQL=kpqQY*t9~$z6@OT zxLHz$@c#-DaW+pSCp-%4{^0QmAaf6qkbKq@-#;-NsT!#aMSQrc<9SS-kSkxgI_Hv< zZJlYb?Y%E8MsR&$ZW+#@@jND+?7Z{%Y&TI6EHugw23ENrm6liRG3nfLtLf5k2nw#? zn5@TvT%1cyTfE|V1%)wTEQv@QHN_)3@JaY!K~0p8iVqIEG^*Voka$*gvltBtXf2}< zmZc)-N%(5}M?0WewdzXPM;I5f=ZB{8Ag}F}Qs2&2XO;MD!fmV@A*B>}eG8!L2u+RH z#r|Lw+~&^%v-e9&32#LWzC~dpN@L|j5I=i%^lu9kfUI|=dH*+7ueEJ>UV7uEu$RR? zE?OF){m&%KsI|P3j**TigTs`#Va_lxTGr3)8bF9RE`qr^LrEh#1+5Dh8W{uEyI%ef{CmP&T25G^hvDf2Z0<29bOCA~m3C=t`Q^;~97aCqX(*!3A+r`>4w zg%w*un2+-c!WAD2tHs>LheCQPhtv+Md~U58z+jac61U=L+}n>|t+p&6l{LWd9$RS! z)oHL`3|r7BL22~H&piX6N??ge$)oy*LQ+7@8auf}qsggu(X=cy!y;FC8ZJ#nwu=Is z=4=oODATm4kW$0W3G|EYTHAX9^;|^%Lxn^0Nq#|ae1#X#Wnovap)y<(^ezl6h>2F3 zlLpiNZyF=mMh&I;U&^0P*&eS;q8oqm9a3BHQiWG>=4h*+@#ER$;pTLj`gllZ}5#Wy%WLR>FbAtGg+5ri%Sejy%_*e^)d zkcy~A+=AV*rqvP@V6D8|fpco!*Vvpwi*;FsmP$Z6;`g_YwbpR3E-Y)%A65)`(QtK< zyW7zrDPTVXbd;UBpTUPBK0tqP2n^Nb!TCqf@`fXm@}PiWx#IZ)8hZYa+gYZ+HKSP7 zIY>Px_C6|2QZGn)lH&^w7Hxh|hU-SVaeYr>!<$ucInqRu8Y_QbL`Swcn{wpdFeu>B zPvW@GR6_!gBtz>Iu}0W`p+A_DB`?=5xP?)q@C3oWiA^50QPCff;4+(6Sy@B_V74?6 z|CIrS(Rljkf-VEXv1@IGp-F`c0%Krg#vLsOb5o$Dqqv;~_Od-9+WFpa09K0@KK>fr zl~L}nMoq3T18fi^$w-nm%e50K?-I5!nTXg_dj>S>Ou*D>C{@}MyhUoDVBgl2$BwsY?npeK$l=UWk}^C?4pn{K;S7_>cAR`d$%;F$em4Ho zWr@lFl>`Ari@-QvNzsfBz4D^A$_Q(<$)tKljID_l&L4rqj3B@wvv%T_bP*2?H&rxo z2z4zkDa!(|FpP&<=A7QaUXm4K# z3qwti4g?s#1A(N9q%Uw6wqXj}n=e=hQh2)OBmTLzw+KW^-JFZLLd3IhQgf$mq8Z;n z*98ovA>oldyuq`7f2KSSIb#{mh|RyD1b9S)o`B#|%D`p?oIk;iCbY#DH(KC|2lUgS z)y%HoLBjn-g~^lXn`>-a_F)OQEglQ0rY%s)ju(-3(Wp$A^$iDB8nyO8~pz{}TQ(rhm@(=wbSZut=8fThUrpo1Wg zue6QVA_IA?ZhpWAO!?0~^3b9v@;uiF>0nuuNikjgS8O(4^MX(`>tELQe zdqh9$n#->72efMoBRBdF9ncqaCFlrmXfvt>B$!&lIjUpW)A8UeCl>*ov2bP=TH+Q3 zr@}fpHPitPL)Co&cBUX~(Ix?MiQ-E6l-uBv%}`^{31U4T(mJYsXtxMNWIGyZRzP+x zJb=%_3X_6PcB<7D|w+M%N5tRCu3C(Uc^JwD4Jrgk9M z93H;}q%$Y$B&05HLuD;#F#HLc`W*Mdk#j0=D~xIa44tn5)?)N@MaODjTOFz8PC*xt z2f}xtl^>Q|SO77c^-L!Gb|e;s{XoQHd(#q+t@=ZHqR*5ew+_+tkwoYsa0)osju>xDj06f+-XFPW9GwPk8mYys2YbKRX z`yUv9s;qKnPF@DAhd*SLRuK`f-bf|H2i?qXI~9rmgNPn8arGmrK_%D65kBKOHDk@b zYa^Tvv_n-MBnwM!RY2hq8+mp@i}H|hKk1DJdS@W*pDz5uG+I3;7rEF2NtvmOA|e=B5- z#sSmm&#$(QWOz8tj{thITI~Zlk`~eij3wy9{T&HpY;oLH-@v+Oh(uHkqCh+kyhE$6 zYsR-LSZLh7Kl; z&cbhsfR69sUxV0;U4rkHF(EPvPG6tf@=SDbXHuM9-iBWZK|D&x784530fa$pYJ3=X z=E6B}*OR%W(ZQOp=e!7tG*KRY0?ozGlECpx*O<7G_+0p<06k3}3^-4O0^V zzkiURlPpXdB-@|lyTAgZ|G#rNyN{q4>j?K|{i8uBzZ(D`Vrl+F7*-LNP!I<`Xscd? z5mN&eDO5JG&zg56nuu4?bXx!C<2y^?+bz$UxNB?xt^-)8bFfSGE{KiKGG$A-kkghwAdxenS zpj1NtO>=N#{Td2-i}F7&!KXtWO%J-b&jkG!Zl4RBny z@S%)4(PvpeK?leR>C=8PvfM7$Nk!-jylo*cSxU$2Q$iKM9kB4&gk1|a)( zONUwN>`#;|N3+P}od2wI2DnhKLaZdarRCu@M?1tB(3Xx@V?|4wvc?XzZ!(~wQ3EtsX5kSm%Fu*onuHahp)HuOh_L27WRfoK*_4gIaD)j^5@}GGTN!?DkQ1%b zR&gP?t|s>f;zDS>q6)17`!+l~lfE<^({9({y9bRmm|Gj>%~pxCLhu03k>Agw(K>MC zl_$j~WOZTszDJ@cI}5@KdQ!|kmi#~(7lC8q*bH+J@iM0n8EDGDOG5q2MR^PpBN~8*qe`N zjsCfnhzA{p)%R&xU~%spWu>wnxS3j3a_CYkDWK5-8WdvDHy+W6qTAU77+-G$Goh$x zglVA7tpvfl<@iFJ*Sdxw^I`T>acS2N$5WU>Kt*L^+(QG5Kx}N+vmiv#ps@|F^*(AN z$YYYZv!$?Z>UT8icWM|x+d^1dJYp?Z9~Zi82^On}dOrySEx=GnBg_5y*`lh{@3Co- za~)mpmycY5eSrEr6(fEQ{;iyJI~yQ3Y$Nu7Hk%0joDGMBEW>CS?fdb6h>vRrWsZvK zS&&YnI|?EU`$SnV&2EDl$mAHMnN?^fUT;t!b56obAZ1UG_(rcSSnND zZ9o((X@vB@7Hw+=K4!zy^aJ}IkaV|Pa(}C;Ch!Dtot)5QBram;8!hrZQ7>A+NT6}r z-!-bSJlJtz{jFfD;C*Xbz11o@qwi7b%RXPG_$lW$8^6>W|8d#_DQqk2dw_uO$L#w- zcu-ko0~~M>T}6;s2aw>s<6n5UHZYzap|p5~v^fVc=WGGuqVQ$Z-7h&->a7@;Wl9IhG35G*;$ zJa9#`b6>aHE1oo#kK5vGSJWgBCcUT`M;?r*L;aFHFhptfW%ib2AI|%IEMOFeWV1ym zMu>|DsY%iD!QW!sK`Zfk0`AQR2Gcb+dSUEd;p|;&=azpoD}=!yMuX-oAmIu<)1l)+ zIie+mwqXb~SKv8U2Sko+w}%Qp>pGCEjfs$Zuy@i6QD}vBoSE0~)Zn4SXYi3e6clEF zoL>}o;40S2#*AS3Kq`$8aUcoF)_XzF#oi1Cvg71m7*?)mSBzdV7`s`nC16yP{+5J4 zA3{;uvLtK)Cp889?%u~H#q6052S=EknS^u|$j%d=l^F64N-gw}$CJn4%494~573}K zNu_d%xws+aq^~5BU>Op4+#(Bgy#S(+Cr#&&8xSylA%+d?5OzA$4R2+x<`{7laV=tE>rLc z%1{HfT*Mxw75`#3lq{M2_aJZWC?rh0MelFe%}^b-K&eu3aN&_HY?FZ%BaRkQKNlSo zI=c~Jpl@j>@qvT%1Q}W{pvh?)E{Az*!y4gB>lKY=KOLoZU_21Y*QfhF>Wk zQcHsY5_C(V#-D#CWj0^)cSx@`d z)kFtyN!$%5vq+nIq9PiekqHdm(8~^`itsNKy@96PZKzf$A@HJA*;W`Iyoiu9VtT-U z0q;OPwQlIx3S-NxHYxA*17NBmTnu2A}1IBy_KlK5ubg`*^?Kq6bo zE5wHxSC%7CfKD+A@@m0#cH(&CTPaP0qi}p>ql52S+_FY9XCkkTD9BsY^?pqlpgg8x zh?PFkY+PUmPy=(ucNYM_i3wGd*=~3RkTH+Yx_jY97}%}UqdlR>K#ROP8N;GBeW9os znbnYsz);|j-X9gD(cltrs-sx$zIS1rH1fX4)Pv?k{+f zyewAm-yRa&_~G218^_A&ApO2FpkX^1k!t1A%K9~35ooyy8yNDZlF79XEJjj36r-Em z0Cgx5Fe-vGGX$Z4MpEAuVJ@rf?iwqLiFn_xFB#);33IMK^Q(h@yEL0 zD~D}(Nm+7o(=J^uh>CX|w6k!nxBT9I?CGwtT?3`MDL;rMvZ!4Uf#A+Vs*wn***Ef zsyty7%Ai@zI8|{vc5(P4cg4?t(lP;GK*1u{Fy#}#`LOs0K(L~dKvV7IyRUvDY*$po zausIN4nI^K~e|1C8luTM>>6C7wP7FyN~QJ32aNJUS0Mg+EAr%yp`)th@d^xJ|=h?CHW(Q zP=T}I9<*}XEC=`pkPc=h%qXT^l>*>DC-J;WMUnVqzOHG)f66=jz;)Woq@7TXV(Vz6kE0fz?LwZ1tq56hPTOvXySk2F-O2)IQBOaYft*~aqxu!qU5=- zQ%FxX3af+N0$jl?0>uPyo}fdazN81w0p;!ut>E3#gGSB+P+)8`fe+++&wU1e9_E+( zCLFy8niGC^HLzw0FSM|<>h}3`by-2pM0o2rZ-r@j$?)NkSGUTi^x6A6*zcy<=x7JQ zmLb{xQgV?+zMzS?@aA(;I(_~R&PlNm@gg>kXPLh4;ZNHh`?3jilRd;(0#`T2n7~t|pK+%bZA{OiGh;P4= z^u^Xkau!~`hTS#jPTvKE2IBg-cJm<~!d>C;&%jLi9qC5*a zzZ|0$$uVQ@#MrRGE(4vko>MipfNNWxc?uE<#qWNw#XAB4SZ%oZ0u;`TCcD!1AqCMO zY?+~UK~CvhCESQlF);Sg33b7MzG$t~4n{i#loCb-ko!Mk5EllBCxCnM$UjAb0E`%p zkc4$JAe_k?o_Q4jZfJ~tM==TrprnxsCg!&aWCuu^5=k2*Rqfm{mcyhT)Ilyf3Cho{ z7#`B;S{BJ-wp>V927!9b8#=stoed>jnriT9lt*tDJD6qHnNDL5mv zOX6vuzMR6qU>qG?(B^xiNrDy}sE-Xv9b%FwwFP}ggwzk#8ZO!{AoH3#N4jffg{&qB z)~rCdg9-}7$)ohlKS#Aii5G;d;5waMh~H|2zz_69S^JU-1S=45I-hltXk^>flsLW_ z23oHEq%KKD(i}c}0rREMR+#NaJn2(4u8M=T9Cwy*NKvSSDw{~S8V~`b zpfL0kX&`VjU{|3}^c(g&)ZRXK2Il+lU|zU>NhR}xNKql^&rk?QMU>{0hmnm?MB?-& zKX_rwd!ea3_bCv9K5Wa%NOl1TL}=?x(1|5|pGZ#%4e=feY(WjH6hyjwK)Y#zU(^}@ zQ6H*d6#1h1A&XIWeWViIG6)V^xFx&DsaeQHD@p00EHv7PuT6l{%UO+tYUD?mkSYb3 z_KNIDn*8urd?l+MdQ4FNYxow4#6n3}I0zu>fJ}UJ*TKMBx>$iONWgIeWk`~vD~gTd zC%SaNyKbN{hai0KSt9nP=`_=bQcijpdX{-UG#1MaUZlV4g+>1nZApLC<8l%HSKPe6 zOb`Kd0BWHOwZ!D+wUH31i0dkkd8PZNXb$77ib?GWlj^ zT!>pUlj=vp5{L-&Pme|7i?|QK+U=kY;m4*{VarFL2vjy&L!5Mt-UM+Xpb3B*6H z09hpRcELJ|j1Ct7mx3?!ev7|}>POfQAerBVcmue91$bCkYCm7F!5JoXd>x*vEiaB0 z;FK(3T9!-qvS7(v0rW%a=)t6ESyjvuRlpJu<(`GV=)Z-KaK=;+V0a;LV?RQ?EQc^Y z7uiZ-5WtkGu2Pwadd7=-p3xIc3T!#RW$fXRSRt*Gh?a&TOtV@Onf?f}GFYB0&7YoSD%x}4!geda7rnrwji!80V;(k^ zX6dpBD82xrK%QYor1ikfpl~EP^WCC~7`QcDe-zDYeeSqNBYztv*KO)@BJ;{-$vp6M zbXuSkRO~_3ga)vj$tt14Uc!#u!mqp4ve+UOMBj)X&_R771%3)-vNNco{soOp+pg3U2= z)?BZHkraqbGHj-8VRiy`voEUYcbc+YH;jVQgMh?4reP|%6%Zdjl+t#oU0Yxl@Z`L5 zb~O_Uv!(1iw%x;r6A(r}6^B6`x$y-i8{pzJei-2ZL03t^wbe%1RON~T&Kv|96~hA8 z!BD~KB3Q7}8YlW{I}#E}1;9%CprAw|Pc-++h`JnM+Bcg}!Sh#5U2xeH6X*8?a2Qh@ zs1&wAEENq0`jf~MempZ25qHoP+&0PX70SZUSO^26PsPnYXr#EqHKX_|LL=2Wkb1~4 zm=%a)k!1CBk1zxWfB9-2|438o9H=-xUZq+mT4bGu68u(51eTsx*Z_%qa{8}1K#?p2 zN5VXUq+>eD_HI2An9efWLBK{snbpu(4f-6$`XdV0<2K{v54b)*0aT?vkfTM$iOiU?3#uVECZ2KqD|2hBJYf?x$# z0SGe+(xVwqFEeAZDiL;7#p{~7%D96C?k)LrkX1?1OqO8RFdG{uo9YB8&_+B4bwRI> zW3SlCH#2~WrdESH8mb|5d+5$3y+ z$z_QU!fVA?ASFTGdM`!phoC@u5L$ZYXFXPzg4Phft#go<7Y74FA4#S!FbMcw_M@Fx z4wC)wFO(2!2j5=S%vGedO`MZysR3&Z#t?d@p-DaJw+hS<#xX}c)HCULfn4*^GG%{8 zpJ}K?_>9||Q+kSVy-falyk02Ph1cvgc&IUBDhd_kB#UuG%Znka-)z+v2?fAvC@HAk7C=m{$ z>3MKS2&R-4j^ciqtWpC<*YpLFMU9DJ$K7j2asmJp1XD|KMx{cLsZ-g@z;yKgJuLSh zqSG8ejVECGqqdNv%0VUY=R@Z>T2%Q9-Ct3SeO?In8?`AP+`Eg{BT!m8 zlFVZqS^(MH3TJMtbfi^~hPj-yr`VE_ucO1R*F`EuezgBrvSWnm)i1)7sPG~y)ukoU zSqBe5BPrGNBG}Bdq=CyM2@04P0H<`8kBJ{g17_w0J0q}?sJ1ahLb;)rO4Njf>V-H# zAkrt|NHXLShCq9EiJvn;mq>f2mK>^+3J^hj%OatRQFSW358h(E^q-YgRb_8=JMmW5 zP?1!HC3J`&YgR?YA*&THg^pY?taar=B9Wo%U6IZ*Y)K@35-1O`Tc59bKIH*Rf7&mA zR)NbGedM)g2Z(907vQi5C|9AxmFreZQHFdeyOJEsnS8-UC^^l9&0orX4CNL5bJ|R%h zrAitlYle(0>xg&)0LVaCfvwX79{&9-gBoaqrU^jWwrVG1wEr{#OkUK%FxbHvUJ5+{ zu$<3cr+**jbTS7&grbX(@T`oh`Kk$6Ej?Pfv8GF$_|rvb9Bg_noG5Auj}KS^U>cx| z+i7IjG)F0>sPB#tGcpfou3Su$PgtUumt7>zw1iffn$}6TioKft@s=pXgSDdwS7{hq z)pKc^8K2004(+6q5ppq28KLPOkfO>aIUFFNIV)M4VsFf>!Q2=-N3ehpT7vR7HMM1g#wlG|M^j=U9 z^t=eoFg!b{W{&ys+a69jyd=QVRpq>bsSfmX#L3V4ITEm#WvVRpOdg{bx`WcvfSXGN zm4HS<4`V!pKCE#7h(_Ubz=p~W-0i|5=sSC#q!QoQ9za52EbpX>x)y`^x_UB>+V3?b z?;T0WQ6|AXj5aGlozIZin?=s3;4Kk$s*w?yjfFhA~?9*%NkZPZq=fYN!o$|N_3~Hv$Jp!R6DyC za>#`$g$t3b>;z3&yVAZs`2?{Y!yMbe|7A*K+iQqSQr?SxW+451~b?8r;!kmi-X(S z28uD1%Hr$$A9w+?xo0XQ1kOMU9XXxFE^qnkv5^4(hLAY=5eGEn?CVBLQMD(qB*SI; zKp@0v#Ie!I>u8BsEr}&l}XG*i0Ga%CQu!;`6PTxG}1*_`-#I;-D&$ zWH3H=I?#_~sC^OL|M59&5*_aTptW#PP=N9So(Ni2o5#obynS70$;3h|*2JKOQD)eI zW})~3QWUQ@q#IZ!2VdzF?~rLWg%pK1x(M??Li;1Z#GG~zk)^Pi-?HCqaN)3uYZH$A zl1v(i0nwkQ($u;#OOPl;5$wz8zF6eHRA7%1mEQ7{gYBwHK96~45uLjMXzXRCV93E~ z!J6hv<~Sn0E;KJmQY?c6yOWKFH2Z9@RCby$7Y<4Qm#CcA)H-J*&7IX93awf@a6UW} zm>|_tU*oCG+-71%QI(_}|Xw@3#fA(xr*l$#)YAI(Wee63)Lz2t5Ec7Xjq*Gb;qX7i1-ZlbO z>+9(;mNnxaMy90;LY>s676H*HI{?CfL&-4gn4=y0+Rnk zht)sdkY^=pK7{gS0ptxyYD_Io+uIxW9nMbTmwrJSHvjQ~qPw7-=MlXTuQv`=oR zu3TK*p(pilsrlspq|2teS>Y)ytZ@jB+oTZTc z;?7VkA383l`jiqv4mubxNlDW{Kw2pi9$7~dBVUmg6)R?Hy!Ahl$_v- zz#?zP={Zq4^hnj_M_?@-Xnl{~g|~zqP(zX_Uop2jl%dMJ<|d+kdDQh+91|3e#e*s| zR}yzGfO};+9DPB!Fp5fOqaY^b1$I=8j-w2AK_h}AY%`Ujv#2tWuH)1kfpGRJrP=9g zvl7wZ^ejHW0x-E^zzRln*U)|+|KL*cXF(p*KslIyccu3-v`Yk#_0GVeynG0bvw@$) z2*0JW2i-h82%T+kGmt(21lDi^usxYW={}bMFzkCW!QUbe%kMyeTxIm~{cuG3yt}{S z{m?|y{1&$P889U-1fTgAaA541b%GoaB4R#Yqly|GAO!9_7U1^$umsn>hQke&*9icn z2mvwZzjUn<-2Ax60%-1B!{#?&3EYMM9>py;E>Hy21#_OoE2w|K6Mc)XmcJ|QS+_Gj z16H0zspfyaCJ1sg&l9etBN?H}loRg+`Qoh3-A#O_poXT(i9N|V-(D4kFQ08E&O4Eb zOw^zAb`J))C7@3@u`}R742$=rA|RDiN53*0;P>?nv<6m|0f5<0S*Ua202*whh$al% zs3!-9FEIh}=@FcM!qQMIO;27lP=-lyiJYf+>YC{_eRp1qK!G5w_3TZ7w?U;L;~Bux z#ia(bCJ4N2QK^Db9z;l;h2mf2je~Ghx>jhiyJBC zkbNe~h|{>&Gf{m}Z9A>!nKb`u!)^FG5O#Lb(EAjw#tI=v$)udWe6Jv1LTCpl7a>y? zC1WN#XpWYJF=!Tqn|4f@YmO6IsoKoqyy;9XeJ%TZo2lQvj@=cX>gap?z zbrkX}ABQ_J+$BuE7@j2yP!V~MM%S3lLs$M~4k8p2vl}0}xR1~~(0(VK5Uc2ZP)&Z; z@{2*0sar9C2!yO7T4Io!g9e9@gRh}fB_bFeV|JKS?U29Celhwv`p_tv?sGLdh93)w z>Z@wP%L>fMBQE^>(qlZza`8<(3UEz~M#$tJ?D_>3SagjAk6sVC2mrkYSjAP#P+v-by$=-wtW# zg>@%~D5xl7P;szyNL~Tps+U|3As)`mY5l1&5mrzim4)0u)v(Ett4QwWK$^D3j(U3x z+5P~4{9Hg1dk>C1FmC7GAU)XR312p~^Mq?4EBAp>NuoTdG?N2P?HyskN!>CvTvQ_(BM;epk zZU+3=7~Vc7Eek_O>da5|Kn)ND3YQbc@DM;N5mqrkV~gr4gD(;}!B9bjO`EfdOKbru z&9VyUFcVZ~?1wBPDUh&?rHTdlT#qA9&nQrD)cK=zM8^+67Nb*60;sBJPL1HnT zjW#SaMRYG>p=r2NslR7|p-?VAR3~+6ApAVj(KuWbIcN|no_KCoh{<^tcTz{*iC{lj zR@k$tY9kGb!h$mdWHSXUVO)OTN+e+E`Vd*VwOA%wX-7pZ;9t>FM1dd9SdOn5DLA^ zZ#6uJ*@92DT}akIxV|NI|6tWTA?qN-LXYERGr(#=^P)aAg7JoKIZ2W7*jNc^}$|27+ zStbt!+kbe{31T;ES!@e_j((CE!dNk2P_c|(U6&&EtTk)ptSvAi#pi~6U_Cj;25&bL z=)ZnX#3$Sn81$XYr#P_g2|q*|4HTqOD1F;$E18xj&Er*?nFKN1v9N}sP;UwP0BpK7 z4mR9&8||OZ8LPnkBsW#{mtV?@ju{clTw9`~tqmZRLEK^>459%X?2Bf?q1x08#wi+* zh6k;U*t+4&cS6rBDJ4?`GBivGp1t_dD*J3~4K!CzC(IGiAS`WB;f0Gx(k0ZGkaDP1 zmyr7Im&O;-f%hm!OmgcYA5kJR*_TvcFkQ5gV^CU2wQIIkpQT)W@|Sw1Jdb-!pjZQN z%i}(@I+;Lcs6y-h*>D{sJ>#;1e)3?tG$jJU95~}=MSY>@Fr+Eaxak0oVWA8+XSy= zk@1t8%o4cv&~4j@9616-ga2{o78L(D^%CyQ~`zdyeIkS?o&;@ ztOV+UVhV;x4HL)7$;~HvMd9m@Hg+-1n<$(<3ZL*JPR|8T+ZhbErsrT3tChQ&ZicrQ{NYLdiQTypzD25P zjM1imPP&vZCB>Efx5u;UjQAr2!$%>R@!<6}3XwB%KZD2YgxG{AdlPm?D z(b?u?)tz#8Aq*Z#Fpo+)Q3;uT_ZaXlEOnT%^Q@31QI05_;v~AGYy(P zAf)C_=wrVCF%-t%AYlQdrvsk`hCdoMY(}n3z<8Y!WEPx4?^xgkG<2YnoT8_qSYu28 zr;=EjLGFN$Oo|?7(%U z;?9}$H9Now9=>(Atxg70L9+#)@U8LjVtZr;z59;~=QZGHv1bi6TF_@gEIP*Nlx&}v z-cmYP;szLH)p!8Ir?GY-`g-u07C0Xw;k=UIF)=8KWPSKghZaCCz2oW|N+!rhyNH2c zoTCdXs7g=_4>PN0j4wa>!ns2yl(@!_1=}NsTYiSP0#bZRwjX532tAK$`Q6VXUHF2( zkOg>)mOT(s!{u?ugKEf`v)GFy!uap~R0y+`V;x*+d0;*M%gMMJ%px`W3AFpZv^<-* z@%u3WTnRLy1UMkLfcgQCjn(jpSyI+A+{^@z8v=A92pb4UMgqH{x{1FKz~Y+lQSBfX zD^p77%E)VX-$U<)R$X~LOVwTxLor>V<);W`qJS@UluO^!n_J3pqs4+PlLFntlEMl` z?fB%~s`ca)Jn-mrgplORvBwUW3;HTwl?aad#Z(WCQ?9CPMlqD|Xc`2L4zU7|r2B7E zM17o%IeeA2rVJpt&P}q4tDJ)TWYfk%CRi~91ck&d+hviB((a7NZdqG5un{Csw_GOd z%k%Ez@1*o@$TwrMX%O}7jFC*2fMEiOK4<}kPWOQ3REQw)$wF%y2>H9kkV(aEv4Y%! zXMKX<0e4qcFJzfqYHJMAm_@9s)y1kWWT@*gdYtfcK|@Q_qLMX>tHTyqk(^sDPDSUh znhLKtFqRvX55~xT^3MRS%ARKIq;`-rW>iboZU=vd1*}oKkkuqO&jMA23R&j=dNA#_ zsBJdOzq>28w)_AR7HwhKl72uJ3%|!MhL6ob=0oH%;*(|hQ-OZ)biAyX{G)q(rt@#f z0vDDbV3Jef(wapn&hqq7Fzts7N)(o)oGt+1lr^_^F)YouYeCNhA*i6sO={6-0~zHe z4QY;~45{vN!_1i zU4fbIFz^8LjTWR-t=R$oMQrzS!kznA$YP**ti0`rEd=EgUV4iU z1!WU#|GOYB0(IgYg;9JehDmF>!J~rtga(}}`gt(7*cJg2txX9d4j_+{V1ZO!zOJ@} zu7RRep{)2$AwEn20emV?1f)v+hCsFUOPs)etfg49_tMD-XXD_D1;!e(g99Fow-PEK zmr4a78P+3~ljDbGw_j4w!_ctrMG#EjM!*blu|^37&m-TUez7HMK*uBsbFQ}Etqkc& zFM+Z@x0MTQn#ln!*JD9a*mqcIF;2drNS8CN-A`6J^ zg2B&Vzz`^OZw;_B8wk>iXTITRB7>wNI3afCe?VYD1tOl_lOFMIbkdfk+po5Fh>hnU z@`bZNL)f*pb(2@Q1=O9lC7XBFMU~x!P9FyGtRTNx*;Txxa=x9p96v`P>3<(Pu}|f{ z0>UdPBFaslJk?weU@IGrv{vCN@knWw}5A|F~B279NeWc!ArD)l%b4vlyn`^)y_;GQJ+(L zFQRoN#DOgYZG~!?@#z8mrfo7?FxNm5j)q7tw`g8%+DJK&a3Nma%Y>o8#m$t2Ad!_K zk|@WO6&_C|yUhF#@(#t8&o~v;q*DZA@c6hkEQ%1>6&Gyz4H~^M(xIev4Gn9B9U2wL z?w4kw;>DkkGX%y#QdYh(SUc0c;07)r%oFOyMnrVgwDyziHSgB^x1xv__Gh zHPq5*8uNsh3Q}0ONLN;1pdhTkK!OIeF{qG|?n6DxV09So{8F8+(f>ZgmKp|7FQk(O zrb$Tgyo<#I(hpY>OgEt2^Omom=$IbopNa@~r6LYPyfO`FD-w3?H-omb-1@Xo{n@wn zB6w7uYDTR&*67V!ii$1+L19MDh<$Sr!aZviEk}xWQiORQoDeNyBJC+MjSmD%m`L^R zB525}keix@!00qX_q4E2t8gL9zqM4gKVxIc6k3O=v^5r?exBm3`U0b-`G_Wi#$wlK zGokkISWi+G)34HKUnz`weiWn%o>yR&LX@%aN@M@4?@AUrhTon?@xbc|`yZi=WL8-0 z@4kkf%TFezhtV&rxj$I~Lwe^t!u#NmLZ1G?0J_tq>P2NbT;+faH^zQ4W^2Z@_|VE> ze;7_X0ikM~vU3aN1Y2x$MHp>JAQ%pckXgU|OXh2mz@NBODc_(a&M5yu2!4 z2?y5nI#y0WTL9@p?SdN{m=qW}35|;o17+CCRMZ5Fqi_Xc#NsA1G8W*T}&|w#F4G5T>T;nfjo@mq@KL8xek9=I`$isD{Ft0}H3Z-+Gv30=T-8 zk}aX^ZJGrfSINB0cn`W^1!U|tUdLp)PzxjG<>Ah;n#P61#R=K>_cEFwiXKxa`^wBz zI2Q_ulO4}-b$KcoQ89T_mZrdOq*1o{nqNf~(zj6p1-_Mb-DK3d=mj(0n~X1kpjQu! zRW*IJ%4N*>a&&SHHp*|L)Td9NHu%xV+U3Bv@v&q-6qy;>vg{5At4$1`LxLhTz*yu- zQH|%IyFnX?=u9w*IkaC&vt(1`=eBlD13L7p$M&de{x6~5DxfjB*^}K{*LIJ^sJJzbQBJ6&L zvmqw^6++?6^9r<>TeX%7FU#^RgOB>m<3(vVY7n+bUj&?3^qcF$HN9nlc7Zw#l|>I` zzULn3yAS74O18!7MB=uF<3zc`nO3W%Fl*T9rP{1OR zYM}K7gxnq?j1a``WIAnTmFR+_PEnsZj4lW5ELWE64`8$+UYZ&MK*cPjg1hw6UAv}R zII!dlpzX(q(dH;B)HuFy4&iNUN}kT*{hh8yB(c8WxY93?3j7Ni6T^bEq<>on45j?7pK;2%h{NWbDA2f!}y zI1IvCr{9375NnK(M#lr7BDs*g2RP~w7MSra7RV;U3JtT8h0*BS!An)ZI8e~#48TKo zggk`orr!5;oe+nyaYypCR2&1oR4P`$nrxDQf&~$&+)&OF1Ut}xSvWzMfN?DvE*Z=0 zq9|Zkf^a|sxQui!NeW=&C9tg|C_6^vev5D=;Z_5M1IS#UQetJgOMH!Ak#>8+jy=7EZpJff5obb~ST_^9@cY+W;yrvKa@~*=?IXnVR#?WUy=xJC_iMWOA0P=);>?>?DJbF&Xa?$W zX}~c-B?oxbm_+LHC5r**czHI#S;mUuQLhvfvfB&H*aYn4R5I09;sd;sZw_(F*s*pPF1kn$Ch)Ts3 zfq^?QFA#TuOf*Vavl^K6wL)rNgtkPwgsT%!$L2ck>k4CAHX4AS^A5QLIu0vqdD8K@z zKS6SUJ0ORvcoGCBBz_RB?x2*17|avl{juW22}2vZ2=l{*U7jJJ-?4kEimHo1gV@`= zI~?~#jEWC!v&q%cEPFKZ`%G7hOK~_o8LM;7_e(f-Obsd<#>!=;(pAL;0}T+EJwb4s zh0+Fv4ndgp!qLfrLXe6A8QM%N%tpZg7`ij1qLCTY888wT11aO%!dRKz;6Lk)bVWwU zAk{QTOeabMLV)2fyWFiFf|{C(O>TY(Lh0>2Gc#0v(LR2TaG6XjLAy5G4C(6ODiofN z76$2^nkBSyv8_4RB0z0$QB#pnFt`qZi-^q1Gi7Edp`i5I0@7}F=-^2II9a3;!j;Qg zyg}7>?EMA&sJgIR`d0?r;&6ktkusK6chK14sj<9?r>M=DOJ+z5)dK664LwNdBnJHs zjp-yp#p#K#of0#_sQpH2{TZvmgzKm%SrH33=#cR)0&zWgk24HI8TGh*cdLyB)tQ1Fj^vO3N|+GSRzpGuzjI>Wg`Y^(B`_6&7ATUI{86k};NjXxCP5%V zT2V$MmP-zYcvTa#c)S{XE)LoV3dm7e97;&sbG0kRA5N#y4i?74etMprCnn~#ldG$% zMP+NC{2fr+I7MaLrGPo0nrZ`#Qn1EmL&7Iq z!e!(fVbS0* zRGpa;xakv!Lw;HM+P`ZgzYHrniaRRSiyKbs&I6;-8hp5q1kEfKC>C7LOD7)mI32uO zO;R_p&ule2KQ>0ZsvnOL@M401J3k+#jU%x_S;|SkfdHo>MgCkocvCDD48oe z{pxM#6j^ATr*!C~Y$lfdJndR-Gl#IgzGF6!hdtUdZ{L&c!$J&Ul`CbjEkzeNlvq<_ zAy|TkMkwBIOiWGAHT9I&9g2^!u*%0JRY$UqAnqDbUjDdJiKn4cQyUkchTUl&6Z+8( zU|a}Z77lLKUrWHgbS^fcqD6uLopE>Ya*eQmRz&ui9X~nwwBw@bJrK#5wu4%G_c4Hq zlz|`@0y1JS*=!hAX0EPS$T4U*tXf>zff&YVv{$V;3*oozV+D=yj7`c;M)$~a8zNpG z@K>YzDI<_bXEdIH1285KoZzm3!rBvujwb(?!#gTA6PE-K5P$t?1GJW)GYPatZ<6xj zdC>Ilv&2yg=EM+Of&uTC(0K!uV(=u^8X6Ci@UZ!H{ed@%^w3N$1TaKv4si z7v!Dy-W9 z|BYJ>UeY;p5+G8L$YoHDP`hVWSF)E3f_Q**d+sHv!54jG$0uymDLd_SPFh#+o8+*sT@SC*50Qr3}>;ofq{uGmo=wH)O@|S0zKbTvdWvO1w_Abq*Lm3y&^_GQ{MGG#*|G65%xfj zuJg6J{ZBS4cm_@T&(?rDyXdQ6Mhin)qUJ;~!L~r)Q9yt~f^y*rNzzw(TfobnceSOI{KB{094^jF>V~hM|}fu4j7}+W64BNFZ$qTbR9i(A0yptU{htpy#D3C548LUn6zkg4)H zq7V;e6{B&FH%6x49qTA_O_>G5zI&0ht;hDkb;pl1xc$++TjQVzfkba-&}Q|Cytp*pVZ*ZPFh z7*0M%-slg|8LWwcF2$5h27^n?38!nd+_i#a#K;rWw1mN>6IBez3x2d5lL`K*5N1<7N@oVGJhML~CL-I|_jh*W~G!w0I-TU3;q z&_Ti4AUAxu8v-8%$^{R}Ip|`*Z5IAS)&TRzzoLrP!H&|kP}0R%XnK~x=3N5hrEVg1 z#F{vIvANhz7fy?O_z{-?sDFU@7bs2RV1_ zq>5>#fC=j13$c_6-co$1!ezRnB(PL&nEXMU)9muR00gc{c(C~N*&InjhrePHP_}Dm z#gbPb{Kj{!4xqS)EAxX8_*gQH8X{oFx;sEQ( zb@9u?5Fcg^B1E7zxQPYiobiZJ5|9EH8!a3&Bl*5(sIVbS4-k+oMWXV`Wt|1lbp;5+ zVM4JxFyny~Aa@cnu+4-@$L_a&OIap%kX=m?7Ix#%+sSJuT!pKqZ5w!Lmf0!}BE;>R zB&Z%7&0agq#TxWB*#ghArd3)BhqDTZU zQIoZzi#=)<7Tq`OL=^}Q0PH5riyu>DK;Dgg2mzJ)&>e<~%RFuO0U^aM(P4`TEKg^% z2G@E&yfK0JCWZzgQ;Um>v~mJgrjfxF4Q+#&AxsQvTv&?;z`-C#GWmTsRvU-ujo9Q` zn)=0pV~x<>H=fOf`exbods1@59w~mp%?VkAI%qIbsq!2jWe!=a@5c-;#66QKLpKe1 z?3Z!@#2qWdotTYif#s!RD-0~bl#3rxzNAFmwmRgMeg0Te|6f8_LSTE{7(jW$#V1&z z#92*8qwC%;1+>WqgyeuSisJUe?>`DJqJZL`5^It=EGA2i|wyz#Gu7IlV2`F{-*DU*=afOUb0643U&{8qf=!PUe8 zMB-kE?;~hj;VBW_$E5G;lTKq!1U@-X+D(A6e*^&SV2FB+Rx~C=sokgGTZI8 zrJsa}b5YyrknW+5BBG;ksclVID5FW>tc*J51Fmx{W%v1vBKsa# zUH*b9%+5DCq)O82{#RIHULm4V?Eyl%m(rjt;&}Fp;pC*YH$?nA8)zlY(X`%cUHO!guJ77IM93*&T7%7}`obOkY8{#fJ>%xBpKt^-P2PtN~@oWHW zSii%A^aX;7TS{{)cjDNazGtjM0j$$eNFIAKDk2U6C&?X%og8L4n@bb&!saRmgq;sh zxQ(VmP_-%wh;!|NCU&s5R<|eCHS=x2oJdJEu`eH!e`~bvcaTf67%`hP6!E|)TEQ0U zpAp&>y+Uv*g7;nOhR~~g6z_`!jNy;nSlS|B9>!iC1IYzxz(>z6Q$ds9E*-QL)VX#H ziv!2}FcX8AKqaY#2D9IOj9h-Z#Z?48PZPt#l(~x2V@q!#UStE420}ZWC|@VFPY<}w zLT7DFyoGnRq`G%8tuBIW2rUG>`iD(M4!KKvg&ByO25LVf1Lq(p3;0+8pLnmP6Pdc+ zDD-9S>lw$&f1_4H0mw}LST*2VMDJFCfPEF<>4*|X$A~<4g`0qq*E&a zpnktTKmh=m-6zr+>yz$*Y|LWHqK4(s|3So1~Lw&9Ry!_n27aE^nxpD?vcIl-5_qJ<-4?DJFMe%(0@X zpBSe&Nf{*EUMz2bQB9+6uyaJ{6j@nUDh7c|1^l>D-{!x}>YOOJO#|S^?C!AIWxet$ zuliVycV97WQy*%odw1*uJnBR@ucWltvh+q&oE-%Q1t4P0$6h6O{NcI&BgR69 z-){mUk_(g66r3cvzOLZU^TeOX9vM>&qaxyPuG*MncOd@(p1)sn1+G4$;;+M}?lKHz z%Rx{)65$5Y2s>LyEgra2B9J|Fv$#ob3AFZEJc9u?|K>e}E-k_jd47|lxaXmm`}}-_ zh0qP!at`9yJshMgy~0+F#pxt$?g1Wp<6mEJo}*v|$%ht@!#!Q5o6KsK*kdAy-jj}3 zSV7>zSVgbu!^$am3`eSDYw$F^r+6Ctf`penZQumO1&pI5G8b*!C%lEcfEXwYpjg>4 z?>KfKfp&p+OW@;e+$X(CHH%WM_+t|53{|qs?BxWl9?0ZFR6HrFM9FnH`;H-EU_4NL!U~;f#Tz7@nae(Dacrzudc{SGyPzCH=iL#D;+i`s8~91Q)N7#1 zLt|hlK4$}-J=Jrx1f7GIde}o=Amc@@=p+D~Asl(?a~;wqbGYk>z>iXWqq2XcNZ#dQDcrP}*g%{e$5y2r>15EM|{y2feIEAd{7lz;O} z{ECtHN5c;0V$cm1Tw;;8N5c+t0n>Yf$R!`T%Xc8ZDt=3qQz z?c(6JcfkssqW1(C1Z3B`fC^SXeNM4^f(!!Ik{^rO6EpF0)mDXAZ)VeXM0e^8hr-IN zDjr6L_H9znp1GGpuFKMIW9?V9L1-_6& zaSJoMT%~^*adLPVgFCYzlOCL&`~7meG6^xtFaYw5o2s%Y8;XFnwo#J}RwEIHLMJsD zg(4zM@V-x5tw%A|6Qx6Bl->aEbgYui-~sB?mH`oi2I6*BJc;V%D=)6Gd>OyssswGIZTGHknqtP5GuO0YaI%EB-v&efB1&!#lNiy6VB1402{ z(}M**30dhzie5P)wbD#Pf?O~y7~zx&UgZ7oZt*5D#w@xYO;Ac5DE$U1y+FHKVeQ_eB2zL3bCnP=!5NLE@df~~BQjy? z*0MQJ%Vg3phN+qSb z@&YGR*;^bDxryN)S_STI~$^nJ3e*13exSb-gKAKh7?hvu>adumCV$@sn|xQVJJ>#wmg<0NWw zy9#jO6FLEEd`uS(W7ivvN&Upx{ju-fYix0qY(p+>oI`{VDi*W`lrf=4GSzW6j&YDM z=+**zZ{P}m3ievY4H#_9TnAO!L@0D-x~_*xjBL|Nv=jG}Sh)pIw}eY#2(7cN11u2& zV5+wGSe}6RZ0tKm#G`~U@mxgL<$}yW>JSa0ydWTKCMW%%t_CjhXnKc@GJ^Q->R^Pn z(#NNj+kwJV_$2umnXG#b1ECNbi--mhIzOUV@Lmky;tVc2-q0F1)LVH!4B*JVe8xGg zKCz)}1qT!WGyz89g$WT6_~-!@H@z=mAOODBM+RbISOpskBfL*4L?F;o>lv0kNhiN2 z3-$>I&KE9wx@Zp>>By|=?x95QaiuWIL|Mx#yV@)gKy%n7qgdQtkg(S-%LB)pNOSiu zpTGzTyf2wddANbD%(vDpX+=y4faU;n`ek3|foKN+63ibJ=cZ;|nWglE{!l~hK9C%t zqn&9lX?1|$3IjE0I6HF)X9jyD1iE9-il+yhfC7Gib*aIQs~1cRLk0EZOiFRrEX9N@ zOCpznHClmb{wfuDU;q~Y^*X=v`z)Icf^Z$6SL$Gd`mXJDhh~>x&a2%sl5JjrMqIND z1fyPu0zJ1O+OjAK4BbaL`A)mEv$z8tjo(Kvmx!b%M9`*a2yi4Swerjz2!fAAq1b{^ zGUzKDIqbHC@P~wC! zs<&dHgK(#t;JjjGbO?yMiIfSeE+9w|0Eo|w_!yg$ucB^jdZY?;mjWV3ks<^Lc@7ka zMPREj<$#5<^hjP=2&lLStI7mL(gG?e2$3fQVY-3`%ET%HR%B;#3b^?3A-nutodenX zYA85?CJ!*+8jH*|QX(1_Xc~eb8M5>l4f!{5D);pfd%|D>(_0g@5Xe1{n-PB|d~u;r z+dKY8Ld|m<|C$Y8B)IEK_+xMbtFO2CXoEz=tZxWPz7A9arR0sANO?mjaY8H!o;U$H z8kq>Q3^=eiz<7zbp2i;3Hh90+e=S_;yK$ZRq!O^YEo3+&iU-{wi|!jfN^l`pd1`1w zGR6f~*a1jy;Y-0`(&o?OX8`ancDV0#fh0r%ZHs)X^=zVFO3B{RJz(!$${L<#I;@9Y zunQY1F&0n_o{}*uC7h6IyCPdTPe8l|*au&(x8MU%b|LDJEDg@%Tj827g_2i`G3q;a zAw1OlNG>sU#w-3?2emMji($w0N3C5d1#K z!H1u?Yat{z zVP-lq9-aL`xaD-Sj4$K=J`!-lGZ0D%>l(4FRbSIA!7yGOj{4=V?CapvPU;3xqFDLFv0>l( z7n@yf0GH7EaxGjugfL!6T!}Y!+#lQt?kL@_xU0KQcSh`<=FQ9-xb7!zBHT6Hb-2^u zlYPSbhV>bbq&c=KYM#|csJ~F8eM>5H`jRyg{s8j)DvGS72&STqPui|^CTi)_5vZ%_ z4Hr3mL+AbaR3D^7qcK@M446+tuo0r$dw(h?xjhT?I*xRzbBb| z+CDoGdW1CjiB206#~L#anxYwd9APuIa&T^~e`*lZqljLwbTf6S*9uP-xWZkfe;Ywe`u-OgZ~f^qjEKz^5OqwnB|cFN3S?kmkr z$J^J>S8QOK_vuQ#6q9Nwkprr3Hb`bB;o0Ra7e+J&S(nVqCEY13W9)=AocAw1$roaf z-pz@89HBh%t^znYV7n)|+@kh1N3{xAq$67*_4N#-ON*?pW-h;hFVS@c?1Hz8r!MHO zn!@Rf6)E>c(wCI@IJz?KSq*NppbkSRIqes)bxM|kWn#8IA~hTEB9VM(%{8lgQ;)zD zQm`rEvLYi|pk~zmqD@KKlj&7ZD22VN2dapa3e`J!e;N)ADlF7|GE$PP_*B${FkUDb zO-P;{7$Z#7M1TjyTB(ADH+H5SPP(}KN0kJWbp?#9A~4qVBM3-~ZQ2&UXhQ}{3hhZB zH*?2}osxE-Q;>?(D{>#MfeeAS9YoDt9*EEwKy~bgE5xlKIPdKYcTwS;dI(`JSd#M1y;xA z11KEJu|NbSW6E@4&>MJv;-qGTfQzJZ7l4{zhDA3OpgAFRCUfKq#_7*r*^oEx%xJuD z!VZVwvlKm-i3^Dd#i^%=i{p(vq!xGy(1+KCKYUZzp#jXNEbLBCYKF^$B5yG$S&L$r z8HP^CuM)E`gKxtSXx~9IsC$F^#cjkd!J{4UIxK2l7#0kq%hf~a=2M(?Y7Io!;_P26 z(>nvJh%U1@w1c}OQ?wc#BA}&Y2Pgi*oYpX6_=Fj96C?3DUs4hdV!9Q{xLPVyUo1v2 zAjfNo)ULIBN?Q4F{tkWaD=EZx1g}UhkDA3+*1VDUVqKEfk%V;>^ z4sGFv`uaK>u1l{c2-#;3=n;ZddzSU)jNEg2FC_X8>Yjpwj5W|kRR}314#W_&Q}_(L zkW^*uk#|rT{pdLEUPVQXoswLLy@c$FJF>MB*6S*4|I60P-3!?pz~row%3U}5BbPJV z@N46Fx%qpo`Fb8WzvUi<>2#b(wv(v8B5W6(fKWSt4BE^Q{IOPoi5APcsHy!z)|umY zvb-yHV>G-8j!LPQWhda{i~d@-e?Xcx!qK^Zuzyd+s&C{am;DsYw}f>|5OcY#uTk%A zKG^7|oY>>MJ@{^JeKaF`^ZYQnE3VM%(yu}XvkV!f=IAPAd$D6g)e#Jh^a1xLc)ba& zH-h)D0IBpNJIN9EnIoJ3N1|^)iF}L_81~!zW5OR=ce*rJ<}pDP>5LauPt+{oh8Is5 zA%-^;+1~X9@!L7kS2m4(yeiTaeS#c~jCKsYZfDSsxSi@p`=f#XUf5CP#aLdc=&Q*n z{e#=E_OB6?=`A4InzTb3CZZEJz~hevD$JO9ivlcF_Rm2gHVcK(gi51uFCj^fIn)tl zEYuIT)<`oEfh!ScAn@f#-{IJNAYxTCMwZ}{&^$GXAqHik;TO0;X1Hj3)`L`lFgzeq zBq%qQ^;YT<8G4L`@4`cLw>+f6Gxv5(&YUEgpLV( zS@3Yx!Fv+895}EI;ckNmwhNp(pkVI62Eii&2+Q+U`G7&P`+c#@1T?R-rI;}RhE?SS zU4Q|v7DXsCz!2p;WT%7pK%fCK9D7|!PxyA(-P`*qGa1VHgd1HHqoC2_FlYne2lV}Ct84E#s8jazs zTb%@#d^%|Wn3IEzE+4WYzbx0^#k4FvXvv>J_U4)})` z_D;a8KwJ19tRo@MUx3YFuphNk=Ah1oYO0~s7tqO+-hmwsCX|j{LB$P4N4(S!5CBYt zLm=T!Y7Wm{&w4Pm@KZB|@M*g+W`;kv8|Dw-g=ZSf6#$|V%n{IO8JIJ`);ln3K_o!T z9njsZj10lp4JZLp%pDj&65PSC7-#^7$QjVc|9+|jh2h^thCtAT<`Zwm8sM%0CF2j! z08D|xk%83MgRknp+yDcP*Vs7h{DY3im^kbBgN}cIIOq3+j(2D{=W&CMcNjS5aD$Fl z0664tgN`2PIN|Msjt0;;+H8Z3p1?TQ`fnVe&_}^RWL8`_29a_pS?maeG{HA*k-@S< z3oLKqs$HKQO~$YVfgeMGf0;TUlvEZO!5v4_&4dO2a{m?( z2BaZqArUfKIMS$yb%DiAaU_+3YLXH5PBYZ z%50KMV$4hy(IJG12PuO9DFcAQM70(Wy}?`X6kLS7TMduIqQTQKG|zF8xIuEL8-ORC z1rhEz{-O`ChotUx`uXghbv^TllC(ft8OxkU1i@n49>dU*qv#?<7PMbLg1@Dp^t7!! zYuv&}l6r$h^)_|~>Hz|^Pxu>3{Q_9L-|GrZe(@!7dk#;6aw*{a2;9$lE0j;eBeTg6 zDx$=IQ3Zj=jP@t1Z(d)JKUzHd-C^KuA`XARGMN{tCb;yAJ{ZAUTr7uLniC1;j2AQ;mor;U*#@q3A?(+;dD&W`77^ zSX(*5Sf@07>yfOqnr~?g@_{u z51g6`PZ1A@#Ddp}2AkN4n+W0x5HGM}q2L@5+c1Y6X^sy;9iZ(F#|=ZWE|h~?k%$j( zGR}64L9l~BCkJ`Z@($qRc9WKL6`8~gF&wJ{p*1S>gjQb}vj@N-F}y+yi=5Pg8G=BC z8f-gB&Ve83{yG5G5f65sTzr;cqp2c33^7>#b2jS>)UJxR`a=v_FeDsEP&h5Ka_x6v zkTh2;4RB`nUwYU(Rk)>zki5faU5?TQvt>9vueiz50 zy(9gWQ?ESgL`6@E=Wfuy?jy*>mAj( zB%B7yFasy54Ml7iVV*A>SSAOpDsGz@z<^K|0w7pQD?EkN2-k)xx5~i?{3QCvWz9CcvTwc^<8R$$cRI_k8L7& zMd8(uH?KsjN-z| zA;vR6L7NN&R58o)L=gqP67`db#MeKM(o$v7024oq!!Vor`bth8L31+Ay(s*|UglzW z+E{-tOPue$@Q}}H_5zTJH(R?_LC-OQ6sU6h>mPpAxX5zJA=(?|kMD^x6)p4!uGiZ5 z-&j%iz8MBnF{Z7zavDnSz6bcD(gSD3*goPy9pjBqp@~$-_>~&6L;4-Hf&au2n`#q+ zgEJs@l6?XNPgo?HK;X0sTfm>d|AaE+(Zcvs-fgOZby3&KfVgA;=c9eYMpUsg^njiP zyf}`rB-IuvXC=^16hg#9_Tv4Y(68A7MFIX|Uo?R0=~=PFOV}Pi*kXaCLFj#FkKQJ| z0f_Y=e~3M+XWtMyt`{e46#nsz2cIe0VtFyE9bYP)0L6|xRfG^jJowNd^k8p9^RGy| zznpqk%;#czmJLI8a%PU@V))UEx>njx1BGy@S#}mDa;})8Zjv)Hk(*9 zF<;pzyqENXo?f#R_}@~kBl7PM-1z=qDyxXR|ATK_2=T8u@s2aTRPrw>ctr?^eUj^X zU-f(O5zR0v8O}SI;ce$(yOC#|?yff!805>WgQsMqLKefnq1o#k}^oBbIbm2X*e*nsm z9bmGv*VToAB`zv@&&!VpG{pc gw3GoW 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 n(Q literal 0 HcmV?d00001 diff --git a/static/src/css/fonts/source_code_pro/WOFF/TTF/SourceCodePro-Regular.ttf.woff b/static/src/css/fonts/source_code_pro/WOFF/TTF/SourceCodePro-Regular.ttf.woff new file mode 100755 index 0000000000000000000000000000000000000000..0af792a1e4b5f8e5bd076fedd38cf23b6f696b00 GIT binary patch literal 89604 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 literal 0 HcmV?d00001 diff --git a/static/src/css/fonts/source_code_pro/WOFF2/OTF/SourceCodePro-Regular.otf.woff2 b/static/src/css/fonts/source_code_pro/WOFF2/OTF/SourceCodePro-Regular.otf.woff2 new file mode 100755 index 0000000000000000000000000000000000000000..65cd591bd84c5bf10421df85aac29b6078107073 GIT binary patch literal 76256 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%0ADN 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

czinNKe-tO)l(rzySrOOi4Cv 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 Date: Wed, 14 Oct 2015 09:57:43 +0800 Subject: [PATCH 02/25] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=20account=20=E9=87=8C?= =?UTF-8?q?=E9=9D=A2=E5=A4=B1=E8=B4=A5=E7=9A=84=E6=B5=8B=E8=AF=95=EF=BC=9B?= =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=9D=83=E9=99=90=20decorator=20=E7=9A=84?= =?UTF-8?q?=E5=86=99=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- account/decorators.py | 80 +++++------- account/serializers.py | 8 +- account/test_urls.py | 9 +- account/tests.py | 289 +++++++++++++++++++++++++++++------------ 4 files changed, 253 insertions(+), 133 deletions(-) diff --git a/account/decorators.py b/account/decorators.py index 1181c6a..2646c02 100644 --- a/account/decorators.py +++ b/account/decorators.py @@ -1,58 +1,48 @@ # coding=utf-8 +import functools from functools import wraps -from django.http import HttpResponse, HttpResponseRedirect -from django.shortcuts import render -from utils.shortcuts import error_response, error_page -from .models import User, SUPER_ADMIN +from django.http import HttpResponseRedirect + +from utils.shortcuts import error_response +from .models import SUPER_ADMIN, ADMIN -def login_required(func): - @wraps(func) - def check(*args, **kwargs): - # 在class based views 里面,args 有两个元素,一个是self, 第二个才是request, - # 在function based views 里面,args 只有request 一个参数 +class BasePermissionDecorator(object): + def __init__(self, func): + self.func = func + + def __get__(self, obj, obj_type): + return functools.partial(self.__call__, obj) + + def __call__(self, *args, **kwargs): if len(args) == 2: - request = args[-1] + self.request = args[1] else: - request = args[0] - if request.user.is_authenticated(): - return func(*args, **kwargs) - if request.is_ajax(): - return error_response(u"请先登录") + self.request = args[0] + + if self.check_permission(): + return self.func(*args, **kwargs) else: - return HttpResponseRedirect("/login/") - return check + if self.request.is_ajax(): + return error_response(u"请先登录") + else: + return HttpResponseRedirect("/login/") + + def check_permission(self): + raise NotImplementedError() -def admin_required(func): - @wraps(func) - def check(*args, **kwargs): - if len(args) == 2: - request = args[-1] - else: - request = args[0] - if request.user.is_authenticated() and request.user.admin_type: - return func(*args, **kwargs) - if request.is_ajax(): - return error_response(u"需要管理员权限") - else: - return error_page(request, u"需要管理员权限,如果没有登录,请先登录") - return check +class login_required(BasePermissionDecorator): + def check_permission(self): + return self.request.user.is_authenticated() -def super_admin_required(func): - @wraps(func) - def check(*args, **kwargs): - if len(args) == 2: - request = args[-1] - else: - request = args[0] - if request.user.is_authenticated() and request.user.admin_type == SUPER_ADMIN: - return func(*args, **kwargs) - if request.is_ajax(): - return error_response(u"需要超级管理员权限") - else: - return error_page(request, u"需要超级管理员权限") +class super_admin_required(BasePermissionDecorator): + def check_permission(self): + return self.request.user.is_authenticated() and self.request.user.admin_type == SUPER_ADMIN - return check \ No newline at end of file + +class admin_required(BasePermissionDecorator): + def check_permission(self): + return self.request.user.is_authenticated() and self.request.user.admin_type in [SUPER_ADMIN, ADMIN] \ No newline at end of file diff --git a/account/serializers.py b/account/serializers.py index 528edc7..13f3e24 100644 --- a/account/serializers.py +++ b/account/serializers.py @@ -7,7 +7,7 @@ from .models import User class UserLoginSerializer(serializers.Serializer): username = serializers.CharField(max_length=30) password = serializers.CharField(max_length=30) - captcha = serializers.CharField(required=False, min_length=4, max_length=4) + captcha = serializers.CharField(min_length=4, max_length=4) class UsernameCheckSerializer(serializers.Serializer): @@ -51,4 +51,10 @@ class EditUserSerializer(serializers.Serializer): class ApplyResetPasswordSerializer(serializers.Serializer): username = serializers.CharField(max_length=30) email = serializers.EmailField() + captcha = serializers.CharField(max_length=4, min_length=4) + + +class ResetPasswordSerializer(serializers.Serializer): + token = serializers.CharField(min_length=1, max_length=40) + password = serializers.CharField(min_length=6, max_length=30) captcha = serializers.CharField(max_length=4, min_length=4) \ No newline at end of file diff --git a/account/test_urls.py b/account/test_urls.py index 5d2bdcc..224ee63 100644 --- a/account/test_urls.py +++ b/account/test_urls.py @@ -3,7 +3,8 @@ from django.conf.urls import include, url from django.views.generic import TemplateView from .tests import (LoginRequiredCBVTestWithArgs, LoginRequiredCBVTestWithoutArgs, - AdminRequiredCBVTestWithArgs, AdminRequiredCBVTestWithoutArgs) + AdminRequiredCBVTestWithArgs, AdminRequiredCBVTestWithoutArgs, + SuperAdminRequiredCBVTestWithArgs, SuperAdminRequiredCBVTestWithoutArgs) urlpatterns = [ @@ -16,5 +17,11 @@ urlpatterns = [ url(r'^admin_required_test/fbv/(?P\d+)/$', "account.tests.admin_required_FBC_test_with_args"), url(r'^admin_required_test/cbv/1/$', AdminRequiredCBVTestWithoutArgs.as_view()), url(r'^admin_required_test/cbv/(?P\d+)/$', AdminRequiredCBVTestWithArgs.as_view()), + + url(r'^super_admin_required_test/fbv/1/$', "account.tests.super_admin_required_FBV_test_without_args"), + url(r'^super_admin_required_test/fbv/(?P\d+)/$', "account.tests.super_admin_required_FBC_test_with_args"), + url(r'^super_admin_required_test/cbv/1/$', SuperAdminRequiredCBVTestWithoutArgs.as_view()), + url(r'^super_admin_required_test/cbv/(?P\d+)/$', SuperAdminRequiredCBVTestWithArgs.as_view()), + url(r'^login/$', TemplateView.as_view(template_name="oj/account/login.html"), name="user_login_page"), ] diff --git a/account/tests.py b/account/tests.py index d0adf8a..968b4c2 100644 --- a/account/tests.py +++ b/account/tests.py @@ -1,17 +1,16 @@ # coding=utf-8 import json +import time from django.core.urlresolvers import reverse from django.test import TestCase, Client from django.http import HttpResponse from django.contrib import auth - from rest_framework.test import APITestCase, APIClient from rest_framework.views import APIView -from rest_framework.response import Response -from .models import User, SUPER_ADMIN -from .decorators import login_required, admin_required +from .models import User, SUPER_ADMIN, REGULAR_USER, ADMIN +from .decorators import login_required, admin_required, super_admin_required class UserLoginTest(TestCase): @@ -21,26 +20,44 @@ class UserLoginTest(TestCase): self.assertTemplateUsed(response, "oj/account/login.html") +def create_user(username="test", real_name="test_real_name", email="test@qq.com", + password="111111", admin_type=REGULAR_USER): + user = User.objects.create(username=username, real_name=real_name, email=email, admin_type=admin_type) + user.set_password(password) + user.save() + return user + + +def set_captcha(session): + session["_django_captcha_key"] = "aaaa" + session["_django_captcha_expires_time"] = time.time() + 10000 + session.save() + + class UserLoginAPITest(APITestCase): def setUp(self): self.client = APIClient() self.url = reverse("user_login_api") - user = User.objects.create(username="test") - user.set_password("test") - user.save() + self.user = create_user() + set_captcha(self.client.session) def test_invalid_data(self): data = {"username": "test"} response = self.client.post(self.url, data=data) self.assertEqual(response.data["code"], 1) + def test_captcha(self): + error_data = {"username": "test", "password": "test11", "captcha": "1111"} + response = self.client.post(self.url, data=error_data) + self.assertEqual(response.data, {"code": 1, "data": u"验证码错误"}) + def test_error_username_or_password(self): - error_data = {"username": "test", "password": "test11"} + error_data = {"username": "test", "password": "test11", "captcha": "aaaa"} response = self.client.post(self.url, data=error_data) self.assertEqual(response.data, {"code": 1, "data": u"用户名或密码错误"}) - def test_success_login(self): - data = {"username": "test", "password": "test"} + def test_login_successfully(self): + data = {"username": "test", "password": "111111", "captcha": "aaaa"} response = self.client.post(self.url, data=data) self.assertEqual(response.data, {"code": 0, "data": u"登录成功"}) @@ -49,69 +66,75 @@ class UsernameCheckTest(APITestCase): def setUp(self): self.client = APIClient() self.url = reverse("username_check_api") - User.objects.create(username="testtest") + create_user() def test_invalid_data(self): - response = self.client.post(self.url, data={"username111": "testtest"}) - self.assertEqual(response.data["code"], 1) + response = self.client.get(self.url, data={"username111": "testtest"}) + self.assertEqual(response.status_code, 200) def test_username_exists(self): - response = self.client.post(self.url, data={"username": "testtest"}) - self.assertEqual(response.data, {"code": 0, "data": True}) + response = self.client.get(self.url, data={"username": "test"}) + self.assertEqual(response.status_code, 400) def test_username_does_not_exist(self): - response = self.client.post(self.url, data={"username": "testtest123"}) - self.assertEqual(response.data, {"code": 0, "data": False}) + response = self.client.get(self.url, data={"username": "testtest123"}) + self.assertEqual(response.status_code, 200) class EmailCheckTest(APITestCase): def setUp(self): self.client = APIClient() self.url = reverse("email_check_api") - User.objects.create(email="11@qq.com") + create_user() def test_invalid_data(self): - response = self.client.post(self.url, data={"email000": "11@qq.com"}) - self.assertEqual(response.data["code"], 1) + response = self.client.get(self.url, data={"email000": "11@qq.com"}) + self.assertEqual(response.status_code, 200) def test_email_exists(self): - response = self.client.post(self.url, data={"email": "11@qq.com"}) - self.assertEqual(response.data, {"code": 0, "data": True}) + response = self.client.get(self.url, data={"email": "test@qq.com"}) + self.assertEqual(response.status_code, 400) def test_email_does_not_exist(self): - response = self.client.post(self.url, data={"email": "33@qq.com"}) - self.assertEqual(response.data, {"code": 0, "data": False}) + response = self.client.get(self.url, data={"email": "33testtest@qq.com"}) + self.assertEqual(response.status_code, 200) class UserRegisterAPITest(APITestCase): def setUp(self): self.client = APIClient() self.url = reverse("user_register_api") + set_captcha(self.client.session) def test_invalid_data(self): data = {"username": "test", "real_name": "TT"} response = self.client.post(self.url, data=data) self.assertEqual(response.data["code"], 1) + def test_captcha(self): + data = {"username": "test", "real_name": "TT", "password": "qqqqqq", "email": "6060@qq.com", "captcha": "bbaa"} + response = self.client.post(self.url, data=data) + self.assertEqual(response.data, {"code": 1, "data": u"验证码错误"}) + def test_short_password(self): - data = {"username": "test", "real_name": "TT", "password": "qq", "email": "6060@qq.com"} + data = {"username": "test", "real_name": "TT", "password": "qq", "email": "6060@qq.com", "captcha": "aaaa"} response = self.client.post(self.url, data=data) self.assertEqual(response.data["code"], 1) def test_same_username(self): - User.objects.create(username="aa") - data = {"username": "aa", "real_name": "ww", "password": "zzzzzzz", "email": "6060@qq.com"} + create_user() + data = {"username": "test", "real_name": "ww", "password": "zzzzzzz", "email": "606fds0@qq.com", "captcha": "aaaa"} response = self.client.post(self.url, data=data) self.assertEqual(response.data, {"code": 1, "data": u"用户名已存在"}) def test_same_email(self): - User.objects.create(username="bb", email="8080@qq.com") - data = {"username": "aa", "real_name": "ww", "password": "zzzzzzz", "email": "8080@qq.com"} + create_user(username="test1", email="test1@qq.com") + data = {"username": "aa", "real_name": "ww", "password": "zzzzzzz", "email": "test1@qq.com", "captcha": "aaaa"} response = self.client.post(self.url, data=data) self.assertEqual(response.data, {"code": 1, "data": u"该邮箱已被注册,请换其他邮箱进行注册"}) - def test_success_email(self): - data = {"username": "cc", "real_name": "dd", "password": "xxxxxx", "email": "9090@qq.com"} + def test_register_successfully(self): + data = {"username": "cc", "real_name": "dd", "password": "xxxxxx", "email": "9090@qq.com", "captcha": "aaaa"} response = self.client.post(self.url, data=data) self.assertEqual(response.data, {"code": 0, "data": u"注册成功!"}) @@ -120,18 +143,27 @@ class UserChangePasswordAPITest(APITestCase): def setUp(self): self.client = APIClient() self.url = reverse("user_change_password_api") - user = User.objects.create(username="test") - user.set_password("aaabbb") - user.save() - self.client.login(username="test",password="aaabbb") + create_user() + self.client.login(username="test",password="111111") + set_captcha(self.client.session) + + def test_captcha(self): + data = {"old_password": "aaaccc", "new_password": "aaaddd", "captcha": "abba"} + response = self.client.post(self.url, data=data) + self.assertEqual(response.data, {"code": 1, "data": u"验证码错误"}) + + def test_invalid_data(self): + data = {"new_password": "aaaddd", "captcha": "aaaa"} + response = self.client.post(self.url, data=data) + self.assertEqual(response.data["code"], 1) def test_error_old_password(self): - data = {"old_password": "aaaccc", "new_password": "aaaddd"} + data = {"old_password": "aaaccc", "new_password": "aaaddd", "captcha": "aaaa"} response = self.client.post(self.url, data=data) self.assertEqual(response.data, {"code": 1, "data": u"密码不正确,请重新修改!"}) - def test_success_change_password(self): - data = {"username": "test", "old_password": "aaabbb", "new_password": "aaaccc"} + def test_change_password_successfully(self): + data = {"old_password": "111111", "new_password": "aaaccc", "captcha": "aaaa"} response = self.client.post(self.url, data=data) self.assertEqual(response.data, {"code": 0, "data": u"用户密码修改成功!"}) @@ -140,81 +172,82 @@ class UserAdminAPITest(APITestCase): def setUp(self): self.client = APIClient() self.url = reverse("user_admin_api") - self.user1 = User.objects.create(username="testx", real_name="xx", admin_type=SUPER_ADMIN) - self.user1.set_password("testxx") - self.user1.save() - self.user = User.objects.create(username="testy", real_name="yy", admin_type=SUPER_ADMIN) - self.user.set_password("testyy") - self.user.save() - self.client.login(username="testx", password="testxx") + self.user = create_user(admin_type=SUPER_ADMIN) + self.client.login(username="test", password="111111") - # 以下是编辑用户的测试 def test_success_get_data(self): self.assertEqual(self.client.get(self.url).data["code"], 0) + def test_super_admin_required(self): + create_user(username="test1", email="test1@qq.com", admin_type=ADMIN) + self.client.login(username="test1", password="111111") + + self.assertEqual(json.loads(self.client.get(self.url, HTTP_X_REQUESTED_WITH="XMLHttpRequest").content), + {"code": 1, "data": u"请先登录"}) + self.assertEqual(json.loads(self.client.put(self.url, HTTP_X_REQUESTED_WITH="XMLHttpRequest").content), + {"code": 1, "data": u"请先登录"}) + + # 这个拦截操作其实是 Middleware 完成的 + create_user(username="test2", email="test2@qq.com") + self.client.login(username="test2", password="111111") + self.assertEqual(json.loads(self.client.get(self.url, HTTP_X_REQUESTED_WITH="XMLHttpRequest").content), + {"code": 1, "data": u"请先登录"}) + self.assertEqual(json.loads(self.client.put(self.url, HTTP_X_REQUESTED_WITH="XMLHttpRequest").content), + {"code": 1, "data": u"请先登录"}) + def test_put_invalid_data(self): - data = {"username": "test", "password": "testaa", "email": "60@qq.com", "admin_type": "2"} + data = {"username": "test", "password": "testaa"} response = self.client.put(self.url, data=data) self.assertEqual(response.data["code"], 1) def test_user_does_not_exist(self): - data = {"id": 3, "username": "test0", "real_name": "test00", + data = {"id": 8888, "username": "test0", "real_name": "test00", "password": "testaa", "email": "60@qq.com", "admin_type": "2"} response = self.client.put(self.url, data=data) self.assertEqual(response.data, {"code": 1, "data": u"该用户不存在!"}) def test_username_exists(self): - data = {"id": self.user.id, "username": "testx", "real_name": "test00", + create_user(username="test1", email="test1@qq.com") + data = {"id": self.user.id, "username": "test1", "real_name": "test00", "password": "testaa", "email": "60@qq.com", "admin_type": "2"} response = self.client.put(self.url, data=data) self.assertEqual(response.data, {"code": 1, "data": u"昵称已经存在"}) - def test_user_edit_not_password_successfully(self): - data = {"id": self.user.id, "username": "test0", "real_name": "test00", + def test_edit_user_without_changing_password(self): + data = {"id": self.user.id, "username": "test2", "real_name": "test00", "email": "60@qq.com", "admin_type": "2"} response = self.client.put(self.url, data=data) self.assertEqual(response.data["code"], 0) - def test_user_edit_change_password_successfully(self): - data = {"id": self.user.id, "username": "test0", "real_name": "test00", "password": "111111", + def test_user_edit_with_changing_password(self): + data = {"id": self.user.id, "username": "test", "real_name": "test00", "password": "111111", "email": "60@qq.com", "admin_type": "2"} response = self.client.put(self.url, data=data) self.assertEqual(response.data["code"], 0) - self.assertIsNotNone(auth.authenticate(username="test0", password="111111")) + self.assertIsNotNone(auth.authenticate(username="test", password="111111")) + + def test_search_user(self): + r = self.assertEqual(self.client.get(self.url + "?keyword=11").status_code, 200) def test_error_admin_type(self): response = self.client.get(self.url + "?admin_type=error") self.assertEqual(response.data, {"code": 1, "data": u"参数错误"}) - # 以下是用户分页的测试 - def test_query_by_keyword(self): - user1 = User.objects.create(username="test1", real_name="aa") - user1.set_password("testaa") - user1.save() - - user2 = User.objects.create(username="test2", real_name="bb") - user2.set_password("testbb") - user2.save() - - user3 = User.objects.create(username="test3", real_name="cc") - user3.set_password("testcc") - user3.save() - - response = self.client.get(self.url + "?keyword=test1") - self.assertEqual(response.data["code"], 0) - class UserInfoAPITest(APITestCase): def setUp(self): self.client = APIClient() self.url = reverse('user_info_api') - user = User.objects.create(username="test1", real_name="aa") - user.set_password("testaa") - user.save() + self.user = create_user() def test_get_data_successfully(self): - self.client.login(username="test1", password="testaa") - self.assertEqual(self.client.get(self.url).data["code"], 0) + self.client.login(username="test", password="111111") + data = self.client.get(self.url).data + self.assertEqual(data["code"], 0) + self.assertEqual(data["data"]["username"], self.user.username) + + def test_get_data_without_logging_in(self): + self.assertEqual(self.client.get(self.url, HTTP_X_REQUESTED_WITH="XMLHttpRequest").data["code"], 1) @login_required @@ -324,7 +357,7 @@ class AdminRequiredDecoratorTest(TestCase): def test_fbv_without_args(self): # 没登陆 response = self.client.get("/admin_required_test/fbv/1/") - self.assertTemplateUsed(response, "utils/error.html") + self.assertRedirects(response, "/login/") # 登陆后 self.client.login(username="test", password="test") @@ -334,7 +367,7 @@ class AdminRequiredDecoratorTest(TestCase): def test_fbv_with_args(self): # 没登陆 response = self.client.get("/admin_required_test/fbv/1024/") - self.assertTemplateUsed(response, "utils/error.html") + self.assertRedirects(response, "/login/") # 登陆后 self.client.login(username="test", password="test") @@ -343,8 +376,8 @@ class AdminRequiredDecoratorTest(TestCase): def test_cbv_without_args(self): # 没登陆 - response = self.client.get("/admin_required_test/cbv/1/") - self.assertTemplateUsed(response, "utils/error.html") + response = self.client.get("/admin_required_test/cbv/1/", HTTP_X_REQUESTED_WITH='XMLHttpRequest') + self.assertEqual(response.data, {"code": 1, "data": u"请先登录"}) # 登陆后 self.client.login(username="test", password="test") @@ -354,7 +387,7 @@ class AdminRequiredDecoratorTest(TestCase): def test_cbv_with_args(self): # 没登陆 response = self.client.get("/admin_required_test/cbv/1024/", HTTP_X_REQUESTED_WITH='XMLHttpRequest') - self.assertEqual(json.loads(response.content), {"code": 1, "data": u"需要管理员权限"}) + self.assertEqual(response.data, {"code": 1, "data": u"请先登录"}) # 登陆后 self.client.login(username="test", password="test") @@ -362,16 +395,100 @@ class AdminRequiredDecoratorTest(TestCase): self.assertEqual(response.content, "1024") +@super_admin_required +def super_admin_required_FBV_test_without_args(request): + return HttpResponse("function based view test1") + + +@super_admin_required +def super_admin_required_FBC_test_with_args(request, problem_id): + return HttpResponse(problem_id) + + +class SuperAdminRequiredCBVTestWithoutArgs(APIView): + @super_admin_required + def get(self, request): + return HttpResponse("class based view login required test1") + + +class SuperAdminRequiredCBVTestWithArgs(APIView): + @super_admin_required + def get(self, request, problem_id): + return HttpResponse(problem_id) + + +class SuperAdminRequiredDecoratorTest(TestCase): + urls = 'account.test_urls' + + def setUp(self): + self.client = Client() + create_user(admin_type=SUPER_ADMIN) + + def test_fbv_without_args(self): + # 没登陆 + response = self.client.get("/super_admin_required_test/fbv/1/") + self.assertRedirects(response, "/login/") + + # 登陆后 + self.client.login(username="test", password="111111") + response = self.client.get("/super_admin_required_test/fbv/1/") + self.assertEqual(response.content, "function based view test1") + + def test_fbv_with_args(self): + # 没登陆 + response = self.client.get("/super_admin_required_test/fbv/1024/") + self.assertRedirects(response, "/login/") + + # 登陆后 + self.client.login(username="test", password="111111") + response = self.client.get("/super_admin_required_test/fbv/1024/") + self.assertEqual(response.content, "1024") + + def test_cbv_without_args(self): + # 没登陆 + response = self.client.get("/super_admin_required_test/cbv/1/", HTTP_X_REQUESTED_WITH='XMLHttpRequest') + self.assertEqual(response.data, {"code": 1, "data": u"请先登录"}) + + # 登陆后 + self.client.login(username="test", password="111111") + response = self.client.get("/super_admin_required_test/cbv/1/") + self.assertEqual(response.content, "class based view login required test1") + + def test_cbv_with_args(self): + # 没登陆 + response = self.client.get("/super_admin_required_test/cbv/1024/", HTTP_X_REQUESTED_WITH='XMLHttpRequest') + self.assertEqual(response.data, {"code": 1, "data": u"请先登录"}) + + # 登陆后 + self.client.login(username="test", password="111111") + response = self.client.get("/super_admin_required_test/cbv/10086/") + self.assertEqual(response.content, "10086") + + class UserLogoutTest(TestCase): def setUp(self): self.client = Client() - user = User.objects.create(username="test") - user.admin_type = 1 - user.set_password("1") - user.save() + create_user() def test_logout_success(self): self.client = Client() - self.client.login(username="test", password="1") + self.client.login(username="test", password="111111") response = self.client.get("/logout/") self.assertEqual(response.status_code, 302) + + +class IndexPageTest(TestCase): + def setUp(self): + create_user() + self.client = Client() + + def test_not_login_user(self): + self.assertTemplateUsed(self.client.get("/"), "oj/index.html") + + def test_no_referer_redirect(self): + self.client.login(username="test", password="111111") + self.assertRedirects(self.client.get("/"), "/problems/") + + def test_visit_with_referer(self): + self.client.login(username="test", password="111111") + self.assertTemplateUsed(self.client.get("/", HTTP_REFERER="/about/"), "oj/index.html") From 723b26a8283a4e9ddcf8c743d3046d656dd59b00 Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Fri, 16 Oct 2015 20:43:34 +0800 Subject: [PATCH 03/25] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E5=85=AC=E5=91=8A?= =?UTF-8?q?=E5=8A=9F=E8=83=BD=EF=BC=9B=E5=8E=BB=E9=99=A4=E5=B0=8F=E7=BB=84?= =?UTF-8?q?=E5=8F=AF=E8=A7=81=E5=85=AC=E5=91=8A=EF=BC=9B=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E5=90=8E=E7=AB=AF=E6=B5=8B=E8=AF=95=EF=BC=9B=E4=BF=AE=E6=94=B9?= =?UTF-8?q?=E5=90=8E=E5=8F=B0=E5=AF=B9=E5=BA=94=E7=9A=84=20js=20=E5=92=8C?= =?UTF-8?q?=E7=95=8C=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- announcement/decorators.py | 43 - announcement/models.py | 3 - announcement/serializers.py | 6 +- announcement/tests.py | 185 +- announcement/views.py | 57 +- .../js/app/admin/announcement/announcement.js | 164 +- static/src/js/lib/avalon/avalon.js | 5718 ----------------- .../src/admin/announcement/announcement.html | 52 +- utils/templatetags/announcement_list.py | 2 +- 9 files changed, 88 insertions(+), 6142 deletions(-) delete mode 100644 announcement/decorators.py delete mode 100644 static/src/js/lib/avalon/avalon.js diff --git a/announcement/decorators.py b/announcement/decorators.py deleted file mode 100644 index 96df987..0000000 --- a/announcement/decorators.py +++ /dev/null @@ -1,43 +0,0 @@ -# coding=utf-8 -from functools import wraps - -from django.http import HttpResponse, HttpResponseRedirect -from django.shortcuts import render - -from utils.shortcuts import error_response, error_page - -from account.models import SUPER_ADMIN -from .models import Announcement - - -def check_user_announcement_permission(func): - @wraps(func) - def _check_user_announcement_permission(*args, **kwargs): - """ - 这个函数检测当前用户能否查看这个公告 - """ - # CBV 的情况,第一个参数是self,第二个参数是request - if len(args) == 2: - request = args[-1] - else: - request = args[0] - - if "announcement_id" not in kwargs: - return error_page(request, u"参数错误") - announcement_id = kwargs["announcement_id"] - - try: - announcement = Announcement.objects.get(id=announcement_id, visible=True) - except Announcement.DoesNotExist: - return error_page(request, u"公告不存在") - - # 如果公告是只有部分小组可见的 - if not announcement.is_global: - # 用户必须是登录状态的 - if not request.user.is_authenticated(): - return HttpResponseRedirect("/login/") - if not announcement.groups.filter(id__in=request.user.group_set.all()).exists(): - return error_page(request, u"公告不存在") - return func(*args, **kwargs) - - return _check_user_announcement_permission diff --git a/announcement/models.py b/announcement/models.py index 7442721..b097d6f 100644 --- a/announcement/models.py +++ b/announcement/models.py @@ -19,9 +19,6 @@ class Announcement(models.Model): last_update_time = models.DateTimeField(auto_now=True) # 是否可见 false的话相当于删除 visible = models.BooleanField(default=True) - # 公告可见范围 True 是全局可见 False 是部分小组可见,需要在下面的字段中存储可见的小组 - is_global = models.BooleanField() - groups = models.ManyToManyField(Group) class Meta: db_table = "announcement" diff --git a/announcement/serializers.py b/announcement/serializers.py index ec24116..75c395f 100644 --- a/announcement/serializers.py +++ b/announcement/serializers.py @@ -8,8 +8,6 @@ from .models import Announcement class CreateAnnouncementSerializer(serializers.Serializer): title = serializers.CharField(max_length=50) content = serializers.CharField(max_length=10000) - is_global = serializers.BooleanField() - groups = serializers.ListField(child=serializers.IntegerField(), required=False, default=[]) class AnnouncementSerializer(serializers.ModelSerializer): @@ -29,6 +27,4 @@ class EditAnnouncementSerializer(serializers.Serializer): id = serializers.IntegerField() title = serializers.CharField(max_length=50) content = serializers.CharField(max_length=10000) - visible = serializers.BooleanField() - is_global = serializers.BooleanField() - groups = serializers.ListField(child=serializers.IntegerField(), required=False, default=[]) + visible = serializers.BooleanField() \ No newline at end of file diff --git a/announcement/tests.py b/announcement/tests.py index e37c183..ff9ca0c 100644 --- a/announcement/tests.py +++ b/announcement/tests.py @@ -1,171 +1,64 @@ # coding=utf-8 from django.core.urlresolvers import reverse -from django.test import TestCase - +from django.test import TestCase, Client from rest_framework.test import APITestCase, APIClient from account.models import User +from account.tests import create_user from group.models import Group from announcement.models import Announcement -from account.models import REGULAR_USER, ADMIN, SUPER_ADMIN +from account.models import ADMIN, SUPER_ADMIN class AnnouncementAdminAPITest(APITestCase): - def setUp(self): - self.client = APIClient() - self.url = reverse("announcement_admin_api") - user1 = User.objects.create(username="test1", admin_type=SUPER_ADMIN) - user1.set_password("testaa") - user1.save() - user2 = User.objects.create(username="test2", admin_type=ADMIN) - user2.set_password("testbb") - user2.save() - self.group = Group.objects.create(name="group1", description="des0", - join_group_setting=0, visible=True, - admin=user2) - self.announcement = Announcement.objects.create(title="bb", - content="BB", - created_by=User.objects.get(username="test2"), - is_global=False) + def setUp(self): + self.client = APIClient() + self.url = reverse("announcement_admin_api") + self.user1 = create_user(admin_type=SUPER_ADMIN) - # 以下是发布公告的测试 - def test_invalid_format(self): - self.client.login(username="test1", password="testaa") - data = {"title": "test1"} - response = self.client.post(self.url, data=data) - self.assertEqual(response.data["code"], 1) + self.user2 = create_user(username="test1", email="test1@qq.com", admin_type=SUPER_ADMIN) - def test_group_at_least_one(self): - self.client.login(username="test1", password="testaa") - data = {"title": "title0", "content": "content0", "is_global": False} - response = self.client.post(self.url, data=data) - self.assertEqual(response.data, {"code": 1, "data": u"至少选择一个小组"}) + self.announcement = Announcement.objects.create(title="bb", + content="BB", + created_by=self.user1) - def test_global_announcement_successfully(self): - self.client.login(username="test1", password="testaa") - data = {"title": "title0", "content": "content0", "is_global": True} - response = self.client.post(self.url, data=data) - self.assertEqual(response.data, {"code": 0, "data": u"公告发布成功!"}) + def test_create_announcement_successfully(self): + self.client.login(username="test", password="111111") + data = {"title": "title0", "content": "content0"} + response = self.client.post(self.url, data=data) + self.assertEqual(response.data, {"code": 0, "data": u"公告发布成功!"}) - def test_group_announcement_successfully(self): - self.client.login(username="test2", password="testbb") - data = {"title": "title0", "content": "content0", "is_global": False, "groups": [self.group.id]} - response = self.client.post(self.url, data=data) - self.assertEqual(response.data, {"code": 0, "data": u"公告发布成功!"}) - - def test_global_announcement_does_not_has_privileges(self): - self.client.login(username="test2", password="testbb") - data = {"title": "title0", "content": "content0", "is_global": True} - response = self.client.post(self.url, data=data) - self.assertEqual(response.data, {"code": 1, "data": u"只有超级管理员可以创建全局公告"}) - - # 以下是编辑公告的测试 - def test_put_invalid_data(self): - self.client.login(username="test1", password="testaa") - data = {"title": "test0", "content": "test0", "visible": "True"} - response = self.client.put(self.url, data=data) - self.assertEqual(response.data["code"], 1) - - def test_announcement_does_not_exist(self): - self.client.login(username="test1", password="testaa") - announcement = Announcement.objects.create(title="aa", - content="AA", - created_by=User.objects.get(username="test1"), - is_global=True) - data = {"id": announcement.id + 1, "title": "11", "content": "22", - "visible": True, "is_global": True} - response = self.client.put(self.url, data=data) - self.assertEqual(response.data, {"code": 1, "data": u"公告不存在"}) - - def test_edit_global_announcement_successfully(self): - self.client.login(username="test1", password="testaa") - data = {"id": self.announcement.id, "title": "11", "content": "22", - "visible": True, "is_global": True} - response = self.client.put(self.url, data=data) - self.assertEqual(response.data["code"], 0) - - def test_edit_group_announcement_successfully(self): - self.client.login(username="test2", password="testbb") - data = {"id": self.announcement.id, "title": "11", "content": "22", - "visible": True, "is_global": False, "groups": [self.group.id]} - response = self.client.put(self.url, data=data) - self.assertEqual(response.data["code"], 0) - self.assertEqual(response.data["data"]["title"], "11") - self.assertEqual(response.data["data"]["content"], "22") - - def test_edit_group_at_least_one(self): - self.client.login(username="test1", password="testaa") - data = {"id": self.announcement.id, "title": "title0", "content": "content0", - "visible": True, "is_global": False} - response = self.client.put(self.url, data=data) - self.assertEqual(response.data, {"code": 1, "data": u"至少选择一个小组"}) - - # 以下是公告分页的测试 - def test_get_data_successfully(self): - self.client.login(username="test1", password="testaa") - self.assertEqual(self.client.get(self.url).data["code"], 0) - - def test_keyword_global_announcement(self): - self.client.login(username="test1", password="testaa") - Announcement.objects.create(title="aa", - content="AA", - created_by=User.objects.get(username="test1"), - visible=True, - is_global=True) - - Announcement.objects.create(title="bb", - content="BB", - created_by=User.objects.get(username="test1"), - visible=False, - is_global=True) - - response = self.client.get(self.url + "?visible=true") - self.assertEqual(response.data["code"], 0) - for item in response.data["data"]: - self.assertEqual(item["visible"], True) - - def test_keyword_group_announcement(self): - self.client.login(username="test2", password="testbb") - Announcement.objects.create(title="aa", - content="AA", - created_by=User.objects.get(username="test2"), - visible=True, - is_global=False) - - Announcement.objects.create(title="cc", - content="CC", - created_by=User.objects.get(username="test2"), - visible=False, - is_global=False) - - response = self.client.get(self.url + "?visible=true") - self.assertEqual(response.data["code"], 0) - for item in response.data["data"]: - self.assertEqual(item["visible"], True) + def test_edit_announcement_successfully(self): + self.client.login(username="test", password="111111") + data = {"id": self.announcement.id, "title": "11", "content": "22", "visible": True} + response = self.client.put(self.url, data=data) + self.assertEqual(response.data["code"], 0) class AnnouncementPageTest(TestCase): def setUp(self): - user = User.objects.create(username="test") - user.set_password("testaa") - user.save() - Announcement.objects.create(title="aa", - content="AA", - created_by=User.objects.get(username="test"), - visible=True, - is_global=True) + self.client = Client() + user = create_user() + self.a1 = Announcement.objects.create(title="aa", + content="AA", + created_by=user, + visible=True, + ) - Announcement.objects.create(title="bb", - content="BB", - created_by=User.objects.get(username="test"), - visible=False, - is_global=True) + self.a2 = Announcement.objects.create(title="bb", + content="BB", + created_by=User.objects.get(username="test"), + visible=False + ) def test_visit_announcement_successfully(self): - response = self.client.get('/announcement/1/') - self.assertEqual(response.status_code, 200) + response = self.client.get('/announcement/' + str(self.a1.id) + "/") + self.assertTemplateUsed(response, "oj/announcement/announcement.html") def test_announcement_does_not_exist(self): - response = self.client.get('/announcement/3/') + response = self.client.get('/announcement/10086/') self.assertTemplateUsed(response, "utils/error.html") + def test_visit_hidden_announcement(self): + response = self.client.get('/announcement/' + str(self.a2.id) + "/") + self.assertTemplateUsed(response, "utils/error.html") diff --git a/announcement/views.py b/announcement/views.py index 3160d9e..17a9fe1 100644 --- a/announcement/views.py +++ b/announcement/views.py @@ -6,20 +6,26 @@ from utils.shortcuts import serializer_invalid_response, error_response, success from utils.shortcuts import paginate, error_page from account.models import SUPER_ADMIN, ADMIN +from account.decorators import super_admin_required from group.models import Group from .models import Announcement from .serializers import (CreateAnnouncementSerializer, AnnouncementSerializer, EditAnnouncementSerializer) -from .decorators import check_user_announcement_permission -@check_user_announcement_permission def announcement_page(request, announcement_id): - announcement = Announcement.objects.get(id=announcement_id, visible=True) + """ + 公告的详情页面 + """ + try: + announcement = Announcement.objects.get(id=announcement_id, visible=True) + except Announcement.DoesNotExist: + return error_page(request, u"公告不存在") return render(request, "oj/announcement/announcement.html", {"announcement": announcement}) class AnnouncementAdminAPIView(APIView): + @super_admin_required def post(self, request): """ 公告发布json api接口 @@ -29,29 +35,12 @@ class AnnouncementAdminAPIView(APIView): serializer = CreateAnnouncementSerializer(data=request.data) if serializer.is_valid(): data = serializer.data - groups = [] - # 如果不是全局公告,就去查询一下小组的id 列表中的内容,注意用户身份 - if not data["is_global"]: - if request.user.admin_type == SUPER_ADMIN: - groups = Group.objects.filter(id__in=data["groups"]) - else: - groups = Group.objects.filter(id__in=data["groups"], admin=request.user) - if not groups.count(): - return error_response(u"至少选择一个小组") - else: - if request.user.admin_type != SUPER_ADMIN: - return error_response(u"只有超级管理员可以创建全局公告") - - announcement = Announcement.objects.create(title=data["title"], - content=data["content"], - created_by=request.user, - is_global=data["is_global"]) - - announcement.groups.add(*groups) + Announcement.objects.create(title=data["title"], content=data["content"], created_by=request.user) return success_response(u"公告发布成功!") else: return serializer_invalid_response(serializer) + @super_admin_required def put(self, request): """ 公告编辑json api接口 @@ -63,43 +52,27 @@ class AnnouncementAdminAPIView(APIView): if serializer.is_valid(): data = serializer.data try: - if request.user.admin_type == SUPER_ADMIN: - announcement = Announcement.objects.get(id=data["id"]) - else: - announcement = Announcement.objects.get(id=data["id"], created_by=request.user) + announcement = Announcement.objects.get(id=data["id"]) except Announcement.DoesNotExist: return error_response(u"公告不存在") - groups = [] - if not data["is_global"]: - if request.user.admin_type == SUPER_ADMIN: - groups = Group.objects.filter(id__in=data["groups"]) - else: - groups = Group.objects.filter(id__in=data["groups"], admin=request.user) - if not groups.count(): - return error_response(u"至少选择一个小组") + announcement.title = data["title"] announcement.content = data["content"] announcement.visible = data["visible"] - announcement.is_global = data["is_global"] announcement.save() - # 重建小组和公告的对应关系 - announcement.groups.clear() - announcement.groups.add(*groups) return success_response(AnnouncementSerializer(announcement).data) else: return serializer_invalid_response(serializer) + @super_admin_required def get(self, request): """ 公告分页json api接口 --- response_serializer: AnnouncementSerializer """ - if request.user.admin_type == SUPER_ADMIN: - announcement = Announcement.objects.all().order_by("-last_update_time") - else: - announcement = Announcement.objects.filter(created_by=request.user) + announcement = Announcement.objects.all().order_by("-create_time") visible = request.GET.get("visible", None) if visible: announcement = announcement.filter(visible=(visible == "true")) diff --git a/static/src/js/app/admin/announcement/announcement.js b/static/src/js/app/admin/announcement/announcement.js index df73769..9b64a19 100644 --- a/static/src/js/app/admin/announcement/announcement.js +++ b/static/src/js/app/admin/announcement/announcement.js @@ -1,69 +1,38 @@ -require(["jquery", "avalon", "csrfToken", "bsAlert", "editor", "validator"], +require(["jquery", "avalon", "csrfToken", "bsAlert", "editor", "validator", "pager"], function ($, avalon, csrfTokenHeader, bsAlert, editor) { avalon.ready(function () { var createAnnouncementEditor = editor("#create-announcement-editor"); var editAnnouncementEditor = editor("#edit-announcement-editor"); + if (avalon.vmodels.announcement){ var vm = avalon.vmodels.announcement; - announcementList = []; } else { - var vm = avalon.define({ $id: "announcement", - //通用变量 - announcementList: [], // 公告列表数据项 - previousPage: 0, // 之前的页数 - nextPage: 0, // 之后的页数 - page: 1, // 当前页数 - editingAnnouncementId: 0, // 正在编辑的公告的ID, 为零说明未在编辑 - totalPage: 1, // 总页数 - showVisibleOnly: false, //仅显示可见公告 - // 编辑 + announcementList: [], + pager: { + getPage: function(page){ + getPage(page); + } + }, + isEditing: false, + announcementId: -1, + showVisibleOnly: false, newTitle: "", announcementVisible: 0, - showGlobalViewRadio: true, - isGlobal: true, - allGroups: [], - getNext: function () { - if (!vm.nextPage) - return; - getPageData(vm.page + 1); - }, - getPrevious: function () { - if (!vm.previousPage) - return; - getPageData(vm.page - 1); - }, - getBtnClass: function (btnType) { - if (btnType == "next") { - return vm.nextPage ? "btn btn-primary" : "btn btn-primary disabled"; - } - else { - return vm.previousPage ? "btn btn-primary" : "btn btn-primary disabled"; - } - }, + editAnnouncement: function (announcement) { vm.newTitle = announcement.title; + vm.announcementId = announcement.id; editAnnouncementEditor.setValue(announcement.content); vm.announcementVisible = announcement.visible; - if (vm.editingAnnouncementId == announcement.id) - vm.editingAnnouncementId = 0; - else - vm.editingAnnouncementId = announcement.id; - vm.isGlobal = announcement.is_global; - for (var i = 0; i < announcement.groups.length; i++) { - for (var j = 0; j < vm.allGroups.length; j++) { - if (announcement.groups[i] == vm.allGroups[j].id) { - vm.allGroups[j].isSelected = true; - } - } - } + vm.isEditing = !vm.isEditing; editAnnouncementEditor.focus(); }, cancelEdit: function () { - vm.editingAnnouncementId = 0; + vm.isEditing = false; }, submitChange: function () { var title = vm.newTitle; @@ -74,39 +43,22 @@ require(["jquery", "avalon", "csrfToken", "bsAlert", "editor", "validator"], return false; } - var selectedGroups = []; - if (!vm.isGlobal) { - for (var i = 0; i < vm.allGroups.length; i++) { - if (vm.allGroups[i].isSelected) { - selectedGroups.push(vm.allGroups[i].id); - } - } - } - - if (!vm.isGlobal && !selectedGroups.length) { - bsAlert("请至少选择一个小组"); - return false; - } - $.ajax({ - beforeSend: csrfTokenHeader, url: "/api/admin/announcement/", contentType: "application/json;charset=UTF-8", dataType: "json", method: "put", data: JSON.stringify({ - id: vm.editingAnnouncementId, + id: vm.announcementId, title: title, content: content, - visible: vm.announcementVisible, - is_global: vm.isGlobal, - groups: selectedGroups + visible: vm.announcementVisible }), success: function (data) { if (!data.code) { bsAlert("修改成功"); - vm.editingAnnouncementId = 0; - getPageData(1); + vm.isEditing = false; + getPage(1); } else { bsAlert(data.data); @@ -116,69 +68,27 @@ require(["jquery", "avalon", "csrfToken", "bsAlert", "editor", "validator"], } }); + vm.$watch("showVisibleOnly", function () { - getPageData(1); + getPage(1); + avalon.vmodels.announcementPager.currentPage = 1; }); } - getPageData(1); - - $.ajax({ - url: "/api/user/", - method: "get", - dataType: "json", - success: function (data) { - if (!data.code) { - var admin_type = data.data.admin_type; - if (data.data.admin_type == 1) { - vm.isGlobal = false; - vm.showGlobalViewRadio = false; - - } - } - $.ajax({ - url: "/api/admin/group/", - method: "get", - dataType: "json", - success: function (data) { - if (!data.code) { - if (!data.data.length) { - if (admin_type != 2) - bsAlert("您的用户权限只能创建组内公告,但是您还没有创建过小组"); - return; - } - for (var i = 0; i < data.data.length; i++) { - var item = data.data[i]; - item["isSelected"] = false; - vm.allGroups.push(item); - } - } - else { - bsAlert(data.data); - } - } - }); - } - }); - - function getPageData(page) { - var url = "/api/admin/announcement/?paging=true&page=" + page + "&page_size=10"; + function getPage(page) { + var url = "/api/admin/announcement/?paging=true&page=" + page + "&page_size=2"; if (vm.showVisibleOnly) url += "&visible=true"; $.ajax({ url: url, - dataType: "json", method: "get", success: function (data) { if (!data.code) { vm.announcementList = data.data.results; - vm.totalPage = data.data.total_page; - vm.previousPage = data.data.previous_page; - vm.nextPage = data.data.next_page; - vm.page = page; + avalon.vmodels.announcementPager.totalPage = data.data.total_page; } else { - bs_alert(data.data); + bsAlert(data.data); } } }); @@ -193,28 +103,12 @@ require(["jquery", "avalon", "csrfToken", "bsAlert", "editor", "validator"], bsAlert("请填写公告内容"); return false; } - var selectedGroups = []; - if (!vm.isGlobal) { - for (var i = 0; i < vm.allGroups.length; i++) { - if (vm.allGroups[i].isSelected) { - selectedGroups.push(vm.allGroups[i].id); - } - } - } - - if (!vm.isGlobal && !selectedGroups.length) { - bsAlert("请至少选择一个小组"); - return false; - } $.ajax({ - beforeSend: csrfTokenHeader, url: "/api/admin/announcement/", - contentType: "application/json;charset=UTF-8", + contentType: "application/json", data: JSON.stringify({ title: title, - content: content, - is_global: vm.isGlobal, - groups: selectedGroups + content: content }), dataType: "json", method: "post", @@ -223,7 +117,7 @@ require(["jquery", "avalon", "csrfToken", "bsAlert", "editor", "validator"], bsAlert("提交成功!"); $("#title").val(""); createAnnouncementEditor.setValue(""); - getPageData(1); + getPage(1); } else { bsAlert(data.data); } diff --git a/static/src/js/lib/avalon/avalon.js b/static/src/js/lib/avalon/avalon.js deleted file mode 100644 index 9f41e82..0000000 --- a/static/src/js/lib/avalon/avalon.js +++ /dev/null @@ -1,5718 +0,0 @@ -/*================================================== - Copyright (c) 2013-2015 司徒正美 and other contributors - http://www.cnblogs.com/rubylouvre/ - https://github.com/RubyLouvre - http://weibo.com/jslouvre/ - - Released under the MIT license - avalon.js 1.44 built in 2015.6.25 - support IE6+ and other browsers - ==================================================*/ -(function(global, factory) { - - if (typeof module === "object" && typeof module.exports === "object") { - // For CommonJS and CommonJS-like environments where a proper `window` - // is present, execute the factory and get avalon. - // For environments that do not have a `window` with a `document` - // (such as Node.js), expose a factory as module.exports. - // This accentuates the need for the creation of a real `window`. - // e.g. var avalon = require("avalon")(window); - module.exports = global.document ? factory(global, true) : function(w) { - if (!w.document) { - throw new Error("Avalon requires a window with a document") - } - return factory(w) - } - } else { - factory(global) - } - -// Pass this if window is not defined yet -}(typeof window !== "undefined" ? window : this, function(window, noGlobal){ - - /********************************************************************* - * 全局变量及方法 * - **********************************************************************/ - var expose = new Date() - 0 -//http://stackoverflow.com/questions/7290086/javascript-use-strict-and-nicks-find-global-function - var DOC = window.document - var head = DOC.getElementsByTagName("head")[0] //HEAD元素 - var ifGroup = head.insertBefore(document.createElement("avalon"), head.firstChild) //避免IE6 base标签BUG - ifGroup.innerHTML = "X" - ifGroup.setAttribute("ms-skip", "1") - ifGroup.className = "avalonHide" - var rnative = /\[native code\]/ //判定是否原生函数 - function log() { - if (window.console && avalon.config.debug) { - // http://stackoverflow.com/questions/8785624/how-to-safely-wrap-console-log - Function.apply.call(console.log, console, arguments) - } - } - - - var subscribers = "$" + expose - var otherRequire = window.require - var otherDefine = window.define - var innerRequire - var stopRepeatAssign = false - var rword = /[^, ]+/g //切割字符串为一个个小块,以空格或豆号分开它们,结合replace实现字符串的forEach - var rcomplexType = /^(?:object|array)$/ - var rsvg = /^\[object SVG\w*Element\]$/ - var rwindow = /^\[object (?:Window|DOMWindow|global)\]$/ - var oproto = Object.prototype - var ohasOwn = oproto.hasOwnProperty - var serialize = oproto.toString - var ap = Array.prototype - var aslice = ap.slice - var Registry = {} //将函数曝光到此对象上,方便访问器收集依赖 - var W3C = window.dispatchEvent - var root = DOC.documentElement - var avalonFragment = DOC.createDocumentFragment() - var cinerator = DOC.createElement("div") - var class2type = {} - "Boolean Number String Function Array Date RegExp Object Error".replace(rword, function (name) { - class2type["[object " + name + "]"] = name.toLowerCase() - }) - - - function noop() { - } - - - function oneObject(array, val) { - if (typeof array === "string") { - array = array.match(rword) || [] - } - var result = {}, - value = val !== void 0 ? val : 1 - for (var i = 0, n = array.length; i < n; i++) { - result[array[i]] = value - } - return result - } - -//生成UUID http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript - var generateID = function (prefix) { - prefix = prefix || "avalon" - return String(Math.random() + Math.random()).replace(/\d\.\d{4}/, prefix) - } - function IE() { - if (window.VBArray) { - var mode = document.documentMode - return mode ? mode : window.XMLHttpRequest ? 7 : 6 - } else { - return 0 - } - } - var IEVersion = IE() - - avalon = function (el) { //创建jQuery式的无new 实例化结构 - return new avalon.init(el) - } - - avalon.profile = function () { - if (window.console && avalon.config.profile) { - Function.apply.call(console.log, console, arguments) - } - } - - /*视浏览器情况采用最快的异步回调*/ - avalon.nextTick = new function () {// jshint ignore:line - var tickImmediate = window.setImmediate - var tickObserver = window.MutationObserver - var tickPost = W3C && window.postMessage - if (tickImmediate) { - return tickImmediate.bind(window) - } - - var queue = [] - function callback() { - var n = queue.length - for (var i = 0; i < n; i++) { - queue[i]() - } - queue = queue.slice(n) - } - - if (tickObserver) { - var node = document.createTextNode("avalon") - new tickObserver(callback).observe(node, {characterData: true})// jshint ignore:line - return function (fn) { - queue.push(fn) - node.data = Math.random() - } - } - - if (tickPost) { - window.addEventListener("message", function (e) { - var source = e.source - if ((source === window || source === null) && e.data === "process-tick") { - e.stopPropagation() - callback() - } - }) - - return function (fn) { - queue.push(fn) - window.postMessage('process-tick', '*') - } - } - - return function (fn) { - setTimeout(fn, 0) - } - }// jshint ignore:line - /********************************************************************* - * avalon的静态方法定义区 * - **********************************************************************/ - avalon.init = function(el) { - this[0] = this.element = el - } - avalon.fn = avalon.prototype = avalon.init.prototype - - avalon.type = function(obj) { //取得目标的类型 - if (obj == null) { - return String(obj) - } - // 早期的webkit内核浏览器实现了已废弃的ecma262v4标准,可以将正则字面量当作函数使用,因此typeof在判定正则时会返回function - return typeof obj === "object" || typeof obj === "function" ? - class2type[serialize.call(obj)] || "object" : - typeof obj - } - - var isFunction = typeof alert === "object" ? function(fn) { - try { - return /^\s*\bfunction\b/.test(fn + "") - } catch (e) { - return false - } - } : function(fn) { - return serialize.call(fn) === "[object Function]" - } - avalon.isFunction = isFunction - - avalon.isWindow = function(obj) { - if (!obj) - return false - // 利用IE678 window == document为true,document == window竟然为false的神奇特性 - // 标准浏览器及IE9,IE10等使用 正则检测 - return obj == obj.document && obj.document != obj //jshint ignore:line - } - - function isWindow(obj) { - return rwindow.test(serialize.call(obj)) - } - if (isWindow(window)) { - avalon.isWindow = isWindow - } - var enu - for (enu in avalon({})) { - break - } - var enumerateBUG = enu !== "0" //IE6下为true, 其他为false - /*判定是否是一个朴素的javascript对象(Object),不是DOM对象,不是BOM对象,不是自定义类的实例*/ - avalon.isPlainObject = function(obj, key) { - if (!obj || avalon.type(obj) !== "object" || obj.nodeType || avalon.isWindow(obj)) { - return false; - } - try { //IE内置对象没有constructor - if (obj.constructor && !ohasOwn.call(obj, "constructor") && !ohasOwn.call(obj.constructor.prototype, "isPrototypeOf")) { - return false; - } - } catch (e) { //IE8 9会在这里抛错 - return false; - } - if (enumerateBUG) { - for (key in obj) { - return ohasOwn.call(obj, key) - } - } - for (key in obj) { - } - return key === void 0 || ohasOwn.call(obj, key) - } - if (rnative.test(Object.getPrototypeOf)) { - avalon.isPlainObject = function(obj) { - // 简单的 typeof obj === "object"检测,会致使用isPlainObject(window)在opera下通不过 - return serialize.call(obj) === "[object Object]" && Object.getPrototypeOf(obj) === oproto - } - } -//与jQuery.extend方法,可用于浅拷贝,深拷贝 - avalon.mix = avalon.fn.mix = function() { - var options, name, src, copy, copyIsArray, clone, - target = arguments[0] || {}, - i = 1, - length = arguments.length, - deep = false - - // 如果第一个参数为布尔,判定是否深拷贝 - if (typeof target === "boolean") { - deep = target - target = arguments[1] || {} - i++ - } - - //确保接受方为一个复杂的数据类型 - if (typeof target !== "object" && !isFunction(target)) { - target = {} - } - - //如果只有一个参数,那么新成员添加于mix所在的对象上 - if (i === length) { - target = this - i-- - } - - for (; i < length; i++) { - //只处理非空参数 - if ((options = arguments[i]) != null) { - for (name in options) { - src = target[name] - try { - copy = options[name] //当options为VBS对象时报错 - } catch (e) { - continue - } - - // 防止环引用 - if (target === copy) { - continue - } - if (deep && copy && (avalon.isPlainObject(copy) || (copyIsArray = Array.isArray(copy)))) { - - if (copyIsArray) { - copyIsArray = false - clone = src && Array.isArray(src) ? src : [] - - } else { - clone = src && avalon.isPlainObject(src) ? src : {} - } - - target[name] = avalon.mix(deep, clone, copy) - } else if (copy !== void 0) { - target[name] = copy - } - } - } - } - return target - } - - function _number(a, len) { //用于模拟slice, splice的效果 - a = Math.floor(a) || 0 - return a < 0 ? Math.max(len + a, 0) : Math.min(a, len); - } - avalon.mix({ - rword: rword, - subscribers: subscribers, - version: 1.44, - ui: {}, - log: log, - slice: W3C ? function(nodes, start, end) { - return aslice.call(nodes, start, end) - } : function(nodes, start, end) { - var ret = [] - var len = nodes.length - if (end === void 0) - end = len - if (typeof end === "number" && isFinite(end)) { - start = _number(start, len) - end = _number(end, len) - for (var i = start; i < end; ++i) { - ret[i - start] = nodes[i] - } - } - return ret - }, - noop: noop, - /*如果不用Error对象封装一下,str在控制台下可能会乱码*/ - error: function(str, e) { - throw (e || Error)(str) - }, - /*将一个以空格或逗号隔开的字符串或数组,转换成一个键值都为1的对象*/ - oneObject: oneObject, - /* avalon.range(10) - => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] - avalon.range(1, 11) - => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] - avalon.range(0, 30, 5) - => [0, 5, 10, 15, 20, 25] - avalon.range(0, -10, -1) - => [0, -1, -2, -3, -4, -5, -6, -7, -8, -9] - avalon.range(0) - => []*/ - range: function(start, end, step) { // 用于生成整数数组 - step || (step = 1) - if (end == null) { - end = start || 0 - start = 0 - } - var index = -1, - length = Math.max(0, Math.ceil((end - start) / step)), - result = new Array(length) - while (++index < length) { - result[index] = start - start += step - } - return result - }, - eventHooks: {}, - /*绑定事件*/ - bind: function(el, type, fn, phase) { - var hooks = avalon.eventHooks - var hook = hooks[type] - if (typeof hook === "object") { - type = hook.type - if (hook.deel) { - fn = hook.deel(el, type, fn, phase) - } - } - var callback = W3C ? fn : function(e) { - fn.call(el, fixEvent(e)); - } - if (W3C) { - el.addEventListener(type, callback, !!phase) - } else { - el.attachEvent("on" + type, callback) - } - return callback - }, - /*卸载事件*/ - unbind: function(el, type, fn, phase) { - var hooks = avalon.eventHooks - var hook = hooks[type] - var callback = fn || noop - if (typeof hook === "object") { - type = hook.type - if (hook.deel) { - fn = hook.deel(el, type, fn, false) - } - } - if (W3C) { - el.removeEventListener(type, callback, !!phase) - } else { - el.detachEvent("on" + type, callback) - } - }, - /*读写删除元素节点的样式*/ - css: function(node, name, value) { - if (node instanceof avalon) { - node = node[0] - } - var prop = /[_-]/.test(name) ? camelize(name) : name, fn - name = avalon.cssName(prop) || prop - if (value === void 0 || typeof value === "boolean") { //获取样式 - fn = cssHooks[prop + ":get"] || cssHooks["@:get"] - if (name === "background") { - name = "backgroundColor" - } - var val = fn(node, name) - return value === true ? parseFloat(val) || 0 : val - } else if (value === "") { //请除样式 - node.style[name] = "" - } else { //设置样式 - if (value == null || value !== value) { - return - } - if (isFinite(value) && !avalon.cssNumber[prop]) { - value += "px" - } - fn = cssHooks[prop + ":set"] || cssHooks["@:set"] - fn(node, name, value) - } - }, - /*遍历数组与对象,回调的第一个参数为索引或键名,第二个或元素或键值*/ - each: function(obj, fn) { - if (obj) { //排除null, undefined - var i = 0 - if (isArrayLike(obj)) { - for (var n = obj.length; i < n; i++) { - if (fn(i, obj[i]) === false) - break - } - } else { - for (i in obj) { - if (obj.hasOwnProperty(i) && fn(i, obj[i]) === false) { - break - } - } - } - } - }, - //收集元素的data-{{prefix}}-*属性,并转换为对象 - getWidgetData: function(elem, prefix) { - var raw = avalon(elem).data() - var result = {} - for (var i in raw) { - if (i.indexOf(prefix) === 0) { - result[i.replace(prefix, "").replace(/\w/, function(a) { - return a.toLowerCase() - })] = raw[i] - } - } - return result - }, - Array: { - /*只有当前数组不存在此元素时只添加它*/ - ensure: function(target, item) { - if (target.indexOf(item) === -1) { - return target.push(item) - } - }, - /*移除数组中指定位置的元素,返回布尔表示成功与否*/ - removeAt: function(target, index) { - return !!target.splice(index, 1).length - }, - /*移除数组中第一个匹配传参的那个元素,返回布尔表示成功与否*/ - remove: function(target, item) { - var index = target.indexOf(item) - if (~index) - return avalon.Array.removeAt(target, index) - return false - } - } - }) - - var bindingHandlers = avalon.bindingHandlers = {} - var bindingExecutors = avalon.bindingExecutors = {} - - /*判定是否类数组,如节点集合,纯数组,arguments与拥有非负整数的length属性的纯JS对象*/ - function isArrayLike(obj) { - if (!obj) - return false - var n = obj.length - if (n === (n >>> 0)) { //检测length属性是否为非负整数 - var type = serialize.call(obj).slice(8, -1) - if (/(?:regexp|string|function|window|global)$/i.test(type)) - return false - if (type === "Array") - return true - try { - if ({}.propertyIsEnumerable.call(obj, "length") === false) { //如果是原生对象 - return /^\s?function/.test(obj.item || obj.callee) - } - return true - } catch (e) { //IE的NodeList直接抛错 - return !obj.window //IE6-8 window - } - } - return false - } - - -// https://github.com/rsms/js-lru - var Cache = new function() {// jshint ignore:line - function LRU(maxLength) { - this.size = 0 - this.limit = maxLength - this.head = this.tail = void 0 - this._keymap = {} - } - - var p = LRU.prototype - - p.put = function(key, value) { - var entry = { - key: key, - value: value - } - this._keymap[key] = entry - if (this.tail) { - this.tail.newer = entry - entry.older = this.tail - } else { - this.head = entry - } - this.tail = entry - if (this.size === this.limit) { - this.shift() - } else { - this.size++ - } - return value - } - - p.shift = function() { - var entry = this.head - if (entry) { - this.head = this.head.newer - this.head.older = - entry.newer = - entry.older = - this._keymap[entry.key] = void 0 - } - } - p.get = function(key) { - var entry = this._keymap[key] - if (entry === void 0) - return - if (entry === this.tail) { - return entry.value - } - // HEAD--------------TAIL - // <.older .newer> - // <--- add direction -- - // A B C E - if (entry.newer) { - if (entry === this.head) { - this.head = entry.newer - } - entry.newer.older = entry.older // C <-- E. - } - if (entry.older) { - entry.older.newer = entry.newer // C. --> E - } - entry.newer = void 0 // D --x - entry.older = this.tail // D. --> E - if (this.tail) { - this.tail.newer = entry // E. <-- D - } - this.tail = entry - return entry.value - } - return LRU - }// jshint ignore:line - - /********************************************************************* - * javascript 底层补丁 * - **********************************************************************/ - if (!"司徒正美".trim) { - var rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g - String.prototype.trim = function () { - return this.replace(rtrim, "") - } - } - var hasDontEnumBug = !({ - 'toString': null - }).propertyIsEnumerable('toString'), - hasProtoEnumBug = (function () { - }).propertyIsEnumerable('prototype'), - dontEnums = [ - "toString", - "toLocaleString", - "valueOf", - "hasOwnProperty", - "isPrototypeOf", - "propertyIsEnumerable", - "constructor" - ], - dontEnumsLength = dontEnums.length; - if (!Object.keys) { - Object.keys = function (object) { //ecma262v5 15.2.3.14 - var theKeys = [] - var skipProto = hasProtoEnumBug && typeof object === "function" - if (typeof object === "string" || (object && object.callee)) { - for (var i = 0; i < object.length; ++i) { - theKeys.push(String(i)) - } - } else { - for (var name in object) { - if (!(skipProto && name === "prototype") && ohasOwn.call(object, name)) { - theKeys.push(String(name)) - } - } - } - - if (hasDontEnumBug) { - var ctor = object.constructor, - skipConstructor = ctor && ctor.prototype === object - for (var j = 0; j < dontEnumsLength; j++) { - var dontEnum = dontEnums[j] - if (!(skipConstructor && dontEnum === "constructor") && ohasOwn.call(object, dontEnum)) { - theKeys.push(dontEnum) - } - } - } - return theKeys - } - } - if (!Array.isArray) { - Array.isArray = function (a) { - return serialize.call(a) === "[object Array]" - } - } - - if (!noop.bind) { - Function.prototype.bind = function (scope) { - if (arguments.length < 2 && scope === void 0) - return this - var fn = this, - argv = arguments - return function () { - var args = [], - i - for (i = 1; i < argv.length; i++) - args.push(argv[i]) - for (i = 0; i < arguments.length; i++) - args.push(arguments[i]) - return fn.apply(scope, args) - } - } - } - - function iterator(vars, body, ret) { - var fun = 'for(var ' + vars + 'i=0,n = this.length; i < n; i++){' + body.replace('_', '((i in this) && fn.call(scope,this[i],i,this))') + '}' + ret - /* jshint ignore:start */ - return Function("fn,scope", fun) - /* jshint ignore:end */ - } - if (!rnative.test([].map)) { - avalon.mix(ap, { - //定位操作,返回数组中第一个等于给定参数的元素的索引值。 - indexOf: function (item, index) { - var n = this.length, - i = ~~index - if (i < 0) - i += n - for (; i < n; i++) - if (this[i] === item) - return i - return -1 - }, - //定位操作,同上,不过是从后遍历。 - lastIndexOf: function (item, index) { - var n = this.length, - i = index == null ? n - 1 : index - if (i < 0) - i = Math.max(0, n + i) - for (; i >= 0; i--) - if (this[i] === item) - return i - return -1 - }, - //迭代操作,将数组的元素挨个儿传入一个函数中执行。Prototype.js的对应名字为each。 - forEach: iterator("", '_', ""), - //迭代类 在数组中的每个项上运行一个函数,如果此函数的值为真,则此元素作为新数组的元素收集起来,并返回新数组 - filter: iterator('r=[],j=0,', 'if(_)r[j++]=this[i]', 'return r'), - //收集操作,将数组的元素挨个儿传入一个函数中执行,然后把它们的返回值组成一个新数组返回。Prototype.js的对应名字为collect。 - map: iterator('r=[],', 'r[i]=_', 'return r'), - //只要数组中有一个元素满足条件(放进给定函数返回true),那么它就返回true。Prototype.js的对应名字为any。 - some: iterator("", 'if(_)return true', 'return false'), - //只有数组中的元素都满足条件(放进给定函数返回true),它才返回true。Prototype.js的对应名字为all。 - every: iterator("", 'if(!_)return false', 'return true') - }) - } - /********************************************************************* - * DOM 底层补丁 * - **********************************************************************/ - - function fixContains(root, el) { - try { //IE6-8,游离于DOM树外的文本节点,访问parentNode有时会抛错 - while ((el = el.parentNode)) - if (el === root) - return true - return false - } catch (e) { - return false - } - } - avalon.contains = fixContains -//IE6-11的文档对象没有contains - if (!DOC.contains) { - DOC.contains = function (b) { - return fixContains(DOC, b) - } - } - - function outerHTML() { - return new XMLSerializer().serializeToString(this) - } - - if (window.SVGElement) { - //safari5+是把contains方法放在Element.prototype上而不是Node.prototype - if (!DOC.createTextNode("x").contains) { - Node.prototype.contains = function (arg) {//IE6-8没有Node对象 - return !!(this.compareDocumentPosition(arg) & 16) - } - } - var svgns = "http://www.w3.org/2000/svg" - var svg = DOC.createElementNS(svgns, "svg") - svg.innerHTML = '' - if (!rsvg.test(svg.firstChild)) { // #409 - function enumerateNode(node, targetNode) {// jshint ignore:line - if (node && node.childNodes) { - var nodes = node.childNodes - for (var i = 0, el; el = nodes[i++]; ) { - if (el.tagName) { - var svg = DOC.createElementNS(svgns, - el.tagName.toLowerCase()) - ap.forEach.call(el.attributes, function (attr) { - svg.setAttribute(attr.name, attr.value) //复制属性 - })// jshint ignore:line - // 递归处理子节点 - enumerateNode(el, svg) - targetNode.appendChild(svg) - } - } - } - } - Object.defineProperties(SVGElement.prototype, { - "outerHTML": {//IE9-11,firefox不支持SVG元素的innerHTML,outerHTML属性 - enumerable: true, - configurable: true, - get: outerHTML, - set: function (html) { - var tagName = this.tagName.toLowerCase(), - par = this.parentNode, - frag = avalon.parseHTML(html) - // 操作的svg,直接插入 - if (tagName === "svg") { - par.insertBefore(frag, this) - // svg节点的子节点类似 - } else { - var newFrag = DOC.createDocumentFragment() - enumerateNode(frag, newFrag) - par.insertBefore(newFrag, this) - } - par.removeChild(this) - } - }, - "innerHTML": { - enumerable: true, - configurable: true, - get: function () { - var s = this.outerHTML - var ropen = new RegExp("<" + this.nodeName + '\\b(?:(["\'])[^"]*?(\\1)|[^>])*>', "i") - var rclose = new RegExp("<\/" + this.nodeName + ">$", "i") - return s.replace(ropen, "").replace(rclose, "") - }, - set: function (html) { - if (avalon.clearHTML) { - avalon.clearHTML(this) - var frag = avalon.parseHTML(html) - enumerateNode(frag, this) - } - } - } - }) - } - } - if (!root.outerHTML && window.HTMLElement) { //firefox 到11时才有outerHTML - HTMLElement.prototype.__defineGetter__("outerHTML", outerHTML); - } - -//============================= event binding ======================= - var rmouseEvent = /^(?:mouse|contextmenu|drag)|click/ - function fixEvent(event) { - var ret = {} - for (var i in event) { - ret[i] = event[i] - } - var target = ret.target = event.srcElement - if (event.type.indexOf("key") === 0) { - ret.which = event.charCode != null ? event.charCode : event.keyCode - } else if (rmouseEvent.test(event.type)) { - var doc = target.ownerDocument || DOC - var box = doc.compatMode === "BackCompat" ? doc.body : doc.documentElement - ret.pageX = event.clientX + (box.scrollLeft >> 0) - (box.clientLeft >> 0) - ret.pageY = event.clientY + (box.scrollTop >> 0) - (box.clientTop >> 0) - ret.wheelDeltaY = ret.wheelDelta - ret.wheelDeltaX = 0 - } - ret.timeStamp = new Date() - 0 - ret.originalEvent = event - ret.preventDefault = function () { //阻止默认行为 - event.returnValue = false - } - ret.stopPropagation = function () { //阻止事件在DOM树中的传播 - event.cancelBubble = true - } - return ret - } - - var eventHooks = avalon.eventHooks -//针对firefox, chrome修正mouseenter, mouseleave - if (!("onmouseenter" in root)) { - avalon.each({ - mouseenter: "mouseover", - mouseleave: "mouseout" - }, function (origType, fixType) { - eventHooks[origType] = { - type: fixType, - deel: function (elem, _, fn) { - return function (e) { - var t = e.relatedTarget - if (!t || (t !== elem && !(elem.compareDocumentPosition(t) & 16))) { - delete e.type - e.type = origType - return fn.call(elem, e) - } - } - } - } - }) - } -//针对IE9+, w3c修正animationend - avalon.each({ - AnimationEvent: "animationend", - WebKitAnimationEvent: "webkitAnimationEnd" - }, function (construct, fixType) { - if (window[construct] && !eventHooks.animationend) { - eventHooks.animationend = { - type: fixType - } - } - }) -//针对IE6-8修正input - if (!("oninput" in DOC.createElement("input"))) { - eventHooks.input = { - type: "propertychange", - deel: function (elem, _, fn) { - return function (e) { - if (e.propertyName === "value") { - e.type = "input" - return fn.call(elem, e) - } - } - } - } - } - if (DOC.onmousewheel === void 0) { - /* IE6-11 chrome mousewheel wheelDetla 下 -120 上 120 - firefox DOMMouseScroll detail 下3 上-3 - firefox wheel detlaY 下3 上-3 - IE9-11 wheel deltaY 下40 上-40 - chrome wheel deltaY 下100 上-100 */ - var fixWheelType = DOC.onwheel !== void 0 ? "wheel" : "DOMMouseScroll" - var fixWheelDelta = fixWheelType === "wheel" ? "deltaY" : "detail" - eventHooks.mousewheel = { - type: fixWheelType, - deel: function (elem, _, fn) { - return function (e) { - e.wheelDeltaY = e.wheelDelta = e[fixWheelDelta] > 0 ? -120 : 120 - e.wheelDeltaX = 0 - if (Object.defineProperty) { - Object.defineProperty(e, "type", { - value: "mousewheel" - }) - } - fn.call(elem, e) - } - } - } - } - - - /********************************************************************* - * 配置系统 * - **********************************************************************/ - - function kernel(settings) { - for (var p in settings) { - if (!ohasOwn.call(settings, p)) - continue - var val = settings[p] - if (typeof kernel.plugins[p] === "function") { - kernel.plugins[p](val) - } else if (typeof kernel[p] === "object") { - avalon.mix(kernel[p], val) - } else { - kernel[p] = val - } - } - return this - } - var openTag, closeTag, rexpr, rexprg, rbind, rregexp = /[-.*+?^${}()|[\]\/\\]/g - - function escapeRegExp(target) { - //http://stevenlevithan.com/regex/xregexp/ - //将字符串安全格式化为正则表达式的源码 - return (target + "").replace(rregexp, "\\$&") - } - - var plugins = { - loader: function (builtin) { - var flag = innerRequire && builtin - window.require = flag ? innerRequire : otherRequire - window.define = flag ? innerRequire.define : otherDefine - }, - interpolate: function (array) { - openTag = array[0] - closeTag = array[1] - if (openTag === closeTag) { - throw new SyntaxError("openTag!==closeTag") - } else if (array + "" === "") { - kernel.commentInterpolate = true - } else { - var test = openTag + "test" + closeTag - cinerator.innerHTML = test - if (cinerator.innerHTML !== test && cinerator.innerHTML.indexOf("<") > -1) { - throw new SyntaxError("此定界符不合法") - } - cinerator.innerHTML = "" - } - var o = escapeRegExp(openTag), - c = escapeRegExp(closeTag) - rexpr = new RegExp(o + "(.*?)" + c) - rexprg = new RegExp(o + "(.*?)" + c, "g") - rbind = new RegExp(o + ".*?" + c + "|\\sms-") - } - } - - kernel.debug = true - kernel.plugins = plugins - kernel.plugins['interpolate'](["{{", "}}"]) - kernel.paths = {} - kernel.shim = {} - kernel.maxRepeatSize = 100 - avalon.config = kernel - var ravalon = /(\w+)\[(avalonctrl)="(\S+)"\]/ - var findNodes = DOC.querySelectorAll ? function(str) { - return DOC.querySelectorAll(str) - } : function(str) { - var match = str.match(ravalon) - var all = DOC.getElementsByTagName(match[1]) - var nodes = [] - for (var i = 0, el; el = all[i++]; ) { - if (el.getAttribute(match[2]) === match[3]) { - nodes.push(el) - } - } - return nodes - } - /********************************************************************* - * 事件总线 * - **********************************************************************/ - var EventBus = { - $watch: function (type, callback) { - if (typeof callback === "function") { - var callbacks = this.$events[type] - if (callbacks) { - callbacks.push(callback) - } else { - this.$events[type] = [callback] - } - } else { //重新开始监听此VM的第一重简单属性的变动 - this.$events = this.$watch.backup - } - return this - }, - $unwatch: function (type, callback) { - var n = arguments.length - if (n === 0) { //让此VM的所有$watch回调无效化 - this.$watch.backup = this.$events - this.$events = {} - } else if (n === 1) { - this.$events[type] = [] - } else { - var callbacks = this.$events[type] || [] - var i = callbacks.length - while (~--i < 0) { - if (callbacks[i] === callback) { - return callbacks.splice(i, 1) - } - } - } - return this - }, - $fire: function (type) { - var special, i, v, callback - if (/^(\w+)!(\S+)$/.test(type)) { - special = RegExp.$1 - type = RegExp.$2 - } - var events = this.$events - if (!events) - return - var args = aslice.call(arguments, 1) - var detail = [type].concat(args) - if (special === "all") { - for (i in avalon.vmodels) { - v = avalon.vmodels[i] - if (v !== this) { - v.$fire.apply(v, detail) - } - } - } else if (special === "up" || special === "down") { - var elements = events.expr ? findNodes(events.expr) : [] - if (elements.length === 0) - return - for (i in avalon.vmodels) { - v = avalon.vmodels[i] - if (v !== this) { - if (v.$events.expr) { - var eventNodes = findNodes(v.$events.expr) - if (eventNodes.length === 0) { - continue - } - //循环两个vmodel中的节点,查找匹配(向上匹配或者向下匹配)的节点并设置标识 - /* jshint ignore:start */ - ap.forEach.call(eventNodes, function (node) { - ap.forEach.call(elements, function (element) { - var ok = special === "down" ? element.contains(node) : //向下捕获 - node.contains(element) //向上冒泡 - if (ok) { - node._avalon = v //符合条件的加一个标识 - } - }); - }) - /* jshint ignore:end */ - } - } - } - var nodes = DOC.getElementsByTagName("*") //实现节点排序 - var alls = [] - ap.forEach.call(nodes, function (el) { - if (el._avalon) { - alls.push(el._avalon) - el._avalon = "" - el.removeAttribute("_avalon") - } - }) - if (special === "up") { - alls.reverse() - } - for (i = 0; callback = alls[i++]; ) { - if (callback.$fire.apply(callback, detail) === false) { - break - } - } - } else { - var callbacks = events[type] || [] - var all = events.$all || [] - for (i = 0; callback = callbacks[i++]; ) { - if (isFunction(callback)) - callback.apply(this, args) - } - for (i = 0; callback = all[i++]; ) { - if (isFunction(callback)) - callback.apply(this, arguments) - } - } - } - } - - /********************************************************************* - * modelFactory * - **********************************************************************/ -//avalon最核心的方法的两个方法之一(另一个是avalon.scan),返回一个ViewModel(VM) - var VMODELS = avalon.vmodels = {} //所有vmodel都储存在这里 - avalon.define = function (id, factory) { - var $id = id.$id || id - if (!$id) { - log("warning: vm必须指定$id") - } - if (VMODELS[$id]) { - log("warning: " + $id + " 已经存在于avalon.vmodels中") - } - if (typeof id === "object") { - var model = modelFactory(id) - } else { - var scope = { - $watch: noop - } - factory(scope) //得到所有定义 - - model = modelFactory(scope) //偷天换日,将scope换为model - stopRepeatAssign = true - factory(model) - stopRepeatAssign = false - } - model.$id = $id - return VMODELS[$id] = model - } - -//一些不需要被监听的属性 - var $$skipArray = String("$id,$watch,$unwatch,$fire,$events,$model,$skipArray,$proxy,$compute").match(rword) - var defineProperty = Object.defineProperty - var canHideOwn = true -//如果浏览器不支持ecma262v5的Object.defineProperties或者存在BUG,比如IE8 -//标准浏览器使用__defineGetter__, __defineSetter__实现 - try { - defineProperty({}, "_", { - value: "x" - }) - var defineProperties = Object.defineProperties - } catch (e) { - canHideOwn = false - } - - function modelFactory(source, $special, $model) { - if (Array.isArray(source)) { - var arr = source.concat() - source.length = 0 - var collection = arrayFactory(source) - collection.pushArray(arr) - return collection - } - //0 null undefined || Node || VModel(fix IE6-8 createWithProxy $val: val引发的BUG) - if (!source || source.nodeType > 0 || (source.$id && source.$events)) { - return source - } - var $skipArray = Array.isArray(source.$skipArray) ? source.$skipArray : [] - $skipArray.$special = $special || {} //强制要监听的属性 - var $vmodel = {} //要返回的对象, 它在IE6-8下可能被偷龙转凤 - $model = $model || {} //vmodels.$model属性 - var $events = {} //vmodel.$events属性 - var accessors = {} //监控属性 - var computed = [] - $$skipArray.forEach(function (name) { - delete source[name] - }) - - for (var i in source) { - (function (name, val, accessor) { - $model[name] = val - if (!isObservable(name, val, $skipArray)) { - return //过滤所有非监控属性 - } - //总共产生三种accessor - $events[name] = [] - var valueType = avalon.type(val) - //总共产生三种accessor - if (valueType === "object" && isFunction(val.get) && Object.keys(val).length <= 2) { - accessor = makeComputedAccessor(name, val) - computed.push(accessor) - } else if (rcomplexType.test(valueType)) { - accessor = makeComplexAccessor(name, val, valueType, $events[name]) - } else { - accessor = makeSimpleAccessor(name, val) - } - accessors[name] = accessor - })(i, source[i])// jshint ignore:line - } - - $vmodel = defineProperties($vmodel, descriptorFactory(accessors), source) //生成一个空的ViewModel - for (var name in source) { - if (!accessors[name]) { - $vmodel[name] = source[name] - } - } - //添加$id, $model, $events, $watch, $unwatch, $fire - $vmodel.$id = generateID() - $vmodel.$model = $model - $vmodel.$events = $events - for (i in EventBus) { - var fn = EventBus[i] - if (!W3C) { //在IE6-8下,VB对象的方法里的this并不指向自身,需要用bind处理一下 - fn = fn.bind($vmodel) - } - $vmodel[i] = fn - } - if (canHideOwn) { - Object.defineProperty($vmodel, "hasOwnProperty", { - value: function (name) { - return name in this.$model - }, - writable: false, - enumerable: false, - configurable: true - }) - - } else { - /* jshint ignore:start */ - $vmodel.hasOwnProperty = function (name) { - return name in $vmodel.$model - } - /* jshint ignore:end */ - } - - $vmodel.$compute = function () { - computed.forEach(function (accessor) { - dependencyDetection.begin({ - callback: function (vm, dependency) {//dependency为一个accessor - var name = dependency._name - if (dependency !== accessor) { - var list = vm.$events[name] - accessor.vm = $vmodel - injectDependency(list, accessor.digest) - } - } - }) - try { - accessor.get.call($vmodel) - } finally { - dependencyDetection.end() - } - }) - } - $vmodel.$compute() - return $vmodel - } -//创建一个简单访问器 - function makeSimpleAccessor(name, value) { - function accessor(value) { - var oldValue = accessor._value - if (arguments.length > 0) { - if (!stopRepeatAssign && !isEqual(value, oldValue)) { - accessor.updateValue(this, value) - accessor.notify(this, value, oldValue) - } - return this - } else { - dependencyDetection.collectDependency(this, accessor) - return oldValue - } - } - accessorFactory(accessor, name) - accessor._value = value - return accessor; - } - -//创建一个计算访问器 - function makeComputedAccessor(name, options) { - options.set = options.set || noop - function accessor(value) {//计算属性 - var oldValue = accessor._value - var init = "_value" in accessor - if (arguments.length > 0) { - if (stopRepeatAssign) { - return this - } - accessor.set.call(this, value) - return this - } else { - //将依赖于自己的高层访问器或视图刷新函数(以绑定对象形式)放到自己的订阅数组中 - value = accessor.get.call(this) - if (oldValue !== value) { - accessor.updateValue(this, value) - init && accessor.notify(this, value, oldValue) //触发$watch回调 - } - //将自己注入到低层访问器的订阅数组中 - return value - } - } - accessor.set = options.set || noop - accessor.get = options.get - accessorFactory(accessor, name) - var id - accessor.digest = function () { - accessor.updateValue = globalUpdateModelValue - accessor.notify = noop - accessor.call(accessor.vm) - clearTimeout(id)//如果计算属性存在多个依赖项,那么等它们都更新了才更新视图 - id = setTimeout(function () { - accessorFactory(accessor, accessor._name) - accessor.call(accessor.vm) - }) - } - return accessor - } - -//创建一个复杂访问器 - function makeComplexAccessor(name, initValue, valueType, list) { - function accessor(value) { - var oldValue = accessor._value - var son = accessor._vmodel - if (arguments.length > 0) { - if (stopRepeatAssign) { - return this - } - if (valueType === "array") { - var old = son._ - son._ = [] - son.clear() - son._ = old - son.pushArray(value) - } else if (valueType === "object") { - var $proxy = son.$proxy - var observes = this.$events[name] || [] - son = accessor._vmodel = modelFactory(value) - son.$events[subscribers] = observes - son.$proxy = $proxy - if (observes.length) { - observes.forEach(function (el) { - var fn = bindingHandlers[el.type] - if (fn) { //#753 - el.rollback && el.rollback() //还原 ms-with ms-on - fn(el, el.vmodels) - } - }) - } - } - accessor.updateValue(this, son.$model) - accessor.notify(this, this._value, oldValue) - return this - } else { - dependencyDetection.collectDependency(this, accessor) - return son - } - } - accessorFactory(accessor, name) - var son = accessor._vmodel = modelFactory(initValue) - son.$events[subscribers] = list - return accessor - } - - function globalUpdateValue(vmodel, value) { - vmodel.$model[this._name] = this._value = value - } - function globalUpdateModelValue(vmodel, value) { - vmodel.$model[this._name] = value - } - function globalNotify(vmodel, value, oldValue) { - var name = this._name - var array = vmodel.$events[name] //刷新值 - if (array) { - fireDependencies(array) //同步视图 - EventBus.$fire.call(vmodel, name, value, oldValue) //触发$watch回调 - } - } - - function accessorFactory(accessor, name) { - accessor._name = name - //同时更新_value与model - accessor.updateValue = globalUpdateValue - accessor.notify = globalNotify - } - -//比较两个值是否相等 - var isEqual = Object.is || function (v1, v2) { - if (v1 === 0 && v2 === 0) { - return 1 / v1 === 1 / v2 - } else if (v1 !== v1) { - return v2 !== v2 - } else { - return v1 === v2 - } - } - - function isObservable(name, value, $skipArray) { - if (isFunction(value) || value && value.nodeType) { - return false - } - if ($skipArray.indexOf(name) !== -1) { - return false - } - var $special = $skipArray.$special - if (name && name.charAt(0) === "$" && !$special[name]) { - return false - } - return true - } - - var descriptorFactory = W3C ? function (obj) { - var descriptors = {} - for (var i in obj) { - descriptors[i] = { - get: obj[i], - set: obj[i], - enumerable: true, - configurable: true - } - } - return descriptors - } : function (a) { - return a - } - - -//===================修复浏览器对Object.defineProperties的支持================= - if (!canHideOwn) { - if ("__defineGetter__" in avalon) { - defineProperty = function (obj, prop, desc) { - if ('value' in desc) { - obj[prop] = desc.value - } - if ("get" in desc) { - obj.__defineGetter__(prop, desc.get) - } - if ('set' in desc) { - obj.__defineSetter__(prop, desc.set) - } - return obj - } - defineProperties = function (obj, descs) { - for (var prop in descs) { - if (descs.hasOwnProperty(prop)) { - defineProperty(obj, prop, descs[prop]) - } - } - return obj - } - } - if (IEVersion) { - var VBClassPool = {} - window.execScript([// jshint ignore:line - "Function parseVB(code)", - "\tExecuteGlobal(code)", - "End Function" //转换一段文本为VB代码 - ].join("\n"), "VBScript") - function VBMediator(instance, accessors, name, value) {// jshint ignore:line - var accessor = accessors[name] - if (arguments.length === 4) { - accessor.call(instance, value) - } else { - return accessor.call(instance) - } - } - defineProperties = function (name, accessors, properties) { - // jshint ignore:line - var buffer = [] - buffer.push( - "\r\n\tPrivate [__data__], [__proxy__]", - "\tPublic Default Function [__const__](d, p)", - "\t\tSet [__data__] = d: set [__proxy__] = p", - "\t\tSet [__const__] = Me", //链式调用 - "\tEnd Function") - //添加普通属性,因为VBScript对象不能像JS那样随意增删属性,必须在这里预先定义好 - for (name in properties) { - if (!accessors.hasOwnProperty(name)) { - buffer.push("\tPublic [" + name + "]") - } - } - $$skipArray.forEach(function (name) { - if (!accessors.hasOwnProperty(name)) { - buffer.push("\tPublic [" + name + "]") - } - }) - buffer.push("\tPublic [" + 'hasOwnProperty' + "]") - //添加访问器属性 - for (name in accessors) { - buffer.push( - //由于不知对方会传入什么,因此set, let都用上 - "\tPublic Property Let [" + name + "](val" + expose + ")", //setter - "\t\tCall [__proxy__](Me,[__data__], \"" + name + "\", val" + expose + ")", - "\tEnd Property", - "\tPublic Property Set [" + name + "](val" + expose + ")", //setter - "\t\tCall [__proxy__](Me,[__data__], \"" + name + "\", val" + expose + ")", - "\tEnd Property", - "\tPublic Property Get [" + name + "]", //getter - "\tOn Error Resume Next", //必须优先使用set语句,否则它会误将数组当字符串返回 - "\t\tSet[" + name + "] = [__proxy__](Me,[__data__],\"" + name + "\")", - "\tIf Err.Number <> 0 Then", - "\t\t[" + name + "] = [__proxy__](Me,[__data__],\"" + name + "\")", - "\tEnd If", - "\tOn Error Goto 0", - "\tEnd Property") - - } - - buffer.push("End Class") - var body = buffer.join("\r\n") - var className =VBClassPool[body] - if (!className) { - className = generateID("VBClass") - window.parseVB("Class " + className + body) - window.parseVB([ - "Function " + className + "Factory(a, b)", //创建实例并传入两个关键的参数 - "\tDim o", - "\tSet o = (New " + className + ")(a, b)", - "\tSet " + className + "Factory = o", - "End Function" - ].join("\r\n")) - VBClassPool[body] = className - } - var ret = window[className + "Factory"](accessors, VBMediator) //得到其产品 - return ret //得到其产品 - } - } - } - - /********************************************************************* - * 监控数组(与ms-each, ms-repeat配合使用) * - **********************************************************************/ - - function arrayFactory(model) { - var array = [] - array.$id = generateID() - array.$model = model //数据模型 - array.$events = {} - array.$events[subscribers] = [] - array._ = modelFactory({ - length: model.length - }) - array._.$watch("length", function (a, b) { - array.$fire("length", a, b) - }) - for (var i in EventBus) { - array[i] = EventBus[i] - } - array.$map = { - el: 1 - } - array.$proxy = [] - avalon.mix(array, arrayPrototype) - return array - } - - function mutateArray(method, pos, n, index, method2, pos2, n2) { - var oldLen = this.length, loop = 2 - while (--loop) { - switch (method) { - case "add": - /* jshint ignore:start */ - var m = pos + n - var array = this.$model.slice(pos, m).map(function (el) { - if (rcomplexType.test(avalon.type(el))) {//转换为VM - return el.$id ? el : modelFactory(el, 0, el) - } else { - return el - } - }) - _splice.apply(this, [pos, 0].concat(array)) - /* jshint ignore:end */ - for (var i = pos; i < m; i++) {//生成代理VM - var proxy = eachProxyAgent(i, this) - this.$proxy.splice(i, 0, proxy) - } - this._fire("add", pos, n) - break - case "del": - var ret = this._splice(pos, n) - var removed = this.$proxy.splice(pos, n) //回收代理VM - eachProxyRecycler(removed, "each") - this._fire("del", pos, n) - break - } - if (method2) { - method = method2 - pos = pos2 - n = n2 - loop = 2 - method2 = 0 - } - } - resetIndex(this.$proxy, index) - if (this.length !== oldLen) { - this._.length = this.length - } - return ret - } - - var _splice = ap.splice - var arrayPrototype = { - _splice: _splice, - _fire: function (method, a, b) { - fireDependencies(this.$events[subscribers], method, a, b) - }, - size: function () { //取得数组长度,这个函数可以同步视图,length不能 - return this._.length - }, - pushArray: function (array) { - var m = array.length, n = this.length - if (m) { - ap.push.apply(this.$model, array) - mutateArray.call(this, "add", n, m, Math.max(0, n - 1)) - } - return m + n - }, - push: function () { - //http://jsperf.com/closure-with-arguments - var array = [] - var i, n = arguments.length - for (i = 0; i < n; i++) { - array[i] = arguments[i] - } - return this.pushArray(array) - }, - unshift: function () { - var m = arguments.length, n = this.length - if (m) { - ap.unshift.apply(this.$model, arguments) - mutateArray.call(this, "add", 0, m, 0) - } - return m + n //IE67的unshift不会返回长度 - }, - shift: function () { - if (this.length) { - var el = this.$model.shift() - mutateArray.call(this, "del", 0, 1, 0) - return el //返回被移除的元素 - } - }, - pop: function () { - var n = this.length - if (n) { - var el = this.$model.pop() - mutateArray.call(this, "del", n - 1, 1, Math.max(0, n - 2)) - return el //返回被移除的元素 - } - }, - splice: function (start) { - var m = arguments.length, args = [], change - var removed = _splice.apply(this.$model, arguments) - if (removed.length) { //如果用户删掉了元素 - args.push("del", start, removed.length, 0) - change = true - } - if (m > 2) { //如果用户添加了元素 - if (change) { - args.splice(3, 1, 0, "add", start, m - 2) - } else { - args.push("add", start, m - 2, 0) - } - change = true - } - if (change) { //返回被移除的元素 - return mutateArray.apply(this, args) - } else { - return [] - } - }, - contains: function (el) { //判定是否包含 - return this.indexOf(el) !== -1 - }, - remove: function (el) { //移除第一个等于给定值的元素 - return this.removeAt(this.indexOf(el)) - }, - removeAt: function (index) { //移除指定索引上的元素 - if (index >= 0) { - this.$model.splice(index, 1) - return mutateArray.call(this, "del", index, 1, 0) - } - return [] - }, - clear: function () { - eachProxyRecycler(this.$proxy, "each") - this.$model.length = this.$proxy.length = this.length = this._.length = 0 //清空数组 - this._fire("clear", 0) - return this - }, - removeAll: function (all) { //移除N个元素 - if (Array.isArray(all)) { - for (var i = this.length - 1; i >= 0; i--) { - if (all.indexOf(this[i]) !== -1) { - this.removeAt(i) - } - } - } else if (typeof all === "function") { - for (i = this.length - 1; i >= 0; i--) { - if (all(this[i], i)) { - this.removeAt(i) - } - } - } else { - this.clear() - } - }, - ensure: function (el) { - if (!this.contains(el)) { //只有不存在才push - this.push(el) - } - return this - }, - set: function (index, val) { - if (index >= 0) { - var valueType = avalon.type(val) - if (val && val.$model) { - val = val.$model - } - var target = this[index] - if (valueType === "object") { - for (var i in val) { - if (target.hasOwnProperty(i)) { - target[i] = val[i] - } - } - } else if (valueType === "array") { - target.clear().push.apply(target, val) - } else if (target !== val) { - this[index] = val - this.$model[index] = val - var proxy = this.$proxy[index] - if (proxy) { - fireDependencies(proxy.$events.$index) - } - } - } - return this - } - } -//相当于原来bindingExecutors.repeat 的index分支 - function resetIndex(array, pos) { - var last = array.length - 1 - for (var el; el = array[pos]; pos++) { - el.$index = pos - el.$first = pos === 0 - el.$last = pos === last - } - } - - function sortByIndex(array, indexes) { - var map = {}; - for (var i = 0, n = indexes.length; i < n; i++) { - map[i] = array[i] // preserve - var j = indexes[i] - if (j in map) { - array[i] = map[j] - delete map[j] - } else { - array[i] = array[j] - } - } - } - - "sort,reverse".replace(rword, function (method) { - arrayPrototype[method] = function () { - var newArray = this.$model//这是要排序的新数组 - var oldArray = newArray.concat() //保持原来状态的旧数组 - var mask = Math.random() - var indexes = [] - var hasSort - ap[method].apply(newArray, arguments) //排序 - for (var i = 0, n = oldArray.length; i < n; i++) { - var neo = newArray[i] - var old = oldArray[i] - if (isEqual(neo, old)) { - indexes.push(i) - } else { - var index = oldArray.indexOf(neo) - indexes.push(index)//得到新数组的每个元素在旧数组对应的位置 - oldArray[index] = mask //屏蔽已经找过的元素 - hasSort = true - } - } - if (hasSort) { - sortByIndex(this, indexes) - sortByIndex(this.$proxy, indexes) - this._fire("move", indexes) - resetIndex(this.$proxy, 0) - } - return this - } - }) - - var eachProxyPool = [] - - function eachProxyFactory() { - var source = { - $index: NaN, - $first: NaN, - $last: NaN, - $map: {}, - $host: {}, - $outer: {}, - $remove: avalon.noop, - el: { - get: function () { - //avalon1.4.4中,计算属性的订阅数组不再添加绑定对象 - return this.$host[this.$index] - }, - set: function (val) { - this.$host.set(this.$index, val) - } - } - } - - var second = { - $last: 1, - $first: 1, - $index: 1 - } - var proxy = modelFactory(source, second) - proxy.$id = generateID("$proxy$each") - return proxy - } - - function eachProxyAgent(index, host) { - var proxy = eachProxyPool.shift() - if (!proxy) { - proxy = eachProxyFactory( ) - }else{ - proxy.$compute() - } - var last = host.length - 1 - proxy.$host = host - proxy.$index = index - proxy.$first = index === 0 - proxy.$last = index === last - proxy.$map = host.$map - proxy.$remove = function () { - return host.removeAt(proxy.$index) - } - return proxy - } - - /********************************************************************* - * 依赖调度系统 * - **********************************************************************/ -//检测两个对象间的依赖关系 - var dependencyDetection = (function () { - var outerFrames = [] - var currentFrame - return { - begin: function (accessorObject) { - //accessorObject为一个拥有callback的对象 - outerFrames.push(currentFrame) - currentFrame = accessorObject - }, - end: function () { - currentFrame = outerFrames.pop() - }, - collectDependency: function (vmodel, accessor) { - if (currentFrame) { - //被dependencyDetection.begin调用 - currentFrame.callback(vmodel, accessor); - } - } - }; - })() -//将绑定对象注入到其依赖项的订阅数组中 - var ronduplex = /^(duplex|on)$/ - avalon.injectBinding = function (data) { - var fn = data.evaluator - if (fn) { //如果是求值函数 - dependencyDetection.begin({ - callback: function (vmodel, dependency) { - injectDependency(vmodel.$events[dependency._name], data) - } - }) - try { - var c = ronduplex.test(data.type) ? data : fn.apply(0, data.args) - data.handler(c, data.element, data) - } catch (e) { - //log("warning:exception throwed in [avalon.injectBinding] " + e) - delete data.evaluator - var node = data.element - if (node.nodeType === 3) { - var parent = node.parentNode - if (kernel.commentInterpolate) { - parent.replaceChild(DOC.createComment(data.value), node) - } else { - node.data = openTag + data.value + closeTag - } - } - } finally { - dependencyDetection.end() - } - } - } - -//将依赖项(比它高层的访问器或构建视图刷新函数的绑定对象)注入到订阅者数组 - function injectDependency(list, data) { - data = data || Registry[expose] - if (list && data && avalon.Array.ensure(list, data) && data.element) { - injectDisposeQueue(data, list) - } - } - -//通知依赖于这个访问器的订阅者更新自身 - function fireDependencies(list) { - if (list && list.length) { - if (new Date() - beginTime > 444 && typeof list[0] === "object") { - rejectDisposeQueue() - } - var args = aslice.call(arguments, 1) - for (var i = list.length, fn; fn = list[--i]; ) { - var el = fn.element - if (el && el.parentNode) { - try { - if (fn.$repeat) { - fn.handler.apply(fn, args) //处理监控数组的方法 - } else if (fn.type !== "on") { //事件绑定只能由用户触发,不能由程序触发 - var fun = fn.evaluator || noop - fn.handler(fun.apply(0, fn.args || []), el, fn) - - } - } catch (e) { - } - } - } - } - } - /********************************************************************* - * 定时GC回收机制 * - **********************************************************************/ - var disposeCount = 0 - var disposeQueue = avalon.$$subscribers = [] - var beginTime = new Date() - var oldInfo = {} - function getUid(obj) { //IE9+,标准浏览器 - return obj.uniqueNumber || (obj.uniqueNumber = ++disposeCount) - } - -//添加到回收列队中 - function injectDisposeQueue(data, list) { - var elem = data.element - if (!data.uuid) { - if (elem.nodeType !== 1) { - data.uuid = data.type + (data.pos || 0) + "-" + getUid(elem.parentNode) - } else { - data.uuid = data.name + "-" + getUid(elem) - } - } - var lists = data.lists || (data.lists = []) - avalon.Array.ensure(lists, list) - list.$uuid = list.$uuid || generateID() - if (!disposeQueue[data.uuid]) { - disposeQueue[data.uuid] = 1 - disposeQueue.push(data) - } - } - - function rejectDisposeQueue(data) { - if(avalon.optimize) - return - var i = disposeQueue.length - var n = i - var allTypes = [] - var iffishTypes = {} - var newInfo = {} - //对页面上所有绑定对象进行分门别类, 只检测个数发生变化的类型 - while (data = disposeQueue[--i]) { - var type = data.type - if (newInfo[type]) { - newInfo[type]++ - } else { - newInfo[type] = 1 - allTypes.push(type) - } - } - var diff = false - allTypes.forEach(function (type) { - if (oldInfo[type] !== newInfo[type]) { - iffishTypes[type] = 1 - diff = true - } - }) - i = n - if (diff) { - while (data = disposeQueue[--i]) { - if (!data.element) - continue - if (iffishTypes[data.type] && shouldDispose(data.element)) { //如果它没有在DOM树 - disposeQueue.splice(i, 1) - delete disposeQueue[data.uuid] - var lists = data.lists - for (var k = 0, list; list = lists[k++]; ) { - avalon.Array.remove(lists, list) - avalon.Array.remove(list, data) - } - disposeData(data) - } - } - } - oldInfo = newInfo - beginTime = new Date() - } - - function disposeData(data) { - data.element = null - data.rollback && data.rollback() - for (var key in data) { - data[key] = null - } - } - - function shouldDispose(el) { - try {//IE下,如果文本节点脱离DOM树,访问parentNode会报错 - if (!el.parentNode) { - return true - } - } catch (e) { - return true - } - - return el.msRetain ? 0 : (el.nodeType === 1 ? !root.contains(el) : !avalon.contains(root, el)) - } - - /************************************************************************ - * HTML处理(parseHTML, innerHTML, clearHTML) * - ************************************************************************/ -// We have to close these tags to support XHTML - var tagHooks = { - area: [1, "", ""], - param: [1, "", ""], - col: [2, "", "
"], - legend: [1, "

", "
"], - option: [1, ""], - thead: [1, "", "
"], - tr: [2, "", "
"], - td: [3, "", "
"], - g: [1, '', ''], - //IE6-8在用innerHTML生成节点时,不能直接创建no-scope元素与HTML5的新标签 - _default: W3C ? [0, "", ""] : [1, "X
", "
"] //div可以不用闭合 - } - tagHooks.th = tagHooks.td - tagHooks.optgroup = tagHooks.option - tagHooks.tbody = tagHooks.tfoot = tagHooks.colgroup = tagHooks.caption = tagHooks.thead - String("circle,defs,ellipse,image,line,path,polygon,polyline,rect,symbol,text,use").replace(rword, function (tag) { - tagHooks[tag] = tagHooks.g //处理SVG - }) - var rtagName = /<([\w:]+)/ //取得其tagName - var rxhtml = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig - var rcreate = W3C ? /[^\d\D]/ : /(<(?:script|link|style|meta|noscript))/ig - var scriptTypes = oneObject(["", "text/javascript", "text/ecmascript", "application/ecmascript", "application/javascript"]) - var rnest = /<(?:tb|td|tf|th|tr|col|opt|leg|cap|area)/ //需要处理套嵌关系的标签 - var script = DOC.createElement("script") - var rhtml = /<|&#?\w+;/ - avalon.parseHTML = function (html) { - var fragment = avalonFragment.cloneNode(false) - if (typeof html !== "string") { - return fragment - } - if (!rhtml.test(html)) { - fragment.appendChild(DOC.createTextNode(html)) - return fragment - } - html = html.replace(rxhtml, "<$1>").trim() - var tag = (rtagName.exec(html) || ["", ""])[1].toLowerCase(), - //取得其标签名 - wrap = tagHooks[tag] || tagHooks._default, - wrapper = cinerator, - firstChild, neo - if (!W3C) { //fix IE - html = html.replace(rcreate, "
$1") //在link style script等标签之前添加一个补丁 - } - wrapper.innerHTML = wrap[1] + html + wrap[2] - var els = wrapper.getElementsByTagName("script") - if (els.length) { //使用innerHTML生成的script节点不会发出请求与执行text属性 - for (var i = 0, el; el = els[i++]; ) { - if (scriptTypes[el.type]) { - //以偷龙转凤方式恢复执行脚本功能 - neo = script.cloneNode(false) //FF不能省略参数 - ap.forEach.call(el.attributes, function (attr) { - if (attr && attr.specified) { - neo[attr.name] = attr.value //复制其属性 - neo.setAttribute(attr.name, attr.value) - } - }) // jshint ignore:line - neo.text = el.text - el.parentNode.replaceChild(neo, el) //替换节点 - } - } - } - if (!W3C) { //fix IE - var target = wrap[1] === "X
" ? wrapper.lastChild.firstChild : wrapper.lastChild - if (target && target.tagName === "TABLE" && tag !== "tbody") { - //IE6-7处理 --> , - // --> , - // -->
- for (els = target.childNodes, i = 0; el = els[i++]; ) { - if (el.tagName === "TBODY" && !el.innerHTML) { - target.removeChild(el) - break - } - } - } - els = wrapper.getElementsByTagName("br") - var n = els.length - while (el = els[--n]) { - if (el.className === "msNoScope") { - el.parentNode.removeChild(el) - } - } - for (els = wrapper.all, i = 0; el = els[i++]; ) { //fix VML - if (isVML(el)) { - fixVML(el) - } - } - } - //移除我们为了符合套嵌关系而添加的标签 - for (i = wrap[0]; i--; wrapper = wrapper.lastChild) { - } - while (firstChild = wrapper.firstChild) { // 将wrapper上的节点转移到文档碎片上! - fragment.appendChild(firstChild) - } - return fragment - } - - function isVML(src) { - var nodeName = src.nodeName - return nodeName.toLowerCase() === nodeName && src.scopeName && src.outerText === "" - } - - function fixVML(node) { - if (node.currentStyle.behavior !== "url(#default#VML)") { - node.style.behavior = "url(#default#VML)" - node.style.display = "inline-block" - node.style.zoom = 1 //hasLayout - } - } - avalon.innerHTML = function (node, html) { - if (!W3C && (!rcreate.test(html) && !rnest.test(html))) { - try { - node.innerHTML = html - return - } catch (e) { - } - } - var a = this.parseHTML(html) - this.clearHTML(node).appendChild(a) - } - avalon.clearHTML = function (node) { - node.textContent = "" - while (node.firstChild) { - node.removeChild(node.firstChild) - } - return node - } - - /********************************************************************* - * avalon的原型方法定义区 * - **********************************************************************/ - - function hyphen(target) { - //转换为连字符线风格 - return target.replace(/([a-z\d])([A-Z]+)/g, "$1-$2").toLowerCase() - } - - function camelize(target) { - //提前判断,提高getStyle等的效率 - if (!target || target.indexOf("-") < 0 && target.indexOf("_") < 0) { - return target - } - //转换为驼峰风格 - return target.replace(/[-_][^-_]/g, function(match) { - return match.charAt(1).toUpperCase() - }) - } - - var fakeClassListMethods = { - _toString: function() { - var node = this.node - var cls = node.className - var str = typeof cls === "string" ? cls : cls.baseVal - return str.split(/\s+/).join(" ") - }, - _contains: function(cls) { - return (" " + this + " ").indexOf(" " + cls + " ") > -1 - }, - _add: function(cls) { - if (!this.contains(cls)) { - this._set(this + " " + cls) - } - }, - _remove: function(cls) { - this._set((" " + this + " ").replace(" " + cls + " ", " ")) - }, - __set: function(cls) { - cls = cls.trim() - var node = this.node - if (rsvg.test(node)) { - //SVG元素的className是一个对象 SVGAnimatedString { baseVal="", animVal=""},只能通过set/getAttribute操作 - node.setAttribute("class", cls) - } else { - node.className = cls - } - } //toggle存在版本差异,因此不使用它 - } - - function fakeClassList(node) { - if (!("classList" in node)) { - node.classList = { - node: node - } - for (var k in fakeClassListMethods) { - node.classList[k.slice(1)] = fakeClassListMethods[k] - } - } - return node.classList - } - - - "add,remove".replace(rword, function(method) { - avalon.fn[method + "Class"] = function(cls) { - var el = this[0] - //https://developer.mozilla.org/zh-CN/docs/Mozilla/Firefox/Releases/26 - if (cls && typeof cls === "string" && el && el.nodeType === 1) { - cls.replace(/\S+/g, function(c) { - fakeClassList(el)[method](c) - }) - } - return this - } - }) - avalon.fn.mix({ - hasClass: function(cls) { - var el = this[0] || {} - return el.nodeType === 1 && fakeClassList(el).contains(cls) - }, - toggleClass: function(value, stateVal) { - var className, i = 0 - var classNames = String(value).split(/\s+/) - var isBool = typeof stateVal === "boolean" - while ((className = classNames[i++])) { - var state = isBool ? stateVal : !this.hasClass(className) - this[state ? "addClass" : "removeClass"](className) - } - return this - }, - attr: function(name, value) { - if (arguments.length === 2) { - this[0].setAttribute(name, value) - return this - } else { - return this[0].getAttribute(name) - } - }, - data: function(name, value) { - name = "data-" + hyphen(name || "") - switch (arguments.length) { - case 2: - this.attr(name, value) - return this - case 1: - var val = this.attr(name) - return parseData(val) - case 0: - var ret = {} - ap.forEach.call(this[0].attributes, function(attr) { - if (attr) { - name = attr.name - if (!name.indexOf("data-")) { - name = camelize(name.slice(5)) - ret[name] = parseData(attr.value) - } - } - }) - return ret - } - }, - removeData: function(name) { - name = "data-" + hyphen(name) - this[0].removeAttribute(name) - return this - }, - css: function(name, value) { - if (avalon.isPlainObject(name)) { - for (var i in name) { - avalon.css(this, i, name[i]) - } - } else { - var ret = avalon.css(this, name, value) - } - return ret !== void 0 ? ret : this - }, - position: function() { - var offsetParent, offset, - elem = this[0], - parentOffset = { - top: 0, - left: 0 - } - if (!elem) { - return - } - if (this.css("position") === "fixed") { - offset = elem.getBoundingClientRect() - } else { - offsetParent = this.offsetParent() //得到真正的offsetParent - offset = this.offset() // 得到正确的offsetParent - if (offsetParent[0].tagName !== "HTML") { - parentOffset = offsetParent.offset() - } - parentOffset.top += avalon.css(offsetParent[0], "borderTopWidth", true) - parentOffset.left += avalon.css(offsetParent[0], "borderLeftWidth", true) - - // Subtract offsetParent scroll positions - parentOffset.top -= offsetParent.scrollTop() - parentOffset.left -= offsetParent.scrollLeft() - } - return { - top: offset.top - parentOffset.top - avalon.css(elem, "marginTop", true), - left: offset.left - parentOffset.left - avalon.css(elem, "marginLeft", true) - } - }, - offsetParent: function() { - var offsetParent = this[0].offsetParent - while (offsetParent && avalon.css(offsetParent, "position") === "static") { - offsetParent = offsetParent.offsetParent; - } - return avalon(offsetParent || root) - }, - bind: function(type, fn, phase) { - if (this[0]) { //此方法不会链 - return avalon.bind(this[0], type, fn, phase) - } - }, - unbind: function(type, fn, phase) { - if (this[0]) { - avalon.unbind(this[0], type, fn, phase) - } - return this - }, - val: function(value) { - var node = this[0] - if (node && node.nodeType === 1) { - var get = arguments.length === 0 - var access = get ? ":get" : ":set" - var fn = valHooks[getValType(node) + access] - if (fn) { - var val = fn(node, value) - } else if (get) { - return (node.value || "").replace(/\r/g, "") - } else { - node.value = value - } - } - return get ? val : this - } - }) - - function parseData(data) { - try { - if (typeof data === "object") - return data - data = data === "true" ? true : - data === "false" ? false : - data === "null" ? null : +data + "" === data ? +data : rbrace.test(data) ? avalon.parseJSON(data) : data - } catch (e) {} - return data - } - var rbrace = /(?:\{[\s\S]*\}|\[[\s\S]*\])$/, - rvalidchars = /^[\],:{}\s]*$/, - rvalidbraces = /(?:^|:|,)(?:\s*\[)+/g, - rvalidescape = /\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g, - rvalidtokens = /"[^"\\\r\n]*"|true|false|null|-?(?:\d+\.|)\d+(?:[eE][+-]?\d+|)/g - avalon.parseJSON = window.JSON ? JSON.parse : function(data) { - if (typeof data === "string") { - data = data.trim(); - if (data) { - if (rvalidchars.test(data.replace(rvalidescape, "@") - .replace(rvalidtokens, "]") - .replace(rvalidbraces, ""))) { - return (new Function("return " + data))() // jshint ignore:line - } - } - avalon.error("Invalid JSON: " + data) - } - return data - } - -//生成avalon.fn.scrollLeft, avalon.fn.scrollTop方法 - avalon.each({ - scrollLeft: "pageXOffset", - scrollTop: "pageYOffset" - }, function(method, prop) { - avalon.fn[method] = function(val) { - var node = this[0] || {}, win = getWindow(node), - top = method === "scrollTop" - if (!arguments.length) { - return win ? (prop in win) ? win[prop] : root[method] : node[method] - } else { - if (win) { - win.scrollTo(!top ? val : avalon(win).scrollLeft(), top ? val : avalon(win).scrollTop()) - } else { - node[method] = val - } - } - } - }) - - function getWindow(node) { - return node.window && node.document ? node : node.nodeType === 9 ? node.defaultView || node.parentWindow : false; - } -//=============================css相关======================= - var cssHooks = avalon.cssHooks = {} - var prefixes = ["", "-webkit-", "-o-", "-moz-", "-ms-"] - var cssMap = { - "float": W3C ? "cssFloat" : "styleFloat" - } - avalon.cssNumber = oneObject("columnCount,order,fillOpacity,fontWeight,lineHeight,opacity,orphans,widows,zIndex,zoom") - - avalon.cssName = function(name, host, camelCase) { - if (cssMap[name]) { - return cssMap[name] - } - host = host || root.style - for (var i = 0, n = prefixes.length; i < n; i++) { - camelCase = camelize(prefixes[i] + name) - if (camelCase in host) { - return (cssMap[name] = camelCase) - } - } - return null - } - cssHooks["@:set"] = function(node, name, value) { - try { //node.style.width = NaN;node.style.width = "xxxxxxx";node.style.width = undefine 在旧式IE下会抛异常 - node.style[name] = value - } catch (e) {} - } - if (window.getComputedStyle) { - cssHooks["@:get"] = function(node, name) { - if (!node || !node.style) { - throw new Error("getComputedStyle要求传入一个节点 " + node) - } - var ret, styles = getComputedStyle(node, null) - if (styles) { - ret = name === "filter" ? styles.getPropertyValue(name) : styles[name] - if (ret === "") { - ret = node.style[name] //其他浏览器需要我们手动取内联样式 - } - } - return ret - } - cssHooks["opacity:get"] = function(node) { - var ret = cssHooks["@:get"](node, "opacity") - return ret === "" ? "1" : ret - } - } else { - var rnumnonpx = /^-?(?:\d*\.)?\d+(?!px)[^\d\s]+$/i - var rposition = /^(top|right|bottom|left)$/ - var ralpha = /alpha\([^)]*\)/i - var ie8 = !! window.XDomainRequest - var salpha = "DXImageTransform.Microsoft.Alpha" - var border = { - thin: ie8 ? '1px' : '2px', - medium: ie8 ? '3px' : '4px', - thick: ie8 ? '5px' : '6px' - } - cssHooks["@:get"] = function(node, name) { - //取得精确值,不过它有可能是带em,pc,mm,pt,%等单位 - var currentStyle = node.currentStyle - var ret = currentStyle[name] - if ((rnumnonpx.test(ret) && !rposition.test(ret))) { - //①,保存原有的style.left, runtimeStyle.left, - var style = node.style, - left = style.left, - rsLeft = node.runtimeStyle.left - //②由于③处的style.left = xxx会影响到currentStyle.left, - //因此把它currentStyle.left放到runtimeStyle.left, - //runtimeStyle.left拥有最高优先级,不会style.left影响 - node.runtimeStyle.left = currentStyle.left - //③将精确值赋给到style.left,然后通过IE的另一个私有属性 style.pixelLeft - //得到单位为px的结果;fontSize的分支见http://bugs.jquery.com/ticket/760 - style.left = name === 'fontSize' ? '1em' : (ret || 0) - ret = style.pixelLeft + "px" - //④还原 style.left,runtimeStyle.left - style.left = left - node.runtimeStyle.left = rsLeft - } - if (ret === "medium") { - name = name.replace("Width", "Style") - //border width 默认值为medium,即使其为0" - if (currentStyle[name] === "none") { - ret = "0px" - } - } - return ret === "" ? "auto" : border[ret] || ret - } - cssHooks["opacity:set"] = function(node, name, value) { - var style = node.style - var opacity = isFinite(value) && value <= 1 ? "alpha(opacity=" + value * 100 + ")" : "" - var filter = style.filter || ""; - style.zoom = 1 - //不能使用以下方式设置透明度 - //node.filters.alpha.opacity = value * 100 - style.filter = (ralpha.test(filter) ? - filter.replace(ralpha, opacity) : - filter + " " + opacity).trim() - if (!style.filter) { - style.removeAttribute("filter") - } - } - cssHooks["opacity:get"] = function(node) { - //这是最快的获取IE透明值的方式,不需要动用正则了! - var alpha = node.filters.alpha || node.filters[salpha], - op = alpha && alpha.enabled ? alpha.opacity : 100 - return (op / 100) + "" //确保返回的是字符串 - } - } - - "top,left".replace(rword, function(name) { - cssHooks[name + ":get"] = function(node) { - var computed = cssHooks["@:get"](node, name) - return /px$/.test(computed) ? computed : - avalon(node).position()[name] + "px" - } - }) - - var cssShow = { - position: "absolute", - visibility: "hidden", - display: "block" - } - - var rdisplayswap = /^(none|table(?!-c[ea]).+)/ - - function showHidden(node, array) { - //http://www.cnblogs.com/rubylouvre/archive/2012/10/27/2742529.html - if (node.offsetWidth <= 0) { //opera.offsetWidth可能小于0 - if (rdisplayswap.test(cssHooks["@:get"](node, "display"))) { - var obj = { - node: node - } - for (var name in cssShow) { - obj[name] = node.style[name] - node.style[name] = cssShow[name] - } - array.push(obj) - } - var parent = node.parentNode - if (parent && parent.nodeType === 1) { - showHidden(parent, array) - } - } - } - "Width,Height".replace(rword, function(name) { //fix 481 - var method = name.toLowerCase(), - clientProp = "client" + name, - scrollProp = "scroll" + name, - offsetProp = "offset" + name - cssHooks[method + ":get"] = function(node, which, override) { - var boxSizing = -4 - if (typeof override === "number") { - boxSizing = override - } - which = name === "Width" ? ["Left", "Right"] : ["Top", "Bottom"] - var ret = node[offsetProp] // border-box 0 - if (boxSizing === 2) { // margin-box 2 - return ret + avalon.css(node, "margin" + which[0], true) + avalon.css(node, "margin" + which[1], true) - } - if (boxSizing < 0) { // padding-box -2 - ret = ret - avalon.css(node, "border" + which[0] + "Width", true) - avalon.css(node, "border" + which[1] + "Width", true) - } - if (boxSizing === -4) { // content-box -4 - ret = ret - avalon.css(node, "padding" + which[0], true) - avalon.css(node, "padding" + which[1], true) - } - return ret - } - cssHooks[method + "&get"] = function(node) { - var hidden = []; - showHidden(node, hidden); - var val = cssHooks[method + ":get"](node) - for (var i = 0, obj; obj = hidden[i++];) { - node = obj.node - for (var n in obj) { - if (typeof obj[n] === "string") { - node.style[n] = obj[n] - } - } - } - return val; - } - avalon.fn[method] = function(value) { //会忽视其display - var node = this[0] - if (arguments.length === 0) { - if (node.setTimeout) { //取得窗口尺寸,IE9后可以用node.innerWidth /innerHeight代替 - return node["inner" + name] || node.document.documentElement[clientProp] - } - if (node.nodeType === 9) { //取得页面尺寸 - var doc = node.documentElement - //FF chrome html.scrollHeight< body.scrollHeight - //IE 标准模式 : html.scrollHeight> body.scrollHeight - //IE 怪异模式 : html.scrollHeight 最大等于可视窗口多一点? - return Math.max(node.body[scrollProp], doc[scrollProp], node.body[offsetProp], doc[offsetProp], doc[clientProp]) - } - return cssHooks[method + "&get"](node) - } else { - return this.css(method, value) - } - } - avalon.fn["inner" + name] = function() { - return cssHooks[method + ":get"](this[0], void 0, -2) - } - avalon.fn["outer" + name] = function(includeMargin) { - return cssHooks[method + ":get"](this[0], void 0, includeMargin === true ? 2 : 0) - } - }) - avalon.fn.offset = function() { //取得距离页面左右角的坐标 - var node = this[0], - box = { - left: 0, - top: 0 - } - if (!node || !node.tagName || !node.ownerDocument) { - return box - } - var doc = node.ownerDocument, - body = doc.body, - root = doc.documentElement, - win = doc.defaultView || doc.parentWindow - if (!avalon.contains(root, node)) { - return box - } - //http://hkom.blog1.fc2.com/?mode=m&no=750 body的偏移量是不包含margin的 - //我们可以通过getBoundingClientRect来获得元素相对于client的rect. - //http://msdn.microsoft.com/en-us/library/ms536433.aspx - if (node.getBoundingClientRect) { - box = node.getBoundingClientRect() // BlackBerry 5, iOS 3 (original iPhone) - } - //chrome/IE6: body.scrollTop, firefox/other: root.scrollTop - var clientTop = root.clientTop || body.clientTop, - clientLeft = root.clientLeft || body.clientLeft, - scrollTop = Math.max(win.pageYOffset || 0, root.scrollTop, body.scrollTop), - scrollLeft = Math.max(win.pageXOffset || 0, root.scrollLeft, body.scrollLeft) - // 把滚动距离加到left,top中去。 - // IE一些版本中会自动为HTML元素加上2px的border,我们需要去掉它 - // http://msdn.microsoft.com/en-us/library/ms533564(VS.85).aspx - return { - top: box.top + scrollTop - clientTop, - left: box.left + scrollLeft - clientLeft - } - } - - //==================================val相关============================ - - function getValType(elem) { - var ret = elem.tagName.toLowerCase() - return ret === "input" && /checkbox|radio/.test(elem.type) ? "checked" : ret - } - var roption = /^]+))?)*\s+value[\s=]/i - var valHooks = { - "option:get": IEVersion ? function(node) { - //在IE11及W3C,如果没有指定value,那么node.value默认为node.text(存在trim作),但IE9-10则是取innerHTML(没trim操作) - //specified并不可靠,因此通过分析outerHTML判定用户有没有显示定义value - return roption.test(node.outerHTML) ? node.value : node.text.trim() - } : function(node) { - return node.value - }, - "select:get": function(node, value) { - var option, options = node.options, - index = node.selectedIndex, - getter = valHooks["option:get"], - one = node.type === "select-one" || index < 0, - values = one ? null : [], - max = one ? index + 1 : options.length, - i = index < 0 ? max : one ? index : 0 - for (; i < max; i++) { - option = options[i] - //旧式IE在reset后不会改变selected,需要改用i === index判定 - //我们过滤所有disabled的option元素,但在safari5下,如果设置select为disable,那么其所有孩子都disable - //因此当一个元素为disable,需要检测其是否显式设置了disable及其父节点的disable情况 - if ((option.selected || i === index) && !option.disabled) { - value = getter(option) - if (one) { - return value - } - //收集所有selected值组成数组返回 - values.push(value) - } - } - return values - }, - "select:set": function(node, values, optionSet) { - values = [].concat(values) //强制转换为数组 - var getter = valHooks["option:get"] - for (var i = 0, el; el = node.options[i++];) { - if ((el.selected = values.indexOf(getter(el)) > -1)) { - optionSet = true - } - } - if (!optionSet) { - node.selectedIndex = -1 - } - } - } - - /********************************************************************* - * 编译系统 * - **********************************************************************/ - var meta = { - '\b': '\\b', - '\t': '\\t', - '\n': '\\n', - '\f': '\\f', - '\r': '\\r', - '"': '\\"', - '\\': '\\\\' - } - var quote = window.JSON && JSON.stringify || function(str) { - return '"' + str.replace(/[\\\"\x00-\x1f]/g, function(a) { - var c = meta[a]; - return typeof c === 'string' ? c : - '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); - }) + '"' - } - - var keywords = [ - "break,case,catch,continue,debugger,default,delete,do,else,false", - "finally,for,function,if,in,instanceof,new,null,return,switch,this", - "throw,true,try,typeof,var,void,while,with", /* 关键字*/ - "abstract,boolean,byte,char,class,const,double,enum,export,extends", - "final,float,goto,implements,import,int,interface,long,native", - "package,private,protected,public,short,static,super,synchronized", - "throws,transient,volatile", /*保留字*/ - "arguments,let,yield,undefined" /* ECMA 5 - use strict*/].join(",") - var rrexpstr = /\/\*[\w\W]*?\*\/|\/\/[^\n]*\n|\/\/[^\n]*$|"(?:[^"\\]|\\[\w\W])*"|'(?:[^'\\]|\\[\w\W])*'|[\s\t\n]*\.[\s\t\n]*[$\w\.]+/g - var rsplit = /[^\w$]+/g - var rkeywords = new RegExp(["\\b" + keywords.replace(/,/g, '\\b|\\b') + "\\b"].join('|'), 'g') - var rnumber = /\b\d[^,]*/g - var rcomma = /^,+|,+$/g - var variablePool = new Cache(512) - var getVariables = function (code) { - var key = "," + code.trim() - var ret = variablePool.get(key) - if (ret) { - return ret - } - var match = code - .replace(rrexpstr, "") - .replace(rsplit, ",") - .replace(rkeywords, "") - .replace(rnumber, "") - .replace(rcomma, "") - .split(/^$|,+/) - return variablePool.put(key, uniqSet(match)) - } - /*添加赋值语句*/ - - function addAssign(vars, scope, name, data) { - var ret = [], - prefix = " = " + name + "." - var isProxy = /\$proxy\$each/.test(scope.$id) - for (var i = vars.length, prop; prop = vars[--i]; ) { - var el = isProxy && scope.$map[prop] ? "el" : prop - if (scope.hasOwnProperty(el)) { - ret.push(prop + prefix + el) - data.vars.push(prop) - if (data.type === "duplex") { - vars.get = name + "." + el - } - vars.splice(i, 1) - } - } - return ret - } - - function uniqSet(array) { - var ret = [], - unique = {} - for (var i = 0; i < array.length; i++) { - var el = array[i] - var id = el && typeof el.$id === "string" ? el.$id : el - if (!unique[id]) { - unique[id] = ret.push(el) - } - } - return ret - } -//缓存求值函数,以便多次利用 - var evaluatorPool = new Cache(128) -//取得求值函数及其传参 - var rduplex = /\w\[.*\]|\w\.\w/ - var rproxy = /(\$proxy\$[a-z]+)\d+$/ - var rthimRightParentheses = /\)\s*$/ - var rthimOtherParentheses = /\)\s*\|/g - var rquoteFilterName = /\|\s*([$\w]+)/g - var rpatchBracket = /"\s*\["/g - var rthimLeftParentheses = /"\s*\(/g - function parseFilter(val, filters) { - filters = filters - .replace(rthimRightParentheses, "")//处理最后的小括号 - .replace(rthimOtherParentheses, function () {//处理其他小括号 - return "],|" - }) - .replace(rquoteFilterName, function (a, b) { //处理|及它后面的过滤器的名字 - return "[" + quote(b) - }) - .replace(rpatchBracket, function () { - return '"],["' - }) - .replace(rthimLeftParentheses, function () { - return '",' - }) + "]" - return "return avalon.filters.$filter(" + val + ", " + filters + ")" - } - - function parseExpr(code, scopes, data) { - var dataType = data.type - var filters = data.filters || "" - var exprId = scopes.map(function (el) { - return String(el.$id).replace(rproxy, "$1") - }) + code + dataType + filters - var vars = getVariables(code).concat(), - assigns = [], - names = [], - args = [], - prefix = "" - //args 是一个对象数组, names 是将要生成的求值函数的参数 - scopes = uniqSet(scopes) - data.vars = [] - for (var i = 0, sn = scopes.length; i < sn; i++) { - if (vars.length) { - var name = "vm" + expose + "_" + i - names.push(name) - args.push(scopes[i]) - assigns.push.apply(assigns, addAssign(vars, scopes[i], name, data)) - } - } - if (!assigns.length && dataType === "duplex") { - return - } - if (dataType !== "duplex" && (code.indexOf("||") > -1 || code.indexOf("&&") > -1)) { - //https://github.com/RubyLouvre/avalon/issues/583 - data.vars.forEach(function (v) { - var reg = new RegExp("\\b" + v + "(?:\\.\\w+|\\[\\w+\\])+", "ig") - code = code.replace(reg, function (_) { - var c = _.charAt(v.length) - var r = IEVersion ? code.slice(arguments[1] + _.length) : RegExp.rightContext - var method = /^\s*\(/.test(r) - if (c === "." || c === "[" || method) {//比如v为aa,我们只匹配aa.bb,aa[cc],不匹配aaa.xxx - var name = "var" + String(Math.random()).replace(/^0\./, "") - if (method) {//array.size() - var array = _.split(".") - if (array.length > 2) { - var last = array.pop() - assigns.push(name + " = " + array.join(".")) - return name + "." + last - } else { - return _ - } - } - assigns.push(name + " = " + _) - return name - } else { - return _ - } - }) - }) - } - //---------------args---------------- - data.args = args - //---------------cache---------------- - delete data.vars - var fn = evaluatorPool.get(exprId) //直接从缓存,免得重复生成 - if (fn) { - data.evaluator = fn - return - } - prefix = assigns.join(", ") - if (prefix) { - prefix = "var " + prefix - } - if (/\S/.test(filters)) { //文本绑定,双工绑定才有过滤器 - if (!/text|html/.test(data.type)) { - throw Error("ms-" + data.type + "不支持过滤器") - } - code = "\nvar ret" + expose + " = " + code + ";\r\n" - code += parseFilter("ret" + expose, filters) - } else if (dataType === "duplex") { //双工绑定 - var _body = "'use strict';\nreturn function(vvv){\n\t" + - prefix + - ";\n\tif(!arguments.length){\n\t\treturn " + - code + - "\n\t}\n\t" + (!rduplex.test(code) ? vars.get : code) + - "= vvv;\n} " - try { - fn = Function.apply(noop, names.concat(_body)) - data.evaluator = evaluatorPool.put(exprId, fn) - } catch (e) { - log("debug: parse error," + e.message) - } - return - } else if (dataType === "on") { //事件绑定 - if (code.indexOf("(") === -1) { - code += ".call(this, $event)" - } else { - code = code.replace("(", ".call(this,") - } - names.push("$event") - code = "\nreturn " + code + ";" //IE全家 Function("return ")出错,需要Function("return ;") - var lastIndex = code.lastIndexOf("\nreturn") - var header = code.slice(0, lastIndex) - var footer = code.slice(lastIndex) - code = header + "\n" + footer - } else { //其他绑定 - code = "\nreturn " + code + ";" //IE全家 Function("return ")出错,需要Function("return ;") - } - try { - fn = Function.apply(noop, names.concat("'use strict';\n" + prefix + code)) - data.evaluator = evaluatorPool.put(exprId, fn) - } catch (e) { - log("debug: parse error," + e.message) - } finally { - vars = assigns = names = null //释放内存 - } - } - - -//parseExpr的智能引用代理 - - function parseExprProxy(code, scopes, data, tokens, noRegister) { - if (Array.isArray(tokens)) { - code = tokens.map(function (el) { - return el.expr ? "(" + el.value + ")" : quote(el.value) - }).join(" + ") - } - parseExpr(code, scopes, data) - if (data.evaluator && !noRegister) { - data.handler = bindingExecutors[data.handlerName || data.type] - //方便调试 - //这里非常重要,我们通过判定视图刷新函数的element是否在DOM树决定 - //将它移出订阅者列表 - avalon.injectBinding(data) - } - } - avalon.parseExprProxy = parseExprProxy - /********************************************************************* - * 扫描系统 * - **********************************************************************/ - - avalon.scan = function(elem, vmodel) { - elem = elem || root - var vmodels = vmodel ? [].concat(vmodel) : [] - scanTag(elem, vmodels) - } - -//http://www.w3.org/TR/html5/syntax.html#void-elements - var stopScan = oneObject("area,base,basefont,br,col,command,embed,hr,img,input,link,meta,param,source,track,wbr,noscript,script,style,textarea".toUpperCase()) - - function checkScan(elem, callback, innerHTML) { - var id = setTimeout(function() { - var currHTML = elem.innerHTML - clearTimeout(id) - if (currHTML === innerHTML) { - callback() - } else { - checkScan(elem, callback, currHTML) - } - }) - } - - - function createSignalTower(elem, vmodel) { - var id = elem.getAttribute("avalonctrl") || vmodel.$id - elem.setAttribute("avalonctrl", id) - vmodel.$events.expr = elem.tagName + '[avalonctrl="' + id + '"]' - } - - var getBindingCallback = function(elem, name, vmodels) { - var callback = elem.getAttribute(name) - if (callback) { - for (var i = 0, vm; vm = vmodels[i++]; ) { - if (vm.hasOwnProperty(callback) && typeof vm[callback] === "function") { - return vm[callback] - } - } - } - } - - function executeBindings(bindings, vmodels) { - for (var i = 0, data; data = bindings[i++]; ) { - data.vmodels = vmodels - bindingHandlers[data.type](data, vmodels) - if (data.evaluator && data.element && data.element.nodeType === 1) { //移除数据绑定,防止被二次解析 - //chrome使用removeAttributeNode移除不存在的特性节点时会报错 https://github.com/RubyLouvre/avalon/issues/99 - data.element.removeAttribute(data.name) - } - } - bindings.length = 0 - } - -//https://github.com/RubyLouvre/avalon/issues/636 - var mergeTextNodes = IEVersion && window.MutationObserver ? function (elem) { - var node = elem.firstChild, text - while (node) { - var aaa = node.nextSibling - if (node.nodeType === 3) { - if (text) { - text.nodeValue += node.nodeValue - elem.removeChild(node) - } else { - text = node - } - } else { - text = null - } - node = aaa - } - } : 0 - - var rmsAttr = /ms-(\w+)-?(.*)/ - var priorityMap = { - "if": 10, - "repeat": 90, - "data": 100, - "widget": 110, - "each": 1400, - "with": 1500, - "duplex": 2000, - "on": 3000 - } - - var events = oneObject("animationend,blur,change,input,click,dblclick,focus,keydown,keypress,keyup,mousedown,mouseenter,mouseleave,mousemove,mouseout,mouseover,mouseup,scan,scroll,submit") - var obsoleteAttrs = oneObject("value,title,alt,checked,selected,disabled,readonly,enabled") - function bindingSorter(a, b) { - return a.priority - b.priority - } - - function scanAttr(elem, vmodels, match) { - var scanNode = true - if (vmodels.length) { - var attributes = getAttributes ? getAttributes(elem) : elem.attributes - var bindings = [] - var fixAttrs = [] - var msData = {} - for (var i = 0, attr; attr = attributes[i++]; ) { - if (attr.specified) { - if (match = attr.name.match(rmsAttr)) { - //如果是以指定前缀命名的 - var type = match[1] - var param = match[2] || "" - var value = attr.value - var name = attr.name - if (events[type]) { - param = type - type = "on" - } else if (obsoleteAttrs[type]) { - if (type === "enabled") {//吃掉ms-enabled绑定,用ms-disabled代替 - log("warning!ms-enabled或ms-attr-enabled已经被废弃") - type = "disabled" - value = "!(" + value + ")" - } - param = type - type = "attr" - name = "ms-" + type + "-"+ param - fixAttrs.push([attr.name, name, value]) - } - msData[name] = value - if (typeof bindingHandlers[type] === "function") { - var binding = { - type: type, - param: param, - element: elem, - name: name, - value: value, - //chrome与firefox下Number(param)得到的值不一样 #855 - priority: (priorityMap[type] || type.charCodeAt(0) * 10 )+ (Number(param.replace(/\D/g, "")) || 0) - } - if (type === "html" || type === "text") { - var token = getToken(value) - avalon.mix(binding, token) - binding.filters = binding.filters.replace(rhasHtml, function () { - binding.type = "html" - binding.group = 1 - return "" - })// jshint ignore:line - } else if (type === "duplex") { - var hasDuplex = name - } else if (name === "ms-if-loop") { - binding.priority += 100 - } - bindings.push(binding) - if (type === "widget") { - elem.msData = elem.msData || msData - } - } - } - } - } - if (bindings.length) { - bindings.sort(bindingSorter) - fixAttrs.forEach(function (arr) { - log("warning!请改用" + arr[1] + "代替" + arr[0] + "!") - elem.removeAttribute(arr[0]) - elem.setAttribute(arr[1], arr[2]) - }) - //http://bugs.jquery.com/ticket/7071 - //在IE下对VML读取type属性,会让此元素所有属性都变成 - if (hasDuplex) { - if (msData["ms-attr-checked"]) { - log("warning!一个控件不能同时定义ms-attr-checked与" + hasDuplex) - } - if (msData["ms-attr-value"]) { - log("warning!一个控件不能同时定义ms-attr-value与" + hasDuplex) - } - } - for (i = 0; binding = bindings[i]; i++) { - type = binding.type - if (rnoscanAttrBinding.test(type)) { - return executeBindings(bindings.slice(0, i + 1), vmodels) - } else if (scanNode) { - scanNode = !rnoscanNodeBinding.test(type) - } - } - executeBindings(bindings, vmodels) - } - } - if (scanNode && !stopScan[elem.tagName] && rbind.test(elem.innerHTML.replace(rlt, "<").replace(rgt, ">"))) { - mergeTextNodes && mergeTextNodes(elem) - scanNodeList(elem, vmodels) //扫描子孙元素 - } - } - - var rnoscanAttrBinding = /^if|widget|repeat$/ - var rnoscanNodeBinding = /^each|with|html|include$/ -//IE67下,在循环绑定中,一个节点如果是通过cloneNode得到,自定义属性的specified为false,无法进入里面的分支, -//但如果我们去掉scanAttr中的attr.specified检测,一个元素会有80+个特性节点(因为它不区分固有属性与自定义属性),很容易卡死页面 - if (!"1" [0]) { - var attrPool = new Cache(512) - var rattrs = /\s+(ms-[^=\s]+)(?:=("[^"]*"|'[^']*'|[^\s>]+))?/g, - rquote = /^['"]/, - rtag = /<\w+\b(?:(["'])[^"]*?(\1)|[^>])*>/i, - ramp = /&/g - //IE6-8解析HTML5新标签,会将它分解两个元素节点与一个文本节点 - //
ddd
- // window.onload = function() { - // var body = document.body - // for (var i = 0, el; el = body.children[i++]; ) { - // avalon.log(el.outerHTML) - // } - // } - //依次输出
,
- var getAttributes = function (elem) { - var html = elem.outerHTML - //处理IE6-8解析HTML5新标签的情况,及
等半闭合标签outerHTML为空的情况 - if (html.slice(0, 2) === " ms-important(1) --> ms-controller(2) --> ms-if(10) --> ms-repeat(100) - //--> ms-if-loop(110) --> ms-attr(970) ...--> ms-each(1400)-->ms-with(1500)--〉ms-duplex(2000)垫后 - var a = elem.getAttribute("ms-skip") - //#360 在旧式IE中 Object标签在引入Flash等资源时,可能出现没有getAttributeNode,innerHTML的情形 - if (!elem.getAttributeNode) { - return log("warning " + elem.tagName + " no getAttributeNode method") - } - var b = elem.getAttributeNode("ms-important") - var c = elem.getAttributeNode("ms-controller") - if (typeof a === "string") { - return - } else if (node = b || c) { - var newVmodel = avalon.vmodels[node.value] - if (!newVmodel) { - return - } - //ms-important不包含父VM,ms-controller相反 - vmodels = node === b ? [newVmodel] : [newVmodel].concat(vmodels) - var name = node.name - elem.removeAttribute(name) //removeAttributeNode不会刷新[ms-controller]样式规则 - avalon(elem).removeClass(name) - createSignalTower(elem, newVmodel) - } - scanAttr(elem, vmodels) //扫描特性节点 - } - var rhasHtml = /\|\s*html\s*/, - r11a = /\|\|/g, - rlt = /</g, - rgt = />/g, - rstringLiteral = /(['"])(\\\1|.)+?\1/g - function getToken(value, pos) { - if (value.indexOf("|") > 0) { - var scapegoat = value.replace( rstringLiteral, function(_){ - return Array(_.length+1).join("1")// jshint ignore:line - }) - var index = scapegoat.replace(r11a, "\u1122\u3344").indexOf("|") //干掉所有短路或 - if (index > -1) { - return { - filters: value.slice(index), - value: value.slice(0, index), - pos: pos || 0, - expr: true - } - } - } - return { - value: value, - filters: "", - expr: true - } - } - - function scanExpr(str) { - var tokens = [], - value, start = 0, - stop - do { - stop = str.indexOf(openTag, start) - if (stop === -1) { - break - } - value = str.slice(start, stop) - if (value) { // {{ 左边的文本 - tokens.push({ - value: value, - filters: "", - expr: false - }) - } - start = stop + openTag.length - stop = str.indexOf(closeTag, start) - if (stop === -1) { - break - } - value = str.slice(start, stop) - if (value) { //处理{{ }}插值表达式 - tokens.push(getToken(value, start)) - } - start = stop + closeTag.length - } while (1) - value = str.slice(start) - if (value) { //}} 右边的文本 - tokens.push({ - value: value, - expr: false, - filters: "" - }) - } - return tokens - } - - function scanText(textNode, vmodels) { - var bindings = [] - if (textNode.nodeType === 8) { - var token = getToken(textNode.nodeValue) - var tokens = [token] - } else { - tokens = scanExpr(textNode.data) - } - if (tokens.length) { - for (var i = 0; token = tokens[i++]; ) { - var node = DOC.createTextNode(token.value) //将文本转换为文本节点,并替换原来的文本节点 - if (token.expr) { - token.type = "text" - token.element = node - token.filters = token.filters.replace(rhasHtml, function() { - token.type = "html" - return "" - })// jshint ignore:line - bindings.push(token) //收集带有插值表达式的文本 - } - avalonFragment.appendChild(node) - } - textNode.parentNode.replaceChild(avalonFragment, textNode) - if (bindings.length) - executeBindings(bindings, vmodels) - } - } - - var bools = ["autofocus,autoplay,async,allowTransparency,checked,controls", - "declare,disabled,defer,defaultChecked,defaultSelected", - "contentEditable,isMap,loop,multiple,noHref,noResize,noShade", - "open,readOnly,selected" - ].join(",") - var boolMap = {} - bools.replace(rword, function(name) { - boolMap[name.toLowerCase()] = name - }) - - var propMap = { //属性名映射 - "accept-charset": "acceptCharset", - "char": "ch", - "charoff": "chOff", - "class": "className", - "for": "htmlFor", - "http-equiv": "httpEquiv" - } - - var anomaly = ["accessKey,bgColor,cellPadding,cellSpacing,codeBase,codeType,colSpan", - "dateTime,defaultValue,frameBorder,longDesc,maxLength,marginWidth,marginHeight", - "rowSpan,tabIndex,useMap,vSpace,valueType,vAlign" - ].join(",") - anomaly.replace(rword, function(name) { - propMap[name.toLowerCase()] = name - }) - - var rnoscripts = /(?:[\s\S]+?)<\/noscript>/img - var rnoscriptText = /([\s\S]+?)<\/noscript>/im - - var getXHR = function() { - return new(window.XMLHttpRequest || ActiveXObject)("Microsoft.XMLHTTP") // jshint ignore:line - } - - var templatePool = avalon.templateCache = {} - - bindingHandlers.attr = function(data, vmodels) { - var text = data.value.trim(), - simple = true - if (text.indexOf(openTag) > -1 && text.indexOf(closeTag) > 2) { - simple = false - if (rexpr.test(text) && RegExp.rightContext === "" && RegExp.leftContext === "") { - simple = true - text = RegExp.$1 - } - } - if (data.type === "include") { - var elem = data.element - data.includeRendered = getBindingCallback(elem, "data-include-rendered", vmodels) - data.includeLoaded = getBindingCallback(elem, "data-include-loaded", vmodels) - var outer = data.includeReplace = !! avalon(elem).data("includeReplace") - if (avalon(elem).data("includeCache")) { - data.templateCache = {} - } - data.startInclude = DOC.createComment("ms-include") - data.endInclude = DOC.createComment("ms-include-end") - if (outer) { - data.element = data.startInclude - elem.parentNode.insertBefore(data.startInclude, elem) - elem.parentNode.insertBefore(data.endInclude, elem.nextSibling) - } else { - elem.insertBefore(data.startInclude, elem.firstChild) - elem.appendChild(data.endInclude) - } - } - data.handlerName = "attr" //handleName用于处理多种绑定共用同一种bindingExecutor的情况 - parseExprProxy(text, vmodels, data, (simple ? 0 : scanExpr(data.value))) - } - - bindingExecutors.attr = function(val, elem, data) { - var method = data.type, - attrName = data.param - if (method === "css") { - avalon(elem).css(attrName, val) - } else if (method === "attr") { - - // ms-attr-class="xxx" vm.xxx="aaa bbb ccc"将元素的className设置为aaa bbb ccc - // ms-attr-class="xxx" vm.xxx=false 清空元素的所有类名 - // ms-attr-name="yyy" vm.yyy="ooo" 为元素设置name属性 - var toRemove = (val === false) || (val === null) || (val === void 0) - - if (!W3C && propMap[attrName]) { //旧式IE下需要进行名字映射 - attrName = propMap[attrName] - } - var bool = boolMap[attrName] - if (typeof elem[bool] === "boolean") { - elem[bool] = !! val //布尔属性必须使用el.xxx = true|false方式设值 - if (!val) { //如果为false, IE全系列下相当于setAttribute(xxx,''),会影响到样式,需要进一步处理 - toRemove = true - } - } - if (toRemove) { - return elem.removeAttribute(attrName) - } - //SVG只能使用setAttribute(xxx, yyy), VML只能使用elem.xxx = yyy ,HTML的固有属性必须elem.xxx = yyy - var isInnate = rsvg.test(elem) ? false : (DOC.namespaces && isVML(elem)) ? true : attrName in elem.cloneNode(false) - if (isInnate) { - elem[attrName] = val - } else { - elem.setAttribute(attrName, val) - } - } else if (method === "include" && val) { - var vmodels = data.vmodels - var rendered = data.includeRendered - var loaded = data.includeLoaded - var replace = data.includeReplace - var target = replace ? elem.parentNode : elem - var scanTemplate = function(text) { - if (loaded) { - var newText = loaded.apply(target, [text].concat(vmodels)) - if (typeof newText === "string") - text = newText - } - if (rendered) { - checkScan(target, function() { - rendered.call(target) - }, NaN) - } - var lastID = data.includeLastID - if (data.templateCache && lastID && lastID !== val) { - var lastTemplate = data.templateCache[lastID] - if (!lastTemplate) { - lastTemplate = data.templateCache[lastID] = DOC.createElement("div") - ifGroup.appendChild(lastTemplate) - } - } - data.includeLastID = val - while (true) { - var node = data.startInclude.nextSibling - if (node && node !== data.endInclude) { - target.removeChild(node) - if (lastTemplate) - lastTemplate.appendChild(node) - } else { - break - } - } - var dom = getTemplateNodes(data, val, text) - var nodes = avalon.slice(dom.childNodes) - target.insertBefore(dom, data.endInclude) - scanNodeArray(nodes, vmodels) - } - - if (data.param === "src") { - if (typeof templatePool[val] === "string") { - avalon.nextTick(function() { - scanTemplate(templatePool[val]) - }) - } else if (Array.isArray(templatePool[val])) { //#805 防止在循环绑定中发出许多相同的请求 - templatePool[val].push(scanTemplate) - } else { - var xhr = getXHR() - xhr.onreadystatechange = function() { - if (xhr.readyState === 4) { - var s = xhr.status - if (s >= 200 && s < 300 || s === 304 || s === 1223) { - var text = xhr.responseText - for (var f = 0, fn; fn = templatePool[val][f++];) { - fn(text) - } - templatePool[val] = text - } - } - } - templatePool[val] = [scanTemplate] - xhr.open("GET", val, true) - if ("withCredentials" in xhr) { - xhr.withCredentials = true - } - xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest") - xhr.send(null) - } - } else { - //IE系列与够新的标准浏览器支持通过ID取得元素(firefox14+) - //http://tjvantoll.com/2012/07/19/dom-element-references-as-global-variables/ - var el = val && val.nodeType === 1 ? val : DOC.getElementById(val) - if (el) { - if (el.tagName === "NOSCRIPT" && !(el.innerHTML || el.fixIE78)) { //IE7-8 innerText,innerHTML都无法取得其内容,IE6能取得其innerHTML - xhr = getXHR() //IE9-11与chrome的innerHTML会得到转义的内容,它们的innerText可以 - xhr.open("GET", location, false) //谢谢Nodejs 乱炖群 深圳-纯属虚构 - xhr.send(null) - //http://bbs.csdn.net/topics/390349046?page=1#post-393492653 - var noscripts = DOC.getElementsByTagName("noscript") - var array = (xhr.responseText || "").match(rnoscripts) || [] - var n = array.length - for (var i = 0; i < n; i++) { - var tag = noscripts[i] - if (tag) { //IE6-8中noscript标签的innerHTML,innerText是只读的 - tag.style.display = "none" //http://haslayout.net/css/noscript-Ghost-Bug - tag.fixIE78 = (array[i].match(rnoscriptText) || ["", " "])[1] - } - } - } - avalon.nextTick(function() { - scanTemplate(el.fixIE78 || el.value || el.innerText || el.innerHTML) - }) - } - } - } else { - if (!root.hasAttribute && typeof val === "string" && (method === "src" || method === "href")) { - val = val.replace(/&/g, "&") //处理IE67自动转义的问题 - } - elem[method] = val - if (window.chrome && elem.tagName === "EMBED") { - var parent = elem.parentNode //#525 chrome1-37下embed标签动态设置src不能发生请求 - var comment = document.createComment("ms-src") - parent.replaceChild(comment, elem) - parent.replaceChild(elem, comment) - } - } - } - - function getTemplateNodes(data, id, text) { - var div = data.templateCache && data.templateCache[id] - if (div) { - var dom = DOC.createDocumentFragment(), - firstChild - while (firstChild = div.firstChild) { - dom.appendChild(firstChild) - } - return dom - } - return avalon.parseHTML(text) - } - -//这几个指令都可以使用插值表达式,如ms-src="aaa/{{b}}/{{c}}.html" - "title,alt,src,value,css,include,href".replace(rword, function(name) { - bindingHandlers[name] = bindingHandlers.attr - }) -//根据VM的属性值或表达式的值切换类名,ms-class="xxx yyy zzz:flag" -//http://www.cnblogs.com/rubylouvre/archive/2012/12/17/2818540.html - bindingHandlers["class"] = function(data, vmodels) { - var oldStyle = data.param, - text = data.value, - rightExpr - data.handlerName = "class" - if (!oldStyle || isFinite(oldStyle)) { - data.param = "" //去掉数字 - var noExpr = text.replace(rexprg, function(a) { - return a.replace(/./g, "0") - //return Math.pow(10, a.length - 1) //将插值表达式插入10的N-1次方来占位 - }) - var colonIndex = noExpr.indexOf(":") //取得第一个冒号的位置 - if (colonIndex === -1) { // 比如 ms-class="aaa bbb ccc" 的情况 - var className = text - } else { // 比如 ms-class-1="ui-state-active:checked" 的情况 - className = text.slice(0, colonIndex) - rightExpr = text.slice(colonIndex + 1) - parseExpr(rightExpr, vmodels, data) //决定是添加还是删除 - if (!data.evaluator) { - log("debug: ms-class '" + (rightExpr || "").trim() + "' 不存在于VM中") - return false - } else { - data._evaluator = data.evaluator - data._args = data.args - } - } - var hasExpr = rexpr.test(className) //比如ms-class="width{{w}}"的情况 - if (!hasExpr) { - data.immobileClass = className - } - parseExprProxy("", vmodels, data, (hasExpr ? scanExpr(className) : 0)) - } else { - data.immobileClass = data.oldStyle = data.param - parseExprProxy(text, vmodels, data) - } - } - - bindingExecutors["class"] = function(val, elem, data) { - var $elem = avalon(elem), - method = data.type - if (method === "class" && data.oldStyle) { //如果是旧风格 - $elem.toggleClass(data.oldStyle, !! val) - } else { - //如果存在冒号就有求值函数 - data.toggleClass = data._evaluator ? !! data._evaluator.apply(elem, data._args) : true - data.newClass = data.immobileClass || val - if (data.oldClass && data.newClass !== data.oldClass) { - $elem.removeClass(data.oldClass) - } - data.oldClass = data.newClass - switch (method) { - case "class": - $elem.toggleClass(data.newClass, data.toggleClass) - break - case "hover": - case "active": - if (!data.hasBindEvent) { //确保只绑定一次 - var activate = "mouseenter" //在移出移入时切换类名 - var abandon = "mouseleave" - if (method === "active") { //在聚焦失焦中切换类名 - elem.tabIndex = elem.tabIndex || -1 - activate = "mousedown" - abandon = "mouseup" - var fn0 = $elem.bind("mouseleave", function() { - data.toggleClass && $elem.removeClass(data.newClass) - }) - } - var fn1 = $elem.bind(activate, function() { - data.toggleClass && $elem.addClass(data.newClass) - }) - var fn2 = $elem.bind(abandon, function() { - data.toggleClass && $elem.removeClass(data.newClass) - }) - data.rollback = function() { - $elem.unbind("mouseleave", fn0) - $elem.unbind(activate, fn1) - $elem.unbind(abandon, fn2) - } - data.hasBindEvent = true - } - break; - } - } - } - - "hover,active".replace(rword, function(method) { - bindingHandlers[method] = bindingHandlers["class"] - }) -//ms-controller绑定已经在scanTag 方法中实现 -//ms-css绑定已由ms-attr绑定实现 - - -// bindingHandlers.data 定义在if.js - bindingExecutors.data = function(val, elem, data) { - var key = "data-" + data.param - if (val && typeof val === "object") { - elem[key] = val - } else { - elem.setAttribute(key, String(val)) - } - } -//双工绑定 - var duplexBinding = bindingHandlers.duplex = function(data, vmodels) { - var elem = data.element, - hasCast - parseExprProxy(data.value, vmodels, data, 0, 1) - - data.changed = getBindingCallback(elem, "data-duplex-changed", vmodels) || noop - if (data.evaluator && data.args) { - var params = [] - var casting = oneObject("string,number,boolean,checked") - if (elem.type === "radio" && data.param === "") { - data.param = "checked" - } - if (elem.msData) { - elem.msData["ms-duplex"] = data.value - } - data.param.replace(/\w+/g, function(name) { - if (/^(checkbox|radio)$/.test(elem.type) && /^(radio|checked)$/.test(name)) { - if (name === "radio") - log("ms-duplex-radio已经更名为ms-duplex-checked") - name = "checked" - data.isChecked = true - } - if (name === "bool") { - name = "boolean" - log("ms-duplex-bool已经更名为ms-duplex-boolean") - } else if (name === "text") { - name = "string" - log("ms-duplex-text已经更名为ms-duplex-string") - } - if (casting[name]) { - hasCast = true - } - avalon.Array.ensure(params, name) - }) - if (!hasCast) { - params.push("string") - } - data.param = params.join("-") - data.bound = function(type, callback) { - if (elem.addEventListener) { - elem.addEventListener(type, callback, false) - } else { - elem.attachEvent("on" + type, callback) - } - var old = data.rollback - data.rollback = function() { - elem.avalonSetter = null - avalon.unbind(elem, type, callback) - old && old() - } - } - for (var i in avalon.vmodels) { - var v = avalon.vmodels[i] - v.$fire("avalon-ms-duplex-init", data) - } - var cpipe = data.pipe || (data.pipe = pipe) - cpipe(null, data, "init") - var tagName = elem.tagName - duplexBinding[tagName] && duplexBinding[tagName](elem, data.evaluator.apply(null, data.args), data) - } - } -//不存在 bindingExecutors.duplex - - function fixNull(val) { - return val == null ? "" : val - } - avalon.duplexHooks = { - checked: { - get: function(val, data) { - return !data.element.oldValue - } - }, - string: { - get: function(val) { //同步到VM - return val - }, - set: fixNull - }, - "boolean": { - get: function(val) { - return val === "true" - }, - set: fixNull - }, - number: { - get: function(val, data) { - var number = parseFloat(val) - if (-val === -number) { - return number - } - var arr = /strong|medium|weak/.exec(data.element.getAttribute("data-duplex-number")) || ["medium"] - switch (arr[0]) { - case "strong": - return 0 - case "medium": - return val === "" ? "" : 0 - case "weak": - return val - } - }, - set: fixNull - } - } - - function pipe(val, data, action, e) { - data.param.replace(/\w+/g, function(name) { - var hook = avalon.duplexHooks[name] - if (hook && typeof hook[action] === "function") { - val = hook[action](val, data) - } - }) - return val - } - - var TimerID, ribbon = [] - - avalon.tick = function(fn) { - if (ribbon.push(fn) === 1) { - TimerID = setInterval(ticker, 60) - } - } - - function ticker() { - for (var n = ribbon.length - 1; n >= 0; n--) { - var el = ribbon[n] - if (el() === false) { - ribbon.splice(n, 1) - } - } - if (!ribbon.length) { - clearInterval(TimerID) - } - } - - var watchValueInTimer = noop - var rmsinput = /text|password|hidden/ - new function() { // jshint ignore:line - try { //#272 IE9-IE11, firefox - var setters = {} - var aproto = HTMLInputElement.prototype - var bproto = HTMLTextAreaElement.prototype - - function newSetter(value) { // jshint ignore:line - if (avalon.optimize || this.parentNode) { - setters[this.tagName].call(this, value) - if (!rmsinput.test(this.type)) - return - if (!this.msFocus && this.avalonSetter) { - this.avalonSetter() - } - } - } - var inputProto = HTMLInputElement.prototype - Object.getOwnPropertyNames(inputProto) //故意引发IE6-8等浏览器报错 - setters["INPUT"] = Object.getOwnPropertyDescriptor(aproto, "value").set - Object.defineProperty(aproto, "value", { - set: newSetter - }) - setters["TEXTAREA"] = Object.getOwnPropertyDescriptor(bproto, "value").set - Object.defineProperty(bproto, "value", { - set: newSetter - }) - } catch (e) { - //在chrome 43中 ms-duplex终于不需要使用定时器实现双向绑定了 - // http://updates.html5rocks.com/2015/04/DOM-attributes-now-on-the-prototype - // https://docs.google.com/document/d/1jwA8mtClwxI-QJuHT7872Z0pxpZz8PBkf2bGAbsUtqs/edit?pli=1 - watchValueInTimer = avalon.tick - } - } // jshint ignore:line - if (IEVersion) { - avalon.bind(DOC, "selectionchange", function(e) { - var el = DOC.activeElement - if (el && typeof el.avalonSetter === "function") { - el.avalonSetter() - } - }) - } - -//处理radio, checkbox, text, textarea, password - duplexBinding.INPUT = function(element, evaluator, data) { - var $type = element.type, - bound = data.bound, - $elem = avalon(element), - composing = false - - function callback(value) { - data.changed.call(this, value, data) - } - - function compositionStart() { - composing = true - } - - function compositionEnd() { - composing = false - } - //当value变化时改变model的值 - var updateVModel = function() { - if (composing) //处理中文输入法在minlengh下引发的BUG - return - var val = element.oldValue = element.value //防止递归调用形成死循环 - var lastValue = data.pipe(val, data, "get") - if ($elem.data("duplexObserve") !== false) { - evaluator(lastValue) - callback.call(element, lastValue) - if ($elem.data("duplex-focus")) { - avalon.nextTick(function() { - element.focus() - }) - } - } - } - //当model变化时,它就会改变value的值 - data.handler = function() { - var val = data.pipe(evaluator(), data, "set") + "" //fix #673 - if (val !== element.oldValue) { - element.value = val - } - } - if (data.isChecked || $type === "radio") { - var IE6 = IEVersion === 6 - updateVModel = function() { - if ($elem.data("duplexObserve") !== false) { - var lastValue = data.pipe(element.value, data, "get") - evaluator(lastValue) - callback.call(element, lastValue) - } - } - data.handler = function() { - var val = evaluator() - var checked = data.isChecked ? !! val : val + "" === element.value - element.oldValue = checked - if (IE6) { - setTimeout(function() { - //IE8 checkbox, radio是使用defaultChecked控制选中状态, - //并且要先设置defaultChecked后设置checked - //并且必须设置延迟 - element.defaultChecked = checked - element.checked = checked - }, 31) - } else { - element.checked = checked - } - } - bound("click", updateVModel) - } else if ($type === "checkbox") { - updateVModel = function() { - if ($elem.data("duplexObserve") !== false) { - var method = element.checked ? "ensure" : "remove" - var array = evaluator() - if (!Array.isArray(array)) { - log("ms-duplex应用于checkbox上要对应一个数组") - array = [array] - } - avalon.Array[method](array, data.pipe(element.value, data, "get")) - callback.call(element, array) - } - } - - data.handler = function() { - var array = [].concat(evaluator()) //强制转换为数组 - element.checked = array.indexOf(data.pipe(element.value, data, "get")) > -1 - } - bound(W3C ? "change" : "click", updateVModel) - } else { - var events = element.getAttribute("data-duplex-event") || "input" - if (element.attributes["data-event"]) { - log("data-event指令已经废弃,请改用data-duplex-event") - } - - function delay(e) { // jshint ignore:line - setTimeout(function() { - updateVModel(e) - }) - } - events.replace(rword, function(name) { - switch (name) { - case "input": - if (!IEVersion) { // W3C - bound("input", updateVModel) - //非IE浏览器才用这个 - bound("compositionstart", compositionStart) - bound("compositionend", compositionEnd) - bound("DOMAutoComplete", updateVModel) - } else { //onpropertychange事件无法区分是程序触发还是用户触发 - // IE下通过selectionchange事件监听IE9+点击input右边的X的清空行为,及粘贴,剪切,删除行为 - if (IEVersion > 8) { - bound("input", updateVModel) //IE9使用propertychange无法监听中文输入改动 - } else { - bound("propertychange", function(e) { //IE6-8下第一次修改时不会触发,需要使用keydown或selectionchange修正 - if (e.propertyName === "value") { - updateVModel() - } - }) - } - bound("dragend", delay) - //http://www.cnblogs.com/rubylouvre/archive/2013/02/17/2914604.html - //http://www.matts411.com/post/internet-explorer-9-oninput/ - } - break - default: - bound(name, updateVModel) - break - } - }) - bound("focus", function() { - element.msFocus = true - }) - bound("blur", function() { - element.msFocus = false - }) - - if (rmsinput.test($type)) { - watchValueInTimer(function() { - if (avalon.optimize || element.parentNode) { - if (!element.msFocus && element.oldValue !== element.value) { - updateVModel() - } - } else if (!element.msRetain) { - return false - } - }) - } - - element.avalonSetter = updateVModel //#765 - } - - element.oldValue = element.value - avalon.injectBinding(data) - callback.call(element, element.value) - } - duplexBinding.TEXTAREA = duplexBinding.INPUT - duplexBinding.SELECT = function(element, evaluator, data) { - var $elem = avalon(element) - - function updateVModel() { - if ($elem.data("duplexObserve") !== false) { - var val = $elem.val() //字符串或字符串数组 - if (Array.isArray(val)) { - val = val.map(function(v) { - return data.pipe(v, data, "get") - }) - } else { - val = data.pipe(val, data, "get") - } - if (val + "" !== element.oldValue) { - evaluator(val) - } - data.changed.call(element, val, data) - } - } - data.handler = function() { - var val = evaluator() - val = val && val.$model || val - if (Array.isArray(val)) { - if (!element.multiple) { - log("ms-duplex在不能对应一个数组") - } - } - //必须变成字符串后才能比较 - val = Array.isArray(val) ? val.map(String) : val + "" - if (val + "" !== element.oldValue) { - $elem.val(val) - element.oldValue = val + "" - } - } - data.bound("change", updateVModel) - element.msCallback = function() { - avalon.injectBinding(data) - data.changed.call(element, evaluator(), data) - } - } -// bindingHandlers.html 定义在if.js - bindingExecutors.html = function (val, elem, data) { - var isHtmlFilter = elem.nodeType !== 1 - var parent = isHtmlFilter ? elem.parentNode : elem - if (!parent) - return - val = val == null ? "" : val - if (data.oldText !== val) { - data.oldText = val - } else { - return - } - if (elem.nodeType === 3) { - var signature = generateID("html") - parent.insertBefore(DOC.createComment(signature), elem) - data.element = DOC.createComment(signature + ":end") - parent.replaceChild(data.element, elem) - elem = data.element - } - if (typeof val !== "object") {//string, number, boolean - var fragment = avalon.parseHTML(String(val)) - } else if (val.nodeType === 11) { //将val转换为文档碎片 - fragment = val - } else if (val.nodeType === 1 || val.item) { - var nodes = val.nodeType === 1 ? val.childNodes : val.item - fragment = avalonFragment.cloneNode(true) - while (nodes[0]) { - fragment.appendChild(nodes[0]) - } - } - - nodes = avalon.slice(fragment.childNodes) - //插入占位符, 如果是过滤器,需要有节制地移除指定的数量,如果是html指令,直接清空 - if (isHtmlFilter) { - var endValue = elem.nodeValue.slice(0, -4) - while (true) { - var node = elem.previousSibling - if (!node || node.nodeType === 8 && node.nodeValue === endValue) { - break - } else { - parent.removeChild(node) - } - } - parent.insertBefore(fragment, elem) - } else { - avalon.clearHTML(elem).appendChild(fragment) - } - scanNodeArray(nodes, data.vmodels) - } - bindingHandlers["if"] = - bindingHandlers.data = - bindingHandlers.text = - bindingHandlers.html = - function(data, vmodels) { - parseExprProxy(data.value, vmodels, data) - } - - bindingExecutors["if"] = function(val, elem, data) { - try { - if(!elem.parentNode) return - } catch(e) {return} - if (val) { //插回DOM树 - if (elem.nodeType === 8) { - elem.parentNode.replaceChild(data.template, elem) - // animate.enter(data.template, elem.parentNode) - elem = data.element = data.template //这时可能为null - } - if (elem.getAttribute(data.name)) { - elem.removeAttribute(data.name) - scanAttr(elem, data.vmodels) - } - data.rollback = null - } else { //移出DOM树,并用注释节点占据原位置 - if (elem.nodeType === 1) { - var node = data.element = DOC.createComment("ms-if") - elem.parentNode.replaceChild(node, elem) - // animate.leave(elem, node.parentNode, node) - data.template = elem //元素节点 - ifGroup.appendChild(elem) - data.rollback = function() { - if (elem.parentNode === ifGroup) { - ifGroup.removeChild(elem) - } - } - } - } - } -//ms-important绑定已经在scanTag 方法中实现 -//ms-include绑定已由ms-attr绑定实现 - - var rdash = /\(([^)]*)\)/ - bindingHandlers.on = function(data, vmodels) { - var value = data.value - data.type = "on" - var eventType = data.param.replace(/-\d+$/, "") // ms-on-mousemove-10 - if (typeof bindingHandlers.on[eventType + "Hook"] === "function") { - bindingHandlers.on[eventType + "Hook"](data) - } - if (value.indexOf("(") > 0 && value.indexOf(")") > -1) { - var matched = (value.match(rdash) || ["", ""])[1].trim() - if (matched === "" || matched === "$event") { // aaa() aaa($event)当成aaa处理 - value = value.replace(rdash, "") - } - } - parseExprProxy(value, vmodels, data) - } - - bindingExecutors.on = function(callback, elem, data) { - callback = function(e) { - var fn = data.evaluator || noop - return fn.apply(this, data.args.concat(e)) - } - var eventType = data.param.replace(/-\d+$/, "") // ms-on-mousemove-10 - if (eventType === "scan") { - callback.call(elem, { - type: eventType - }) - } else if (typeof data.specialBind === "function") { - data.specialBind(elem, callback) - } else { - var removeFn = avalon.bind(elem, eventType, callback) - } - data.rollback = function() { - if (typeof data.specialUnbind === "function") { - data.specialUnbind() - } else { - avalon.unbind(elem, eventType, removeFn) - } - } - } - bindingHandlers.repeat = function (data, vmodels) { - var type = data.type - parseExprProxy(data.value, vmodels, data, 0, 1) - var freturn = false - try { - var $repeat = data.$repeat = data.evaluator.apply(0, data.args || []) - var xtype = avalon.type($repeat) - if (xtype !== "object" && xtype !== "array") { - freturn = true - avalon.log("warning:" + data.value + "只能是对象或数组") - } - } catch (e) { - freturn = true - } - var arr = data.value.split(".") || [] - if (arr.length > 1) { - arr.pop() - var n = arr[0] - for (var i = 0, v; v = vmodels[i++]; ) { - if (v && v.hasOwnProperty(n)) { - var events = v[n].$events || {} - events[subscribers] = events[subscribers] || [] - events[subscribers].push(data) - break - } - } - } - - var elem = data.element - if (elem.nodeType === 1) { - elem.removeAttribute(data.name) - data.sortedCallback = getBindingCallback(elem, "data-with-sorted", vmodels) - data.renderedCallback = getBindingCallback(elem, "data-" + type + "-rendered", vmodels) - var signature = generateID(type) - var start = DOC.createComment(signature) - var end = DOC.createComment(signature + ":end") - data.signature = signature - data.template = avalonFragment.cloneNode(false) - if (type === "repeat") { - var parent = elem.parentNode - parent.replaceChild(end, elem) - parent.insertBefore(start, end) - data.template.appendChild(elem) - } else { - while (elem.firstChild) { - data.template.appendChild(elem.firstChild) - } - elem.appendChild(start) - elem.appendChild(end) - } - data.element = end - data.handler = bindingExecutors.repeat - data.rollback = function () { - var elem = data.element - if (!elem) - return - data.handler("clear") - } - } - - if (freturn) { - return - } - - data.$outer = {} - var check0 = "$key" - var check1 = "$val" - if (Array.isArray($repeat)) { - if (!$repeat.$map) { - $repeat.$map = { - el: 1 - } - var m = $repeat.length - var $proxy = [] - for (i = 0; i < m; i++) {//生成代理VM - $proxy.push(eachProxyAgent(i, $repeat)) - } - $repeat.$proxy = $proxy - } - $repeat.$map[data.param || "el"] = 1 - check0 = "$first" - check1 = "$last" - } - for (i = 0; v = vmodels[i++]; ) { - if (v.hasOwnProperty(check0) && v.hasOwnProperty(check1)) { - data.$outer = v - break - } - } - var $events = $repeat.$events - var $list = ($events || {})[subscribers] - injectDependency($list, data) - if (xtype === "object") { - data.$with = true - $repeat.$proxy || ($repeat.$proxy = {}) - data.handler("append", $repeat) - } else if ($repeat.length) { - data.handler("add", 0, $repeat.length) - } - } - - bindingExecutors.repeat = function (method, pos, el) { - if (method) { - var data = this, start, fragment - var end = data.element - var comments = getComments(data) - var parent = end.parentNode - var transation = avalonFragment.cloneNode(false) - switch (method) { - case "add": //在pos位置后添加el数组(pos为插入位置,el为要插入的个数) - var n = pos + el - var fragments = [] - var array = data.$repeat - for (var i = pos; i < n; i++) { - var proxy = array.$proxy[i] - proxy.$outer = data.$outer - shimController(data, transation, proxy, fragments) - } - var now = new Date - 0 - avalon.optimize = avalon.optimize || now - for (i = 0; fragment = fragments[i++]; ) { - scanNodeArray(fragment.nodes, fragment.vmodels) - fragment.nodes = fragment.vmodels = null - } - if(avalon.optimize === now){ - delete avalon.optimize - } - parent.insertBefore(transation, comments[pos] || end) - avalon.profile("插入操作花费了 "+ (new Date - now)) - break - case "del": //将pos后的el个元素删掉(pos, el都是数字) - sweepNodes(comments[pos], comments[pos + el] || end) - break - case "clear": - start = comments[0] - if (start) { - sweepNodes(start, end) - } - break - case "move": - start = comments[0] - if (start) { - var signature = start.nodeValue - var rooms = [] - var room = [], - node - sweepNodes(start, end, function () { - room.unshift(this) - if (this.nodeValue === signature) { - rooms.unshift(room) - room = [] - } - }) - sortByIndex(rooms, pos) - while (room = rooms.shift()) { - while (node = room.shift()) { - transation.appendChild(node) - } - } - parent.insertBefore(transation, end) - } - break - case "append": - var object = pos //原来第2参数, 被循环对象 - var pool = object.$proxy //代理对象组成的hash - var keys = [] - fragments = [] - for (var key in pool) { - if (!object.hasOwnProperty(key)) { - proxyRecycler(pool[key], withProxyPool) //去掉之前的代理VM - delete(pool[key]) - } - } - for (key in object) { //得到所有键名 - if (object.hasOwnProperty(key) && key !== "hasOwnProperty" && key !== "$proxy") { - keys.push(key) - } - } - if (data.sortedCallback) { //如果有回调,则让它们排序 - var keys2 = data.sortedCallback.call(parent, keys) - if (keys2 && Array.isArray(keys2) && keys2.length) { - keys = keys2 - } - } - - for (i = 0; key = keys[i++]; ) { - if (key !== "hasOwnProperty") { - pool[key] = withProxyAgent(pool[key], key, data) - shimController(data, transation, pool[key], fragments) - } - } - - parent.insertBefore(transation, end) - for (i = 0; fragment = fragments[i++]; ) { - scanNodeArray(fragment.nodes, fragment.vmodels) - fragment.nodes = fragment.vmodels = null - } - break - } - if (method === "clear") - method = "del" - var callback = data.renderedCallback || noop, - args = arguments - checkScan(parent, function () { - callback.apply(parent, args) - if (parent.oldValue && parent.tagName === "SELECT") { //fix #503 - avalon(parent).val(parent.oldValue.split(",")) - } - }, NaN) - } - } - - "with,each".replace(rword, function (name) { - bindingHandlers[name] = bindingHandlers.repeat - }) - avalon.pool = eachProxyPool - function shimController(data, transation, proxy, fragments) { - var content = data.template.cloneNode(true) - var nodes = avalon.slice(content.childNodes) - if (!data.$with) { - content.insertBefore(DOC.createComment(data.signature), content.firstChild) - } - transation.appendChild(content) - var nv = [proxy].concat(data.vmodels) - var fragment = { - nodes: nodes, - vmodels: nv - } - fragments.push(fragment) - } - - function getComments(data) { - var end = data.element - var signature = end.nodeValue.replace(":end", "") - var node = end.previousSibling - var array = [] - while (node) { - if (node.nodeValue === signature) { - array.unshift(node) - } - node = node.previousSibling - } - return array - } - - -//移除掉start与end之间的节点(保留end) - function sweepNodes(start, end, callback) { - while (true) { - var node = end.previousSibling - if (!node) - break - node.parentNode.removeChild(node) - callback && callback.call(node) - if (node === start) { - break - } - } - } - -// 为ms-each,ms-with, ms-repeat会创建一个代理VM, -// 通过它们保持一个下上文,让用户能调用$index,$first,$last,$remove,$key,$val,$outer等属性与方法 -// 所有代理VM的产生,消费,收集,存放通过xxxProxyFactory,xxxProxyAgent, recycleProxies,xxxProxyPool实现 - var withProxyPool = [] - function withProxyFactory() { - var proxy = modelFactory({ - $key: "", - $outer: {}, - $host: {}, - $val: { - get: function () { - return this.$host[this.$key] - }, - set: function (val) { - this.$host[this.$key] = val - } - } - }, { - $val: 1 - }) - proxy.$id = generateID("$proxy$with") - return proxy - } - - function withProxyAgent(proxy, key, data) { - proxy = proxy || withProxyPool.pop() - if (!proxy) { - proxy = withProxyFactory() - } - var host = data.$repeat - proxy.$key = key - proxy.$host = host - proxy.$outer = data.$outer - if (host.$events) { - proxy.$events.$val = host.$events[key] - } else { - proxy.$events = {} - } - return proxy - } - - function eachProxyRecycler(proxies) { - proxies.forEach(function (proxy) { - proxyRecycler(proxy, eachProxyPool) - }) - proxies.length = 0 - } - - function proxyRecycler(proxy, proxyPool) { - for (var i in proxy.$events) { - if (Array.isArray(proxy.$events[i])) { - proxy.$events[i].forEach(function (data) { - if (typeof data === "object") - disposeData(data) - })// jshint ignore:line - proxy.$events[i].length = 0 - } - } - proxy.$host = proxy.$outer = {} - if (proxyPool.unshift(proxy) > kernel.maxRepeatSize) { - proxyPool.pop() - } - } - /********************************************************************* - * 各种指令 * - **********************************************************************/ -//ms-skip绑定已经在scanTag 方法中实现 -// bindingHandlers.text 定义在if.js - bindingExecutors.text = function(val, elem) { - val = val == null ? "" : val //不在页面上显示undefined null - if (elem.nodeType === 3) { //绑定在文本节点上 - try { //IE对游离于DOM树外的节点赋值会报错 - elem.data = val - } catch (e) {} - } else { //绑定在特性节点上 - if ("textContent" in elem) { - elem.textContent = val - } else { - elem.innerText = val - } - } - } - function parseDisplay(nodeName, val) { - //用于取得此类标签的默认display值 - var key = "_" + nodeName - if (!parseDisplay[key]) { - var node = DOC.createElement(nodeName) - root.appendChild(node) - if (W3C) { - val = getComputedStyle(node, null).display - } else { - val = node.currentStyle.display - } - root.removeChild(node) - parseDisplay[key] = val - } - return parseDisplay[key] - } - - avalon.parseDisplay = parseDisplay - - bindingHandlers.visible = function(data, vmodels) { - var elem = avalon(data.element) - var display = elem.css("display") - if (display === "none") { - var style = elem[0].style - var has = /visibility/i.test(style.cssText) - var visible = elem.css("visibility") - style.display = "" - style.visibility = "hidden" - display = elem.css("display") - if (display === "none") { - display = parseDisplay(elem[0].nodeName) - } - style.visibility = has ? visible : "" - } - data.display = display - parseExprProxy(data.value, vmodels, data) - } - - bindingExecutors.visible = function(val, elem, data) { - elem.style.display = val ? data.display : "none" - } - bindingHandlers.widget = function(data, vmodels) { - var args = data.value.match(rword) - var elem = data.element - var widget = args[0] - var id = args[1] - if (!id || id === "$") { //没有定义或为$时,取组件名+随机数 - id = generateID(widget) - } - var optName = args[2] || widget //没有定义,取组件名 - var constructor = avalon.ui[widget] - if (typeof constructor === "function") { //ms-widget="tabs,tabsAAA,optname" - vmodels = elem.vmodels || vmodels - for (var i = 0, v; v = vmodels[i++];) { - if (v.hasOwnProperty(optName) && typeof v[optName] === "object") { - var vmOptions = v[optName] - vmOptions = vmOptions.$model || vmOptions - break - } - } - if (vmOptions) { - var wid = vmOptions[widget + "Id"] - if (typeof wid === "string") { - log("warning!不再支持" + widget + "Id") - id = wid - } - } - //抽取data-tooltip-text、data-tooltip-attr属性,组成一个配置对象 - var widgetData = avalon.getWidgetData(elem, widget) - data.value = [widget, id, optName].join(",") - data[widget + "Id"] = id - data.evaluator = noop - elem.msData["ms-widget-id"] = id - var options = data[widget + "Options"] = avalon.mix({}, constructor.defaults, vmOptions || {}, widgetData) - elem.removeAttribute("ms-widget") - var vmodel = constructor(elem, data, vmodels) || {} //防止组件不返回VM - if (vmodel.$id) { - avalon.vmodels[id] = vmodel - createSignalTower(elem, vmodel) - try { - vmodel.$init(function() { - avalon.scan(elem, [vmodel].concat(vmodels)) - if (typeof options.onInit === "function") { - options.onInit.call(elem, vmodel, options, vmodels) - } - }) - } catch (e) {} - data.rollback = function() { - try { - vmodel.widgetElement = null - vmodel.$remove() - } catch (e) {} - elem.msData = {} - delete avalon.vmodels[vmodel.$id] - } - injectDisposeQueue(data, widgetList) - if (window.chrome) { - elem.addEventListener("DOMNodeRemovedFromDocument", function() { - setTimeout(rejectDisposeQueue) - }) - } - } else { - avalon.scan(elem, vmodels) - } - } else if (vmodels.length) { //如果该组件还没有加载,那么保存当前的vmodels - elem.vmodels = vmodels - } - } - var widgetList = [] -//不存在 bindingExecutors.widget - /********************************************************************* - * 自带过滤器 * - **********************************************************************/ - var rscripts = /]*>([\S\s]*?)<\/script\s*>/gim - var ron = /\s+(on[^=\s]+)(?:=("[^"]*"|'[^']*'|[^\s>]+))?/g - var ropen = /<\w+\b(?:(["'])[^"]*?(\1)|[^>])*>/ig - var rsanitize = { - a: /\b(href)\=("javascript[^"]*"|'javascript[^']*')/ig, - img: /\b(src)\=("javascript[^"]*"|'javascript[^']*')/ig, - form: /\b(action)\=("javascript[^"]*"|'javascript[^']*')/ig - } - var rsurrogate = /[\uD800-\uDBFF][\uDC00-\uDFFF]/g - var rnoalphanumeric = /([^\#-~| |!])/g; - - function numberFormat(number, decimals, point, thousands) { - //form http://phpjs.org/functions/number_format/ - //number 必需,要格式化的数字 - //decimals 可选,规定多少个小数位。 - //point 可选,规定用作小数点的字符串(默认为 . )。 - //thousands 可选,规定用作千位分隔符的字符串(默认为 , ),如果设置了该参数,那么所有其他参数都是必需的。 - number = (number + '') - .replace(/[^0-9+\-Ee.]/g, '') - var n = !isFinite(+number) ? 0 : +number, - prec = !isFinite(+decimals) ? 3 : Math.abs(decimals), - sep = thousands || ",", - dec = point || ".", - s = '', - toFixedFix = function(n, prec) { - var k = Math.pow(10, prec) - return '' + (Math.round(n * k) / k) - .toFixed(prec) - } - // Fix for IE parseFloat(0.55).toFixed(0) = 0; - s = (prec ? toFixedFix(n, prec) : '' + Math.round(n)) - .split('.') - if (s[0].length > 3) { - s[0] = s[0].replace(/\B(?=(?:\d{3})+(?!\d))/g, sep) - } - if ((s[1] || '') - .length < prec) { - s[1] = s[1] || '' - s[1] += new Array(prec - s[1].length + 1) - .join('0') - } - return s.join(dec) - } - - - var filters = avalon.filters = { - uppercase: function(str) { - return str.toUpperCase() - }, - lowercase: function(str) { - return str.toLowerCase() - }, - truncate: function(str, length, truncation) { - //length,新字符串长度,truncation,新字符串的结尾的字段,返回新字符串 - length = length || 30 - truncation = typeof truncation === "string" ? truncation : "..." - return str.length > length ? str.slice(0, length - truncation.length) + truncation : String(str) - }, - $filter: function(val) { - for (var i = 1, n = arguments.length; i < n; i++) { - var array = arguments[i] - var fn = avalon.filters[array.shift()] - if (typeof fn === "function") { - var arr = [val].concat(array) - val = fn.apply(null, arr) - } - } - return val - }, - camelize: camelize, - //https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet - //
chrome - // chrome - // IE67chrome - // IE67chrome - // IE67chrome - sanitize: function(str) { - return str.replace(rscripts, "").replace(ropen, function(a, b) { - var match = a.toLowerCase().match(/<(\w+)\s/) - if (match) { //处理a标签的href属性,img标签的src属性,form标签的action属性 - var reg = rsanitize[match[1]] - if (reg) { - a = a.replace(reg, function(s, name, value) { - var quote = value.charAt(0) - return name + "=" + quote + "javascript:void(0)" + quote// jshint ignore:line - }) - } - } - return a.replace(ron, " ").replace(/\s+/g, " ") //移除onXXX事件 - }) - }, - escape: function(str) { - //将字符串经过 str 转义得到适合在页面中显示的内容, 例如替换 < 为 < - return String(str). - replace(/&/g, '&'). - replace(rsurrogate, function(value) { - var hi = value.charCodeAt(0) - var low = value.charCodeAt(1) - return '&#' + (((hi - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000) + ';' - }). - replace(rnoalphanumeric, function(value) { - return '&#' + value.charCodeAt(0) + ';' - }). - replace(//g, '>') - }, - currency: function(amount, symbol, fractionSize) { - return (symbol || "\uFFE5") + numberFormat(amount, isFinite(fractionSize) ? fractionSize : 2) - }, - number: numberFormat - } - /* - 'yyyy': 4 digit representation of year (e.g. AD 1 => 0001, AD 2010 => 2010) - 'yy': 2 digit representation of year, padded (00-99). (e.g. AD 2001 => 01, AD 2010 => 10) - 'y': 1 digit representation of year, e.g. (AD 1 => 1, AD 199 => 199) - 'MMMM': Month in year (January-December) - 'MMM': Month in year (Jan-Dec) - 'MM': Month in year, padded (01-12) - 'M': Month in year (1-12) - 'dd': Day in month, padded (01-31) - 'd': Day in month (1-31) - 'EEEE': Day in Week,(Sunday-Saturday) - 'EEE': Day in Week, (Sun-Sat) - 'HH': Hour in day, padded (00-23) - 'H': Hour in day (0-23) - 'hh': Hour in am/pm, padded (01-12) - 'h': Hour in am/pm, (1-12) - 'mm': Minute in hour, padded (00-59) - 'm': Minute in hour (0-59) - 'ss': Second in minute, padded (00-59) - 's': Second in minute (0-59) - 'a': am/pm marker - 'Z': 4 digit (+sign) representation of the timezone offset (-1200-+1200) - format string can also be one of the following predefined localizable formats: - - 'medium': equivalent to 'MMM d, y h:mm:ss a' for en_US locale (e.g. Sep 3, 2010 12:05:08 pm) - 'short': equivalent to 'M/d/yy h:mm a' for en_US locale (e.g. 9/3/10 12:05 pm) - 'fullDate': equivalent to 'EEEE, MMMM d,y' for en_US locale (e.g. Friday, September 3, 2010) - 'longDate': equivalent to 'MMMM d, y' for en_US locale (e.g. September 3, 2010 - 'mediumDate': equivalent to 'MMM d, y' for en_US locale (e.g. Sep 3, 2010) - 'shortDate': equivalent to 'M/d/yy' for en_US locale (e.g. 9/3/10) - 'mediumTime': equivalent to 'h:mm:ss a' for en_US locale (e.g. 12:05:08 pm) - 'shortTime': equivalent to 'h:mm a' for en_US locale (e.g. 12:05 pm) - */ - new function() {// jshint ignore:line - function toInt(str) { - return parseInt(str, 10) || 0 - } - - function padNumber(num, digits, trim) { - var neg = "" - if (num < 0) { - neg = '-' - num = -num - } - num = "" + num - while (num.length < digits) - num = "0" + num - if (trim) - num = num.substr(num.length - digits) - return neg + num - } - - function dateGetter(name, size, offset, trim) { - return function(date) { - var value = date["get" + name]() - if (offset > 0 || value > -offset) - value += offset - if (value === 0 && offset === -12) { - value = 12 - } - return padNumber(value, size, trim) - } - } - - function dateStrGetter(name, shortForm) { - return function(date, formats) { - var value = date["get" + name]() - var get = (shortForm ? ("SHORT" + name) : name).toUpperCase() - return formats[get][value] - } - } - - function timeZoneGetter(date) { - var zone = -1 * date.getTimezoneOffset() - var paddedZone = (zone >= 0) ? "+" : "" - paddedZone += padNumber(Math[zone > 0 ? "floor" : "ceil"](zone / 60), 2) + padNumber(Math.abs(zone % 60), 2) - return paddedZone - } - //取得上午下午 - - function ampmGetter(date, formats) { - return date.getHours() < 12 ? formats.AMPMS[0] : formats.AMPMS[1] - } - var DATE_FORMATS = { - yyyy: dateGetter("FullYear", 4), - yy: dateGetter("FullYear", 2, 0, true), - y: dateGetter("FullYear", 1), - MMMM: dateStrGetter("Month"), - MMM: dateStrGetter("Month", true), - MM: dateGetter("Month", 2, 1), - M: dateGetter("Month", 1, 1), - dd: dateGetter("Date", 2), - d: dateGetter("Date", 1), - HH: dateGetter("Hours", 2), - H: dateGetter("Hours", 1), - hh: dateGetter("Hours", 2, -12), - h: dateGetter("Hours", 1, -12), - mm: dateGetter("Minutes", 2), - m: dateGetter("Minutes", 1), - ss: dateGetter("Seconds", 2), - s: dateGetter("Seconds", 1), - sss: dateGetter("Milliseconds", 3), - EEEE: dateStrGetter("Day"), - EEE: dateStrGetter("Day", true), - a: ampmGetter, - Z: timeZoneGetter - } - var rdateFormat = /((?:[^yMdHhmsaZE']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+|H+|h+|m+|s+|a|Z))(.*)/ - var raspnetjson = /^\/Date\((\d+)\)\/$/ - filters.date = function(date, format) { - var locate = filters.date.locate, - text = "", - parts = [], - fn, match - format = format || "mediumDate" - format = locate[format] || format - if (typeof date === "string") { - if (/^\d+$/.test(date)) { - date = toInt(date) - } else if (raspnetjson.test(date)) { - date = +RegExp.$1 - } else { - var trimDate = date.trim() - var dateArray = [0, 0, 0, 0, 0, 0, 0] - var oDate = new Date(0) - //取得年月日 - trimDate = trimDate.replace(/^(\d+)\D(\d+)\D(\d+)/, function(_, a, b, c) { - var array = c.length === 4 ? [c, a, b] : [a, b, c] - dateArray[0] = toInt(array[0]) //年 - dateArray[1] = toInt(array[1]) - 1 //月 - dateArray[2] = toInt(array[2]) //日 - return "" - }) - var dateSetter = oDate.setFullYear - var timeSetter = oDate.setHours - trimDate = trimDate.replace(/[T\s](\d+):(\d+):?(\d+)?\.?(\d)?/, function(_, a, b, c, d) { - dateArray[3] = toInt(a) //小时 - dateArray[4] = toInt(b) //分钟 - dateArray[5] = toInt(c) //秒 - if (d) { //毫秒 - dateArray[6] = Math.round(parseFloat("0." + d) * 1000) - } - return "" - }) - var tzHour = 0 - var tzMin = 0 - trimDate = trimDate.replace(/Z|([+-])(\d\d):?(\d\d)/, function(z, symbol, c, d) { - dateSetter = oDate.setUTCFullYear - timeSetter = oDate.setUTCHours - if (symbol) { - tzHour = toInt(symbol + c) - tzMin = toInt(symbol + d) - } - return "" - }) - - dateArray[3] -= tzHour - dateArray[4] -= tzMin - dateSetter.apply(oDate, dateArray.slice(0, 3)) - timeSetter.apply(oDate, dateArray.slice(3)) - date = oDate - } - } - if (typeof date === "number") { - date = new Date(date) - } - if (avalon.type(date) !== "date") { - return - } - while (format) { - match = rdateFormat.exec(format) - if (match) { - parts = parts.concat(match.slice(1)) - format = parts.pop() - } else { - parts.push(format) - format = null - } - } - parts.forEach(function(value) { - fn = DATE_FORMATS[value] - text += fn ? fn(date, locate) : value.replace(/(^'|'$)/g, "").replace(/''/g, "'") - }) - return text - } - var locate = { - AMPMS: { - 0: "上午", - 1: "下午" - }, - DAY: { - 0: "星期日", - 1: "星期一", - 2: "星期二", - 3: "星期三", - 4: "星期四", - 5: "星期五", - 6: "星期六" - }, - MONTH: { - 0: "1月", - 1: "2月", - 2: "3月", - 3: "4月", - 4: "5月", - 5: "6月", - 6: "7月", - 7: "8月", - 8: "9月", - 9: "10月", - 10: "11月", - 11: "12月" - }, - SHORTDAY: { - "0": "周日", - "1": "周一", - "2": "周二", - "3": "周三", - "4": "周四", - "5": "周五", - "6": "周六" - }, - fullDate: "y年M月d日EEEE", - longDate: "y年M月d日", - medium: "yyyy-M-d H:mm:ss", - mediumDate: "yyyy-M-d", - mediumTime: "H:mm:ss", - "short": "yy-M-d ah:mm", - shortDate: "yy-M-d", - shortTime: "ah:mm" - } - locate.SHORTMONTH = locate.MONTH - filters.date.locate = locate - }// jshint ignore:line - /********************************************************************* - * AMD加载器 * - **********************************************************************/ -//https://www.devbridge.com/articles/understanding-amd-requirejs/ -//http://maxogden.com/nested-dependencies.html - /*var modules = avalon.modules = { - "domReady!": { - exports: avalon, - state: 3 - }, - "avalon": { - exports: avalon, - state: 4 - } - } -//Object(modules[id]).state拥有如下值 -// undefined 没有定义 -// 1(send) 已经发出请求 -// 2(loading) 已经被执行但还没有执行完成,在这个阶段define方法会被执行 -// 3(loaded) 执行完毕,通过onload/onreadystatechange回调判定,在这个阶段checkDeps方法会执行 -// 4(execute) 其依赖也执行完毕, 值放到exports对象上,在这个阶段fireFactory方法会执行 - modules.exports = modules.avalon - - new function () {// jshint ignore:line - var loadings = [] //正在加载中的模块列表 - var factorys = [] //放置define方法的factory函数 - var rjsext = /\.js$/i - function makeRequest(name, config) { -//1. 去掉资源前缀 - var res = "js" - name = name.replace(/^(\w+)\!/, function (a, b) { - res = b - return "" - }) - if (res === "ready") { - log("debug: ready!已经被废弃,请使用domReady!") - res = "domReady" - } -//2. 去掉querystring, hash - var query = "" - name = name.replace(rquery, function (a) { - query = a - return "" - }) - //3. 去掉扩展名 - var suffix = "." + res - var ext = /js|css/.test(suffix) ? suffix : "" - name = name.replace(/\.[a-z0-9]+$/g, function (a) { - if (a === suffix) { - ext = a - return "" - } else { - return a - } - }) - var req = avalon.mix({ - query: query, - ext: ext, - res: res, - name: name, - toUrl: toUrl - }, config) - req.toUrl(name) - return req - } - - function fireRequest(req) { - var name = req.name - var res = req.res - //1. 如果该模块已经发出请求,直接返回 - var module = modules[name] - var urlNoQuery = name && req.urlNoQuery - if (module && module.state >= 1) { - return name - } - module = modules[urlNoQuery] - if (module && module.state >= 3) { - innerRequire(module.deps || [], module.factory, urlNoQuery) - return urlNoQuery - } - if (name && !module) { - module = modules[urlNoQuery] = { - id: urlNoQuery, - state: 1 //send - } - var wrap = function (obj) { - resources[res] = obj - obj.load(name, req, function (a) { - if (arguments.length && a !== void 0) { - module.exports = a - } - module.state = 4 - checkDeps() - }) - } - - if (!resources[res]) { - innerRequire([res], wrap) - } else { - wrap(resources[res]) - } - } - return name ? urlNoQuery : res + "!" - } - -//核心API之一 require - var requireQueue = [] - var isUserFirstRequire = false - innerRequire = avalon.require = function (array, factory, parentUrl, defineConfig) { - if (!isUserFirstRequire) { - requireQueue.push(avalon.slice(arguments)) - if (arguments.length <= 2) { - isUserFirstRequire = true - var queue = requireQueue.splice(0, requireQueue.length), args - while (args = queue.shift()) { - innerRequire.apply(null, args) - } - } - return - } - - if (!Array.isArray(array)) { - avalon.error("require方法的第一个参数应为数组 " + array) - } - var deps = [] // 放置所有依赖项的完整路径 - var uniq = {} - var id = parentUrl || "callback" + setTimeout("1")// jshint ignore:line - defineConfig = defineConfig || {} - defineConfig.baseUrl = kernel.baseUrl - var isBuilt = !!defineConfig.built - if (parentUrl) { - defineConfig.parentUrl = parentUrl.substr(0, parentUrl.lastIndexOf("/")) - defineConfig.mapUrl = parentUrl.replace(rjsext, "") - } - if (isBuilt) { - var req = makeRequest(defineConfig.defineName, defineConfig) - id = req.urlNoQuery - } else { - array.forEach(function (name) { - var req = makeRequest(name, defineConfig) - var url = fireRequest(req) //加载资源,并返回该资源的完整地址 - if (url) { - if (!uniq[url]) { - deps.push(url) - uniq[url] = "司徒正美" //去重 - } - } - }) - } - - var module = modules[id] - if (!module || module.state !== 4) { - modules[id] = { - id: id, - deps: isBuilt ? array.concat() : deps, - factory: factory || noop, - state: 3 - } - } - if (!module) { - //如果此模块是定义在另一个JS文件中, 那必须等该文件加载完毕, 才能放到检测列队中 - loadings.push(id) - } - checkDeps() - } - -//核心API之二 require - innerRequire.define = function (name, deps, factory) { //模块名,依赖列表,模块本身 - if (typeof name !== "string") { - factory = deps - deps = name - name = "anonymous" - } - if (!Array.isArray(deps)) { - factory = deps - deps = [] - } - var config = { - built: !isUserFirstRequire, //用r.js打包后,所有define会放到requirejs之前 - defineName: name - } - var args = [deps, factory, config] - factory.require = function (url) { - args.splice(2, 0, url) - if (modules[url]) { - modules[url].state = 3 //loaded - var isCycle = false - try { - isCycle = checkCycle(modules[url].deps, url) - } catch (e) { - } - if (isCycle) { - avalon.error(url + "模块与之前的模块存在循环依赖,请不要直接用script标签引入" + url + "模块") - } - } - delete factory.require //释放内存 - innerRequire.apply(null, args) //0,1,2 --> 1,2,0 - } -//根据标准,所有遵循W3C标准的浏览器,script标签会按标签的出现顺序执行。 -//老的浏览器中,加载也是按顺序的:一个文件下载完成后,才开始下载下一个文件。 -//较新的浏览器中(IE8+ 、FireFox3.5+ 、Chrome4+ 、Safari4+),为了减小请求时间以优化体验, -//下载可以是并行的,但是执行顺序还是按照标签出现的顺序。 -//但如果script标签是动态插入的, 就未必按照先请求先执行的原则了,目测只有firefox遵守 -//唯一比较一致的是,IE10+及其他标准浏览器,一旦开始解析脚本, 就会一直堵在那里,直接脚本解析完毕 -//亦即,先进入loading阶段的script标签(模块)必然会先进入loaded阶段 - var url = config.built ? "unknown" : getCurrentScript() - if (url) { - var module = modules[url] - if (module) { - module.state = 2 - } - factory.require(url) - } else {//合并前后的safari,合并后的IE6-9走此分支 - factorys.push(factory) - } - } -//核心API之三 require.config(settings) - innerRequire.config = kernel - //核心API之四 define.amd 标识其符合AMD规范 - innerRequire.define.amd = modules - - //==========================对用户配置项进行再加工========================== - var allpaths = kernel["orig.paths"] = {} - var allmaps = kernel["orig.map"] = {} - var allpackages = kernel["packages"] = [] - var allargs = kernel["orig.args"] = {} - avalon.mix(plugins, { - paths: function (hash) { - avalon.mix(allpaths, hash) - kernel.paths = makeIndexArray(allpaths) - }, - map: function (hash) { - avalon.mix(allmaps, hash) - var list = makeIndexArray(allmaps, 1, 1) - avalon.each(list, function (_, item) { - item.val = makeIndexArray(item.val) - }) - kernel.map = list - }, - packages: function (array) { - array = array.concat(allpackages) - var uniq = {} - var ret = [] - for (var i = 0, pkg; pkg = array[i++]; ) { - pkg = typeof pkg === "string" ? {name: pkg} : pkg - var name = pkg.name - if (!uniq[name]) { - var url = joinPath(pkg.location || name, pkg.main || "main") - url = url.replace(rjsext, "") - ret.push(pkg) - uniq[name] = pkg.location = url - pkg.reg = makeMatcher(name) - } - } - kernel.packages = ret.sort() - }, - urlArgs: function (hash) { - if (typeof hash === "string") { - hash = {"*": hash} - } - avalon.mix(allargs, hash) - kernel.urlArgs = makeIndexArray(allargs, 1) - }, - baseUrl: function (url) { - if (!isAbsUrl(url)) { - var baseElement = head.getElementsByTagName("base")[0] - if (baseElement) { - head.removeChild(baseElement) - } - var node = DOC.createElement("a") - node.href = url - url = getFullUrl(node, "href") - if (baseElement) { - head.insertBefore(baseElement, head.firstChild) - } - } - if (url.length > 3) - kernel.baseUrl = url - }, - shim: function (obj) { - for (var i in obj) { - var value = obj[i] - if (Array.isArray(value)) { - value = obj[i] = { - deps: value - } - } - if (!value.exportsFn && (value.exports || value.init)) { - value.exportsFn = makeExports(value) - } - } - kernel.shim = obj - } - - }) - - - //==============================内部方法================================= - function checkCycle(deps, nick) { - //检测是否存在循环依赖 - for (var i = 0, id; id = deps[i++]; ) { - if (modules[id].state !== 4 && - (id === nick || checkCycle(modules[id].deps, nick))) { - return true - } - } - } - - function checkFail(node, onError, fuckIE) { - var id = trimQuery(node.src) //检测是否死链 - node.onload = node.onreadystatechange = node.onerror = null - if (onError || (fuckIE && modules[id] && !modules[id].state)) { - setTimeout(function () { - head.removeChild(node) - node = null // 处理旧式IE下的循环引用问题 - }) - log("debug: 加载 " + id + " 失败" + onError + " " + (!modules[id].state)) - } else { - return true - } - } - - function checkDeps() { - //检测此JS模块的依赖是否都已安装完毕,是则安装自身 - loop: for (var i = loadings.length, id; id = loadings[--i]; ) { - var obj = modules[id], - deps = obj.deps - if (!deps) - continue - for (var j = 0, key; key = deps[j]; j++) { - if (Object(modules[key]).state !== 4) { - continue loop - } - } - //如果deps是空对象或者其依赖的模块的状态都是2 - if (obj.state !== 4) { - loadings.splice(i, 1) //必须先移除再安装,防止在IE下DOM树建完后手动刷新页面,会多次执行它 - fireFactory(obj.id, obj.deps, obj.factory) - checkDeps() //如果成功,则再执行一次,以防有些模块就差本模块没有安装好 - } - } - } - - var rreadyState = /complete|loaded/ - function loadJS(url, id, callback) { - //通过script节点加载目标模块 - var node = DOC.createElement("script") - node.className = subscribers //让getCurrentScript只处理类名为subscribers的script节点 - var supportLoad = "onload" in node - var onEvent = supportLoad ? "onload" : "onreadystatechange" - function onload() { - var factory = factorys.pop() - factory && factory.require(id) - if (callback) { - callback() - } - if (checkFail(node, false, !supportLoad)) { - log("debug: 已成功加载 " + url) - id && loadings.push(id) - checkDeps() - } - } - var index = 0, loadID - node[onEvent] = supportLoad ? onload : function () { - if (rreadyState.test(node.readyState)) { - ++index - if (index === 1) { - loadID = setTimeout(onload, 500) - } else { - clearTimeout(loadID) - onload() - } - } - } - node.onerror = function () { - checkFail(node, true) - } - - head.insertBefore(node, head.firstChild) //chrome下第二个参数不能为null - node.src = url //插入到head的第一个节点前,防止IE6下head标签没闭合前使用appendChild抛错 - log("debug: 正准备加载 " + url) //更重要的是IE6下可以收窄getCurrentScript的寻找范围 - } - - var resources = innerRequire.plugins = { - //三大常用资源插件 js!, css!, text!, ready! - ready: { - load: noop - }, - js: { - load: function (name, req, onLoad) { - var url = req.url - var id = req.urlNoQuery - var shim = kernel.shim[name.replace(rjsext, "")] - if (shim) { //shim机制 - innerRequire(shim.deps || [], function () { - var args = avalon.slice(arguments) - loadJS(url, id, function () { - onLoad(shim.exportsFn ? shim.exportsFn.apply(0, args) : void 0) - }) - }) - } else { - loadJS(url, id) - } - } - }, - css: { - load: function (name, req, onLoad) { - var url = req.url - var node = DOC.createElement("link") - node.rel = "stylesheet" - node.href = url - head.insertBefore(node, head.firstChild) - log("debug: 已成功加载 " + url) - onLoad() - } - }, - text: { - load: function (name, req, onLoad) { - var url = req.url - var xhr = getXHR() - xhr.onreadystatechange = function () { - if (xhr.readyState === 4) { - var status = xhr.status; - if (status > 399 && status < 600) { - avalon.error(url + " 对应资源不存在或没有开启 CORS") - } else { - log("debug: 已成功加载 " + url) - onLoad(xhr.responseText) - } - } - } - var time = "_=" + (new Date() - 0) - var _url = url.indexOf("?") === -1 ? url + "?" + time : url + "&" + time - xhr.open("GET", _url, true) - if ("withCredentials" in xhr) {//这是处理跨域 - xhr.withCredentials = true - } - xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest")//告诉后端这是AJAX请求 - xhr.send() - log("debug: 正准备加载 " + url) - } - } - } - innerRequire.checkDeps = checkDeps - - var rquery = /(\?[^#]*)$/ - function trimQuery(url) { - return (url || "").replace(rquery, "") - } - - function isAbsUrl(path) { - //http://stackoverflow.com/questions/10687099/how-to-test-if-a-url-string-is-absolute-or-relative - return /^(?:[a-z]+:)?\/\//i.test(String(path)) - } - - function getFullUrl(node, src) { - return"1"[0] ? node[src] : node.getAttribute(src, 4) - } - - function getCurrentScript() { - // inspireb by https://github.com/samyk/jiagra/blob/master/jiagra.js - var stack - try { - a.b.c() //强制报错,以便捕获e.stack - } catch (e) { //safari5的sourceURL,firefox的fileName,它们的效果与e.stack不一样 - stack = e.stack - if (!stack && window.opera) { - //opera 9没有e.stack,但有e.Backtrace,但不能直接取得,需要对e对象转字符串进行抽取 - stack = (String(e).match(/of linked script \S+/g) || []).join(" ") - } - } - if (stack) { - /!**e.stack最后一行在所有支持的浏览器大致如下: - *chrome23: - * at http://113.93.50.63/data.js:4:1 - *firefox17: - *@http://113.93.50.63/query.js:4 - *opera12:http://www.oldapps.com/opera.php?system=Windows_XP - *@http://113.93.50.63/data.js:4 - *IE10: - * at Global code (http://113.93.50.63/data.js:4:1) - * //firefox4+ 可以用document.currentScript - *!/ - stack = stack.split(/[@ ]/g).pop() //取得最后一行,最后一个空格或@之后的部分 - stack = stack[0] === "(" ? stack.slice(1, -1) : stack.replace(/\s/, "") //去掉换行符 - return trimQuery(stack.replace(/(:\d+)?:\d+$/i, "")) //去掉行号与或许存在的出错字符起始位置 - } - var nodes = head.getElementsByTagName("script") //只在head标签中寻找 - for (var i = nodes.length, node; node = nodes[--i]; ) { - if (node.className === subscribers && node.readyState === "interactive") { - var url = getFullUrl(node, "src") - return node.className = trimQuery(url) - } - } - } - - var rcallback = /^callback\d+$/ - function fireFactory(id, deps, factory) { - var module = Object(modules[id]) - module.state = 4 - for (var i = 0, array = [], d; d = deps[i++]; ) { - if (d === "exports") { - var obj = module.exports || (module.exports = {}) - array.push(obj) - } else { - array.push(modules[d].exports) - } - } - try { - var ret = factory.apply(window, array) - } catch (e) { - log("执行[" + id + "]模块的factory抛错: " + e) - } - if (ret !== void 0) { - module.exports = ret - } - if (rcallback.test(id)) { - delete modules[id] - } - delete module.factory - return ret - } - function toUrl(id) { - if (id.indexOf(this.res + "!") === 0) { - id = id.slice(this.res.length + 1) //处理define("css!style",[], function(){})的情况 - } - var url = id - //1. 是否命中paths配置项 - var usePath = 0 - var baseUrl = this.baseUrl - var rootUrl = this.parentUrl || baseUrl - eachIndexArray(id, kernel.paths, function (value, key) { - url = url.replace(key, value) - usePath = 1 - }) - //2. 是否命中packages配置项 - if (!usePath) { - eachIndexArray(id, kernel.packages, function (value, key, item) { - url = url.replace(item.name, item.location) - }) - } - //3. 是否命中map配置项 - if (this.mapUrl) { - eachIndexArray(this.mapUrl, kernel.map, function (array) { - eachIndexArray(url, array, function (mdValue, mdKey) { - url = url.replace(mdKey, mdValue) - rootUrl = baseUrl - }) - }) - } - var ext = this.ext - if (ext && usePath && url.slice(-ext.length) === ext) { - url = url.slice(0, -ext.length) - } - //4. 转换为绝对路径 - if (!isAbsUrl(url)) { - rootUrl = this.built || /^\w/.test(url) ? baseUrl : rootUrl - url = joinPath(rootUrl, url) - } - //5. 还原扩展名,query - var urlNoQuery = url + ext - url = urlNoQuery + this.query - //6. 处理urlArgs - eachIndexArray(id, kernel.urlArgs, function (value) { - url += (url.indexOf("?") === -1 ? "?" : "&") + value; - }) - this.url = url - return this.urlNoQuery = urlNoQuery - } - - function makeIndexArray(hash, useStar, part) { - //创建一个经过特殊算法排好序的数组 - var index = hash2array(hash, useStar, part) - index.sort(descSorterByName) - return index - } - - function makeMatcher(prefix) { - return new RegExp('^' + prefix + '(/|$)') - } - - function makeExports(value) { - return function () { - var ret - if (value.init) { - ret = value.init.apply(window, arguments) - } - return ret || (value.exports && getGlobal(value.exports)) - } - } - - - function hash2array(hash, useStar, part) { - var array = []; - for (var key in hash) { - if (ohasOwn.call(hash, key)) { - var item = { - name: key, - val: hash[key] - } - array.push(item) - item.reg = key === "*" && useStar ? /^/ : makeMatcher(key) - if (part && key !== "*") { - item.reg = new RegExp('\/' + key.replace(/^\//, "") + '(/|$)') - } - } - } - return array - } - - function eachIndexArray(moduleID, array, matcher) { - array = array || [] - for (var i = 0, el; el = array[i++]; ) { - if (el.reg.test(moduleID)) { - matcher(el.val, el.name, el) - return false - } - } - } - // 根据元素的name项进行数组字符数逆序的排序函数 - function descSorterByName(a, b) { - var aaa = a.name - var bbb = b.name - if (bbb === "*") { - return -1 - } - if (aaa === "*") { - return 1 - } - return bbb.length - aaa.length - } - - var rdeuce = /\/\w+\/\.\./ - function joinPath(a, b) { - if (a.charAt(a.length - 1) !== "/") { - a += "/" - } - if (b.slice(0, 2) === "./") { //相对于兄弟路径 - return a + b.slice(2) - } - if (b.slice(0, 2) === "..") { //相对于父路径 - a += b - while (rdeuce.test(a)) { - a = a.replace(rdeuce, "") - } - return a - } - if (b.slice(0, 1) === "/") { - return a + b.slice(1) - } - return a + b - } - - function getGlobal(value) { - if (!value) { - return value - } - var g = window - value.split(".").forEach(function (part) { - g = g[part] - }) - return g - } - - var mainNode = DOC.scripts[DOC.scripts.length - 1] - var dataMain = mainNode.getAttribute("data-main") - if (dataMain) { - plugins.baseUrl(dataMain) - var href = kernel.baseUrl - kernel.baseUrl = href.slice(0, href.lastIndexOf("/") + 1) - loadJS(href.replace(rjsext, "") + ".js") - } else { - var loaderUrl = trimQuery(getFullUrl(mainNode, "src")) - kernel.baseUrl = loaderUrl.slice(0, loaderUrl.lastIndexOf("/") + 1) - } - }// jshint ignore:line*/ - - /********************************************************************* - * DOMReady * - **********************************************************************/ - - var readyList = [], isReady - var fireReady = function(fn) { - isReady = true - if (innerRequire) { - modules["domReady!"].state = 4 - innerRequire.checkDeps() - } - while(fn = readyList.shift()){ - fn(avalon) - } - } - - function doScrollCheck() { - try { //IE下通过doScrollCheck检测DOM树是否建完 - root.doScroll("left") - fireReady() - } catch (e) { - setTimeout(doScrollCheck) - } - } - - if (DOC.readyState === "complete") { - setTimeout(fireReady) //如果在domReady之外加载 - } else if (W3C) { - DOC.addEventListener("DOMContentLoaded", fireReady) - } else { - DOC.attachEvent("onreadystatechange", function() { - if (DOC.readyState === "complete") { - fireReady() - } - }) - try { - var isTop = window.frameElement === null - } catch (e) { - } - if (root.doScroll && isTop && window.external) {//fix IE iframe BUG - doScrollCheck() - } - } - avalon.bind(window, "load", fireReady) - - avalon.ready = function(fn) { - if (!isReady) { - readyList.push(fn) - } else { - fn(avalon) - } - } - - avalon.config({ - loader: false - }) - - avalon.ready(function() { - avalon.scan(DOC.body) - }) - -// Register as a named AMD module, since avalon can be concatenated with other -// files that may use define, but not via a proper concatenation script that -// understands anonymous AMD modules. A named AMD is safest and most robust -// way to register. Lowercase avalon is used because AMD module names are -// derived from file names, and Avalon is normally delivered in a lowercase -// file name. Do this after creating the global so that if an AMD module wants -// to call noConflict to hide this version of avalon, it will work. - -// Note that for maximum portability, libraries that are not avalon should -// declare themselves as anonymous modules, and avoid setting a global if an -// AMD loader is present. avalon is a special case. For more information, see -// https://github.com/jrburke/requirejs/wiki/Updating-existing-libraries#wiki-anon - if (typeof define === "function" && define.amd) { - define("avalon", [], function() { - return avalon - }) - } -// Map over avalon in case of overwrite - var _avalon = window.avalon - avalon.noConflict = function(deep) { - if (deep && window.avalon === avalon) { - window.avalon = _avalon - } - return avalon - } -// Expose avalon identifiers, even in AMD -// and CommonJS for browser emulators - if (noGlobal === void 0) { - window.avalon = avalon - } - return avalon - -})); \ No newline at end of file diff --git a/template/src/admin/announcement/announcement.html b/template/src/admin/announcement/announcement.html index f08ccb0..7bfab36 100644 --- a/template/src/admin/announcement/announcement.html +++ b/template/src/admin/announcement/announcement.html @@ -7,7 +7,6 @@ 创建时间 更新时间 创建者 - 类型 可见 @@ -17,7 +16,6 @@ {{ el.create_time|date("yyyy-MM-dd HH:mm:ss")}} {{ el.last_update_time|date("yyyy-MM-dd HH:mm:ss")}} {{ el.created_by.username }} - @@ -28,12 +26,10 @@
- 页数:{{ page }}/{{ totalPage }}   - - +
-
+

编辑公告

@@ -47,32 +43,14 @@
-
- -
- - 全局可见 - - - 小组内可见 - -
- -
-
- -
-   {{ el.name }} -
-
  
-

添加公告

+

发布公告

@@ -89,31 +67,7 @@
-
- -
- - - - - - -
- -
-
- -
-   {{ el.name }} -
-
diff --git a/utils/templatetags/announcement_list.py b/utils/templatetags/announcement_list.py index 1b2f331..04fa6a5 100644 --- a/utils/templatetags/announcement_list.py +++ b/utils/templatetags/announcement_list.py @@ -5,7 +5,7 @@ from announcement.models import Announcement def public_announcement_list(): - return Announcement.objects.filter(is_global=True, visible=True).order_by("-create_time") + return Announcement.objects.filter(visible=True).order_by("-create_time") register = template.Library() register.assignment_tag(public_announcement_list, name="public_announcement_list") \ No newline at end of file From e7aa9ce9a6a4dc41679d94f9507ace3cdce5020e Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Fri, 16 Oct 2015 20:44:41 +0800 Subject: [PATCH 04/25] =?UTF-8?q?=E5=8D=87=E7=BA=A7=20avalon=20=E5=88=B01.?= =?UTF-8?q?5.4=EF=BC=8C=E5=A2=9E=E5=8A=A0=20pager=20=E7=9A=84=20web=20?= =?UTF-8?q?=E7=BB=84=E4=BB=B6=E3=80=82=E4=BF=AE=E5=A4=8D=E5=8D=87=E7=BA=A7?= =?UTF-8?q?=E5=B8=A6=E6=9D=A5=E7=9A=84=E9=83=A8=E5=88=86=E5=85=BC=E5=AE=B9?= =?UTF-8?q?=E6=80=A7=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- static/src/js/app/admin/admin.js | 12 +- static/src/js/components/pager.js | 32 + static/src/js/config.js | 9 + static/src/js/lib/avalon/avalon.js | 5798 ++++++++++++++++++++++++ static/src/js/lib/avalon/avalon14.js | 5718 +++++++++++++++++++++++ template/src/admin/admin.html | 4 +- template/src/admin/user/user_list.html | 2 +- 7 files changed, 11570 insertions(+), 5 deletions(-) create mode 100644 static/src/js/components/pager.js create mode 100644 static/src/js/lib/avalon/avalon.js create mode 100644 static/src/js/lib/avalon/avalon14.js diff --git a/static/src/js/app/admin/admin.js b/static/src/js/app/admin/admin.js index d68dba1..705edf7 100644 --- a/static/src/js/app/admin/admin.js +++ b/static/src/js/app/admin/admin.js @@ -1,4 +1,4 @@ -require(["jquery", "avalon", "bootstrap"], function ($, avalon) { +require(["jquery", "avalon", "csrfToken", "bsAlert", "bootstrap"], function ($, avalon, csrfTokenHeader, bsAlert) { avalon.ready(function () { @@ -155,7 +155,15 @@ require(["jquery", "avalon", "bootstrap"], function ($, avalon) { show_template("template/" + hash + ".html"); } }; - setTimeout(function(){li_active("#li-" + hash.replace("/", "-"));}, 500) + setTimeout(function(){li_active("#li-" + hash.replace("/", "-"));}, 500); + + $.ajaxSetup({ + beforeSend: csrfTokenHeader, + dataType: "json", + error: function(){ + bsAlert("请求失败"); + } + }); }); diff --git a/static/src/js/components/pager.js b/static/src/js/components/pager.js new file mode 100644 index 0000000..b6918f7 --- /dev/null +++ b/static/src/js/components/pager.js @@ -0,0 +1,32 @@ +define("pager", ["avalon"], function (avalon) { + var _interface = function () { + }; + avalon.component("ms:pager", { + $template: "页数: {{ currentPage }}/{{ totalPage }} " + + " " + + " ", + currentPage: 1, + totalPage: 1, + _getPrevPage: _interface, + _getNextPage: _interface, + $init: function (vm, el) { + vm._getPrevPage = function () { + if (vm.currentPage > 1) { + vm.currentPage--; + vm.getPage(vm.currentPage); + } + }; + vm._getNextPage = function () { + if (vm.currentPage < vm.totalPage) { + vm.currentPage++; + vm.getPage(vm.currentPage); + } + }; + }, + $ready: function(vm, el){ + el.msRetain = true; + vm.getPage(1); + + } + }) +}); \ No newline at end of file diff --git a/static/src/js/config.js b/static/src/js/config.js index c543f5f..67d8c99 100644 --- a/static/src/js/config.js +++ b/static/src/js/config.js @@ -6,6 +6,7 @@ var require = { jquery: "lib/jquery/jquery", jcountdown: "lib/jcountdown/jcountdown", avalon: "lib/avalon/avalon", + //avalon15: "lib/avalon/avalon15", editor: "utils/editor", uploader: "utils/uploader", formValidation: "utils/formValidation", @@ -23,6 +24,9 @@ var require = { validator: "lib/validator/validator", ZeroClipboard: "lib/ZeroClipboard/ZeroClipboard", + // ------ admin web 组件 ---------- + pager: "components/pager", + // ------ 下面写的都不要直接用,而是使用上面的封装版本 ------ //富文本编辑器simditor -> editor @@ -69,5 +73,10 @@ var require = { contestPassword_19_pack: "app/oj/contest/contestPassword", group_20_pack: "app/admin/group/group", submissionList_21_pack: "app/admin/contest/submissionList" + }, + shim: { + avalon: { + exports: "avalon" + } } }; \ No newline at end of file diff --git a/static/src/js/lib/avalon/avalon.js b/static/src/js/lib/avalon/avalon.js new file mode 100644 index 0000000..bb809d5 --- /dev/null +++ b/static/src/js/lib/avalon/avalon.js @@ -0,0 +1,5798 @@ +/*================================================== + Copyright (c) 2013-2015 司徒正美 and other contributors + http://www.cnblogs.com/rubylouvre/ + https://github.com/RubyLouvre + http://weibo.com/jslouvre/ + + Released under the MIT license + avalon.shim.js 1.5.4 built in 2015.10.15 + support IE6+ and other browsers + ==================================================*/ +(function(global, factory) { + + if (typeof module === "object" && typeof module.exports === "object") { + // For CommonJS and CommonJS-like environments where a proper `window` + // is present, execute the factory and get avalon. + // For environments that do not have a `window` with a `document` + // (such as Node.js), expose a factory as module.exports. + // This accentuates the need for the creation of a real `window`. + // e.g. var avalon = require("avalon")(window); + module.exports = global.document ? factory(global, true) : function(w) { + if (!w.document) { + throw new Error("Avalon requires a window with a document") + } + return factory(w) + } + } else { + factory(global) + } + +// Pass this if window is not defined yet +}(typeof window !== "undefined" ? window : this, function(window, noGlobal){ + +/********************************************************************* + * 全局变量及方法 * + **********************************************************************/ +var expose = new Date() - 0 +//http://stackoverflow.com/questions/7290086/javascript-use-strict-and-nicks-find-global-function +var DOC = window.document +var head = DOC.getElementsByTagName("head")[0] //HEAD元素 +var ifGroup = head.insertBefore(document.createElement("avalon"), head.firstChild) //避免IE6 base标签BUG +ifGroup.innerHTML = "X" +ifGroup.setAttribute("ms-skip", "1") +ifGroup.className = "avalonHide" +var rnative = /\[native code\]/ //判定是否原生函数 +function log() { + if (window.console && avalon.config.debug) { + // http://stackoverflow.com/questions/8785624/how-to-safely-wrap-console-log + Function.apply.call(console.log, console, arguments) + } +} + + +var subscribers = "$" + expose + +var stopRepeatAssign = false +var nullObject = {} //作用类似于noop,只用于代码防御,千万不要在它上面添加属性 +var rword = /[^, ]+/g //切割字符串为一个个小块,以空格或豆号分开它们,结合replace实现字符串的forEach +var rw20g = /\w+/g +var rcomplexType = /^(?:object|array)$/ +var rsvg = /^\[object SVG\w*Element\]$/ +var rwindow = /^\[object (?:Window|DOMWindow|global)\]$/ +var oproto = Object.prototype +var ohasOwn = oproto.hasOwnProperty +var serialize = oproto.toString +var ap = Array.prototype +var aslice = ap.slice +var W3C = window.dispatchEvent +var root = DOC.documentElement +var avalonFragment = DOC.createDocumentFragment() +var cinerator = DOC.createElement("div") +var class2type = {} +"Boolean Number String Function Array Date RegExp Object Error".replace(rword, function (name) { + class2type["[object " + name + "]"] = name.toLowerCase() +}) +function scpCompile(array){ + return Function.apply(noop,array) +} +function noop(){} + +function oneObject(array, val) { + if (typeof array === "string") { + array = array.match(rword) || [] + } + var result = {}, + value = val !== void 0 ? val : 1 + for (var i = 0, n = array.length; i < n; i++) { + result[array[i]] = value + } + return result +} + +//生成UUID http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript +var generateID = function (prefix) { + prefix = prefix || "avalon" + return String(Math.random() + Math.random()).replace(/\d\.\d{4}/, prefix) +} +function IE() { + if (window.VBArray) { + var mode = document.documentMode + return mode ? mode : window.XMLHttpRequest ? 7 : 6 + } else { + return NaN + } +} +var IEVersion = IE() + +avalon = function (el) { //创建jQuery式的无new 实例化结构 + return new avalon.init(el) +} + + +/*视浏览器情况采用最快的异步回调*/ +avalon.nextTick = new function () {// jshint ignore:line + var tickImmediate = window.setImmediate + var tickObserver = window.MutationObserver + if (tickImmediate) { + return tickImmediate.bind(window) + } + + var queue = [] + function callback() { + var n = queue.length + for (var i = 0; i < n; i++) { + queue[i]() + } + queue = queue.slice(n) + } + + if (tickObserver) { + var node = document.createTextNode("avalon") + new tickObserver(callback).observe(node, {characterData: true})// jshint ignore:line + var bool = false + return function (fn) { + queue.push(fn) + bool = !bool + node.data = bool + } + } + + + return function (fn) { + setTimeout(fn, 4) + } +}// jshint ignore:line +/********************************************************************* + * avalon的静态方法定义区 * + **********************************************************************/ +avalon.init = function (el) { + this[0] = this.element = el +} +avalon.fn = avalon.prototype = avalon.init.prototype + +avalon.type = function (obj) { //取得目标的类型 + if (obj == null) { + return String(obj) + } + // 早期的webkit内核浏览器实现了已废弃的ecma262v4标准,可以将正则字面量当作函数使用,因此typeof在判定正则时会返回function + return typeof obj === "object" || typeof obj === "function" ? + class2type[serialize.call(obj)] || "object" : + typeof obj +} + +var isFunction = typeof alert === "object" ? function (fn) { + try { + return /^\s*\bfunction\b/.test(fn + "") + } catch (e) { + return false + } +} : function (fn) { + return serialize.call(fn) === "[object Function]" +} +avalon.isFunction = isFunction + +avalon.isWindow = function (obj) { + if (!obj) + return false + // 利用IE678 window == document为true,document == window竟然为false的神奇特性 + // 标准浏览器及IE9,IE10等使用 正则检测 + return obj == obj.document && obj.document != obj //jshint ignore:line +} + +function isWindow(obj) { + return rwindow.test(serialize.call(obj)) +} +if (isWindow(window)) { + avalon.isWindow = isWindow +} +var enu +for (enu in avalon({})) { + break +} +var enumerateBUG = enu !== "0" //IE6下为true, 其他为false +/*判定是否是一个朴素的javascript对象(Object),不是DOM对象,不是BOM对象,不是自定义类的实例*/ +avalon.isPlainObject = function (obj, key) { + if (!obj || avalon.type(obj) !== "object" || obj.nodeType || avalon.isWindow(obj)) { + return false; + } + try { //IE内置对象没有constructor + if (obj.constructor && !ohasOwn.call(obj, "constructor") && !ohasOwn.call(obj.constructor.prototype, "isPrototypeOf")) { + return false; + } + } catch (e) { //IE8 9会在这里抛错 + return false; + } + if (enumerateBUG) { + for (key in obj) { + return ohasOwn.call(obj, key) + } + } + for (key in obj) { + } + return key === void 0 || ohasOwn.call(obj, key) +} +if (rnative.test(Object.getPrototypeOf)) { + avalon.isPlainObject = function (obj) { + // 简单的 typeof obj === "object"检测,会致使用isPlainObject(window)在opera下通不过 + return serialize.call(obj) === "[object Object]" && Object.getPrototypeOf(obj) === oproto + } +} +//与jQuery.extend方法,可用于浅拷贝,深拷贝 +avalon.mix = avalon.fn.mix = function () { + var options, name, src, copy, copyIsArray, clone, + target = arguments[0] || {}, + i = 1, + length = arguments.length, + deep = false + + // 如果第一个参数为布尔,判定是否深拷贝 + if (typeof target === "boolean") { + deep = target + target = arguments[1] || {} + i++ + } + + //确保接受方为一个复杂的数据类型 + if (typeof target !== "object" && !isFunction(target)) { + target = {} + } + + //如果只有一个参数,那么新成员添加于mix所在的对象上 + if (i === length) { + target = this + i-- + } + + for (; i < length; i++) { + //只处理非空参数 + if ((options = arguments[i]) != null) { + for (name in options) { + src = target[name] + try { + copy = options[name] //当options为VBS对象时报错 + } catch (e) { + continue + } + + // 防止环引用 + if (target === copy) { + continue + } + if (deep && copy && (avalon.isPlainObject(copy) || (copyIsArray = Array.isArray(copy)))) { + + if (copyIsArray) { + copyIsArray = false + clone = src && Array.isArray(src) ? src : [] + + } else { + clone = src && avalon.isPlainObject(src) ? src : {} + } + + target[name] = avalon.mix(deep, clone, copy) + } else if (copy !== void 0) { + target[name] = copy + } + } + } + } + return target +} + +function _number(a, len) { //用于模拟slice, splice的效果 + a = Math.floor(a) || 0 + return a < 0 ? Math.max(len + a, 0) : Math.min(a, len); +} +avalon.mix({ + rword: rword, + subscribers: subscribers, + version: 1.54, + ui: {}, + log: log, + slice: W3C ? function (nodes, start, end) { + return aslice.call(nodes, start, end) + } : function (nodes, start, end) { + var ret = [] + var len = nodes.length + if (end === void 0) + end = len + if (typeof end === "number" && isFinite(end)) { + start = _number(start, len) + end = _number(end, len) + for (var i = start; i < end; ++i) { + ret[i - start] = nodes[i] + } + } + return ret + }, + noop: noop, + /*如果不用Error对象封装一下,str在控制台下可能会乱码*/ + error: function (str, e) { + throw (e || Error)(str) + }, + /*将一个以空格或逗号隔开的字符串或数组,转换成一个键值都为1的对象*/ + oneObject: oneObject, + /* avalon.range(10) + => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] + avalon.range(1, 11) + => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + avalon.range(0, 30, 5) + => [0, 5, 10, 15, 20, 25] + avalon.range(0, -10, -1) + => [0, -1, -2, -3, -4, -5, -6, -7, -8, -9] + avalon.range(0) + => []*/ + range: function (start, end, step) { // 用于生成整数数组 + step || (step = 1) + if (end == null) { + end = start || 0 + start = 0 + } + var index = -1, + length = Math.max(0, Math.ceil((end - start) / step)), + result = new Array(length) + while (++index < length) { + result[index] = start + start += step + } + return result + }, + eventHooks: {}, + /*绑定事件*/ + bind: function (el, type, fn, phase) { + var hooks = avalon.eventHooks + var hook = hooks[type] + if (typeof hook === "object") { + type = hook.type || type + phase = hook.phase || !!phase + fn = hook.fn ? hook.fn(el, fn) : fn + } + var callback = W3C ? fn : function (e) { + fn.call(el, fixEvent(e)); + } + if (W3C) { + el.addEventListener(type, callback, phase) + } else { + el.attachEvent("on" + type, callback) + } + return callback + }, + /*卸载事件*/ + unbind: function (el, type, fn, phase) { + var hooks = avalon.eventHooks + var hook = hooks[type] + var callback = fn || noop + if (typeof hook === "object") { + type = hook.type || type + phase = hook.phase || !!phase + } + if (W3C) { + el.removeEventListener(type, callback, phase) + } else { + el.detachEvent("on" + type, callback) + } + }, + /*读写删除元素节点的样式*/ + css: function (node, name, value) { + if (node instanceof avalon) { + node = node[0] + } + var prop = /[_-]/.test(name) ? camelize(name) : name, + fn + name = avalon.cssName(prop) || prop + if (value === void 0 || typeof value === "boolean") { //获取样式 + fn = cssHooks[prop + ":get"] || cssHooks["@:get"] + if (name === "background") { + name = "backgroundColor" + } + var val = fn(node, name) + return value === true ? parseFloat(val) || 0 : val + } else if (value === "") { //请除样式 + node.style[name] = "" + } else { //设置样式 + if (value == null || value !== value) { + return + } + if (isFinite(value) && !avalon.cssNumber[prop]) { + value += "px" + } + fn = cssHooks[prop + ":set"] || cssHooks["@:set"] + fn(node, name, value) + } + }, + /*遍历数组与对象,回调的第一个参数为索引或键名,第二个或元素或键值*/ + each: function (obj, fn) { + if (obj) { //排除null, undefined + var i = 0 + if (isArrayLike(obj)) { + for (var n = obj.length; i < n; i++) { + if (fn(i, obj[i]) === false) + break + } + } else { + for (i in obj) { + if (obj.hasOwnProperty(i) && fn(i, obj[i]) === false) { + break + } + } + } + } + }, + //收集元素的data-{{prefix}}-*属性,并转换为对象 + getWidgetData: function (elem, prefix) { + var raw = avalon(elem).data() + var result = {} + for (var i in raw) { + if (i.indexOf(prefix) === 0) { + result[i.replace(prefix, "").replace(/\w/, function (a) { + return a.toLowerCase() + })] = raw[i] + } + } + return result + }, + Array: { + /*只有当前数组不存在此元素时只添加它*/ + ensure: function (target, item) { + if (target.indexOf(item) === -1) { + return target.push(item) + } + }, + /*移除数组中指定位置的元素,返回布尔表示成功与否*/ + removeAt: function (target, index) { + return !!target.splice(index, 1).length + }, + /*移除数组中第一个匹配传参的那个元素,返回布尔表示成功与否*/ + remove: function (target, item) { + var index = target.indexOf(item) + if (~index) + return avalon.Array.removeAt(target, index) + return false + } + } +}) + +var bindingHandlers = avalon.bindingHandlers = {} +var bindingExecutors = avalon.bindingExecutors = {} + +var directives = avalon.directives = {} +avalon.directive = function (name, obj) { + bindingHandlers[name] = obj.init = (obj.init || noop) + bindingExecutors[name] = obj.update = (obj.update || noop) + + return directives[name] = obj +} +/*判定是否类数组,如节点集合,纯数组,arguments与拥有非负整数的length属性的纯JS对象*/ +function isArrayLike(obj) { + if (!obj) + return false + var n = obj.length + if (n === (n >>> 0)) { //检测length属性是否为非负整数 + var type = serialize.call(obj).slice(8, -1) + if (/(?:regexp|string|function|window|global)$/i.test(type)) + return false + if (type === "Array") + return true + try { + if ({}.propertyIsEnumerable.call(obj, "length") === false) { //如果是原生对象 + return /^\s?function/.test(obj.item || obj.callee) + } + return true + } catch (e) { //IE的NodeList直接抛错 + return !obj.window //IE6-8 window + } + } + return false +} + + +// https://github.com/rsms/js-lru +var Cache = new function() {// jshint ignore:line + function LRU(maxLength) { + this.size = 0 + this.limit = maxLength + this.head = this.tail = void 0 + this._keymap = {} + } + + var p = LRU.prototype + + p.put = function(key, value) { + var entry = { + key: key, + value: value + } + this._keymap[key] = entry + if (this.tail) { + this.tail.newer = entry + entry.older = this.tail + } else { + this.head = entry + } + this.tail = entry + if (this.size === this.limit) { + this.shift() + } else { + this.size++ + } + return value + } + + p.shift = function() { + var entry = this.head + if (entry) { + this.head = this.head.newer + this.head.older = + entry.newer = + entry.older = + this._keymap[entry.key] = void 0 + delete this._keymap[entry.key] //#1029 + } + } + p.get = function(key) { + var entry = this._keymap[key] + if (entry === void 0) + return + if (entry === this.tail) { + return entry.value + } + // HEAD--------------TAIL + // <.older .newer> + // <--- add direction -- + // A B C E + if (entry.newer) { + if (entry === this.head) { + this.head = entry.newer + } + entry.newer.older = entry.older // C <-- E. + } + if (entry.older) { + entry.older.newer = entry.newer // C. --> E + } + entry.newer = void 0 // D --x + entry.older = this.tail // D. --> E + if (this.tail) { + this.tail.newer = entry // E. <-- D + } + this.tail = entry + return entry.value + } + return LRU +}// jshint ignore:line + +/********************************************************************* + * javascript 底层补丁 * + **********************************************************************/ +if (!"司徒正美".trim) { + var rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g + String.prototype.trim = function () { + return this.replace(rtrim, "") + } +} +var hasDontEnumBug = !({ + 'toString': null +}).propertyIsEnumerable('toString'), + hasProtoEnumBug = (function () { + }).propertyIsEnumerable('prototype'), + dontEnums = [ + "toString", + "toLocaleString", + "valueOf", + "hasOwnProperty", + "isPrototypeOf", + "propertyIsEnumerable", + "constructor" + ], + dontEnumsLength = dontEnums.length; +if (!Object.keys) { + Object.keys = function (object) { //ecma262v5 15.2.3.14 + var theKeys = [] + var skipProto = hasProtoEnumBug && typeof object === "function" + if (typeof object === "string" || (object && object.callee)) { + for (var i = 0; i < object.length; ++i) { + theKeys.push(String(i)) + } + } else { + for (var name in object) { + if (!(skipProto && name === "prototype") && ohasOwn.call(object, name)) { + theKeys.push(String(name)) + } + } + } + + if (hasDontEnumBug) { + var ctor = object.constructor, + skipConstructor = ctor && ctor.prototype === object + for (var j = 0; j < dontEnumsLength; j++) { + var dontEnum = dontEnums[j] + if (!(skipConstructor && dontEnum === "constructor") && ohasOwn.call(object, dontEnum)) { + theKeys.push(dontEnum) + } + } + } + return theKeys + } +} +if (!Array.isArray) { + Array.isArray = function (a) { + return serialize.call(a) === "[object Array]" + } +} + +if (!noop.bind) { + Function.prototype.bind = function (scope) { + if (arguments.length < 2 && scope === void 0) + return this + var fn = this, + argv = arguments + return function () { + var args = [], + i + for (i = 1; i < argv.length; i++) + args.push(argv[i]) + for (i = 0; i < arguments.length; i++) + args.push(arguments[i]) + return fn.apply(scope, args) + } + } +} + +function iterator(vars, body, ret) { + var fun = 'for(var ' + vars + 'i=0,n = this.length; i < n; i++){' + body.replace('_', '((i in this) && fn.call(scope,this[i],i,this))') + '}' + ret + /* jshint ignore:start */ + return Function("fn,scope", fun) + /* jshint ignore:end */ +} +if (!rnative.test([].map)) { + avalon.mix(ap, { + //定位操作,返回数组中第一个等于给定参数的元素的索引值。 + indexOf: function (item, index) { + var n = this.length, + i = ~~index + if (i < 0) + i += n + for (; i < n; i++) + if (this[i] === item) + return i + return -1 + }, + //定位操作,同上,不过是从后遍历。 + lastIndexOf: function (item, index) { + var n = this.length, + i = index == null ? n - 1 : index + if (i < 0) + i = Math.max(0, n + i) + for (; i >= 0; i--) + if (this[i] === item) + return i + return -1 + }, + //迭代操作,将数组的元素挨个儿传入一个函数中执行。Prototype.js的对应名字为each。 + forEach: iterator("", '_', ""), + //迭代类 在数组中的每个项上运行一个函数,如果此函数的值为真,则此元素作为新数组的元素收集起来,并返回新数组 + filter: iterator('r=[],j=0,', 'if(_)r[j++]=this[i]', 'return r'), + //收集操作,将数组的元素挨个儿传入一个函数中执行,然后把它们的返回值组成一个新数组返回。Prototype.js的对应名字为collect。 + map: iterator('r=[],', 'r[i]=_', 'return r'), + //只要数组中有一个元素满足条件(放进给定函数返回true),那么它就返回true。Prototype.js的对应名字为any。 + some: iterator("", 'if(_)return true', 'return false'), + //只有数组中的元素都满足条件(放进给定函数返回true),它才返回true。Prototype.js的对应名字为all。 + every: iterator("", 'if(!_)return false', 'return true') + }) +} +/********************************************************************* + * DOM 底层补丁 * + **********************************************************************/ + +function fixContains(root, el) { + try { //IE6-8,游离于DOM树外的文本节点,访问parentNode有时会抛错 + while ((el = el.parentNode)) + if (el === root) + return true + return false + } catch (e) { + return false + } +} +avalon.contains = fixContains +//IE6-11的文档对象没有contains +if (!DOC.contains) { + DOC.contains = function (b) { + return fixContains(DOC, b) + } +} + +function outerHTML() { + return new XMLSerializer().serializeToString(this) +} + +if (window.SVGElement) { + //safari5+是把contains方法放在Element.prototype上而不是Node.prototype + if (!DOC.createTextNode("x").contains) { + Node.prototype.contains = function (arg) {//IE6-8没有Node对象 + return !!(this.compareDocumentPosition(arg) & 16) + } + } + var svgns = "http://www.w3.org/2000/svg" + var svg = DOC.createElementNS(svgns, "svg") + svg.innerHTML = '' + if (!rsvg.test(svg.firstChild)) { // #409 + function enumerateNode(node, targetNode) {// jshint ignore:line + if (node && node.childNodes) { + var nodes = node.childNodes + for (var i = 0, el; el = nodes[i++]; ) { + if (el.tagName) { + var svg = DOC.createElementNS(svgns, + el.tagName.toLowerCase()) + ap.forEach.call(el.attributes, function (attr) { + svg.setAttribute(attr.name, attr.value) //复制属性 + })// jshint ignore:line + // 递归处理子节点 + enumerateNode(el, svg) + targetNode.appendChild(svg) + } + } + } + } + Object.defineProperties(SVGElement.prototype, { + "outerHTML": {//IE9-11,firefox不支持SVG元素的innerHTML,outerHTML属性 + enumerable: true, + configurable: true, + get: outerHTML, + set: function (html) { + var tagName = this.tagName.toLowerCase(), + par = this.parentNode, + frag = avalon.parseHTML(html) + // 操作的svg,直接插入 + if (tagName === "svg") { + par.insertBefore(frag, this) + // svg节点的子节点类似 + } else { + var newFrag = DOC.createDocumentFragment() + enumerateNode(frag, newFrag) + par.insertBefore(newFrag, this) + } + par.removeChild(this) + } + }, + "innerHTML": { + enumerable: true, + configurable: true, + get: function () { + var s = this.outerHTML + var ropen = new RegExp("<" + this.nodeName + '\\b(?:(["\'])[^"]*?(\\1)|[^>])*>', "i") + var rclose = new RegExp("<\/" + this.nodeName + ">$", "i") + return s.replace(ropen, "").replace(rclose, "") + }, + set: function (html) { + if (avalon.clearHTML) { + avalon.clearHTML(this) + var frag = avalon.parseHTML(html) + enumerateNode(frag, this) + } + } + } + }) + } +} +if (!root.outerHTML && window.HTMLElement) { //firefox 到11时才有outerHTML + HTMLElement.prototype.__defineGetter__("outerHTML", outerHTML); +} + + +//============================= event binding ======================= +var rmouseEvent = /^(?:mouse|contextmenu|drag)|click/ +function fixEvent(event) { + var ret = {} + for (var i in event) { + ret[i] = event[i] + } + var target = ret.target = event.srcElement + if (event.type.indexOf("key") === 0) { + ret.which = event.charCode != null ? event.charCode : event.keyCode + } else if (rmouseEvent.test(event.type)) { + var doc = target.ownerDocument || DOC + var box = doc.compatMode === "BackCompat" ? doc.body : doc.documentElement + ret.pageX = event.clientX + (box.scrollLeft >> 0) - (box.clientLeft >> 0) + ret.pageY = event.clientY + (box.scrollTop >> 0) - (box.clientTop >> 0) + ret.wheelDeltaY = ret.wheelDelta + ret.wheelDeltaX = 0 + } + ret.timeStamp = new Date() - 0 + ret.originalEvent = event + ret.preventDefault = function () { //阻止默认行为 + event.returnValue = false + } + ret.stopPropagation = function () { //阻止事件在DOM树中的传播 + event.cancelBubble = true + } + return ret +} + +var eventHooks = avalon.eventHooks +//针对firefox, chrome修正mouseenter, mouseleave +if (!("onmouseenter" in root)) { + avalon.each({ + mouseenter: "mouseover", + mouseleave: "mouseout" + }, function (origType, fixType) { + eventHooks[origType] = { + type: fixType, + fn: function (elem, fn) { + return function (e) { + var t = e.relatedTarget + if (!t || (t !== elem && !(elem.compareDocumentPosition(t) & 16))) { + delete e.type + e.type = origType + return fn.call(elem, e) + } + } + } + } + }) +} +//针对IE9+, w3c修正animationend +avalon.each({ + AnimationEvent: "animationend", + WebKitAnimationEvent: "webkitAnimationEnd" +}, function (construct, fixType) { + if (window[construct] && !eventHooks.animationend) { + eventHooks.animationend = { + type: fixType + } + } +}) +//针对IE6-8修正input +if (!("oninput" in DOC.createElement("input"))) { + eventHooks.input = { + type: "propertychange", + deel: function (elem, fn) { + return function (e) { + if (e.propertyName === "value") { + e.type = "input" + return fn.call(elem, e) + } + } + } + } +} +if (DOC.onmousewheel === void 0) { + /* IE6-11 chrome mousewheel wheelDetla 下 -120 上 120 + firefox DOMMouseScroll detail 下3 上-3 + firefox wheel detlaY 下3 上-3 + IE9-11 wheel deltaY 下40 上-40 + chrome wheel deltaY 下100 上-100 */ + var fixWheelType = DOC.onwheel !== void 0 ? "wheel" : "DOMMouseScroll" + var fixWheelDelta = fixWheelType === "wheel" ? "deltaY" : "detail" + eventHooks.mousewheel = { + type: fixWheelType, + fn: function (elem, fn) { + return function (e) { + e.wheelDeltaY = e.wheelDelta = e[fixWheelDelta] > 0 ? -120 : 120 + e.wheelDeltaX = 0 + if (Object.defineProperty) { + Object.defineProperty(e, "type", { + value: "mousewheel" + }) + } + fn.call(elem, e) + } + } + } +} + + + +/********************************************************************* + * 配置系统 * + **********************************************************************/ + +function kernel(settings) { + for (var p in settings) { + if (!ohasOwn.call(settings, p)) + continue + var val = settings[p] + if (typeof kernel.plugins[p] === "function") { + kernel.plugins[p](val) + } else if (typeof kernel[p] === "object") { + avalon.mix(kernel[p], val) + } else { + kernel[p] = val + } + } + return this +} +var openTag, closeTag, rexpr, rexprg, rbind, rregexp = /[-.*+?^${}()|[\]\/\\]/g + +function escapeRegExp(target) { + //http://stevenlevithan.com/regex/xregexp/ + //将字符串安全格式化为正则表达式的源码 + return (target + "").replace(rregexp, "\\$&") +} + +var plugins = { + interpolate: function (array) { + openTag = array[0] + closeTag = array[1] + if (openTag === closeTag) { + throw new SyntaxError("openTag!==closeTag") + var test = openTag + "test" + closeTag + cinerator.innerHTML = test + if (cinerator.innerHTML !== test && cinerator.innerHTML.indexOf("<") > -1) { + throw new SyntaxError("此定界符不合法") + } + cinerator.innerHTML = "" + } + kernel.openTag = openTag + kernel.closeTag = closeTag + var o = escapeRegExp(openTag), + c = escapeRegExp(closeTag) + rexpr = new RegExp(o + "(.*?)" + c) + rexprg = new RegExp(o + "(.*?)" + c, "g") + rbind = new RegExp(o + ".*?" + c + "|\\sms-") + } +} +kernel.async =true +kernel.debug = true +kernel.plugins = plugins +kernel.plugins['interpolate'](["{{", "}}"]) +kernel.paths = {} +kernel.shim = {} +kernel.maxRepeatSize = 100 +avalon.config = kernel +function $watch(expr, binding) { + var $events = this.$events || (this.$events = {}) + var queue = $events[expr] || ($events[expr] = []) + if (typeof binding === "function") { + var backup = binding + backup.uniqueNumber = Math.random() + binding = { + element: root, + type: "user-watcher", + handler: noop, + vmodels: [this], + expr: expr, + uniqueNumber: backup.uniqueNumber + } + binding.wildcard = /\*/.test(expr) + } + + if (!binding.update) { + if (/\w\.*\B/.test(expr)) { + binding.getter = noop + var host = this + binding.update = function () { + var args = this.fireArgs || [] + if (args[2]) + binding.handler.apply(host, args) + delete this.fireArgs + } + queue.sync = true + avalon.Array.ensure(queue, binding) + } else { + avalon.injectBinding(binding) + } + if (backup) { + binding.handler = backup + } + } else if (!binding.oneTime) { + avalon.Array.ensure(queue, binding) + } + return function () { + binding.update = binding.getter = binding.handler = noop + binding.element = DOC.createElement("a") + } +} + +function $emit(key, args) { + var event = this.$events + if (event && event[key]) { + if (args) { + args[2] = key + } + notifySubscribers(event[key], args) + var parent = this.$up + if (parent) { + if (this.$pathname) { + $emit.call(parent, this.$pathname + "." + key, args)//以确切的值往上冒泡 + } + + $emit.call(parent, "*." + key, args)//以模糊的值往上冒泡 + } + } else { + parent = this.$up + if (parent) { + var path = this.$pathname + "." + key + var arr = path.split(".") + if (arr.indexOf("*") === -1) { + $emit.call(parent, path, args)//以确切的值往上冒泡 + arr[1] = "*" + $emit.call(parent, arr.join("."), args)//以确切的值往上冒泡 + } else { + $emit.call(parent, path, args)//以确切的值往上冒泡 + } + } + } +} + + +function collectDependency(el, key) { + do { + if (el.$watch) { + var e = el.$events || (el.$events = {}) + var array = e[key] || (e[key] = []) + dependencyDetection.collectDependency(array) + return + } + el = el.$up + if (el) { + key = el.$pathname + "." + key + } else { + break + } + + } while (true) +} + + +function notifySubscribers(subs, args) { + if (!subs) + return + if (new Date() - beginTime > 444 && typeof subs[0] === "object") { + rejectDisposeQueue() + } + var users = [], renders = [] + for (var i = 0, sub; sub = subs[i++]; ) { + if (sub.type === "user-watcher") { + users.push(sub) + } else { + renders.push(sub) + } + + } + if (kernel.async) { + buffer.render()//1 + for (i = 0; sub = renders[i++]; ) { + if (sub.update) { + var uuid = getUid(sub) + if (!buffer.queue[uuid]) { + buffer.queue[uuid] = 1 + buffer.queue.push(sub) + } + } + } + } else { + for (i = 0; sub = renders[i++]; ) { + if (sub.update) { + sub.update()//最小化刷新DOM树 + } + } + } + for (i = 0; sub = users[i++]; ) { + if (args && args[2] === sub.expr || sub.wildcard) { + sub.fireArgs = args + } + sub.update() + } +} +//avalon最核心的方法的两个方法之一(另一个是avalon.scan),返回一个ViewModel(VM) +var VMODELS = avalon.vmodels = {} //所有vmodel都储存在这里 +avalon.define = function (source) { + var $id = source.$id + if (!$id) { + log("warning: vm必须指定$id") + } + var vmodel = modelFactory(source) + vmodel.$id = $id + return VMODELS[$id] = vmodel +} + +//一些不需要被监听的属性 +var $$skipArray = oneObject("$id,$watch,$fire,$events,$model,$skipArray,$active,$pathname,$up,$track,$accessors") +var defineProperty = Object.defineProperty +var canHideOwn = true +//如果浏览器不支持ecma262v5的Object.defineProperties或者存在BUG,比如IE8 +//标准浏览器使用__defineGetter__, __defineSetter__实现 +try { + defineProperty({}, "_", { + value: "x" + }) + var defineProperties = Object.defineProperties +} catch (e) { + canHideOwn = false +} + +function modelFactory(source, options) { + options = options || {} + options.watch = true + return observeObject(source, options) +} + +//监听对象属性值的变化(注意,数组元素不是数组的属性),通过对劫持当前对象的访问器实现 +//监听对象或数组的结构变化, 对对象的键值对进行增删重排, 或对数组的进行增删重排,都属于这范畴 +// 通过比较前后代理VM顺序实现 +function Component() { +} + +function observeObject(source, options) { + if (!source || (source.$id && source.$accessors)) { + return source + } + //source为原对象,不能是元素节点或null + //options,可选,配置对象,里面有old, force, watch这三个属性 + options = options || nullObject + var force = options.force || nullObject + var old = options.old + var oldAccessors = old && old.$accessors || nullObject + var $vmodel = new Component() //要返回的对象, 它在IE6-8下可能被偷龙转凤 + var accessors = {} //监控属性 + var hasOwn = {} + var skip = [] + var simple = [] + var $skipArray = {} + if (source.$skipArray) { + $skipArray = oneObject(source.$skipArray) + delete source.$skipArray + } + //处理计算属性 + var computed = source.$computed + if (computed) { + delete source.$computed + for (var name in computed) { + hasOwn[name] = true; + (function (key, value) { + var old + accessors[key] = { + get: function () { + return old = value.get.call(this) + }, + set: function (x) { + if (typeof value.set === "function") { + var older = old + value.set.call(this, x) + var newer = this[key] + if (this.$fire && (newer !== older)) { + this.$fire(key, newer, older) + } + } + }, + enumerable: true, + configurable: true + } + })(name, computed[name])// jshint ignore:line + } + } + + + for (name in source) { + var value = source[name] + if (!$$skipArray[name]) + hasOwn[name] = true + if (typeof value === "function" || (value && value.nodeType) || + (!force[name] && (name.charAt(0) === "$" || $$skipArray[name] || $skipArray[name]))) { + skip.push(name) + } else if (isComputed(value)) { + log("warning:计算属性建议放在$computed对象中统一定义"); + (function (key, value) { + var old + accessors[key] = { + get: function () { + return old = value.get.call(this) + }, + set: function (x) { + if (typeof value.set === "function") { + var older = old + value.set.call(this, x) + var newer = this[key] + if (this.$fire && (newer !== older)) { + this.$fire(key, newer, older) + } + } + }, + enumerable: true, + configurable: true + } + })(name, value)// jshint ignore:line + } else { + simple.push(name) + if (oldAccessors[name]) { + accessors[name] = oldAccessors[name] + } else { + accessors[name] = makeGetSet(name, value) + } + } + } + + + accessors["$model"] = $modelDescriptor + $vmodel = defineProperties($vmodel, accessors, source) + function trackBy(name) { + return hasOwn[name] === true + } + skip.forEach(function (name) { + $vmodel[name] = source[name] + }) + + /* jshint ignore:start */ + hideProperty($vmodel, "$id", "anonymous") + hideProperty($vmodel, "$up", old ? old.$up : null) + hideProperty($vmodel, "$track", Object.keys(hasOwn)) + hideProperty($vmodel, "$active", false) + hideProperty($vmodel, "$pathname", old ? old.$pathname : "") + hideProperty($vmodel, "$accessors", accessors) + hideProperty($vmodel, "hasOwnProperty", trackBy) + if (options.watch) { + hideProperty($vmodel, "$watch", function () { + return $watch.apply($vmodel, arguments) + }) + hideProperty($vmodel, "$fire", function (path, a) { + if(path.indexOf("all!") === 0 ){ + var ee = path.slice(4) + for(var i in avalon.vmodels){ + var v = avalon.vmodels[i] + v.$fire && v.$fire.apply(v, [ee, a]) + } + }else{ + $emit.call($vmodel, path, [a]) + } + }) + } + /* jshint ignore:end */ + + //必须设置了$active,$events + simple.forEach(function (name) { + var val = $vmodel[name] = source[name] + if (val && typeof val === "object") { + val.$up = $vmodel + val.$pathname = name + } + $emit.call($vmodel, name) + }) + for (name in computed) { + value = $vmodel[name] + } + $vmodel.$active = true + return $vmodel +} +/* + 新的VM拥有如下私有属性 + $id: vm.id + $events: 放置$watch回调与绑定对象 + $watch: 增强版$watch + $fire: 触发$watch回调 + $track:一个数组,里面包含用户定义的所有键名 + $active:boolean,false时防止依赖收集 + $model:返回一个纯净的JS对象 + $accessors:放置所有读写器的数据描述对象 + $up:返回其上级对象 + $pathname:返回此对象在上级对象的名字,注意,数组元素的$pathname为空字符串 + ============================= + $skipArray:用于指定不可监听的属性,但VM生成是没有此属性的 + */ +function isComputed(val) {//speed up! + if (val && typeof val === "object") { + for (var i in val) { + if (i !== "get" && i !== "set") { + return false + } + } + return typeof val.get === "function" + } +} +function makeGetSet(key, value) { + var childVm + value = NaN + return { + get: function () { + if (this.$active) { + collectDependency(this, key) + } + return value + }, + set: function (newVal) { + if (value === newVal) + return + var oldValue = value + childVm = observe(newVal, value) + if (childVm) { + value = childVm + } else { + childVm = void 0 + value = newVal + } + + if (Object(childVm) === childVm) { + childVm.$pathname = key + childVm.$up = this + } + if (this.$active) { + $emit.call(this, key, [value, oldValue]) + } + }, + enumerable: true, + configurable: true + } +} + +function observe(obj, old, hasReturn, watch) { + if (Array.isArray(obj)) { + return observeArray(obj, old, watch) + } else if (avalon.isPlainObject(obj)) { + if (old) { + var keys = getKeys(obj) + var keys2 = getKeys(old) + if (keys.join(";") === keys2.join(";")) { + for (var i in obj) { + if (obj.hasOwnProperty(i)) { + old[i] = obj[i] + } + } + return old + } + old.$active = false + } + return observeObject(obj, { + old: old, + watch: watch + }) + } + if (hasReturn) { + return obj + } +} +var getKeys = rnative.test(Object.key) ? Object.key : function (a) { + var ret = [] + for (var i in a) { + if (a.hasOwnProperty(i) && !$$skipArray[i]) { + ret.push(i) + } + } + return ret +} +function observeArray(array, old, watch) { + if (old) { + var args = [0, old.length].concat(array) + old.splice.apply(old, args) + return old + } else { + for (var i in newProto) { + array[i] = newProto[i] + } + + hideProperty(array, "$up", null) + hideProperty(array, "$pathname", "") + hideProperty(array, "$track", createTrack(array.length)) + + array._ = observeObject({ + length: NaN + }, { + watch: true + }) + array._.length = array.length + array._.$watch("length", function (a, b) { + $emit.call(array.$up, array.$pathname + ".length", [a, b]) + }) + if (watch) { + hideProperty(array, "$watch", function () { + return $watch.apply(array, arguments) + }) + } + + if (W3C) { + Object.defineProperty(array, "$model", $modelDescriptor) + } else { + array.$model = toJson(array) + } + for (var j = 0, n = array.length; j < n; j++) { + var el = array[j] = observe(array[j], 0, 1, 1) + if (Object(el) === el) {//#1077 + el.$up = array + } + } + + return array + } +} + +function hideProperty(host, name, value) { + if (canHideOwn) { + Object.defineProperty(host, name, { + value: value, + writable: true, + enumerable: false, + configurable: true + }) + } else { + host[name] = value + } +} + +function toJson(val) { + var xtype = avalon.type(val) + if (xtype === "array") { + var array = [] + for (var i = 0; i < val.length; i++) { + array[i] = toJson(val[i]) + } + return array + } else if (xtype === "object") { + var obj = {} + for (i in val) { + if(i === "__proxy__" || i === "__data__" || i === "__const__") + continue + if (val.hasOwnProperty(i)) { + var value = val[i] + obj[i] = value && value.nodeType ? value :toJson(value) + } + } + return obj + } + return val +} + +var $modelDescriptor = { + get: function () { + return toJson(this) + }, + set: noop, + enumerable: false, + configurable: true +} + + +//===================修复浏览器对Object.defineProperties的支持================= +if (!canHideOwn) { + if ("__defineGetter__" in avalon) { + defineProperty = function (obj, prop, desc) { + if ('value' in desc) { + obj[prop] = desc.value + } + if ("get" in desc) { + obj.__defineGetter__(prop, desc.get) + } + if ('set' in desc) { + obj.__defineSetter__(prop, desc.set) + } + return obj + } + defineProperties = function (obj, descs) { + for (var prop in descs) { + if (descs.hasOwnProperty(prop)) { + defineProperty(obj, prop, descs[prop]) + } + } + return obj + } + } + if (IEVersion) { + var VBClassPool = {} + window.execScript([// jshint ignore:line + "Function parseVB(code)", + "\tExecuteGlobal(code)", + "End Function" //转换一段文本为VB代码 + ].join("\n"), "VBScript") + function VBMediator(instance, accessors, name, value) {// jshint ignore:line + var accessor = accessors[name] + if (arguments.length === 4) { + accessor.set.call(instance, value) + } else { + return accessor.get.call(instance) + } + } + defineProperties = function (name, accessors, properties) { + // jshint ignore:line + var buffer = [] + buffer.push( + "\r\n\tPrivate [__data__], [__proxy__]", + "\tPublic Default Function [__const__](d" + expose + ", p" + expose + ")", + "\t\tSet [__data__] = d" + expose + ": set [__proxy__] = p" + expose, + "\t\tSet [__const__] = Me", //链式调用 + "\tEnd Function") + //添加普通属性,因为VBScript对象不能像JS那样随意增删属性,必须在这里预先定义好 + var uniq = {} + + //添加访问器属性 + for (name in accessors) { + uniq[name] = true + buffer.push( + //由于不知对方会传入什么,因此set, let都用上 + "\tPublic Property Let [" + name + "](val" + expose + ")", //setter + "\t\tCall [__proxy__](Me,[__data__], \"" + name + "\", val" + expose + ")", + "\tEnd Property", + "\tPublic Property Set [" + name + "](val" + expose + ")", //setter + "\t\tCall [__proxy__](Me,[__data__], \"" + name + "\", val" + expose + ")", + "\tEnd Property", + "\tPublic Property Get [" + name + "]", //getter + "\tOn Error Resume Next", //必须优先使用set语句,否则它会误将数组当字符串返回 + "\t\tSet[" + name + "] = [__proxy__](Me,[__data__],\"" + name + "\")", + "\tIf Err.Number <> 0 Then", + "\t\t[" + name + "] = [__proxy__](Me,[__data__],\"" + name + "\")", + "\tEnd If", + "\tOn Error Goto 0", + "\tEnd Property") + + } + for (name in properties) { + if (uniq[name] !== true) { + uniq[name] = true + buffer.push("\tPublic [" + name + "]") + } + } + for (name in $$skipArray) { + if (uniq[name] !== true) { + uniq[name] = true + buffer.push("\tPublic [" + name + "]") + } + } + buffer.push("\tPublic [" + 'hasOwnProperty' + "]") + buffer.push("End Class") + var body = buffer.join("\r\n") + var className = VBClassPool[body] + if (!className) { + className = generateID("VBClass") + window.parseVB("Class " + className + body) + window.parseVB([ + "Function " + className + "Factory(a, b)", //创建实例并传入两个关键的参数 + "\tDim o", + "\tSet o = (New " + className + ")(a, b)", + "\tSet " + className + "Factory = o", + "End Function" + ].join("\r\n")) + VBClassPool[body] = className + } + var ret = window[className + "Factory"](accessors, VBMediator) //得到其产品 + return ret //得到其产品 + } + } +} + +/********************************************************************* + * 监控数组(与ms-each, ms-repeat配合使用) * + **********************************************************************/ + +var arrayMethods = ['push', 'pop', 'shift', 'unshift', 'splice'] +var arrayProto = Array.prototype +var newProto = { + notify: function () { + $emit.call(this.$up, this.$pathname) + }, + set: function (index, val) { + if (((index >>> 0) === index) && this[index] !== val) { + if (index > this.length) { + throw Error(index + "set方法的第一个参数不能大于原数组长度") + } + $emit.call(this.$up, this.$pathname + ".*", [val, this[index]]) + this.splice(index, 1, val) + } + }, + contains: function (el) { //判定是否包含 + return this.indexOf(el) !== -1 + }, + ensure: function (el) { + if (!this.contains(el)) { //只有不存在才push + this.push(el) + } + return this + }, + pushArray: function (arr) { + return this.push.apply(this, arr) + }, + remove: function (el) { //移除第一个等于给定值的元素 + return this.removeAt(this.indexOf(el)) + }, + removeAt: function (index) { //移除指定索引上的元素 + if ((index >>> 0) === index) { + return this.splice(index, 1) + } + return [] + }, + size: function () { //取得数组长度,这个函数可以同步视图,length不能 + return this._.length + }, + removeAll: function (all) { //移除N个元素 + if (Array.isArray(all)) { + for (var i = this.length - 1; i >= 0; i--) { + if (all.indexOf(this[i]) !== -1) { + _splice.call(this.$track, i, 1) + _splice.call(this, i, 1) + + } + } + } else if (typeof all === "function") { + for (i = this.length - 1; i >= 0; i--) { + var el = this[i] + if (all(el, i)) { + _splice.call(this.$track, i, 1) + _splice.call(this, i, 1) + + } + } + } else { + _splice.call(this.$track, 0, this.length) + _splice.call(this, 0, this.length) + + } + if (!W3C) { + this.$model = toJson(this) + } + this.notify() + this._.length = this.length + }, + clear: function () { + return this.removeAll() + } +} +var _splice = arrayProto.splice +arrayMethods.forEach(function (method) { + var original = arrayProto[method] + newProto[method] = function () { + // 继续尝试劫持数组元素的属性 + var args = [] + for (var i = 0, n = arguments.length; i < n; i++) { + args[i] = observe(arguments[i], 0, 1, 1) + } + var result = original.apply(this, args) + addTrack(this.$track, method, args) + if (!W3C) { + this.$model = toJson(this) + } + this.notify() + this._.length = this.length + return result + } +}) + +"sort,reverse".replace(rword, function (method) { + newProto[method] = function () { + var oldArray = this.concat() //保持原来状态的旧数组 + var newArray = this + var mask = Math.random() + var indexes = [] + var hasSort = false + arrayProto[method].apply(newArray, arguments) //排序 + for (var i = 0, n = oldArray.length; i < n; i++) { + var neo = newArray[i] + var old = oldArray[i] + if (neo === old) { + indexes.push(i) + } else { + var index = oldArray.indexOf(neo) + indexes.push(index)//得到新数组的每个元素在旧数组对应的位置 + oldArray[index] = mask //屏蔽已经找过的元素 + hasSort = true + } + } + if (hasSort) { + sortByIndex(this.$track, indexes) + if (!W3C) { + this.$model = toJson(this) + } + this.notify() + } + return this + } +}) + +function sortByIndex(array, indexes) { + var map = {}; + for (var i = 0, n = indexes.length; i < n; i++) { + map[i] = array[i] + var j = indexes[i] + if (j in map) { + array[i] = map[j] + delete map[j] + } else { + array[i] = array[j] + } + } +} + +function createTrack(n) { + var ret = [] + for (var i = 0; i < n; i++) { + ret[i] = generateID("$proxy$each") + } + return ret +} + +function addTrack(track, method, args) { + switch (method) { + case 'push': + case 'unshift': + args = createTrack(args.length) + break + case 'splice': + if (args.length > 2) { + // 0, 5, a, b, c --> 0, 2, 0 + // 0, 5, a, b, c, d, e, f, g--> 0, 0, 3 + var del = args[1] + var add = args.length - 2 + // args = [args[0], Math.max(del - add, 0)].concat(createTrack(Math.max(add - del, 0))) + args = [args[0], args[1]].concat(createTrack(args.length - 2)) + } + break + } + Array.prototype[method].apply(track, args) +} +/********************************************************************* + * 依赖调度系统 * + **********************************************************************/ +//检测两个对象间的依赖关系 +var dependencyDetection = (function () { + var outerFrames = [] + var currentFrame + return { + begin: function (binding) { + //accessorObject为一个拥有callback的对象 + outerFrames.push(currentFrame) + currentFrame = binding + }, + end: function () { + currentFrame = outerFrames.pop() + }, + collectDependency: function (array) { + if (currentFrame) { + //被dependencyDetection.begin调用 + currentFrame.callback(array) + } + } + }; +})() +//将绑定对象注入到其依赖项的订阅数组中 +var roneval = /^on$/ + +function returnRandom() { + return new Date() - 0 +} + +avalon.injectBinding = function (binding) { + + binding.handler = binding.handler || directives[binding.type].update || noop + binding.update = function () { + var begin = false + if (!binding.getter) { + begin = true + dependencyDetection.begin({ + callback: function (array) { + injectDependency(array, binding) + } + }) + binding.getter = parseExpr(binding.expr, binding.vmodels, binding) + binding.observers.forEach(function (a) { + a.v.$watch(a.p, binding) + }) + delete binding.observers + } + try { + var args = binding.fireArgs, a, b + delete binding.fireArgs + if (!args) { + if (binding.type === "on") { + a = binding.getter + "" + } else { + a = binding.getter.apply(0, binding.args) + } + } else { + a = args[0] + b = args[1] + + } + b = typeof b === "undefined" ? binding.oldValue : b + if (binding._filters) { + a = filters.$filter.apply(0, [a].concat(binding._filters)) + } + if (binding.signature) { + var xtype = avalon.type(a) + if (xtype !== "array" && xtype !== "object") { + throw Error("warning:" + binding.expr + "只能是对象或数组") + } + binding.xtype = xtype + var vtrack = getProxyIds(binding.proxies || [], xtype) + var mtrack = a.$track || (xtype === "array" ? createTrack(a.length) : + Object.keys(a)) + binding.track = mtrack + if (vtrack !== mtrack.join(";")) { + binding.handler(a, b) + binding.oldValue = 1 + } + } else if (Array.isArray(a) ? a.length !== (b && b.length) : false) { + binding.handler(a, b) + binding.oldValue = a.concat() + } else if (!("oldValue" in binding) || a !== b) { + binding.handler(a, b) + binding.oldValue = a + } + } catch (e) { + delete binding.getter + log("warning:exception throwed in [avalon.injectBinding] ", e) + var node = binding.element + if (node && node.nodeType === 3) { + node.nodeValue = openTag + (binding.oneTime ? "::" : "") + binding.expr + closeTag + } + } finally { + begin && dependencyDetection.end() + + } + } + binding.update() +} + + +//将依赖项(比它高层的访问器或构建视图刷新函数的绑定对象)注入到订阅者数组 +function injectDependency(list, binding) { + if (binding.oneTime) + return + if (list && avalon.Array.ensure(list, binding) && binding.element) { + injectDisposeQueue(binding, list) + if (new Date() - beginTime > 444) { + rejectDisposeQueue() + } + } +} + + +function getProxyIds(a, isArray) { + var ret = [] + for (var i = 0, el; el = a[i++]; ) { + ret.push(isArray ? el.$id : el.$key) + } + return ret.join(";") +} + +/********************************************************************* + * 定时GC回收机制 * + **********************************************************************/ +var disposeCount = 0 +var disposeQueue = avalon.$$subscribers = [] +var beginTime = new Date() +var oldInfo = {} + +function getUid(data) { //IE9+,标准浏览器 + if (!data.uniqueNumber) { + var elem = data.element + if (elem) { + if (elem.nodeType !== 1) { + //如果是注释节点,则data.pos不存在,当一个元素下有两个注释节点就会出问题 + data.uniqueNumber = data.type + "-" + getUid(elem.parentNode) + "-" + (++disposeCount) + } else { + data.uniqueNumber = data.name + "-" + getUid(elem) + } + } else { + data.uniqueNumber = ++disposeCount + } + } + return data.uniqueNumber +} + +//添加到回收列队中 +function injectDisposeQueue(data, list) { + var lists = data.lists || (data.lists = []) + var uuid = getUid(data) + avalon.Array.ensure(lists, list) + list.$uuid = list.$uuid || generateID() + if (!disposeQueue[uuid]) { + disposeQueue[uuid] = 1 + disposeQueue.push(data) + } +} + +function rejectDisposeQueue(data) { + + var i = disposeQueue.length + var n = i + var allTypes = [] + var iffishTypes = {} + var newInfo = {} + //对页面上所有绑定对象进行分门别类, 只检测个数发生变化的类型 + while (data = disposeQueue[--i]) { + var type = data.type + if (newInfo[type]) { + newInfo[type]++ + } else { + newInfo[type] = 1 + allTypes.push(type) + } + } + var diff = false + allTypes.forEach(function (type) { + if (oldInfo[type] !== newInfo[type]) { + iffishTypes[type] = 1 + diff = true + } + }) + i = n + if (diff) { + while (data = disposeQueue[--i]) { + if (data.element === null) { + disposeQueue.splice(i, 1) + continue + } + if (iffishTypes[data.type] && shouldDispose(data.element)) { //如果它没有在DOM树 + disposeQueue.splice(i, 1) + delete disposeQueue[data.uniqueNumber] + var lists = data.lists + for (var k = 0, list; list = lists[k++]; ) { + avalon.Array.remove(lists, list) + avalon.Array.remove(list, data) + } + disposeData(data) + } + } + } + oldInfo = newInfo + beginTime = new Date() +} + +function disposeData(data) { + delete disposeQueue[data.uniqueNumber] // 先清除,不然无法回收了 + data.element = null + data.rollback && data.rollback() + for (var key in data) { + data[key] = null + } +} + +function shouldDispose(el) { + try {//IE下,如果文本节点脱离DOM树,访问parentNode会报错 + var fireError = el.parentNode.nodeType + } catch (e) { + return true + } + if (el.ifRemove) { + // 如果节点被放到ifGroup,才移除 + if (!root.contains(el.ifRemove) && (ifGroup === el.parentNode)) { + el.parentNode && el.parentNode.removeChild(el) + return true + } + } + return el.msRetain ? 0 : (el.nodeType === 1 ? !root.contains(el) : !avalon.contains(root, el)) +} + + + +/************************************************************************ + * HTML处理(parseHTML, innerHTML, clearHTML) * + ************************************************************************/ +// We have to close these tags to support XHTML +var tagHooks = { + area: [1, "", ""], + param: [1, "", ""], + col: [2, "", "
"], + legend: [1, "
", "
"], + option: [1, ""], + thead: [1, "", "
"], + tr: [2, "", "
"], + td: [3, "", "
"], + g: [1, '', ''], + //IE6-8在用innerHTML生成节点时,不能直接创建no-scope元素与HTML5的新标签 + _default: W3C ? [0, "", ""] : [1, "X
", "
"] //div可以不用闭合 +} +tagHooks.th = tagHooks.td +tagHooks.optgroup = tagHooks.option +tagHooks.tbody = tagHooks.tfoot = tagHooks.colgroup = tagHooks.caption = tagHooks.thead +String("circle,defs,ellipse,image,line,path,polygon,polyline,rect,symbol,text,use").replace(rword, function (tag) { + tagHooks[tag] = tagHooks.g //处理SVG +}) +var rtagName = /<([\w:]+)/ //取得其tagName +var rxhtml = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig +var rcreate = W3C ? /[^\d\D]/ : /(<(?:script|link|style|meta|noscript))/ig +var scriptTypes = oneObject(["", "text/javascript", "text/ecmascript", "application/ecmascript", "application/javascript"]) +var rnest = /<(?:tb|td|tf|th|tr|col|opt|leg|cap|area)/ //需要处理套嵌关系的标签 +var script = DOC.createElement("script") +var rhtml = /<|&#?\w+;/ +avalon.parseHTML = function (html) { + var fragment = avalonFragment.cloneNode(false) + if (typeof html !== "string") { + return fragment + } + if (!rhtml.test(html)) { + fragment.appendChild(DOC.createTextNode(html)) + return fragment + } + html = html.replace(rxhtml, "<$1>").trim() + var tag = (rtagName.exec(html) || ["", ""])[1].toLowerCase(), + //取得其标签名 + wrap = tagHooks[tag] || tagHooks._default, + wrapper = cinerator, + firstChild, neo + if (!W3C) { //fix IE + html = html.replace(rcreate, "
$1") //在link style script等标签之前添加一个补丁 + } + wrapper.innerHTML = wrap[1] + html + wrap[2] + var els = wrapper.getElementsByTagName("script") + if (els.length) { //使用innerHTML生成的script节点不会发出请求与执行text属性 + for (var i = 0, el; el = els[i++];) { + if (scriptTypes[el.type]) { + //以偷龙转凤方式恢复执行脚本功能 + neo = script.cloneNode(false) //FF不能省略参数 + ap.forEach.call(el.attributes, function (attr) { + if (attr && attr.specified) { + neo[attr.name] = attr.value //复制其属性 + neo.setAttribute(attr.name, attr.value) + } + }) // jshint ignore:line + neo.text = el.text + el.parentNode.replaceChild(neo, el) //替换节点 + } + } + } + if (!W3C) { //fix IE + var target = wrap[1] === "X
" ? wrapper.lastChild.firstChild : wrapper.lastChild + if (target && target.tagName === "TABLE" && tag !== "tbody") { + //IE6-7处理 --> , + // --> , + // -->
+ for (els = target.childNodes, i = 0; el = els[i++];) { + if (el.tagName === "TBODY" && !el.innerHTML) { + target.removeChild(el) + break + } + } + } + els = wrapper.getElementsByTagName("br") + var n = els.length + while (el = els[--n]) { + if (el.className === "msNoScope") { + el.parentNode.removeChild(el) + } + } + for (els = wrapper.all, i = 0; el = els[i++];) { //fix VML + if (isVML(el)) { + fixVML(el) + } + } + } + //移除我们为了符合套嵌关系而添加的标签 + for (i = wrap[0]; i--; wrapper = wrapper.lastChild) {} + while (firstChild = wrapper.firstChild) { // 将wrapper上的节点转移到文档碎片上! + fragment.appendChild(firstChild) + } + return fragment +} + +function isVML(src) { + var nodeName = src.nodeName + return nodeName.toLowerCase() === nodeName && src.scopeName && src.outerText === "" +} + +function fixVML(node) { + if (node.currentStyle.behavior !== "url(#default#VML)") { + node.style.behavior = "url(#default#VML)" + node.style.display = "inline-block" + node.style.zoom = 1 //hasLayout + } +} +avalon.innerHTML = function (node, html) { + if (!W3C && (!rcreate.test(html) && !rnest.test(html))) { + try { + node.innerHTML = html + return + } catch (e) {} + } + var a = this.parseHTML(html) + this.clearHTML(node).appendChild(a) +} +avalon.clearHTML = function (node) { + node.textContent = "" + while (node.firstChild) { + node.removeChild(node.firstChild) + } + return node +} + +/********************************************************************* + * avalon的原型方法定义区 * + **********************************************************************/ + +function hyphen(target) { + //转换为连字符线风格 + return target.replace(/([a-z\d])([A-Z]+)/g, "$1-$2").toLowerCase() +} + +function camelize(target) { + //提前判断,提高getStyle等的效率 + if (!target || target.indexOf("-") < 0 && target.indexOf("_") < 0) { + return target + } + //转换为驼峰风格 + return target.replace(/[-_][^-_]/g, function (match) { + return match.charAt(1).toUpperCase() + }) +} + +var fakeClassListMethods = { + _toString: function () { + var node = this.node + var cls = node.className + var str = typeof cls === "string" ? cls : cls.baseVal + return str.split(/\s+/).join(" ") + }, + _contains: function (cls) { + return (" " + this + " ").indexOf(" " + cls + " ") > -1 + }, + _add: function (cls) { + if (!this.contains(cls)) { + this._set(this + " " + cls) + } + }, + _remove: function (cls) { + this._set((" " + this + " ").replace(" " + cls + " ", " ")) + }, + __set: function (cls) { + cls = cls.trim() + var node = this.node + if (rsvg.test(node)) { + //SVG元素的className是一个对象 SVGAnimatedString { baseVal="", animVal=""},只能通过set/getAttribute操作 + node.setAttribute("class", cls) + } else { + node.className = cls + } + } //toggle存在版本差异,因此不使用它 +} + +function fakeClassList(node) { + if (!("classList" in node)) { + node.classList = { + node: node + } + for (var k in fakeClassListMethods) { + node.classList[k.slice(1)] = fakeClassListMethods[k] + } + } + return node.classList +} + + +"add,remove".replace(rword, function (method) { + avalon.fn[method + "Class"] = function (cls) { + var el = this[0] + //https://developer.mozilla.org/zh-CN/docs/Mozilla/Firefox/Releases/26 + if (cls && typeof cls === "string" && el && el.nodeType === 1) { + cls.replace(/\S+/g, function (c) { + fakeClassList(el)[method](c) + }) + } + return this + } +}) +avalon.fn.mix({ + hasClass: function (cls) { + var el = this[0] || {} + return el.nodeType === 1 && fakeClassList(el).contains(cls) + }, + toggleClass: function (value, stateVal) { + var className, i = 0 + var classNames = String(value).split(/\s+/) + var isBool = typeof stateVal === "boolean" + while ((className = classNames[i++])) { + var state = isBool ? stateVal : !this.hasClass(className) + this[state ? "addClass" : "removeClass"](className) + } + return this + }, + attr: function (name, value) { + if (arguments.length === 2) { + this[0].setAttribute(name, value) + return this + } else { + return this[0].getAttribute(name) + } + }, + data: function (name, value) { + name = "data-" + hyphen(name || "") + switch (arguments.length) { + case 2: + this.attr(name, value) + return this + case 1: + var val = this.attr(name) + return parseData(val) + case 0: + var ret = {} + ap.forEach.call(this[0].attributes, function (attr) { + if (attr) { + name = attr.name + if (!name.indexOf("data-")) { + name = camelize(name.slice(5)) + ret[name] = parseData(attr.value) + } + } + }) + return ret + } + }, + removeData: function (name) { + name = "data-" + hyphen(name) + this[0].removeAttribute(name) + return this + }, + css: function (name, value) { + if (avalon.isPlainObject(name)) { + for (var i in name) { + avalon.css(this, i, name[i]) + } + } else { + var ret = avalon.css(this, name, value) + } + return ret !== void 0 ? ret : this + }, + position: function () { + var offsetParent, offset, + elem = this[0], + parentOffset = { + top: 0, + left: 0 + } + if (!elem) { + return + } + if (this.css("position") === "fixed") { + offset = elem.getBoundingClientRect() + } else { + offsetParent = this.offsetParent() //得到真正的offsetParent + offset = this.offset() // 得到正确的offsetParent + if (offsetParent[0].tagName !== "HTML") { + parentOffset = offsetParent.offset() + } + parentOffset.top += avalon.css(offsetParent[0], "borderTopWidth", true) + parentOffset.left += avalon.css(offsetParent[0], "borderLeftWidth", true) + + // Subtract offsetParent scroll positions + parentOffset.top -= offsetParent.scrollTop() + parentOffset.left -= offsetParent.scrollLeft() + } + return { + top: offset.top - parentOffset.top - avalon.css(elem, "marginTop", true), + left: offset.left - parentOffset.left - avalon.css(elem, "marginLeft", true) + } + }, + offsetParent: function () { + var offsetParent = this[0].offsetParent + while (offsetParent && avalon.css(offsetParent, "position") === "static") { + offsetParent = offsetParent.offsetParent; + } + return avalon(offsetParent || root) + }, + bind: function (type, fn, phase) { + if (this[0]) { //此方法不会链 + return avalon.bind(this[0], type, fn, phase) + } + }, + unbind: function (type, fn, phase) { + if (this[0]) { + avalon.unbind(this[0], type, fn, phase) + } + return this + }, + val: function (value) { + var node = this[0] + if (node && node.nodeType === 1) { + var get = arguments.length === 0 + var access = get ? ":get" : ":set" + var fn = valHooks[getValType(node) + access] + if (fn) { + var val = fn(node, value) + } else if (get) { + return (node.value || "").replace(/\r/g, "") + } else { + node.value = value + } + } + return get ? val : this + } +}) + +function parseData(data) { + try { + if (typeof data === "object") + return data + data = data === "true" ? true : + data === "false" ? false : + data === "null" ? null : +data + "" === data ? +data : rbrace.test(data) ? avalon.parseJSON(data) : data + } catch (e) {} + return data +} +var rbrace = /(?:\{[\s\S]*\}|\[[\s\S]*\])$/, + rvalidchars = /^[\],:{}\s]*$/, + rvalidbraces = /(?:^|:|,)(?:\s*\[)+/g, + rvalidescape = /\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g, + rvalidtokens = /"[^"\\\r\n]*"|true|false|null|-?(?:\d+\.|)\d+(?:[eE][+-]?\d+|)/g +avalon.parseJSON = window.JSON ? JSON.parse : function (data) { + if (typeof data === "string") { + data = data.trim(); + if (data) { + if (rvalidchars.test(data.replace(rvalidescape, "@") + .replace(rvalidtokens, "]") + .replace(rvalidbraces, ""))) { + return (new Function("return " + data))() // jshint ignore:line + } + } + avalon.error("Invalid JSON: " + data) + } + return data +} + +//生成avalon.fn.scrollLeft, avalon.fn.scrollTop方法 +avalon.each({ + scrollLeft: "pageXOffset", + scrollTop: "pageYOffset" +}, function (method, prop) { + avalon.fn[method] = function (val) { + var node = this[0] || {}, + win = getWindow(node), + top = method === "scrollTop" + if (!arguments.length) { + return win ? (prop in win) ? win[prop] : root[method] : node[method] + } else { + if (win) { + win.scrollTo(!top ? val : avalon(win).scrollLeft(), top ? val : avalon(win).scrollTop()) + } else { + node[method] = val + } + } + } +}) + +function getWindow(node) { + return node.window && node.document ? node : node.nodeType === 9 ? node.defaultView || node.parentWindow : false; +} +//=============================css相关======================= +var cssHooks = avalon.cssHooks = {} +var prefixes = ["", "-webkit-", "-o-", "-moz-", "-ms-"] +var cssMap = { + "float": W3C ? "cssFloat" : "styleFloat" +} +avalon.cssNumber = oneObject("animationIterationCount,columnCount,order,flex,flexGrow,flexShrink,fillOpacity,fontWeight,lineHeight,opacity,orphans,widows,zIndex,zoom") + +avalon.cssName = function (name, host, camelCase) { + if (cssMap[name]) { + return cssMap[name] + } + host = host || root.style + for (var i = 0, n = prefixes.length; i < n; i++) { + camelCase = camelize(prefixes[i] + name) + if (camelCase in host) { + return (cssMap[name] = camelCase) + } + } + return null +} +cssHooks["@:set"] = function (node, name, value) { + try { //node.style.width = NaN;node.style.width = "xxxxxxx";node.style.width = undefine 在旧式IE下会抛异常 + node.style[name] = value + } catch (e) {} +} +if (window.getComputedStyle) { + cssHooks["@:get"] = function (node, name) { + if (!node || !node.style) { + throw new Error("getComputedStyle要求传入一个节点 " + node) + } + var ret, styles = getComputedStyle(node, null) + if (styles) { + ret = name === "filter" ? styles.getPropertyValue(name) : styles[name] + if (ret === "") { + ret = node.style[name] //其他浏览器需要我们手动取内联样式 + } + } + return ret + } + cssHooks["opacity:get"] = function (node) { + var ret = cssHooks["@:get"](node, "opacity") + return ret === "" ? "1" : ret + } +} else { + var rnumnonpx = /^-?(?:\d*\.)?\d+(?!px)[^\d\s]+$/i + var rposition = /^(top|right|bottom|left)$/ + var ralpha = /alpha\([^)]*\)/i + var ie8 = !!window.XDomainRequest + var salpha = "DXImageTransform.Microsoft.Alpha" + var border = { + thin: ie8 ? '1px' : '2px', + medium: ie8 ? '3px' : '4px', + thick: ie8 ? '5px' : '6px' + } + cssHooks["@:get"] = function (node, name) { + //取得精确值,不过它有可能是带em,pc,mm,pt,%等单位 + var currentStyle = node.currentStyle + var ret = currentStyle[name] + if ((rnumnonpx.test(ret) && !rposition.test(ret))) { + //①,保存原有的style.left, runtimeStyle.left, + var style = node.style, + left = style.left, + rsLeft = node.runtimeStyle.left + //②由于③处的style.left = xxx会影响到currentStyle.left, + //因此把它currentStyle.left放到runtimeStyle.left, + //runtimeStyle.left拥有最高优先级,不会style.left影响 + node.runtimeStyle.left = currentStyle.left + //③将精确值赋给到style.left,然后通过IE的另一个私有属性 style.pixelLeft + //得到单位为px的结果;fontSize的分支见http://bugs.jquery.com/ticket/760 + style.left = name === 'fontSize' ? '1em' : (ret || 0) + ret = style.pixelLeft + "px" + //④还原 style.left,runtimeStyle.left + style.left = left + node.runtimeStyle.left = rsLeft + } + if (ret === "medium") { + name = name.replace("Width", "Style") + //border width 默认值为medium,即使其为0" + if (currentStyle[name] === "none") { + ret = "0px" + } + } + return ret === "" ? "auto" : border[ret] || ret + } + cssHooks["opacity:set"] = function (node, name, value) { + var style = node.style + var opacity = isFinite(value) && value <= 1 ? "alpha(opacity=" + value * 100 + ")" : "" + var filter = style.filter || ""; + style.zoom = 1 + //不能使用以下方式设置透明度 + //node.filters.alpha.opacity = value * 100 + style.filter = (ralpha.test(filter) ? + filter.replace(ralpha, opacity) : + filter + " " + opacity).trim() + if (!style.filter) { + style.removeAttribute("filter") + } + } + cssHooks["opacity:get"] = function (node) { + //这是最快的获取IE透明值的方式,不需要动用正则了! + var alpha = node.filters.alpha || node.filters[salpha], + op = alpha && alpha.enabled ? alpha.opacity : 100 + return (op / 100) + "" //确保返回的是字符串 + } +} + +"top,left".replace(rword, function (name) { + cssHooks[name + ":get"] = function (node) { + var computed = cssHooks["@:get"](node, name) + return /px$/.test(computed) ? computed : + avalon(node).position()[name] + "px" + } +}) + +var cssShow = { + position: "absolute", + visibility: "hidden", + display: "block" +} + +var rdisplayswap = /^(none|table(?!-c[ea]).+)/ + +function showHidden(node, array) { + //http://www.cnblogs.com/rubylouvre/archive/2012/10/27/2742529.html + if (node.offsetWidth <= 0) { //opera.offsetWidth可能小于0 + if (rdisplayswap.test(cssHooks["@:get"](node, "display"))) { + var obj = { + node: node + } + for (var name in cssShow) { + obj[name] = node.style[name] + node.style[name] = cssShow[name] + } + array.push(obj) + } + var parent = node.parentNode + if (parent && parent.nodeType === 1) { + showHidden(parent, array) + } + } +} +"Width,Height".replace(rword, function (name) { //fix 481 + var method = name.toLowerCase(), + clientProp = "client" + name, + scrollProp = "scroll" + name, + offsetProp = "offset" + name + cssHooks[method + ":get"] = function (node, which, override) { + var boxSizing = -4 + if (typeof override === "number") { + boxSizing = override + } + which = name === "Width" ? ["Left", "Right"] : ["Top", "Bottom"] + var ret = node[offsetProp] // border-box 0 + if (boxSizing === 2) { // margin-box 2 + return ret + avalon.css(node, "margin" + which[0], true) + avalon.css(node, "margin" + which[1], true) + } + if (boxSizing < 0) { // padding-box -2 + ret = ret - avalon.css(node, "border" + which[0] + "Width", true) - avalon.css(node, "border" + which[1] + "Width", true) + } + if (boxSizing === -4) { // content-box -4 + ret = ret - avalon.css(node, "padding" + which[0], true) - avalon.css(node, "padding" + which[1], true) + } + return ret + } + cssHooks[method + "&get"] = function (node) { + var hidden = []; + showHidden(node, hidden); + var val = cssHooks[method + ":get"](node) + for (var i = 0, obj; obj = hidden[i++];) { + node = obj.node + for (var n in obj) { + if (typeof obj[n] === "string") { + node.style[n] = obj[n] + } + } + } + return val; + } + avalon.fn[method] = function (value) { //会忽视其display + var node = this[0] + if (arguments.length === 0) { + if (node.setTimeout) { //取得窗口尺寸 + return node["inner" + name] || + node.document.documentElement[clientProp] || + node.document.body[clientProp] //IE6下前两个分别为undefined,0 + } + if (node.nodeType === 9) { //取得页面尺寸 + var doc = node.documentElement + //FF chrome html.scrollHeight< body.scrollHeight + //IE 标准模式 : html.scrollHeight> body.scrollHeight + //IE 怪异模式 : html.scrollHeight 最大等于可视窗口多一点? + return Math.max(node.body[scrollProp], doc[scrollProp], node.body[offsetProp], doc[offsetProp], doc[clientProp]) + } + return cssHooks[method + "&get"](node) + } else { + return this.css(method, value) + } + } + avalon.fn["inner" + name] = function () { + return cssHooks[method + ":get"](this[0], void 0, -2) + } + avalon.fn["outer" + name] = function (includeMargin) { + return cssHooks[method + ":get"](this[0], void 0, includeMargin === true ? 2 : 0) + } +}) +avalon.fn.offset = function () { //取得距离页面左右角的坐标 + var node = this[0], + box = { + left: 0, + top: 0 + } + if (!node || !node.tagName || !node.ownerDocument) { + return box + } + var doc = node.ownerDocument, + body = doc.body, + root = doc.documentElement, + win = doc.defaultView || doc.parentWindow + if (!avalon.contains(root, node)) { + return box + } + //http://hkom.blog1.fc2.com/?mode=m&no=750 body的偏移量是不包含margin的 + //我们可以通过getBoundingClientRect来获得元素相对于client的rect. + //http://msdn.microsoft.com/en-us/library/ms536433.aspx + if (node.getBoundingClientRect) { + box = node.getBoundingClientRect() // BlackBerry 5, iOS 3 (original iPhone) + } + //chrome/IE6: body.scrollTop, firefox/other: root.scrollTop + var clientTop = root.clientTop || body.clientTop, + clientLeft = root.clientLeft || body.clientLeft, + scrollTop = Math.max(win.pageYOffset || 0, root.scrollTop, body.scrollTop), + scrollLeft = Math.max(win.pageXOffset || 0, root.scrollLeft, body.scrollLeft) + // 把滚动距离加到left,top中去。 + // IE一些版本中会自动为HTML元素加上2px的border,我们需要去掉它 + // http://msdn.microsoft.com/en-us/library/ms533564(VS.85).aspx + return { + top: box.top + scrollTop - clientTop, + left: box.left + scrollLeft - clientLeft + } +} + +//==================================val相关============================ + +function getValType(elem) { + var ret = elem.tagName.toLowerCase() + return ret === "input" && /checkbox|radio/.test(elem.type) ? "checked" : ret +} +var roption = /^]+))?)*\s+value[\s=]/i +var valHooks = { + "option:get": IEVersion ? function (node) { + //在IE11及W3C,如果没有指定value,那么node.value默认为node.text(存在trim作),但IE9-10则是取innerHTML(没trim操作) + //specified并不可靠,因此通过分析outerHTML判定用户有没有显示定义value + return roption.test(node.outerHTML) ? node.value : node.text.trim() + } : function (node) { + return node.value + }, + "select:get": function (node, value) { + var option, options = node.options, + index = node.selectedIndex, + getter = valHooks["option:get"], + one = node.type === "select-one" || index < 0, + values = one ? null : [], + max = one ? index + 1 : options.length, + i = index < 0 ? max : one ? index : 0 + for (; i < max; i++) { + option = options[i] + //旧式IE在reset后不会改变selected,需要改用i === index判定 + //我们过滤所有disabled的option元素,但在safari5下,如果设置select为disable,那么其所有孩子都disable + //因此当一个元素为disable,需要检测其是否显式设置了disable及其父节点的disable情况 + if ((option.selected || i === index) && !option.disabled) { + value = getter(option) + if (one) { + return value + } + //收集所有selected值组成数组返回 + values.push(value) + } + } + return values + }, + "select:set": function (node, values, optionSet) { + values = [].concat(values) //强制转换为数组 + var getter = valHooks["option:get"] + for (var i = 0, el; el = node.options[i++];) { + if ((el.selected = values.indexOf(getter(el)) > -1)) { + optionSet = true + } + } + if (!optionSet) { + node.selectedIndex = -1 + } + } +} + +var keyMap = {} +var keys = ["break,case,catch,continue,debugger,default,delete,do,else,false", + "finally,for,function,if,in,instanceof,new,null,return,switch,this", + "throw,true,try,typeof,var,void,while,with", /* 关键字*/ + "abstract,boolean,byte,char,class,const,double,enum,export,extends", + "final,float,goto,implements,import,int,interface,long,native", + "package,private,protected,public,short,static,super,synchronized", + "throws,transient,volatile", /*保留字*/ + "arguments,let,yield,undefined"].join(",") +keys.replace(/\w+/g, function (a) { + keyMap[a] = true +}) +var ridentStart = /[a-z_$]/i +var rwhiteSpace = /[\s\uFEFF\xA0]/ +function getIdent(input, lastIndex) { + var result = [] + var subroutine = !!lastIndex + lastIndex = lastIndex || 0 + + //将表达式中的标识符抽取出来 + var state = "unknown" + var variable = "" + for (var i = 0; i < input.length; i++) { + var c = input.charAt(i) + if (c === "'" || c === '"') {//字符串开始 + if (state === "unknown") { + state = c + } else if (state === c) {//字符串结束 + state = "unknown" + } + } else if (c === "\\") { + if (state === "'" || state === '"') { + i++ + } + } else if (ridentStart.test(c)) {//碰到标识符 + if (state === "unknown") { + state = "variable" + variable = c + } else if (state === "maybePath") { + variable = result.pop() + variable += "." + c + state = "variable" + } else if (state === "variable") { + variable += c + } + } else if (/\w/.test(c)) { + if (state === "variable") { + variable += c + } + } else if (c === ".") { + if (state === "variable") { + if (variable) { + result.push(variable) + variable = "" + state = "maybePath" + } + } + } else if (c === "[") { + if (state === "variable" || state === "maybePath") { + if (variable) {//如果前面存在变量,收集它 + result.push(variable) + variable = "" + } + var lastLength = result.length + var last = result[lastLength - 1] + var innerResult = getIdent(input.slice(i), i) + if (innerResult.length) {//如果括号中存在变量,那么这里添加通配符 + result[lastLength - 1] = last + ".*" + result = innerResult.concat(result) + } else { //如果括号中的东西是确定的,直接转换为其子属性 + var content = input.slice(i + 1, innerResult.i) + try { + var text = (scpCompile(["return " + content]))() + result[lastLength - 1] = last + "." + text + } catch (e) { + } + } + state = "maybePath"//]后面可能还接东西 + i = innerResult.i + } + } else if (c === "]") { + if (subroutine) { + result.i = i + lastIndex + addVar(result, variable) + return result + } + } else if (rwhiteSpace.test(c) && c !== "\r" && c !== "\n") { + if (state === "variable") { + if (addVar(result, variable)) { + state = "maybePath" // aaa . bbb 这样的情况 + } + variable = "" + } + } else { + addVar(result, variable) + state = "unknown" + variable = "" + } + } + addVar(result, variable) + return result +} +function addVar(array, element) { + if (element && !keyMap[element]) { + array.push(element) + return true + } +} +function addAssign(vars, vmodel, name, binding) { + var ret = [], + prefix = " = " + name + "." + for (var i = vars.length, prop; prop = vars[--i]; ) { + var arr = prop.split("."), a + var first = arr[0] + while (a = arr.shift()) { + if (vmodel.hasOwnProperty(a)) { + ret.push(first + prefix + first) + + binding.observers.push({ + v: vmodel, + p: prop + }) + + vars.splice(i, 1) + } + } + } + return ret +} +var rproxy = /(\$proxy\$[a-z]+)\d+$/ +var variablePool = new Cache(218) +//缓存求值函数,以便多次利用 +var evaluatorPool = new Cache(128) + +function getVars(expr) { + expr = expr.trim() + var ret = variablePool.get(expr) + if (ret) { + return ret.concat() + } + var array = getIdent(expr) + var uniq = {} + var result = [] + for (var i = 0, el; el = array[i++]; ) { + if (!uniq[el]) { + uniq[el] = 1 + result.push(el) + } + } + return variablePool.put(expr, result).concat() +} + +function parseExpr(expr, vmodels, binding) { + var filters = binding.filters + if (typeof filters === "string" && filters.trim() && !binding._filters) { + binding._filters = parseFilter(filters.trim()) + } + + var vars = getVars(expr) + + var expose = new Date() - 0 + var assigns = [] + var names = [] + var args = [] + binding.observers = [] + for (var i = 0, sn = vmodels.length; i < sn; i++) { + if (vars.length) { + var name = "vm" + expose + "_" + i + names.push(name) + args.push(vmodels[i]) + assigns.push.apply(assigns, addAssign(vars, vmodels[i], name, binding)) + } + } + binding.args = args + var dataType = binding.type + var exprId = vmodels.map(function (el) { + return String(el.$id).replace(rproxy, "$1") + }) + expr + dataType + var getter = evaluatorPool.get(exprId) //直接从缓存,免得重复生成 + if (getter) { + if (dataType === "duplex") { + var setter = evaluatorPool.get(exprId + "setter") + binding.setter = setter.apply(setter, binding.args) + } + return binding.getter = getter + } + + if (!assigns.length) { + assigns.push("fix" + expose) + } + + if (dataType === "duplex") { + var nameOne = {} + assigns.forEach(function (a) { + var arr = a.split("=") + nameOne[arr[0].trim()] = arr[1].trim() + }) + expr = expr.replace(/[\$\w]+/, function (a) { + return nameOne[a] ? nameOne[a] : a + }) + /* jshint ignore:start */ + var fn2 = scpCompile(names.concat("'use strict';" + + "return function(vvv){" + expr + " = vvv\n}\n")) + /* jshint ignore:end */ + evaluatorPool.put(exprId + "setter", fn2) + binding.setter = fn2.apply(fn2, binding.args) + } + + if (dataType === "on") { //事件绑定 + if (expr.indexOf("(") === -1) { + expr += ".call(this, $event)" + } else { + expr = expr.replace("(", ".call(this,") + } + names.push("$event") + expr = "\nreturn " + expr + ";" //IE全家 Function("return ")出错,需要Function("return ;") + var lastIndex = expr.lastIndexOf("\nreturn") + var header = expr.slice(0, lastIndex) + var footer = expr.slice(lastIndex) + expr = header + "\n" + footer + } else { + expr = "\nreturn " + expr + ";" //IE全家 Function("return ")出错,需要Function("return ;") + } + /* jshint ignore:start */ + getter = scpCompile(names.concat("'use strict';\nvar " + + assigns.join(",\n") + expr)) + /* jshint ignore:end */ + + return evaluatorPool.put(exprId, getter) + +} +//======== + +function stringifyExpr(code) { + var hasExpr = rexpr.test(code) //比如ms-class="width{{w}}"的情况 + if (hasExpr) { + var array = scanExpr(code) + if (array.length === 1) { + return array[0].expr + } + return array.map(function (el) { + return el.type ? "(" + el.expr + ")" : quote(el.expr) + }).join(" + ") + } else { + return code + } +} + +avalon.parseExprProxy = parseExpr + +var rthimRightParentheses = /\)\s*$/ +var rthimOtherParentheses = /\)\s*\|/g +var rquoteFilterName = /\|\s*([$\w]+)/g +var rpatchBracket = /"\s*\["/g +var rthimLeftParentheses = /"\s*\(/g +function parseFilter(filters) { + filters = filters + .replace(rthimRightParentheses, "")//处理最后的小括号 + .replace(rthimOtherParentheses, function () {//处理其他小括号 + return "],|" + }) + .replace(rquoteFilterName, function (a, b) { //处理|及它后面的过滤器的名字 + return "[" + quote(b) + }) + .replace(rpatchBracket, function () { + return '"],["' + }) + .replace(rthimLeftParentheses, function () { + return '",' + }) + "]" + /* jshint ignore:start */ + return scpCompile(["return [" + filters + "]"])() + /* jshint ignore:end */ + +} +/********************************************************************* + * 编译系统 * + **********************************************************************/ +var meta = { + '\b': '\\b', + '\t': '\\t', + '\n': '\\n', + '\f': '\\f', + '\r': '\\r', + '"': '\\"', + '\\': '\\\\' +} +var quote = window.JSON && JSON.stringify || function(str) { + return '"' + str.replace(/[\\\"\x00-\x1f]/g, function(a) { + var c = meta[a]; + return typeof c === 'string' ? c : + '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); + }) + '"' +} +/********************************************************************* + * 扫描系统 * + **********************************************************************/ + +avalon.scan = function (elem, vmodel) { + elem = elem || root + var vmodels = vmodel ? [].concat(vmodel) : [] + scanTag(elem, vmodels) +} + +//http://www.w3.org/TR/html5/syntax.html#void-elements +var stopScan = oneObject("area,base,basefont,br,col,command,embed,hr,img,input,link,meta,param,source,track,wbr,noscript,script,style,textarea".toUpperCase()) + +function checkScan(elem, callback, innerHTML) { + var id = setTimeout(function () { + var currHTML = elem.innerHTML + clearTimeout(id) + if (currHTML === innerHTML) { + callback() + } else { + checkScan(elem, callback, currHTML) + } + }) +} + + +function createSignalTower(elem, vmodel) { + var id = elem.getAttribute("avalonctrl") || vmodel.$id + elem.setAttribute("avalonctrl", id) + if (vmodel.$events) { + vmodel.$events.expr = elem.tagName + '[avalonctrl="' + id + '"]' + } +} + +var getBindingCallback = function (elem, name, vmodels) { + var callback = elem.getAttribute(name) + if (callback) { + for (var i = 0, vm; vm = vmodels[i++]; ) { + if (vm.hasOwnProperty(callback) && typeof vm[callback] === "function") { + return vm[callback] + } + } + } +} + +function executeBindings(bindings, vmodels) { + for (var i = 0, binding; binding = bindings[i++]; ) { + binding.vmodels = vmodels + directives[binding.type].init(binding) + + avalon.injectBinding(binding) + if (binding.getter && binding.element.nodeType === 1) { //移除数据绑定,防止被二次解析 + //chrome使用removeAttributeNode移除不存在的特性节点时会报错 https://github.com/RubyLouvre/avalon/issues/99 + binding.element.removeAttribute(binding.name) + } + } + bindings.length = 0 +} + +//https://github.com/RubyLouvre/avalon/issues/636 +var mergeTextNodes = IEVersion && window.MutationObserver ? function (elem) { + var node = elem.firstChild, text + while (node) { + var aaa = node.nextSibling + if (node.nodeType === 3) { + if (text) { + text.nodeValue += node.nodeValue + elem.removeChild(node) + } else { + text = node + } + } else { + text = null + } + node = aaa + } +} : 0 +var roneTime = /^\s*::/ +var rmsAttr = /ms-(\w+)-?(.*)/ + +var events = oneObject("animationend,blur,change,input,click,dblclick,focus,keydown,keypress,keyup,mousedown,mouseenter,mouseleave,mousemove,mouseout,mouseover,mouseup,scan,scroll,submit") +var obsoleteAttrs = oneObject("value,title,alt,checked,selected,disabled,readonly,enabled,href,src") +function bindingSorter(a, b) { + return a.priority - b.priority +} + +function scanAttr(elem, vmodels, match) { + var scanNode = true + if (vmodels.length) { + var attributes = getAttributes ? getAttributes(elem) : elem.attributes + var bindings = [] + var uniq = {} + for (var i = 0, attr; attr = attributes[i++]; ) { + var name = attr.name + if (uniq[name]) {//IE8下ms-repeat,ms-with BUG + continue + } + uniq[name] = 1 + if (attr.specified) { + if (match = name.match(rmsAttr)) { + //如果是以指定前缀命名的 + var type = match[1] + var param = match[2] || "" + var value = attr.value + if (events[type]) { + param = type + type = "on" + } else if (obsoleteAttrs[type]) { + param = type + type = "attr" + name = "ms-" + type + "-" + param + log("warning!请改用" + name + "代替" + attr.name + "!") + } + if (directives[type]) { + var newValue = value.replace(roneTime, "") + var oneTime = value !== newValue + var binding = { + type: type, + param: param, + element: elem, + name: name, + expr: newValue, + oneTime: oneTime, + uniqueNumber: attr.name + "-" + getUid(elem), + //chrome与firefox下Number(param)得到的值不一样 #855 + priority: (directives[type].priority || type.charCodeAt(0) * 10) + (Number(param.replace(/\D/g, "")) || 0) + } + if (type === "html" || type === "text") { + + var filters = getToken(value).filters + binding.expr = binding.expr.replace(filters, "") + + binding.filters = filters.replace(rhasHtml, function () { + binding.type = "html" + binding.group = 1 + return "" + }).trim() // jshint ignore:line + } else if (type === "duplex") { + var hasDuplex = name + } else if (name === "ms-if-loop") { + binding.priority += 100 + } else if (name === "ms-attr-value") { + var hasAttrValue = name + } + bindings.push(binding) + } + } + } + } + if (bindings.length) { + bindings.sort(bindingSorter) + //http://bugs.jquery.com/ticket/7071 + //在IE下对VML读取type属性,会让此元素所有属性都变成 + if (hasDuplex && hasAttrValue && elem.nodeName === "INPUT" && elem.type === "text") { + log("warning!一个控件不能同时定义ms-attr-value与" + hasDuplex) + } + for (i = 0; binding = bindings[i]; i++) { + type = binding.type + if (rnoscanAttrBinding.test(type)) { + return executeBindings(bindings.slice(0, i + 1), vmodels) + } else if (scanNode) { + scanNode = !rnoscanNodeBinding.test(type) + } + } + + executeBindings(bindings, vmodels) + } + } + if (scanNode && !stopScan[elem.tagName] && (isWidget(elem) ? elem.msResolved : 1)) { + mergeTextNodes && mergeTextNodes(elem) + scanNodeList(elem, vmodels) //扫描子孙元素 + + } +} +var rnoscanAttrBinding = /^if|widget|repeat$/ +var rnoscanNodeBinding = /^each|with|html|include$/ +//IE67下,在循环绑定中,一个节点如果是通过cloneNode得到,自定义属性的specified为false,无法进入里面的分支, +//但如果我们去掉scanAttr中的attr.specified检测,一个元素会有80+个特性节点(因为它不区分固有属性与自定义属性),很容易卡死页面 +if (!W3C) { + var attrPool = new Cache(512) + var rattrs = /\s+([^=\s]+)(?:=("[^"]*"|'[^']*'|[^\s>]+))?/g, + rquote = /^['"]/, + rtag = /<\w+\b(?:(["'])[^"]*?(\1)|[^>])*>/i, + ramp = /&/g +//IE6-8解析HTML5新标签,会将它分解两个元素节点与一个文本节点 +//
ddd
+// window.onload = function() { +// var body = document.body +// for (var i = 0, el; el = body.children[i++]; ) { +// avalon.log(el.outerHTML) +// } +// } +//依次输出
,
+ var getAttributes = function (elem) { + var html = elem.outerHTML + //处理IE6-8解析HTML5新标签的情况,及
等半闭合标签outerHTML为空的情况 + if (html.slice(0, 2) === " ms-important(1) --> ms-controller(2) --> ms-if(10) --> ms-repeat(100) + //--> ms-if-loop(110) --> ms-attr(970) ...--> ms-each(1400)-->ms-with(1500)--〉ms-duplex(2000)垫后 + var a = elem.getAttribute("ms-skip") + //#360 在旧式IE中 Object标签在引入Flash等资源时,可能出现没有getAttributeNode,innerHTML的情形 + if (!elem.getAttributeNode) { + return log("warning " + elem.tagName + " no getAttributeNode method") + } + var b = elem.getAttributeNode("ms-important") + var c = elem.getAttributeNode("ms-controller") + if (typeof a === "string") { + return + } else if (node = b || c) { + var newVmodel = avalon.vmodels[node.value] + if (!newVmodel) { + return + } + //ms-important不包含父VM,ms-controller相反 + vmodels = node === b ? [newVmodel] : [newVmodel].concat(vmodels) + var name = node.name + elem.removeAttribute(name) //removeAttributeNode不会刷新[ms-controller]样式规则 + avalon(elem).removeClass(name) + createSignalTower(elem, newVmodel) + } + + scanAttr(elem, vmodels) //扫描特性节点 +} + + + +var rhasHtml = /\|\s*html(?:\b|$)/, + r11a = /\|\|/g, + rlt = /</g, + rgt = />/g, + rstringLiteral = /(['"])(\\\1|.)+?\1/g + +function getToken(value) { + if (value.indexOf("|") > 0) { + var scapegoat = value.replace(rstringLiteral, function (_) { + return Array(_.length + 1).join("1") // jshint ignore:line + }) + var index = scapegoat.replace(r11a, "\u1122\u3344").indexOf("|") //干掉所有短路或 + if (index > -1) { + return { + type: "text", + filters: value.slice(index).trim(), + expr: value.slice(0, index) + } + } + } + return { + type: "text", + expr: value, + filters: "" + } +} + +function scanExpr(str) { + var tokens = [], + value, start = 0, + stop + do { + stop = str.indexOf(openTag, start) + if (stop === -1) { + break + } + value = str.slice(start, stop) + if (value) { // {{ 左边的文本 + tokens.push({ + expr: value + }) + } + start = stop + openTag.length + stop = str.indexOf(closeTag, start) + if (stop === -1) { + break + } + value = str.slice(start, stop) + if (value) { //处理{{ }}插值表达式 + tokens.push(getToken(value, start)) + } + start = stop + closeTag.length + } while (1) + value = str.slice(start) + if (value) { //}} 右边的文本 + tokens.push({ + expr: value + }) + } + return tokens +} + +function scanText(textNode, vmodels, index) { + var bindings = [], + tokens = scanExpr(textNode.data) + if (tokens.length) { + for (var i = 0, token; token = tokens[i++];) { + var node = DOC.createTextNode(token.expr) //将文本转换为文本节点,并替换原来的文本节点 + if (token.type) { + token.expr = token.expr.replace(roneTime, function () { + token.oneTime = true + return "" + }) // jshint ignore:line + token.element = node + token.filters = token.filters.replace(rhasHtml, function () { + token.type = "html" + return "" + }) // jshint ignore:line + token.pos = index * 1000 + i + bindings.push(token) //收集带有插值表达式的文本 + } + avalonFragment.appendChild(node) + } + textNode.parentNode.replaceChild(avalonFragment, textNode) + if (bindings.length) + executeBindings(bindings, vmodels) + } +} + + + +//使用来自游戏界的双缓冲技术,减少对视图的冗余刷新 +var Buffer = function () { + this.queue = [] +} +Buffer.prototype = { + render: function (isAnimate) { + if (!this.locked) { + this.locked = isAnimate ? root.offsetHeight + 10 : 1 + var me = this + avalon.nextTick(function () { + me.flush() + }) + } + }, + flush: function () { + for (var i = 0, sub; sub = this.queue[i++]; ) { + sub.update && sub.update() + } + this.locked = 0 + this.queue = [] + } +} + +var buffer = new Buffer() +var componentQueue = [] +var widgetList = [] +var componentHooks = { + $construct: function () { + return avalon.mix.apply(null, arguments) + }, + $ready: noop, + $init: noop, + $dispose: noop, + $container: null, + $childReady: noop, + $replace: false, + $extend: null, + $$template: function (str) { + return str + } +} + + +avalon.components = {} +avalon.component = function (name, opts) { + if (opts) { + avalon.components[name] = avalon.mix({}, componentHooks, opts) + } + for (var i = 0, obj; obj = componentQueue[i]; i++) { + if (name === obj.fullName) { + componentQueue.splice(i, 1) + i--; + + (function (host, hooks, elem, widget) { + + var dependencies = 1 + var library = host.library + var global = avalon.libraries[library] || componentHooks + + //===========收集各种配置======= + + var elemOpts = getOptionsFromTag(elem) + var vmOpts = getOptionsFromVM(host.vmodels, elemOpts.config || host.widget) + var $id = elemOpts.$id || elemOpts.identifier || generateID(widget) + delete elemOpts.config + delete elemOpts.$id + delete elemOpts.identifier + var componentDefinition = {} + + var parentHooks = avalon.components[hooks.$extend] + if (parentHooks) { + avalon.mix(true, componentDefinition, parentHooks) + componentDefinition = parentHooks.$construct.call(elem, componentDefinition, {}, {}) + } else { + avalon.mix(true, componentDefinition, hooks) + } + componentDefinition = avalon.components[name].$construct.call(elem, componentDefinition, vmOpts, elemOpts) + + componentDefinition.$refs = {} + componentDefinition.$id = $id + + //==========构建VM========= + var keepSolt = componentDefinition.$slot + var keepReplace = componentDefinition.$replace + var keepContainer = componentDefinition.$container + var keepTemplate = componentDefinition.$template + delete componentDefinition.$slot + delete componentDefinition.$replace + delete componentDefinition.$container + delete componentDefinition.$construct + + var vmodel = avalon.define(componentDefinition) || {} + elem.msResolved = 1 + vmodel.$init(vmodel, elem) + global.$init(vmodel, elem) + var nodes = elem.childNodes + //收集插入点 + var slots = {}, snode + + for (var s = 0, el; el = nodes[s++]; ) { + var type = el.nodeType === 1 && el.getAttribute("slot") || keepSolt + if (type) { + if (slots[type]) { + slots[type].push(el) + } else { + slots[type] = [el] + } + } + } + + + if (vmodel.$$template) { + avalon.clearHTML(elem) + elem.innerHTML = vmodel.$$template(keepTemplate) + } + for (s in slots) { + if (vmodel.hasOwnProperty(s)) { + var ss = slots[s] + if (ss.length) { + var fragment = avalonFragment.cloneNode(true) + for (var ns = 0; snode = ss[ns++]; ) { + fragment.appendChild(snode) + } + vmodel[s] = fragment + } + slots[s] = null + } + } + slots = null + var child = elem.firstChild + if (keepReplace) { + child = elem.firstChild + elem.parentNode.replaceChild(child, elem) + child.msResolved = 1 + elem = host.element = child + } + if (keepContainer) { + keepContainer.appendChild(elem) + } + avalon.fireDom(elem, "datasetchanged", + {library: library, vm: vmodel, childReady: 1}) + var children = 0 + var removeFn = avalon.bind(elem, "datasetchanged", function (e) { + if (e.childReady && e.library === library) { + dependencies += e.childReady + if (vmodel !== e.vm) { + vmodel.$refs[e.vm.$id] = e.vm + if (e.childReady === -1) { + children++ + vmodel.$childReady(vmodel, elem, e) + } + e.stopPropagation() + } + } + + if (dependencies === 0) { + var id1 = setTimeout(function () { + clearTimeout(id1) + + vmodel.$ready(vmodel, elem, host.vmodels) + global.$ready(vmodel, elem, host.vmodels) + }, children ? Math.max(children * 17, 100) : 17) + avalon.unbind(elem, "datasetchanged", removeFn) + //================== + host.rollback = function () { + try { + vmodel.$dispose(vmodel, elem) + global.$dispose(vmodel, elem) + } catch (e) { + } + delete avalon.vmodels[vmodel.$id] + } + injectDisposeQueue(host, widgetList) + if (window.chrome) { + elem.addEventListener("DOMNodeRemovedFromDocument", function () { + setTimeout(rejectDisposeQueue) + }) + } + + } + }) + scanTag(elem, [vmodel].concat(host.vmodels)) + + avalon.vmodels[vmodel.$id] = vmodel + if (!elem.childNodes.length) { + avalon.fireDom(elem, "datasetchanged", {library: library, vm: vmodel, childReady: -1}) + } else { + var id2 = setTimeout(function () { + clearTimeout(id2) + avalon.fireDom(elem, "datasetchanged", {library: library, vm: vmodel, childReady: -1}) + }, 17) + } + + + })(obj, avalon.components[name], obj.element, obj.widget)// jshint ignore:line + + + } + } +} + +avalon.fireDom = function (elem, type, opts) { + if (DOC.createEvent) { + var hackEvent = DOC.createEvent("Events"); + hackEvent.initEvent(type, true, true, opts) + avalon.mix(hackEvent, opts) + + elem.dispatchEvent(hackEvent) + } else if (root.contains(elem)) {//IE6-8触发事件必须保证在DOM树中,否则报"SCRIPT16389: 未指明的错误" + hackEvent = DOC.createEventObject() + avalon.mix(hackEvent, opts) + elem.fireEvent("on" + type, hackEvent) + } +} + + +function getOptionsFromVM(vmodels, pre) { + if (pre) { + for (var i = 0, v; v = vmodels[i++]; ) { + if (v.hasOwnProperty(pre) && typeof v[pre] === "object") { + var vmOptions = v[pre] + return vmOptions.$model || vmOptions + break + } + } + } + return {} +} + + + +avalon.libraries = [] +avalon.library = function (name, opts) { + if (DOC.namespaces) { + DOC.namespaces.add(name, 'http://www.w3.org/1999/xhtml'); + } + avalon.libraries[name] = avalon.mix({ + $init: noop, + $ready: noop, + $dispose: noop + }, opts || {}) +} + +avalon.library("ms") +/* +broswer nodeName scopeName localName +IE9 ONI:BUTTON oni button +IE10 ONI:BUTTON undefined oni:button +IE8 button oni undefined +chrome ONI:BUTTON undefined oni:button + +*/ +function isWidget(el) { //如果为自定义标签,返回UI库的名字 + if(el.scopeName && el.scopeName !== "HTML" ){ + return el.scopeName + } + var fullName = el.nodeName.toLowerCase() + var index = fullName.indexOf(":") + if (index > 0) { + return fullName.slice(0, index) + } +} +//各种MVVM框架在大型表格下的性能测试 +// https://github.com/RubyLouvre/avalon/issues/859 + + +var bools = ["autofocus,autoplay,async,allowTransparency,checked,controls", + "declare,disabled,defer,defaultChecked,defaultSelected", + "contentEditable,isMap,loop,multiple,noHref,noResize,noShade", + "open,readOnly,selected" +].join(",") +var boolMap = {} +bools.replace(rword, function (name) { + boolMap[name.toLowerCase()] = name +}) + +var propMap = {//属性名映射 + "accept-charset": "acceptCharset", + "char": "ch", + "charoff": "chOff", + "class": "className", + "for": "htmlFor", + "http-equiv": "httpEquiv" +} + +var anomaly = ["accessKey,bgColor,cellPadding,cellSpacing,codeBase,codeType,colSpan", + "dateTime,defaultValue,frameBorder,longDesc,maxLength,marginWidth,marginHeight", + "rowSpan,tabIndex,useMap,vSpace,valueType,vAlign" +].join(",") +anomaly.replace(rword, function (name) { + propMap[name.toLowerCase()] = name +}) + + +var attrDir = avalon.directive("attr", { + init: function (binding) { + //{{aaa}} --> aaa + //{{aaa}}/bbb.html --> (aaa) + "/bbb.html" + binding.expr = stringifyExpr(binding.expr.trim()) + if (binding.type === "include") { + var elem = binding.element + effectBinding(elem, binding) + binding.includeRendered = getBindingCallback(elem, "data-include-rendered", binding.vmodels) + binding.includeLoaded = getBindingCallback(elem, "data-include-loaded", binding.vmodels) + var outer = binding.includeReplace = !!avalon(elem).data("includeReplace") + if (avalon(elem).data("includeCache")) { + binding.templateCache = {} + } + binding.start = DOC.createComment("ms-include") + binding.end = DOC.createComment("ms-include-end") + if (outer) { + binding.element = binding.end + binding._element = elem + elem.parentNode.insertBefore(binding.start, elem) + elem.parentNode.insertBefore(binding.end, elem.nextSibling) + } else { + elem.insertBefore(binding.start, elem.firstChild) + elem.appendChild(binding.end) + } + } + }, + update: function (val) { + var elem = this.element + var attrName = this.param + if (attrName === "href" || attrName === "src") { + if (typeof val === "string" && !root.hasAttribute) { + val = val.replace(/&/g, "&") //处理IE67自动转义的问题 + } + elem[attrName] = val + if (window.chrome && elem.tagName === "EMBED") { + var parent = elem.parentNode //#525 chrome1-37下embed标签动态设置src不能发生请求 + var comment = document.createComment("ms-src") + parent.replaceChild(comment, elem) + parent.replaceChild(elem, comment) + } + } else { + + // ms-attr-class="xxx" vm.xxx="aaa bbb ccc"将元素的className设置为aaa bbb ccc + // ms-attr-class="xxx" vm.xxx=false 清空元素的所有类名 + // ms-attr-name="yyy" vm.yyy="ooo" 为元素设置name属性 + var toRemove = (val === false) || (val === null) || (val === void 0) + if (!W3C && propMap[attrName]) { //旧式IE下需要进行名字映射 + attrName = propMap[attrName] + } + var bool = boolMap[attrName] + if (typeof elem[bool] === "boolean") { + elem[bool] = !!val //布尔属性必须使用el.xxx = true|false方式设值 + if (!val) { //如果为false, IE全系列下相当于setAttribute(xxx,''),会影响到样式,需要进一步处理 + toRemove = true + } + } + if (toRemove) { + return elem.removeAttribute(attrName) + } + //SVG只能使用setAttribute(xxx, yyy), VML只能使用elem.xxx = yyy ,HTML的固有属性必须elem.xxx = yyy + var isInnate = rsvg.test(elem) ? false : (DOC.namespaces && isVML(elem)) ? true : attrName in elem.cloneNode(false) + if (isInnate) { + elem[attrName] = val + "" + } else { + elem.setAttribute(attrName, val) + } + } + } +}) + + + +//这几个指令都可以使用插值表达式,如ms-src="aaa/{{b}}/{{c}}.html" +"title,alt,src,value,css,include,href".replace(rword, function (name) { + directives[name] = attrDir +}) + +//根据VM的属性值或表达式的值切换类名,ms-class="xxx yyy zzz:flag" +//http://www.cnblogs.com/rubylouvre/archive/2012/12/17/2818540.html +avalon.directive("class", { + init: function (binding) { + var oldStyle = binding.param + var method = binding.type + if (!oldStyle || isFinite(oldStyle)) { + binding.param = "" //去掉数字 + directives.effect.init(binding) + } else { + log('ms-' + method + '-xxx="yyy"这种用法已经过时,请使用ms-' + method + '="xxx:yyy"') + binding.expr = '[' + quote(oldStyle) + "," + binding.expr + "]" + binding.oldStyle = oldStyle + } + if (method === "hover" || method === "active") { //确保只绑定一次 + if (!binding.hasBindEvent) { + var elem = binding.element + var $elem = avalon(elem) + var activate = "mouseenter" //在移出移入时切换类名 + var abandon = "mouseleave" + if (method === "active") { //在聚焦失焦中切换类名 + elem.tabIndex = elem.tabIndex || -1 + activate = "mousedown" + abandon = "mouseup" + var fn0 = $elem.bind("mouseleave", function () { + binding.toggleClass && $elem.removeClass(binding.newClass) + }) + } + } + + var fn1 = $elem.bind(activate, function () { + binding.toggleClass && $elem.addClass(binding.newClass) + }) + var fn2 = $elem.bind(abandon, function () { + binding.toggleClass && $elem.removeClass(binding.newClass) + }) + binding.rollback = function () { + $elem.unbind("mouseleave", fn0) + $elem.unbind(activate, fn1) + $elem.unbind(abandon, fn2) + } + binding.hasBindEvent = true + } + + }, + update: function (arr) { + var binding = this + var $elem = avalon(this.element) + binding.newClass = arr[0] + binding.toggleClass = !!arr[1] + if (binding.oldClass && binding.newClass !== binding.oldClass) { + $elem.removeClass(binding.oldClass) + } + binding.oldClass = binding.newClass + if (binding.type === "class") { + if (binding.oldStyle) { + $elem.toggleClass(binding.oldStyle, !!arr[1]) + } else { + $elem.toggleClass(binding.newClass, binding.toggleClass) + } + } + } +}) + +"hover,active".replace(rword, function (name) { + directives[name] = directives["class"] +}) + + +//ms-controller绑定已经在scanTag 方法中实现 +avalon.directive("css", { + init: directives.attr.init, + update: function (val) { + avalon(this.element).css(this.param, val) + } +}) + +avalon.directive("data", { + priority: 100, + update: function (val) { + var elem = this.element + var key = "data-" + this.param + if (val && typeof val === "object") { + elem[key] = val + } else { + elem.setAttribute(key, String(val)) + } + } +}) + +//双工绑定 +var rduplexType = /^(?:checkbox|radio)$/ +var rduplexParam = /^(?:radio|checked)$/ +var rnoduplexInput = /^(file|button|reset|submit|checkbox|radio)$/ +var duplexBinding = avalon.directive("duplex", { + priority: 2000, + init: function (binding, hasCast) { + var elem = binding.element + var vmodels = binding.vmodels + binding.changed = getBindingCallback(elem, "data-duplex-changed", vmodels) || noop + var params = [] + var casting = oneObject("string,number,boolean,checked") + if (elem.type === "radio" && binding.param === "") { + binding.param = "checked" + } + + + binding.param.replace(rw20g, function (name) { + if (rduplexType.test(elem.type) && rduplexParam.test(name)) { + if (name === "radio") + log("ms-duplex-radio已经更名为ms-duplex-checked") + name = "checked" + binding.isChecked = true + binding.xtype = "radio" + } + if (name === "bool") { + name = "boolean" + log("ms-duplex-bool已经更名为ms-duplex-boolean") + } else if (name === "text") { + name = "string" + log("ms-duplex-text已经更名为ms-duplex-string") + } + if (casting[name]) { + hasCast = true + } + avalon.Array.ensure(params, name) + }) + if (!hasCast) { + params.push("string") + } + binding.param = params.join("-") + if (!binding.xtype) { + binding.xtype = elem.tagName === "SELECT" ? "select" : + elem.type === "checkbox" ? "checkbox" : + elem.type === "radio" ? "radio" : + /^change/.test(elem.getAttribute("data-duplex-event")) ? "change" : + "input" + } + //===================绑定事件====================== + binding.bound = function (type, callback) { + if (elem.addEventListener) { + elem.addEventListener(type, callback, false) + } else { + elem.attachEvent("on" + type, callback) + } + var old = binding.rollback + binding.rollback = function () { + elem.avalonSetter = null + avalon.unbind(elem, type, callback) + old && old() + } + } + var composing = false + function callback(value) { + binding.changed.call(this, value, binding) + } + function compositionStart() { + composing = true + } + function compositionEnd() { + composing = false + } + var updateVModel = function () { + var val = elem.value //防止递归调用形成死循环 + if (composing || val === binding.oldValue) //处理中文输入法在minlengh下引发的BUG + return + var lastValue = binding.pipe(val, binding, "get") + try { + binding.setter(lastValue) + callback.call(elem, lastValue) + } catch (ex) { + log(ex) + } + } + switch (binding.xtype) { + case "radio": + binding.bound("click", function () { + var lastValue = binding.pipe(elem.value, binding, "get") + try { + binding.setter(lastValue) + callback.call(elem, lastValue) + } catch (ex) { + log(ex) + } + }) + break + case "checkbox": + binding.bound(W3C ? "change" : "click", function () { + var method = elem.checked ? "ensure" : "remove" + var array = binding.getter.apply(0, binding.vmodels) + if (!Array.isArray(array)) { + log("ms-duplex应用于checkbox上要对应一个数组") + array = [array] + } + var val = binding.pipe(elem.value, binding, "get") + avalon.Array[method](array, val) + callback.call(elem, array) + }) + break + case "change": + binding.bound("change", updateVModel) + break + case "input": + if (!IEVersion) { // W3C + binding.bound("input", updateVModel) + //非IE浏览器才用这个 + binding.bound("compositionstart", compositionStart) + binding.bound("compositionend", compositionEnd) + binding.bound("DOMAutoComplete", updateVModel) + } else { //onpropertychange事件无法区分是程序触发还是用户触发 + // IE下通过selectionchange事件监听IE9+点击input右边的X的清空行为,及粘贴,剪切,删除行为 + if (IEVersion > 8) { + binding.bound("input", updateVModel) //IE9使用propertychange无法监听中文输入改动 + } else { + binding.bound("propertychange", function (e) { //IE6-8下第一次修改时不会触发,需要使用keydown或selectionchange修正 + if (e.propertyName === "value") { + updateVModel() + } + }) + } + binding.bound("dragend", function () { + setTimeout(function () { + updateVModel() + }, 17) + }) + //http://www.cnblogs.com/rubylouvre/archive/2013/02/17/2914604.html + //http://www.matts411.com/post/internet-explorer-9-oninput/ + } + break + case "select": + binding.bound("change", function () { + var val = avalon(elem).val() //字符串或字符串数组 + if (Array.isArray(val)) { + val = val.map(function (v) { + return binding.pipe(v, binding, "get") + }) + } else { + val = binding.pipe(val, binding, "get") + } + if (val + "" !== binding.oldValue) { + try { + binding.setter(val) + callback.call(elem, val) + } catch (ex) { + log(ex) + } + } + }) + break + } + binding.bound("focus", function() { + elem.msFocus = true + }) + binding.bound("blur", function() { + elem.msFocus = false + }) + if (binding.xtype === "input" && !rnoduplexInput.test(elem.type)) { + elem.avalonSetter = updateVModel //#765 + watchValueInTimer(function () { + if (elem.contains(elem)) { + if (!this.msFocus && binding.oldValue !== elem.value) { + updateVModel() + } + } else if (!elem.msRetain) { + return false + } + }) + } + + }, + update: function (value) { + var elem = this.element, binding = this, curValue + if (!this.init) { + for (var i in avalon.vmodels) { + var v = avalon.vmodels[i] + v.$fire("avalon-ms-duplex-init", binding) + } + var cpipe = binding.pipe || (binding.pipe = pipe) + cpipe(null, binding, "init") + this.init = 1 + } + switch (this.xtype) { + case "input": + case "change": + curValue = this.pipe(value, this, "set") //fix #673 + if (curValue !== this.oldValue) { + elem.value = this.oldValue = curValue + } + break + case "radio": + curValue = binding.isChecked ? !!value : value + "" === elem.value + if (IEVersion === 6) { + setTimeout(function () { + //IE8 checkbox, radio是使用defaultChecked控制选中状态, + //并且要先设置defaultChecked后设置checked + //并且必须设置延迟 + elem.defaultChecked = curValue + elem.checked = curValue + }, 31) + } else { + elem.checked = curValue + } + break + case "checkbox": + var array = [].concat(value) //强制转换为数组 + curValue = this.pipe(elem.value, this, "get") + elem.checked = array.indexOf(curValue) > -1 + break + case "select": + //必须变成字符串后才能比较 + binding._value = value + elem.msHasEvent = "selectDuplex" + //必须等到其孩子准备好才触发 + avalon.bind(elem, "datasetchanged", function (e) { + if (e.bubble === "selectDuplex") { + var value = binding._value + var curValue = Array.isArray(value) ? value.map(String) : value + "" + avalon(elem).val(curValue) + elem.oldValue = curValue + "" + binding.changed.call(elem, curValue) + } + }) + + break + } + if (binding.xtype !== "select") { + binding.changed.call(elem, curValue) + } + } +}) + +if (IEVersion) { + avalon.bind(DOC, "selectionchange", function (e) { + var el = DOC.activeElement + if (el && typeof el.avalonSetter === "function") { + el.avalonSetter() + } + }) +} + +function fixNull(val) { + return val == null ? "" : val +} +avalon.duplexHooks = { + checked: { + get: function (val, binding) { + return !binding.oldValue + } + }, + string: { + get: function (val) { //同步到VM + return val + }, + set: fixNull + }, + "boolean": { + get: function (val) { + return val === "true" + }, + set: fixNull + }, + number: { + get: function (val, binding) { + var number = parseFloat(val + "") + if (-val === -number) { + return number + } + + var arr = /strong|medium|weak/.exec(binding.element.getAttribute("data-duplex-number")) || ["medium"] + switch (arr[0]) { + case "strong": + return 0 + case "medium": + return val === "" ? "" : 0 + case "weak": + return val + } + }, + set: fixNull + } +} + +function pipe(val, binding, action) { + binding.param.replace(rw20g, function (name) { + var hook = avalon.duplexHooks[name] + if (hook && typeof hook[action] === "function") { + val = hook[action](val, binding) + } + }) + return val +} + +var TimerID, ribbon = [] + +avalon.tick = function (fn) { + if (ribbon.push(fn) === 1) { + TimerID = setInterval(ticker, 60) + } +} + +function ticker() { + for (var n = ribbon.length - 1; n >= 0; n--) { + var el = ribbon[n] + if (el() === false) { + ribbon.splice(n, 1) + } + } + if (!ribbon.length) { + clearInterval(TimerID) + } +} + +var watchValueInTimer = noop +new function () { // jshint ignore:line + try { //#272 IE9-IE11, firefox + var setters = {} + var aproto = HTMLInputElement.prototype + var bproto = HTMLTextAreaElement.prototype + function newSetter(value) { // jshint ignore:line + setters[this.tagName].call(this, value) + if (!this.msFocus && this.avalonSetter && this.oldValue !== value) { + this.avalonSetter() + } + } + var inputProto = HTMLInputElement.prototype + Object.getOwnPropertyNames(inputProto) //故意引发IE6-8等浏览器报错 + setters["INPUT"] = Object.getOwnPropertyDescriptor(aproto, "value").set + + Object.defineProperty(aproto, "value", { + set: newSetter + }) + setters["TEXTAREA"] = Object.getOwnPropertyDescriptor(bproto, "value").set + Object.defineProperty(bproto, "value", { + set: newSetter + }) + } catch (e) { + //在chrome 43中 ms-duplex终于不需要使用定时器实现双向绑定了 + // http://updates.html5rocks.com/2015/04/DOM-attributes-now-on-the-prototype + // https://docs.google.com/document/d/1jwA8mtClwxI-QJuHT7872Z0pxpZz8PBkf2bGAbsUtqs/edit?pli=1 + watchValueInTimer = avalon.tick + } +} // jshint ignore:line + +avalon.directive("effect", { + priority: 5, + init: function (binding) { + var text = binding.expr, + className, + rightExpr + var colonIndex = text.replace(rexprg, function (a) { + return a.replace(/./g, "0") + }).indexOf(":") //取得第一个冒号的位置 + if (colonIndex === -1) { // 比如 ms-class/effect="aaa bbb ccc" 的情况 + className = text + rightExpr = true + } else { // 比如 ms-class/effect-1="ui-state-active:checked" 的情况 + className = text.slice(0, colonIndex) + rightExpr = text.slice(colonIndex + 1) + } + if (!rexpr.test(text)) { + className = quote(className) + } else { + className = stringifyExpr(className) + } + binding.expr = "[" + className + "," + rightExpr + "]" + }, + update: function (arr) { + var name = arr[0] + var elem = this.element + if (elem.getAttribute("data-effect-name") === name) { + return + } else { + elem.removeAttribute("data-effect-driver") + } + var inlineStyles = elem.style + var computedStyles = window.getComputedStyle ? window.getComputedStyle(elem) : null + var useAni = false + if (computedStyles && (supportTransition || supportAnimation)) { + + //如果支持CSS动画 + var duration = inlineStyles[transitionDuration] || computedStyles[transitionDuration] + if (duration && duration !== '0s') { + elem.setAttribute("data-effect-driver", "t") + useAni = true + } + + if (!useAni) { + + duration = inlineStyles[animationDuration] || computedStyles[animationDuration] + if (duration && duration !== '0s') { + elem.setAttribute("data-effect-driver", "a") + useAni = true + } + + } + } + + if (!useAni) { + if (avalon.effects[name]) { + elem.setAttribute("data-effect-driver", "j") + useAni = true + } + } + if (useAni) { + elem.setAttribute("data-effect-name", name) + } + } +}) + +avalon.effects = {} +avalon.effect = function (name, callbacks) { + avalon.effects[name] = callbacks +} + + + +var supportTransition = false +var supportAnimation = false + +var transitionEndEvent +var animationEndEvent +var transitionDuration = avalon.cssName("transition-duration") +var animationDuration = avalon.cssName("animation-duration") +new function () {// jshint ignore:line + var checker = { + 'TransitionEvent': 'transitionend', + 'WebKitTransitionEvent': 'webkitTransitionEnd', + 'OTransitionEvent': 'oTransitionEnd', + 'otransitionEvent': 'otransitionEnd' + } + var tran + //有的浏览器同时支持私有实现与标准写法,比如webkit支持前两种,Opera支持1、3、4 + for (var name in checker) { + if (window[name]) { + tran = checker[name] + break; + } + try { + var a = document.createEvent(name); + tran = checker[name] + break; + } catch (e) { + } + } + if (typeof tran === "string") { + supportTransition = true + transitionEndEvent = tran + } + + //大致上有两种选择 + //IE10+, Firefox 16+ & Opera 12.1+: animationend + //Chrome/Safari: webkitAnimationEnd + //http://blogs.msdn.com/b/davrous/archive/2011/12/06/introduction-to-css3-animat ions.aspx + //IE10也可以使用MSAnimationEnd监听,但是回调里的事件 type依然为animationend + // el.addEventListener("MSAnimationEnd", function(e) { + // alert(e.type)// animationend!!! + // }) + checker = { + 'AnimationEvent': 'animationend', + 'WebKitAnimationEvent': 'webkitAnimationEnd' + } + var ani; + for (name in checker) { + if (window[name]) { + ani = checker[name]; + break; + } + } + if (typeof ani === "string") { + supportTransition = true + animationEndEvent = ani + } + +}() + +var effectPool = []//重复利用动画实例 +function effectFactory(el, opts) { + if (!el || el.nodeType !== 1) { + return null + } + if (opts) { + var name = opts.effectName + var driver = opts.effectDriver + } else { + name = el.getAttribute("data-effect-name") + driver = el.getAttribute("data-effect-driver") + } + if (!name || !driver) { + return null + } + + var instance = effectPool.pop() || new Effect() + instance.el = el + instance.driver = driver + instance.useCss = driver !== "j" + if (instance.useCss) { + opts && avalon(el).addClass(opts.effectClass) + instance.cssEvent = driver === "t" ? transitionEndEvent : animationEndEvent + } + instance.name = name + instance.callbacks = avalon.effects[name] || {} + + return instance + + +} + +function effectBinding(elem, binding) { + var name = elem.getAttribute("data-effect-name") + if (name) { + binding.effectName = name + binding.effectDriver = elem.getAttribute("data-effect-driver") + var stagger = +elem.getAttribute("data-effect-stagger") + binding.effectLeaveStagger = +elem.getAttribute("data-effect-leave-stagger") || stagger + binding.effectEnterStagger = +elem.getAttribute("data-effect-enter-stagger") || stagger + binding.effectClass = elem.className || NaN + } +} +function upperFirstChar(str) { + return str.replace(/^[\S]/g, function (m) { + return m.toUpperCase() + }) +} +var effectBuffer = new Buffer() +function Effect() { +}// 动画实例,做成类的形式,是为了共用所有原型方法 + +Effect.prototype = { + contrustor: Effect, + enterClass: function () { + return getEffectClass(this, "enter") + }, + leaveClass: function () { + return getEffectClass(this, "leave") + }, + // 共享一个函数 + actionFun: function (name, before, after) { + if (document.hidden) { + return + } + var me = this + var el = me.el + var isLeave = name === "leave" + name = isLeave ? "leave" : "enter" + var oppositeName = isLeave ? "enter" : "leave" + callEffectHook(me, "abort" + upperFirstChar(oppositeName)) + callEffectHook(me, "before" + upperFirstChar(name)) + if (!isLeave) + before(el) //  这里可能做插入DOM树的操作,因此必须在修改类名前执行 + var cssCallback = function (cancel) { + el.removeEventListener(me.cssEvent, me.cssCallback) + if (isLeave) { + before(el) //这里可能做移出DOM树操作,因此必须位于动画之后 + avalon(el).removeClass(me.cssClass) + } else { + if (me.driver === "a") { + avalon(el).removeClass(me.cssClass) + } + } + if (cancel !== true) { + callEffectHook(me, "after" + upperFirstChar(name)) + after && after(el) + } + me.dispose() + } + if (me.useCss) { + if (me.cssCallback) { //如果leave动画还没有完成,立即完成 + me.cssCallback(true) + } + + me.cssClass = getEffectClass(me, name) + me.cssCallback = cssCallback + + me.update = function () { + el.addEventListener(me.cssEvent, me.cssCallback) + if (!isLeave && me.driver === "t") {//transtion延迟触发 + avalon(el).removeClass(me.cssClass) + } + } + avalon(el).addClass(me.cssClass)//animation会立即触发 + + effectBuffer.render(true) + effectBuffer.queue.push(me) + + } else { + callEffectHook(me, name, cssCallback) + + } + }, + enter: function (before, after) { + this.actionFun.apply(this, ["enter"].concat(avalon.slice(arguments))) + + }, + leave: function (before, after) { + this.actionFun.apply(this, ["leave"].concat(avalon.slice(arguments))) + + }, + dispose: function () {//销毁与回收到池子中 + this.update = this.cssCallback = null + if (effectPool.unshift(this) > 100) { + effectPool.pop() + } + } + + +} + + +function getEffectClass(instance, type) { + var a = instance.callbacks[type + "Class"] + if (typeof a === "string") + return a + if (typeof a === "function") + return a() + return instance.name + "-" + type +} + + +function callEffectHook(effect, name, cb) { + var hook = effect.callbacks[name] + if (hook) { + hook.call(effect, effect.el, cb) + } +} + +var applyEffect = function (el, dir/*[before, [after, [opts]]]*/) { + var args = aslice.call(arguments, 0) + if (typeof args[2] !== "function") { + args.splice(2, 0, noop) + } + if (typeof args[3] !== "function") { + args.splice(3, 0, noop) + } + var before = args[2] + var after = args[3] + var opts = args[4] + var effect = effectFactory(el, opts) + if (!effect) { + before() + after() + return false + } else { + var method = dir ? 'enter' : 'leave' + effect[method](before, after) + } +} + +avalon.mix(avalon.effect, { + apply: applyEffect, + append: function (el, parent, after, opts) { + return applyEffect(el, 1, function () { + parent.appendChild(el) + }, after, opts) + }, + before: function (el, target, after, opts) { + return applyEffect(el, 1, function () { + target.parentNode.insertBefore(el, target) + }, after, opts) + }, + remove: function (el, parent, after, opts) { + return applyEffect(el, 0, function () { + if (el.parentNode === parent) + parent.removeChild(el) + }, after, opts) + } +}) + + +avalon.directive("html", { + update: function (val) { + var binding = this + var elem = this.element + var isHtmlFilter = elem.nodeType !== 1 + var parent = isHtmlFilter ? elem.parentNode : elem + if (!parent) + return + val = val == null ? "" : val + + if (elem.nodeType === 3) { + var signature = generateID("html") + parent.insertBefore(DOC.createComment(signature), elem) + binding.element = DOC.createComment(signature + ":end") + parent.replaceChild(binding.element, elem) + elem = binding.element + } + if (typeof val !== "object") {//string, number, boolean + var fragment = avalon.parseHTML(String(val)) + } else if (val.nodeType === 11) { //将val转换为文档碎片 + fragment = val + } else if (val.nodeType === 1 || val.item) { + var nodes = val.nodeType === 1 ? val.childNodes : val.item + fragment = avalonFragment.cloneNode(true) + while (nodes[0]) { + fragment.appendChild(nodes[0]) + } + } + + nodes = avalon.slice(fragment.childNodes) + //插入占位符, 如果是过滤器,需要有节制地移除指定的数量,如果是html指令,直接清空 + if (isHtmlFilter) { + var endValue = elem.nodeValue.slice(0, -4) + while (true) { + var node = elem.previousSibling + if (!node || node.nodeType === 8 && node.nodeValue === endValue) { + break + } else { + parent.removeChild(node) + } + } + parent.insertBefore(fragment, elem) + } else { + avalon.clearHTML(elem).appendChild(fragment) + } + scanNodeArray(nodes, binding.vmodels) + } +}) + +avalon.directive("if", { + priority: 10, + update: function (val) { + var binding = this + var elem = this.element + var stamp = binding.stamp = +new Date() + var par + var after = function () { + if (stamp !== binding.stamp) + return + binding.recoverNode = null + } + if (binding.recoverNode) + binding.recoverNode() // 还原现场,有移动节点的都需要还原现场 + try { + if (!elem.parentNode) + return + par = elem.parentNode + } catch (e) { + return + } + if (val) { //插回DOM树 + function alway() {// jshint ignore:line + if (elem.getAttribute(binding.name)) { + elem.removeAttribute(binding.name) + scanAttr(elem, binding.vmodels) + } + binding.rollback = null + } + if (elem.nodeType === 8) { + var keep = binding.keep + var hasEffect = avalon.effect.apply(keep, 1, function () { + if (stamp !== binding.stamp) + return + elem.parentNode.replaceChild(keep, elem) + elem = binding.element = keep //这时可能为null + if (keep.getAttribute("_required")) {//#1044 + elem.required = true + elem.removeAttribute("_required") + } + if (elem.querySelectorAll) { + avalon.each(elem.querySelectorAll("[_required=true]"), function (el) { + el.required = true + el.removeAttribute("_required") + }) + } + alway() + }, after) + hasEffect = hasEffect === false + } + if (!hasEffect) + alway() + } else { //移出DOM树,并用注释节点占据原位置 + if (elem.nodeType === 1) { + if (elem.required === true) { + elem.required = false + elem.setAttribute("_required", "true") + } + try {// 如果不支持querySelectorAll或:required,可以直接无视 + avalon.each(elem.querySelectorAll(":required"), function (el) { + elem.required = false + el.setAttribute("_required", "true") + }) + } catch (e) { + } + + var node = binding.element = DOC.createComment("ms-if"), + pos = elem.nextSibling + binding.recoverNode = function () { + binding.recoverNode = null + if (node.parentNode !== par) { + par.insertBefore(node, pos) + binding.keep = elem + } + } + + avalon.effect.apply(elem, 0, function () { + binding.recoverNode = null + if (stamp !== binding.stamp) + return + elem.parentNode.replaceChild(node, elem) + binding.keep = elem //元素节点 + ifGroup.appendChild(elem) + binding.rollback = function () { + if (elem.parentNode === ifGroup) { + ifGroup.removeChild(elem) + } + } + }, after) + } + } + } +}) + + + +//ms-important绑定已经在scanTag 方法中实现 +var rnoscripts = /(?:[\s\S]+?)<\/noscript>/img +var rnoscriptText = /([\s\S]+?)<\/noscript>/im + +var getXHR = function () { + return new (window.XMLHttpRequest || ActiveXObject)("Microsoft.XMLHTTP") // jshint ignore:line +} +//将所有远程加载的模板,以字符串形式存放到这里 +var templatePool = avalon.templateCache = {} + +function getTemplateContainer(binding, id, text) { + var div = binding.templateCache && binding.templateCache[id] + if (div) { + var dom = DOC.createDocumentFragment(), + firstChild + while (firstChild = div.firstChild) { + dom.appendChild(firstChild) + } + return dom + } + return avalon.parseHTML(text) + +} +function nodesToFrag(nodes) { + var frag = DOC.createDocumentFragment() + for (var i = 0, len = nodes.length; i < len; i++) { + frag.appendChild(nodes[i]) + } + return frag +} +avalon.directive("include", { + init: directives.attr.init, + update: function (val) { + var binding = this + var elem = this.element + var vmodels = binding.vmodels + var rendered = binding.includeRendered + var effectClass = binding.effectName && binding.effectClass // 是否开启动画 + var templateCache = binding.templateCache // 是否data-include-cache + var outer = binding.includeReplace // 是否data-include-replace + var loaded = binding.includeLoaded + var target = outer ? elem.parentNode : elem + var _ele = binding._element // data-include-replace binding.element === binding.end + + binding.recoverNodes = binding.recoverNodes || avalon.noop + var scanTemplate = function (text) { + var _stamp = binding._stamp = +(new Date()) // 过滤掉频繁操作 + if (loaded) { + var newText = loaded.apply(target, [text].concat(vmodels)) + if (typeof newText === "string") + text = newText + } + if (rendered) { + checkScan(target, function () { + rendered.call(target) + }, NaN) + } + var lastID = binding.includeLastID || "_default" // 默认 + + binding.includeLastID = val + var leaveEl = templateCache && templateCache[lastID] || DOC.createElement(elem.tagName || binding._element.tagName) // 创建一个离场元素 + if (effectClass) { + leaveEl.className = effectClass + target.insertBefore(leaveEl, binding.start) // 插入到start之前,防止被错误的移动 + } + + // cache or animate,移动节点 + (templateCache || {})[lastID] = leaveEl + var fragOnDom = binding.recoverNodes() // 恢复动画中的节点 + if (fragOnDom) { + target.insertBefore(fragOnDom, binding.end) + } + while (true) { + var node = binding.start.nextSibling + if (node && node !== leaveEl && node !== binding.end) { + leaveEl.appendChild(node) + } else { + break + } + } + // 元素退场 + avalon.effect.remove(leaveEl, target, function () { + if (templateCache) { // write cache + if (_stamp === binding._stamp) + ifGroup.appendChild(leaveEl) + } + }, binding) + + + var enterEl = target, + before = avalon.noop, + after = avalon.noop + + var fragment = getTemplateContainer(binding, val, text) + var nodes = avalon.slice(fragment.childNodes) + + if (outer && effectClass) { + enterEl = _ele + enterEl.innerHTML = "" // 清空 + enterEl.setAttribute("ms-skip", "true") + target.insertBefore(enterEl, binding.end.nextSibling) // 插入到bingding.end之后避免被错误的移动 + before = function () { + enterEl.insertBefore(fragment, null) // 插入节点 + } + after = function () { + binding.recoverNodes = avalon.noop + if (_stamp === binding._stamp) { + fragment = nodesToFrag(nodes) + target.insertBefore(fragment, binding.end) // 插入真实element + scanNodeArray(nodes, vmodels) + } + if (enterEl.parentNode === target) + target.removeChild(enterEl) // 移除入场动画元素 + } + binding.recoverNodes = function () { + binding.recoverNodes = avalon.noop + return nodesToFrag(nodes) + } + } else { + before = function () {// 新添加元素的动画  + target.insertBefore(fragment, binding.end) + scanNodeArray(nodes, vmodels) + } + } + + avalon.effect.apply(enterEl, "enter", before, after) + } + + if (binding.param === "src") { + if (typeof templatePool[val] === "string") { + avalon.nextTick(function () { + scanTemplate(templatePool[val]) + }) + } else if (Array.isArray(templatePool[val])) { //#805 防止在循环绑定中发出许多相同的请求 + templatePool[val].push(scanTemplate) + } else { + var xhr = getXHR() + xhr.onreadystatechange = function () { + if (xhr.readyState === 4) { + var s = xhr.status + if (s >= 200 && s < 300 || s === 304 || s === 1223) { + var text = xhr.responseText + for (var f = 0, fn; fn = templatePool[val][f++]; ) { + fn(text) + } + templatePool[val] = text + }else{ + log("ms-include load ["+ val +"] error") + } + } + } + templatePool[val] = [scanTemplate] + xhr.open("GET", val, true) + if ("withCredentials" in xhr) { + xhr.withCredentials = true + } + xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest") + xhr.send(null) + } + } else { + //IE系列与够新的标准浏览器支持通过ID取得元素(firefox14+) + //http://tjvantoll.com/2012/07/19/dom-element-references-as-global-variables/ + var el = val && val.nodeType === 1 ? val : DOC.getElementById(val) + if (el) { + if (el.tagName === "NOSCRIPT" && !(el.innerHTML || el.fixIE78)) { //IE7-8 innerText,innerHTML都无法取得其内容,IE6能取得其innerHTML + xhr = getXHR() //IE9-11与chrome的innerHTML会得到转义的内容,它们的innerText可以 + xhr.open("GET", location, false) + xhr.send(null) + //http://bbs.csdn.net/topics/390349046?page=1#post-393492653 + var noscripts = DOC.getElementsByTagName("noscript") + var array = (xhr.responseText || "").match(rnoscripts) || [] + var n = array.length + for (var i = 0; i < n; i++) { + var tag = noscripts[i] + if (tag) { //IE6-8中noscript标签的innerHTML,innerText是只读的 + tag.style.display = "none" //http://haslayout.net/css/noscript-Ghost-Bug + tag.fixIE78 = (array[i].match(rnoscriptText) || ["", " "])[1] + } + } + } + avalon.nextTick(function () { + scanTemplate(el.fixIE78 || el.value || el.innerText || el.innerHTML) + }) + } + } + } +}) + +var rdash = /\(([^)]*)\)/ +var onDir = avalon.directive("on", { + priority: 3000, + init: function (binding) { + var value = binding.expr + binding.type = "on" + var eventType = binding.param.replace(/-\d+$/, "") // ms-on-mousemove-10 + if (typeof onDir[eventType + "Hook"] === "function") { + onDir[eventType + "Hook"](binding) + } + if (value.indexOf("(") > 0 && value.indexOf(")") > -1) { + var matched = (value.match(rdash) || ["", ""])[1].trim() + if (matched === "" || matched === "$event") { // aaa() aaa($event)当成aaa处理 + value = value.replace(rdash, "") + } + } + binding.expr = value + }, + update: function (callback) { + var binding = this + var elem = this.element + callback = function (e) { + var fn = binding.getter || noop + return fn.apply(this, binding.args.concat(e)) + } + + var eventType = binding.param.replace(/-\d+$/, "") // ms-on-mousemove-10 + if (eventType === "scan") { + callback.call(elem, { + type: eventType + }) + } else if (typeof binding.specialBind === "function") { + binding.specialBind(elem, callback) + } else { + var removeFn = avalon.bind(elem, eventType, callback) + } + binding.rollback = function () { + if (typeof binding.specialUnbind === "function") { + binding.specialUnbind() + } else { + avalon.unbind(elem, eventType, removeFn) + } + } + } +}) +avalon.directive("repeat", { + priority: 90, + init: function (binding) { + var type = binding.type + binding.cache = {} //用于存放代理VM + binding.enterCount = 0 + + var elem = binding.element + if (elem.nodeType === 1) { + elem.removeAttribute(binding.name) + effectBinding(elem, binding) + binding.param = binding.param || "el" + binding.sortedCallback = getBindingCallback(elem, "data-with-sorted", binding.vmodels) + // binding.renderedCallback = + var rendered = getBindingCallback(elem, "data-" + type + "-rendered", binding.vmodels) + + var signature = generateID(type) + var start = DOC.createComment(signature + ":start") + var end = binding.element = DOC.createComment(signature + ":end") + binding.signature = signature + binding.start = start + binding.template = avalonFragment.cloneNode(false) + if (type === "repeat") { + var parent = elem.parentNode + parent.replaceChild(end, elem) + parent.insertBefore(start, end) + binding.template.appendChild(elem) + } else { + while (elem.firstChild) { + binding.template.appendChild(elem.firstChild) + } + elem.appendChild(start) + elem.appendChild(end) + parent = elem + } + binding.element = end + + if (rendered) { + var removeFn = avalon.bind(parent, "datasetchanged", function () { + rendered.apply(parent, parent.args) + avalon.unbind(parent, "datasetchanged", removeFn) + parent.msRendered = rendered + }) + } + } + }, + update: function (value, oldValue) { + var binding = this + var xtype = this.xtype + + this.enterCount += 1 + var init = !oldValue + if (init) { + binding.$outer = {} + var check0 = "$key" + var check1 = "$val" + if (xtype === "array") { + check0 = "$first" + check1 = "$last" + } + for (var i = 0, v; v = binding.vmodels[i++]; ) { + if (v.hasOwnProperty(check0) && v.hasOwnProperty(check1)) { + binding.$outer = v + break + } + } + } + var track = this.track + if (binding.sortedCallback) { //如果有回调,则让它们排序 + var keys2 = binding.sortedCallback.call(parent, track) + if (keys2 && Array.isArray(keys2)) { + track = keys2 + } + } + + var action = "move" + binding.$repeat = value + var fragments = [] + var transation = init && avalonFragment.cloneNode(false) + var proxies = [] + var param = this.param + var retain = avalon.mix({}, this.cache) + var elem = this.element + var length = track.length + + var parent = elem.parentNode + for (i = 0; i < length; i++) { + + var keyOrId = track[i] //array为随机数, object 为keyName + var proxy = retain[keyOrId] + if (!proxy) { + proxy = getProxyVM(this) + + if (xtype === "array") { + action = "add" + proxy.$id = keyOrId + + proxy[param] = value[i] //index + + } else { + action = "append" + proxy.$key = keyOrId + proxy.$val = value[keyOrId] //key + } + this.cache[keyOrId] = proxy + var node = proxy.$anchor || (proxy.$anchor = elem.cloneNode(false)) + node.nodeValue = this.signature + shimController(binding, transation, proxy, fragments, init && !binding.effectDriver) + decorateProxy(proxy, binding, xtype) + } else { +// if (xtype === "array") { +// proxy[param] = value[i] +// } + fragments.push({}) + retain[keyOrId] = true + } + + //重写proxy + if (this.enterCount === 1) {// 防止多次进入,导致位置不对 + proxy.$active = false + proxy.$oldIndex = proxy.$index + proxy.$active = true + proxy.$index = i + + } + + if (xtype === "array") { + proxy.$first = i === 0 + proxy.$last = i === length - 1 + // proxy[param] = value[i] + } else { + proxy.$val = toJson(value[keyOrId]) // 这里是处理vm.object = newObject的情况 + } + proxies.push(proxy) + } + this.proxies = proxies + if (init && !binding.effectDriver) { + parent.insertBefore(transation, elem) + fragments.forEach(function (fragment) { + scanNodeArray(fragment.nodes || [], fragment.vmodels) + //if(fragment.vmodels.length > 2) + fragment.nodes = fragment.vmodels = null + })// jshint ignore:line + } else { + + var staggerIndex = binding.staggerIndex = 0 + for (keyOrId in retain) { + if (retain[keyOrId] !== true) { + + action = "del" + removeItem(retain[keyOrId].$anchor, binding) + // avalon.log("删除", keyOrId) + // 相当于delete binding.cache[key] + proxyRecycler(this.cache, keyOrId, param) + retain[keyOrId] = null + } + } + + // console.log(effectEnterStagger) + for (i = 0; i < length; i++) { + proxy = proxies[i] + keyOrId = xtype === "array" ? proxy.$id : proxy.$key + var pre = proxies[i - 1] + var preEl = pre ? pre.$anchor : binding.start + if (!retain[keyOrId]) {//如果还没有插入到DOM树 + (function (fragment, preElement) { + var nodes = fragment.nodes + var vmodels = fragment.vmodels + if (nodes) { + staggerIndex = mayStaggerAnimate(binding.effectEnterStagger, function () { + parent.insertBefore(fragment.content, preElement.nextSibling) + scanNodeArray(nodes, vmodels) + animateRepeat(nodes, 1, binding) + }, staggerIndex) + } + fragment.nodes = fragment.vmodels = null + })(fragments[i], preEl)// jshint ignore:line + // avalon.log("插入") + + } else if (proxy.$index !== proxy.$oldIndex) { + (function (proxy2, preElement) { + staggerIndex = mayStaggerAnimate(binding.effectEnterStagger, function () { + var curNode = removeItem(proxy2.$anchor)// 如果位置被挪动了 + var inserted = avalon.slice(curNode.childNodes) + parent.insertBefore(curNode, preElement.nextSibling) + animateRepeat(inserted, 1, binding) + }, staggerIndex) + })(proxy, preEl)// jshint ignore:line + + // avalon.log("移动", proxy.$oldIndex, "-->", proxy.$index) + } + } + + } + if (!value.$track) {//如果是非监控对象,那么就将其$events清空,阻止其持续监听 + for (keyOrId in this.cache) { + proxyRecycler(this.cache, keyOrId, param) + } + + } + + //repeat --> duplex + (function (args) { + parent.args = args + if (parent.msRendered) {//第一次事件触发,以后直接调用 + parent.msRendered.apply(parent, args) + } + })(kernel.newWatch ? arguments : [action]); + var id = setTimeout(function () { + clearTimeout(id) + //触发上层的select回调及自己的rendered回调 + avalon.fireDom(parent, "datasetchanged", { + bubble: parent.msHasEvent + }) + }) + this.enterCount -= 1 + + } + +}) + +"with,each".replace(rword, function (name) { + directives[name] = avalon.mix({}, directives.repeat, { + priority: 1400 + }) +}) + + +function animateRepeat(nodes, isEnter, binding) { + for (var i = 0, node; node = nodes[i++]; ) { + if (node.className === binding.effectClass) { + avalon.effect.apply(node, isEnter, noop, noop, binding) + } + } +} + +function mayStaggerAnimate(staggerTime, callback, index) { + if (staggerTime) { + setTimeout(callback, (++index) * staggerTime) + } else { + callback() + } + return index +} + + +function removeItem(node, binding) { + var fragment = avalonFragment.cloneNode(false) + var last = node + var breakText = last.nodeValue + var staggerIndex = binding && Math.max(+binding.staggerIndex, 0) + var nodes = avalon.slice(last.parentNode.childNodes) + var index = nodes.indexOf(last) + while (true) { + var pre = nodes[--index] //node.previousSibling + if (!pre || String(pre.nodeValue).indexOf(breakText) === 0) { + break + } + + if (binding && (pre.className === binding.effectClass)) { + node = pre; + (function (cur) { + binding.staggerIndex = mayStaggerAnimate(binding.effectLeaveStagger, function () { + avalon.effect.apply(cur, 0, noop, function () { + fragment.appendChild(cur) + }, binding) + }, staggerIndex) + })(pre);// jshint ignore:line + } else { + fragment.insertBefore(pre, fragment.firstChild) + } + } + fragment.appendChild(last) + return fragment +} + + +function shimController(data, transation, proxy, fragments, init) { + var content = data.template.cloneNode(true) + var nodes = avalon.slice(content.childNodes) + content.appendChild(proxy.$anchor) + init && transation.appendChild(content) + var nv = [proxy].concat(data.vmodels) + var fragment = { + nodes: nodes, + vmodels: nv, + content: content + } + fragments.push(fragment) +} +// {} --> {xx: 0, yy: 1, zz: 2} add +// {xx: 0, yy: 1, zz: 2} --> {xx: 0, yy: 1, zz: 2, uu: 3} +// [xx: 0, yy: 1, zz: 2} --> {xx: 0, zz: 1, yy: 2} + +function getProxyVM(binding) { + var agent = binding.xtype === "object" ? withProxyAgent : eachProxyAgent + var proxy = agent(binding) + var node = proxy.$anchor || (proxy.$anchor = binding.element.cloneNode(false)) + node.nodeValue = binding.signature + proxy.$outer = binding.$outer + return proxy +} + +var eachProxyPool = [] + +function eachProxyAgent(data, proxy) { + var itemName = data.param || "el" + for (var i = 0, n = eachProxyPool.length; i < n; i++) { + var candidate = eachProxyPool[i] + if (candidate && candidate.hasOwnProperty(itemName)) { + eachProxyPool.splice(i, 1) + proxy = candidate + break + } + } + if (!proxy) { + proxy = eachProxyFactory(itemName) + } + return proxy +} + +function eachProxyFactory(itemName) { + var source = { + $outer: {}, + $index: 0, + $oldIndex: 0, + $anchor: null, + //----- + $first: false, + $last: false, + $remove: avalon.noop + } + source[itemName] = NaN + + var force = { + $last: 1, + $first: 1, + $index: 1 + } + force[itemName] = 1 + var proxy = modelFactory(source, { + force: force + }) + proxy.$id = generateID("$proxy$each") + return proxy +} + +function decorateProxy(proxy, binding, type) { + if (type === "array") { + proxy.$remove = function () { + + binding.$repeat.removeAt(proxy.$index) + } + var param = binding.param + + + proxy.$watch(param, function (a) { + var index = proxy.$index + binding.$repeat[index] = a + }) + } else { + proxy.$watch("$val", function fn(a) { + binding.$repeat[proxy.$key] = a + }) + } +} + +var withProxyPool = [] + +function withProxyAgent() { + return withProxyPool.pop() || withProxyFactory() +} + +function withProxyFactory() { + var proxy = modelFactory({ + $key: "", + $val: NaN, + $index: 0, + $oldIndex: 0, + $outer: {}, + $anchor: null + }, { + force: { + $key: 1, + $val: 1, + $index: 1 + } + }) + proxy.$id = generateID("$proxy$with") + return proxy +} + + +function proxyRecycler(cache, key, param) { + var proxy = cache[key] + if (proxy) { + var proxyPool = proxy.$id.indexOf("$proxy$each") === 0 ? eachProxyPool : withProxyPool + proxy.$outer = {} + + for (var i in proxy.$events) { + var a = proxy.$events[i] + if (Array.isArray(a)) { + a.length = 0 + if (i === param) { + proxy[param] = NaN + + } else if (i === "$val") { + proxy.$val = NaN + } + } + } + + if (proxyPool.unshift(proxy) > kernel.maxRepeatSize) { + proxyPool.pop() + } + delete cache[key] + } +} +/********************************************************************* + * 各种指令 * + **********************************************************************/ +//ms-skip绑定已经在scanTag 方法中实现 +avalon.directive("text", { + update: function (value) { + var elem = this.element + value = value == null ? "" : value //不在页面上显示undefined null + if (elem.nodeType === 3) { //绑定在文本节点上 + try { //IE对游离于DOM树外的节点赋值会报错 + elem.data = value + } catch (e) { + } + } else { //绑定在特性节点上 + if ("textContent" in elem) { + elem.textContent = value + } else { + elem.innerText = value + } + } + } +}) +function parseDisplay(nodeName, val) { + //用于取得此类标签的默认display值 + var key = "_" + nodeName + if (!parseDisplay[key]) { + var node = DOC.createElement(nodeName) + root.appendChild(node) + if (W3C) { + val = getComputedStyle(node, null).display + } else { + val = node.currentStyle.display + } + root.removeChild(node) + parseDisplay[key] = val + } + return parseDisplay[key] +} + +avalon.parseDisplay = parseDisplay + +avalon.directive("visible", { + init: function (binding) { + effectBinding(binding.element, binding) + }, + update: function (val) { + var binding = this, elem = this.element, stamp + var noEffect = !this.effectName + if (!this.stamp) { + stamp = this.stamp = +new Date + if (val) { + elem.style.display = binding.display || "" + if (avalon(elem).css("display") === "none") { + elem.style.display = binding.display = parseDisplay(elem.nodeName) + } + } else { + elem.style.display = "none" + } + return + } + stamp = this.stamp = +new Date + if (val) { + avalon.effect.apply(elem, 1, function () { + if (stamp !== binding.stamp) + return + var driver = elem.getAttribute("data-effect-driver") || "a" + + if (noEffect) {//不用动画时走这里 + elem.style.display = binding.display || "" + } + // "a", "t" + if (driver === "a" || driver === "t") { + if (avalon(elem).css("display") === "none") { + elem.style.display = binding.display || parseDisplay(elem.nodeName) + } + } + }) + } else { + avalon.effect.apply(elem, 0, function () { + if (stamp !== binding.stamp) + return + elem.style.display = "none" + }) + } + } +}) + +/********************************************************************* + * 自带过滤器 * + **********************************************************************/ +var rscripts = /]*>([\S\s]*?)<\/script\s*>/gim +var ron = /\s+(on[^=\s]+)(?:=("[^"]*"|'[^']*'|[^\s>]+))?/g +var ropen = /<\w+\b(?:(["'])[^"]*?(\1)|[^>])*>/ig +var rsanitize = { + a: /\b(href)\=("javascript[^"]*"|'javascript[^']*')/ig, + img: /\b(src)\=("javascript[^"]*"|'javascript[^']*')/ig, + form: /\b(action)\=("javascript[^"]*"|'javascript[^']*')/ig +} +var rsurrogate = /[\uD800-\uDBFF][\uDC00-\uDFFF]/g +var rnoalphanumeric = /([^\#-~| |!])/g; + +function numberFormat(number, decimals, point, thousands) { + //form http://phpjs.org/functions/number_format/ + //number 必需,要格式化的数字 + //decimals 可选,规定多少个小数位。 + //point 可选,规定用作小数点的字符串(默认为 . )。 + //thousands 可选,规定用作千位分隔符的字符串(默认为 , ),如果设置了该参数,那么所有其他参数都是必需的。 + number = (number + '') + .replace(/[^0-9+\-Ee.]/g, '') + var n = !isFinite(+number) ? 0 : +number, + prec = !isFinite(+decimals) ? 3 : Math.abs(decimals), + sep = thousands || ",", + dec = point || ".", + s = '', + toFixedFix = function(n, prec) { + var k = Math.pow(10, prec) + return '' + (Math.round(n * k) / k) + .toFixed(prec) + } + // Fix for IE parseFloat(0.55).toFixed(0) = 0; + s = (prec ? toFixedFix(n, prec) : '' + Math.round(n)) + .split('.') + if (s[0].length > 3) { + s[0] = s[0].replace(/\B(?=(?:\d{3})+(?!\d))/g, sep) + } + if ((s[1] || '') + .length < prec) { + s[1] = s[1] || '' + s[1] += new Array(prec - s[1].length + 1) + .join('0') + } + return s.join(dec) +} + + +var filters = avalon.filters = { + uppercase: function(str) { + return str.toUpperCase() + }, + lowercase: function(str) { + return str.toLowerCase() + }, + truncate: function(str, length, truncation) { + //length,新字符串长度,truncation,新字符串的结尾的字段,返回新字符串 + length = length || 30 + truncation = typeof truncation === "string" ? truncation : "..." + return str.length > length ? str.slice(0, length - truncation.length) + truncation : String(str) + }, + $filter: function(val) { + for (var i = 1, n = arguments.length; i < n; i++) { + var array = arguments[i] + var fn = avalon.filters[array[0]] + if (typeof fn === "function") { + var arr = [val].concat(array.slice(1)) + val = fn.apply(null, arr) + } + } + return val + }, + camelize: camelize, + //https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet + // chrome + // chrome + // IE67chrome + // IE67chrome + // IE67chrome + sanitize: function(str) { + return str.replace(rscripts, "").replace(ropen, function(a, b) { + var match = a.toLowerCase().match(/<(\w+)\s/) + if (match) { //处理a标签的href属性,img标签的src属性,form标签的action属性 + var reg = rsanitize[match[1]] + if (reg) { + a = a.replace(reg, function(s, name, value) { + var quote = value.charAt(0) + return name + "=" + quote + "javascript:void(0)" + quote// jshint ignore:line + }) + } + } + return a.replace(ron, " ").replace(/\s+/g, " ") //移除onXXX事件 + }) + }, + escape: function(str) { + //将字符串经过 str 转义得到适合在页面中显示的内容, 例如替换 < 为 < + return String(str). + replace(/&/g, '&'). + replace(rsurrogate, function(value) { + var hi = value.charCodeAt(0) + var low = value.charCodeAt(1) + return '&#' + (((hi - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000) + ';' + }). + replace(rnoalphanumeric, function(value) { + return '&#' + value.charCodeAt(0) + ';' + }). + replace(//g, '>') + }, + currency: function(amount, symbol, fractionSize) { + return (symbol || "\uFFE5") + numberFormat(amount, isFinite(fractionSize) ? fractionSize : 2) + }, + number: numberFormat +} +/* + 'yyyy': 4 digit representation of year (e.g. AD 1 => 0001, AD 2010 => 2010) + 'yy': 2 digit representation of year, padded (00-99). (e.g. AD 2001 => 01, AD 2010 => 10) + 'y': 1 digit representation of year, e.g. (AD 1 => 1, AD 199 => 199) + 'MMMM': Month in year (January-December) + 'MMM': Month in year (Jan-Dec) + 'MM': Month in year, padded (01-12) + 'M': Month in year (1-12) + 'dd': Day in month, padded (01-31) + 'd': Day in month (1-31) + 'EEEE': Day in Week,(Sunday-Saturday) + 'EEE': Day in Week, (Sun-Sat) + 'HH': Hour in day, padded (00-23) + 'H': Hour in day (0-23) + 'hh': Hour in am/pm, padded (01-12) + 'h': Hour in am/pm, (1-12) + 'mm': Minute in hour, padded (00-59) + 'm': Minute in hour (0-59) + 'ss': Second in minute, padded (00-59) + 's': Second in minute (0-59) + 'a': am/pm marker + 'Z': 4 digit (+sign) representation of the timezone offset (-1200-+1200) + format string can also be one of the following predefined localizable formats: + + 'medium': equivalent to 'MMM d, y h:mm:ss a' for en_US locale (e.g. Sep 3, 2010 12:05:08 pm) + 'short': equivalent to 'M/d/yy h:mm a' for en_US locale (e.g. 9/3/10 12:05 pm) + 'fullDate': equivalent to 'EEEE, MMMM d,y' for en_US locale (e.g. Friday, September 3, 2010) + 'longDate': equivalent to 'MMMM d, y' for en_US locale (e.g. September 3, 2010 + 'mediumDate': equivalent to 'MMM d, y' for en_US locale (e.g. Sep 3, 2010) + 'shortDate': equivalent to 'M/d/yy' for en_US locale (e.g. 9/3/10) + 'mediumTime': equivalent to 'h:mm:ss a' for en_US locale (e.g. 12:05:08 pm) + 'shortTime': equivalent to 'h:mm a' for en_US locale (e.g. 12:05 pm) + */ +new function() {// jshint ignore:line + function toInt(str) { + return parseInt(str, 10) || 0 + } + + function padNumber(num, digits, trim) { + var neg = "" + if (num < 0) { + neg = '-' + num = -num + } + num = "" + num + while (num.length < digits) + num = "0" + num + if (trim) + num = num.substr(num.length - digits) + return neg + num + } + + function dateGetter(name, size, offset, trim) { + return function(date) { + var value = date["get" + name]() + if (offset > 0 || value > -offset) + value += offset + if (value === 0 && offset === -12) { + value = 12 + } + return padNumber(value, size, trim) + } + } + + function dateStrGetter(name, shortForm) { + return function(date, formats) { + var value = date["get" + name]() + var get = (shortForm ? ("SHORT" + name) : name).toUpperCase() + return formats[get][value] + } + } + + function timeZoneGetter(date) { + var zone = -1 * date.getTimezoneOffset() + var paddedZone = (zone >= 0) ? "+" : "" + paddedZone += padNumber(Math[zone > 0 ? "floor" : "ceil"](zone / 60), 2) + padNumber(Math.abs(zone % 60), 2) + return paddedZone + } + //取得上午下午 + + function ampmGetter(date, formats) { + return date.getHours() < 12 ? formats.AMPMS[0] : formats.AMPMS[1] + } + var DATE_FORMATS = { + yyyy: dateGetter("FullYear", 4), + yy: dateGetter("FullYear", 2, 0, true), + y: dateGetter("FullYear", 1), + MMMM: dateStrGetter("Month"), + MMM: dateStrGetter("Month", true), + MM: dateGetter("Month", 2, 1), + M: dateGetter("Month", 1, 1), + dd: dateGetter("Date", 2), + d: dateGetter("Date", 1), + HH: dateGetter("Hours", 2), + H: dateGetter("Hours", 1), + hh: dateGetter("Hours", 2, -12), + h: dateGetter("Hours", 1, -12), + mm: dateGetter("Minutes", 2), + m: dateGetter("Minutes", 1), + ss: dateGetter("Seconds", 2), + s: dateGetter("Seconds", 1), + sss: dateGetter("Milliseconds", 3), + EEEE: dateStrGetter("Day"), + EEE: dateStrGetter("Day", true), + a: ampmGetter, + Z: timeZoneGetter + } + var rdateFormat = /((?:[^yMdHhmsaZE']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+|H+|h+|m+|s+|a|Z))(.*)/ + var raspnetjson = /^\/Date\((\d+)\)\/$/ + filters.date = function(date, format) { + var locate = filters.date.locate, + text = "", + parts = [], + fn, match + format = format || "mediumDate" + format = locate[format] || format + if (typeof date === "string") { + if (/^\d+$/.test(date)) { + date = toInt(date) + } else if (raspnetjson.test(date)) { + date = +RegExp.$1 + } else { + var trimDate = date.trim() + var dateArray = [0, 0, 0, 0, 0, 0, 0] + var oDate = new Date(0) + //取得年月日 + trimDate = trimDate.replace(/^(\d+)\D(\d+)\D(\d+)/, function(_, a, b, c) { + var array = c.length === 4 ? [c, a, b] : [a, b, c] + dateArray[0] = toInt(array[0]) //年 + dateArray[1] = toInt(array[1]) - 1 //月 + dateArray[2] = toInt(array[2]) //日 + return "" + }) + var dateSetter = oDate.setFullYear + var timeSetter = oDate.setHours + trimDate = trimDate.replace(/[T\s](\d+):(\d+):?(\d+)?\.?(\d)?/, function(_, a, b, c, d) { + dateArray[3] = toInt(a) //小时 + dateArray[4] = toInt(b) //分钟 + dateArray[5] = toInt(c) //秒 + if (d) { //毫秒 + dateArray[6] = Math.round(parseFloat("0." + d) * 1000) + } + return "" + }) + var tzHour = 0 + var tzMin = 0 + trimDate = trimDate.replace(/Z|([+-])(\d\d):?(\d\d)/, function(z, symbol, c, d) { + dateSetter = oDate.setUTCFullYear + timeSetter = oDate.setUTCHours + if (symbol) { + tzHour = toInt(symbol + c) + tzMin = toInt(symbol + d) + } + return "" + }) + + dateArray[3] -= tzHour + dateArray[4] -= tzMin + dateSetter.apply(oDate, dateArray.slice(0, 3)) + timeSetter.apply(oDate, dateArray.slice(3)) + date = oDate + } + } + if (typeof date === "number") { + date = new Date(date) + } + if (avalon.type(date) !== "date") { + return + } + while (format) { + match = rdateFormat.exec(format) + if (match) { + parts = parts.concat(match.slice(1)) + format = parts.pop() + } else { + parts.push(format) + format = null + } + } + parts.forEach(function(value) { + fn = DATE_FORMATS[value] + text += fn ? fn(date, locate) : value.replace(/(^'|'$)/g, "").replace(/''/g, "'") + }) + return text + } + var locate = { + AMPMS: { + 0: "上午", + 1: "下午" + }, + DAY: { + 0: "星期日", + 1: "星期一", + 2: "星期二", + 3: "星期三", + 4: "星期四", + 5: "星期五", + 6: "星期六" + }, + MONTH: { + 0: "1月", + 1: "2月", + 2: "3月", + 3: "4月", + 4: "5月", + 5: "6月", + 6: "7月", + 7: "8月", + 8: "9月", + 9: "10月", + 10: "11月", + 11: "12月" + }, + SHORTDAY: { + "0": "周日", + "1": "周一", + "2": "周二", + "3": "周三", + "4": "周四", + "5": "周五", + "6": "周六" + }, + fullDate: "y年M月d日EEEE", + longDate: "y年M月d日", + medium: "yyyy-M-d H:mm:ss", + mediumDate: "yyyy-M-d", + mediumTime: "H:mm:ss", + "short": "yy-M-d ah:mm", + shortDate: "yy-M-d", + shortTime: "ah:mm" + } + locate.SHORTMONTH = locate.MONTH + filters.date.locate = locate +}// jshint ignore:line +/********************************************************************* + * DOMReady * + **********************************************************************/ + +var readyList = [], + isReady +var fireReady = function (fn) { + isReady = true + var require = avalon.require + if (require && require.checkDeps) { + modules["domReady!"].state = 4 + require.checkDeps() + } + while (fn = readyList.shift()) { + fn(avalon) + } +} + +function doScrollCheck() { + try { //IE下通过doScrollCheck检测DOM树是否建完 + root.doScroll("left") + fireReady() + } catch (e) { + setTimeout(doScrollCheck) + } +} + +if (DOC.readyState === "complete") { + setTimeout(fireReady) //如果在domReady之外加载 +} else if (W3C) { + DOC.addEventListener("DOMContentLoaded", fireReady) +} else { + DOC.attachEvent("onreadystatechange", function () { + if (DOC.readyState === "complete") { + fireReady() + } + }) + try { + var isTop = window.frameElement === null + } catch (e) {} + if (root.doScroll && isTop && window.external) { //fix IE iframe BUG + doScrollCheck() + } +} +avalon.bind(window, "load", fireReady) + +avalon.ready = function (fn) { + if (!isReady) { + readyList.push(fn) + } else { + fn(avalon) + } +} + +avalon.config({ + loader: true +}) + +avalon.ready(function () { + avalon.scan(DOC.body) +}) + + +// Register as a named AMD module, since avalon can be concatenated with other +// files that may use define, but not via a proper concatenation script that +// understands anonymous AMD modules. A named AMD is safest and most robust +// way to register. Lowercase avalon is used because AMD module names are +// derived from file names, and Avalon is normally delivered in a lowercase +// file name. Do this after creating the global so that if an AMD module wants +// to call noConflict to hide this version of avalon, it will work. + +// Note that for maximum portability, libraries that are not avalon should +// declare themselves as anonymous modules, and avoid setting a global if an +// AMD loader is present. avalon is a special case. For more information, see +// https://github.com/jrburke/requirejs/wiki/Updating-existing-libraries#wiki-anon + if (typeof define === "function" && define.amd) { + define("avalon", [], function() { + return avalon + }) + } +// Map over avalon in case of overwrite + var _avalon = window.avalon + avalon.noConflict = function(deep) { + if (deep && window.avalon === avalon) { + window.avalon = _avalon + } + return avalon + } +// Expose avalon identifiers, even in AMD +// and CommonJS for browser emulators + if (noGlobal === void 0) { + window.avalon = avalon + } + return avalon + +})); \ No newline at end of file diff --git a/static/src/js/lib/avalon/avalon14.js b/static/src/js/lib/avalon/avalon14.js new file mode 100644 index 0000000..9f41e82 --- /dev/null +++ b/static/src/js/lib/avalon/avalon14.js @@ -0,0 +1,5718 @@ +/*================================================== + Copyright (c) 2013-2015 司徒正美 and other contributors + http://www.cnblogs.com/rubylouvre/ + https://github.com/RubyLouvre + http://weibo.com/jslouvre/ + + Released under the MIT license + avalon.js 1.44 built in 2015.6.25 + support IE6+ and other browsers + ==================================================*/ +(function(global, factory) { + + if (typeof module === "object" && typeof module.exports === "object") { + // For CommonJS and CommonJS-like environments where a proper `window` + // is present, execute the factory and get avalon. + // For environments that do not have a `window` with a `document` + // (such as Node.js), expose a factory as module.exports. + // This accentuates the need for the creation of a real `window`. + // e.g. var avalon = require("avalon")(window); + module.exports = global.document ? factory(global, true) : function(w) { + if (!w.document) { + throw new Error("Avalon requires a window with a document") + } + return factory(w) + } + } else { + factory(global) + } + +// Pass this if window is not defined yet +}(typeof window !== "undefined" ? window : this, function(window, noGlobal){ + + /********************************************************************* + * 全局变量及方法 * + **********************************************************************/ + var expose = new Date() - 0 +//http://stackoverflow.com/questions/7290086/javascript-use-strict-and-nicks-find-global-function + var DOC = window.document + var head = DOC.getElementsByTagName("head")[0] //HEAD元素 + var ifGroup = head.insertBefore(document.createElement("avalon"), head.firstChild) //避免IE6 base标签BUG + ifGroup.innerHTML = "X" + ifGroup.setAttribute("ms-skip", "1") + ifGroup.className = "avalonHide" + var rnative = /\[native code\]/ //判定是否原生函数 + function log() { + if (window.console && avalon.config.debug) { + // http://stackoverflow.com/questions/8785624/how-to-safely-wrap-console-log + Function.apply.call(console.log, console, arguments) + } + } + + + var subscribers = "$" + expose + var otherRequire = window.require + var otherDefine = window.define + var innerRequire + var stopRepeatAssign = false + var rword = /[^, ]+/g //切割字符串为一个个小块,以空格或豆号分开它们,结合replace实现字符串的forEach + var rcomplexType = /^(?:object|array)$/ + var rsvg = /^\[object SVG\w*Element\]$/ + var rwindow = /^\[object (?:Window|DOMWindow|global)\]$/ + var oproto = Object.prototype + var ohasOwn = oproto.hasOwnProperty + var serialize = oproto.toString + var ap = Array.prototype + var aslice = ap.slice + var Registry = {} //将函数曝光到此对象上,方便访问器收集依赖 + var W3C = window.dispatchEvent + var root = DOC.documentElement + var avalonFragment = DOC.createDocumentFragment() + var cinerator = DOC.createElement("div") + var class2type = {} + "Boolean Number String Function Array Date RegExp Object Error".replace(rword, function (name) { + class2type["[object " + name + "]"] = name.toLowerCase() + }) + + + function noop() { + } + + + function oneObject(array, val) { + if (typeof array === "string") { + array = array.match(rword) || [] + } + var result = {}, + value = val !== void 0 ? val : 1 + for (var i = 0, n = array.length; i < n; i++) { + result[array[i]] = value + } + return result + } + +//生成UUID http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript + var generateID = function (prefix) { + prefix = prefix || "avalon" + return String(Math.random() + Math.random()).replace(/\d\.\d{4}/, prefix) + } + function IE() { + if (window.VBArray) { + var mode = document.documentMode + return mode ? mode : window.XMLHttpRequest ? 7 : 6 + } else { + return 0 + } + } + var IEVersion = IE() + + avalon = function (el) { //创建jQuery式的无new 实例化结构 + return new avalon.init(el) + } + + avalon.profile = function () { + if (window.console && avalon.config.profile) { + Function.apply.call(console.log, console, arguments) + } + } + + /*视浏览器情况采用最快的异步回调*/ + avalon.nextTick = new function () {// jshint ignore:line + var tickImmediate = window.setImmediate + var tickObserver = window.MutationObserver + var tickPost = W3C && window.postMessage + if (tickImmediate) { + return tickImmediate.bind(window) + } + + var queue = [] + function callback() { + var n = queue.length + for (var i = 0; i < n; i++) { + queue[i]() + } + queue = queue.slice(n) + } + + if (tickObserver) { + var node = document.createTextNode("avalon") + new tickObserver(callback).observe(node, {characterData: true})// jshint ignore:line + return function (fn) { + queue.push(fn) + node.data = Math.random() + } + } + + if (tickPost) { + window.addEventListener("message", function (e) { + var source = e.source + if ((source === window || source === null) && e.data === "process-tick") { + e.stopPropagation() + callback() + } + }) + + return function (fn) { + queue.push(fn) + window.postMessage('process-tick', '*') + } + } + + return function (fn) { + setTimeout(fn, 0) + } + }// jshint ignore:line + /********************************************************************* + * avalon的静态方法定义区 * + **********************************************************************/ + avalon.init = function(el) { + this[0] = this.element = el + } + avalon.fn = avalon.prototype = avalon.init.prototype + + avalon.type = function(obj) { //取得目标的类型 + if (obj == null) { + return String(obj) + } + // 早期的webkit内核浏览器实现了已废弃的ecma262v4标准,可以将正则字面量当作函数使用,因此typeof在判定正则时会返回function + return typeof obj === "object" || typeof obj === "function" ? + class2type[serialize.call(obj)] || "object" : + typeof obj + } + + var isFunction = typeof alert === "object" ? function(fn) { + try { + return /^\s*\bfunction\b/.test(fn + "") + } catch (e) { + return false + } + } : function(fn) { + return serialize.call(fn) === "[object Function]" + } + avalon.isFunction = isFunction + + avalon.isWindow = function(obj) { + if (!obj) + return false + // 利用IE678 window == document为true,document == window竟然为false的神奇特性 + // 标准浏览器及IE9,IE10等使用 正则检测 + return obj == obj.document && obj.document != obj //jshint ignore:line + } + + function isWindow(obj) { + return rwindow.test(serialize.call(obj)) + } + if (isWindow(window)) { + avalon.isWindow = isWindow + } + var enu + for (enu in avalon({})) { + break + } + var enumerateBUG = enu !== "0" //IE6下为true, 其他为false + /*判定是否是一个朴素的javascript对象(Object),不是DOM对象,不是BOM对象,不是自定义类的实例*/ + avalon.isPlainObject = function(obj, key) { + if (!obj || avalon.type(obj) !== "object" || obj.nodeType || avalon.isWindow(obj)) { + return false; + } + try { //IE内置对象没有constructor + if (obj.constructor && !ohasOwn.call(obj, "constructor") && !ohasOwn.call(obj.constructor.prototype, "isPrototypeOf")) { + return false; + } + } catch (e) { //IE8 9会在这里抛错 + return false; + } + if (enumerateBUG) { + for (key in obj) { + return ohasOwn.call(obj, key) + } + } + for (key in obj) { + } + return key === void 0 || ohasOwn.call(obj, key) + } + if (rnative.test(Object.getPrototypeOf)) { + avalon.isPlainObject = function(obj) { + // 简单的 typeof obj === "object"检测,会致使用isPlainObject(window)在opera下通不过 + return serialize.call(obj) === "[object Object]" && Object.getPrototypeOf(obj) === oproto + } + } +//与jQuery.extend方法,可用于浅拷贝,深拷贝 + avalon.mix = avalon.fn.mix = function() { + var options, name, src, copy, copyIsArray, clone, + target = arguments[0] || {}, + i = 1, + length = arguments.length, + deep = false + + // 如果第一个参数为布尔,判定是否深拷贝 + if (typeof target === "boolean") { + deep = target + target = arguments[1] || {} + i++ + } + + //确保接受方为一个复杂的数据类型 + if (typeof target !== "object" && !isFunction(target)) { + target = {} + } + + //如果只有一个参数,那么新成员添加于mix所在的对象上 + if (i === length) { + target = this + i-- + } + + for (; i < length; i++) { + //只处理非空参数 + if ((options = arguments[i]) != null) { + for (name in options) { + src = target[name] + try { + copy = options[name] //当options为VBS对象时报错 + } catch (e) { + continue + } + + // 防止环引用 + if (target === copy) { + continue + } + if (deep && copy && (avalon.isPlainObject(copy) || (copyIsArray = Array.isArray(copy)))) { + + if (copyIsArray) { + copyIsArray = false + clone = src && Array.isArray(src) ? src : [] + + } else { + clone = src && avalon.isPlainObject(src) ? src : {} + } + + target[name] = avalon.mix(deep, clone, copy) + } else if (copy !== void 0) { + target[name] = copy + } + } + } + } + return target + } + + function _number(a, len) { //用于模拟slice, splice的效果 + a = Math.floor(a) || 0 + return a < 0 ? Math.max(len + a, 0) : Math.min(a, len); + } + avalon.mix({ + rword: rword, + subscribers: subscribers, + version: 1.44, + ui: {}, + log: log, + slice: W3C ? function(nodes, start, end) { + return aslice.call(nodes, start, end) + } : function(nodes, start, end) { + var ret = [] + var len = nodes.length + if (end === void 0) + end = len + if (typeof end === "number" && isFinite(end)) { + start = _number(start, len) + end = _number(end, len) + for (var i = start; i < end; ++i) { + ret[i - start] = nodes[i] + } + } + return ret + }, + noop: noop, + /*如果不用Error对象封装一下,str在控制台下可能会乱码*/ + error: function(str, e) { + throw (e || Error)(str) + }, + /*将一个以空格或逗号隔开的字符串或数组,转换成一个键值都为1的对象*/ + oneObject: oneObject, + /* avalon.range(10) + => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] + avalon.range(1, 11) + => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + avalon.range(0, 30, 5) + => [0, 5, 10, 15, 20, 25] + avalon.range(0, -10, -1) + => [0, -1, -2, -3, -4, -5, -6, -7, -8, -9] + avalon.range(0) + => []*/ + range: function(start, end, step) { // 用于生成整数数组 + step || (step = 1) + if (end == null) { + end = start || 0 + start = 0 + } + var index = -1, + length = Math.max(0, Math.ceil((end - start) / step)), + result = new Array(length) + while (++index < length) { + result[index] = start + start += step + } + return result + }, + eventHooks: {}, + /*绑定事件*/ + bind: function(el, type, fn, phase) { + var hooks = avalon.eventHooks + var hook = hooks[type] + if (typeof hook === "object") { + type = hook.type + if (hook.deel) { + fn = hook.deel(el, type, fn, phase) + } + } + var callback = W3C ? fn : function(e) { + fn.call(el, fixEvent(e)); + } + if (W3C) { + el.addEventListener(type, callback, !!phase) + } else { + el.attachEvent("on" + type, callback) + } + return callback + }, + /*卸载事件*/ + unbind: function(el, type, fn, phase) { + var hooks = avalon.eventHooks + var hook = hooks[type] + var callback = fn || noop + if (typeof hook === "object") { + type = hook.type + if (hook.deel) { + fn = hook.deel(el, type, fn, false) + } + } + if (W3C) { + el.removeEventListener(type, callback, !!phase) + } else { + el.detachEvent("on" + type, callback) + } + }, + /*读写删除元素节点的样式*/ + css: function(node, name, value) { + if (node instanceof avalon) { + node = node[0] + } + var prop = /[_-]/.test(name) ? camelize(name) : name, fn + name = avalon.cssName(prop) || prop + if (value === void 0 || typeof value === "boolean") { //获取样式 + fn = cssHooks[prop + ":get"] || cssHooks["@:get"] + if (name === "background") { + name = "backgroundColor" + } + var val = fn(node, name) + return value === true ? parseFloat(val) || 0 : val + } else if (value === "") { //请除样式 + node.style[name] = "" + } else { //设置样式 + if (value == null || value !== value) { + return + } + if (isFinite(value) && !avalon.cssNumber[prop]) { + value += "px" + } + fn = cssHooks[prop + ":set"] || cssHooks["@:set"] + fn(node, name, value) + } + }, + /*遍历数组与对象,回调的第一个参数为索引或键名,第二个或元素或键值*/ + each: function(obj, fn) { + if (obj) { //排除null, undefined + var i = 0 + if (isArrayLike(obj)) { + for (var n = obj.length; i < n; i++) { + if (fn(i, obj[i]) === false) + break + } + } else { + for (i in obj) { + if (obj.hasOwnProperty(i) && fn(i, obj[i]) === false) { + break + } + } + } + } + }, + //收集元素的data-{{prefix}}-*属性,并转换为对象 + getWidgetData: function(elem, prefix) { + var raw = avalon(elem).data() + var result = {} + for (var i in raw) { + if (i.indexOf(prefix) === 0) { + result[i.replace(prefix, "").replace(/\w/, function(a) { + return a.toLowerCase() + })] = raw[i] + } + } + return result + }, + Array: { + /*只有当前数组不存在此元素时只添加它*/ + ensure: function(target, item) { + if (target.indexOf(item) === -1) { + return target.push(item) + } + }, + /*移除数组中指定位置的元素,返回布尔表示成功与否*/ + removeAt: function(target, index) { + return !!target.splice(index, 1).length + }, + /*移除数组中第一个匹配传参的那个元素,返回布尔表示成功与否*/ + remove: function(target, item) { + var index = target.indexOf(item) + if (~index) + return avalon.Array.removeAt(target, index) + return false + } + } + }) + + var bindingHandlers = avalon.bindingHandlers = {} + var bindingExecutors = avalon.bindingExecutors = {} + + /*判定是否类数组,如节点集合,纯数组,arguments与拥有非负整数的length属性的纯JS对象*/ + function isArrayLike(obj) { + if (!obj) + return false + var n = obj.length + if (n === (n >>> 0)) { //检测length属性是否为非负整数 + var type = serialize.call(obj).slice(8, -1) + if (/(?:regexp|string|function|window|global)$/i.test(type)) + return false + if (type === "Array") + return true + try { + if ({}.propertyIsEnumerable.call(obj, "length") === false) { //如果是原生对象 + return /^\s?function/.test(obj.item || obj.callee) + } + return true + } catch (e) { //IE的NodeList直接抛错 + return !obj.window //IE6-8 window + } + } + return false + } + + +// https://github.com/rsms/js-lru + var Cache = new function() {// jshint ignore:line + function LRU(maxLength) { + this.size = 0 + this.limit = maxLength + this.head = this.tail = void 0 + this._keymap = {} + } + + var p = LRU.prototype + + p.put = function(key, value) { + var entry = { + key: key, + value: value + } + this._keymap[key] = entry + if (this.tail) { + this.tail.newer = entry + entry.older = this.tail + } else { + this.head = entry + } + this.tail = entry + if (this.size === this.limit) { + this.shift() + } else { + this.size++ + } + return value + } + + p.shift = function() { + var entry = this.head + if (entry) { + this.head = this.head.newer + this.head.older = + entry.newer = + entry.older = + this._keymap[entry.key] = void 0 + } + } + p.get = function(key) { + var entry = this._keymap[key] + if (entry === void 0) + return + if (entry === this.tail) { + return entry.value + } + // HEAD--------------TAIL + // <.older .newer> + // <--- add direction -- + // A B C E + if (entry.newer) { + if (entry === this.head) { + this.head = entry.newer + } + entry.newer.older = entry.older // C <-- E. + } + if (entry.older) { + entry.older.newer = entry.newer // C. --> E + } + entry.newer = void 0 // D --x + entry.older = this.tail // D. --> E + if (this.tail) { + this.tail.newer = entry // E. <-- D + } + this.tail = entry + return entry.value + } + return LRU + }// jshint ignore:line + + /********************************************************************* + * javascript 底层补丁 * + **********************************************************************/ + if (!"司徒正美".trim) { + var rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g + String.prototype.trim = function () { + return this.replace(rtrim, "") + } + } + var hasDontEnumBug = !({ + 'toString': null + }).propertyIsEnumerable('toString'), + hasProtoEnumBug = (function () { + }).propertyIsEnumerable('prototype'), + dontEnums = [ + "toString", + "toLocaleString", + "valueOf", + "hasOwnProperty", + "isPrototypeOf", + "propertyIsEnumerable", + "constructor" + ], + dontEnumsLength = dontEnums.length; + if (!Object.keys) { + Object.keys = function (object) { //ecma262v5 15.2.3.14 + var theKeys = [] + var skipProto = hasProtoEnumBug && typeof object === "function" + if (typeof object === "string" || (object && object.callee)) { + for (var i = 0; i < object.length; ++i) { + theKeys.push(String(i)) + } + } else { + for (var name in object) { + if (!(skipProto && name === "prototype") && ohasOwn.call(object, name)) { + theKeys.push(String(name)) + } + } + } + + if (hasDontEnumBug) { + var ctor = object.constructor, + skipConstructor = ctor && ctor.prototype === object + for (var j = 0; j < dontEnumsLength; j++) { + var dontEnum = dontEnums[j] + if (!(skipConstructor && dontEnum === "constructor") && ohasOwn.call(object, dontEnum)) { + theKeys.push(dontEnum) + } + } + } + return theKeys + } + } + if (!Array.isArray) { + Array.isArray = function (a) { + return serialize.call(a) === "[object Array]" + } + } + + if (!noop.bind) { + Function.prototype.bind = function (scope) { + if (arguments.length < 2 && scope === void 0) + return this + var fn = this, + argv = arguments + return function () { + var args = [], + i + for (i = 1; i < argv.length; i++) + args.push(argv[i]) + for (i = 0; i < arguments.length; i++) + args.push(arguments[i]) + return fn.apply(scope, args) + } + } + } + + function iterator(vars, body, ret) { + var fun = 'for(var ' + vars + 'i=0,n = this.length; i < n; i++){' + body.replace('_', '((i in this) && fn.call(scope,this[i],i,this))') + '}' + ret + /* jshint ignore:start */ + return Function("fn,scope", fun) + /* jshint ignore:end */ + } + if (!rnative.test([].map)) { + avalon.mix(ap, { + //定位操作,返回数组中第一个等于给定参数的元素的索引值。 + indexOf: function (item, index) { + var n = this.length, + i = ~~index + if (i < 0) + i += n + for (; i < n; i++) + if (this[i] === item) + return i + return -1 + }, + //定位操作,同上,不过是从后遍历。 + lastIndexOf: function (item, index) { + var n = this.length, + i = index == null ? n - 1 : index + if (i < 0) + i = Math.max(0, n + i) + for (; i >= 0; i--) + if (this[i] === item) + return i + return -1 + }, + //迭代操作,将数组的元素挨个儿传入一个函数中执行。Prototype.js的对应名字为each。 + forEach: iterator("", '_', ""), + //迭代类 在数组中的每个项上运行一个函数,如果此函数的值为真,则此元素作为新数组的元素收集起来,并返回新数组 + filter: iterator('r=[],j=0,', 'if(_)r[j++]=this[i]', 'return r'), + //收集操作,将数组的元素挨个儿传入一个函数中执行,然后把它们的返回值组成一个新数组返回。Prototype.js的对应名字为collect。 + map: iterator('r=[],', 'r[i]=_', 'return r'), + //只要数组中有一个元素满足条件(放进给定函数返回true),那么它就返回true。Prototype.js的对应名字为any。 + some: iterator("", 'if(_)return true', 'return false'), + //只有数组中的元素都满足条件(放进给定函数返回true),它才返回true。Prototype.js的对应名字为all。 + every: iterator("", 'if(!_)return false', 'return true') + }) + } + /********************************************************************* + * DOM 底层补丁 * + **********************************************************************/ + + function fixContains(root, el) { + try { //IE6-8,游离于DOM树外的文本节点,访问parentNode有时会抛错 + while ((el = el.parentNode)) + if (el === root) + return true + return false + } catch (e) { + return false + } + } + avalon.contains = fixContains +//IE6-11的文档对象没有contains + if (!DOC.contains) { + DOC.contains = function (b) { + return fixContains(DOC, b) + } + } + + function outerHTML() { + return new XMLSerializer().serializeToString(this) + } + + if (window.SVGElement) { + //safari5+是把contains方法放在Element.prototype上而不是Node.prototype + if (!DOC.createTextNode("x").contains) { + Node.prototype.contains = function (arg) {//IE6-8没有Node对象 + return !!(this.compareDocumentPosition(arg) & 16) + } + } + var svgns = "http://www.w3.org/2000/svg" + var svg = DOC.createElementNS(svgns, "svg") + svg.innerHTML = '' + if (!rsvg.test(svg.firstChild)) { // #409 + function enumerateNode(node, targetNode) {// jshint ignore:line + if (node && node.childNodes) { + var nodes = node.childNodes + for (var i = 0, el; el = nodes[i++]; ) { + if (el.tagName) { + var svg = DOC.createElementNS(svgns, + el.tagName.toLowerCase()) + ap.forEach.call(el.attributes, function (attr) { + svg.setAttribute(attr.name, attr.value) //复制属性 + })// jshint ignore:line + // 递归处理子节点 + enumerateNode(el, svg) + targetNode.appendChild(svg) + } + } + } + } + Object.defineProperties(SVGElement.prototype, { + "outerHTML": {//IE9-11,firefox不支持SVG元素的innerHTML,outerHTML属性 + enumerable: true, + configurable: true, + get: outerHTML, + set: function (html) { + var tagName = this.tagName.toLowerCase(), + par = this.parentNode, + frag = avalon.parseHTML(html) + // 操作的svg,直接插入 + if (tagName === "svg") { + par.insertBefore(frag, this) + // svg节点的子节点类似 + } else { + var newFrag = DOC.createDocumentFragment() + enumerateNode(frag, newFrag) + par.insertBefore(newFrag, this) + } + par.removeChild(this) + } + }, + "innerHTML": { + enumerable: true, + configurable: true, + get: function () { + var s = this.outerHTML + var ropen = new RegExp("<" + this.nodeName + '\\b(?:(["\'])[^"]*?(\\1)|[^>])*>', "i") + var rclose = new RegExp("<\/" + this.nodeName + ">$", "i") + return s.replace(ropen, "").replace(rclose, "") + }, + set: function (html) { + if (avalon.clearHTML) { + avalon.clearHTML(this) + var frag = avalon.parseHTML(html) + enumerateNode(frag, this) + } + } + } + }) + } + } + if (!root.outerHTML && window.HTMLElement) { //firefox 到11时才有outerHTML + HTMLElement.prototype.__defineGetter__("outerHTML", outerHTML); + } + +//============================= event binding ======================= + var rmouseEvent = /^(?:mouse|contextmenu|drag)|click/ + function fixEvent(event) { + var ret = {} + for (var i in event) { + ret[i] = event[i] + } + var target = ret.target = event.srcElement + if (event.type.indexOf("key") === 0) { + ret.which = event.charCode != null ? event.charCode : event.keyCode + } else if (rmouseEvent.test(event.type)) { + var doc = target.ownerDocument || DOC + var box = doc.compatMode === "BackCompat" ? doc.body : doc.documentElement + ret.pageX = event.clientX + (box.scrollLeft >> 0) - (box.clientLeft >> 0) + ret.pageY = event.clientY + (box.scrollTop >> 0) - (box.clientTop >> 0) + ret.wheelDeltaY = ret.wheelDelta + ret.wheelDeltaX = 0 + } + ret.timeStamp = new Date() - 0 + ret.originalEvent = event + ret.preventDefault = function () { //阻止默认行为 + event.returnValue = false + } + ret.stopPropagation = function () { //阻止事件在DOM树中的传播 + event.cancelBubble = true + } + return ret + } + + var eventHooks = avalon.eventHooks +//针对firefox, chrome修正mouseenter, mouseleave + if (!("onmouseenter" in root)) { + avalon.each({ + mouseenter: "mouseover", + mouseleave: "mouseout" + }, function (origType, fixType) { + eventHooks[origType] = { + type: fixType, + deel: function (elem, _, fn) { + return function (e) { + var t = e.relatedTarget + if (!t || (t !== elem && !(elem.compareDocumentPosition(t) & 16))) { + delete e.type + e.type = origType + return fn.call(elem, e) + } + } + } + } + }) + } +//针对IE9+, w3c修正animationend + avalon.each({ + AnimationEvent: "animationend", + WebKitAnimationEvent: "webkitAnimationEnd" + }, function (construct, fixType) { + if (window[construct] && !eventHooks.animationend) { + eventHooks.animationend = { + type: fixType + } + } + }) +//针对IE6-8修正input + if (!("oninput" in DOC.createElement("input"))) { + eventHooks.input = { + type: "propertychange", + deel: function (elem, _, fn) { + return function (e) { + if (e.propertyName === "value") { + e.type = "input" + return fn.call(elem, e) + } + } + } + } + } + if (DOC.onmousewheel === void 0) { + /* IE6-11 chrome mousewheel wheelDetla 下 -120 上 120 + firefox DOMMouseScroll detail 下3 上-3 + firefox wheel detlaY 下3 上-3 + IE9-11 wheel deltaY 下40 上-40 + chrome wheel deltaY 下100 上-100 */ + var fixWheelType = DOC.onwheel !== void 0 ? "wheel" : "DOMMouseScroll" + var fixWheelDelta = fixWheelType === "wheel" ? "deltaY" : "detail" + eventHooks.mousewheel = { + type: fixWheelType, + deel: function (elem, _, fn) { + return function (e) { + e.wheelDeltaY = e.wheelDelta = e[fixWheelDelta] > 0 ? -120 : 120 + e.wheelDeltaX = 0 + if (Object.defineProperty) { + Object.defineProperty(e, "type", { + value: "mousewheel" + }) + } + fn.call(elem, e) + } + } + } + } + + + /********************************************************************* + * 配置系统 * + **********************************************************************/ + + function kernel(settings) { + for (var p in settings) { + if (!ohasOwn.call(settings, p)) + continue + var val = settings[p] + if (typeof kernel.plugins[p] === "function") { + kernel.plugins[p](val) + } else if (typeof kernel[p] === "object") { + avalon.mix(kernel[p], val) + } else { + kernel[p] = val + } + } + return this + } + var openTag, closeTag, rexpr, rexprg, rbind, rregexp = /[-.*+?^${}()|[\]\/\\]/g + + function escapeRegExp(target) { + //http://stevenlevithan.com/regex/xregexp/ + //将字符串安全格式化为正则表达式的源码 + return (target + "").replace(rregexp, "\\$&") + } + + var plugins = { + loader: function (builtin) { + var flag = innerRequire && builtin + window.require = flag ? innerRequire : otherRequire + window.define = flag ? innerRequire.define : otherDefine + }, + interpolate: function (array) { + openTag = array[0] + closeTag = array[1] + if (openTag === closeTag) { + throw new SyntaxError("openTag!==closeTag") + } else if (array + "" === "") { + kernel.commentInterpolate = true + } else { + var test = openTag + "test" + closeTag + cinerator.innerHTML = test + if (cinerator.innerHTML !== test && cinerator.innerHTML.indexOf("<") > -1) { + throw new SyntaxError("此定界符不合法") + } + cinerator.innerHTML = "" + } + var o = escapeRegExp(openTag), + c = escapeRegExp(closeTag) + rexpr = new RegExp(o + "(.*?)" + c) + rexprg = new RegExp(o + "(.*?)" + c, "g") + rbind = new RegExp(o + ".*?" + c + "|\\sms-") + } + } + + kernel.debug = true + kernel.plugins = plugins + kernel.plugins['interpolate'](["{{", "}}"]) + kernel.paths = {} + kernel.shim = {} + kernel.maxRepeatSize = 100 + avalon.config = kernel + var ravalon = /(\w+)\[(avalonctrl)="(\S+)"\]/ + var findNodes = DOC.querySelectorAll ? function(str) { + return DOC.querySelectorAll(str) + } : function(str) { + var match = str.match(ravalon) + var all = DOC.getElementsByTagName(match[1]) + var nodes = [] + for (var i = 0, el; el = all[i++]; ) { + if (el.getAttribute(match[2]) === match[3]) { + nodes.push(el) + } + } + return nodes + } + /********************************************************************* + * 事件总线 * + **********************************************************************/ + var EventBus = { + $watch: function (type, callback) { + if (typeof callback === "function") { + var callbacks = this.$events[type] + if (callbacks) { + callbacks.push(callback) + } else { + this.$events[type] = [callback] + } + } else { //重新开始监听此VM的第一重简单属性的变动 + this.$events = this.$watch.backup + } + return this + }, + $unwatch: function (type, callback) { + var n = arguments.length + if (n === 0) { //让此VM的所有$watch回调无效化 + this.$watch.backup = this.$events + this.$events = {} + } else if (n === 1) { + this.$events[type] = [] + } else { + var callbacks = this.$events[type] || [] + var i = callbacks.length + while (~--i < 0) { + if (callbacks[i] === callback) { + return callbacks.splice(i, 1) + } + } + } + return this + }, + $fire: function (type) { + var special, i, v, callback + if (/^(\w+)!(\S+)$/.test(type)) { + special = RegExp.$1 + type = RegExp.$2 + } + var events = this.$events + if (!events) + return + var args = aslice.call(arguments, 1) + var detail = [type].concat(args) + if (special === "all") { + for (i in avalon.vmodels) { + v = avalon.vmodels[i] + if (v !== this) { + v.$fire.apply(v, detail) + } + } + } else if (special === "up" || special === "down") { + var elements = events.expr ? findNodes(events.expr) : [] + if (elements.length === 0) + return + for (i in avalon.vmodels) { + v = avalon.vmodels[i] + if (v !== this) { + if (v.$events.expr) { + var eventNodes = findNodes(v.$events.expr) + if (eventNodes.length === 0) { + continue + } + //循环两个vmodel中的节点,查找匹配(向上匹配或者向下匹配)的节点并设置标识 + /* jshint ignore:start */ + ap.forEach.call(eventNodes, function (node) { + ap.forEach.call(elements, function (element) { + var ok = special === "down" ? element.contains(node) : //向下捕获 + node.contains(element) //向上冒泡 + if (ok) { + node._avalon = v //符合条件的加一个标识 + } + }); + }) + /* jshint ignore:end */ + } + } + } + var nodes = DOC.getElementsByTagName("*") //实现节点排序 + var alls = [] + ap.forEach.call(nodes, function (el) { + if (el._avalon) { + alls.push(el._avalon) + el._avalon = "" + el.removeAttribute("_avalon") + } + }) + if (special === "up") { + alls.reverse() + } + for (i = 0; callback = alls[i++]; ) { + if (callback.$fire.apply(callback, detail) === false) { + break + } + } + } else { + var callbacks = events[type] || [] + var all = events.$all || [] + for (i = 0; callback = callbacks[i++]; ) { + if (isFunction(callback)) + callback.apply(this, args) + } + for (i = 0; callback = all[i++]; ) { + if (isFunction(callback)) + callback.apply(this, arguments) + } + } + } + } + + /********************************************************************* + * modelFactory * + **********************************************************************/ +//avalon最核心的方法的两个方法之一(另一个是avalon.scan),返回一个ViewModel(VM) + var VMODELS = avalon.vmodels = {} //所有vmodel都储存在这里 + avalon.define = function (id, factory) { + var $id = id.$id || id + if (!$id) { + log("warning: vm必须指定$id") + } + if (VMODELS[$id]) { + log("warning: " + $id + " 已经存在于avalon.vmodels中") + } + if (typeof id === "object") { + var model = modelFactory(id) + } else { + var scope = { + $watch: noop + } + factory(scope) //得到所有定义 + + model = modelFactory(scope) //偷天换日,将scope换为model + stopRepeatAssign = true + factory(model) + stopRepeatAssign = false + } + model.$id = $id + return VMODELS[$id] = model + } + +//一些不需要被监听的属性 + var $$skipArray = String("$id,$watch,$unwatch,$fire,$events,$model,$skipArray,$proxy,$compute").match(rword) + var defineProperty = Object.defineProperty + var canHideOwn = true +//如果浏览器不支持ecma262v5的Object.defineProperties或者存在BUG,比如IE8 +//标准浏览器使用__defineGetter__, __defineSetter__实现 + try { + defineProperty({}, "_", { + value: "x" + }) + var defineProperties = Object.defineProperties + } catch (e) { + canHideOwn = false + } + + function modelFactory(source, $special, $model) { + if (Array.isArray(source)) { + var arr = source.concat() + source.length = 0 + var collection = arrayFactory(source) + collection.pushArray(arr) + return collection + } + //0 null undefined || Node || VModel(fix IE6-8 createWithProxy $val: val引发的BUG) + if (!source || source.nodeType > 0 || (source.$id && source.$events)) { + return source + } + var $skipArray = Array.isArray(source.$skipArray) ? source.$skipArray : [] + $skipArray.$special = $special || {} //强制要监听的属性 + var $vmodel = {} //要返回的对象, 它在IE6-8下可能被偷龙转凤 + $model = $model || {} //vmodels.$model属性 + var $events = {} //vmodel.$events属性 + var accessors = {} //监控属性 + var computed = [] + $$skipArray.forEach(function (name) { + delete source[name] + }) + + for (var i in source) { + (function (name, val, accessor) { + $model[name] = val + if (!isObservable(name, val, $skipArray)) { + return //过滤所有非监控属性 + } + //总共产生三种accessor + $events[name] = [] + var valueType = avalon.type(val) + //总共产生三种accessor + if (valueType === "object" && isFunction(val.get) && Object.keys(val).length <= 2) { + accessor = makeComputedAccessor(name, val) + computed.push(accessor) + } else if (rcomplexType.test(valueType)) { + accessor = makeComplexAccessor(name, val, valueType, $events[name]) + } else { + accessor = makeSimpleAccessor(name, val) + } + accessors[name] = accessor + })(i, source[i])// jshint ignore:line + } + + $vmodel = defineProperties($vmodel, descriptorFactory(accessors), source) //生成一个空的ViewModel + for (var name in source) { + if (!accessors[name]) { + $vmodel[name] = source[name] + } + } + //添加$id, $model, $events, $watch, $unwatch, $fire + $vmodel.$id = generateID() + $vmodel.$model = $model + $vmodel.$events = $events + for (i in EventBus) { + var fn = EventBus[i] + if (!W3C) { //在IE6-8下,VB对象的方法里的this并不指向自身,需要用bind处理一下 + fn = fn.bind($vmodel) + } + $vmodel[i] = fn + } + if (canHideOwn) { + Object.defineProperty($vmodel, "hasOwnProperty", { + value: function (name) { + return name in this.$model + }, + writable: false, + enumerable: false, + configurable: true + }) + + } else { + /* jshint ignore:start */ + $vmodel.hasOwnProperty = function (name) { + return name in $vmodel.$model + } + /* jshint ignore:end */ + } + + $vmodel.$compute = function () { + computed.forEach(function (accessor) { + dependencyDetection.begin({ + callback: function (vm, dependency) {//dependency为一个accessor + var name = dependency._name + if (dependency !== accessor) { + var list = vm.$events[name] + accessor.vm = $vmodel + injectDependency(list, accessor.digest) + } + } + }) + try { + accessor.get.call($vmodel) + } finally { + dependencyDetection.end() + } + }) + } + $vmodel.$compute() + return $vmodel + } +//创建一个简单访问器 + function makeSimpleAccessor(name, value) { + function accessor(value) { + var oldValue = accessor._value + if (arguments.length > 0) { + if (!stopRepeatAssign && !isEqual(value, oldValue)) { + accessor.updateValue(this, value) + accessor.notify(this, value, oldValue) + } + return this + } else { + dependencyDetection.collectDependency(this, accessor) + return oldValue + } + } + accessorFactory(accessor, name) + accessor._value = value + return accessor; + } + +//创建一个计算访问器 + function makeComputedAccessor(name, options) { + options.set = options.set || noop + function accessor(value) {//计算属性 + var oldValue = accessor._value + var init = "_value" in accessor + if (arguments.length > 0) { + if (stopRepeatAssign) { + return this + } + accessor.set.call(this, value) + return this + } else { + //将依赖于自己的高层访问器或视图刷新函数(以绑定对象形式)放到自己的订阅数组中 + value = accessor.get.call(this) + if (oldValue !== value) { + accessor.updateValue(this, value) + init && accessor.notify(this, value, oldValue) //触发$watch回调 + } + //将自己注入到低层访问器的订阅数组中 + return value + } + } + accessor.set = options.set || noop + accessor.get = options.get + accessorFactory(accessor, name) + var id + accessor.digest = function () { + accessor.updateValue = globalUpdateModelValue + accessor.notify = noop + accessor.call(accessor.vm) + clearTimeout(id)//如果计算属性存在多个依赖项,那么等它们都更新了才更新视图 + id = setTimeout(function () { + accessorFactory(accessor, accessor._name) + accessor.call(accessor.vm) + }) + } + return accessor + } + +//创建一个复杂访问器 + function makeComplexAccessor(name, initValue, valueType, list) { + function accessor(value) { + var oldValue = accessor._value + var son = accessor._vmodel + if (arguments.length > 0) { + if (stopRepeatAssign) { + return this + } + if (valueType === "array") { + var old = son._ + son._ = [] + son.clear() + son._ = old + son.pushArray(value) + } else if (valueType === "object") { + var $proxy = son.$proxy + var observes = this.$events[name] || [] + son = accessor._vmodel = modelFactory(value) + son.$events[subscribers] = observes + son.$proxy = $proxy + if (observes.length) { + observes.forEach(function (el) { + var fn = bindingHandlers[el.type] + if (fn) { //#753 + el.rollback && el.rollback() //还原 ms-with ms-on + fn(el, el.vmodels) + } + }) + } + } + accessor.updateValue(this, son.$model) + accessor.notify(this, this._value, oldValue) + return this + } else { + dependencyDetection.collectDependency(this, accessor) + return son + } + } + accessorFactory(accessor, name) + var son = accessor._vmodel = modelFactory(initValue) + son.$events[subscribers] = list + return accessor + } + + function globalUpdateValue(vmodel, value) { + vmodel.$model[this._name] = this._value = value + } + function globalUpdateModelValue(vmodel, value) { + vmodel.$model[this._name] = value + } + function globalNotify(vmodel, value, oldValue) { + var name = this._name + var array = vmodel.$events[name] //刷新值 + if (array) { + fireDependencies(array) //同步视图 + EventBus.$fire.call(vmodel, name, value, oldValue) //触发$watch回调 + } + } + + function accessorFactory(accessor, name) { + accessor._name = name + //同时更新_value与model + accessor.updateValue = globalUpdateValue + accessor.notify = globalNotify + } + +//比较两个值是否相等 + var isEqual = Object.is || function (v1, v2) { + if (v1 === 0 && v2 === 0) { + return 1 / v1 === 1 / v2 + } else if (v1 !== v1) { + return v2 !== v2 + } else { + return v1 === v2 + } + } + + function isObservable(name, value, $skipArray) { + if (isFunction(value) || value && value.nodeType) { + return false + } + if ($skipArray.indexOf(name) !== -1) { + return false + } + var $special = $skipArray.$special + if (name && name.charAt(0) === "$" && !$special[name]) { + return false + } + return true + } + + var descriptorFactory = W3C ? function (obj) { + var descriptors = {} + for (var i in obj) { + descriptors[i] = { + get: obj[i], + set: obj[i], + enumerable: true, + configurable: true + } + } + return descriptors + } : function (a) { + return a + } + + +//===================修复浏览器对Object.defineProperties的支持================= + if (!canHideOwn) { + if ("__defineGetter__" in avalon) { + defineProperty = function (obj, prop, desc) { + if ('value' in desc) { + obj[prop] = desc.value + } + if ("get" in desc) { + obj.__defineGetter__(prop, desc.get) + } + if ('set' in desc) { + obj.__defineSetter__(prop, desc.set) + } + return obj + } + defineProperties = function (obj, descs) { + for (var prop in descs) { + if (descs.hasOwnProperty(prop)) { + defineProperty(obj, prop, descs[prop]) + } + } + return obj + } + } + if (IEVersion) { + var VBClassPool = {} + window.execScript([// jshint ignore:line + "Function parseVB(code)", + "\tExecuteGlobal(code)", + "End Function" //转换一段文本为VB代码 + ].join("\n"), "VBScript") + function VBMediator(instance, accessors, name, value) {// jshint ignore:line + var accessor = accessors[name] + if (arguments.length === 4) { + accessor.call(instance, value) + } else { + return accessor.call(instance) + } + } + defineProperties = function (name, accessors, properties) { + // jshint ignore:line + var buffer = [] + buffer.push( + "\r\n\tPrivate [__data__], [__proxy__]", + "\tPublic Default Function [__const__](d, p)", + "\t\tSet [__data__] = d: set [__proxy__] = p", + "\t\tSet [__const__] = Me", //链式调用 + "\tEnd Function") + //添加普通属性,因为VBScript对象不能像JS那样随意增删属性,必须在这里预先定义好 + for (name in properties) { + if (!accessors.hasOwnProperty(name)) { + buffer.push("\tPublic [" + name + "]") + } + } + $$skipArray.forEach(function (name) { + if (!accessors.hasOwnProperty(name)) { + buffer.push("\tPublic [" + name + "]") + } + }) + buffer.push("\tPublic [" + 'hasOwnProperty' + "]") + //添加访问器属性 + for (name in accessors) { + buffer.push( + //由于不知对方会传入什么,因此set, let都用上 + "\tPublic Property Let [" + name + "](val" + expose + ")", //setter + "\t\tCall [__proxy__](Me,[__data__], \"" + name + "\", val" + expose + ")", + "\tEnd Property", + "\tPublic Property Set [" + name + "](val" + expose + ")", //setter + "\t\tCall [__proxy__](Me,[__data__], \"" + name + "\", val" + expose + ")", + "\tEnd Property", + "\tPublic Property Get [" + name + "]", //getter + "\tOn Error Resume Next", //必须优先使用set语句,否则它会误将数组当字符串返回 + "\t\tSet[" + name + "] = [__proxy__](Me,[__data__],\"" + name + "\")", + "\tIf Err.Number <> 0 Then", + "\t\t[" + name + "] = [__proxy__](Me,[__data__],\"" + name + "\")", + "\tEnd If", + "\tOn Error Goto 0", + "\tEnd Property") + + } + + buffer.push("End Class") + var body = buffer.join("\r\n") + var className =VBClassPool[body] + if (!className) { + className = generateID("VBClass") + window.parseVB("Class " + className + body) + window.parseVB([ + "Function " + className + "Factory(a, b)", //创建实例并传入两个关键的参数 + "\tDim o", + "\tSet o = (New " + className + ")(a, b)", + "\tSet " + className + "Factory = o", + "End Function" + ].join("\r\n")) + VBClassPool[body] = className + } + var ret = window[className + "Factory"](accessors, VBMediator) //得到其产品 + return ret //得到其产品 + } + } + } + + /********************************************************************* + * 监控数组(与ms-each, ms-repeat配合使用) * + **********************************************************************/ + + function arrayFactory(model) { + var array = [] + array.$id = generateID() + array.$model = model //数据模型 + array.$events = {} + array.$events[subscribers] = [] + array._ = modelFactory({ + length: model.length + }) + array._.$watch("length", function (a, b) { + array.$fire("length", a, b) + }) + for (var i in EventBus) { + array[i] = EventBus[i] + } + array.$map = { + el: 1 + } + array.$proxy = [] + avalon.mix(array, arrayPrototype) + return array + } + + function mutateArray(method, pos, n, index, method2, pos2, n2) { + var oldLen = this.length, loop = 2 + while (--loop) { + switch (method) { + case "add": + /* jshint ignore:start */ + var m = pos + n + var array = this.$model.slice(pos, m).map(function (el) { + if (rcomplexType.test(avalon.type(el))) {//转换为VM + return el.$id ? el : modelFactory(el, 0, el) + } else { + return el + } + }) + _splice.apply(this, [pos, 0].concat(array)) + /* jshint ignore:end */ + for (var i = pos; i < m; i++) {//生成代理VM + var proxy = eachProxyAgent(i, this) + this.$proxy.splice(i, 0, proxy) + } + this._fire("add", pos, n) + break + case "del": + var ret = this._splice(pos, n) + var removed = this.$proxy.splice(pos, n) //回收代理VM + eachProxyRecycler(removed, "each") + this._fire("del", pos, n) + break + } + if (method2) { + method = method2 + pos = pos2 + n = n2 + loop = 2 + method2 = 0 + } + } + resetIndex(this.$proxy, index) + if (this.length !== oldLen) { + this._.length = this.length + } + return ret + } + + var _splice = ap.splice + var arrayPrototype = { + _splice: _splice, + _fire: function (method, a, b) { + fireDependencies(this.$events[subscribers], method, a, b) + }, + size: function () { //取得数组长度,这个函数可以同步视图,length不能 + return this._.length + }, + pushArray: function (array) { + var m = array.length, n = this.length + if (m) { + ap.push.apply(this.$model, array) + mutateArray.call(this, "add", n, m, Math.max(0, n - 1)) + } + return m + n + }, + push: function () { + //http://jsperf.com/closure-with-arguments + var array = [] + var i, n = arguments.length + for (i = 0; i < n; i++) { + array[i] = arguments[i] + } + return this.pushArray(array) + }, + unshift: function () { + var m = arguments.length, n = this.length + if (m) { + ap.unshift.apply(this.$model, arguments) + mutateArray.call(this, "add", 0, m, 0) + } + return m + n //IE67的unshift不会返回长度 + }, + shift: function () { + if (this.length) { + var el = this.$model.shift() + mutateArray.call(this, "del", 0, 1, 0) + return el //返回被移除的元素 + } + }, + pop: function () { + var n = this.length + if (n) { + var el = this.$model.pop() + mutateArray.call(this, "del", n - 1, 1, Math.max(0, n - 2)) + return el //返回被移除的元素 + } + }, + splice: function (start) { + var m = arguments.length, args = [], change + var removed = _splice.apply(this.$model, arguments) + if (removed.length) { //如果用户删掉了元素 + args.push("del", start, removed.length, 0) + change = true + } + if (m > 2) { //如果用户添加了元素 + if (change) { + args.splice(3, 1, 0, "add", start, m - 2) + } else { + args.push("add", start, m - 2, 0) + } + change = true + } + if (change) { //返回被移除的元素 + return mutateArray.apply(this, args) + } else { + return [] + } + }, + contains: function (el) { //判定是否包含 + return this.indexOf(el) !== -1 + }, + remove: function (el) { //移除第一个等于给定值的元素 + return this.removeAt(this.indexOf(el)) + }, + removeAt: function (index) { //移除指定索引上的元素 + if (index >= 0) { + this.$model.splice(index, 1) + return mutateArray.call(this, "del", index, 1, 0) + } + return [] + }, + clear: function () { + eachProxyRecycler(this.$proxy, "each") + this.$model.length = this.$proxy.length = this.length = this._.length = 0 //清空数组 + this._fire("clear", 0) + return this + }, + removeAll: function (all) { //移除N个元素 + if (Array.isArray(all)) { + for (var i = this.length - 1; i >= 0; i--) { + if (all.indexOf(this[i]) !== -1) { + this.removeAt(i) + } + } + } else if (typeof all === "function") { + for (i = this.length - 1; i >= 0; i--) { + if (all(this[i], i)) { + this.removeAt(i) + } + } + } else { + this.clear() + } + }, + ensure: function (el) { + if (!this.contains(el)) { //只有不存在才push + this.push(el) + } + return this + }, + set: function (index, val) { + if (index >= 0) { + var valueType = avalon.type(val) + if (val && val.$model) { + val = val.$model + } + var target = this[index] + if (valueType === "object") { + for (var i in val) { + if (target.hasOwnProperty(i)) { + target[i] = val[i] + } + } + } else if (valueType === "array") { + target.clear().push.apply(target, val) + } else if (target !== val) { + this[index] = val + this.$model[index] = val + var proxy = this.$proxy[index] + if (proxy) { + fireDependencies(proxy.$events.$index) + } + } + } + return this + } + } +//相当于原来bindingExecutors.repeat 的index分支 + function resetIndex(array, pos) { + var last = array.length - 1 + for (var el; el = array[pos]; pos++) { + el.$index = pos + el.$first = pos === 0 + el.$last = pos === last + } + } + + function sortByIndex(array, indexes) { + var map = {}; + for (var i = 0, n = indexes.length; i < n; i++) { + map[i] = array[i] // preserve + var j = indexes[i] + if (j in map) { + array[i] = map[j] + delete map[j] + } else { + array[i] = array[j] + } + } + } + + "sort,reverse".replace(rword, function (method) { + arrayPrototype[method] = function () { + var newArray = this.$model//这是要排序的新数组 + var oldArray = newArray.concat() //保持原来状态的旧数组 + var mask = Math.random() + var indexes = [] + var hasSort + ap[method].apply(newArray, arguments) //排序 + for (var i = 0, n = oldArray.length; i < n; i++) { + var neo = newArray[i] + var old = oldArray[i] + if (isEqual(neo, old)) { + indexes.push(i) + } else { + var index = oldArray.indexOf(neo) + indexes.push(index)//得到新数组的每个元素在旧数组对应的位置 + oldArray[index] = mask //屏蔽已经找过的元素 + hasSort = true + } + } + if (hasSort) { + sortByIndex(this, indexes) + sortByIndex(this.$proxy, indexes) + this._fire("move", indexes) + resetIndex(this.$proxy, 0) + } + return this + } + }) + + var eachProxyPool = [] + + function eachProxyFactory() { + var source = { + $index: NaN, + $first: NaN, + $last: NaN, + $map: {}, + $host: {}, + $outer: {}, + $remove: avalon.noop, + el: { + get: function () { + //avalon1.4.4中,计算属性的订阅数组不再添加绑定对象 + return this.$host[this.$index] + }, + set: function (val) { + this.$host.set(this.$index, val) + } + } + } + + var second = { + $last: 1, + $first: 1, + $index: 1 + } + var proxy = modelFactory(source, second) + proxy.$id = generateID("$proxy$each") + return proxy + } + + function eachProxyAgent(index, host) { + var proxy = eachProxyPool.shift() + if (!proxy) { + proxy = eachProxyFactory( ) + }else{ + proxy.$compute() + } + var last = host.length - 1 + proxy.$host = host + proxy.$index = index + proxy.$first = index === 0 + proxy.$last = index === last + proxy.$map = host.$map + proxy.$remove = function () { + return host.removeAt(proxy.$index) + } + return proxy + } + + /********************************************************************* + * 依赖调度系统 * + **********************************************************************/ +//检测两个对象间的依赖关系 + var dependencyDetection = (function () { + var outerFrames = [] + var currentFrame + return { + begin: function (accessorObject) { + //accessorObject为一个拥有callback的对象 + outerFrames.push(currentFrame) + currentFrame = accessorObject + }, + end: function () { + currentFrame = outerFrames.pop() + }, + collectDependency: function (vmodel, accessor) { + if (currentFrame) { + //被dependencyDetection.begin调用 + currentFrame.callback(vmodel, accessor); + } + } + }; + })() +//将绑定对象注入到其依赖项的订阅数组中 + var ronduplex = /^(duplex|on)$/ + avalon.injectBinding = function (data) { + var fn = data.evaluator + if (fn) { //如果是求值函数 + dependencyDetection.begin({ + callback: function (vmodel, dependency) { + injectDependency(vmodel.$events[dependency._name], data) + } + }) + try { + var c = ronduplex.test(data.type) ? data : fn.apply(0, data.args) + data.handler(c, data.element, data) + } catch (e) { + //log("warning:exception throwed in [avalon.injectBinding] " + e) + delete data.evaluator + var node = data.element + if (node.nodeType === 3) { + var parent = node.parentNode + if (kernel.commentInterpolate) { + parent.replaceChild(DOC.createComment(data.value), node) + } else { + node.data = openTag + data.value + closeTag + } + } + } finally { + dependencyDetection.end() + } + } + } + +//将依赖项(比它高层的访问器或构建视图刷新函数的绑定对象)注入到订阅者数组 + function injectDependency(list, data) { + data = data || Registry[expose] + if (list && data && avalon.Array.ensure(list, data) && data.element) { + injectDisposeQueue(data, list) + } + } + +//通知依赖于这个访问器的订阅者更新自身 + function fireDependencies(list) { + if (list && list.length) { + if (new Date() - beginTime > 444 && typeof list[0] === "object") { + rejectDisposeQueue() + } + var args = aslice.call(arguments, 1) + for (var i = list.length, fn; fn = list[--i]; ) { + var el = fn.element + if (el && el.parentNode) { + try { + if (fn.$repeat) { + fn.handler.apply(fn, args) //处理监控数组的方法 + } else if (fn.type !== "on") { //事件绑定只能由用户触发,不能由程序触发 + var fun = fn.evaluator || noop + fn.handler(fun.apply(0, fn.args || []), el, fn) + + } + } catch (e) { + } + } + } + } + } + /********************************************************************* + * 定时GC回收机制 * + **********************************************************************/ + var disposeCount = 0 + var disposeQueue = avalon.$$subscribers = [] + var beginTime = new Date() + var oldInfo = {} + function getUid(obj) { //IE9+,标准浏览器 + return obj.uniqueNumber || (obj.uniqueNumber = ++disposeCount) + } + +//添加到回收列队中 + function injectDisposeQueue(data, list) { + var elem = data.element + if (!data.uuid) { + if (elem.nodeType !== 1) { + data.uuid = data.type + (data.pos || 0) + "-" + getUid(elem.parentNode) + } else { + data.uuid = data.name + "-" + getUid(elem) + } + } + var lists = data.lists || (data.lists = []) + avalon.Array.ensure(lists, list) + list.$uuid = list.$uuid || generateID() + if (!disposeQueue[data.uuid]) { + disposeQueue[data.uuid] = 1 + disposeQueue.push(data) + } + } + + function rejectDisposeQueue(data) { + if(avalon.optimize) + return + var i = disposeQueue.length + var n = i + var allTypes = [] + var iffishTypes = {} + var newInfo = {} + //对页面上所有绑定对象进行分门别类, 只检测个数发生变化的类型 + while (data = disposeQueue[--i]) { + var type = data.type + if (newInfo[type]) { + newInfo[type]++ + } else { + newInfo[type] = 1 + allTypes.push(type) + } + } + var diff = false + allTypes.forEach(function (type) { + if (oldInfo[type] !== newInfo[type]) { + iffishTypes[type] = 1 + diff = true + } + }) + i = n + if (diff) { + while (data = disposeQueue[--i]) { + if (!data.element) + continue + if (iffishTypes[data.type] && shouldDispose(data.element)) { //如果它没有在DOM树 + disposeQueue.splice(i, 1) + delete disposeQueue[data.uuid] + var lists = data.lists + for (var k = 0, list; list = lists[k++]; ) { + avalon.Array.remove(lists, list) + avalon.Array.remove(list, data) + } + disposeData(data) + } + } + } + oldInfo = newInfo + beginTime = new Date() + } + + function disposeData(data) { + data.element = null + data.rollback && data.rollback() + for (var key in data) { + data[key] = null + } + } + + function shouldDispose(el) { + try {//IE下,如果文本节点脱离DOM树,访问parentNode会报错 + if (!el.parentNode) { + return true + } + } catch (e) { + return true + } + + return el.msRetain ? 0 : (el.nodeType === 1 ? !root.contains(el) : !avalon.contains(root, el)) + } + + /************************************************************************ + * HTML处理(parseHTML, innerHTML, clearHTML) * + ************************************************************************/ +// We have to close these tags to support XHTML + var tagHooks = { + area: [1, "", ""], + param: [1, "", ""], + col: [2, "", "
"], + legend: [1, "
", "
"], + option: [1, ""], + thead: [1, "", "
"], + tr: [2, "", "
"], + td: [3, "", "
"], + g: [1, '', ''], + //IE6-8在用innerHTML生成节点时,不能直接创建no-scope元素与HTML5的新标签 + _default: W3C ? [0, "", ""] : [1, "X
", "
"] //div可以不用闭合 + } + tagHooks.th = tagHooks.td + tagHooks.optgroup = tagHooks.option + tagHooks.tbody = tagHooks.tfoot = tagHooks.colgroup = tagHooks.caption = tagHooks.thead + String("circle,defs,ellipse,image,line,path,polygon,polyline,rect,symbol,text,use").replace(rword, function (tag) { + tagHooks[tag] = tagHooks.g //处理SVG + }) + var rtagName = /<([\w:]+)/ //取得其tagName + var rxhtml = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig + var rcreate = W3C ? /[^\d\D]/ : /(<(?:script|link|style|meta|noscript))/ig + var scriptTypes = oneObject(["", "text/javascript", "text/ecmascript", "application/ecmascript", "application/javascript"]) + var rnest = /<(?:tb|td|tf|th|tr|col|opt|leg|cap|area)/ //需要处理套嵌关系的标签 + var script = DOC.createElement("script") + var rhtml = /<|&#?\w+;/ + avalon.parseHTML = function (html) { + var fragment = avalonFragment.cloneNode(false) + if (typeof html !== "string") { + return fragment + } + if (!rhtml.test(html)) { + fragment.appendChild(DOC.createTextNode(html)) + return fragment + } + html = html.replace(rxhtml, "<$1>").trim() + var tag = (rtagName.exec(html) || ["", ""])[1].toLowerCase(), + //取得其标签名 + wrap = tagHooks[tag] || tagHooks._default, + wrapper = cinerator, + firstChild, neo + if (!W3C) { //fix IE + html = html.replace(rcreate, "
$1") //在link style script等标签之前添加一个补丁 + } + wrapper.innerHTML = wrap[1] + html + wrap[2] + var els = wrapper.getElementsByTagName("script") + if (els.length) { //使用innerHTML生成的script节点不会发出请求与执行text属性 + for (var i = 0, el; el = els[i++]; ) { + if (scriptTypes[el.type]) { + //以偷龙转凤方式恢复执行脚本功能 + neo = script.cloneNode(false) //FF不能省略参数 + ap.forEach.call(el.attributes, function (attr) { + if (attr && attr.specified) { + neo[attr.name] = attr.value //复制其属性 + neo.setAttribute(attr.name, attr.value) + } + }) // jshint ignore:line + neo.text = el.text + el.parentNode.replaceChild(neo, el) //替换节点 + } + } + } + if (!W3C) { //fix IE + var target = wrap[1] === "X
" ? wrapper.lastChild.firstChild : wrapper.lastChild + if (target && target.tagName === "TABLE" && tag !== "tbody") { + //IE6-7处理 --> , + // --> , + // -->
+ for (els = target.childNodes, i = 0; el = els[i++]; ) { + if (el.tagName === "TBODY" && !el.innerHTML) { + target.removeChild(el) + break + } + } + } + els = wrapper.getElementsByTagName("br") + var n = els.length + while (el = els[--n]) { + if (el.className === "msNoScope") { + el.parentNode.removeChild(el) + } + } + for (els = wrapper.all, i = 0; el = els[i++]; ) { //fix VML + if (isVML(el)) { + fixVML(el) + } + } + } + //移除我们为了符合套嵌关系而添加的标签 + for (i = wrap[0]; i--; wrapper = wrapper.lastChild) { + } + while (firstChild = wrapper.firstChild) { // 将wrapper上的节点转移到文档碎片上! + fragment.appendChild(firstChild) + } + return fragment + } + + function isVML(src) { + var nodeName = src.nodeName + return nodeName.toLowerCase() === nodeName && src.scopeName && src.outerText === "" + } + + function fixVML(node) { + if (node.currentStyle.behavior !== "url(#default#VML)") { + node.style.behavior = "url(#default#VML)" + node.style.display = "inline-block" + node.style.zoom = 1 //hasLayout + } + } + avalon.innerHTML = function (node, html) { + if (!W3C && (!rcreate.test(html) && !rnest.test(html))) { + try { + node.innerHTML = html + return + } catch (e) { + } + } + var a = this.parseHTML(html) + this.clearHTML(node).appendChild(a) + } + avalon.clearHTML = function (node) { + node.textContent = "" + while (node.firstChild) { + node.removeChild(node.firstChild) + } + return node + } + + /********************************************************************* + * avalon的原型方法定义区 * + **********************************************************************/ + + function hyphen(target) { + //转换为连字符线风格 + return target.replace(/([a-z\d])([A-Z]+)/g, "$1-$2").toLowerCase() + } + + function camelize(target) { + //提前判断,提高getStyle等的效率 + if (!target || target.indexOf("-") < 0 && target.indexOf("_") < 0) { + return target + } + //转换为驼峰风格 + return target.replace(/[-_][^-_]/g, function(match) { + return match.charAt(1).toUpperCase() + }) + } + + var fakeClassListMethods = { + _toString: function() { + var node = this.node + var cls = node.className + var str = typeof cls === "string" ? cls : cls.baseVal + return str.split(/\s+/).join(" ") + }, + _contains: function(cls) { + return (" " + this + " ").indexOf(" " + cls + " ") > -1 + }, + _add: function(cls) { + if (!this.contains(cls)) { + this._set(this + " " + cls) + } + }, + _remove: function(cls) { + this._set((" " + this + " ").replace(" " + cls + " ", " ")) + }, + __set: function(cls) { + cls = cls.trim() + var node = this.node + if (rsvg.test(node)) { + //SVG元素的className是一个对象 SVGAnimatedString { baseVal="", animVal=""},只能通过set/getAttribute操作 + node.setAttribute("class", cls) + } else { + node.className = cls + } + } //toggle存在版本差异,因此不使用它 + } + + function fakeClassList(node) { + if (!("classList" in node)) { + node.classList = { + node: node + } + for (var k in fakeClassListMethods) { + node.classList[k.slice(1)] = fakeClassListMethods[k] + } + } + return node.classList + } + + + "add,remove".replace(rword, function(method) { + avalon.fn[method + "Class"] = function(cls) { + var el = this[0] + //https://developer.mozilla.org/zh-CN/docs/Mozilla/Firefox/Releases/26 + if (cls && typeof cls === "string" && el && el.nodeType === 1) { + cls.replace(/\S+/g, function(c) { + fakeClassList(el)[method](c) + }) + } + return this + } + }) + avalon.fn.mix({ + hasClass: function(cls) { + var el = this[0] || {} + return el.nodeType === 1 && fakeClassList(el).contains(cls) + }, + toggleClass: function(value, stateVal) { + var className, i = 0 + var classNames = String(value).split(/\s+/) + var isBool = typeof stateVal === "boolean" + while ((className = classNames[i++])) { + var state = isBool ? stateVal : !this.hasClass(className) + this[state ? "addClass" : "removeClass"](className) + } + return this + }, + attr: function(name, value) { + if (arguments.length === 2) { + this[0].setAttribute(name, value) + return this + } else { + return this[0].getAttribute(name) + } + }, + data: function(name, value) { + name = "data-" + hyphen(name || "") + switch (arguments.length) { + case 2: + this.attr(name, value) + return this + case 1: + var val = this.attr(name) + return parseData(val) + case 0: + var ret = {} + ap.forEach.call(this[0].attributes, function(attr) { + if (attr) { + name = attr.name + if (!name.indexOf("data-")) { + name = camelize(name.slice(5)) + ret[name] = parseData(attr.value) + } + } + }) + return ret + } + }, + removeData: function(name) { + name = "data-" + hyphen(name) + this[0].removeAttribute(name) + return this + }, + css: function(name, value) { + if (avalon.isPlainObject(name)) { + for (var i in name) { + avalon.css(this, i, name[i]) + } + } else { + var ret = avalon.css(this, name, value) + } + return ret !== void 0 ? ret : this + }, + position: function() { + var offsetParent, offset, + elem = this[0], + parentOffset = { + top: 0, + left: 0 + } + if (!elem) { + return + } + if (this.css("position") === "fixed") { + offset = elem.getBoundingClientRect() + } else { + offsetParent = this.offsetParent() //得到真正的offsetParent + offset = this.offset() // 得到正确的offsetParent + if (offsetParent[0].tagName !== "HTML") { + parentOffset = offsetParent.offset() + } + parentOffset.top += avalon.css(offsetParent[0], "borderTopWidth", true) + parentOffset.left += avalon.css(offsetParent[0], "borderLeftWidth", true) + + // Subtract offsetParent scroll positions + parentOffset.top -= offsetParent.scrollTop() + parentOffset.left -= offsetParent.scrollLeft() + } + return { + top: offset.top - parentOffset.top - avalon.css(elem, "marginTop", true), + left: offset.left - parentOffset.left - avalon.css(elem, "marginLeft", true) + } + }, + offsetParent: function() { + var offsetParent = this[0].offsetParent + while (offsetParent && avalon.css(offsetParent, "position") === "static") { + offsetParent = offsetParent.offsetParent; + } + return avalon(offsetParent || root) + }, + bind: function(type, fn, phase) { + if (this[0]) { //此方法不会链 + return avalon.bind(this[0], type, fn, phase) + } + }, + unbind: function(type, fn, phase) { + if (this[0]) { + avalon.unbind(this[0], type, fn, phase) + } + return this + }, + val: function(value) { + var node = this[0] + if (node && node.nodeType === 1) { + var get = arguments.length === 0 + var access = get ? ":get" : ":set" + var fn = valHooks[getValType(node) + access] + if (fn) { + var val = fn(node, value) + } else if (get) { + return (node.value || "").replace(/\r/g, "") + } else { + node.value = value + } + } + return get ? val : this + } + }) + + function parseData(data) { + try { + if (typeof data === "object") + return data + data = data === "true" ? true : + data === "false" ? false : + data === "null" ? null : +data + "" === data ? +data : rbrace.test(data) ? avalon.parseJSON(data) : data + } catch (e) {} + return data + } + var rbrace = /(?:\{[\s\S]*\}|\[[\s\S]*\])$/, + rvalidchars = /^[\],:{}\s]*$/, + rvalidbraces = /(?:^|:|,)(?:\s*\[)+/g, + rvalidescape = /\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g, + rvalidtokens = /"[^"\\\r\n]*"|true|false|null|-?(?:\d+\.|)\d+(?:[eE][+-]?\d+|)/g + avalon.parseJSON = window.JSON ? JSON.parse : function(data) { + if (typeof data === "string") { + data = data.trim(); + if (data) { + if (rvalidchars.test(data.replace(rvalidescape, "@") + .replace(rvalidtokens, "]") + .replace(rvalidbraces, ""))) { + return (new Function("return " + data))() // jshint ignore:line + } + } + avalon.error("Invalid JSON: " + data) + } + return data + } + +//生成avalon.fn.scrollLeft, avalon.fn.scrollTop方法 + avalon.each({ + scrollLeft: "pageXOffset", + scrollTop: "pageYOffset" + }, function(method, prop) { + avalon.fn[method] = function(val) { + var node = this[0] || {}, win = getWindow(node), + top = method === "scrollTop" + if (!arguments.length) { + return win ? (prop in win) ? win[prop] : root[method] : node[method] + } else { + if (win) { + win.scrollTo(!top ? val : avalon(win).scrollLeft(), top ? val : avalon(win).scrollTop()) + } else { + node[method] = val + } + } + } + }) + + function getWindow(node) { + return node.window && node.document ? node : node.nodeType === 9 ? node.defaultView || node.parentWindow : false; + } +//=============================css相关======================= + var cssHooks = avalon.cssHooks = {} + var prefixes = ["", "-webkit-", "-o-", "-moz-", "-ms-"] + var cssMap = { + "float": W3C ? "cssFloat" : "styleFloat" + } + avalon.cssNumber = oneObject("columnCount,order,fillOpacity,fontWeight,lineHeight,opacity,orphans,widows,zIndex,zoom") + + avalon.cssName = function(name, host, camelCase) { + if (cssMap[name]) { + return cssMap[name] + } + host = host || root.style + for (var i = 0, n = prefixes.length; i < n; i++) { + camelCase = camelize(prefixes[i] + name) + if (camelCase in host) { + return (cssMap[name] = camelCase) + } + } + return null + } + cssHooks["@:set"] = function(node, name, value) { + try { //node.style.width = NaN;node.style.width = "xxxxxxx";node.style.width = undefine 在旧式IE下会抛异常 + node.style[name] = value + } catch (e) {} + } + if (window.getComputedStyle) { + cssHooks["@:get"] = function(node, name) { + if (!node || !node.style) { + throw new Error("getComputedStyle要求传入一个节点 " + node) + } + var ret, styles = getComputedStyle(node, null) + if (styles) { + ret = name === "filter" ? styles.getPropertyValue(name) : styles[name] + if (ret === "") { + ret = node.style[name] //其他浏览器需要我们手动取内联样式 + } + } + return ret + } + cssHooks["opacity:get"] = function(node) { + var ret = cssHooks["@:get"](node, "opacity") + return ret === "" ? "1" : ret + } + } else { + var rnumnonpx = /^-?(?:\d*\.)?\d+(?!px)[^\d\s]+$/i + var rposition = /^(top|right|bottom|left)$/ + var ralpha = /alpha\([^)]*\)/i + var ie8 = !! window.XDomainRequest + var salpha = "DXImageTransform.Microsoft.Alpha" + var border = { + thin: ie8 ? '1px' : '2px', + medium: ie8 ? '3px' : '4px', + thick: ie8 ? '5px' : '6px' + } + cssHooks["@:get"] = function(node, name) { + //取得精确值,不过它有可能是带em,pc,mm,pt,%等单位 + var currentStyle = node.currentStyle + var ret = currentStyle[name] + if ((rnumnonpx.test(ret) && !rposition.test(ret))) { + //①,保存原有的style.left, runtimeStyle.left, + var style = node.style, + left = style.left, + rsLeft = node.runtimeStyle.left + //②由于③处的style.left = xxx会影响到currentStyle.left, + //因此把它currentStyle.left放到runtimeStyle.left, + //runtimeStyle.left拥有最高优先级,不会style.left影响 + node.runtimeStyle.left = currentStyle.left + //③将精确值赋给到style.left,然后通过IE的另一个私有属性 style.pixelLeft + //得到单位为px的结果;fontSize的分支见http://bugs.jquery.com/ticket/760 + style.left = name === 'fontSize' ? '1em' : (ret || 0) + ret = style.pixelLeft + "px" + //④还原 style.left,runtimeStyle.left + style.left = left + node.runtimeStyle.left = rsLeft + } + if (ret === "medium") { + name = name.replace("Width", "Style") + //border width 默认值为medium,即使其为0" + if (currentStyle[name] === "none") { + ret = "0px" + } + } + return ret === "" ? "auto" : border[ret] || ret + } + cssHooks["opacity:set"] = function(node, name, value) { + var style = node.style + var opacity = isFinite(value) && value <= 1 ? "alpha(opacity=" + value * 100 + ")" : "" + var filter = style.filter || ""; + style.zoom = 1 + //不能使用以下方式设置透明度 + //node.filters.alpha.opacity = value * 100 + style.filter = (ralpha.test(filter) ? + filter.replace(ralpha, opacity) : + filter + " " + opacity).trim() + if (!style.filter) { + style.removeAttribute("filter") + } + } + cssHooks["opacity:get"] = function(node) { + //这是最快的获取IE透明值的方式,不需要动用正则了! + var alpha = node.filters.alpha || node.filters[salpha], + op = alpha && alpha.enabled ? alpha.opacity : 100 + return (op / 100) + "" //确保返回的是字符串 + } + } + + "top,left".replace(rword, function(name) { + cssHooks[name + ":get"] = function(node) { + var computed = cssHooks["@:get"](node, name) + return /px$/.test(computed) ? computed : + avalon(node).position()[name] + "px" + } + }) + + var cssShow = { + position: "absolute", + visibility: "hidden", + display: "block" + } + + var rdisplayswap = /^(none|table(?!-c[ea]).+)/ + + function showHidden(node, array) { + //http://www.cnblogs.com/rubylouvre/archive/2012/10/27/2742529.html + if (node.offsetWidth <= 0) { //opera.offsetWidth可能小于0 + if (rdisplayswap.test(cssHooks["@:get"](node, "display"))) { + var obj = { + node: node + } + for (var name in cssShow) { + obj[name] = node.style[name] + node.style[name] = cssShow[name] + } + array.push(obj) + } + var parent = node.parentNode + if (parent && parent.nodeType === 1) { + showHidden(parent, array) + } + } + } + "Width,Height".replace(rword, function(name) { //fix 481 + var method = name.toLowerCase(), + clientProp = "client" + name, + scrollProp = "scroll" + name, + offsetProp = "offset" + name + cssHooks[method + ":get"] = function(node, which, override) { + var boxSizing = -4 + if (typeof override === "number") { + boxSizing = override + } + which = name === "Width" ? ["Left", "Right"] : ["Top", "Bottom"] + var ret = node[offsetProp] // border-box 0 + if (boxSizing === 2) { // margin-box 2 + return ret + avalon.css(node, "margin" + which[0], true) + avalon.css(node, "margin" + which[1], true) + } + if (boxSizing < 0) { // padding-box -2 + ret = ret - avalon.css(node, "border" + which[0] + "Width", true) - avalon.css(node, "border" + which[1] + "Width", true) + } + if (boxSizing === -4) { // content-box -4 + ret = ret - avalon.css(node, "padding" + which[0], true) - avalon.css(node, "padding" + which[1], true) + } + return ret + } + cssHooks[method + "&get"] = function(node) { + var hidden = []; + showHidden(node, hidden); + var val = cssHooks[method + ":get"](node) + for (var i = 0, obj; obj = hidden[i++];) { + node = obj.node + for (var n in obj) { + if (typeof obj[n] === "string") { + node.style[n] = obj[n] + } + } + } + return val; + } + avalon.fn[method] = function(value) { //会忽视其display + var node = this[0] + if (arguments.length === 0) { + if (node.setTimeout) { //取得窗口尺寸,IE9后可以用node.innerWidth /innerHeight代替 + return node["inner" + name] || node.document.documentElement[clientProp] + } + if (node.nodeType === 9) { //取得页面尺寸 + var doc = node.documentElement + //FF chrome html.scrollHeight< body.scrollHeight + //IE 标准模式 : html.scrollHeight> body.scrollHeight + //IE 怪异模式 : html.scrollHeight 最大等于可视窗口多一点? + return Math.max(node.body[scrollProp], doc[scrollProp], node.body[offsetProp], doc[offsetProp], doc[clientProp]) + } + return cssHooks[method + "&get"](node) + } else { + return this.css(method, value) + } + } + avalon.fn["inner" + name] = function() { + return cssHooks[method + ":get"](this[0], void 0, -2) + } + avalon.fn["outer" + name] = function(includeMargin) { + return cssHooks[method + ":get"](this[0], void 0, includeMargin === true ? 2 : 0) + } + }) + avalon.fn.offset = function() { //取得距离页面左右角的坐标 + var node = this[0], + box = { + left: 0, + top: 0 + } + if (!node || !node.tagName || !node.ownerDocument) { + return box + } + var doc = node.ownerDocument, + body = doc.body, + root = doc.documentElement, + win = doc.defaultView || doc.parentWindow + if (!avalon.contains(root, node)) { + return box + } + //http://hkom.blog1.fc2.com/?mode=m&no=750 body的偏移量是不包含margin的 + //我们可以通过getBoundingClientRect来获得元素相对于client的rect. + //http://msdn.microsoft.com/en-us/library/ms536433.aspx + if (node.getBoundingClientRect) { + box = node.getBoundingClientRect() // BlackBerry 5, iOS 3 (original iPhone) + } + //chrome/IE6: body.scrollTop, firefox/other: root.scrollTop + var clientTop = root.clientTop || body.clientTop, + clientLeft = root.clientLeft || body.clientLeft, + scrollTop = Math.max(win.pageYOffset || 0, root.scrollTop, body.scrollTop), + scrollLeft = Math.max(win.pageXOffset || 0, root.scrollLeft, body.scrollLeft) + // 把滚动距离加到left,top中去。 + // IE一些版本中会自动为HTML元素加上2px的border,我们需要去掉它 + // http://msdn.microsoft.com/en-us/library/ms533564(VS.85).aspx + return { + top: box.top + scrollTop - clientTop, + left: box.left + scrollLeft - clientLeft + } + } + + //==================================val相关============================ + + function getValType(elem) { + var ret = elem.tagName.toLowerCase() + return ret === "input" && /checkbox|radio/.test(elem.type) ? "checked" : ret + } + var roption = /^]+))?)*\s+value[\s=]/i + var valHooks = { + "option:get": IEVersion ? function(node) { + //在IE11及W3C,如果没有指定value,那么node.value默认为node.text(存在trim作),但IE9-10则是取innerHTML(没trim操作) + //specified并不可靠,因此通过分析outerHTML判定用户有没有显示定义value + return roption.test(node.outerHTML) ? node.value : node.text.trim() + } : function(node) { + return node.value + }, + "select:get": function(node, value) { + var option, options = node.options, + index = node.selectedIndex, + getter = valHooks["option:get"], + one = node.type === "select-one" || index < 0, + values = one ? null : [], + max = one ? index + 1 : options.length, + i = index < 0 ? max : one ? index : 0 + for (; i < max; i++) { + option = options[i] + //旧式IE在reset后不会改变selected,需要改用i === index判定 + //我们过滤所有disabled的option元素,但在safari5下,如果设置select为disable,那么其所有孩子都disable + //因此当一个元素为disable,需要检测其是否显式设置了disable及其父节点的disable情况 + if ((option.selected || i === index) && !option.disabled) { + value = getter(option) + if (one) { + return value + } + //收集所有selected值组成数组返回 + values.push(value) + } + } + return values + }, + "select:set": function(node, values, optionSet) { + values = [].concat(values) //强制转换为数组 + var getter = valHooks["option:get"] + for (var i = 0, el; el = node.options[i++];) { + if ((el.selected = values.indexOf(getter(el)) > -1)) { + optionSet = true + } + } + if (!optionSet) { + node.selectedIndex = -1 + } + } + } + + /********************************************************************* + * 编译系统 * + **********************************************************************/ + var meta = { + '\b': '\\b', + '\t': '\\t', + '\n': '\\n', + '\f': '\\f', + '\r': '\\r', + '"': '\\"', + '\\': '\\\\' + } + var quote = window.JSON && JSON.stringify || function(str) { + return '"' + str.replace(/[\\\"\x00-\x1f]/g, function(a) { + var c = meta[a]; + return typeof c === 'string' ? c : + '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); + }) + '"' + } + + var keywords = [ + "break,case,catch,continue,debugger,default,delete,do,else,false", + "finally,for,function,if,in,instanceof,new,null,return,switch,this", + "throw,true,try,typeof,var,void,while,with", /* 关键字*/ + "abstract,boolean,byte,char,class,const,double,enum,export,extends", + "final,float,goto,implements,import,int,interface,long,native", + "package,private,protected,public,short,static,super,synchronized", + "throws,transient,volatile", /*保留字*/ + "arguments,let,yield,undefined" /* ECMA 5 - use strict*/].join(",") + var rrexpstr = /\/\*[\w\W]*?\*\/|\/\/[^\n]*\n|\/\/[^\n]*$|"(?:[^"\\]|\\[\w\W])*"|'(?:[^'\\]|\\[\w\W])*'|[\s\t\n]*\.[\s\t\n]*[$\w\.]+/g + var rsplit = /[^\w$]+/g + var rkeywords = new RegExp(["\\b" + keywords.replace(/,/g, '\\b|\\b') + "\\b"].join('|'), 'g') + var rnumber = /\b\d[^,]*/g + var rcomma = /^,+|,+$/g + var variablePool = new Cache(512) + var getVariables = function (code) { + var key = "," + code.trim() + var ret = variablePool.get(key) + if (ret) { + return ret + } + var match = code + .replace(rrexpstr, "") + .replace(rsplit, ",") + .replace(rkeywords, "") + .replace(rnumber, "") + .replace(rcomma, "") + .split(/^$|,+/) + return variablePool.put(key, uniqSet(match)) + } + /*添加赋值语句*/ + + function addAssign(vars, scope, name, data) { + var ret = [], + prefix = " = " + name + "." + var isProxy = /\$proxy\$each/.test(scope.$id) + for (var i = vars.length, prop; prop = vars[--i]; ) { + var el = isProxy && scope.$map[prop] ? "el" : prop + if (scope.hasOwnProperty(el)) { + ret.push(prop + prefix + el) + data.vars.push(prop) + if (data.type === "duplex") { + vars.get = name + "." + el + } + vars.splice(i, 1) + } + } + return ret + } + + function uniqSet(array) { + var ret = [], + unique = {} + for (var i = 0; i < array.length; i++) { + var el = array[i] + var id = el && typeof el.$id === "string" ? el.$id : el + if (!unique[id]) { + unique[id] = ret.push(el) + } + } + return ret + } +//缓存求值函数,以便多次利用 + var evaluatorPool = new Cache(128) +//取得求值函数及其传参 + var rduplex = /\w\[.*\]|\w\.\w/ + var rproxy = /(\$proxy\$[a-z]+)\d+$/ + var rthimRightParentheses = /\)\s*$/ + var rthimOtherParentheses = /\)\s*\|/g + var rquoteFilterName = /\|\s*([$\w]+)/g + var rpatchBracket = /"\s*\["/g + var rthimLeftParentheses = /"\s*\(/g + function parseFilter(val, filters) { + filters = filters + .replace(rthimRightParentheses, "")//处理最后的小括号 + .replace(rthimOtherParentheses, function () {//处理其他小括号 + return "],|" + }) + .replace(rquoteFilterName, function (a, b) { //处理|及它后面的过滤器的名字 + return "[" + quote(b) + }) + .replace(rpatchBracket, function () { + return '"],["' + }) + .replace(rthimLeftParentheses, function () { + return '",' + }) + "]" + return "return avalon.filters.$filter(" + val + ", " + filters + ")" + } + + function parseExpr(code, scopes, data) { + var dataType = data.type + var filters = data.filters || "" + var exprId = scopes.map(function (el) { + return String(el.$id).replace(rproxy, "$1") + }) + code + dataType + filters + var vars = getVariables(code).concat(), + assigns = [], + names = [], + args = [], + prefix = "" + //args 是一个对象数组, names 是将要生成的求值函数的参数 + scopes = uniqSet(scopes) + data.vars = [] + for (var i = 0, sn = scopes.length; i < sn; i++) { + if (vars.length) { + var name = "vm" + expose + "_" + i + names.push(name) + args.push(scopes[i]) + assigns.push.apply(assigns, addAssign(vars, scopes[i], name, data)) + } + } + if (!assigns.length && dataType === "duplex") { + return + } + if (dataType !== "duplex" && (code.indexOf("||") > -1 || code.indexOf("&&") > -1)) { + //https://github.com/RubyLouvre/avalon/issues/583 + data.vars.forEach(function (v) { + var reg = new RegExp("\\b" + v + "(?:\\.\\w+|\\[\\w+\\])+", "ig") + code = code.replace(reg, function (_) { + var c = _.charAt(v.length) + var r = IEVersion ? code.slice(arguments[1] + _.length) : RegExp.rightContext + var method = /^\s*\(/.test(r) + if (c === "." || c === "[" || method) {//比如v为aa,我们只匹配aa.bb,aa[cc],不匹配aaa.xxx + var name = "var" + String(Math.random()).replace(/^0\./, "") + if (method) {//array.size() + var array = _.split(".") + if (array.length > 2) { + var last = array.pop() + assigns.push(name + " = " + array.join(".")) + return name + "." + last + } else { + return _ + } + } + assigns.push(name + " = " + _) + return name + } else { + return _ + } + }) + }) + } + //---------------args---------------- + data.args = args + //---------------cache---------------- + delete data.vars + var fn = evaluatorPool.get(exprId) //直接从缓存,免得重复生成 + if (fn) { + data.evaluator = fn + return + } + prefix = assigns.join(", ") + if (prefix) { + prefix = "var " + prefix + } + if (/\S/.test(filters)) { //文本绑定,双工绑定才有过滤器 + if (!/text|html/.test(data.type)) { + throw Error("ms-" + data.type + "不支持过滤器") + } + code = "\nvar ret" + expose + " = " + code + ";\r\n" + code += parseFilter("ret" + expose, filters) + } else if (dataType === "duplex") { //双工绑定 + var _body = "'use strict';\nreturn function(vvv){\n\t" + + prefix + + ";\n\tif(!arguments.length){\n\t\treturn " + + code + + "\n\t}\n\t" + (!rduplex.test(code) ? vars.get : code) + + "= vvv;\n} " + try { + fn = Function.apply(noop, names.concat(_body)) + data.evaluator = evaluatorPool.put(exprId, fn) + } catch (e) { + log("debug: parse error," + e.message) + } + return + } else if (dataType === "on") { //事件绑定 + if (code.indexOf("(") === -1) { + code += ".call(this, $event)" + } else { + code = code.replace("(", ".call(this,") + } + names.push("$event") + code = "\nreturn " + code + ";" //IE全家 Function("return ")出错,需要Function("return ;") + var lastIndex = code.lastIndexOf("\nreturn") + var header = code.slice(0, lastIndex) + var footer = code.slice(lastIndex) + code = header + "\n" + footer + } else { //其他绑定 + code = "\nreturn " + code + ";" //IE全家 Function("return ")出错,需要Function("return ;") + } + try { + fn = Function.apply(noop, names.concat("'use strict';\n" + prefix + code)) + data.evaluator = evaluatorPool.put(exprId, fn) + } catch (e) { + log("debug: parse error," + e.message) + } finally { + vars = assigns = names = null //释放内存 + } + } + + +//parseExpr的智能引用代理 + + function parseExprProxy(code, scopes, data, tokens, noRegister) { + if (Array.isArray(tokens)) { + code = tokens.map(function (el) { + return el.expr ? "(" + el.value + ")" : quote(el.value) + }).join(" + ") + } + parseExpr(code, scopes, data) + if (data.evaluator && !noRegister) { + data.handler = bindingExecutors[data.handlerName || data.type] + //方便调试 + //这里非常重要,我们通过判定视图刷新函数的element是否在DOM树决定 + //将它移出订阅者列表 + avalon.injectBinding(data) + } + } + avalon.parseExprProxy = parseExprProxy + /********************************************************************* + * 扫描系统 * + **********************************************************************/ + + avalon.scan = function(elem, vmodel) { + elem = elem || root + var vmodels = vmodel ? [].concat(vmodel) : [] + scanTag(elem, vmodels) + } + +//http://www.w3.org/TR/html5/syntax.html#void-elements + var stopScan = oneObject("area,base,basefont,br,col,command,embed,hr,img,input,link,meta,param,source,track,wbr,noscript,script,style,textarea".toUpperCase()) + + function checkScan(elem, callback, innerHTML) { + var id = setTimeout(function() { + var currHTML = elem.innerHTML + clearTimeout(id) + if (currHTML === innerHTML) { + callback() + } else { + checkScan(elem, callback, currHTML) + } + }) + } + + + function createSignalTower(elem, vmodel) { + var id = elem.getAttribute("avalonctrl") || vmodel.$id + elem.setAttribute("avalonctrl", id) + vmodel.$events.expr = elem.tagName + '[avalonctrl="' + id + '"]' + } + + var getBindingCallback = function(elem, name, vmodels) { + var callback = elem.getAttribute(name) + if (callback) { + for (var i = 0, vm; vm = vmodels[i++]; ) { + if (vm.hasOwnProperty(callback) && typeof vm[callback] === "function") { + return vm[callback] + } + } + } + } + + function executeBindings(bindings, vmodels) { + for (var i = 0, data; data = bindings[i++]; ) { + data.vmodels = vmodels + bindingHandlers[data.type](data, vmodels) + if (data.evaluator && data.element && data.element.nodeType === 1) { //移除数据绑定,防止被二次解析 + //chrome使用removeAttributeNode移除不存在的特性节点时会报错 https://github.com/RubyLouvre/avalon/issues/99 + data.element.removeAttribute(data.name) + } + } + bindings.length = 0 + } + +//https://github.com/RubyLouvre/avalon/issues/636 + var mergeTextNodes = IEVersion && window.MutationObserver ? function (elem) { + var node = elem.firstChild, text + while (node) { + var aaa = node.nextSibling + if (node.nodeType === 3) { + if (text) { + text.nodeValue += node.nodeValue + elem.removeChild(node) + } else { + text = node + } + } else { + text = null + } + node = aaa + } + } : 0 + + var rmsAttr = /ms-(\w+)-?(.*)/ + var priorityMap = { + "if": 10, + "repeat": 90, + "data": 100, + "widget": 110, + "each": 1400, + "with": 1500, + "duplex": 2000, + "on": 3000 + } + + var events = oneObject("animationend,blur,change,input,click,dblclick,focus,keydown,keypress,keyup,mousedown,mouseenter,mouseleave,mousemove,mouseout,mouseover,mouseup,scan,scroll,submit") + var obsoleteAttrs = oneObject("value,title,alt,checked,selected,disabled,readonly,enabled") + function bindingSorter(a, b) { + return a.priority - b.priority + } + + function scanAttr(elem, vmodels, match) { + var scanNode = true + if (vmodels.length) { + var attributes = getAttributes ? getAttributes(elem) : elem.attributes + var bindings = [] + var fixAttrs = [] + var msData = {} + for (var i = 0, attr; attr = attributes[i++]; ) { + if (attr.specified) { + if (match = attr.name.match(rmsAttr)) { + //如果是以指定前缀命名的 + var type = match[1] + var param = match[2] || "" + var value = attr.value + var name = attr.name + if (events[type]) { + param = type + type = "on" + } else if (obsoleteAttrs[type]) { + if (type === "enabled") {//吃掉ms-enabled绑定,用ms-disabled代替 + log("warning!ms-enabled或ms-attr-enabled已经被废弃") + type = "disabled" + value = "!(" + value + ")" + } + param = type + type = "attr" + name = "ms-" + type + "-"+ param + fixAttrs.push([attr.name, name, value]) + } + msData[name] = value + if (typeof bindingHandlers[type] === "function") { + var binding = { + type: type, + param: param, + element: elem, + name: name, + value: value, + //chrome与firefox下Number(param)得到的值不一样 #855 + priority: (priorityMap[type] || type.charCodeAt(0) * 10 )+ (Number(param.replace(/\D/g, "")) || 0) + } + if (type === "html" || type === "text") { + var token = getToken(value) + avalon.mix(binding, token) + binding.filters = binding.filters.replace(rhasHtml, function () { + binding.type = "html" + binding.group = 1 + return "" + })// jshint ignore:line + } else if (type === "duplex") { + var hasDuplex = name + } else if (name === "ms-if-loop") { + binding.priority += 100 + } + bindings.push(binding) + if (type === "widget") { + elem.msData = elem.msData || msData + } + } + } + } + } + if (bindings.length) { + bindings.sort(bindingSorter) + fixAttrs.forEach(function (arr) { + log("warning!请改用" + arr[1] + "代替" + arr[0] + "!") + elem.removeAttribute(arr[0]) + elem.setAttribute(arr[1], arr[2]) + }) + //http://bugs.jquery.com/ticket/7071 + //在IE下对VML读取type属性,会让此元素所有属性都变成 + if (hasDuplex) { + if (msData["ms-attr-checked"]) { + log("warning!一个控件不能同时定义ms-attr-checked与" + hasDuplex) + } + if (msData["ms-attr-value"]) { + log("warning!一个控件不能同时定义ms-attr-value与" + hasDuplex) + } + } + for (i = 0; binding = bindings[i]; i++) { + type = binding.type + if (rnoscanAttrBinding.test(type)) { + return executeBindings(bindings.slice(0, i + 1), vmodels) + } else if (scanNode) { + scanNode = !rnoscanNodeBinding.test(type) + } + } + executeBindings(bindings, vmodels) + } + } + if (scanNode && !stopScan[elem.tagName] && rbind.test(elem.innerHTML.replace(rlt, "<").replace(rgt, ">"))) { + mergeTextNodes && mergeTextNodes(elem) + scanNodeList(elem, vmodels) //扫描子孙元素 + } + } + + var rnoscanAttrBinding = /^if|widget|repeat$/ + var rnoscanNodeBinding = /^each|with|html|include$/ +//IE67下,在循环绑定中,一个节点如果是通过cloneNode得到,自定义属性的specified为false,无法进入里面的分支, +//但如果我们去掉scanAttr中的attr.specified检测,一个元素会有80+个特性节点(因为它不区分固有属性与自定义属性),很容易卡死页面 + if (!"1" [0]) { + var attrPool = new Cache(512) + var rattrs = /\s+(ms-[^=\s]+)(?:=("[^"]*"|'[^']*'|[^\s>]+))?/g, + rquote = /^['"]/, + rtag = /<\w+\b(?:(["'])[^"]*?(\1)|[^>])*>/i, + ramp = /&/g + //IE6-8解析HTML5新标签,会将它分解两个元素节点与一个文本节点 + //
ddd
+ // window.onload = function() { + // var body = document.body + // for (var i = 0, el; el = body.children[i++]; ) { + // avalon.log(el.outerHTML) + // } + // } + //依次输出
,
+ var getAttributes = function (elem) { + var html = elem.outerHTML + //处理IE6-8解析HTML5新标签的情况,及
等半闭合标签outerHTML为空的情况 + if (html.slice(0, 2) === " ms-important(1) --> ms-controller(2) --> ms-if(10) --> ms-repeat(100) + //--> ms-if-loop(110) --> ms-attr(970) ...--> ms-each(1400)-->ms-with(1500)--〉ms-duplex(2000)垫后 + var a = elem.getAttribute("ms-skip") + //#360 在旧式IE中 Object标签在引入Flash等资源时,可能出现没有getAttributeNode,innerHTML的情形 + if (!elem.getAttributeNode) { + return log("warning " + elem.tagName + " no getAttributeNode method") + } + var b = elem.getAttributeNode("ms-important") + var c = elem.getAttributeNode("ms-controller") + if (typeof a === "string") { + return + } else if (node = b || c) { + var newVmodel = avalon.vmodels[node.value] + if (!newVmodel) { + return + } + //ms-important不包含父VM,ms-controller相反 + vmodels = node === b ? [newVmodel] : [newVmodel].concat(vmodels) + var name = node.name + elem.removeAttribute(name) //removeAttributeNode不会刷新[ms-controller]样式规则 + avalon(elem).removeClass(name) + createSignalTower(elem, newVmodel) + } + scanAttr(elem, vmodels) //扫描特性节点 + } + var rhasHtml = /\|\s*html\s*/, + r11a = /\|\|/g, + rlt = /</g, + rgt = />/g, + rstringLiteral = /(['"])(\\\1|.)+?\1/g + function getToken(value, pos) { + if (value.indexOf("|") > 0) { + var scapegoat = value.replace( rstringLiteral, function(_){ + return Array(_.length+1).join("1")// jshint ignore:line + }) + var index = scapegoat.replace(r11a, "\u1122\u3344").indexOf("|") //干掉所有短路或 + if (index > -1) { + return { + filters: value.slice(index), + value: value.slice(0, index), + pos: pos || 0, + expr: true + } + } + } + return { + value: value, + filters: "", + expr: true + } + } + + function scanExpr(str) { + var tokens = [], + value, start = 0, + stop + do { + stop = str.indexOf(openTag, start) + if (stop === -1) { + break + } + value = str.slice(start, stop) + if (value) { // {{ 左边的文本 + tokens.push({ + value: value, + filters: "", + expr: false + }) + } + start = stop + openTag.length + stop = str.indexOf(closeTag, start) + if (stop === -1) { + break + } + value = str.slice(start, stop) + if (value) { //处理{{ }}插值表达式 + tokens.push(getToken(value, start)) + } + start = stop + closeTag.length + } while (1) + value = str.slice(start) + if (value) { //}} 右边的文本 + tokens.push({ + value: value, + expr: false, + filters: "" + }) + } + return tokens + } + + function scanText(textNode, vmodels) { + var bindings = [] + if (textNode.nodeType === 8) { + var token = getToken(textNode.nodeValue) + var tokens = [token] + } else { + tokens = scanExpr(textNode.data) + } + if (tokens.length) { + for (var i = 0; token = tokens[i++]; ) { + var node = DOC.createTextNode(token.value) //将文本转换为文本节点,并替换原来的文本节点 + if (token.expr) { + token.type = "text" + token.element = node + token.filters = token.filters.replace(rhasHtml, function() { + token.type = "html" + return "" + })// jshint ignore:line + bindings.push(token) //收集带有插值表达式的文本 + } + avalonFragment.appendChild(node) + } + textNode.parentNode.replaceChild(avalonFragment, textNode) + if (bindings.length) + executeBindings(bindings, vmodels) + } + } + + var bools = ["autofocus,autoplay,async,allowTransparency,checked,controls", + "declare,disabled,defer,defaultChecked,defaultSelected", + "contentEditable,isMap,loop,multiple,noHref,noResize,noShade", + "open,readOnly,selected" + ].join(",") + var boolMap = {} + bools.replace(rword, function(name) { + boolMap[name.toLowerCase()] = name + }) + + var propMap = { //属性名映射 + "accept-charset": "acceptCharset", + "char": "ch", + "charoff": "chOff", + "class": "className", + "for": "htmlFor", + "http-equiv": "httpEquiv" + } + + var anomaly = ["accessKey,bgColor,cellPadding,cellSpacing,codeBase,codeType,colSpan", + "dateTime,defaultValue,frameBorder,longDesc,maxLength,marginWidth,marginHeight", + "rowSpan,tabIndex,useMap,vSpace,valueType,vAlign" + ].join(",") + anomaly.replace(rword, function(name) { + propMap[name.toLowerCase()] = name + }) + + var rnoscripts = /(?:[\s\S]+?)<\/noscript>/img + var rnoscriptText = /([\s\S]+?)<\/noscript>/im + + var getXHR = function() { + return new(window.XMLHttpRequest || ActiveXObject)("Microsoft.XMLHTTP") // jshint ignore:line + } + + var templatePool = avalon.templateCache = {} + + bindingHandlers.attr = function(data, vmodels) { + var text = data.value.trim(), + simple = true + if (text.indexOf(openTag) > -1 && text.indexOf(closeTag) > 2) { + simple = false + if (rexpr.test(text) && RegExp.rightContext === "" && RegExp.leftContext === "") { + simple = true + text = RegExp.$1 + } + } + if (data.type === "include") { + var elem = data.element + data.includeRendered = getBindingCallback(elem, "data-include-rendered", vmodels) + data.includeLoaded = getBindingCallback(elem, "data-include-loaded", vmodels) + var outer = data.includeReplace = !! avalon(elem).data("includeReplace") + if (avalon(elem).data("includeCache")) { + data.templateCache = {} + } + data.startInclude = DOC.createComment("ms-include") + data.endInclude = DOC.createComment("ms-include-end") + if (outer) { + data.element = data.startInclude + elem.parentNode.insertBefore(data.startInclude, elem) + elem.parentNode.insertBefore(data.endInclude, elem.nextSibling) + } else { + elem.insertBefore(data.startInclude, elem.firstChild) + elem.appendChild(data.endInclude) + } + } + data.handlerName = "attr" //handleName用于处理多种绑定共用同一种bindingExecutor的情况 + parseExprProxy(text, vmodels, data, (simple ? 0 : scanExpr(data.value))) + } + + bindingExecutors.attr = function(val, elem, data) { + var method = data.type, + attrName = data.param + if (method === "css") { + avalon(elem).css(attrName, val) + } else if (method === "attr") { + + // ms-attr-class="xxx" vm.xxx="aaa bbb ccc"将元素的className设置为aaa bbb ccc + // ms-attr-class="xxx" vm.xxx=false 清空元素的所有类名 + // ms-attr-name="yyy" vm.yyy="ooo" 为元素设置name属性 + var toRemove = (val === false) || (val === null) || (val === void 0) + + if (!W3C && propMap[attrName]) { //旧式IE下需要进行名字映射 + attrName = propMap[attrName] + } + var bool = boolMap[attrName] + if (typeof elem[bool] === "boolean") { + elem[bool] = !! val //布尔属性必须使用el.xxx = true|false方式设值 + if (!val) { //如果为false, IE全系列下相当于setAttribute(xxx,''),会影响到样式,需要进一步处理 + toRemove = true + } + } + if (toRemove) { + return elem.removeAttribute(attrName) + } + //SVG只能使用setAttribute(xxx, yyy), VML只能使用elem.xxx = yyy ,HTML的固有属性必须elem.xxx = yyy + var isInnate = rsvg.test(elem) ? false : (DOC.namespaces && isVML(elem)) ? true : attrName in elem.cloneNode(false) + if (isInnate) { + elem[attrName] = val + } else { + elem.setAttribute(attrName, val) + } + } else if (method === "include" && val) { + var vmodels = data.vmodels + var rendered = data.includeRendered + var loaded = data.includeLoaded + var replace = data.includeReplace + var target = replace ? elem.parentNode : elem + var scanTemplate = function(text) { + if (loaded) { + var newText = loaded.apply(target, [text].concat(vmodels)) + if (typeof newText === "string") + text = newText + } + if (rendered) { + checkScan(target, function() { + rendered.call(target) + }, NaN) + } + var lastID = data.includeLastID + if (data.templateCache && lastID && lastID !== val) { + var lastTemplate = data.templateCache[lastID] + if (!lastTemplate) { + lastTemplate = data.templateCache[lastID] = DOC.createElement("div") + ifGroup.appendChild(lastTemplate) + } + } + data.includeLastID = val + while (true) { + var node = data.startInclude.nextSibling + if (node && node !== data.endInclude) { + target.removeChild(node) + if (lastTemplate) + lastTemplate.appendChild(node) + } else { + break + } + } + var dom = getTemplateNodes(data, val, text) + var nodes = avalon.slice(dom.childNodes) + target.insertBefore(dom, data.endInclude) + scanNodeArray(nodes, vmodels) + } + + if (data.param === "src") { + if (typeof templatePool[val] === "string") { + avalon.nextTick(function() { + scanTemplate(templatePool[val]) + }) + } else if (Array.isArray(templatePool[val])) { //#805 防止在循环绑定中发出许多相同的请求 + templatePool[val].push(scanTemplate) + } else { + var xhr = getXHR() + xhr.onreadystatechange = function() { + if (xhr.readyState === 4) { + var s = xhr.status + if (s >= 200 && s < 300 || s === 304 || s === 1223) { + var text = xhr.responseText + for (var f = 0, fn; fn = templatePool[val][f++];) { + fn(text) + } + templatePool[val] = text + } + } + } + templatePool[val] = [scanTemplate] + xhr.open("GET", val, true) + if ("withCredentials" in xhr) { + xhr.withCredentials = true + } + xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest") + xhr.send(null) + } + } else { + //IE系列与够新的标准浏览器支持通过ID取得元素(firefox14+) + //http://tjvantoll.com/2012/07/19/dom-element-references-as-global-variables/ + var el = val && val.nodeType === 1 ? val : DOC.getElementById(val) + if (el) { + if (el.tagName === "NOSCRIPT" && !(el.innerHTML || el.fixIE78)) { //IE7-8 innerText,innerHTML都无法取得其内容,IE6能取得其innerHTML + xhr = getXHR() //IE9-11与chrome的innerHTML会得到转义的内容,它们的innerText可以 + xhr.open("GET", location, false) //谢谢Nodejs 乱炖群 深圳-纯属虚构 + xhr.send(null) + //http://bbs.csdn.net/topics/390349046?page=1#post-393492653 + var noscripts = DOC.getElementsByTagName("noscript") + var array = (xhr.responseText || "").match(rnoscripts) || [] + var n = array.length + for (var i = 0; i < n; i++) { + var tag = noscripts[i] + if (tag) { //IE6-8中noscript标签的innerHTML,innerText是只读的 + tag.style.display = "none" //http://haslayout.net/css/noscript-Ghost-Bug + tag.fixIE78 = (array[i].match(rnoscriptText) || ["", " "])[1] + } + } + } + avalon.nextTick(function() { + scanTemplate(el.fixIE78 || el.value || el.innerText || el.innerHTML) + }) + } + } + } else { + if (!root.hasAttribute && typeof val === "string" && (method === "src" || method === "href")) { + val = val.replace(/&/g, "&") //处理IE67自动转义的问题 + } + elem[method] = val + if (window.chrome && elem.tagName === "EMBED") { + var parent = elem.parentNode //#525 chrome1-37下embed标签动态设置src不能发生请求 + var comment = document.createComment("ms-src") + parent.replaceChild(comment, elem) + parent.replaceChild(elem, comment) + } + } + } + + function getTemplateNodes(data, id, text) { + var div = data.templateCache && data.templateCache[id] + if (div) { + var dom = DOC.createDocumentFragment(), + firstChild + while (firstChild = div.firstChild) { + dom.appendChild(firstChild) + } + return dom + } + return avalon.parseHTML(text) + } + +//这几个指令都可以使用插值表达式,如ms-src="aaa/{{b}}/{{c}}.html" + "title,alt,src,value,css,include,href".replace(rword, function(name) { + bindingHandlers[name] = bindingHandlers.attr + }) +//根据VM的属性值或表达式的值切换类名,ms-class="xxx yyy zzz:flag" +//http://www.cnblogs.com/rubylouvre/archive/2012/12/17/2818540.html + bindingHandlers["class"] = function(data, vmodels) { + var oldStyle = data.param, + text = data.value, + rightExpr + data.handlerName = "class" + if (!oldStyle || isFinite(oldStyle)) { + data.param = "" //去掉数字 + var noExpr = text.replace(rexprg, function(a) { + return a.replace(/./g, "0") + //return Math.pow(10, a.length - 1) //将插值表达式插入10的N-1次方来占位 + }) + var colonIndex = noExpr.indexOf(":") //取得第一个冒号的位置 + if (colonIndex === -1) { // 比如 ms-class="aaa bbb ccc" 的情况 + var className = text + } else { // 比如 ms-class-1="ui-state-active:checked" 的情况 + className = text.slice(0, colonIndex) + rightExpr = text.slice(colonIndex + 1) + parseExpr(rightExpr, vmodels, data) //决定是添加还是删除 + if (!data.evaluator) { + log("debug: ms-class '" + (rightExpr || "").trim() + "' 不存在于VM中") + return false + } else { + data._evaluator = data.evaluator + data._args = data.args + } + } + var hasExpr = rexpr.test(className) //比如ms-class="width{{w}}"的情况 + if (!hasExpr) { + data.immobileClass = className + } + parseExprProxy("", vmodels, data, (hasExpr ? scanExpr(className) : 0)) + } else { + data.immobileClass = data.oldStyle = data.param + parseExprProxy(text, vmodels, data) + } + } + + bindingExecutors["class"] = function(val, elem, data) { + var $elem = avalon(elem), + method = data.type + if (method === "class" && data.oldStyle) { //如果是旧风格 + $elem.toggleClass(data.oldStyle, !! val) + } else { + //如果存在冒号就有求值函数 + data.toggleClass = data._evaluator ? !! data._evaluator.apply(elem, data._args) : true + data.newClass = data.immobileClass || val + if (data.oldClass && data.newClass !== data.oldClass) { + $elem.removeClass(data.oldClass) + } + data.oldClass = data.newClass + switch (method) { + case "class": + $elem.toggleClass(data.newClass, data.toggleClass) + break + case "hover": + case "active": + if (!data.hasBindEvent) { //确保只绑定一次 + var activate = "mouseenter" //在移出移入时切换类名 + var abandon = "mouseleave" + if (method === "active") { //在聚焦失焦中切换类名 + elem.tabIndex = elem.tabIndex || -1 + activate = "mousedown" + abandon = "mouseup" + var fn0 = $elem.bind("mouseleave", function() { + data.toggleClass && $elem.removeClass(data.newClass) + }) + } + var fn1 = $elem.bind(activate, function() { + data.toggleClass && $elem.addClass(data.newClass) + }) + var fn2 = $elem.bind(abandon, function() { + data.toggleClass && $elem.removeClass(data.newClass) + }) + data.rollback = function() { + $elem.unbind("mouseleave", fn0) + $elem.unbind(activate, fn1) + $elem.unbind(abandon, fn2) + } + data.hasBindEvent = true + } + break; + } + } + } + + "hover,active".replace(rword, function(method) { + bindingHandlers[method] = bindingHandlers["class"] + }) +//ms-controller绑定已经在scanTag 方法中实现 +//ms-css绑定已由ms-attr绑定实现 + + +// bindingHandlers.data 定义在if.js + bindingExecutors.data = function(val, elem, data) { + var key = "data-" + data.param + if (val && typeof val === "object") { + elem[key] = val + } else { + elem.setAttribute(key, String(val)) + } + } +//双工绑定 + var duplexBinding = bindingHandlers.duplex = function(data, vmodels) { + var elem = data.element, + hasCast + parseExprProxy(data.value, vmodels, data, 0, 1) + + data.changed = getBindingCallback(elem, "data-duplex-changed", vmodels) || noop + if (data.evaluator && data.args) { + var params = [] + var casting = oneObject("string,number,boolean,checked") + if (elem.type === "radio" && data.param === "") { + data.param = "checked" + } + if (elem.msData) { + elem.msData["ms-duplex"] = data.value + } + data.param.replace(/\w+/g, function(name) { + if (/^(checkbox|radio)$/.test(elem.type) && /^(radio|checked)$/.test(name)) { + if (name === "radio") + log("ms-duplex-radio已经更名为ms-duplex-checked") + name = "checked" + data.isChecked = true + } + if (name === "bool") { + name = "boolean" + log("ms-duplex-bool已经更名为ms-duplex-boolean") + } else if (name === "text") { + name = "string" + log("ms-duplex-text已经更名为ms-duplex-string") + } + if (casting[name]) { + hasCast = true + } + avalon.Array.ensure(params, name) + }) + if (!hasCast) { + params.push("string") + } + data.param = params.join("-") + data.bound = function(type, callback) { + if (elem.addEventListener) { + elem.addEventListener(type, callback, false) + } else { + elem.attachEvent("on" + type, callback) + } + var old = data.rollback + data.rollback = function() { + elem.avalonSetter = null + avalon.unbind(elem, type, callback) + old && old() + } + } + for (var i in avalon.vmodels) { + var v = avalon.vmodels[i] + v.$fire("avalon-ms-duplex-init", data) + } + var cpipe = data.pipe || (data.pipe = pipe) + cpipe(null, data, "init") + var tagName = elem.tagName + duplexBinding[tagName] && duplexBinding[tagName](elem, data.evaluator.apply(null, data.args), data) + } + } +//不存在 bindingExecutors.duplex + + function fixNull(val) { + return val == null ? "" : val + } + avalon.duplexHooks = { + checked: { + get: function(val, data) { + return !data.element.oldValue + } + }, + string: { + get: function(val) { //同步到VM + return val + }, + set: fixNull + }, + "boolean": { + get: function(val) { + return val === "true" + }, + set: fixNull + }, + number: { + get: function(val, data) { + var number = parseFloat(val) + if (-val === -number) { + return number + } + var arr = /strong|medium|weak/.exec(data.element.getAttribute("data-duplex-number")) || ["medium"] + switch (arr[0]) { + case "strong": + return 0 + case "medium": + return val === "" ? "" : 0 + case "weak": + return val + } + }, + set: fixNull + } + } + + function pipe(val, data, action, e) { + data.param.replace(/\w+/g, function(name) { + var hook = avalon.duplexHooks[name] + if (hook && typeof hook[action] === "function") { + val = hook[action](val, data) + } + }) + return val + } + + var TimerID, ribbon = [] + + avalon.tick = function(fn) { + if (ribbon.push(fn) === 1) { + TimerID = setInterval(ticker, 60) + } + } + + function ticker() { + for (var n = ribbon.length - 1; n >= 0; n--) { + var el = ribbon[n] + if (el() === false) { + ribbon.splice(n, 1) + } + } + if (!ribbon.length) { + clearInterval(TimerID) + } + } + + var watchValueInTimer = noop + var rmsinput = /text|password|hidden/ + new function() { // jshint ignore:line + try { //#272 IE9-IE11, firefox + var setters = {} + var aproto = HTMLInputElement.prototype + var bproto = HTMLTextAreaElement.prototype + + function newSetter(value) { // jshint ignore:line + if (avalon.optimize || this.parentNode) { + setters[this.tagName].call(this, value) + if (!rmsinput.test(this.type)) + return + if (!this.msFocus && this.avalonSetter) { + this.avalonSetter() + } + } + } + var inputProto = HTMLInputElement.prototype + Object.getOwnPropertyNames(inputProto) //故意引发IE6-8等浏览器报错 + setters["INPUT"] = Object.getOwnPropertyDescriptor(aproto, "value").set + Object.defineProperty(aproto, "value", { + set: newSetter + }) + setters["TEXTAREA"] = Object.getOwnPropertyDescriptor(bproto, "value").set + Object.defineProperty(bproto, "value", { + set: newSetter + }) + } catch (e) { + //在chrome 43中 ms-duplex终于不需要使用定时器实现双向绑定了 + // http://updates.html5rocks.com/2015/04/DOM-attributes-now-on-the-prototype + // https://docs.google.com/document/d/1jwA8mtClwxI-QJuHT7872Z0pxpZz8PBkf2bGAbsUtqs/edit?pli=1 + watchValueInTimer = avalon.tick + } + } // jshint ignore:line + if (IEVersion) { + avalon.bind(DOC, "selectionchange", function(e) { + var el = DOC.activeElement + if (el && typeof el.avalonSetter === "function") { + el.avalonSetter() + } + }) + } + +//处理radio, checkbox, text, textarea, password + duplexBinding.INPUT = function(element, evaluator, data) { + var $type = element.type, + bound = data.bound, + $elem = avalon(element), + composing = false + + function callback(value) { + data.changed.call(this, value, data) + } + + function compositionStart() { + composing = true + } + + function compositionEnd() { + composing = false + } + //当value变化时改变model的值 + var updateVModel = function() { + if (composing) //处理中文输入法在minlengh下引发的BUG + return + var val = element.oldValue = element.value //防止递归调用形成死循环 + var lastValue = data.pipe(val, data, "get") + if ($elem.data("duplexObserve") !== false) { + evaluator(lastValue) + callback.call(element, lastValue) + if ($elem.data("duplex-focus")) { + avalon.nextTick(function() { + element.focus() + }) + } + } + } + //当model变化时,它就会改变value的值 + data.handler = function() { + var val = data.pipe(evaluator(), data, "set") + "" //fix #673 + if (val !== element.oldValue) { + element.value = val + } + } + if (data.isChecked || $type === "radio") { + var IE6 = IEVersion === 6 + updateVModel = function() { + if ($elem.data("duplexObserve") !== false) { + var lastValue = data.pipe(element.value, data, "get") + evaluator(lastValue) + callback.call(element, lastValue) + } + } + data.handler = function() { + var val = evaluator() + var checked = data.isChecked ? !! val : val + "" === element.value + element.oldValue = checked + if (IE6) { + setTimeout(function() { + //IE8 checkbox, radio是使用defaultChecked控制选中状态, + //并且要先设置defaultChecked后设置checked + //并且必须设置延迟 + element.defaultChecked = checked + element.checked = checked + }, 31) + } else { + element.checked = checked + } + } + bound("click", updateVModel) + } else if ($type === "checkbox") { + updateVModel = function() { + if ($elem.data("duplexObserve") !== false) { + var method = element.checked ? "ensure" : "remove" + var array = evaluator() + if (!Array.isArray(array)) { + log("ms-duplex应用于checkbox上要对应一个数组") + array = [array] + } + avalon.Array[method](array, data.pipe(element.value, data, "get")) + callback.call(element, array) + } + } + + data.handler = function() { + var array = [].concat(evaluator()) //强制转换为数组 + element.checked = array.indexOf(data.pipe(element.value, data, "get")) > -1 + } + bound(W3C ? "change" : "click", updateVModel) + } else { + var events = element.getAttribute("data-duplex-event") || "input" + if (element.attributes["data-event"]) { + log("data-event指令已经废弃,请改用data-duplex-event") + } + + function delay(e) { // jshint ignore:line + setTimeout(function() { + updateVModel(e) + }) + } + events.replace(rword, function(name) { + switch (name) { + case "input": + if (!IEVersion) { // W3C + bound("input", updateVModel) + //非IE浏览器才用这个 + bound("compositionstart", compositionStart) + bound("compositionend", compositionEnd) + bound("DOMAutoComplete", updateVModel) + } else { //onpropertychange事件无法区分是程序触发还是用户触发 + // IE下通过selectionchange事件监听IE9+点击input右边的X的清空行为,及粘贴,剪切,删除行为 + if (IEVersion > 8) { + bound("input", updateVModel) //IE9使用propertychange无法监听中文输入改动 + } else { + bound("propertychange", function(e) { //IE6-8下第一次修改时不会触发,需要使用keydown或selectionchange修正 + if (e.propertyName === "value") { + updateVModel() + } + }) + } + bound("dragend", delay) + //http://www.cnblogs.com/rubylouvre/archive/2013/02/17/2914604.html + //http://www.matts411.com/post/internet-explorer-9-oninput/ + } + break + default: + bound(name, updateVModel) + break + } + }) + bound("focus", function() { + element.msFocus = true + }) + bound("blur", function() { + element.msFocus = false + }) + + if (rmsinput.test($type)) { + watchValueInTimer(function() { + if (avalon.optimize || element.parentNode) { + if (!element.msFocus && element.oldValue !== element.value) { + updateVModel() + } + } else if (!element.msRetain) { + return false + } + }) + } + + element.avalonSetter = updateVModel //#765 + } + + element.oldValue = element.value + avalon.injectBinding(data) + callback.call(element, element.value) + } + duplexBinding.TEXTAREA = duplexBinding.INPUT + duplexBinding.SELECT = function(element, evaluator, data) { + var $elem = avalon(element) + + function updateVModel() { + if ($elem.data("duplexObserve") !== false) { + var val = $elem.val() //字符串或字符串数组 + if (Array.isArray(val)) { + val = val.map(function(v) { + return data.pipe(v, data, "get") + }) + } else { + val = data.pipe(val, data, "get") + } + if (val + "" !== element.oldValue) { + evaluator(val) + } + data.changed.call(element, val, data) + } + } + data.handler = function() { + var val = evaluator() + val = val && val.$model || val + if (Array.isArray(val)) { + if (!element.multiple) { + log("ms-duplex在不能对应一个数组") + } + } + //必须变成字符串后才能比较 + val = Array.isArray(val) ? val.map(String) : val + "" + if (val + "" !== element.oldValue) { + $elem.val(val) + element.oldValue = val + "" + } + } + data.bound("change", updateVModel) + element.msCallback = function() { + avalon.injectBinding(data) + data.changed.call(element, evaluator(), data) + } + } +// bindingHandlers.html 定义在if.js + bindingExecutors.html = function (val, elem, data) { + var isHtmlFilter = elem.nodeType !== 1 + var parent = isHtmlFilter ? elem.parentNode : elem + if (!parent) + return + val = val == null ? "" : val + if (data.oldText !== val) { + data.oldText = val + } else { + return + } + if (elem.nodeType === 3) { + var signature = generateID("html") + parent.insertBefore(DOC.createComment(signature), elem) + data.element = DOC.createComment(signature + ":end") + parent.replaceChild(data.element, elem) + elem = data.element + } + if (typeof val !== "object") {//string, number, boolean + var fragment = avalon.parseHTML(String(val)) + } else if (val.nodeType === 11) { //将val转换为文档碎片 + fragment = val + } else if (val.nodeType === 1 || val.item) { + var nodes = val.nodeType === 1 ? val.childNodes : val.item + fragment = avalonFragment.cloneNode(true) + while (nodes[0]) { + fragment.appendChild(nodes[0]) + } + } + + nodes = avalon.slice(fragment.childNodes) + //插入占位符, 如果是过滤器,需要有节制地移除指定的数量,如果是html指令,直接清空 + if (isHtmlFilter) { + var endValue = elem.nodeValue.slice(0, -4) + while (true) { + var node = elem.previousSibling + if (!node || node.nodeType === 8 && node.nodeValue === endValue) { + break + } else { + parent.removeChild(node) + } + } + parent.insertBefore(fragment, elem) + } else { + avalon.clearHTML(elem).appendChild(fragment) + } + scanNodeArray(nodes, data.vmodels) + } + bindingHandlers["if"] = + bindingHandlers.data = + bindingHandlers.text = + bindingHandlers.html = + function(data, vmodels) { + parseExprProxy(data.value, vmodels, data) + } + + bindingExecutors["if"] = function(val, elem, data) { + try { + if(!elem.parentNode) return + } catch(e) {return} + if (val) { //插回DOM树 + if (elem.nodeType === 8) { + elem.parentNode.replaceChild(data.template, elem) + // animate.enter(data.template, elem.parentNode) + elem = data.element = data.template //这时可能为null + } + if (elem.getAttribute(data.name)) { + elem.removeAttribute(data.name) + scanAttr(elem, data.vmodels) + } + data.rollback = null + } else { //移出DOM树,并用注释节点占据原位置 + if (elem.nodeType === 1) { + var node = data.element = DOC.createComment("ms-if") + elem.parentNode.replaceChild(node, elem) + // animate.leave(elem, node.parentNode, node) + data.template = elem //元素节点 + ifGroup.appendChild(elem) + data.rollback = function() { + if (elem.parentNode === ifGroup) { + ifGroup.removeChild(elem) + } + } + } + } + } +//ms-important绑定已经在scanTag 方法中实现 +//ms-include绑定已由ms-attr绑定实现 + + var rdash = /\(([^)]*)\)/ + bindingHandlers.on = function(data, vmodels) { + var value = data.value + data.type = "on" + var eventType = data.param.replace(/-\d+$/, "") // ms-on-mousemove-10 + if (typeof bindingHandlers.on[eventType + "Hook"] === "function") { + bindingHandlers.on[eventType + "Hook"](data) + } + if (value.indexOf("(") > 0 && value.indexOf(")") > -1) { + var matched = (value.match(rdash) || ["", ""])[1].trim() + if (matched === "" || matched === "$event") { // aaa() aaa($event)当成aaa处理 + value = value.replace(rdash, "") + } + } + parseExprProxy(value, vmodels, data) + } + + bindingExecutors.on = function(callback, elem, data) { + callback = function(e) { + var fn = data.evaluator || noop + return fn.apply(this, data.args.concat(e)) + } + var eventType = data.param.replace(/-\d+$/, "") // ms-on-mousemove-10 + if (eventType === "scan") { + callback.call(elem, { + type: eventType + }) + } else if (typeof data.specialBind === "function") { + data.specialBind(elem, callback) + } else { + var removeFn = avalon.bind(elem, eventType, callback) + } + data.rollback = function() { + if (typeof data.specialUnbind === "function") { + data.specialUnbind() + } else { + avalon.unbind(elem, eventType, removeFn) + } + } + } + bindingHandlers.repeat = function (data, vmodels) { + var type = data.type + parseExprProxy(data.value, vmodels, data, 0, 1) + var freturn = false + try { + var $repeat = data.$repeat = data.evaluator.apply(0, data.args || []) + var xtype = avalon.type($repeat) + if (xtype !== "object" && xtype !== "array") { + freturn = true + avalon.log("warning:" + data.value + "只能是对象或数组") + } + } catch (e) { + freturn = true + } + var arr = data.value.split(".") || [] + if (arr.length > 1) { + arr.pop() + var n = arr[0] + for (var i = 0, v; v = vmodels[i++]; ) { + if (v && v.hasOwnProperty(n)) { + var events = v[n].$events || {} + events[subscribers] = events[subscribers] || [] + events[subscribers].push(data) + break + } + } + } + + var elem = data.element + if (elem.nodeType === 1) { + elem.removeAttribute(data.name) + data.sortedCallback = getBindingCallback(elem, "data-with-sorted", vmodels) + data.renderedCallback = getBindingCallback(elem, "data-" + type + "-rendered", vmodels) + var signature = generateID(type) + var start = DOC.createComment(signature) + var end = DOC.createComment(signature + ":end") + data.signature = signature + data.template = avalonFragment.cloneNode(false) + if (type === "repeat") { + var parent = elem.parentNode + parent.replaceChild(end, elem) + parent.insertBefore(start, end) + data.template.appendChild(elem) + } else { + while (elem.firstChild) { + data.template.appendChild(elem.firstChild) + } + elem.appendChild(start) + elem.appendChild(end) + } + data.element = end + data.handler = bindingExecutors.repeat + data.rollback = function () { + var elem = data.element + if (!elem) + return + data.handler("clear") + } + } + + if (freturn) { + return + } + + data.$outer = {} + var check0 = "$key" + var check1 = "$val" + if (Array.isArray($repeat)) { + if (!$repeat.$map) { + $repeat.$map = { + el: 1 + } + var m = $repeat.length + var $proxy = [] + for (i = 0; i < m; i++) {//生成代理VM + $proxy.push(eachProxyAgent(i, $repeat)) + } + $repeat.$proxy = $proxy + } + $repeat.$map[data.param || "el"] = 1 + check0 = "$first" + check1 = "$last" + } + for (i = 0; v = vmodels[i++]; ) { + if (v.hasOwnProperty(check0) && v.hasOwnProperty(check1)) { + data.$outer = v + break + } + } + var $events = $repeat.$events + var $list = ($events || {})[subscribers] + injectDependency($list, data) + if (xtype === "object") { + data.$with = true + $repeat.$proxy || ($repeat.$proxy = {}) + data.handler("append", $repeat) + } else if ($repeat.length) { + data.handler("add", 0, $repeat.length) + } + } + + bindingExecutors.repeat = function (method, pos, el) { + if (method) { + var data = this, start, fragment + var end = data.element + var comments = getComments(data) + var parent = end.parentNode + var transation = avalonFragment.cloneNode(false) + switch (method) { + case "add": //在pos位置后添加el数组(pos为插入位置,el为要插入的个数) + var n = pos + el + var fragments = [] + var array = data.$repeat + for (var i = pos; i < n; i++) { + var proxy = array.$proxy[i] + proxy.$outer = data.$outer + shimController(data, transation, proxy, fragments) + } + var now = new Date - 0 + avalon.optimize = avalon.optimize || now + for (i = 0; fragment = fragments[i++]; ) { + scanNodeArray(fragment.nodes, fragment.vmodels) + fragment.nodes = fragment.vmodels = null + } + if(avalon.optimize === now){ + delete avalon.optimize + } + parent.insertBefore(transation, comments[pos] || end) + avalon.profile("插入操作花费了 "+ (new Date - now)) + break + case "del": //将pos后的el个元素删掉(pos, el都是数字) + sweepNodes(comments[pos], comments[pos + el] || end) + break + case "clear": + start = comments[0] + if (start) { + sweepNodes(start, end) + } + break + case "move": + start = comments[0] + if (start) { + var signature = start.nodeValue + var rooms = [] + var room = [], + node + sweepNodes(start, end, function () { + room.unshift(this) + if (this.nodeValue === signature) { + rooms.unshift(room) + room = [] + } + }) + sortByIndex(rooms, pos) + while (room = rooms.shift()) { + while (node = room.shift()) { + transation.appendChild(node) + } + } + parent.insertBefore(transation, end) + } + break + case "append": + var object = pos //原来第2参数, 被循环对象 + var pool = object.$proxy //代理对象组成的hash + var keys = [] + fragments = [] + for (var key in pool) { + if (!object.hasOwnProperty(key)) { + proxyRecycler(pool[key], withProxyPool) //去掉之前的代理VM + delete(pool[key]) + } + } + for (key in object) { //得到所有键名 + if (object.hasOwnProperty(key) && key !== "hasOwnProperty" && key !== "$proxy") { + keys.push(key) + } + } + if (data.sortedCallback) { //如果有回调,则让它们排序 + var keys2 = data.sortedCallback.call(parent, keys) + if (keys2 && Array.isArray(keys2) && keys2.length) { + keys = keys2 + } + } + + for (i = 0; key = keys[i++]; ) { + if (key !== "hasOwnProperty") { + pool[key] = withProxyAgent(pool[key], key, data) + shimController(data, transation, pool[key], fragments) + } + } + + parent.insertBefore(transation, end) + for (i = 0; fragment = fragments[i++]; ) { + scanNodeArray(fragment.nodes, fragment.vmodels) + fragment.nodes = fragment.vmodels = null + } + break + } + if (method === "clear") + method = "del" + var callback = data.renderedCallback || noop, + args = arguments + checkScan(parent, function () { + callback.apply(parent, args) + if (parent.oldValue && parent.tagName === "SELECT") { //fix #503 + avalon(parent).val(parent.oldValue.split(",")) + } + }, NaN) + } + } + + "with,each".replace(rword, function (name) { + bindingHandlers[name] = bindingHandlers.repeat + }) + avalon.pool = eachProxyPool + function shimController(data, transation, proxy, fragments) { + var content = data.template.cloneNode(true) + var nodes = avalon.slice(content.childNodes) + if (!data.$with) { + content.insertBefore(DOC.createComment(data.signature), content.firstChild) + } + transation.appendChild(content) + var nv = [proxy].concat(data.vmodels) + var fragment = { + nodes: nodes, + vmodels: nv + } + fragments.push(fragment) + } + + function getComments(data) { + var end = data.element + var signature = end.nodeValue.replace(":end", "") + var node = end.previousSibling + var array = [] + while (node) { + if (node.nodeValue === signature) { + array.unshift(node) + } + node = node.previousSibling + } + return array + } + + +//移除掉start与end之间的节点(保留end) + function sweepNodes(start, end, callback) { + while (true) { + var node = end.previousSibling + if (!node) + break + node.parentNode.removeChild(node) + callback && callback.call(node) + if (node === start) { + break + } + } + } + +// 为ms-each,ms-with, ms-repeat会创建一个代理VM, +// 通过它们保持一个下上文,让用户能调用$index,$first,$last,$remove,$key,$val,$outer等属性与方法 +// 所有代理VM的产生,消费,收集,存放通过xxxProxyFactory,xxxProxyAgent, recycleProxies,xxxProxyPool实现 + var withProxyPool = [] + function withProxyFactory() { + var proxy = modelFactory({ + $key: "", + $outer: {}, + $host: {}, + $val: { + get: function () { + return this.$host[this.$key] + }, + set: function (val) { + this.$host[this.$key] = val + } + } + }, { + $val: 1 + }) + proxy.$id = generateID("$proxy$with") + return proxy + } + + function withProxyAgent(proxy, key, data) { + proxy = proxy || withProxyPool.pop() + if (!proxy) { + proxy = withProxyFactory() + } + var host = data.$repeat + proxy.$key = key + proxy.$host = host + proxy.$outer = data.$outer + if (host.$events) { + proxy.$events.$val = host.$events[key] + } else { + proxy.$events = {} + } + return proxy + } + + function eachProxyRecycler(proxies) { + proxies.forEach(function (proxy) { + proxyRecycler(proxy, eachProxyPool) + }) + proxies.length = 0 + } + + function proxyRecycler(proxy, proxyPool) { + for (var i in proxy.$events) { + if (Array.isArray(proxy.$events[i])) { + proxy.$events[i].forEach(function (data) { + if (typeof data === "object") + disposeData(data) + })// jshint ignore:line + proxy.$events[i].length = 0 + } + } + proxy.$host = proxy.$outer = {} + if (proxyPool.unshift(proxy) > kernel.maxRepeatSize) { + proxyPool.pop() + } + } + /********************************************************************* + * 各种指令 * + **********************************************************************/ +//ms-skip绑定已经在scanTag 方法中实现 +// bindingHandlers.text 定义在if.js + bindingExecutors.text = function(val, elem) { + val = val == null ? "" : val //不在页面上显示undefined null + if (elem.nodeType === 3) { //绑定在文本节点上 + try { //IE对游离于DOM树外的节点赋值会报错 + elem.data = val + } catch (e) {} + } else { //绑定在特性节点上 + if ("textContent" in elem) { + elem.textContent = val + } else { + elem.innerText = val + } + } + } + function parseDisplay(nodeName, val) { + //用于取得此类标签的默认display值 + var key = "_" + nodeName + if (!parseDisplay[key]) { + var node = DOC.createElement(nodeName) + root.appendChild(node) + if (W3C) { + val = getComputedStyle(node, null).display + } else { + val = node.currentStyle.display + } + root.removeChild(node) + parseDisplay[key] = val + } + return parseDisplay[key] + } + + avalon.parseDisplay = parseDisplay + + bindingHandlers.visible = function(data, vmodels) { + var elem = avalon(data.element) + var display = elem.css("display") + if (display === "none") { + var style = elem[0].style + var has = /visibility/i.test(style.cssText) + var visible = elem.css("visibility") + style.display = "" + style.visibility = "hidden" + display = elem.css("display") + if (display === "none") { + display = parseDisplay(elem[0].nodeName) + } + style.visibility = has ? visible : "" + } + data.display = display + parseExprProxy(data.value, vmodels, data) + } + + bindingExecutors.visible = function(val, elem, data) { + elem.style.display = val ? data.display : "none" + } + bindingHandlers.widget = function(data, vmodels) { + var args = data.value.match(rword) + var elem = data.element + var widget = args[0] + var id = args[1] + if (!id || id === "$") { //没有定义或为$时,取组件名+随机数 + id = generateID(widget) + } + var optName = args[2] || widget //没有定义,取组件名 + var constructor = avalon.ui[widget] + if (typeof constructor === "function") { //ms-widget="tabs,tabsAAA,optname" + vmodels = elem.vmodels || vmodels + for (var i = 0, v; v = vmodels[i++];) { + if (v.hasOwnProperty(optName) && typeof v[optName] === "object") { + var vmOptions = v[optName] + vmOptions = vmOptions.$model || vmOptions + break + } + } + if (vmOptions) { + var wid = vmOptions[widget + "Id"] + if (typeof wid === "string") { + log("warning!不再支持" + widget + "Id") + id = wid + } + } + //抽取data-tooltip-text、data-tooltip-attr属性,组成一个配置对象 + var widgetData = avalon.getWidgetData(elem, widget) + data.value = [widget, id, optName].join(",") + data[widget + "Id"] = id + data.evaluator = noop + elem.msData["ms-widget-id"] = id + var options = data[widget + "Options"] = avalon.mix({}, constructor.defaults, vmOptions || {}, widgetData) + elem.removeAttribute("ms-widget") + var vmodel = constructor(elem, data, vmodels) || {} //防止组件不返回VM + if (vmodel.$id) { + avalon.vmodels[id] = vmodel + createSignalTower(elem, vmodel) + try { + vmodel.$init(function() { + avalon.scan(elem, [vmodel].concat(vmodels)) + if (typeof options.onInit === "function") { + options.onInit.call(elem, vmodel, options, vmodels) + } + }) + } catch (e) {} + data.rollback = function() { + try { + vmodel.widgetElement = null + vmodel.$remove() + } catch (e) {} + elem.msData = {} + delete avalon.vmodels[vmodel.$id] + } + injectDisposeQueue(data, widgetList) + if (window.chrome) { + elem.addEventListener("DOMNodeRemovedFromDocument", function() { + setTimeout(rejectDisposeQueue) + }) + } + } else { + avalon.scan(elem, vmodels) + } + } else if (vmodels.length) { //如果该组件还没有加载,那么保存当前的vmodels + elem.vmodels = vmodels + } + } + var widgetList = [] +//不存在 bindingExecutors.widget + /********************************************************************* + * 自带过滤器 * + **********************************************************************/ + var rscripts = /]*>([\S\s]*?)<\/script\s*>/gim + var ron = /\s+(on[^=\s]+)(?:=("[^"]*"|'[^']*'|[^\s>]+))?/g + var ropen = /<\w+\b(?:(["'])[^"]*?(\1)|[^>])*>/ig + var rsanitize = { + a: /\b(href)\=("javascript[^"]*"|'javascript[^']*')/ig, + img: /\b(src)\=("javascript[^"]*"|'javascript[^']*')/ig, + form: /\b(action)\=("javascript[^"]*"|'javascript[^']*')/ig + } + var rsurrogate = /[\uD800-\uDBFF][\uDC00-\uDFFF]/g + var rnoalphanumeric = /([^\#-~| |!])/g; + + function numberFormat(number, decimals, point, thousands) { + //form http://phpjs.org/functions/number_format/ + //number 必需,要格式化的数字 + //decimals 可选,规定多少个小数位。 + //point 可选,规定用作小数点的字符串(默认为 . )。 + //thousands 可选,规定用作千位分隔符的字符串(默认为 , ),如果设置了该参数,那么所有其他参数都是必需的。 + number = (number + '') + .replace(/[^0-9+\-Ee.]/g, '') + var n = !isFinite(+number) ? 0 : +number, + prec = !isFinite(+decimals) ? 3 : Math.abs(decimals), + sep = thousands || ",", + dec = point || ".", + s = '', + toFixedFix = function(n, prec) { + var k = Math.pow(10, prec) + return '' + (Math.round(n * k) / k) + .toFixed(prec) + } + // Fix for IE parseFloat(0.55).toFixed(0) = 0; + s = (prec ? toFixedFix(n, prec) : '' + Math.round(n)) + .split('.') + if (s[0].length > 3) { + s[0] = s[0].replace(/\B(?=(?:\d{3})+(?!\d))/g, sep) + } + if ((s[1] || '') + .length < prec) { + s[1] = s[1] || '' + s[1] += new Array(prec - s[1].length + 1) + .join('0') + } + return s.join(dec) + } + + + var filters = avalon.filters = { + uppercase: function(str) { + return str.toUpperCase() + }, + lowercase: function(str) { + return str.toLowerCase() + }, + truncate: function(str, length, truncation) { + //length,新字符串长度,truncation,新字符串的结尾的字段,返回新字符串 + length = length || 30 + truncation = typeof truncation === "string" ? truncation : "..." + return str.length > length ? str.slice(0, length - truncation.length) + truncation : String(str) + }, + $filter: function(val) { + for (var i = 1, n = arguments.length; i < n; i++) { + var array = arguments[i] + var fn = avalon.filters[array.shift()] + if (typeof fn === "function") { + var arr = [val].concat(array) + val = fn.apply(null, arr) + } + } + return val + }, + camelize: camelize, + //https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet + // chrome + // chrome + // IE67chrome + // IE67chrome + // IE67chrome + sanitize: function(str) { + return str.replace(rscripts, "").replace(ropen, function(a, b) { + var match = a.toLowerCase().match(/<(\w+)\s/) + if (match) { //处理a标签的href属性,img标签的src属性,form标签的action属性 + var reg = rsanitize[match[1]] + if (reg) { + a = a.replace(reg, function(s, name, value) { + var quote = value.charAt(0) + return name + "=" + quote + "javascript:void(0)" + quote// jshint ignore:line + }) + } + } + return a.replace(ron, " ").replace(/\s+/g, " ") //移除onXXX事件 + }) + }, + escape: function(str) { + //将字符串经过 str 转义得到适合在页面中显示的内容, 例如替换 < 为 < + return String(str). + replace(/&/g, '&'). + replace(rsurrogate, function(value) { + var hi = value.charCodeAt(0) + var low = value.charCodeAt(1) + return '&#' + (((hi - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000) + ';' + }). + replace(rnoalphanumeric, function(value) { + return '&#' + value.charCodeAt(0) + ';' + }). + replace(//g, '>') + }, + currency: function(amount, symbol, fractionSize) { + return (symbol || "\uFFE5") + numberFormat(amount, isFinite(fractionSize) ? fractionSize : 2) + }, + number: numberFormat + } + /* + 'yyyy': 4 digit representation of year (e.g. AD 1 => 0001, AD 2010 => 2010) + 'yy': 2 digit representation of year, padded (00-99). (e.g. AD 2001 => 01, AD 2010 => 10) + 'y': 1 digit representation of year, e.g. (AD 1 => 1, AD 199 => 199) + 'MMMM': Month in year (January-December) + 'MMM': Month in year (Jan-Dec) + 'MM': Month in year, padded (01-12) + 'M': Month in year (1-12) + 'dd': Day in month, padded (01-31) + 'd': Day in month (1-31) + 'EEEE': Day in Week,(Sunday-Saturday) + 'EEE': Day in Week, (Sun-Sat) + 'HH': Hour in day, padded (00-23) + 'H': Hour in day (0-23) + 'hh': Hour in am/pm, padded (01-12) + 'h': Hour in am/pm, (1-12) + 'mm': Minute in hour, padded (00-59) + 'm': Minute in hour (0-59) + 'ss': Second in minute, padded (00-59) + 's': Second in minute (0-59) + 'a': am/pm marker + 'Z': 4 digit (+sign) representation of the timezone offset (-1200-+1200) + format string can also be one of the following predefined localizable formats: + + 'medium': equivalent to 'MMM d, y h:mm:ss a' for en_US locale (e.g. Sep 3, 2010 12:05:08 pm) + 'short': equivalent to 'M/d/yy h:mm a' for en_US locale (e.g. 9/3/10 12:05 pm) + 'fullDate': equivalent to 'EEEE, MMMM d,y' for en_US locale (e.g. Friday, September 3, 2010) + 'longDate': equivalent to 'MMMM d, y' for en_US locale (e.g. September 3, 2010 + 'mediumDate': equivalent to 'MMM d, y' for en_US locale (e.g. Sep 3, 2010) + 'shortDate': equivalent to 'M/d/yy' for en_US locale (e.g. 9/3/10) + 'mediumTime': equivalent to 'h:mm:ss a' for en_US locale (e.g. 12:05:08 pm) + 'shortTime': equivalent to 'h:mm a' for en_US locale (e.g. 12:05 pm) + */ + new function() {// jshint ignore:line + function toInt(str) { + return parseInt(str, 10) || 0 + } + + function padNumber(num, digits, trim) { + var neg = "" + if (num < 0) { + neg = '-' + num = -num + } + num = "" + num + while (num.length < digits) + num = "0" + num + if (trim) + num = num.substr(num.length - digits) + return neg + num + } + + function dateGetter(name, size, offset, trim) { + return function(date) { + var value = date["get" + name]() + if (offset > 0 || value > -offset) + value += offset + if (value === 0 && offset === -12) { + value = 12 + } + return padNumber(value, size, trim) + } + } + + function dateStrGetter(name, shortForm) { + return function(date, formats) { + var value = date["get" + name]() + var get = (shortForm ? ("SHORT" + name) : name).toUpperCase() + return formats[get][value] + } + } + + function timeZoneGetter(date) { + var zone = -1 * date.getTimezoneOffset() + var paddedZone = (zone >= 0) ? "+" : "" + paddedZone += padNumber(Math[zone > 0 ? "floor" : "ceil"](zone / 60), 2) + padNumber(Math.abs(zone % 60), 2) + return paddedZone + } + //取得上午下午 + + function ampmGetter(date, formats) { + return date.getHours() < 12 ? formats.AMPMS[0] : formats.AMPMS[1] + } + var DATE_FORMATS = { + yyyy: dateGetter("FullYear", 4), + yy: dateGetter("FullYear", 2, 0, true), + y: dateGetter("FullYear", 1), + MMMM: dateStrGetter("Month"), + MMM: dateStrGetter("Month", true), + MM: dateGetter("Month", 2, 1), + M: dateGetter("Month", 1, 1), + dd: dateGetter("Date", 2), + d: dateGetter("Date", 1), + HH: dateGetter("Hours", 2), + H: dateGetter("Hours", 1), + hh: dateGetter("Hours", 2, -12), + h: dateGetter("Hours", 1, -12), + mm: dateGetter("Minutes", 2), + m: dateGetter("Minutes", 1), + ss: dateGetter("Seconds", 2), + s: dateGetter("Seconds", 1), + sss: dateGetter("Milliseconds", 3), + EEEE: dateStrGetter("Day"), + EEE: dateStrGetter("Day", true), + a: ampmGetter, + Z: timeZoneGetter + } + var rdateFormat = /((?:[^yMdHhmsaZE']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+|H+|h+|m+|s+|a|Z))(.*)/ + var raspnetjson = /^\/Date\((\d+)\)\/$/ + filters.date = function(date, format) { + var locate = filters.date.locate, + text = "", + parts = [], + fn, match + format = format || "mediumDate" + format = locate[format] || format + if (typeof date === "string") { + if (/^\d+$/.test(date)) { + date = toInt(date) + } else if (raspnetjson.test(date)) { + date = +RegExp.$1 + } else { + var trimDate = date.trim() + var dateArray = [0, 0, 0, 0, 0, 0, 0] + var oDate = new Date(0) + //取得年月日 + trimDate = trimDate.replace(/^(\d+)\D(\d+)\D(\d+)/, function(_, a, b, c) { + var array = c.length === 4 ? [c, a, b] : [a, b, c] + dateArray[0] = toInt(array[0]) //年 + dateArray[1] = toInt(array[1]) - 1 //月 + dateArray[2] = toInt(array[2]) //日 + return "" + }) + var dateSetter = oDate.setFullYear + var timeSetter = oDate.setHours + trimDate = trimDate.replace(/[T\s](\d+):(\d+):?(\d+)?\.?(\d)?/, function(_, a, b, c, d) { + dateArray[3] = toInt(a) //小时 + dateArray[4] = toInt(b) //分钟 + dateArray[5] = toInt(c) //秒 + if (d) { //毫秒 + dateArray[6] = Math.round(parseFloat("0." + d) * 1000) + } + return "" + }) + var tzHour = 0 + var tzMin = 0 + trimDate = trimDate.replace(/Z|([+-])(\d\d):?(\d\d)/, function(z, symbol, c, d) { + dateSetter = oDate.setUTCFullYear + timeSetter = oDate.setUTCHours + if (symbol) { + tzHour = toInt(symbol + c) + tzMin = toInt(symbol + d) + } + return "" + }) + + dateArray[3] -= tzHour + dateArray[4] -= tzMin + dateSetter.apply(oDate, dateArray.slice(0, 3)) + timeSetter.apply(oDate, dateArray.slice(3)) + date = oDate + } + } + if (typeof date === "number") { + date = new Date(date) + } + if (avalon.type(date) !== "date") { + return + } + while (format) { + match = rdateFormat.exec(format) + if (match) { + parts = parts.concat(match.slice(1)) + format = parts.pop() + } else { + parts.push(format) + format = null + } + } + parts.forEach(function(value) { + fn = DATE_FORMATS[value] + text += fn ? fn(date, locate) : value.replace(/(^'|'$)/g, "").replace(/''/g, "'") + }) + return text + } + var locate = { + AMPMS: { + 0: "上午", + 1: "下午" + }, + DAY: { + 0: "星期日", + 1: "星期一", + 2: "星期二", + 3: "星期三", + 4: "星期四", + 5: "星期五", + 6: "星期六" + }, + MONTH: { + 0: "1月", + 1: "2月", + 2: "3月", + 3: "4月", + 4: "5月", + 5: "6月", + 6: "7月", + 7: "8月", + 8: "9月", + 9: "10月", + 10: "11月", + 11: "12月" + }, + SHORTDAY: { + "0": "周日", + "1": "周一", + "2": "周二", + "3": "周三", + "4": "周四", + "5": "周五", + "6": "周六" + }, + fullDate: "y年M月d日EEEE", + longDate: "y年M月d日", + medium: "yyyy-M-d H:mm:ss", + mediumDate: "yyyy-M-d", + mediumTime: "H:mm:ss", + "short": "yy-M-d ah:mm", + shortDate: "yy-M-d", + shortTime: "ah:mm" + } + locate.SHORTMONTH = locate.MONTH + filters.date.locate = locate + }// jshint ignore:line + /********************************************************************* + * AMD加载器 * + **********************************************************************/ +//https://www.devbridge.com/articles/understanding-amd-requirejs/ +//http://maxogden.com/nested-dependencies.html + /*var modules = avalon.modules = { + "domReady!": { + exports: avalon, + state: 3 + }, + "avalon": { + exports: avalon, + state: 4 + } + } +//Object(modules[id]).state拥有如下值 +// undefined 没有定义 +// 1(send) 已经发出请求 +// 2(loading) 已经被执行但还没有执行完成,在这个阶段define方法会被执行 +// 3(loaded) 执行完毕,通过onload/onreadystatechange回调判定,在这个阶段checkDeps方法会执行 +// 4(execute) 其依赖也执行完毕, 值放到exports对象上,在这个阶段fireFactory方法会执行 + modules.exports = modules.avalon + + new function () {// jshint ignore:line + var loadings = [] //正在加载中的模块列表 + var factorys = [] //放置define方法的factory函数 + var rjsext = /\.js$/i + function makeRequest(name, config) { +//1. 去掉资源前缀 + var res = "js" + name = name.replace(/^(\w+)\!/, function (a, b) { + res = b + return "" + }) + if (res === "ready") { + log("debug: ready!已经被废弃,请使用domReady!") + res = "domReady" + } +//2. 去掉querystring, hash + var query = "" + name = name.replace(rquery, function (a) { + query = a + return "" + }) + //3. 去掉扩展名 + var suffix = "." + res + var ext = /js|css/.test(suffix) ? suffix : "" + name = name.replace(/\.[a-z0-9]+$/g, function (a) { + if (a === suffix) { + ext = a + return "" + } else { + return a + } + }) + var req = avalon.mix({ + query: query, + ext: ext, + res: res, + name: name, + toUrl: toUrl + }, config) + req.toUrl(name) + return req + } + + function fireRequest(req) { + var name = req.name + var res = req.res + //1. 如果该模块已经发出请求,直接返回 + var module = modules[name] + var urlNoQuery = name && req.urlNoQuery + if (module && module.state >= 1) { + return name + } + module = modules[urlNoQuery] + if (module && module.state >= 3) { + innerRequire(module.deps || [], module.factory, urlNoQuery) + return urlNoQuery + } + if (name && !module) { + module = modules[urlNoQuery] = { + id: urlNoQuery, + state: 1 //send + } + var wrap = function (obj) { + resources[res] = obj + obj.load(name, req, function (a) { + if (arguments.length && a !== void 0) { + module.exports = a + } + module.state = 4 + checkDeps() + }) + } + + if (!resources[res]) { + innerRequire([res], wrap) + } else { + wrap(resources[res]) + } + } + return name ? urlNoQuery : res + "!" + } + +//核心API之一 require + var requireQueue = [] + var isUserFirstRequire = false + innerRequire = avalon.require = function (array, factory, parentUrl, defineConfig) { + if (!isUserFirstRequire) { + requireQueue.push(avalon.slice(arguments)) + if (arguments.length <= 2) { + isUserFirstRequire = true + var queue = requireQueue.splice(0, requireQueue.length), args + while (args = queue.shift()) { + innerRequire.apply(null, args) + } + } + return + } + + if (!Array.isArray(array)) { + avalon.error("require方法的第一个参数应为数组 " + array) + } + var deps = [] // 放置所有依赖项的完整路径 + var uniq = {} + var id = parentUrl || "callback" + setTimeout("1")// jshint ignore:line + defineConfig = defineConfig || {} + defineConfig.baseUrl = kernel.baseUrl + var isBuilt = !!defineConfig.built + if (parentUrl) { + defineConfig.parentUrl = parentUrl.substr(0, parentUrl.lastIndexOf("/")) + defineConfig.mapUrl = parentUrl.replace(rjsext, "") + } + if (isBuilt) { + var req = makeRequest(defineConfig.defineName, defineConfig) + id = req.urlNoQuery + } else { + array.forEach(function (name) { + var req = makeRequest(name, defineConfig) + var url = fireRequest(req) //加载资源,并返回该资源的完整地址 + if (url) { + if (!uniq[url]) { + deps.push(url) + uniq[url] = "司徒正美" //去重 + } + } + }) + } + + var module = modules[id] + if (!module || module.state !== 4) { + modules[id] = { + id: id, + deps: isBuilt ? array.concat() : deps, + factory: factory || noop, + state: 3 + } + } + if (!module) { + //如果此模块是定义在另一个JS文件中, 那必须等该文件加载完毕, 才能放到检测列队中 + loadings.push(id) + } + checkDeps() + } + +//核心API之二 require + innerRequire.define = function (name, deps, factory) { //模块名,依赖列表,模块本身 + if (typeof name !== "string") { + factory = deps + deps = name + name = "anonymous" + } + if (!Array.isArray(deps)) { + factory = deps + deps = [] + } + var config = { + built: !isUserFirstRequire, //用r.js打包后,所有define会放到requirejs之前 + defineName: name + } + var args = [deps, factory, config] + factory.require = function (url) { + args.splice(2, 0, url) + if (modules[url]) { + modules[url].state = 3 //loaded + var isCycle = false + try { + isCycle = checkCycle(modules[url].deps, url) + } catch (e) { + } + if (isCycle) { + avalon.error(url + "模块与之前的模块存在循环依赖,请不要直接用script标签引入" + url + "模块") + } + } + delete factory.require //释放内存 + innerRequire.apply(null, args) //0,1,2 --> 1,2,0 + } +//根据标准,所有遵循W3C标准的浏览器,script标签会按标签的出现顺序执行。 +//老的浏览器中,加载也是按顺序的:一个文件下载完成后,才开始下载下一个文件。 +//较新的浏览器中(IE8+ 、FireFox3.5+ 、Chrome4+ 、Safari4+),为了减小请求时间以优化体验, +//下载可以是并行的,但是执行顺序还是按照标签出现的顺序。 +//但如果script标签是动态插入的, 就未必按照先请求先执行的原则了,目测只有firefox遵守 +//唯一比较一致的是,IE10+及其他标准浏览器,一旦开始解析脚本, 就会一直堵在那里,直接脚本解析完毕 +//亦即,先进入loading阶段的script标签(模块)必然会先进入loaded阶段 + var url = config.built ? "unknown" : getCurrentScript() + if (url) { + var module = modules[url] + if (module) { + module.state = 2 + } + factory.require(url) + } else {//合并前后的safari,合并后的IE6-9走此分支 + factorys.push(factory) + } + } +//核心API之三 require.config(settings) + innerRequire.config = kernel + //核心API之四 define.amd 标识其符合AMD规范 + innerRequire.define.amd = modules + + //==========================对用户配置项进行再加工========================== + var allpaths = kernel["orig.paths"] = {} + var allmaps = kernel["orig.map"] = {} + var allpackages = kernel["packages"] = [] + var allargs = kernel["orig.args"] = {} + avalon.mix(plugins, { + paths: function (hash) { + avalon.mix(allpaths, hash) + kernel.paths = makeIndexArray(allpaths) + }, + map: function (hash) { + avalon.mix(allmaps, hash) + var list = makeIndexArray(allmaps, 1, 1) + avalon.each(list, function (_, item) { + item.val = makeIndexArray(item.val) + }) + kernel.map = list + }, + packages: function (array) { + array = array.concat(allpackages) + var uniq = {} + var ret = [] + for (var i = 0, pkg; pkg = array[i++]; ) { + pkg = typeof pkg === "string" ? {name: pkg} : pkg + var name = pkg.name + if (!uniq[name]) { + var url = joinPath(pkg.location || name, pkg.main || "main") + url = url.replace(rjsext, "") + ret.push(pkg) + uniq[name] = pkg.location = url + pkg.reg = makeMatcher(name) + } + } + kernel.packages = ret.sort() + }, + urlArgs: function (hash) { + if (typeof hash === "string") { + hash = {"*": hash} + } + avalon.mix(allargs, hash) + kernel.urlArgs = makeIndexArray(allargs, 1) + }, + baseUrl: function (url) { + if (!isAbsUrl(url)) { + var baseElement = head.getElementsByTagName("base")[0] + if (baseElement) { + head.removeChild(baseElement) + } + var node = DOC.createElement("a") + node.href = url + url = getFullUrl(node, "href") + if (baseElement) { + head.insertBefore(baseElement, head.firstChild) + } + } + if (url.length > 3) + kernel.baseUrl = url + }, + shim: function (obj) { + for (var i in obj) { + var value = obj[i] + if (Array.isArray(value)) { + value = obj[i] = { + deps: value + } + } + if (!value.exportsFn && (value.exports || value.init)) { + value.exportsFn = makeExports(value) + } + } + kernel.shim = obj + } + + }) + + + //==============================内部方法================================= + function checkCycle(deps, nick) { + //检测是否存在循环依赖 + for (var i = 0, id; id = deps[i++]; ) { + if (modules[id].state !== 4 && + (id === nick || checkCycle(modules[id].deps, nick))) { + return true + } + } + } + + function checkFail(node, onError, fuckIE) { + var id = trimQuery(node.src) //检测是否死链 + node.onload = node.onreadystatechange = node.onerror = null + if (onError || (fuckIE && modules[id] && !modules[id].state)) { + setTimeout(function () { + head.removeChild(node) + node = null // 处理旧式IE下的循环引用问题 + }) + log("debug: 加载 " + id + " 失败" + onError + " " + (!modules[id].state)) + } else { + return true + } + } + + function checkDeps() { + //检测此JS模块的依赖是否都已安装完毕,是则安装自身 + loop: for (var i = loadings.length, id; id = loadings[--i]; ) { + var obj = modules[id], + deps = obj.deps + if (!deps) + continue + for (var j = 0, key; key = deps[j]; j++) { + if (Object(modules[key]).state !== 4) { + continue loop + } + } + //如果deps是空对象或者其依赖的模块的状态都是2 + if (obj.state !== 4) { + loadings.splice(i, 1) //必须先移除再安装,防止在IE下DOM树建完后手动刷新页面,会多次执行它 + fireFactory(obj.id, obj.deps, obj.factory) + checkDeps() //如果成功,则再执行一次,以防有些模块就差本模块没有安装好 + } + } + } + + var rreadyState = /complete|loaded/ + function loadJS(url, id, callback) { + //通过script节点加载目标模块 + var node = DOC.createElement("script") + node.className = subscribers //让getCurrentScript只处理类名为subscribers的script节点 + var supportLoad = "onload" in node + var onEvent = supportLoad ? "onload" : "onreadystatechange" + function onload() { + var factory = factorys.pop() + factory && factory.require(id) + if (callback) { + callback() + } + if (checkFail(node, false, !supportLoad)) { + log("debug: 已成功加载 " + url) + id && loadings.push(id) + checkDeps() + } + } + var index = 0, loadID + node[onEvent] = supportLoad ? onload : function () { + if (rreadyState.test(node.readyState)) { + ++index + if (index === 1) { + loadID = setTimeout(onload, 500) + } else { + clearTimeout(loadID) + onload() + } + } + } + node.onerror = function () { + checkFail(node, true) + } + + head.insertBefore(node, head.firstChild) //chrome下第二个参数不能为null + node.src = url //插入到head的第一个节点前,防止IE6下head标签没闭合前使用appendChild抛错 + log("debug: 正准备加载 " + url) //更重要的是IE6下可以收窄getCurrentScript的寻找范围 + } + + var resources = innerRequire.plugins = { + //三大常用资源插件 js!, css!, text!, ready! + ready: { + load: noop + }, + js: { + load: function (name, req, onLoad) { + var url = req.url + var id = req.urlNoQuery + var shim = kernel.shim[name.replace(rjsext, "")] + if (shim) { //shim机制 + innerRequire(shim.deps || [], function () { + var args = avalon.slice(arguments) + loadJS(url, id, function () { + onLoad(shim.exportsFn ? shim.exportsFn.apply(0, args) : void 0) + }) + }) + } else { + loadJS(url, id) + } + } + }, + css: { + load: function (name, req, onLoad) { + var url = req.url + var node = DOC.createElement("link") + node.rel = "stylesheet" + node.href = url + head.insertBefore(node, head.firstChild) + log("debug: 已成功加载 " + url) + onLoad() + } + }, + text: { + load: function (name, req, onLoad) { + var url = req.url + var xhr = getXHR() + xhr.onreadystatechange = function () { + if (xhr.readyState === 4) { + var status = xhr.status; + if (status > 399 && status < 600) { + avalon.error(url + " 对应资源不存在或没有开启 CORS") + } else { + log("debug: 已成功加载 " + url) + onLoad(xhr.responseText) + } + } + } + var time = "_=" + (new Date() - 0) + var _url = url.indexOf("?") === -1 ? url + "?" + time : url + "&" + time + xhr.open("GET", _url, true) + if ("withCredentials" in xhr) {//这是处理跨域 + xhr.withCredentials = true + } + xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest")//告诉后端这是AJAX请求 + xhr.send() + log("debug: 正准备加载 " + url) + } + } + } + innerRequire.checkDeps = checkDeps + + var rquery = /(\?[^#]*)$/ + function trimQuery(url) { + return (url || "").replace(rquery, "") + } + + function isAbsUrl(path) { + //http://stackoverflow.com/questions/10687099/how-to-test-if-a-url-string-is-absolute-or-relative + return /^(?:[a-z]+:)?\/\//i.test(String(path)) + } + + function getFullUrl(node, src) { + return"1"[0] ? node[src] : node.getAttribute(src, 4) + } + + function getCurrentScript() { + // inspireb by https://github.com/samyk/jiagra/blob/master/jiagra.js + var stack + try { + a.b.c() //强制报错,以便捕获e.stack + } catch (e) { //safari5的sourceURL,firefox的fileName,它们的效果与e.stack不一样 + stack = e.stack + if (!stack && window.opera) { + //opera 9没有e.stack,但有e.Backtrace,但不能直接取得,需要对e对象转字符串进行抽取 + stack = (String(e).match(/of linked script \S+/g) || []).join(" ") + } + } + if (stack) { + /!**e.stack最后一行在所有支持的浏览器大致如下: + *chrome23: + * at http://113.93.50.63/data.js:4:1 + *firefox17: + *@http://113.93.50.63/query.js:4 + *opera12:http://www.oldapps.com/opera.php?system=Windows_XP + *@http://113.93.50.63/data.js:4 + *IE10: + * at Global code (http://113.93.50.63/data.js:4:1) + * //firefox4+ 可以用document.currentScript + *!/ + stack = stack.split(/[@ ]/g).pop() //取得最后一行,最后一个空格或@之后的部分 + stack = stack[0] === "(" ? stack.slice(1, -1) : stack.replace(/\s/, "") //去掉换行符 + return trimQuery(stack.replace(/(:\d+)?:\d+$/i, "")) //去掉行号与或许存在的出错字符起始位置 + } + var nodes = head.getElementsByTagName("script") //只在head标签中寻找 + for (var i = nodes.length, node; node = nodes[--i]; ) { + if (node.className === subscribers && node.readyState === "interactive") { + var url = getFullUrl(node, "src") + return node.className = trimQuery(url) + } + } + } + + var rcallback = /^callback\d+$/ + function fireFactory(id, deps, factory) { + var module = Object(modules[id]) + module.state = 4 + for (var i = 0, array = [], d; d = deps[i++]; ) { + if (d === "exports") { + var obj = module.exports || (module.exports = {}) + array.push(obj) + } else { + array.push(modules[d].exports) + } + } + try { + var ret = factory.apply(window, array) + } catch (e) { + log("执行[" + id + "]模块的factory抛错: " + e) + } + if (ret !== void 0) { + module.exports = ret + } + if (rcallback.test(id)) { + delete modules[id] + } + delete module.factory + return ret + } + function toUrl(id) { + if (id.indexOf(this.res + "!") === 0) { + id = id.slice(this.res.length + 1) //处理define("css!style",[], function(){})的情况 + } + var url = id + //1. 是否命中paths配置项 + var usePath = 0 + var baseUrl = this.baseUrl + var rootUrl = this.parentUrl || baseUrl + eachIndexArray(id, kernel.paths, function (value, key) { + url = url.replace(key, value) + usePath = 1 + }) + //2. 是否命中packages配置项 + if (!usePath) { + eachIndexArray(id, kernel.packages, function (value, key, item) { + url = url.replace(item.name, item.location) + }) + } + //3. 是否命中map配置项 + if (this.mapUrl) { + eachIndexArray(this.mapUrl, kernel.map, function (array) { + eachIndexArray(url, array, function (mdValue, mdKey) { + url = url.replace(mdKey, mdValue) + rootUrl = baseUrl + }) + }) + } + var ext = this.ext + if (ext && usePath && url.slice(-ext.length) === ext) { + url = url.slice(0, -ext.length) + } + //4. 转换为绝对路径 + if (!isAbsUrl(url)) { + rootUrl = this.built || /^\w/.test(url) ? baseUrl : rootUrl + url = joinPath(rootUrl, url) + } + //5. 还原扩展名,query + var urlNoQuery = url + ext + url = urlNoQuery + this.query + //6. 处理urlArgs + eachIndexArray(id, kernel.urlArgs, function (value) { + url += (url.indexOf("?") === -1 ? "?" : "&") + value; + }) + this.url = url + return this.urlNoQuery = urlNoQuery + } + + function makeIndexArray(hash, useStar, part) { + //创建一个经过特殊算法排好序的数组 + var index = hash2array(hash, useStar, part) + index.sort(descSorterByName) + return index + } + + function makeMatcher(prefix) { + return new RegExp('^' + prefix + '(/|$)') + } + + function makeExports(value) { + return function () { + var ret + if (value.init) { + ret = value.init.apply(window, arguments) + } + return ret || (value.exports && getGlobal(value.exports)) + } + } + + + function hash2array(hash, useStar, part) { + var array = []; + for (var key in hash) { + if (ohasOwn.call(hash, key)) { + var item = { + name: key, + val: hash[key] + } + array.push(item) + item.reg = key === "*" && useStar ? /^/ : makeMatcher(key) + if (part && key !== "*") { + item.reg = new RegExp('\/' + key.replace(/^\//, "") + '(/|$)') + } + } + } + return array + } + + function eachIndexArray(moduleID, array, matcher) { + array = array || [] + for (var i = 0, el; el = array[i++]; ) { + if (el.reg.test(moduleID)) { + matcher(el.val, el.name, el) + return false + } + } + } + // 根据元素的name项进行数组字符数逆序的排序函数 + function descSorterByName(a, b) { + var aaa = a.name + var bbb = b.name + if (bbb === "*") { + return -1 + } + if (aaa === "*") { + return 1 + } + return bbb.length - aaa.length + } + + var rdeuce = /\/\w+\/\.\./ + function joinPath(a, b) { + if (a.charAt(a.length - 1) !== "/") { + a += "/" + } + if (b.slice(0, 2) === "./") { //相对于兄弟路径 + return a + b.slice(2) + } + if (b.slice(0, 2) === "..") { //相对于父路径 + a += b + while (rdeuce.test(a)) { + a = a.replace(rdeuce, "") + } + return a + } + if (b.slice(0, 1) === "/") { + return a + b.slice(1) + } + return a + b + } + + function getGlobal(value) { + if (!value) { + return value + } + var g = window + value.split(".").forEach(function (part) { + g = g[part] + }) + return g + } + + var mainNode = DOC.scripts[DOC.scripts.length - 1] + var dataMain = mainNode.getAttribute("data-main") + if (dataMain) { + plugins.baseUrl(dataMain) + var href = kernel.baseUrl + kernel.baseUrl = href.slice(0, href.lastIndexOf("/") + 1) + loadJS(href.replace(rjsext, "") + ".js") + } else { + var loaderUrl = trimQuery(getFullUrl(mainNode, "src")) + kernel.baseUrl = loaderUrl.slice(0, loaderUrl.lastIndexOf("/") + 1) + } + }// jshint ignore:line*/ + + /********************************************************************* + * DOMReady * + **********************************************************************/ + + var readyList = [], isReady + var fireReady = function(fn) { + isReady = true + if (innerRequire) { + modules["domReady!"].state = 4 + innerRequire.checkDeps() + } + while(fn = readyList.shift()){ + fn(avalon) + } + } + + function doScrollCheck() { + try { //IE下通过doScrollCheck检测DOM树是否建完 + root.doScroll("left") + fireReady() + } catch (e) { + setTimeout(doScrollCheck) + } + } + + if (DOC.readyState === "complete") { + setTimeout(fireReady) //如果在domReady之外加载 + } else if (W3C) { + DOC.addEventListener("DOMContentLoaded", fireReady) + } else { + DOC.attachEvent("onreadystatechange", function() { + if (DOC.readyState === "complete") { + fireReady() + } + }) + try { + var isTop = window.frameElement === null + } catch (e) { + } + if (root.doScroll && isTop && window.external) {//fix IE iframe BUG + doScrollCheck() + } + } + avalon.bind(window, "load", fireReady) + + avalon.ready = function(fn) { + if (!isReady) { + readyList.push(fn) + } else { + fn(avalon) + } + } + + avalon.config({ + loader: false + }) + + avalon.ready(function() { + avalon.scan(DOC.body) + }) + +// Register as a named AMD module, since avalon can be concatenated with other +// files that may use define, but not via a proper concatenation script that +// understands anonymous AMD modules. A named AMD is safest and most robust +// way to register. Lowercase avalon is used because AMD module names are +// derived from file names, and Avalon is normally delivered in a lowercase +// file name. Do this after creating the global so that if an AMD module wants +// to call noConflict to hide this version of avalon, it will work. + +// Note that for maximum portability, libraries that are not avalon should +// declare themselves as anonymous modules, and avoid setting a global if an +// AMD loader is present. avalon is a special case. For more information, see +// https://github.com/jrburke/requirejs/wiki/Updating-existing-libraries#wiki-anon + if (typeof define === "function" && define.amd) { + define("avalon", [], function() { + return avalon + }) + } +// Map over avalon in case of overwrite + var _avalon = window.avalon + avalon.noConflict = function(deep) { + if (deep && window.avalon === avalon) { + window.avalon = _avalon + } + return avalon + } +// Expose avalon identifiers, even in AMD +// and CommonJS for browser emulators + if (noGlobal === void 0) { + window.avalon = avalon + } + return avalon + +})); \ No newline at end of file diff --git a/template/src/admin/admin.html b/template/src/admin/admin.html index bde4fb9..37493d7 100644 --- a/template/src/admin/admin.html +++ b/template/src/admin/admin.html @@ -69,8 +69,8 @@ diff --git a/template/src/admin/user/user_list.html b/template/src/admin/user/user_list.html index 8e8c0f4..8227997 100644 --- a/template/src/admin/user/user_list.html +++ b/template/src/admin/user/user_list.html @@ -64,7 +64,7 @@
-
From b69be7eed5918464481abd251e9ef2eb281eb705 Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Fri, 16 Oct 2015 20:45:01 +0800 Subject: [PATCH 05/25] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=8D=87=E7=BA=A7=20an?= =?UTF-8?q?nouncement=20=E7=9A=84=20migration?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../migrations/0004_auto_20151015_1555.py | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 announcement/migrations/0004_auto_20151015_1555.py diff --git a/announcement/migrations/0004_auto_20151015_1555.py b/announcement/migrations/0004_auto_20151015_1555.py new file mode 100644 index 0000000..bb8e6e1 --- /dev/null +++ b/announcement/migrations/0004_auto_20151015_1555.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('announcement', '0003_auto_20150922_1703'), + ] + + operations = [ + migrations.RemoveField( + model_name='announcement', + name='groups', + ), + migrations.RemoveField( + model_name='announcement', + name='is_global', + ), + ] From 3a2f92e896bd39122430ed1cbe4bad7b78548d28 Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Fri, 16 Oct 2015 20:45:41 +0800 Subject: [PATCH 06/25] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=20contest=20submission?= =?UTF-8?q?=20=E8=AE=A1=E6=95=B0=E5=99=A8=20bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- contest_submission/views.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/contest_submission/views.py b/contest_submission/views.py index d47302f..2f037de 100644 --- a/contest_submission/views.py +++ b/contest_submission/views.py @@ -37,9 +37,6 @@ class ContestSubmissionAPIView(APIView): contest = Contest.objects.get(id=data["contest_id"]) try: problem = ContestProblem.objects.get(contest=contest, id=data["problem_id"]) - # 更新题目提交计数器 - problem.total_submit_number += 1 - problem.save() except ContestProblem.DoesNotExist: return error_response(u"题目不存在") submission = Submission.objects.create(user_id=request.user.id, language=int(data["language"]), From 9f486d4a6f511cc365b706fed0bfa20a958a1cef Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Fri, 16 Oct 2015 20:49:01 +0800 Subject: [PATCH 07/25] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=89=BE=E5=9B=9E?= =?UTF-8?q?=E5=AF=86=E7=A0=81=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- account/views.py | 58 ++++++++++++++++++++++++++++++++++++++---------- mail/tasks.py | 27 +++++++++++++--------- 2 files changed, 62 insertions(+), 23 deletions(-) diff --git a/account/views.py b/account/views.py index 03ff442..b50e0a3 100644 --- a/account/views.py +++ b/account/views.py @@ -6,21 +6,21 @@ from django.shortcuts import render from django.db.models import Q from django.conf import settings from django.core.exceptions import MultipleObjectsReturned +from django.utils.timezone import now from rest_framework.views import APIView from rest_framework.response import Response -from utils.shortcuts import serializer_invalid_response, error_response, success_response, paginate, rand_str +from utils.shortcuts import (serializer_invalid_response, error_response, + success_response, error_page, paginate, rand_str) from utils.captcha import Captcha from mail.tasks import send_email -from envelopes import Envelope - from .decorators import login_required from .models import User from .serializers import (UserLoginSerializer, UsernameCheckSerializer, UserRegisterSerializer, UserChangePasswordSerializer, EmailCheckSerializer, UserSerializer, EditUserSerializer, - ApplyResetPasswordSerializer) + ApplyResetPasswordSerializer, ResetPasswordSerializer) from .decorators import super_admin_required @@ -34,13 +34,9 @@ class UserLoginAPIView(APIView): serializer = UserLoginSerializer(data=request.data) if serializer.is_valid(): data = serializer.data - - if "captcha" not in data: - return error_response(u"请填写验证码!") captcha = Captcha(request) if not captcha.check(data["captcha"]): return error_response(u"验证码错误") - user = auth.authenticate(username=data["username"], password=data["password"]) # 用户名或密码错误的话 返回None if user: @@ -244,21 +240,59 @@ class ApplyResetPasswordAPIView(APIView): user = User.objects.get(username=data["username"], email=data["email"]) except User.DoesNotExist: return error_response(u"用户不存在") + if user.reset_password_token_create_time and (now() - user.reset_password_token_create_time).total_seconds() < 20 * 60: + return error_response(u"20分钟内只能找回一次密码") user.reset_password_token = rand_str() + user.reset_password_token_create_time = now() user.save() email_template = codecs.open(settings.TEMPLATES[0]["DIRS"][0] + "utils/reset_password_email.html", "r", "utf-8").read() - email_template = email_template.replace("{{ username }}", user.username).replace("{{ link }}", request.scheme + "://" + request.META['HTTP_HOST'] + "/reset_password/?token=" + user.reset_password_token) + email_template = email_template.replace("{{ username }}", user.username).\ + replace("{{ website_name }}", settings.WEBSITE_INFO["website_name"]).\ + replace("{{ link }}", request.scheme + "://" + request.META['HTTP_HOST'] + "/reset_password/?token=" + user.reset_password_token) - send_email(user.email, user.username, u"qduoj 密码找回邮件", email_template) - return success_response(u"邮件发生成功") + send_email(settings.WEBSITE_INFO["website_name"], + user.email, + user.username, + settings.WEBSITE_INFO["website_name"] + u" 密码找回邮件", + email_template) + return success_response(u"邮件发送成功") else: return serializer_invalid_response(serializer) class ResetPasswordAPIView(APIView): - pass + def post(self, request): + serializer = ResetPasswordSerializer(data=request.data) + if serializer.is_valid(): + data = serializer.data + captcha = Captcha(request) + if not captcha.check(data["captcha"]): + return error_response(u"验证码错误") + try: + user = User.objects.get(reset_password_token=data["token"]) + except User.DoesNotExist: + return error_response(u"token 不存在") + if (now() - user.reset_password_token_create_time).total_seconds() > 30 * 60: + return error_response(u"token 已经过期,请在30分钟内重置密码") + user.reset_password_token = None + user.set_password(data["password"]) + user.save() + return success_response(u"密码重置成功") + else: + return serializer_invalid_response(serializer) def user_index_page(request, username): return render(request, "oj/account/user_index.html") + + +def auth_page(request): + if not request.user.is_authenticated(): + return render(request, "oj/account/oauth.html") + callback = request.GET.get("callback", None) + if not callback: + return error_page(request, u"参数错误") + token = rand_str() + request.user.auth_token = token + return render(request, "oj/account/oauth.html", {"callback": callback, "token": token}) diff --git a/mail/tasks.py b/mail/tasks.py index c314548..490472b 100644 --- a/mail/tasks.py +++ b/mail/tasks.py @@ -1,14 +1,19 @@ # coding=utf-8 +import os +from envelopes import Envelope + +SMTP_CONFIG = {"smtp_server": "smtp.mxhichina.com", + "email": "noreply@qduoj.com", + "password": os.environ.get("smtp_password", "111111"), + "tls": False} -def send_email(*args, **kwargs): - pass - - -''' -envelope = Envelope(from_addr=("noreply@qduoj.com", u"qduoj 密码找回邮件", email_template), - to_addr=(user.email, user.username), - subject=u"qduoj 密码找回邮件", - html_body=email_template) -envelope.send("smtp.mxhichina.com", login="noreply@qduoj.com", password="092122302Zarpe2015", tls=False) -''' \ No newline at end of file +def send_email(from_name, to_email, to_name, subject, content): + envelope = Envelope(from_addr=(SMTP_CONFIG["email"], from_name), + to_addr=(to_email, to_name), + subject=subject, + html_body=content) + envelope.send(SMTP_CONFIG["smtp_server"], + login=SMTP_CONFIG["email"], + password=SMTP_CONFIG["password"], + tls=SMTP_CONFIG["tls"]) From c0b48c219f7ee7aeb636430b843b04079f4944cc Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Fri, 16 Oct 2015 21:34:50 +0800 Subject: [PATCH 08/25] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=20pager=20=E7=9A=84?= =?UTF-8?q?=E6=89=93=E5=8C=85=E9=80=89=E9=A1=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- static/src/js/build.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/static/src/js/build.js b/static/src/js/build.js index ba08615..1236881 100644 --- a/static/src/js/build.js +++ b/static/src/js/build.js @@ -23,6 +23,9 @@ validator: "lib/validator/validator", ZeroClipboard: "lib/ZeroClipboard/ZeroClipboard", + // ------ admin web 组件 ---------- + pager: "components/pager", + // ------ 下面写的都不要直接用,而是使用上面的封装版本 ------ //富文本编辑器simditor -> editor simditor: "lib/simditor/simditor", @@ -69,6 +72,11 @@ group_20_pack: "app/admin/group/group", submissionList_21_pack: "app/admin/contest/submissionList" }, + shim: { + avalon: { + exports: "avalon" + } + }, findNestedDependencies: true, appDir: "../", dir: "../../release/", From e8ffa09ec4de8301ed54af5960f248ef235d1c82 Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Fri, 16 Oct 2015 21:35:18 +0800 Subject: [PATCH 09/25] =?UTF-8?q?=E9=87=8D=E6=9E=84=E7=BC=96=E8=BE=91?= =?UTF-8?q?=E7=94=A8=E6=88=B7=E7=95=8C=E9=9D=A2=E5=92=8C=20js?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- static/src/js/app/admin/user/userList.js | 223 ++++++++++------------- template/src/admin/user/user_list.html | 16 +- 2 files changed, 104 insertions(+), 135 deletions(-) diff --git a/static/src/js/app/admin/user/userList.js b/static/src/js/app/admin/user/userList.js index d2949f0..f468b55 100644 --- a/static/src/js/app/admin/user/userList.js +++ b/static/src/js/app/admin/user/userList.js @@ -1,133 +1,106 @@ -require(["jquery", "avalon", "csrfToken", "bsAlert", "validator"], function ($, avalon, csrfTokenHeader, bsAlert) { +require(["jquery", "avalon", "csrfToken", "bsAlert", "pager", "validator"], + function ($, avalon, csrfTokenHeader, bsAlert) { + avalon.ready(function () { + if (avalon.vmodels.userList) { + var vm = avalon.vmodels.userList; + } + else { + var vm = avalon.define({ + $id: "userList", + userList: [], + userType: ["一般用户", "管理员", "超级管理员"], - // avalon:定义模式 userList - avalon.ready(function () { + keyword: "", + showAdminOnly: false, + isEditing: false, - if (avalon.vmodels.userList) { - var vm = avalon.vmodels.userList; - // initialize avalon object - userList = []; //previousPage= 0; nextPage= 0; page = 1; - //editingUserId= 0; totalPage = 1; keyword= ""; showAdminOnly= false; - //user editor fields - username= ""; realName= ""; email= ""; adminType= 0; id= 0; - } - else { - var vm = avalon.define({ - $id: "userList", - //通用变量 - userList: [], - previousPage: 0, - nextPage: 0, - page: 1, - editingUserId: 0, - totalPage: 1, - userType: ["一般用户", "管理员", "超级管理员"], - keyword: "", - showAdminOnly: false, - //编辑区域同步变量 - username: "", - realName: "", - email: "", - adminType: 0, - id: 0, - getNext: function () { - if (!vm.nextPage) - return; - getPageData(vm.page + 1); - }, - getPrevious: function () { - if (!vm.previousPage) - return; - getPageData(vm.page - 1); - }, - getBtnClass: function (btn) { //上一页/下一页按钮启用禁用逻辑 - if (btn == "next") { - return vm.nextPage ? "btn btn-primary" : "btn btn-primary disabled"; + username: "", + realName: "", + email: "", + adminType: 0, + userId: -1, + + pager: { + getPage: function (page) { + getPage(page); + } + }, + editUser: function (user) { + vm.username = user.username; + vm.realName = user.real_name; + vm.adminType = user.admin_type; + vm.email = user.email; + vm.userId = user.id; + + vm.isEditing = true; + }, + search: function () { + getPage(1); + avalon.vmodels.userPager.currentPage = 1; } - else { - return vm.previousPage ? "btn btn-primary" : "btn btn-primary disabled"; - } - }, - editUser: function (user) { //点击编辑按钮的事件,显示/隐藏编辑区 - vm.username = user.username; - vm.realName = user.real_name; - vm.adminType = user.admin_type; - vm.email = user.email; - vm.id = user.id; - if (vm.editingUserId == user.id) - vm.editingUserId = 0; - else - vm.editingUserId = user.id; - }, - search: function () { - getPageData(1); - } + }); + } + vm.$watch("showAdminOnly", function () { + getPage(1); + avalon.vmodels.userPager.currentPage = 1; }); - } - vm.$watch("showAdminOnly", function () { - getPageData(1); + + function getPage(page) { + var url = "/api/admin/user/?paging=true&page=" + page + "&page_size=10"; + if (vm.showAdminOnly == true) + url += "&admin_type=1"; + if (vm.keyword != "") + url += "&keyword=" + vm.keyword; + $.ajax({ + beforeSend: csrfTokenHeader, + url: url, + dataType: "json", + method: "get", + success: function (data) { + if (!data.code) { + vm.userList = data.data.results; + avalon.vmodels.userPager.totalPage = data.data.total_page; + } + else { + bsAlert(data.data); + } + } + }); + } + + $("#edit-user-form").validator() + .on('submit', function (e) { + if (!e.isDefaultPrevented()) { + var data = { + username: vm.username, + real_name: vm.realName, + email: vm.email, + id: vm.userId, + admin_type: vm.adminType + }; + if ($("#password").val() !== "") + data.password = $("#password").val(); + $.ajax({ + url: "/api/admin/user/", + data: data, + dataType: "json", + method: "put", + success: function (data) { + if (!data.code) { + bsAlert("编辑成功!"); + getPage(1); + $("#password").val(""); + vm.isEditing = false; + } else { + bsAlert(data.data); + } + } + }); + return false; + } + }); }); avalon.scan(); - getPageData(1); //用户列表初始化 - //Ajax get数据 - function getPageData(page) { - var url = "/api/admin/user/?paging=true&page=" + page + "&page_size=10"; - if (vm.showAdminOnly == true) - url += "&admin_type=1"; - if (vm.keyword != "") - url += "&keyword=" + vm.keyword; - $.ajax({ - beforeSend: csrfTokenHeader, - url: url, - dataType: "json", - method: "get", - success: function (data) { - if (!data.code) { - vm.userList = data.data.results; - vm.totalPage = data.data.total_page; - vm.previousPage = data.data.previous_page; - vm.nextPage = data.data.next_page; - vm.page = page; - } - else { - bsAlert(data.data); - } - } - }); - } - $("#edit_user-form").validator() - .on('submit', function (e) { - if (!e.isDefaultPrevented()) { - var data = { - username: vm.username, - real_name: vm.realName, - email: vm.email, - id: vm.id, - admin_type: vm.adminType - }; - if ($("#password").val() !== "") - data.password = $("#password").val(); - $.ajax({ - beforeSend: csrfTokenHeader, - url: "/api/admin/user/", - data: data, - dataType: "json", - method: "put", - success: function (data) { - if (!data.code) { - bsAlert("编辑成功!"); - getPageData(1); - $("#password").val(""); - } else { - bsAlert(data.data); - } - } - }); - return false; - } - }); - }); - -}) \ No newline at end of file + }); \ No newline at end of file diff --git a/template/src/admin/user/user_list.html b/template/src/admin/user/user_list.html index 8227997..937696e 100644 --- a/template/src/admin/user/user_list.html +++ b/template/src/admin/user/user_list.html @@ -16,7 +16,6 @@ ID 用户名 注册时间 - 最近登陆 真实姓名 电子邮箱 用户类型 @@ -26,7 +25,6 @@ {{ el.id }} {{ el.username }} {{ el.create_time|date("yyyy-MM-dd HH:mm:ss")}} - {{ el.last_login|date("yyyy-MM-dd HH:mm:ss")}} {{ el.real_name }} {{ el.email }} {{ userType[el.admin_type]}} @@ -39,17 +37,15 @@
- 页数:{{ page }}/{{ totalPage }}   - - +
-
+

修改用户信息

- +
- +
-
@@ -80,7 +76,7 @@
- +
From 8a8f35468a08cb8de61b52ece7e5c0b06988903a Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Fri, 16 Oct 2015 21:35:27 +0800 Subject: [PATCH 10/25] fix typo --- .../js/app/admin/announcement/announcement.js | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/static/src/js/app/admin/announcement/announcement.js b/static/src/js/app/admin/announcement/announcement.js index 9b64a19..9b3054d 100644 --- a/static/src/js/app/admin/announcement/announcement.js +++ b/static/src/js/app/admin/announcement/announcement.js @@ -12,23 +12,25 @@ require(["jquery", "avalon", "csrfToken", "bsAlert", "editor", "validator", "pag var vm = avalon.define({ $id: "announcement", announcementList: [], + isEditing: false, + showVisibleOnly: false, + + //编辑器同步变量 + announcementId: -1, + newTitle: "", + announcementVisible: false, + pager: { getPage: function(page){ getPage(page); } }, - isEditing: false, - announcementId: -1, - showVisibleOnly: false, - newTitle: "", - announcementVisible: 0, - editAnnouncement: function (announcement) { vm.newTitle = announcement.title; vm.announcementId = announcement.id; editAnnouncementEditor.setValue(announcement.content); vm.announcementVisible = announcement.visible; - vm.isEditing = !vm.isEditing; + vm.isEditing = true; editAnnouncementEditor.focus(); }, cancelEdit: function () { @@ -76,7 +78,7 @@ require(["jquery", "avalon", "csrfToken", "bsAlert", "editor", "validator", "pag } function getPage(page) { - var url = "/api/admin/announcement/?paging=true&page=" + page + "&page_size=2"; + var url = "/api/admin/announcement/?paging=true&page=" + page + "&page_size=20"; if (vm.showVisibleOnly) url += "&visible=true"; $.ajax({ From 5f45c3204508479b79db1b1b5719c92b47f53b3b Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Fri, 16 Oct 2015 21:41:52 +0800 Subject: [PATCH 11/25] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E7=9B=91=E6=8E=A7?= =?UTF-8?q?=E5=9B=BE=E8=A1=A8=E6=97=A0=E6=B3=95=E6=B8=85=E7=A9=BA=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E7=9A=84=20bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- static/src/js/app/admin/monitor/monitor.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/static/src/js/app/admin/monitor/monitor.js b/static/src/js/app/admin/monitor/monitor.js index 0747618..1a0e46f 100644 --- a/static/src/js/app/admin/monitor/monitor.js +++ b/static/src/js/app/admin/monitor/monitor.js @@ -39,8 +39,8 @@ require(["jquery", "chart"], function ($, Chart) { $("#clear-chart-data").click(function(){ for(var i = 0;i < dataCounter;i++) { chart.removeData(); - dataCounter = 0; } + dataCounter = 0; }); var intervalId = setInterval(getMonitorData, 3000); From 489383c9206d0b99a0ff44e90e3dc89a9fbd18a8 Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Sat, 17 Oct 2015 12:22:05 +0800 Subject: [PATCH 12/25] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=20simditor=20=E7=9A=84?= =?UTF-8?q?=E8=87=AA=E5=8A=A8=E6=9D=BF=E5=AF=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/app/admin/announcement/announcement.js | 4 +- static/src/js/build.js | 1 + static/src/js/config.js | 1 + .../src/js/lib/simditor/simditor-autosave.js | 138 ++++++++++++++++++ static/src/js/utils/editor.js | 2 +- .../src/admin/announcement/announcement.html | 4 +- 6 files changed, 146 insertions(+), 4 deletions(-) create mode 100644 static/src/js/lib/simditor/simditor-autosave.js diff --git a/static/src/js/app/admin/announcement/announcement.js b/static/src/js/app/admin/announcement/announcement.js index 9b3054d..debb594 100644 --- a/static/src/js/app/admin/announcement/announcement.js +++ b/static/src/js/app/admin/announcement/announcement.js @@ -60,6 +60,7 @@ require(["jquery", "avalon", "csrfToken", "bsAlert", "editor", "validator", "pag if (!data.code) { bsAlert("修改成功"); vm.isEditing = false; + localStorage.removeItem("/admin/autosave/edit-announcement-editor/"); getPage(1); } else { @@ -97,7 +98,7 @@ require(["jquery", "avalon", "csrfToken", "bsAlert", "editor", "validator", "pag } //新建公告表单验证与数据提交 - $("#announcement-form").validator().on('submit', function (e) { + $("#announcement-form").validator().on('submit', function (e) { if (!e.isDefaultPrevented()) { var title = $("#title").val(); var content = createAnnouncementEditor.getValue(); @@ -119,6 +120,7 @@ require(["jquery", "avalon", "csrfToken", "bsAlert", "editor", "validator", "pag bsAlert("提交成功!"); $("#title").val(""); createAnnouncementEditor.setValue(""); + localStorage.removeItem("/admin/autosave/create-announcement-editor/"); getPage(1); } else { bsAlert(data.data); diff --git a/static/src/js/build.js b/static/src/js/build.js index 1236881..34a05e6 100644 --- a/static/src/js/build.js +++ b/static/src/js/build.js @@ -32,6 +32,7 @@ "simple-module": "lib/simditor/module", "simple-hotkeys": "lib/simditor/hotkeys", "simple-uploader": "lib/simditor/uploader", + "simditor-autosave": "lib/simditor/simditor-autosave", //code mirror 代码编辑器 ->codeMirror _codeMirror: "lib/codeMirror/codemirror", diff --git a/static/src/js/config.js b/static/src/js/config.js index 67d8c99..56b4b8a 100644 --- a/static/src/js/config.js +++ b/static/src/js/config.js @@ -34,6 +34,7 @@ var require = { "simple-module": "lib/simditor/module", "simple-hotkeys": "lib/simditor/hotkeys", "simple-uploader": "lib/simditor/uploader", + "simditor-autosave": "lib/simditor/simditor-autosave", //code mirror 代码编辑器 ->codeMirror _codeMirror: "lib/codeMirror/codemirror", diff --git a/static/src/js/lib/simditor/simditor-autosave.js b/static/src/js/lib/simditor/simditor-autosave.js new file mode 100644 index 0000000..23f10be --- /dev/null +++ b/static/src/js/lib/simditor/simditor-autosave.js @@ -0,0 +1,138 @@ +(function (root, factory) { + if (typeof define === 'function' && define.amd) { + // AMD. Register as an anonymous module unless amdModuleId is set + define('simditor-autosave', ["jquery","simple-module","simditor"], function (a0,b1,c2) { + return (root['SimditorAutosave'] = factory(a0,b1,c2)); + }); + } else if (typeof exports === 'object') { + // Node. Does not work with strict CommonJS, but + // only CommonJS-like environments that support module.exports, + // like Node. + module.exports = factory(require("jquery"),require("simple-module"),require("simditor")); + } else { + root['SimditorAutosave'] = factory(jQuery,SimpleModule,Simditor); + } +}(this, function ($, SimpleModule, Simditor) { + +var SimditorAutosave, + extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, + hasProp = {}.hasOwnProperty; + +SimditorAutosave = (function(superClass) { + extend(SimditorAutosave, superClass); + + function SimditorAutosave() { + return SimditorAutosave.__super__.constructor.apply(this, arguments); + } + + SimditorAutosave.pluginName = 'Autosave'; + + SimditorAutosave.prototype.opts = { + autosave: true, + autosavePath: null + }; + + SimditorAutosave.prototype._init = function() { + var currentVal, link, name, val; + this.editor = this._module; + if (!this.opts.autosave) { + return; + } + this.name = typeof this.opts.autosave === 'string' ? this.opts.autosave : 'simditor'; + if (this.opts.autosavePath) { + this.path = this.opts.autosavePath; + } else { + link = $("", { + href: location.href + }); + name = this.editor.textarea.data('autosave') || this.name; + this.path = "/" + (link[0].pathname.replace(/\/$/g, "").replace(/^\//g, "")) + "/autosave/" + name + "/"; + } + if (!this.path) { + return; + } + this.editor.on("valuechanged", (function(_this) { + return function() { + return _this.storage.set(_this.path, _this.editor.getValue()); + }; + })(this)); + this.editor.el.closest('form').on('ajax:success.simditor-' + this.editor.id, (function(_this) { + return function(e) { + return _this.storage.remove(_this.path); + }; + })(this)); + val = this.storage.get(this.path); + if (!val) { + return; + } + currentVal = this.editor.textarea.val(); + if (val === currentVal) { + return; + } + if (this.editor.textarea.is('[data-autosave-confirm]')) { + if (confirm(this.editor.textarea.data('autosave-confirm') || 'Are you sure to restore unsaved changes?')) { + return this.editor.setValue(val); + } else { + return this.storage.remove(this.path); + } + } else { + return this.editor.setValue(val); + } + }; + + SimditorAutosave.prototype.storage = { + supported: function() { + var error; + try { + localStorage.setItem('_storageSupported', 'yes'); + localStorage.removeItem('_storageSupported'); + return true; + } catch (_error) { + error = _error; + return false; + } + }, + set: function(key, val, session) { + var storage; + if (session == null) { + session = false; + } + if (!this.supported()) { + return; + } + storage = session ? sessionStorage : localStorage; + return storage.setItem(key, val); + }, + get: function(key, session) { + var storage; + if (session == null) { + session = false; + } + if (!this.supported()) { + return; + } + storage = session ? sessionStorage : localStorage; + return storage[key]; + }, + remove: function(key, session) { + var storage; + if (session == null) { + session = false; + } + if (!this.supported()) { + return; + } + storage = session ? sessionStorage : localStorage; + return storage.removeItem(key); + } + }; + + return SimditorAutosave; + +})(SimpleModule); + +Simditor.connect(SimditorAutosave); + +return SimditorAutosave; + +})); diff --git a/static/src/js/utils/editor.js b/static/src/js/utils/editor.js index 53a7d55..e332008 100644 --- a/static/src/js/utils/editor.js +++ b/static/src/js/utils/editor.js @@ -1,4 +1,4 @@ -define("editor", ["simditor"], function(Simditor){ +define("editor", ["simditor", "simditor-autosave"], function(Simditor){ function editor(selector){ return new Simditor({ textarea: $(selector), diff --git a/template/src/admin/announcement/announcement.html b/template/src/admin/announcement/announcement.html index 7bfab36..24990ce 100644 --- a/template/src/admin/announcement/announcement.html +++ b/template/src/admin/announcement/announcement.html @@ -38,7 +38,7 @@ ms-duplex="newTitle">
- +
@@ -62,7 +62,7 @@
-
From 69628160537d5e42cdde39d9114c415e25d0bea0 Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Sat, 17 Oct 2015 13:58:38 +0800 Subject: [PATCH 13/25] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=89=8D=E7=AB=AF?= =?UTF-8?q?=E4=BB=A3=E7=A0=81=E7=9A=84=E8=87=AA=E5=8A=A8=E4=BF=9D=E5=AD=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- static/src/js/app/oj/problem/problem.js | 33 ++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/static/src/js/app/oj/problem/problem.js b/static/src/js/app/oj/problem/problem.js index cdb1460..ea78e70 100644 --- a/static/src/js/app/oj/problem/problem.js +++ b/static/src/js/app/oj/problem/problem.js @@ -27,14 +27,41 @@ require(["jquery", "codeMirror", "csrfToken", "bsAlert", "ZeroClipboard"], return; } + function getLanguage(){ + return $("input[name='language'][checked]").val(); + } + var codeEditor = codeMirror(codeEditorSelector, "text/x-csrc"); - var language = $("input[name='language'][checked]").val(); + var language = getLanguage(); var submissionId; + function setLanguage(language){ + var languageTypes = {"1": "text/x-csrc", "2": "text/x-c++src", "3": "text/x-java"}; + codeEditor.setOption("mode", languageTypes[language]); + } + + function saveCode(code){ + localStorage.setItem(location.href, JSON.stringify({code: code, language: language})) + } + + if(window.localStorage){ + var data = localStorage[location.href]; + if(data){ + data = JSON.parse(data); + $("input[name='language'][value='" + data.language + "']").prop("checked", true); + language = data.language; + codeEditor.setValue(data.code); + setLanguage(data.language); + } + + setInterval(function(){ + saveCode(codeEditor.getValue()) + }, 3000); + } + $("input[name='language']").change(function () { language = this.value; - var languageTypes = {"1": "text/x-csrc", "2": "text/x-c++src", "3": "text/x-java"}; - codeEditor.setOption("mode", languageTypes[language]); + setLanguage(language); }); $("#show-more-btn").click(function () { From 156be0b21d8f868718ba7bc157e50e8f189892c2 Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Sat, 17 Oct 2015 13:59:21 +0800 Subject: [PATCH 14/25] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=92=8C=E5=88=A0?= =?UTF-8?q?=E9=99=A4=20contest=20=E5=92=8C=20problem=20=E9=83=A8=E5=88=86?= =?UTF-8?q?=E5=AD=97=E6=AE=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- contest/models.py | 41 +------------------ contest/views.py | 2 +- problem/migrations/0010_auto_20151017_1226.py | 24 +++++++++++ problem/migrations/0011_auto_20151017_1227.py | 20 +++++++++ problem/models.py | 9 ++-- 5 files changed, 52 insertions(+), 44 deletions(-) create mode 100644 problem/migrations/0010_auto_20151017_1226.py create mode 100644 problem/migrations/0011_auto_20151017_1227.py diff --git a/contest/models.py b/contest/models.py index 2708bcf..2213f43 100644 --- a/contest/models.py +++ b/contest/models.py @@ -22,12 +22,8 @@ CONTEST_UNDERWAY = 0 class Contest(models.Model): title = models.CharField(max_length=40, unique=True) description = RichTextField() - # 比赛模式:0 即为是acm模式,1 即为是按照总的 ac 题目数量排名模式 - mode = models.IntegerField() # 是否显示实时排名结果 real_time_rank = models.BooleanField() - # 是否显示别人的提交记录 - show_user_submission = models.BooleanField() # 只能超级管理员创建公开赛,管理员只能创建小组内部的比赛 # 如果这一项不为空,即为有密码的公开赛,没有密码的可以为小组赛或者是公开赛(此时用比赛的类型来表示) password = models.CharField(max_length=30, blank=True, null=True) @@ -68,46 +64,13 @@ class ContestProblem(AbstractProblem): contest = models.ForeignKey(Contest) # 比如A B 或者1 2 或者 a b 将按照这个排序 sort_index = models.CharField(max_length=30) - score = models.IntegerField(default=0) + # 是否已经公开了题目,防止重复公开 + is_public = models.BooleanField(default=False) class Meta: db_table = "contest_problem" -class ContestProblemTestCase(models.Model): - """ - 如果比赛是按照通过的测试用例总分计算的话,就需要这个model 记录每个测试用例的分数 - """ - # 测试用例的id 这个还在测试用例的配置文件里面有对应 - id = models.CharField(max_length=40, primary_key=True, db_index=True) - problem = models.ForeignKey(ContestProblem) - score = models.IntegerField() - - class Meta: - db_table = "contest_problem_test_case" - - -class ContestSubmission(models.Model): - """ - 用于保存比赛提交和排名的一些数据,加快检索速度 - """ - user = models.ForeignKey(User) - problem = models.ForeignKey(ContestProblem) - contest = models.ForeignKey(Contest) - total_submission_number = models.IntegerField(default=1) - # 这道题是 AC 还是没过 - ac = models.BooleanField() - # ac 用时以秒计 - ac_time = models.IntegerField(default=0) - # 总的时间,用于acm 类型的,也需要保存罚时 - total_time = models.IntegerField(default=0) - # 第一个解出此题目 - first_achieved = models.BooleanField(default=False) - - class Meta: - db_table = "contest_submission" - - class ContestRank(models.Model): user = models.ForeignKey(User) contest = models.ForeignKey(Contest) diff --git a/contest/views.py b/contest/views.py index cdf9397..e498e40 100644 --- a/contest/views.py +++ b/contest/views.py @@ -19,7 +19,7 @@ from account.models import SUPER_ADMIN, User from account.decorators import login_required from group.models import Group from utils.cache import get_cache_redis -from .models import (Contest, ContestProblem, ContestSubmission, CONTEST_ENDED, +from .models import (Contest, ContestProblem, CONTEST_ENDED, CONTEST_NOT_START, CONTEST_UNDERWAY, ContestRank) from .models import GROUP_CONTEST, PUBLIC_CONTEST, PASSWORD_PROTECTED_CONTEST from .decorators import check_user_contest_permission diff --git a/problem/migrations/0010_auto_20151017_1226.py b/problem/migrations/0010_auto_20151017_1226.py new file mode 100644 index 0000000..743497d --- /dev/null +++ b/problem/migrations/0010_auto_20151017_1226.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('problem', '0009_auto_20151008_1125'), + ] + + operations = [ + migrations.AddField( + model_name='problem', + name='last_update_time', + field=models.DateTimeField(null=True, blank=True), + ), + migrations.AlterField( + model_name='problem', + name='source', + field=models.CharField(max_length=200, null=True, blank=True), + ), + ] diff --git a/problem/migrations/0011_auto_20151017_1227.py b/problem/migrations/0011_auto_20151017_1227.py new file mode 100644 index 0000000..ae8b7d0 --- /dev/null +++ b/problem/migrations/0011_auto_20151017_1227.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations +import utils.models + + +class Migration(migrations.Migration): + + dependencies = [ + ('problem', '0010_auto_20151017_1226'), + ] + + operations = [ + migrations.AlterField( + model_name='problem', + name='hint', + field=utils.models.RichTextField(null=True, blank=True), + ), + ] diff --git a/problem/models.py b/problem/models.py index a9e8f74..5944ca6 100644 --- a/problem/models.py +++ b/problem/models.py @@ -26,11 +26,12 @@ class AbstractProblem(models.Model): # 测试用例id 这个id 可以用来拼接得到测试用例的文件存储位置 test_case_id = models.CharField(max_length=40) # 提示 - hint = models.TextField(blank=True, null=True) + hint = RichTextField(blank=True, null=True) # 创建时间 create_time = models.DateTimeField(auto_now_add=True) - # 最后更新时间 - # last_update_time = models.DateTimeField(auto_now=True) + # 最后更新时间,不适用auto_now,因为本 model 里面的提交数量是变化的,导致每次 last_update_time 也更新了 + # 需要每次编辑后手动赋值 + last_update_time = models.DateTimeField(blank=True, null=True) # 这个题是谁创建的 created_by = models.ForeignKey(User) # 时间限制 单位是毫秒 @@ -63,4 +64,4 @@ class Problem(AbstractProblem): # 标签 tags = models.ManyToManyField(ProblemTag) # 来源 - source = models.CharField(max_length=30, blank=True, null=True) + source = models.CharField(max_length=200, blank=True, null=True) From 9e39fa18aba1bb0d62179365aac6ff6ce2db3bc0 Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Sat, 17 Oct 2015 15:12:46 +0800 Subject: [PATCH 15/25] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=AF=94=E8=B5=9B?= =?UTF-8?q?=E6=8E=92=E5=90=8D=E9=A1=B5=E9=9D=A2=E9=87=8D=E5=A4=8D=E5=8F=82?= =?UTF-8?q?=E6=95=B0=E7=9A=84=20bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- template/src/oj/contest/contest_rank.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/template/src/oj/contest/contest_rank.html b/template/src/oj/contest/contest_rank.html index e065105..b14eb28 100644 --- a/template/src/oj/contest/contest_rank.html +++ b/template/src/oj/contest/contest_rank.html @@ -75,14 +75,14 @@
    {% if paging_info.previous_page %} {% endif %} {% if paging_info.next_page %} {% endif %} From 28011f9a0ee5f78aff2c8f28c40483b424231c29 Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Sat, 17 Oct 2015 15:13:23 +0800 Subject: [PATCH 16/25] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=20contest=20model=20?= =?UTF-8?q?=E5=8F=98=E5=8C=96=E7=9A=84=20migrations?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- contest/migrations/0013_auto_20151017_1511.py | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 contest/migrations/0013_auto_20151017_1511.py diff --git a/contest/migrations/0013_auto_20151017_1511.py b/contest/migrations/0013_auto_20151017_1511.py new file mode 100644 index 0000000..ba3b201 --- /dev/null +++ b/contest/migrations/0013_auto_20151017_1511.py @@ -0,0 +1,64 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations +import utils.models + + +class Migration(migrations.Migration): + + dependencies = [ + ('contest', '0012_auto_20151008_1124'), + ] + + operations = [ + migrations.RemoveField( + model_name='contestproblemtestcase', + name='problem', + ), + migrations.RemoveField( + model_name='contestsubmission', + name='contest', + ), + migrations.RemoveField( + model_name='contestsubmission', + name='problem', + ), + migrations.RemoveField( + model_name='contestsubmission', + name='user', + ), + migrations.RemoveField( + model_name='contest', + name='mode', + ), + migrations.RemoveField( + model_name='contest', + name='show_user_submission', + ), + migrations.RemoveField( + model_name='contestproblem', + name='score', + ), + migrations.AddField( + model_name='contestproblem', + name='is_public', + field=models.BooleanField(default=False), + ), + migrations.AddField( + model_name='contestproblem', + name='last_update_time', + field=models.DateTimeField(null=True, blank=True), + ), + migrations.AlterField( + model_name='contestproblem', + name='hint', + field=utils.models.RichTextField(null=True, blank=True), + ), + migrations.DeleteModel( + name='ContestProblemTestCase', + ), + migrations.DeleteModel( + name='ContestSubmission', + ), + ] From 4dd5aa3eba2f09c1a72cd59a02a27f78a58a7b25 Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Sat, 17 Oct 2015 16:49:02 +0800 Subject: [PATCH 17/25] =?UTF-8?q?=E9=87=8D=E6=9E=84=20problem=20=E5=92=8C?= =?UTF-8?q?=20contest=EF=BC=9B=E4=BF=AE=E6=94=B9=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E5=92=8C=E9=83=A8=E5=88=86=E6=A8=A1=E6=9D=BF=E7=9A=84=E7=BB=93?= =?UTF-8?q?=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- contest/decorators.py | 9 +- contest/views.py | 25 +++-- contest_submission/views.py | 2 +- problem/decorators.py | 12 +- problem/serizalizers.py | 12 +- problem/tests.py | 66 ++++------- problem/views.py | 23 ++-- static/src/js/build.js | 3 + submission/views.py | 4 +- template/src/oj/contest/contest_problem.html | 103 ----------------- .../src/oj/contest/contest_problems_list.html | 104 +++++++++--------- template/src/oj/contest/my_submission.html | 74 ------------- .../src/oj/contest/my_submissions_list.html | 52 --------- template/src/oj/problem/_problem_header.html | 8 +- .../src/oj/problem/_problem_main_info.html | 29 +++++ template/src/oj/problem/_submit_problem.html | 25 +++++ template/src/oj/problem/contest_problem.html | 42 +++++++ template/src/oj/problem/problem.html | 56 +--------- .../my_submission.html | 0 .../problem_my_submissions_list.html} | 17 ++- 20 files changed, 238 insertions(+), 428 deletions(-) delete mode 100644 template/src/oj/contest/contest_problem.html delete mode 100644 template/src/oj/contest/my_submission.html delete mode 100644 template/src/oj/contest/my_submissions_list.html create mode 100644 template/src/oj/problem/_problem_main_info.html create mode 100644 template/src/oj/problem/_submit_problem.html create mode 100644 template/src/oj/problem/contest_problem.html rename template/src/oj/{problem => submission}/my_submission.html (100%) rename template/src/oj/{problem/my_submissions_list.html => submission/problem_my_submissions_list.html} (70%) diff --git a/contest/decorators.py b/contest/decorators.py index 600d98b..a016032 100644 --- a/contest/decorators.py +++ b/contest/decorators.py @@ -32,7 +32,7 @@ def check_user_contest_permission(func): else: return HttpResponseRedirect("/login/") - # kwargs 就包含了url 里面的播或参数 + # kwargs 就包含了 url 里面的参数 if "contest_id" in kwargs: contest_id = kwargs["contest_id"] elif "contest_id" in request.data: @@ -54,6 +54,13 @@ def check_user_contest_permission(func): if request.user.admin_type == SUPER_ADMIN or request.user == contest.created_by: return func(*args, **kwargs) + # 管理员可见隐藏的比赛,已经先判断了身份 + if not contest.visible: + if request.is_ajax(): + return error_response(u"比赛不存在") + else: + return error_page(request, u"比赛不存在") + # 有密码的公开赛 if contest.contest_type == PASSWORD_PROTECTED_CONTEST: # 没有输入过密码 diff --git a/contest/views.py b/contest/views.py index e498e40..191e54a 100644 --- a/contest/views.py +++ b/contest/views.py @@ -293,7 +293,6 @@ def contest_page(request, contest_id): 单个比赛的详情页 """ contest = Contest.objects.get(id=contest_id) - return render(request, "oj/contest/contest_index.html", {"contest": contest}) @@ -304,15 +303,17 @@ def contest_problem_page(request, contest_id, contest_problem_id): """ contest = Contest.objects.get(id=contest_id) try: - contest_problem = ContestProblem.objects.get(id=contest_problem_id, visible=True) + problem = ContestProblem.objects.get(id=contest_problem_id, visible=True) except ContestProblem.DoesNotExist: return error_page(request, u"比赛题目不存在") warning = u"您已经提交过本题的正确答案,重复提交可能造成时间累计。" show_warning = False + try: - submission = ContestSubmission.objects.get(user=request.user, contest=contest, problem=contest_problem) - show_warning = submission.ac - except ContestSubmission.DoesNotExist: + rank = ContestRank.objects.get(user=request.user, contest=contest) + # 提示已经 ac 过这道题了 + show_warning = rank.submission_info.get(str(problem.id), {"is_ac": False})["is_ac"] + except ContestRank.DoesNotExist: pass # 已经结束 @@ -324,14 +325,16 @@ def contest_problem_page(request, contest_id, contest_problem_id): warning = u"比赛没有开始,您是管理员,可以提交和测试题目,但是目前的提交不会计入排名。" show_submit_code_area = False - if contest.status == CONTEST_UNDERWAY: - show_submit_code_area = True - if request.user.admin_type == SUPER_ADMIN or request.user == contest.created_by: + if contest.status == CONTEST_UNDERWAY or \ + request.user.admin_type == SUPER_ADMIN or \ + request.user == contest.created_by: show_submit_code_area = True - return render(request, "oj/contest/contest_problem.html", {"contest_problem": contest_problem, "contest": contest, - "samples": json.loads(contest_problem.samples), - "show_warning": show_warning, "warning": warning, + return render(request, "oj/problem/contest_problem.html", {"problem": problem, + "contest": contest, + "samples": json.loads(problem.samples), + "show_warning": show_warning, + "warning": warning, "show_submit_code_area": show_submit_code_area}) diff --git a/contest_submission/views.py b/contest_submission/views.py index 2f037de..4c893d2 100644 --- a/contest_submission/views.py +++ b/contest_submission/views.py @@ -76,7 +76,7 @@ def contest_problem_my_submissions_list_page(request, contest_id, contest_proble submissions = Submission.objects.filter(user_id=request.user.id, problem_id=contest_problem.id).order_by( "-create_time"). \ values("id", "result", "create_time", "accepted_answer_time", "language") - return render(request, "oj/contest/my_submissions_list.html", + return render(request, "oj/submission/problem_my_submissions_list.html", {"submissions": submissions, "problem": contest_problem}) diff --git a/problem/decorators.py b/problem/decorators.py index 762c54a..cc2ac2a 100644 --- a/problem/decorators.py +++ b/problem/decorators.py @@ -17,12 +17,16 @@ def check_user_problem_permission(func): request = args[0] # 这是在后台使用的url middleware 已经确保用户是登录状态的了 - if request.user.admin_type == SUPER_ADMIN: - return func(*args, **kwargs) try: - Problem.objects.get(id=request.data.get("problem_id", -1), created_by=request.user) + problem = Problem.objects.get(id=request.data.get("id", -1)) except Problem.DoesNotExist: return error_response(u"问题不存在") - return func(*args, **kwargs) + + if request.user.admin_type == SUPER_ADMIN: + return func(*args, **kwargs) + else: + if problem.created_by != request.user: + return error_response(u"问题不存在") + return func(*args, **kwargs) return check diff --git a/problem/serizalizers.py b/problem/serizalizers.py index afa7f43..0865b8f 100644 --- a/problem/serizalizers.py +++ b/problem/serizalizers.py @@ -25,13 +25,13 @@ class CreateProblemSerializer(serializers.Serializer): # [{"input": "1 1", "output": "2"}] samples = ProblemSampleSerializer() test_case_id = serializers.CharField(max_length=40) - source = serializers.CharField(max_length=30, required=False, default=None) time_limit = serializers.IntegerField(min_value=1) memory_limit = serializers.IntegerField(min_value=1) difficulty = serializers.IntegerField() tags = serializers.ListField(child=serializers.CharField(max_length=10)) hint = serializers.CharField(max_length=3000, allow_blank=True) - visible = visible = serializers.BooleanField() + source = serializers.CharField(max_length=100, required=False, default=None) + visible = serializers.BooleanField() class ProblemTagSerializer(serializers.ModelSerializer): @@ -61,15 +61,11 @@ class EditProblemSerializer(serializers.Serializer): input_description = serializers.CharField(max_length=10000) output_description = serializers.CharField(max_length=10000) test_case_id = serializers.CharField(max_length=40) - source = serializers.CharField(max_length=30) + source = serializers.CharField(max_length=100) time_limit = serializers.IntegerField(min_value=1) memory_limit = serializers.IntegerField(min_value=1) difficulty = serializers.IntegerField() tags = serializers.ListField(child=serializers.CharField(max_length=20)) samples = ProblemSampleSerializer() hint = serializers.CharField(max_length=3000, allow_blank=True) - visible = serializers.BooleanField() - - -class CreateProblemTagSerializer(serializers.Serializer): - name = serializers.CharField(max_length=10) + visible = serializers.BooleanField() \ No newline at end of file diff --git a/problem/tests.py b/problem/tests.py index 9819486..e168ef8 100644 --- a/problem/tests.py +++ b/problem/tests.py @@ -7,16 +7,15 @@ from django.core.urlresolvers import reverse from rest_framework.test import APITestCase, APIClient from account.models import User, SUPER_ADMIN +from account.tests import create_user from problem.models import Problem, ProblemTag class ProblemPageTest(TestCase): def setUp(self): self.client = APIClient() - self.user = User.objects.create(username="test", admin_type=SUPER_ADMIN) - self.user.set_password("testaa") - self.user.save() - self.client.login(username="test", password="testaa") + self.user = create_user() + self.client.login(username="test", password="111111") self.problem = Problem.objects.create(title="title1", description="description1", input_description="input1_description", @@ -31,11 +30,11 @@ class ProblemPageTest(TestCase): created_by=User.objects.get(username="test")) def test_visit_problem_successfully(self): - response = self.client.get('/problem/1/') - self.assertEqual(response.status_code, 200) + response = self.client.get('/problem/' + str(self.problem.id) + "/") + self.assertTemplateUsed(response, "oj/problem/problem.html") def test_problem_does_not_exist(self): - response = self.client.get('/problem/3/') + response = self.client.get('/problem/3000/') self.assertTemplateUsed(response, "utils/error.html") @@ -60,10 +59,8 @@ class ProblemAdminTest(APITestCase): def setUp(self): self.client = APIClient() self.url = reverse("problem_admin_api") - self.user = User.objects.create(username="test", admin_type=SUPER_ADMIN) - self.user.set_password("testaa") - self.user.save() - self.client.login(username="test", password="testaa") + self.user = create_user(admin_type=SUPER_ADMIN) + self.client.login(username="test", password="111111") ProblemTag.objects.create(name="tag1") ProblemTag.objects.create(name="tag2") self.problem = Problem.objects.create(title="title1", @@ -77,15 +74,9 @@ class ProblemAdminTest(APITestCase): memory_limit=1000, difficulty=1, hint="hint1", - created_by=User.objects.get(username="test")) + created_by=self.user) - # 以下是发布题目的测试 - def test_invalid_format(self): - data = {"title": "test1"} - response = self.client.post(self.url, data=data) - self.assertEqual(response.data["code"], 1) - - def test_release_problem_successfully(self): + def test_create_problem_successfully(self): data = {"title": "title2", "description": "description2", "input_description": "input_description2", @@ -97,30 +88,15 @@ class ProblemAdminTest(APITestCase): "memory_limit": "1000", "difficulty": "1", "hint": "hint1", + "visible": True, "tags": [1]} response = self.client.post(self.url, data=json.dumps(data), content_type="application/json") self.assertEqual(response.data["code"], 0) - # 以下是编辑题目的测试 - def test_invalid_data(self): - data = {"title": "test0"} - response = self.client.put(self.url, data=data) - self.assertEqual(response.data["code"], 1) - - def test_problem_does_not_exist(self): - tags = ProblemTag.objects.filter(id__in=[1]) - self.problem.tags.add(*tags) - data = self._create_data(2, False, [1]) - response = self.client.put(self.url, data=json.dumps(data), content_type="application/json") - self.assertEqual(response.data, {"code": 1, "data": u"该题目不存在!"}) - def test_edit_problem_successfully(self): tags = ProblemTag.objects.filter(id__in=[1]) self.problem.tags.add(*tags) - data = self._create_data(1, True, [1, 2]) - problem = Problem.objects.get(id=data["id"]) - problem.tags.remove(*problem.tags.all()) - problem.tags.add(*ProblemTag.objects.filter(id__in=data["tags"])) + data = self._create_data(self.problem.id, True, [1, 2]) response = self.client.put(self.url, data=json.dumps(data), content_type="application/json") self.assertEqual(response.data["code"], 0) @@ -139,11 +115,11 @@ class ProblemAdminTest(APITestCase): self.assertEqual(item["visible"], True) def test_query_problem_does_not_exist(self): - data = {"problem_id": 2} + data = {"problem_id": 200} response = self.client.get(self.url, data=data) self.assertEqual(response.data, {"code": 1, "data": u"题目不存在"}) - def test_query_problem_exists(self): + def test_query_existed_problem(self): data = {"problem_id": self.problem.id} response = self.client.get(self.url, data=data) self.assertEqual(response.data["code"], 0) @@ -153,10 +129,8 @@ class ProblemTagAdminAPITest(APITestCase): def setUp(self): self.client = APIClient() self.url = reverse('problem_tag_admin_api') - self.user = User.objects.create(username="testx", admin_type=SUPER_ADMIN) - self.user.set_password("testxx") - self.user.save() - self.client.login(username="testx", password="testxx") + self.user = create_user(admin_type=SUPER_ADMIN) + self.client.login(username="test", password="111111") ProblemTag.objects.create(name="tag1") # 以下是返回所有的问题的标签 @@ -168,10 +142,8 @@ class ProblemListPageTest(TestCase): def setUp(self): self.client = Client() self.url = reverse('problem_list_page', kwargs={"page": 1}) - self.user = User.objects.create(username="test", admin_type=SUPER_ADMIN) - self.user.set_password("testaa") - self.user.save() - self.client.login(username="test", password="testaa") + self.user = create_user(admin_type=SUPER_ADMIN) + self.client.login(username="test", password="111111") ProblemTag.objects.create(name="tag1") ProblemTag.objects.create(name="tag2") self.problem = Problem.objects.create(title="title1", @@ -185,7 +157,7 @@ class ProblemListPageTest(TestCase): memory_limit=1000, difficulty=1, hint="hint1", - created_by=User.objects.get(username="test")) + created_by=self.user) def test_problemListPage_not_exist(self): response = self.client.get('/problems/999/') diff --git a/problem/views.py b/problem/views.py index 6b2c19f..3b68bae 100644 --- a/problem/views.py +++ b/problem/views.py @@ -9,21 +9,19 @@ import logging from django.shortcuts import render from django.db.models import Q, Count from django.core.paginator import Paginator - -from rest_framework.views import APIView - +from django.utils.timezone import now from django.conf import settings +from rest_framework.views import APIView from account.models import SUPER_ADMIN from account.decorators import super_admin_required from utils.shortcuts import (serializer_invalid_response, error_response, success_response, paginate, rand_str, error_page) from .serizalizers import (CreateProblemSerializer, EditProblemSerializer, ProblemSerializer, - ProblemTagSerializer, CreateProblemTagSerializer) + ProblemTagSerializer) from .models import Problem, ProblemTag from .decorators import check_user_problem_permission - logger = logging.getLogger("app_info") @@ -59,7 +57,7 @@ class ProblemAdminAPIView(APIView): if serializer.is_valid(): data = serializer.data try: - Problem.objects.get(title=data["title"], description=data["description"]) + Problem.objects.get(title=data["title"]) return error_response(u"添加失败,存在重复的题目") except Problem.DoesNotExist: pass @@ -110,6 +108,7 @@ class ProblemAdminAPIView(APIView): problem.samples = json.dumps(data["samples"]) problem.hint = data["hint"] problem.visible = data["visible"] + problem.last_update_time = now() # 删除原有的标签的对应关系 problem.tags.remove(*problem.tags.all()) @@ -155,7 +154,7 @@ class ProblemAdminAPIView(APIView): keyword = request.GET.get("keyword", None) if keyword: problems = problems.filter(Q(title__contains=keyword) | - Q(description__contains=keyword)) + Q(description__contains=keyword)) return paginate(request, problems, ProblemSerializer) @@ -164,6 +163,7 @@ class TestCaseUploadAPIView(APIView): """ 上传题目的测试用例 """ + def _is_legal_test_case_file_name(self, file_name): # 正整数开头的 .in 或者.out 结尾的 regex = r"^[1-9]\d*\.(in|out)$" @@ -190,10 +190,7 @@ class TestCaseUploadAPIView(APIView): l = [] # 如果文件是直接打包的,那么name_list 就是["1.in", "1.out"]这样的 - # 如果文件还有一层文件夹test_case,那么name_list就是["test_case/", "test_case/1.in", "test_case/1.out"] - # 现在暂时只支持第一种,先判断一下是什么格式的 - # 第一种格式的 if "1.in" in name_list and "1.out" in name_list: for file_name in name_list: if self._is_legal_test_case_file_name(file_name): @@ -251,7 +248,8 @@ class TestCaseUploadAPIView(APIView): "striped_output_md5": striped_md5.hexdigest(), "output_size": os.path.getsize(test_case_dir + str(i + 1) + ".out")} # 写入配置文件 - open(test_case_dir + "info", "w").write(json.dumps(file_info)) + with open(test_case_dir + "info", "w") as f: + f.write(json.dumps(file_info)) return success_response({"test_case_id": problem_test_dir, "file_list": {"input": l[0::2], @@ -311,7 +309,8 @@ def problem_list_page(request, page=1): pass # 右侧标签列表 按照关联的题目的数量排序 排除题目数量为0的 - tags = ProblemTag.objects.annotate(problem_number=Count("problem")).filter(problem_number__gt=0).order_by("-problem_number") + tags = ProblemTag.objects.annotate(problem_number=Count("problem")).filter(problem_number__gt=0).order_by( + "-problem_number") return render(request, "oj/problem/problem_list.html", {"problems": current_page, "page": int(page), diff --git a/static/src/js/build.js b/static/src/js/build.js index 34a05e6..96247c1 100644 --- a/static/src/js/build.js +++ b/static/src/js/build.js @@ -85,6 +85,9 @@ { name: "bootstrap", }, + { + name: "codeMirror" + }, { name: "announcement_0_pack" }, diff --git a/submission/views.py b/submission/views.py index caa2e68..100c5f4 100644 --- a/submission/views.py +++ b/submission/views.py @@ -85,7 +85,7 @@ def problem_my_submissions_list_page(request, problem_id): contest_id__isnull=True).order_by("-create_time"). \ values("id", "result", "create_time", "accepted_answer_time", "language") - return render(request, "oj/problem/my_submissions_list.html", + return render(request, "oj/submission/problem_my_submissions_list.html", {"submissions": submissions, "problem": problem}) @@ -139,7 +139,7 @@ def my_submission(request, submission_id): else: info = None user = User.objects.get(id=submission.user_id) - return render(request, "oj/problem/my_submission.html", + return render(request, "oj/submission/my_submission.html", {"submission": submission, "problem": problem, "info": info, "user": user, "can_share": result["can_share"]}) diff --git a/template/src/oj/contest/contest_problem.html b/template/src/oj/contest/contest_problem.html deleted file mode 100644 index cf60ef8..0000000 --- a/template/src/oj/contest/contest_problem.html +++ /dev/null @@ -1,103 +0,0 @@ -{% extends 'oj_base.html' %} -{% block title %} - {{ contest_problem.title }} -{% endblock %} -{% block body %} -
    - -

    {{ contest_problem.title }}

    - -

    发布时间 : {{ contest_problem.create_time }}   - 时间限制 : {{ contest_problem.time_limit }}ms   - 内存限制 : {{ contest_problem.memory_limit }}M -

    - -
    -
    - - -
    {{ contest_problem.description|safe }}
    -
    -
    - - -

    {{ contest_problem.input_description }}

    - -
    - -
    - - -

    {{ contest_problem.output_description }}

    -
    - {% for item in samples %} -
    - - 复制 -
    {{ item.input }}
    -
    -
    - -
    {{ item.output }}
    -
    - {% endfor %} - - {% if contest_problem.hint %} -
    - -
    {{ contest_problem.hint|safe }}
    -
    - {% endif %} - {% if show_submit_code_area %} -
    - -
    - - - -
    -
    -
    - - -
    -
    -
    - - -
    - {% endif %} - {% if show_warning %} - - {% endif %} -
    -
    -
    -
    -
    -
    -
    -{% endblock %} -{% block js_block %} - -{% endblock %} \ No newline at end of file diff --git a/template/src/oj/contest/contest_problems_list.html b/template/src/oj/contest/contest_problems_list.html index e35e28e..056ba6c 100644 --- a/template/src/oj/contest/contest_problems_list.html +++ b/template/src/oj/contest/contest_problems_list.html @@ -3,66 +3,66 @@ 比赛题目列表 {% endblock %} {% block body %} -{% load problem %} -
    + {% load problem %} +
    -
    -
    +
    +
    - + -
    - - -
    -
    - - - - - - - - - - - {% for item in contest_problems %} - - - - - - - {% endfor %} - -
    #题目通过率
    - - - {{ item.sort_index }} - - {{ item.title }} - {{ item|accepted_radio }}
    -
    -
    + +
    +
    + + + + + + + + + + + {% for item in contest_problems %} + + + + + + + {% endfor %} + +
    #题目通过率
    + + + {{ item.sort_index }} + + {{ item.title }} + {{ item|accepted_radio }}
    + +
    +
    + +
    -
    {% endblock %} {% block js_block %} diff --git a/template/src/oj/contest/my_submission.html b/template/src/oj/contest/my_submission.html deleted file mode 100644 index 1e09103..0000000 --- a/template/src/oj/contest/my_submission.html +++ /dev/null @@ -1,74 +0,0 @@ -{% extends 'oj_base.html' %} -{% block title %} - 我的提交详情 -{% endblock %} -{% block css_block %} - -{% endblock %} -{% block body %} - {% load submission %} -
    - - {% include "oj/problem/_problem_header.html" %} -
    -
    -

    运行结果 : - {{ submission.result|translate_result }} - -

    - {% ifequal submission.result 0 %} -

    时间 : {{ submission.accepted_answer_time }}ms 语言 : - {{ submission.language|translate_language }} -

    - {% endifequal %} - {% ifequal submission.result 4 %} -

    {{ submission.info }}

    - {% endifequal %} -

    提交时间 : {{ submission.create_time }}

    -
    -
    - {% ifequal request.user.admin_type 2 %} -

    本调试信息仅超级管理员可见

    - {% ifequal submission.result 7 %} -
    System Error: {{ submission.info }}
    - {% else %} -
    {{ info }}
    - {% endifequal %} - - {% endifequal %} - - -
    - -
    -
    - -{% endblock %} -{% block js_block %} - -{% endblock %} \ No newline at end of file diff --git a/template/src/oj/contest/my_submissions_list.html b/template/src/oj/contest/my_submissions_list.html deleted file mode 100644 index 638318a..0000000 --- a/template/src/oj/contest/my_submissions_list.html +++ /dev/null @@ -1,52 +0,0 @@ -{% extends 'oj_base.html' %} -{% block title %} - 我的提交列表 -{% endblock %} -{% block body %} - - {% load submission %} -
    - - {% include "oj/problem/_problem_header.html" %} - - - - - - - - - - - - {% for item in submissions %} - - - - - - - - {% endfor %} - - -
    #提交时间语言运行时间结果
    {{ forloop.counter }}{{ item.create_time }} - {{ item.language|translate_language }} - - {% if item.accepted_answer_time %} - {{ item.accepted_answer_time }}ms - {% else %} - -- - {% endif %} - - {{ item.result|translate_result }} -
    -
    -{% endblock %} \ No newline at end of file diff --git a/template/src/oj/problem/_problem_header.html b/template/src/oj/problem/_problem_header.html index afb89cd..a2da6c7 100644 --- a/template/src/oj/problem/_problem_header.html +++ b/template/src/oj/problem/_problem_header.html @@ -1,6 +1,8 @@

    {{ problem.title }}

    -

    发布时间 : {{ problem.create_time }}   - 时间限制 : {{ problem.time_limit }}ms   - 内存限制 : {{ problem.memory_limit }}M +

    + 发布时间: {{ problem.create_time }}   + {% if problem.last_update_time %}最后更新: {{ problem.last_update_time }}  {% endif %} + 时间限制: {{ problem.time_limit }}ms   + 内存限制: {{ problem.memory_limit }}M

    \ No newline at end of file diff --git a/template/src/oj/problem/_problem_main_info.html b/template/src/oj/problem/_problem_main_info.html new file mode 100644 index 0000000..cdc80a0 --- /dev/null +++ b/template/src/oj/problem/_problem_main_info.html @@ -0,0 +1,29 @@ +
    + + +
    {{ problem.description|safe }}
    +
    +
    + + +

    {{ problem.input_description }}

    +
    +
    + + +

    {{ problem.output_description }}

    +
    + {% for item in samples %} +
    + + 复制 +
    +{{ item.input }}
    + +
    +
    + +
    +{{ item.output }}
    +
    + {% endfor %} \ No newline at end of file diff --git a/template/src/oj/problem/_submit_problem.html b/template/src/oj/problem/_submit_problem.html new file mode 100644 index 0000000..f21f45d --- /dev/null +++ b/template/src/oj/problem/_submit_problem.html @@ -0,0 +1,25 @@ +
    + +
    + + + +
    +
    +
    + + +
    +
    +
    + + +
    \ No newline at end of file diff --git a/template/src/oj/problem/contest_problem.html b/template/src/oj/problem/contest_problem.html new file mode 100644 index 0000000..b844ec1 --- /dev/null +++ b/template/src/oj/problem/contest_problem.html @@ -0,0 +1,42 @@ +{% extends 'oj_base.html' %} +{% block title %} + {{ problem.title }} +{% endblock %} +{% block body %} +
    + + {% include "oj/problem/_problem_header.html" %} +
    + {% include "oj/problem/_problem_main_info.html" %} + {% if problem.hint %} +
    + + +
    {{ problem.hint|safe }}
    +
    + {% endif %} + {% if show_submit_code_area %} + {% include "oj/problem/_submit_problem.html" %} + {% endif %} + {% if show_warning %} + + {% endif %} +
    +
    +
    +
    +
    +{% endblock %} +{% block js_block %} + +{% endblock %} \ No newline at end of file diff --git a/template/src/oj/problem/problem.html b/template/src/oj/problem/problem.html index d7c360b..0d24566 100644 --- a/template/src/oj/problem/problem.html +++ b/template/src/oj/problem/problem.html @@ -15,35 +15,7 @@ {% include "oj/problem/_problem_header.html" %}
    -
    - - -
    {{ problem.description|safe }}
    -
    -
    - - -

    {{ problem.input_description }}

    -
    -
    - - -

    {{ problem.output_description }}

    -
    - {% for item in samples %} -
    - - 复制 -
    -{{ item.input }}
    - -
    -
    - -
    -{{ item.output }}
    -
    - {% endfor %} + {% include "oj/problem/_problem_main_info.html" %}
    @@ -67,31 +39,7 @@
    {{ problem.source }}
    {% endif %} -
    - -
    - - - -
    -
    -
    - - -
    -
    -
    - - -
    + {% include "oj/problem/submit_problem.html" %}

    diff --git a/template/src/oj/problem/my_submission.html b/template/src/oj/submission/my_submission.html similarity index 100% rename from template/src/oj/problem/my_submission.html rename to template/src/oj/submission/my_submission.html diff --git a/template/src/oj/problem/my_submissions_list.html b/template/src/oj/submission/problem_my_submissions_list.html similarity index 70% rename from template/src/oj/problem/my_submissions_list.html rename to template/src/oj/submission/problem_my_submissions_list.html index faa9d62..5931601 100644 --- a/template/src/oj/problem/my_submissions_list.html +++ b/template/src/oj/submission/problem_my_submissions_list.html @@ -7,10 +7,19 @@ {% load submission %}
    {% include "oj/problem/_problem_header.html" %} {% if submissions %} From efdf6f21417f810bb44e204e180ff082606e7d5e Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Sat, 17 Oct 2015 17:59:26 +0800 Subject: [PATCH 18/25] =?UTF-8?q?=E5=88=A0=E9=99=A4=20contest=5Fsubmission?= =?UTF-8?q?=20app?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- contest_submission/__init__.py | 0 contest_submission/admin.py | 3 - contest_submission/migrations/__init__.py | 0 contest_submission/models.py | 3 - contest_submission/serializers.py | 11 -- contest_submission/tests.py | 172 -------------------- contest_submission/views.py | 183 ---------------------- 7 files changed, 372 deletions(-) delete mode 100644 contest_submission/__init__.py delete mode 100644 contest_submission/admin.py delete mode 100644 contest_submission/migrations/__init__.py delete mode 100644 contest_submission/models.py delete mode 100644 contest_submission/serializers.py delete mode 100644 contest_submission/tests.py delete mode 100644 contest_submission/views.py diff --git a/contest_submission/__init__.py b/contest_submission/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/contest_submission/admin.py b/contest_submission/admin.py deleted file mode 100644 index 8c38f3f..0000000 --- a/contest_submission/admin.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.contrib import admin - -# Register your models here. diff --git a/contest_submission/migrations/__init__.py b/contest_submission/migrations/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/contest_submission/models.py b/contest_submission/models.py deleted file mode 100644 index 71a8362..0000000 --- a/contest_submission/models.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.db import models - -# Create your models here. diff --git a/contest_submission/serializers.py b/contest_submission/serializers.py deleted file mode 100644 index eb9a860..0000000 --- a/contest_submission/serializers.py +++ /dev/null @@ -1,11 +0,0 @@ -# coding=utf-8 -from rest_framework import serializers - -from account.models import User - - -class CreateContestSubmissionSerializer(serializers.Serializer): - contest_id = serializers.IntegerField() - problem_id = serializers.IntegerField() - language = serializers.IntegerField() - code = serializers.CharField(max_length=3000) diff --git a/contest_submission/tests.py b/contest_submission/tests.py deleted file mode 100644 index cb44331..0000000 --- a/contest_submission/tests.py +++ /dev/null @@ -1,172 +0,0 @@ -# coding=utf-8 -import json - -from django.test import TestCase, Client -from django.core.urlresolvers import reverse -from rest_framework.test import APITestCase, APIClient - -from account.models import User, REGULAR_USER, ADMIN, SUPER_ADMIN -from contest.models import Contest, ContestProblem -from contest.models import PUBLIC_CONTEST, PASSWORD_PROTECTED_CONTEST -from submission.models import Submission - - -class ContestSubmissionAPITest(APITestCase): - def setUp(self): - self.client = APIClient() - self.url = reverse('contest_submission_api') - self.user1 = User.objects.create(username="test1", admin_type=SUPER_ADMIN) - self.user1.set_password("testaa") - self.user1.save() - self.user2 = User.objects.create(username="test2", admin_type=REGULAR_USER) - self.user2.set_password("testbb") - self.user2.save() - self.global_contest = Contest.objects.create(title="titlex", description="descriptionx", mode=1, - contest_type=PUBLIC_CONTEST, show_rank=True, - show_user_submission=True, - start_time="2015-08-15T10:00:00.000Z", - end_time="2015-08-30T12:00:00.000Z", - created_by=User.objects.get(username="test1")) - self.contest_problem = ContestProblem.objects.create(title="titlex", - description="descriptionx", - input_description="input1_description", - output_description="output1_description", - test_case_id="1", - samples=json.dumps([{"input": "1 1", "output": "2"}]), - time_limit=100, - memory_limit=1000, - hint="hint1", - created_by=User.objects.get(username="test1"), - contest=Contest.objects.get(title="titlex"), - sort_index="a") - - # 以下是创建比赛的提交 - def test_invalid_format(self): - self.client.login(username="test1", password="testaa") - data = {"contest_id": self.global_contest.id, "language": 1} - response = self.client.post(self.url, data=data) - self.assertEqual(response.data["code"], 1) - - def test_contest_submission_successfully(self): - self.client.login(username="test1", password="testaa") - data = {"contest_id": self.global_contest.id, "problem_id": self.contest_problem.id, - "language": 1, "code": '#include "stdio.h"\nint main(){\n\treturn 0;\n}'} - response = self.client.post(self.url, data=data) - self.assertEqual(response.data["code"], 0) - - def test_contest_problem_does_not_exist(self): - self.client.login(username="test1", password="testaa") - data = {"contest_id": self.global_contest.id, "problem_id": self.contest_problem.id + 10, - "language": 1, "code": '#include "stdio.h"\nint main(){\n\treturn 0;\n}'} - response = self.client.post(self.url, data=data) - self.assertEqual(response.data, {"code": 1, "data": u"题目不存在"}) - - -class ContestProblemMySubmissionListTest(TestCase): - # 以下是我比赛单个题目的提交列表的测试 - def setUp(self): - self.client = Client() - self.user1 = User.objects.create(username="test1", admin_type=SUPER_ADMIN) - self.user1.set_password("testaa") - self.user1.save() - self.user2 = User.objects.create(username="test2", admin_type=REGULAR_USER) - self.user2.set_password("testbb") - self.user2.save() - self.global_contest = Contest.objects.create(title="titlex", description="descriptionx", mode=1, - contest_type=PUBLIC_CONTEST, show_rank=True, - show_user_submission=True, - start_time="2015-08-15T10:00:00.000Z", - end_time="2015-08-30T12:00:00.000Z", - created_by=User.objects.get(username="test1")) - self.contest_problem = ContestProblem.objects.create(title="titlex", - description="descriptionx", - input_description="input1_description", - output_description="output1_description", - test_case_id="1", - samples=json.dumps([{"input": "1 1", "output": "2"}]), - time_limit=100, - memory_limit=1000, - hint="hint1", - created_by=self.user1, - contest=self.global_contest, - sort_index="a") - - def test_contestsList_page_not_exist(self): - self.client.login(username="test1", password="testaa") - response = self.client.get('/contest/1/submissions/999/') - self.assertTemplateUsed(response, "utils/error.html") - - -class SubmissionAPITest(APITestCase): - def setUp(self): - self.client = APIClient() - self.url = reverse('contest_submission_admin_api_view') - self.userA = User.objects.create(username="test1", admin_type=ADMIN) - self.userA.set_password("testaa") - self.userA.save() - self.userS = User.objects.create(username="test2", admin_type=SUPER_ADMIN) - self.userS.set_password("testbb") - self.userS.save() - self.global_contest = Contest.objects.create(title="titlex", description="descriptionx", mode=1, - contest_type=PASSWORD_PROTECTED_CONTEST, show_rank=True, - show_user_submission=True, - start_time="2015-08-15T10:00:00.000Z", - end_time="2015-08-15T12:00:00.000Z", - password="aacc", created_by=self.userS - ) - self.problem = ContestProblem.objects.create(title="title1", - description="description1", - input_description="input1_description", - output_description="output1_description", - test_case_id="1", - sort_index="1", - samples=json.dumps([{"input": "1 1", "output": "2"}]), - time_limit=100, - memory_limit=1000, - hint="hint1", - contest=self.global_contest, - created_by=self.userS) - self.submission = Submission.objects.create(user_id=self.userA.id, - language=1, - code='#include "stdio.h"\nint main(){\n\treturn 0;\n}', - problem_id=self.problem.id) - self.submissionS = Submission.objects.create(user_id=self.userS.id, - language=2, - code='#include "stdio.h"\nint main(){\n\treturn 0;\n}', - problem_id=self.problem.id) - - def test_submission_contest_does_not_exist(self): - self.client.login(username="test2", password="testbb") - response = self.client.get(self.url + "?contest_id=99") - self.assertEqual(response.data["code"], 1) - - def test_submission_contest_parameter_error(self): - self.client.login(username="test2", password="testbb") - response = self.client.get(self.url) - self.assertEqual(response.data["code"], 1) - - def test_submission_access_denied(self): - self.client.login(username="test1", password="testaa") - response = self.client.get(self.url + "?problem_id=" + str(self.problem.id)) - self.assertEqual(response.data["code"], 1) - - def test_submission_access_denied_with_contest_id(self): - self.client.login(username="test1", password="testaa") - response = self.client.get(self.url + "?contest_id=" + str(self.global_contest.id)) - self.assertEqual(response.data["code"], 1) - - def test_get_submission_successfully(self): - self.client.login(username="test2", password="testbb") - response = self.client.get( - self.url + "?contest_id=" + str(self.global_contest.id) + "&problem_id=" + str(self.problem.id)) - self.assertEqual(response.data["code"], 0) - - def test_get_submission_successfully_problem(self): - self.client.login(username="test2", password="testbb") - response = self.client.get(self.url + "?problem_id=" + str(self.problem.id)) - self.assertEqual(response.data["code"], 0) - - def test_get_submission_problem_do_not_exist(self): - self.client.login(username="test2", password="testbb") - response = self.client.get(self.url + "?problem_id=9999") - self.assertEqual(response.data["code"], 1) diff --git a/contest_submission/views.py b/contest_submission/views.py deleted file mode 100644 index 4c893d2..0000000 --- a/contest_submission/views.py +++ /dev/null @@ -1,183 +0,0 @@ -# coding=utf-8 -import json -from datetime import datetime -import redis -import pytz -from django.shortcuts import render -from django.core.paginator import Paginator -from django.utils import timezone -from rest_framework.views import APIView - -from judge.judger_controller.tasks import judge - -from account.decorators import login_required -from account.models import SUPER_ADMIN - -from contest.decorators import check_user_contest_permission -from contest.models import Contest, ContestProblem - -from utils.shortcuts import serializer_invalid_response, error_response, success_response, error_page, paginate -from utils.cache import get_cache_redis -from submission.models import Submission -from .serializers import CreateContestSubmissionSerializer -from submission.serializers import SubmissionSerializer - - -class ContestSubmissionAPIView(APIView): - @check_user_contest_permission - def post(self, request): - """ - 创建比赛的提交 - --- - request_serializer: CreateContestSubmissionSerializer - """ - serializer = CreateContestSubmissionSerializer(data=request.data) - if serializer.is_valid(): - data = serializer.data - contest = Contest.objects.get(id=data["contest_id"]) - try: - problem = ContestProblem.objects.get(contest=contest, id=data["problem_id"]) - except ContestProblem.DoesNotExist: - return error_response(u"题目不存在") - submission = Submission.objects.create(user_id=request.user.id, language=int(data["language"]), - contest_id=contest.id, code=data["code"], problem_id=problem.id) - try: - judge.delay(submission.id, problem.time_limit, problem.memory_limit, problem.test_case_id) - except Exception: - return error_response(u"提交判题任务失败") - # 修改用户解题状态 - problems_status = request.user.problems_status - if "contest_problems" not in problems_status: - problems_status["contest_problems"] = {} - problems_status["contest_problems"][str(data["problem_id"])] = 2 - request.user.problems_status = problems_status - request.user.save() - # 增加redis 中判题队列长度的计数器 - r = get_cache_redis() - r.incr("judge_queue_length") - return success_response({"submission_id": submission.id}) - else: - return serializer_invalid_response(serializer) - - -@login_required -def contest_problem_my_submissions_list_page(request, contest_id, contest_problem_id): - """ - 我比赛单个题目的所有提交列表 - """ - try: - Contest.objects.get(id=contest_id) - except Contest.DoesNotExist: - return error_page(request, u"比赛不存在") - try: - contest_problem = ContestProblem.objects.get(id=contest_problem_id, visible=True) - except ContestProblem.DoesNotExist: - return error_page(request, u"比赛问题不存在") - submissions = Submission.objects.filter(user_id=request.user.id, problem_id=contest_problem.id).order_by( - "-create_time"). \ - values("id", "result", "create_time", "accepted_answer_time", "language") - return render(request, "oj/submission/problem_my_submissions_list.html", - {"submissions": submissions, "problem": contest_problem}) - - -@check_user_contest_permission -def contest_problem_submissions_list_page(request, contest_id, page=1): - """ - 单个比赛中的所有提交(包含自己和别人,自己可查提交结果,其他人不可查) - """ - try: - contest = Contest.objects.get(id=contest_id) - except Contest.DoesNotExist: - return error_page(request, u"比赛不存在") - - submissions = Submission.objects.filter(contest_id=contest_id).\ - values("id", "contest_id", "problem_id", "result", "create_time", - "accepted_answer_time", "language", "user_id").order_by("-create_time") - - user_id = request.GET.get("user_id", None) - if user_id: - submissions = submissions.filter(user_id=request.GET.get("user_id")) - - # 封榜的时候只能看到自己的提交 - if not contest.real_time_rank: - if not (request.user.admin_type == SUPER_ADMIN or request.user == contest.created_by): - submissions = submissions.filter(user_id=request.user.id) - - language = request.GET.get("language", None) - filter = None - if language: - submissions = submissions.filter(language=int(language)) - filter = {"name": "language", "content": language} - result = request.GET.get("result", None) - if result: - submissions = submissions.filter(result=int(result)) - filter = {"name": "result", "content": result} - paginator = Paginator(submissions, 20) - - # 为查询题目标题创建新字典 - title = {} - contest_problems = ContestProblem.objects.filter(contest=contest) - for item in contest_problems: - title[item.id] = item.title - for item in submissions: - item['title'] = title[item['problem_id']] - - try: - current_page = paginator.page(int(page)) - except Exception: - return error_page(request, u"不存在的页码") - previous_page = next_page = None - try: - previous_page = current_page.previous_page_number() - except Exception: - pass - try: - next_page = current_page.next_page_number() - except Exception: - pass - - for item in current_page: - # 自己提交的 管理员和创建比赛的可以看到所有的提交链接 - if item["user_id"] == request.user.id or request.user.admin_type == SUPER_ADMIN or \ - request.user == contest.created_by: - item["show_link"] = True - else: - item["show_link"] = False - - return render(request, "oj/contest/submissions_list.html", - {"submissions": current_page, "page": int(page), - "previous_page": previous_page, "next_page": next_page, "start_id": int(page) * 20 - 20, - "contest": contest, "filter": filter, "user_id": user_id}) - - -class ContestSubmissionAdminAPIView(APIView): - def get(self, request): - """ - 查询比赛提交,单个比赛题目提交的adminAPI - --- - response_serializer: SubmissionSerializer - """ - problem_id = request.GET.get("problem_id", None) - contest_id = request.GET.get("contest_id", None) - if contest_id: - try: - contest = Contest.objects.get(pk=contest_id) - except Contest.DoesNotExist: - return error_response(u"比赛不存在!") - if request.user.admin_type != SUPER_ADMIN and contest.created_by != request.user: - return error_response(u"您无权查看该信息!") - submissions = Submission.objects.filter(contest_id=contest_id).order_by("-create_time") - else: - if problem_id: - try: - contest_problem = ContestProblem.objects.get(pk=problem_id) - except ContestProblem.DoesNotExist: - return error_response(u"问题不存在!") - if request.user.admin_type != SUPER_ADMIN and contest_problem.contest.created_by != request.user: - return error_response(u"您无权查看该信息!") - submissions = Submission.objects.filter(contest_id=contest_problem.contest_id).order_by("-create_time") - else: - return error_response(u"参数错误!") - if problem_id: - submissions = submissions.filter(problem_id=problem_id) - return paginate(request, submissions, SubmissionSerializer) From e46847880e76c441c50178de481e6a2c908e436f Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Sat, 17 Oct 2015 17:59:54 +0800 Subject: [PATCH 19/25] fix typo --- template/src/oj/problem/problem.html | 2 +- template/src/oj/submission/my_submission.html | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/template/src/oj/problem/problem.html b/template/src/oj/problem/problem.html index 0d24566..99e8729 100644 --- a/template/src/oj/problem/problem.html +++ b/template/src/oj/problem/problem.html @@ -39,7 +39,7 @@
    {{ problem.source }}
    {% endif %} - {% include "oj/problem/submit_problem.html" %} + {% include "oj/problem/_submit_problem.html" %}

    diff --git a/template/src/oj/submission/my_submission.html b/template/src/oj/submission/my_submission.html index c458cd5..bd348e9 100644 --- a/template/src/oj/submission/my_submission.html +++ b/template/src/oj/submission/my_submission.html @@ -44,14 +44,14 @@

    作者:{{ user.username }}

    {% endifnotequal %} {% ifequal submission.result 0 %} -

    时间 : {{ submission.accepted_answer_time }}ms 语言 : +

    时间: {{ submission.accepted_answer_time }}ms 语言 : {{ submission.language|translate_language }}

    {% endifequal %} {% ifequal submission.result 4 %}
    {{ submission.info }}
    {% endifequal %} -

    提交时间 : {{ submission.create_time }}

    +

    提交时间: {{ submission.create_time }}

    {% ifequal request.user.admin_type 2 %} From bb5be25371beecce11fbc55058437c388dc726de Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Sat, 17 Oct 2015 19:59:53 +0800 Subject: [PATCH 20/25] =?UTF-8?q?=E5=90=8E=E5=8F=B0=E9=A2=98=E7=9B=AE?= =?UTF-8?q?=E5=88=9B=E5=BB=BA=E7=BC=96=E8=BE=91=20js=20=E7=9A=84=20bug=20?= =?UTF-8?q?=E4=BF=AE=E5=A4=8D=EF=BC=9B=E5=A2=9E=E5=8A=A0=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E4=B8=8A=E4=BC=A0=E8=BF=9B=E5=BA=A6=E6=9D=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- static/src/js/app/admin/admin.js | 14 -- static/src/js/app/admin/problem/addProblem.js | 68 ++++----- .../src/js/app/admin/problem/editProblem.js | 133 +++++++++--------- static/src/js/app/admin/problem/problem.js | 55 +++----- static/src/js/utils/uploader.js | 17 ++- template/src/admin/problem/add_problem.html | 1 + template/src/admin/problem/edit_problem.html | 1 + template/src/admin/problem/problem_list.html | 6 +- template/src/oj/submission/my_submission.html | 2 +- 9 files changed, 131 insertions(+), 166 deletions(-) diff --git a/static/src/js/app/admin/admin.js b/static/src/js/app/admin/admin.js index 705edf7..6ef30f6 100644 --- a/static/src/js/app/admin/admin.js +++ b/static/src/js/app/admin/admin.js @@ -109,24 +109,10 @@ require(["jquery", "avalon", "csrfToken", "bsAlert", "bootstrap"], function ($, vm.template_url = "template/group/group_detail.html"; }); - vm.$watch("showEditProblemPage", function (problemId) { - vm.problemId = problemId; - vm.template_url = "template/problem/edit_problem.html"; - }); - - vm.$watch("showProblemListPage", function () { - vm.template_url = "template/problem/problem_list.html"; - }); - vm.$watch("showGroupListPage", function () { vm.template_url = "template/group/group.html"; }); - vm.$watch("showProblemSubmissionPage", function (problemId) { - vm.problemId = problemId; - vm.template_url = "template/problem/submission_list.html"; - }); - vm.$watch("showContestProblemPage", function (problemId, contestId, contestMode) { vm.$problemId = problemId; vm.$contestId = contestId; diff --git a/static/src/js/app/admin/problem/addProblem.js b/static/src/js/app/admin/problem/addProblem.js index e67e9a7..391f70f 100644 --- a/static/src/js/app/admin/problem/addProblem.js +++ b/static/src/js/app/admin/problem/addProblem.js @@ -4,7 +4,7 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "tagE $("#add-problem-form").validator() .on('submit', function (e) { - if (!e.isDefaultPrevented()){ + if (!e.isDefaultPrevented()) { if (vm.testCaseId == "") { bsAlert("你还没有上传测试数据!"); return false; @@ -13,8 +13,8 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "tagE bsAlert("题目描述不能为空!"); return false; } - if (vm.timeLimit < 100 || vm.timeLimit > 5000) { - bsAlert("保证时间限制是一个100-5000的合法整数"); + if (vm.timeLimit < 30 || vm.timeLimit > 5000) { + bsAlert("保证时间限制是一个30-5000的合法整数"); return false; } if (vm.samples.length == 0) { @@ -50,7 +50,10 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "tagE }; for (var i = 0; i < vm.samples.$model.length; i++) { - ajaxData.samples.push({input: vm.samples.$model[i].input, output: vm.samples.$model[i].output}); + ajaxData.samples.push({ + input: vm.samples.$model[i].input, + output: vm.samples.$model[i].output + }); } $.ajax({ @@ -74,44 +77,11 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "tagE } }); - var testCaseUploader = uploader("#testCaseFile", "/api/admin/test_case_upload/", function (file, response) { - if (response.code) - bsAlert(response.data); - else { - vm.testCaseId = response.data.test_case_id; - vm.uploadSuccess = true; - vm.testCaseList = []; - for (var i = 0; i < response.data.file_list.input.length; i++) { - vm.testCaseList.push({ - input: response.data.file_list.input[i], - output: response.data.file_list.output[i] - }); - } - bsAlert("测试数据添加成功!共添加" + vm.testCaseList.length + "组测试数据"); - } - }); var hintEditor = editor("#hint"); var problemDescription = editor("#problemDescription"); if (avalon.vmodels.addProblem) { var vm = avalon.vmodels.addProblem; - vm.title = ""; - vm.description = ""; - vm.timeLimit = 1000; - vm.memoryLimit = 256; - vm.samples = [{input: "", output: "", "visible": true}]; - vm.hint = ""; - vm.visible = true; - vm.difficulty = 0; - vm.tags = []; - vm.inputDescription = ""; - vm.outputDescription = ""; - vm.testCaseId = ""; - vm.testCaseList = []; - vm.uploadSuccess = false; - vm.source = ""; - hintEditor.setValue(""); - problemDescription.setValue(""); } else var vm = avalon.define({ @@ -119,7 +89,7 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "tagE title: "", description: "", timeLimit: 1000, - memoryLimit: 256, + memoryLimit: 128, samples: [{input: "", output: "", "visible": true}], hint: "", visible: true, @@ -131,6 +101,7 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "tagE testCaseList: [], uploadSuccess: false, source: "", + uploadProgress: 0, addSample: function () { vm.samples.push({input: "", output: "", "visible": true}); }, @@ -149,6 +120,27 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "tagE } }); + var testCaseUploader = uploader("#testCaseFile", "/api/admin/test_case_upload/", + function (file, response) { + if (response.code) + bsAlert(response.data); + else { + vm.testCaseId = response.data.test_case_id; + vm.uploadSuccess = true; + vm.testCaseList = []; + for (var i = 0; i < response.data.file_list.input.length; i++) { + vm.testCaseList.push({ + input: response.data.file_list.input[i], + output: response.data.file_list.output[i] + }); + } + bsAlert("测试数据添加成功!共添加" + vm.testCaseList.length + "组测试数据"); + } + }, + function (file, percentage) { + vm.uploadProgress = percentage; + }); + var tagAutoCompleteList = []; $.ajax({ diff --git a/static/src/js/app/admin/problem/editProblem.js b/static/src/js/app/admin/problem/editProblem.js index 9036178..8784f76 100644 --- a/static/src/js/app/admin/problem/editProblem.js +++ b/static/src/js/app/admin/problem/editProblem.js @@ -5,7 +5,7 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "tagE $("#edit-problem-form").validator() .on('submit', function (e) { - if (!e.isDefaultPrevented()){ + if (!e.isDefaultPrevented()) { if (vm.testCaseId == "") { bsAlert("你还没有上传测试数据!"); return false; @@ -51,7 +51,10 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "tagE }; for (var i = 0; i < vm.samples.$model.length; i++) { - ajaxData.samples.push({input: vm.samples.$model[i].input, output: vm.samples.$model[i].output}); + ajaxData.samples.push({ + input: vm.samples.$model[i].input, + output: vm.samples.$model[i].output + }); } $.ajax({ @@ -75,78 +78,70 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "tagE return false; } }); - if (avalon.vmodels.editProblem) { - var vm = avalon.vmodels.editProblem; - vm.title= "", - vm.description= ""; - vm.timeLimit= -1; - vm.memoryLimit= -1; - vm.samples= []; - vm.hint= ""; - vm.visible= true; - vm.difficulty= 0; - vm.inputDescription= ""; - vm.outputDescription= ""; - vm.testCaseIdd= ""; - vm.uploadSuccess= false; - vm.source= ""; - vm.testCaseList= []; - } - else - var vm = avalon.define({ - $id: "editProblem", - title: "", - description: "", - timeLimit: -1, - memoryLimit: -1, - samples: [], - hint: "", - visible: true, - difficulty: 0, - inputDescription: "", - outputDescription: "", - testCaseIdd: "", - uploadSuccess: false, - source: "", - testCaseList: [], - addSample: function () { - vm.samples.push({input: "", output: "", "visible": true}); - }, - delSample: function (sample) { - if (confirm("你确定要删除么?")) { - vm.samples.remove(sample); + if (avalon.vmodels.editProblem) { + var vm = avalon.vmodels.editProblem; + } + else + var vm = avalon.define({ + $id: "editProblem", + title: "", + description: "", + timeLimit: -1, + memoryLimit: -1, + samples: [], + hint: "", + visible: true, + difficulty: 0, + inputDescription: "", + outputDescription: "", + testCaseIdd: "", + uploadSuccess: false, + source: "", + testCaseList: [], + uploadProgress: 0, + addSample: function () { + vm.samples.push({input: "", output: "", "visible": true}); + }, + delSample: function (sample) { + if (confirm("你确定要删除么?")) { + vm.samples.remove(sample); + } + }, + toggleSample: function (sample) { + sample.visible = !sample.visible; + }, + getBtnContent: function (item) { + if (item.visible) + return "折叠"; + return "展开"; + }, + showProblemListPage: function () { + avalon.vmodels.admin.template_url = "template/problem/problem_list.html"; } - }, - toggleSample: function (sample) { - sample.visible = !sample.visible; - }, - getBtnContent: function (item) { - if (item.visible) - return "折叠"; - return "展开"; - }, - showProblemListPage: function(){ - vm.$fire("up!showProblemListPage"); - } - }); + }); var hintEditor = editor("#hint"); var descriptionEditor = editor("#problemDescription"); - var testCaseUploader = uploader("#testCaseFile", "/api/admin/test_case_upload/", function (file, response) { - if (response.code) - bsAlert(response.data); - else { - vm.testCaseId = response.data.test_case_id; - vm.uploadSuccess = true; - vm.testCaseList = []; - for (var i = 0; i < response.data.file_list.input.length; i++) { - vm.testCaseList.push({ - input: response.data.file_list.input[i], - output: response.data.file_list.output[i] - }); + var testCaseUploader = uploader("#testCaseFile", "/api/admin/test_case_upload/", + function (file, response) { + if (response.code) + bsAlert(response.data); + else { + vm.testCaseId = response.data.test_case_id; + vm.uploadSuccess = true; + vm.testCaseList = []; + for (var i = 0; i < response.data.file_list.input.length; i++) { + vm.testCaseList.push({ + input: response.data.file_list.input[i], + output: response.data.file_list.output[i] + }); + } + bsAlert("测试数据添加成功!共添加" + vm.testCaseList.length + "组测试数据"); } - bsAlert("测试数据添加成功!共添加" + vm.testCaseList.length + "组测试数据"); + }, + function (file, percentage) { + vm.uploadProgress = percentage; } - }); + ); $.ajax({ url: "/api/admin/problem/?problem_id=" + avalon.vmodels.admin.problemId, diff --git a/static/src/js/app/admin/problem/problem.js b/static/src/js/app/admin/problem/problem.js index 9fe56df..53d8a76 100644 --- a/static/src/js/app/admin/problem/problem.js +++ b/static/src/js/app/admin/problem/problem.js @@ -1,54 +1,44 @@ -require(["jquery", "avalon", "csrfToken", "bsAlert"], function ($, avalon, csrfTokenHeader, bsAlert) { +require(["jquery", "avalon", "csrfToken", "bsAlert", "pager"], function ($, avalon, csrfTokenHeader, bsAlert) { avalon.ready(function () { if(avalon.vmodels.problemList){ vm = avalon.vmodels.problemList; - problemList = []; } else { var vm = avalon.define({ $id: "problemList", problemList: [], - previousPage: 0, - nextPage: 0, - page: 1, - totalPage: 1, + keyword: "", showVisibleOnly: false, - getNext: function () { - if (!vm.nextPage) - return; - getPageData(vm.page + 1); - }, - getPrevious: function () { - if (!vm.previousPage) - return; - getPageData(vm.page - 1); - }, - getBtnClass: function (btn) { - if (btn == "next") { - return vm.nextPage ? "btn btn-primary" : "btn btn-primary disabled"; - } - else { - return vm.previousPage ? "btn btn-primary" : "btn btn-primary disabled"; + + pager: { + getPage: function (page) { + getPage(page); } }, - getPage: function (page_index) { - getPageData(page_index); - }, + showEditProblemPage: function (problemId) { - vm.$fire("up!showEditProblemPage", problemId); + avalon.vmodels.admin.problemId = problemId; + avalon.vmodels.admin.template_url = "template/problem/edit_problem.html"; }, showProblemSubmissionPage: function(problemId){ - vm.$fire("up!showProblemSubmissionPage", problemId); + avalon.vmodels.admin.problemId = problemId; + avalon.vmodels.admin.template_url = "template/problem/submission_list.html"; + }, + + search: function(){ + getPage(1); + avalon.vmodels.problemPager.currentPage = 1; } }); vm.$watch("showVisibleOnly", function () { - getPageData(1); + getPage(1); + avalon.vmodels.problemPager.currentPage = 1; }); } - getPageData(1); - function getPageData(page) { + + function getPage(page) { var url = "/api/admin/problem/?paging=true&page=" + page + "&page_size=10"; if (vm.keyword != "") url += "&keyword=" + vm.keyword; @@ -61,10 +51,7 @@ require(["jquery", "avalon", "csrfToken", "bsAlert"], function ($, avalon, csrfT success: function (data) { if (!data.code) { vm.problemList = data.data.results; - vm.totalPage = data.data.total_page; - vm.previousPage = data.data.previous_page; - vm.nextPage = data.data.next_page; - vm.page = page; + avalon.vmodels.problemPager.totalPage = data.data.total_page; } else { bsAlert(data.data); diff --git a/static/src/js/utils/uploader.js b/static/src/js/utils/uploader.js index a4ce038..e6d3817 100644 --- a/static/src/js/utils/uploader.js +++ b/static/src/js/utils/uploader.js @@ -1,6 +1,6 @@ -define("uploader", ["webUploader", "csrfToken"], function(webuploader,csrfTokenHeader){ - function uploader(selector, server, onSuccess, beforeUpload) { - var Webuploader= webuploader.create({ +define("uploader", ["webUploader", "csrfToken"], function (webuploader, csrfTokenHeader) { + function uploader(selector, server, onSuccess, uploadProgress) { + var Webuploader = webuploader.create({ auto: true, // swf文件路径 swf: "/static/img/Uploader.swf", @@ -11,11 +11,16 @@ define("uploader", ["webUploader", "csrfToken"], function(webuploader,csrfTokenH pick: selector, // 不压缩image, 默认如果是jpeg,文件上传前会压缩一把再上传! resize: false, - uploadBeforeSend : csrfTokenHeader + uploadBeforeSend: csrfTokenHeader, + accept: { + title: 'testcase zip', + extensions: 'zip', + mimeTypes: 'application/zip' + } }); - Webuploader.on("uploadBeforeSend",csrfTokenHeader); + Webuploader.on("uploadBeforeSend", csrfTokenHeader); Webuploader.on("uploadSuccess", onSuccess); - Webuploader.on("beforeFileQueued", beforeUpload); + Webuploader.on("uploadProgress", uploadProgress); return Webuploader; } diff --git a/template/src/admin/problem/add_problem.html b/template/src/admin/problem/add_problem.html index 37c48bb..f6016e0 100644 --- a/template/src/admin/problem/add_problem.html +++ b/template/src/admin/problem/add_problem.html @@ -105,6 +105,7 @@ 请将所有测试用例打包在一个文件中上传,所有文件要在压缩包的根目录,且输入输出文件名要以从1开始连续数字标识要对应例如:
    1.in 1.out 2.in 2.out
    +

    上传进度%

    diff --git a/template/src/admin/problem/edit_problem.html b/template/src/admin/problem/edit_problem.html index d7c9743..d15f58f 100644 --- a/template/src/admin/problem/edit_problem.html +++ b/template/src/admin/problem/edit_problem.html @@ -111,6 +111,7 @@ 请将所有测试用例打包在一个文件中上传,所有文件要在压缩包的根目录,且输入输出文件名要以从1开始连续数字标识要对应例如:
    1.in 1.out 2.in 2.out
    +

    上传进度%

    编号
    diff --git a/template/src/admin/problem/problem_list.html b/template/src/admin/problem/problem_list.html index 284d2da..3fa5ac5 100644 --- a/template/src/admin/problem/problem_list.html +++ b/template/src/admin/problem/problem_list.html @@ -6,7 +6,7 @@
    - +

    @@ -38,9 +38,7 @@
    - 页数:{{ page }}/{{ totalPage }}   - - +
    \ No newline at end of file diff --git a/template/src/oj/submission/my_submission.html b/template/src/oj/submission/my_submission.html index bd348e9..9611cd8 100644 --- a/template/src/oj/submission/my_submission.html +++ b/template/src/oj/submission/my_submission.html @@ -36,7 +36,7 @@ {% include "oj/problem/_problem_header.html" %}
    -

    运行结果 : +

    运行结果: {{ submission.result|translate_result }}

    From 4342c91985b335b8919c802e64a41511122aafdb Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Sat, 17 Oct 2015 20:00:40 +0800 Subject: [PATCH 21/25] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=AF=B9=E8=A7=A3?= =?UTF-8?q?=E5=8E=8B=E5=A4=B1=E8=B4=A5=E7=9A=84=E6=8F=90=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problem/views.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/problem/views.py b/problem/views.py index 3b68bae..e48b8a5 100644 --- a/problem/views.py +++ b/problem/views.py @@ -183,8 +183,10 @@ class TestCaseUploadAPIView(APIView): except IOError as e: logger.error(e) return error_response(u"上传失败") - - test_case_file = zipfile.ZipFile(tmp_zip, 'r') + try: + test_case_file = zipfile.ZipFile(tmp_zip, 'r') + except Exception: + return error_response(u"解压失败") name_list = test_case_file.namelist() l = [] From bd61b5404810b2b15152ba302940d2c5b913c489 Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Sun, 18 Oct 2015 11:12:22 +0800 Subject: [PATCH 22/25] =?UTF-8?q?=E5=B0=81=E8=A3=85=20simditor=20=E4=B8=BA?= =?UTF-8?q?=20web=20=E7=BB=84=E5=BB=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- static/src/js/components/editorComponent.js | 16 ++++++++++++++++ static/src/js/config.js | 1 + 2 files changed, 17 insertions(+) create mode 100644 static/src/js/components/editorComponent.js diff --git a/static/src/js/components/editorComponent.js b/static/src/js/components/editorComponent.js new file mode 100644 index 0000000..706f289 --- /dev/null +++ b/static/src/js/components/editorComponent.js @@ -0,0 +1,16 @@ +define("editorComponent", ["jquery", "avalon", "editor"], function ($, avalon, editor) { + avalon.component("ms:editor", { + $template: "", + editorId: "editorId", + content: "", + placeholder: "", + $editor: {}, + $ready: function (vm, el) { + el.msRetain = true; + vm.$editor = editor($("#" + vm.editorId)); + vm.$watch("content", function (oldValue, newValue) { + vm.$editor.setValue(oldValue); + }) + } + }) +}); diff --git a/static/src/js/config.js b/static/src/js/config.js index 56b4b8a..2e744a5 100644 --- a/static/src/js/config.js +++ b/static/src/js/config.js @@ -26,6 +26,7 @@ var require = { // ------ admin web 组件 ---------- pager: "components/pager", + editorComponent: "components/editorComponent", // ------ 下面写的都不要直接用,而是使用上面的封装版本 ------ From 50880e1f368355f7e4a66d29dda36db547ab5a2d Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Sun, 18 Oct 2015 11:12:53 +0800 Subject: [PATCH 23/25] =?UTF-8?q?=E5=8D=87=E7=BA=A7=20avalon=EF=BC=8C?= =?UTF-8?q?=E4=BF=AE=E5=A4=8D=20select=20=E6=97=A0=E6=B3=95=E5=8F=8C?= =?UTF-8?q?=E5=90=91=E5=90=8C=E6=AD=A5=E7=9A=84=20bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- static/src/js/lib/avalon/avalon.js | 146 +++++++++++++++++++---------- 1 file changed, 96 insertions(+), 50 deletions(-) diff --git a/static/src/js/lib/avalon/avalon.js b/static/src/js/lib/avalon/avalon.js index bb809d5..48c2021 100644 --- a/static/src/js/lib/avalon/avalon.js +++ b/static/src/js/lib/avalon/avalon.js @@ -3,9 +3,9 @@ http://www.cnblogs.com/rubylouvre/ https://github.com/RubyLouvre http://weibo.com/jslouvre/ - + Released under the MIT license - avalon.shim.js 1.5.4 built in 2015.10.15 + avalon.shim.js 1.5.4 built in 2015.10.18 support IE6+ and other browsers ==================================================*/ (function(global, factory) { @@ -954,7 +954,7 @@ function $watch(expr, binding) { } binding.wildcard = /\*/.test(expr) } - + if (!binding.update) { if (/\w\.*\B/.test(expr)) { binding.getter = noop @@ -1489,7 +1489,7 @@ if (!canHideOwn) { //添加普通属性,因为VBScript对象不能像JS那样随意增删属性,必须在这里预先定义好 var uniq = {} - //添加访问器属性 + //添加访问器属性 for (name in accessors) { uniq[name] = true buffer.push( @@ -1593,7 +1593,7 @@ var newProto = { if (all.indexOf(this[i]) !== -1) { _splice.call(this.$track, i, 1) _splice.call(this, i, 1) - + } } } else if (typeof all === "function") { @@ -1602,7 +1602,7 @@ var newProto = { if (all(el, i)) { _splice.call(this.$track, i, 1) _splice.call(this, i, 1) - + } } } else { @@ -2517,7 +2517,7 @@ function showHidden(node, array) { var node = this[0] if (arguments.length === 0) { if (node.setTimeout) { //取得窗口尺寸 - return node["inner" + name] || + return node["inner" + name] || node.document.documentElement[clientProp] || node.document.body[clientProp] //IE6下前两个分别为undefined,0 } @@ -2972,7 +2972,7 @@ function executeBindings(bindings, vmodels) { for (var i = 0, binding; binding = bindings[i++]; ) { binding.vmodels = vmodels directives[binding.type].init(binding) - + avalon.injectBinding(binding) if (binding.getter && binding.element.nodeType === 1) { //移除数据绑定,防止被二次解析 //chrome使用removeAttributeNode移除不存在的特性节点时会报错 https://github.com/RubyLouvre/avalon/issues/99 @@ -3169,9 +3169,6 @@ function scanNodeArray(nodes, vmodels) { switch (node.nodeType) { case 1: var elem = node, fn - - scanTag(node, vmodels) //扫描元素节点 - if (!elem.msResolved && elem.parentNode && elem.parentNode.nodeType === 1) { var library = isWidget(elem) if (library) { @@ -3190,6 +3187,7 @@ function scanNodeArray(nodes, vmodels) { } } } + scanTag(node, vmodels) //扫描元素节点 if (node.msHasEvent) { avalon.fireDom(node, "datasetchanged", { bubble: node.msHasEvent @@ -3232,7 +3230,7 @@ function scanTag(elem, vmodels, node) { avalon(elem).removeClass(name) createSignalTower(elem, newVmodel) } - + scanAttr(elem, vmodels) //扫描特性节点 } @@ -3424,6 +3422,7 @@ avalon.component = function (name, opts) { elem.msResolved = 1 vmodel.$init(vmodel, elem) global.$init(vmodel, elem) + console.log("init") var nodes = elem.childNodes //收集插入点 var slots = {}, snode @@ -3483,11 +3482,11 @@ avalon.component = function (name, opts) { e.stopPropagation() } } - + console.log("dependencies "+dependencies) if (dependencies === 0) { var id1 = setTimeout(function () { clearTimeout(id1) - + vmodel.$ready(vmodel, elem, host.vmodels) global.$ready(vmodel, elem, host.vmodels) }, children ? Math.max(children * 17, 100) : 17) @@ -3510,9 +3509,10 @@ avalon.component = function (name, opts) { } }) - scanTag(elem, [vmodel].concat(host.vmodels)) + scanTag(elem, [vmodel].concat(host.vmodels)) avalon.vmodels[vmodel.$id] = vmodel + avalon.log("添加组件VM: "+vmodel.$id) if (!elem.childNodes.length) { avalon.fireDom(elem, "datasetchanged", {library: library, vm: vmodel, childReady: -1}) } else { @@ -3585,7 +3585,7 @@ function isWidget(el) { //如果为自定义标签,返回UI库的名字 if(el.scopeName && el.scopeName !== "HTML" ){ return el.scopeName } - var fullName = el.nodeName.toLowerCase() + var fullName = el.nodeName.toLowerCase() var index = fullName.indexOf(":") if (index > 0) { return fullName.slice(0, index) @@ -3794,7 +3794,7 @@ avalon.directive("data", { //双工绑定 var rduplexType = /^(?:checkbox|radio)$/ var rduplexParam = /^(?:radio|checked)$/ -var rnoduplexInput = /^(file|button|reset|submit|checkbox|radio)$/ +var rnoduplexInput = /^(file|button|reset|submit|checkbox|radio|range)$/ var duplexBinding = avalon.directive("duplex", { priority: 2000, init: function (binding, hasCast) { @@ -3949,15 +3949,26 @@ var duplexBinding = avalon.directive("duplex", { } } }) + binding.bound("datasetchanged", function (e) { + if (e.bubble === "selectDuplex") { + var value = binding._value + var curValue = Array.isArray(value) ? value.map(String) : value + "" + avalon(elem).val(curValue) + elem.oldValue = curValue + "" + binding.changed.call(elem, curValue) + } + }) break } - binding.bound("focus", function() { - elem.msFocus = true - }) - binding.bound("blur", function() { - elem.msFocus = false - }) if (binding.xtype === "input" && !rnoduplexInput.test(elem.type)) { + if (elem.type !== "hidden") { + binding.bound("focus", function () { + elem.msFocus = true + }) + binding.bound("blur", function () { + elem.msFocus = false + }) + } elem.avalonSetter = updateVModel //#765 watchValueInTimer(function () { if (elem.contains(elem)) { @@ -3987,7 +3998,18 @@ var duplexBinding = avalon.directive("duplex", { case "change": curValue = this.pipe(value, this, "set") //fix #673 if (curValue !== this.oldValue) { + var fixCaret = false + if (elem.msFocus) { + var pos = getCaret(elem) + if (pos.start === pos.end) { + pos = pos.start + fixCaret = true + } + } elem.value = this.oldValue = curValue + if (fixCaret) { + setCaret(element, pos, pos) + } } break case "radio": @@ -4012,18 +4034,14 @@ var duplexBinding = avalon.directive("duplex", { case "select": //必须变成字符串后才能比较 binding._value = value - elem.msHasEvent = "selectDuplex" - //必须等到其孩子准备好才触发 - avalon.bind(elem, "datasetchanged", function (e) { - if (e.bubble === "selectDuplex") { - var value = binding._value - var curValue = Array.isArray(value) ? value.map(String) : value + "" - avalon(elem).val(curValue) - elem.oldValue = curValue + "" - binding.changed.call(elem, curValue) - } - }) - + if(!elem.msHasEvent){ + elem.msHasEvent = "selectDuplex" + //必须等到其孩子准备好才触发 + }else{ + avalon.fireDom(elem, "datasetchanged", { + bubble: elem.msHasEvent + }) + } break } if (binding.xtype !== "select") { @@ -4121,7 +4139,7 @@ new function () { // jshint ignore:line var bproto = HTMLTextAreaElement.prototype function newSetter(value) { // jshint ignore:line setters[this.tagName].call(this, value) - if (!this.msFocus && this.avalonSetter && this.oldValue !== value) { + if (!this.msFocus && this.avalonSetter && this.oldValue !== value) { this.avalonSetter() } } @@ -4143,6 +4161,34 @@ new function () { // jshint ignore:line watchValueInTimer = avalon.tick } } // jshint ignore:line +function getCaret(ctrl, start, end) { + if (ctrl.setSelectionRange) { + start = ctrl.selectionStart + end = ctrl.selectionEnd + } else if (document.selection && document.selection.createRange) { + var range = document.selection.createRange() + start = 0 - range.duplicate().moveStart('character', -100000) + end = start + range.text.length + } + return { + start: start, + end: end + } +} +function setCaret(ctrl, begin, end) { + if (!ctrl.value || ctrl.readOnly) + return + if (ctrl.setSelectionRange) { + ctrl.selectionStart = begin + ctrl.selectionEnd = end + } else { + var range = ctrl.createTextRange() + range.collapse(true); + range.moveStart("character", begin) + range.moveEnd("character", end - begin) + range.select() + } +} avalon.directive("effect", { priority: 5, @@ -4326,7 +4372,7 @@ function upperFirstChar(str) { } var effectBuffer = new Buffer() function Effect() { -}// 动画实例,做成类的形式,是为了共用所有原型方法 +}// 动画实例,做成类的形式,是为了共用所有原型方法 Effect.prototype = { contrustor: Effect, @@ -4349,7 +4395,7 @@ Effect.prototype = { callEffectHook(me, "abort" + upperFirstChar(oppositeName)) callEffectHook(me, "before" + upperFirstChar(name)) if (!isLeave) - before(el) //  这里可能做插入DOM树的操作,因此必须在修改类名前执行 + before(el) // 这里可能做插入DOM树的操作,因此必须在修改类名前执行 var cssCallback = function (cancel) { el.removeEventListener(me.cssEvent, me.cssCallback) if (isLeave) { @@ -4576,7 +4622,7 @@ avalon.directive("if", { elem.required = false elem.setAttribute("_required", "true") } - try {// 如果不支持querySelectorAll或:required,可以直接无视 + try {// 如果不支持querySelectorAll或:required,可以直接无视 avalon.each(elem.querySelectorAll(":required"), function (el) { elem.required = false el.setAttribute("_required", "true") @@ -4679,7 +4725,7 @@ avalon.directive("include", { leaveEl.className = effectClass target.insertBefore(leaveEl, binding.start) // 插入到start之前,防止被错误的移动 } - + // cache or animate,移动节点 (templateCache || {})[lastID] = leaveEl var fragOnDom = binding.recoverNodes() // 恢复动画中的节点 @@ -4733,7 +4779,7 @@ avalon.directive("include", { return nodesToFrag(nodes) } } else { - before = function () {// 新添加元素的动画  + before = function () {// 新添加元素的动画 target.insertBefore(fragment, binding.end) scanNodeArray(nodes, vmodels) } @@ -4827,7 +4873,7 @@ var onDir = avalon.directive("on", { var fn = binding.getter || noop return fn.apply(this, binding.args.concat(e)) } - + var eventType = binding.param.replace(/-\d+$/, "") // ms-on-mousemove-10 if (eventType === "scan") { callback.call(elem, { @@ -4860,7 +4906,7 @@ avalon.directive("repeat", { effectBinding(elem, binding) binding.param = binding.param || "el" binding.sortedCallback = getBindingCallback(elem, "data-with-sorted", binding.vmodels) - // binding.renderedCallback = + // binding.renderedCallback = var rendered = getBindingCallback(elem, "data-" + type + "-rendered", binding.vmodels) var signature = generateID(type) @@ -4965,7 +5011,7 @@ avalon.directive("repeat", { } //重写proxy - if (this.enterCount === 1) {// 防止多次进入,导致位置不对 + if (this.enterCount === 1) {// 防止多次进入,导致位置不对 proxy.$active = false proxy.$oldIndex = proxy.$index proxy.$active = true @@ -4978,7 +5024,7 @@ avalon.directive("repeat", { proxy.$last = i === length - 1 // proxy[param] = value[i] } else { - proxy.$val = toJson(value[keyOrId]) // 这里是处理vm.object = newObject的情况 + proxy.$val = toJson(value[keyOrId]) // 这里是处理vm.object = newObject的情况 } proxies.push(proxy) } @@ -5029,7 +5075,7 @@ avalon.directive("repeat", { } else if (proxy.$index !== proxy.$oldIndex) { (function (proxy2, preElement) { staggerIndex = mayStaggerAnimate(binding.effectEnterStagger, function () { - var curNode = removeItem(proxy2.$anchor)// 如果位置被挪动了 + var curNode = removeItem(proxy2.$anchor)// 如果位置被挪动了 var inserted = avalon.slice(curNode.childNodes) parent.insertBefore(curNode, preElement.nextSibling) animateRepeat(inserted, 1, binding) @@ -5410,7 +5456,7 @@ var filters = avalon.filters = { truncate: function(str, length, truncation) { //length,新字符串长度,truncation,新字符串的结尾的字段,返回新字符串 length = length || 30 - truncation = typeof truncation === "string" ? truncation : "..." + truncation = typeof truncation === "string" ? truncation : "..." return str.length > length ? str.slice(0, length - truncation.length) + truncation : String(str) }, $filter: function(val) { @@ -5426,7 +5472,7 @@ var filters = avalon.filters = { }, camelize: camelize, //https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet - // chrome + // chrome // chrome // IE67chrome // IE67chrome @@ -5447,7 +5493,7 @@ var filters = avalon.filters = { }) }, escape: function(str) { - //将字符串经过 str 转义得到适合在页面中显示的内容, 例如替换 < 为 < + //将字符串经过 str 转义得到适合在页面中显示的内容, 例如替换 < 为 < return String(str). replace(/&/g, '&'). replace(rsurrogate, function(value) { @@ -5489,7 +5535,7 @@ var filters = avalon.filters = { 'a': am/pm marker 'Z': 4 digit (+sign) representation of the timezone offset (-1200-+1200) format string can also be one of the following predefined localizable formats: - + 'medium': equivalent to 'MMM d, y h:mm:ss a' for en_US locale (e.g. Sep 3, 2010 12:05:08 pm) 'short': equivalent to 'M/d/yy h:mm a' for en_US locale (e.g. 9/3/10 12:05 pm) 'fullDate': equivalent to 'EEEE, MMMM d,y' for en_US locale (e.g. Friday, September 3, 2010) From ad73a36944ca6be5b1e7d002a78e541ca1fd9ffa Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Sun, 18 Oct 2015 11:13:44 +0800 Subject: [PATCH 24/25] =?UTF-8?q?=E5=90=8E=E5=8F=B0=E4=BD=BF=E7=94=A8?= =?UTF-8?q?=E5=B0=81=E8=A3=85=E5=A5=BD=E7=9A=84=20web=20=E7=BB=84=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/app/admin/announcement/announcement.js | 27 +++++--- static/src/js/app/admin/problem/addProblem.js | 29 +++++--- .../src/js/app/admin/problem/editProblem.js | 34 ++++++---- .../js/app/admin/problem/submissionList.js | 68 +++++++------------ static/src/js/utils/editor.js | 2 +- .../src/admin/announcement/announcement.html | 6 +- template/src/admin/problem/add_problem.html | 5 +- template/src/admin/problem/edit_problem.html | 5 +- .../src/admin/problem/submission_list.html | 4 +- 9 files changed, 86 insertions(+), 94 deletions(-) diff --git a/static/src/js/app/admin/announcement/announcement.js b/static/src/js/app/admin/announcement/announcement.js index debb594..c10741e 100644 --- a/static/src/js/app/admin/announcement/announcement.js +++ b/static/src/js/app/admin/announcement/announcement.js @@ -1,10 +1,7 @@ -require(["jquery", "avalon", "csrfToken", "bsAlert", "editor", "validator", "pager"], +require(["jquery", "avalon", "csrfToken", "bsAlert", "validator", "pager", "editorComponent"], function ($, avalon, csrfTokenHeader, bsAlert, editor) { avalon.ready(function () { - var createAnnouncementEditor = editor("#create-announcement-editor"); - var editAnnouncementEditor = editor("#edit-announcement-editor"); - if (avalon.vmodels.announcement){ var vm = avalon.vmodels.announcement; } @@ -25,20 +22,30 @@ require(["jquery", "avalon", "csrfToken", "bsAlert", "editor", "validator", "pag getPage(page); } }, + + createAnnouncementEditor: { + editorId: "create-announcement-editor", + placeholder: "公告内容" + }, + + editAnnouncementEditor: { + editorId: "edit-announcement-editor", + placeholder: "公告内容" + }, + editAnnouncement: function (announcement) { vm.newTitle = announcement.title; vm.announcementId = announcement.id; - editAnnouncementEditor.setValue(announcement.content); + avalon.vmodels.editAnnouncementEditor.content = announcement.content; vm.announcementVisible = announcement.visible; vm.isEditing = true; - editAnnouncementEditor.focus(); }, cancelEdit: function () { vm.isEditing = false; }, submitChange: function () { var title = vm.newTitle; - var content = editAnnouncementEditor.getValue(); + var content = avalon.vmodels.editAnnouncementEditor.content; if (content == "" || title == "") { bsAlert("标题和内容都不能为空"); @@ -60,7 +67,6 @@ require(["jquery", "avalon", "csrfToken", "bsAlert", "editor", "validator", "pag if (!data.code) { bsAlert("修改成功"); vm.isEditing = false; - localStorage.removeItem("/admin/autosave/edit-announcement-editor/"); getPage(1); } else { @@ -101,7 +107,7 @@ require(["jquery", "avalon", "csrfToken", "bsAlert", "editor", "validator", "pag $("#announcement-form").validator().on('submit', function (e) { if (!e.isDefaultPrevented()) { var title = $("#title").val(); - var content = createAnnouncementEditor.getValue(); + var content = avalon.vmodels.createAnnouncementEditor.content; if (content == "") { bsAlert("请填写公告内容"); return false; @@ -119,8 +125,7 @@ require(["jquery", "avalon", "csrfToken", "bsAlert", "editor", "validator", "pag if (!data.code) { bsAlert("提交成功!"); $("#title").val(""); - createAnnouncementEditor.setValue(""); - localStorage.removeItem("/admin/autosave/create-announcement-editor/"); + avalon.vmodels.createAnnouncementEditor.content = ""; getPage(1); } else { bsAlert(data.data); diff --git a/static/src/js/app/admin/problem/addProblem.js b/static/src/js/app/admin/problem/addProblem.js index 391f70f..61cb940 100644 --- a/static/src/js/app/admin/problem/addProblem.js +++ b/static/src/js/app/admin/problem/addProblem.js @@ -1,4 +1,4 @@ -require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "tagEditor", "validator", "jqueryUI"], +require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "tagEditor", "validator", "jqueryUI", "editorComponent"], function ($, avalon, editor, uploader, bsAlert, csrfTokenHeader) { avalon.ready(function () { @@ -9,7 +9,7 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "tagE bsAlert("你还没有上传测试数据!"); return false; } - if (vm.description == "") { + if (avalon.vmodels.problemDescriptionEditor.content == "") { bsAlert("题目描述不能为空!"); return false; } @@ -35,12 +35,12 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "tagE var ajaxData = { id: avalon.vmodels.admin.problemId, title: vm.title, - description: vm.description, + description: avalon.vmodels.problemDescriptionEditor.content, time_limit: vm.timeLimit, memory_limit: vm.memoryLimit, samples: [], test_case_id: vm.testCaseId, - hint: vm.hint, + hint: avalon.vmodels.problemHintEditor.content, source: vm.source, visible: vm.visible, tags: tags, @@ -77,9 +77,6 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "tagE } }); - - var hintEditor = editor("#hint"); - var problemDescription = editor("#problemDescription"); if (avalon.vmodels.addProblem) { var vm = avalon.vmodels.addProblem; } @@ -87,13 +84,11 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "tagE var vm = avalon.define({ $id: "addProblem", title: "", - description: "", timeLimit: 1000, memoryLimit: 128, samples: [{input: "", output: "", "visible": true}], - hint: "", visible: true, - difficulty: 0, + difficulty: "1", tags: [], inputDescription: "", outputDescription: "", @@ -102,6 +97,16 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "tagE uploadSuccess: false, source: "", uploadProgress: 0, + + problemDescriptionEditor: { + editorId: "problem-description-editor", + placeholder: "题目描述" + }, + problemHintEditor: { + editorId: "problem-hint-editor", + placeholder: "提示" + }, + addSample: function () { vm.samples.push({input: "", output: "", "visible": true}); }, @@ -122,8 +127,10 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "tagE var testCaseUploader = uploader("#testCaseFile", "/api/admin/test_case_upload/", function (file, response) { - if (response.code) + if (response.code) { + vm.uploadProgress = 0; bsAlert(response.data); + } else { vm.testCaseId = response.data.test_case_id; vm.uploadSuccess = true; diff --git a/static/src/js/app/admin/problem/editProblem.js b/static/src/js/app/admin/problem/editProblem.js index 8784f76..4b936e0 100644 --- a/static/src/js/app/admin/problem/editProblem.js +++ b/static/src/js/app/admin/problem/editProblem.js @@ -1,4 +1,4 @@ -require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "tagEditor", "validator", "jqueryUI"], +require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "tagEditor", "validator", "jqueryUI", "editorComponent"], function ($, avalon, editor, uploader, bsAlert, csrfTokenHeader) { avalon.ready(function () { @@ -10,7 +10,7 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "tagE bsAlert("你还没有上传测试数据!"); return false; } - if (vm.description == "") { + if (avalon.vmodels.problemDescriptionEditor.content == "") { bsAlert("题目描述不能为空!"); return false; } @@ -36,12 +36,12 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "tagE var ajaxData = { id: avalon.vmodels.admin.problemId, title: vm.title, - description: vm.description, + description: avalon.vmodels.problemDescriptionEditor.content, time_limit: vm.timeLimit, memory_limit: vm.memoryLimit, samples: [], test_case_id: vm.testCaseId, - hint: vm.hint, + hint: avalon.vmodels.problemHintEditor.content, source: vm.source, visible: vm.visible, tags: tags, @@ -85,13 +85,11 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "tagE var vm = avalon.define({ $id: "editProblem", title: "", - description: "", timeLimit: -1, memoryLimit: -1, samples: [], - hint: "", visible: true, - difficulty: 0, + difficulty: "1", inputDescription: "", outputDescription: "", testCaseIdd: "", @@ -99,6 +97,16 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "tagE source: "", testCaseList: [], uploadProgress: 0, + + problemDescriptionEditor: { + editorId: "problem-description-editor", + placeholder: "题目描述" + }, + problemHintEditor: { + editorId: "problem-hint-editor", + placeholder: "提示" + }, + addSample: function () { vm.samples.push({input: "", output: "", "visible": true}); }, @@ -119,12 +127,12 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "tagE avalon.vmodels.admin.template_url = "template/problem/problem_list.html"; } }); - var hintEditor = editor("#hint"); - var descriptionEditor = editor("#problemDescription"); var testCaseUploader = uploader("#testCaseFile", "/api/admin/test_case_upload/", function (file, response) { - if (response.code) + if (response.code) { + vm.uploadProgress = 0; bsAlert(response.data); + } else { vm.testCaseId = response.data.test_case_id; vm.uploadSuccess = true; @@ -154,7 +162,7 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "tagE else { var problem = data.data; vm.title = problem.title; - vm.description = problem.description; + avalon.vmodels.problemDescriptionEditor.content = problem.description; vm.timeLimit = problem.time_limit; vm.memoryLimit = problem.memory_limit; for (var i = 0; i < problem.samples.length; i++) { @@ -164,7 +172,7 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "tagE visible: false }) } - vm.hint = problem.hint; + avalon.vmodels.problemHintEditor.content = problem.hint; vm.visible = problem.visible; vm.difficulty = problem.difficulty; vm.inputDescription = problem.input_description; @@ -172,8 +180,6 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "tagE vm.testCaseId = problem.test_case_id; vm.source = problem.source; var problemTags = problem.tags; - hintEditor.setValue(vm.hint); - descriptionEditor.setValue(vm.description); $.ajax({ url: "/api/admin/tag/", dataType: "json", diff --git a/static/src/js/app/admin/problem/submissionList.js b/static/src/js/app/admin/problem/submissionList.js index e8d00bb..98c228e 100644 --- a/static/src/js/app/admin/problem/submissionList.js +++ b/static/src/js/app/admin/problem/submissionList.js @@ -1,12 +1,11 @@ -require(["jquery", "avalon", "csrfToken", "bsAlert"], function ($, avalon, csrfTokenHeader, bsAlert) { +require(["jquery", "avalon", "csrfToken", "bsAlert", "pager"], function ($, avalon, csrfTokenHeader, bsAlert) { avalon.ready(function () { - if (avalon.vmodels.submissionList){ + if (avalon.vmodels.submissionList) { var vm = avalon.vmodels.submissionList; } else { - var vm = avalon.define({ $id: "submissionList", submissionList: [], @@ -14,65 +13,43 @@ require(["jquery", "avalon", "csrfToken", "bsAlert"], function ($, avalon, csrfT nextPage: 0, page: 1, totalPage: 1, - results : { - 0: "Accepted", - 1: "Runtime Error", - 2: "Time Limit Exceeded", - 3: "Memory Limit Exceeded", - 4: "Compile Error", - 5: "Format Error", - 6: "Wrong Answer", - 7: "System Error", - 8: "Waiting" - }, - getNext: function () { - if (!vm.nextPage) - return; - getPageData(vm.page + 1); + results: { + 0: "Accepted", + 1: "Runtime Error", + 2: "Time Limit Exceeded", + 3: "Memory Limit Exceeded", + 4: "Compile Error", + 5: "Format Error", + 6: "Wrong Answer", + 7: "System Error", + 8: "Waiting" }, - getPrevious: function () { - if (!vm.previousPage) - return; - getPageData(vm.page - 1); - }, - getBtnClass: function (btn) { - if (btn == "next") { - return vm.nextPage ? "btn btn-primary" : "btn btn-primary disabled"; - } - else { - return vm.previousPage ? "btn btn-primary" : "btn btn-primary disabled"; + pager: { + getPage: function (page) { + getPage(page); } }, - getPage: function (page_index) { - getPageData(page_index); + showProblemListPage: function () { + avalon.vmodels.admin.template_url = "template/problem/problem_list.html"; }, - showSubmissionDetailPage: function (submissionId) { - }, - showProblemListPage: function(){ - vm.$fire("up!showProblemListPage"); - }, - rejudge: function(submission_id){ + rejudge: function (submission_id) { $.ajax({ - beforeSend: csrfTokenHeader, url: "/api/admin/rejudge/", method: "post", data: {"submission_id": submission_id}, - success: function(data){ - if(!data.code){ + success: function (data) { + if (!data.code) { bsAlert("重判任务提交成功"); } } - }) } }); } - getPageData(1); - - function getPageData(page) { - var url = "/api/admin/submission/?paging=true&page=" + page + "&page_size=10&problem_id=" + avalon.vmodels.admin.problemId; + function getPage(page) { + var url = "/api/admin/submission/?paging=true&page=" + page + "&page_size=20&problem_id=" + avalon.vmodels.admin.problemId; $.ajax({ url: url, dataType: "json", @@ -84,6 +61,7 @@ require(["jquery", "avalon", "csrfToken", "bsAlert"], function ($, avalon, csrfT vm.previousPage = data.data.previous_page; vm.nextPage = data.data.next_page; vm.page = page; + avalon.vmodels.submissionsListPager.totalPage = data.data.total_page; } else { bsAlert(data.data); diff --git a/static/src/js/utils/editor.js b/static/src/js/utils/editor.js index e332008..53a7d55 100644 --- a/static/src/js/utils/editor.js +++ b/static/src/js/utils/editor.js @@ -1,4 +1,4 @@ -define("editor", ["simditor", "simditor-autosave"], function(Simditor){ +define("editor", ["simditor"], function(Simditor){ function editor(selector){ return new Simditor({ textarea: $(selector), diff --git a/template/src/admin/announcement/announcement.html b/template/src/admin/announcement/announcement.html index 24990ce..ab19406 100644 --- a/template/src/admin/announcement/announcement.html +++ b/template/src/admin/announcement/announcement.html @@ -38,7 +38,7 @@ ms-duplex="newTitle">

    - +
    @@ -62,8 +62,8 @@
    - + +
    diff --git a/template/src/admin/problem/add_problem.html b/template/src/admin/problem/add_problem.html index f6016e0..0a706ad 100644 --- a/template/src/admin/problem/add_problem.html +++ b/template/src/admin/problem/add_problem.html @@ -10,8 +10,7 @@
    - -

    请填写题目描述

    +
    @@ -126,7 +125,7 @@
    - +
    diff --git a/template/src/admin/problem/edit_problem.html b/template/src/admin/problem/edit_problem.html index d15f58f..b8a6fa8 100644 --- a/template/src/admin/problem/edit_problem.html +++ b/template/src/admin/problem/edit_problem.html @@ -16,8 +16,7 @@
    - -

    请填写题目描述

    +
    @@ -132,7 +131,7 @@
    - +
    diff --git a/template/src/admin/problem/submission_list.html b/template/src/admin/problem/submission_list.html index e0880af..8c0840a 100644 --- a/template/src/admin/problem/submission_list.html +++ b/template/src/admin/problem/submission_list.html @@ -27,9 +27,7 @@
    编号
    - 页数:{{ page }}/{{ totalPage }}   - - +
\ No newline at end of file From 9089ad15ddaca5a59870b3d7734d496e55efece4 Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Sun, 18 Oct 2015 11:45:06 +0800 Subject: [PATCH 25/25] =?UTF-8?q?=E6=95=B4=E7=90=86=E6=A8=A1=E5=9D=97?= =?UTF-8?q?=EF=BC=8C=E9=83=A8=E5=88=86=E5=8A=9F=E8=83=BD=E5=8F=AA=E6=9C=89?= =?UTF-8?q?=20api?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- contest/decorators.py | 2 + contest/tests.py | 37 +++++- contest/views.py | 93 ++++++++++++- oj/settings.py | 3 +- oj/urls.py | 14 +- submission/serializers.py | 7 + submission/tests.py | 132 ++++++++++++++++++- submission/views.py | 118 ++++++++++++----- template/src/oj/account/oauth.html | 24 ++++ template/src/utils/reset_password_email.html | 4 +- 10 files changed, 382 insertions(+), 52 deletions(-) create mode 100644 template/src/oj/account/oauth.html diff --git a/contest/decorators.py b/contest/decorators.py index a016032..d08783c 100644 --- a/contest/decorators.py +++ b/contest/decorators.py @@ -37,6 +37,8 @@ def check_user_contest_permission(func): contest_id = kwargs["contest_id"] elif "contest_id" in request.data: contest_id = request.data["contest_id"] + elif "contest_id" in request.GET: + contest_id = request.GET["contest_id"] else: if request.is_ajax(): return error_response(u"参数错误") diff --git a/contest/tests.py b/contest/tests.py index 8875997..51fca4f 100644 --- a/contest/tests.py +++ b/contest/tests.py @@ -8,8 +8,7 @@ from rest_framework.test import APITestCase, APIClient from account.models import User from group.models import Group from contest.models import Contest, ContestProblem -from .models import ContestSubmission -from .models import GROUP_CONTEST, PASSWORD_PROTECTED_CONTEST +from .models import GROUP_CONTEST, PASSWORD_PROTECTED_CONTEST, PUBLIC_CONTEST from account.models import REGULAR_USER, ADMIN, SUPER_ADMIN @@ -582,5 +581,39 @@ class ContestListPageTest(TestCase): self.assertEqual(response.status_code, 200) +class ContestProblemMySubmissionListTest(TestCase): + # 以下是我比赛单个题目的提交列表的测试 + def setUp(self): + self.client = Client() + self.user1 = User.objects.create(username="test1", admin_type=SUPER_ADMIN) + self.user1.set_password("testaa") + self.user1.save() + self.user2 = User.objects.create(username="test2", admin_type=REGULAR_USER) + self.user2.set_password("testbb") + self.user2.save() + self.global_contest = Contest.objects.create(title="titlex", description="descriptionx", mode=1, + contest_type=PUBLIC_CONTEST, show_rank=True, + show_user_submission=True, + start_time="2015-08-15T10:00:00.000Z", + end_time="2015-08-30T12:00:00.000Z", + created_by=User.objects.get(username="test1")) + self.contest_problem = ContestProblem.objects.create(title="titlex", + description="descriptionx", + input_description="input1_description", + output_description="output1_description", + test_case_id="1", + samples=json.dumps([{"input": "1 1", "output": "2"}]), + time_limit=100, + memory_limit=1000, + hint="hint1", + created_by=self.user1, + contest=self.global_contest, + sort_index="a") + + def test_contestsList_page_not_exist(self): + self.client.login(username="test1", password="testaa") + response = self.client.get('/contest/1/submissions/999/') + self.assertTemplateUsed(response, "utils/error.html") + diff --git a/contest/views.py b/contest/views.py index 191e54a..d9eee7b 100644 --- a/contest/views.py +++ b/contest/views.py @@ -19,6 +19,7 @@ from account.models import SUPER_ADMIN, User from account.decorators import login_required from group.models import Group from utils.cache import get_cache_redis +from submission.models import Submission from .models import (Contest, ContestProblem, CONTEST_ENDED, CONTEST_NOT_START, CONTEST_UNDERWAY, ContestRank) from .models import GROUP_CONTEST, PUBLIC_CONTEST, PASSWORD_PROTECTED_CONTEST @@ -444,4 +445,94 @@ class ContestTimeAPIView(APIView): return error_response(u"比赛不存在") return success_response({"start": int((contest.start_time - now()).total_seconds() * 1000), "end": int((contest.end_time - now()).total_seconds() * 1000), - "status": contest.status}) \ No newline at end of file + "status": contest.status}) + + +@login_required +def contest_problem_my_submissions_list_page(request, contest_id, contest_problem_id): + """ + 我比赛单个题目的所有提交列表 + """ + try: + Contest.objects.get(id=contest_id) + except Contest.DoesNotExist: + return error_page(request, u"比赛不存在") + try: + contest_problem = ContestProblem.objects.get(id=contest_problem_id, visible=True) + except ContestProblem.DoesNotExist: + return error_page(request, u"比赛问题不存在") + submissions = Submission.objects.filter(user_id=request.user.id, problem_id=contest_problem.id).\ + order_by("-create_time").\ + values("id", "result", "create_time", "accepted_answer_time", "language") + return render(request, "oj/submission/problem_my_submissions_list.html", + {"submissions": submissions, "problem": contest_problem}) + + +@check_user_contest_permission +def contest_problem_submissions_list_page(request, contest_id, page=1): + """ + 单个比赛中的所有提交(包含自己和别人,自己可查提交结果,其他人不可查) + """ + try: + contest = Contest.objects.get(id=contest_id) + except Contest.DoesNotExist: + return error_page(request, u"比赛不存在") + + submissions = Submission.objects.filter(contest_id=contest_id).\ + values("id", "contest_id", "problem_id", "result", "create_time", + "accepted_answer_time", "language", "user_id").order_by("-create_time") + + user_id = request.GET.get("user_id", None) + if user_id: + submissions = submissions.filter(user_id=request.GET.get("user_id")) + + # 封榜的时候只能看到自己的提交 + if not contest.real_time_rank: + if not (request.user.admin_type == SUPER_ADMIN or request.user == contest.created_by): + submissions = submissions.filter(user_id=request.user.id) + + language = request.GET.get("language", None) + filter = None + if language: + submissions = submissions.filter(language=int(language)) + filter = {"name": "language", "content": language} + result = request.GET.get("result", None) + if result: + submissions = submissions.filter(result=int(result)) + filter = {"name": "result", "content": result} + paginator = Paginator(submissions, 20) + + # 为查询题目标题创建新字典 + title = {} + contest_problems = ContestProblem.objects.filter(contest=contest) + for item in contest_problems: + title[item.id] = item.title + for item in submissions: + item['title'] = title[item['problem_id']] + + try: + current_page = paginator.page(int(page)) + except Exception: + return error_page(request, u"不存在的页码") + previous_page = next_page = None + try: + previous_page = current_page.previous_page_number() + except Exception: + pass + try: + next_page = current_page.next_page_number() + except Exception: + pass + + for item in current_page: + # 自己提交的 管理员和创建比赛的可以看到所有的提交链接 + if item["user_id"] == request.user.id or request.user.admin_type == SUPER_ADMIN or \ + request.user == contest.created_by: + item["show_link"] = True + else: + item["show_link"] = False + + return render(request, "oj/contest/submissions_list.html", + {"submissions": current_page, "page": int(page), + "previous_page": previous_page, "next_page": next_page, "start_id": int(page) * 20 - 20, + "contest": contest, "filter": filter, "user_id": user_id}) \ No newline at end of file diff --git a/oj/settings.py b/oj/settings.py index dd437a9..2e86e75 100644 --- a/oj/settings.py +++ b/oj/settings.py @@ -50,7 +50,6 @@ INSTALLED_APPS = ( 'submission', 'mq', 'contest', - 'contest_submission', 'mail', 'django_extensions', @@ -59,7 +58,7 @@ INSTALLED_APPS = ( if DEBUG: INSTALLED_APPS += ( - 'debug_toolbar', + # 'debug_toolbar', 'rest_framework_swagger', ) diff --git a/oj/urls.py b/oj/urls.py index 59db3b4..f95fa2b 100644 --- a/oj/urls.py +++ b/oj/urls.py @@ -19,13 +19,12 @@ from group.views import (GroupAdminAPIView, GroupMemberAdminAPIView, from admin.views import AdminTemplateView from problem.views import TestCaseUploadAPIView, ProblemTagAdminAPIView, ProblemAdminAPIView -from submission.views import (SubmissionAPIView, SubmissionAdminAPIView, - SubmissionShareAPIView, SubmissionRejudgeAdminAPIView) -from contest_submission.views import ContestSubmissionAPIView, ContestSubmissionAdminAPIView +from submission.views import (SubmissionAPIView, SubmissionAdminAPIView, ContestSubmissionAPIView, + SubmissionShareAPIView, SubmissionRejudgeAdminAPIView, + ContestSubmissionAdminAPIView) from monitor.views import QueueLengthMonitorAPIView from utils.views import SimditorImageUploadAPIView -from contest_submission.views import contest_problem_my_submissions_list_page urlpatterns = [ @@ -78,16 +77,16 @@ urlpatterns = [ url(r'^contest/(?P\d+)/problem/(?P\d+)/$', "contest.views.contest_problem_page", name="contest_problem_page"), url(r'^contest/(?P\d+)/problem/(?P\d+)/submissions/$', - "contest_submission.views.contest_problem_my_submissions_list_page", + "contest.views.contest_problem_my_submissions_list_page", name="contest_problem_my_submissions_list_page"), url(r'^contest/(?P\d+)/$', "contest.views.contest_page", name="contest_page"), url(r'^contest/(?P\d+)/problems/$', "contest.views.contest_problems_list_page", name="contest_problems_list_page"), - url(r'^contest/(?P\d+)/submissions/$', "contest_submission.views.contest_problem_submissions_list_page", + url(r'^contest/(?P\d+)/submissions/$', "contest.views.contest_problem_submissions_list_page", name="contest_problem_submissions_list_page"), url(r'^contest/(?P\d+)/submissions/(?P\d+)/$', - "contest_submission.views.contest_problem_submissions_list_page", name="contest_problem_submissions_list_page"), + "contest.views.contest_problem_submissions_list_page", name="contest_problem_submissions_list_page"), url(r'^contests/$', "contest.views.contest_list_page", name="contest_list_page"), url(r'^contests/(?P\d+)/$', "contest.views.contest_list_page", name="contest_list_page"), @@ -128,6 +127,7 @@ urlpatterns = [ url(r'^account/settings/$', TemplateView.as_view(template_name="oj/account/settings.html"), name="account_setting_page"), url(r'^account/settings/avatar/$', TemplateView.as_view(template_name="oj/account/avatar.html"), name="avatar_settings_page"), + url(r'^account/auth/$', "account.views.auth_page", name="auth_login_page"), ] diff --git a/submission/serializers.py b/submission/serializers.py index 3a5308c..dcaf701 100644 --- a/submission/serializers.py +++ b/submission/serializers.py @@ -30,3 +30,10 @@ class SubmissionRejudgeSerializer(serializers.Serializer): submission_id = serializers.CharField(max_length=40) +class CreateContestSubmissionSerializer(serializers.Serializer): + contest_id = serializers.IntegerField() + problem_id = serializers.IntegerField() + language = serializers.IntegerField() + code = serializers.CharField(max_length=3000) + + diff --git a/submission/tests.py b/submission/tests.py index 96d06f0..a6342da 100644 --- a/submission/tests.py +++ b/submission/tests.py @@ -4,8 +4,8 @@ from django.test import TestCase, Client from django.core.urlresolvers import reverse from account.models import User, REGULAR_USER, ADMIN, SUPER_ADMIN from problem.models import Problem -from contest.models import Contest -from contest.models import GROUP_CONTEST, PUBLIC_CONTEST, PASSWORD_PUBLIC_CONTEST +from contest.models import Contest, ContestProblem +from contest.models import GROUP_CONTEST, PUBLIC_CONTEST, PASSWORD_PROTECTED_CONTEST from submission.models import Submission from rest_framework.test import APITestCase, APIClient @@ -193,7 +193,7 @@ class SubmissionPageTest(TestCase): hint="hint1", created_by=User.objects.get(username="test1")) self.global_contest = Contest.objects.create(title="titlex", description="descriptionx", mode=1, - contest_type=PASSWORD_PUBLIC_CONTEST, show_rank=True, + contest_type=PUBLIC_CONTEST, show_rank=True, show_user_submission=True, start_time="2015-08-15T10:00:00.000Z", end_time="2015-08-15T12:00:00.000Z", @@ -203,3 +203,129 @@ class SubmissionPageTest(TestCase): language=1, code='#include "stdio.h"\nint main(){\n\treturn 0;\n}', problem_id=self.problem.id) + + +class ContestSubmissionAPITest(APITestCase): + def setUp(self): + self.client = APIClient() + self.url = reverse('contest_submission_api') + self.user1 = User.objects.create(username="test1", admin_type=SUPER_ADMIN) + self.user1.set_password("testaa") + self.user1.save() + self.user2 = User.objects.create(username="test2", admin_type=REGULAR_USER) + self.user2.set_password("testbb") + self.user2.save() + self.global_contest = Contest.objects.create(title="titlex", description="descriptionx", mode=1, + contest_type=PUBLIC_CONTEST, show_rank=True, + show_user_submission=True, + start_time="2015-08-15T10:00:00.000Z", + end_time="2015-08-30T12:00:00.000Z", + created_by=User.objects.get(username="test1")) + self.contest_problem = ContestProblem.objects.create(title="titlex", + description="descriptionx", + input_description="input1_description", + output_description="output1_description", + test_case_id="1", + samples=json.dumps([{"input": "1 1", "output": "2"}]), + time_limit=100, + memory_limit=1000, + hint="hint1", + created_by=User.objects.get(username="test1"), + contest=Contest.objects.get(title="titlex"), + sort_index="a") + + # 以下是创建比赛的提交 + def test_invalid_format(self): + self.client.login(username="test1", password="testaa") + data = {"contest_id": self.global_contest.id, "language": 1} + response = self.client.post(self.url, data=data) + self.assertEqual(response.data["code"], 1) + + def test_contest_submission_successfully(self): + self.client.login(username="test1", password="testaa") + data = {"contest_id": self.global_contest.id, "problem_id": self.contest_problem.id, + "language": 1, "code": '#include "stdio.h"\nint main(){\n\treturn 0;\n}'} + response = self.client.post(self.url, data=data) + self.assertEqual(response.data["code"], 0) + + def test_contest_problem_does_not_exist(self): + self.client.login(username="test1", password="testaa") + data = {"contest_id": self.global_contest.id, "problem_id": self.contest_problem.id + 10, + "language": 1, "code": '#include "stdio.h"\nint main(){\n\treturn 0;\n}'} + response = self.client.post(self.url, data=data) + self.assertEqual(response.data, {"code": 1, "data": u"题目不存在"}) + + +class ContestSubmissionAdminAPITest(APITestCase): + def setUp(self): + self.client = APIClient() + self.url = reverse('contest_submission_admin_api_view') + self.userA = User.objects.create(username="test1", admin_type=ADMIN) + self.userA.set_password("testaa") + self.userA.save() + self.userS = User.objects.create(username="test2", admin_type=SUPER_ADMIN) + self.userS.set_password("testbb") + self.userS.save() + self.global_contest = Contest.objects.create(title="titlex", description="descriptionx", mode=1, + contest_type=PASSWORD_PROTECTED_CONTEST, show_rank=True, + show_user_submission=True, + start_time="2015-08-15T10:00:00.000Z", + end_time="2015-08-15T12:00:00.000Z", + password="aacc", created_by=self.userS + ) + self.problem = ContestProblem.objects.create(title="title1", + description="description1", + input_description="input1_description", + output_description="output1_description", + test_case_id="1", + sort_index="1", + samples=json.dumps([{"input": "1 1", "output": "2"}]), + time_limit=100, + memory_limit=1000, + hint="hint1", + contest=self.global_contest, + created_by=self.userS) + self.submission = Submission.objects.create(user_id=self.userA.id, + language=1, + code='#include "stdio.h"\nint main(){\n\treturn 0;\n}', + problem_id=self.problem.id) + self.submissionS = Submission.objects.create(user_id=self.userS.id, + language=2, + code='#include "stdio.h"\nint main(){\n\treturn 0;\n}', + problem_id=self.problem.id) + + def test_submission_contest_does_not_exist(self): + self.client.login(username="test2", password="testbb") + response = self.client.get(self.url + "?contest_id=99") + self.assertEqual(response.data["code"], 1) + + def test_submission_contest_parameter_error(self): + self.client.login(username="test2", password="testbb") + response = self.client.get(self.url) + self.assertEqual(response.data["code"], 1) + + def test_submission_access_denied(self): + self.client.login(username="test1", password="testaa") + response = self.client.get(self.url + "?problem_id=" + str(self.problem.id)) + self.assertEqual(response.data["code"], 1) + + def test_submission_access_denied_with_contest_id(self): + self.client.login(username="test1", password="testaa") + response = self.client.get(self.url + "?contest_id=" + str(self.global_contest.id)) + self.assertEqual(response.data["code"], 1) + + def test_get_submission_successfully(self): + self.client.login(username="test2", password="testbb") + response = self.client.get( + self.url + "?contest_id=" + str(self.global_contest.id) + "&problem_id=" + str(self.problem.id)) + self.assertEqual(response.data["code"], 0) + + def test_get_submission_successfully_problem(self): + self.client.login(username="test2", password="testbb") + response = self.client.get(self.url + "?problem_id=" + str(self.problem.id)) + self.assertEqual(response.data["code"], 0) + + def test_get_submission_problem_do_not_exist(self): + self.client.login(username="test2", password="testbb") + response = self.client.get(self.url + "?problem_id=9999") + self.assertEqual(response.data["code"], 1) \ No newline at end of file diff --git a/submission/views.py b/submission/views.py index 100c5f4..e9eaa44 100644 --- a/submission/views.py +++ b/submission/views.py @@ -5,27 +5,29 @@ import logging import redis from django.shortcuts import render from django.core.paginator import Paginator - from rest_framework.views import APIView from judge.judger_controller.tasks import judge -from judge.judger_controller.settings import redis_config from account.decorators import login_required, super_admin_required -from account.models import SUPER_ADMIN, User, REGULAR_USER - +from account.models import SUPER_ADMIN, User from problem.models import Problem from contest.models import ContestProblem, Contest -from announcement.models import Announcement +from contest.decorators import check_user_contest_permission from utils.shortcuts import serializer_invalid_response, error_response, success_response, error_page, paginate - +from utils.cache import get_cache_redis from .models import Submission from .serializers import (CreateSubmissionSerializer, SubmissionSerializer, - SubmissionhareSerializer, SubmissionRejudgeSerializer) - + SubmissionhareSerializer, SubmissionRejudgeSerializer, + CreateContestSubmissionSerializer) logger = logging.getLogger("app_info") +def _judge(submission_id, time_limit, memory_limit, test_case_id): + judge.delay(submission_id, time_limit, memory_limit, test_case_id) + get_cache_redis().incr("judge_queue_length") + + class SubmissionAPIView(APIView): @login_required def post(self, request): @@ -41,17 +43,16 @@ class SubmissionAPIView(APIView): problem = Problem.objects.get(id=data["problem_id"]) except Problem.DoesNotExist: return error_response(u"题目不存在") - submission = Submission.objects.create(user_id=request.user.id, language=int(data["language"]), - code=data["code"], problem_id=problem.id) + submission = Submission.objects.create(user_id=request.user.id, + language=int(data["language"]), + code=data["code"], + problem_id=problem.id) try: - judge.delay(submission.id, problem.time_limit, problem.memory_limit, problem.test_case_id) + _judge(submission.id, problem.time_limit, problem.memory_limit, problem.test_case_id) except Exception as e: logger.error(e) return error_response(u"提交判题任务失败") - r = redis.Redis(host=redis_config["host"], port=redis_config["port"], db=redis_config["db"]) - r.incr("judge_queue_length") - return success_response({"submission_id": submission.id}) else: return serializer_invalid_response(serializer) @@ -71,6 +72,37 @@ class SubmissionAPIView(APIView): return success_response(response_data) +class ContestSubmissionAPIView(APIView): + @check_user_contest_permission + def post(self, request): + """ + 创建比赛的提交 + --- + request_serializer: CreateContestSubmissionSerializer + """ + serializer = CreateContestSubmissionSerializer(data=request.data) + if serializer.is_valid(): + data = serializer.data + contest = Contest.objects.get(id=data["contest_id"]) + try: + problem = ContestProblem.objects.get(contest=contest, id=data["problem_id"]) + except ContestProblem.DoesNotExist: + return error_response(u"题目不存在") + submission = Submission.objects.create(user_id=request.user.id, + language=int(data["language"]), + contest_id=contest.id, + code=data["code"], + problem_id=problem.id) + try: + _judge(submission.id, problem.time_limit, problem.memory_limit, problem.test_case_id) + except Exception as e: + logger.error(e) + return error_response(u"提交判题任务失败") + return success_response({"submission_id": submission.id}) + else: + return serializer_invalid_response(serializer) + + @login_required def problem_my_submissions_list_page(request, problem_id): """ @@ -81,8 +113,8 @@ def problem_my_submissions_list_page(request, problem_id): except Problem.DoesNotExist: return error_page(request, u"问题不存在") - submissions = Submission.objects.filter(user_id=request.user.id, problem_id=problem.id, - contest_id__isnull=True).order_by("-create_time"). \ + submissions = Submission.objects.filter(user_id=request.user.id, problem_id=problem.id,contest_id__isnull=True).\ + order_by("-create_time"). \ values("id", "result", "create_time", "accepted_answer_time", "language") return render(request, "oj/submission/problem_my_submissions_list.html", @@ -119,17 +151,13 @@ def my_submission(request, submission_id): except Submission.DoesNotExist: return error_page(request, u"提交不存在") - if submission.contest_id: - try: - problem = ContestProblem.objects.get(id=submission.problem_id, - visible=True) - except ContestProblem.DoesNotExist: - return error_page(request, u"提交不存在") - else: - try: + try: + if submission.contest_id: + problem = ContestProblem.objects.get(id=submission.problem_id, visible=True) + else: problem = Problem.objects.get(id=submission.problem_id, visible=True) - except Problem.DoesNotExist: - return error_page(request, u"提交不存在") + except Exception: + return error_page(request, u"提交不存在") if submission.info: try: @@ -145,6 +173,7 @@ def my_submission(request, submission_id): class SubmissionAdminAPIView(APIView): + @super_admin_required def get(self, request): problem_id = request.GET.get("problem_id", None) if not problem_id: @@ -164,7 +193,8 @@ def my_submission_list_page(request, page=1): submissions = Submission.objects.filter(contest_id__isnull=True) else: submissions = Submission.objects.filter(user_id=request.user.id, contest_id__isnull=True) - submissions = submissions.values("id", "user_id", "problem_id", "result", "create_time", "accepted_answer_time", "language").order_by("-create_time") + submissions = submissions.values("id", "user_id", "problem_id", "result", "create_time", "accepted_answer_time", + "language").order_by("-create_time") language = request.GET.get("language", None) filter = None @@ -238,24 +268,42 @@ class SubmissionRejudgeAdminAPIView(APIView): serializer = SubmissionRejudgeSerializer(data=request.data) if serializer.is_valid(): submission_id = serializer.data["submission_id"] + # 目前只考虑前台公开题目的重新判题 try: - submission = Submission.objects.get(id=submission_id) + submission = Submission.objects.get(id=submission_id, contest_id__isnull=True) except Submission.DoesNotExist: return error_response(u"提交不存在") - # 目前只考虑前台公开题目的重新判题 + try: problem = Problem.objects.get(id=submission.problem_id) except Problem.DoesNotExist: return error_response(u"题目不存在") try: - judge.delay(submission_id, problem.time_limit, problem.memory_limit, problem.test_case_id) + _judge(submission.id, problem.time_limit, problem.memory_limit, problem.test_case_id) except Exception as e: logger.error(e) return error_response(u"提交判题任务失败") - - # 增加redis 中判题队列长度的计数器 - r = redis.Redis(host=redis_config["host"], port=redis_config["port"], db=redis_config["db"]) - r.incr("judge_queue_length") return success_response(u"任务提交成功") else: - return serializer_invalid_response(serializer) \ No newline at end of file + return serializer_invalid_response(serializer) + + +class ContestSubmissionAdminAPIView(APIView): + @check_user_contest_permission + def get(self, request): + """ + 查询比赛提交,单个比赛题目提交的adminAPI + --- + response_serializer: SubmissionSerializer + """ + problem_id = request.GET.get("problem_id", None) + contest_id = request.GET.get("contest_id", None) + + # 需要 problem_id 和 contest_id 两个参数 否则会在check_user_contest_permission 的时候被拦截 + if problem_id: + submissions = Submission.objects.filter(contest_id=contest_id, problem_id=problem_id).order_by("-create_time") + # 需要 contest_id 参数 + else: + submissions = Submission.objects.filter(contest_id=contest_id).order_by("-create_time") + + return paginate(request, submissions, SubmissionSerializer) diff --git a/template/src/oj/account/oauth.html b/template/src/oj/account/oauth.html new file mode 100644 index 0000000..2713d19 --- /dev/null +++ b/template/src/oj/account/oauth.html @@ -0,0 +1,24 @@ +{% extends "oj_base.html" %} +{% block title %} + 授权登录 +{% endblock %} +{% block body %} +
+
+ {% if request.user.is_authenticated %} +

3秒钟后将跳转到{{ callback }}

+ + {% else %} + + {% endif %} + + +
+
+{% endblock %} +{% block js_block %} + +{% endblock %} \ No newline at end of file diff --git a/template/src/utils/reset_password_email.html b/template/src/utils/reset_password_email.html index 3852adb..f4c8b93 100644 --- a/template/src/utils/reset_password_email.html +++ b/template/src/utils/reset_password_email.html @@ -8,7 +8,7 @@ - Online Judge + {{ website_name }} 密码找回邮件 @@ -32,7 +32,7 @@ - 您刚刚在 青岛大学在线评测系统 使用了找回密码功能。 + 您刚刚在 {{ website_name }} 使用了找回密码功能。

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 z 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 R;nJmcnB45yX7*B^{|~|z2W9{O literal 0 HcmV?d00001 diff --git a/static/src/css/fonts/source_code_pro/TTF/SourceCodePro-Regular.ttf b/static/src/css/fonts/source_code_pro/TTF/SourceCodePro-Regular.ttf new file mode 100755 index 0000000000000000000000000000000000000000..437f47280b4f611f2b211506c5c5eb887661194f GIT binary patch literal 197644 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%0A_ga!fgk_H{|QsoYlXD8DIo)mg2t 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