From b85182e81b9c62d174b89a1f737ca4abcc1084cb Mon Sep 17 00:00:00 2001 From: antv Date: Wed, 28 Aug 2024 18:03:26 +0800 Subject: [PATCH] fix(plugins): fix tooltip with incorrect position after graph resize --- .../g6/__tests__/demos/bug-tooltip-resize.ts | 53 ++++++++++++++++++ packages/g6/__tests__/demos/index.ts | 1 + packages/g6/src/plugins/tooltip.ts | 35 ++++++------ tests/g6/plugins/plugins-tooltip.spec.ts | 26 +++++++++ ...n-after-graph-resize-1-chromium-darwin.png | Bin 0 -> 10806 bytes ...n-after-graph-resize-2-chromium-darwin.png | Bin 0 -> 12599 bytes 6 files changed, 98 insertions(+), 17 deletions(-) create mode 100644 packages/g6/__tests__/demos/bug-tooltip-resize.ts create mode 100644 tests/g6/plugins/plugins-tooltip.spec.ts create mode 100644 tests/g6/plugins/plugins-tooltip.spec.ts-snapshots/plugin-tooltip-bug-tooltip-should-has-correct-position-after-graph-resize-1-chromium-darwin.png create mode 100644 tests/g6/plugins/plugins-tooltip.spec.ts-snapshots/plugin-tooltip-bug-tooltip-should-has-correct-position-after-graph-resize-2-chromium-darwin.png diff --git a/packages/g6/__tests__/demos/bug-tooltip-resize.ts b/packages/g6/__tests__/demos/bug-tooltip-resize.ts new file mode 100644 index 00000000000..fec9b6a339b --- /dev/null +++ b/packages/g6/__tests__/demos/bug-tooltip-resize.ts @@ -0,0 +1,53 @@ +import { Graph } from '@antv/g6'; + +export const bugTooltipResize: TestCase = async (context) => { + const graph = new Graph({ + ...context, + data: { + nodes: [{ id: 'node1' }, { id: 'node2' }, { id: 'node3' }, { id: 'node4' }, { id: 'node5' }], + edges: [ + { source: 'node1', target: 'node2' }, + { source: 'node1', target: 'node3' }, + { source: 'node1', target: 'node4' }, + { source: 'node2', target: 'node3' }, + { source: 'node3', target: 'node4' }, + { source: 'node4', target: 'node5' }, + ], + }, + layout: { + type: 'grid', + }, + plugins: [ + { + type: 'tooltip', + style: { + ['.tooltip']: { + transition: 'none', + }, + }, + }, + ], + }); + + await graph.render(); + + bugTooltipResize.form = (panel) => { + let width = 500; + return [ + panel.add( + { + resize: () => { + const newWidth = width === 500 ? 300 : 500; + width = newWidth; + document.querySelector('#container')!.style.width = `${newWidth}px`; + graph.resize(); + graph.fitView(); + }, + }, + 'resize', + ), + ]; + }; + + return graph; +}; diff --git a/packages/g6/__tests__/demos/index.ts b/packages/g6/__tests__/demos/index.ts index 025c7f86d42..faaac7c9cb7 100644 --- a/packages/g6/__tests__/demos/index.ts +++ b/packages/g6/__tests__/demos/index.ts @@ -19,6 +19,7 @@ export { behaviorLassoSelect } from './behavior-lasso-select'; export { behaviorOptimizeViewportTransform } from './behavior-optimize-viewport-transform'; export { behaviorScrollCanvas } from './behavior-scroll-canvas'; export { behaviorZoomCanvas } from './behavior-zoom-canvas'; +export { bugTooltipResize } from './bug-tooltip-resize'; export { canvasCursor } from './canvas-cursor'; export { caseDecisionTree } from './case-decision-tree'; export { caseIndentedTree } from './case-indented-tree'; diff --git a/packages/g6/src/plugins/tooltip.ts b/packages/g6/src/plugins/tooltip.ts index 392a4073120..2bf52b76d88 100644 --- a/packages/g6/src/plugins/tooltip.ts +++ b/packages/g6/src/plugins/tooltip.ts @@ -271,6 +271,7 @@ export class Tooltip extends BasePlugin { }; } this.tooltipElement.update({ + ...this.tooltipStyleProps, x, y, style: { @@ -300,7 +301,7 @@ export class Tooltip extends BasePlugin { this.tooltipElement.hide(x, y); }; - private initTooltip = () => { + private get tooltipStyleProps() { const { canvas } = this.context; const { center } = canvas.getBounds(); const $container = canvas.getContainer() as HTMLElement; @@ -308,24 +309,24 @@ export class Tooltip extends BasePlugin { const { style, position, enterable, container = { x: -left, y: -top }, title, offset } = this.options; const [x, y] = center; const [width, height] = canvas.getSize(); + + return { + x, + y, + container, + title, + bounding: { x: 0, y: 0, width, height }, + position, + enterable, + offset, + style, + }; + } + + private initTooltip = () => { const tooltipElement = new TooltipComponent({ className: 'tooltip', - style: { - x, - y, - container, - title, - bounding: { - x: 0, - y: 0, - width, - height, - }, - position, - enterable, - offset, - style, - }, + style: this.tooltipStyleProps, }); this.container?.appendChild(tooltipElement.HTMLTooltipElement); return tooltipElement; diff --git a/tests/g6/plugins/plugins-tooltip.spec.ts b/tests/g6/plugins/plugins-tooltip.spec.ts new file mode 100644 index 00000000000..0a1f996c93e --- /dev/null +++ b/tests/g6/plugins/plugins-tooltip.spec.ts @@ -0,0 +1,26 @@ +import { expect, test } from '@playwright/test'; + +test.describe('plugin tooltip', () => { + test('bug: tooltip should has correct position after graph resize', async ({ page }) => { + page.goto('/?Demo=bugTooltipResize&Renderer=canvas&GridLine=true&Theme=light&Animation=false'); + + await page.waitForSelector('.tooltip'); + + const clip = { x: 0, y: 0, width: 500, height: 500 }; + + await page.mouse.move(375, 250); + + await expect(page).toHaveScreenshot({ clip }); + + await page.mouse.move(250, 250); + + // wait for div content is 'resize' + const resize = page.getByRole('button', { name: 'resize' }); + + await resize?.click(); + + await page.mouse.move(285, 250); + + await expect(page).toHaveScreenshot({ clip }); + }); +}); diff --git a/tests/g6/plugins/plugins-tooltip.spec.ts-snapshots/plugin-tooltip-bug-tooltip-should-has-correct-position-after-graph-resize-1-chromium-darwin.png b/tests/g6/plugins/plugins-tooltip.spec.ts-snapshots/plugin-tooltip-bug-tooltip-should-has-correct-position-after-graph-resize-1-chromium-darwin.png new file mode 100644 index 0000000000000000000000000000000000000000..ec99e429312e141a051515c4bdca736d1996a5c5 GIT binary patch literal 10806 zcmeHtXH-+`)-G-plqyJ*suYP7ktSkjY9I*Glq%8%1f&>*0HMfMsubzcn=}In9Wg+V zPG~9}5)etG2MAI_xr=?ibH@JuocrTDcieHuxEW-SwcfVooa>!)zVmq^4fLQ)^gQ%5 zG&D^2wIB~^Xioh;`JVd&xC5!tA^`tRc|C;Qr9lnwEz;0jrnwKfW8|B;N^teGAT_OR z949!K$D*qe$FD}7uS>j;;K_1^KI8n^qR^yksYBPK=4;ru)xoRe70a-pfh0X?uSuoV zuY{?^qUtH8m3zH4zY{F!MV%Rn&ohN2=|nVi=-qwJW^IS)sglyE6V2k1V2uhGBAGwd ziEGZ`x^jy$s6$E2IrgimdHQ>2hk-_2+KScL#pPjwL%QST3zwrn`4K|64NzPHJIyPk z0huQE3XY#f9@237RTv0P^YsFWnT97He@gvc5}4*$1dNV`$?$*L$%<7*_tAZm8{9?- z_t*^~tQtnyjY4ea&KPyDYMfk?!a#KnH2=vJ|IM?%Gle`DGD_fJq)jGX_-pxOF?7V4 zKTa9H0G40*?+)#86Whtz{p~okh`v;Ps0)itB zkC^thPEAbIcK2%vQ>OIx+=`#nYq}Fe6tf@Yww-3TtHsch+*9+;`>mhd>#<6Ub=oio zV~7*?D37stuTshKs*QD;P_xzRkcO$B^L45lSqBzF~!}#St1KzH#FC%oFwp( zIO5gSC+-)E3XG^o1}~Uw73%HZ+eug+F#rDTJ=M@56qGL;4M?U8m?t^g_mP!q(XCCA zJ*7P(N-pG}a?&yuX6k73Bs*v-g;+e367s?#Ap~DBNr;1){wlPu!Vabo4+AL)ORVR+ z>5zm&5Zu8^$*OEtz0Pcv3~i9u+*hv*J3Rp`AV@+zh)chy&3}%rs>21Yc3myE4^yxm zmXX(;8O)ykKBcPR7-RuqO|~7z6aea&%xfh!19e zo19$gH+$L;;=s-E7Hv#1{&l`y8uP}0j2kw^MV{wvJDyVTI`)>;eKdj;h!7G_EP>o@ z4r-Ymi9M*CM}^sTQ(SmA-I(aAT3w{+lQ6QbshqC2-btBYsD@rb7LG|VJqMRrC zAc5e??$fzJ&Q?R57+bdoFF^0=m&e*s&W{>p>&sIuZhnlBv;;T%LR7DVCO#8CLYvI}KBZFsl z{>G2IUA^2w(JKSAwMc%P0l98BRieZC`ms|S<9toV_fHU2L_>goZ_jc0dUClllNra1 z-!tD4-{E~k;r9EGvyfuD(Km(`#?6&YGTyBQn9018O*QyDUe1N6W}Tt}t){m34kjH3 zh2Z6voeN1!(d(X_D&_&m8w*rl|bRfNQ)|c~z}S zft=fS&0Y26yoKAZTAVrg$A`u%C*$SN-fsG$=3jEt8SXE084JVgGw|bA=N@U!d@2s^ z+wA=IjY6R)I7vFABI}L1kb&n}8V_QsbgO#y;jYMUpE8% zaY^aoZr=2)!H2TXRV#g@WF`f+>~o=Vm`*f5Ql%#}F4?u9Q7C`ukFOA^qeUTPIt0RV zu`LL@lfUI>IVFGLU9wcABFk1xw3s8?khae(VH23tX!wWLCpd^bDybIj(mh{_s6`$& znJKE6xX0RA6n!$@#SJ`1-4^~5DQ|GuJ3O!-x3q1tO^xCvp-$_4xO4Q~XT0s=mEyQ_ zetCF>FAeM66Pd;BPd~LWkLBZQThGNkcR*+}Mx7<0GKN`?)OQSAy_kaSWs3U-ZJ{#x z_^tMHaiKC5u5qw2PGRL=?JZV9Ab7t_t1!oD#9}UftLq#K?KT^vA(dWp_hDy?)t{gp zw$^jje{zrOaFVQG+_O4#rxDi*@H(u*joaMgcN$Vr6T$%vv%h8ToTc?KAQxVDd*e~I zAz7Nw5H&vhvJu(!)P|14e4l@oWh=H%M6ZIu`U5Sr)p9LXw*z-Feon;o&5lS>U*s9g zx`tRo8(HEat&bT9-dLPBT3=O|a?v?TXNeh6t~OB5m=ZAKeKEaKb2nx+MhKRRC_#2b z>vd$Rg8HOxb1W~(fLWN{c6V-fDwKyD%s{y&ZD3t|x45PcZ;8?}6yRkP2SVXe1Gdns zAh_fk)@e8H@pvHSzqNS-KTqrPv_nFu$XUFHp7ymiO#6yBTV#npk->|um&v@g2JhO( zx5R&mo4!_lpVuWYR?v7667!~LKTNDk%)XzALn?+u(j1mA02;!g+-sLvPki|0T=Ccv z#=XNU*cr5VxedI+d9TMU1Wud(qOE@Y+*)gR6L*0g^UCW-wZ=!aNUj-9uUUS{?;}<# zhR=I1gNxT%^=ogARjAnr`i#fR;rPw%xteLlZBOW-U?v*moi)+4B z?1eTXeIyKe8_Coo8eYBW|R4sU~P@2@_Fl-^`}n3 zzLLs>?`De-V#*rS(eix`-&V((B&}Fha%H)q>doJHPte5b-ZF z_lLp_?i*dcEMZ~iA+W@INxx6TF=?fBKG+EG#?yHQpogR1CUaeE;3I7)<){&|7W-QJ z8B^qXa@NEXQbN5?)%2}J^__PSW#;DnykcMa$GxDeo^6rx&ft~VNP`d-nr`|;aMbJ9 zYXj-GD>n6tL7WfBpsS&Rvr3}08c{H4{I&63bER;8jTjizunO_U$p%_kfHy3A&I9N- zj}FJ*p}TsbbT>fh{@R+pv_8+mVBuFOmrj}mas2z;w!oNh6bQa>b@N2iJO%i?JPqhc z$2eGconzcODeOi?0R$K?8wnBGoJl$N-hYys^)E!%{LI`*t@j&UueCGr2L1 zwqzUIEZGvbBRd2fxWf_x#$#aNQzUD%(Q`nIDZz~W_Psh~GVJ!;8rPDYQL}c|a_#XG z4z}ybw?vOYuHh=TINeIFSx*~W;(NROJDjiadZQkDP)06iBKV0@NKtnlq>O*NGIG&a zcck{nH!v_zKe(hmSn?KU|2(Ozqc%1Frptgc;j_%^M#PPVHjQ7BNsy?r?`6vsa37Y) z?ywV2amu=(>vp{3Jw$<4{?KB*kN87|0OR9m@nF?9b979shN**ED;MEYxiXIDdt&|ltci<}XM`K)K=4m@UE)3*s!qsW z8Z~(-J~}11bcq{@+|KeEbuMoIz-dB(q)&%@4Iy7F*Hu7PDM?jn@tPYa*>XGAX4gsG z!vdLb_~x(Y+Yq7I4PRveeS3TRUkdSBT7}r>{Y!<*1CMW0t5dG)9{;%1>pEy=m7yi_ z^Slg%QFusN0W9v+A&?B**zm3Txd>0S8RTcqAgqk`J$q_Zb1g?eUj)*gNSfyfo0VZ? zjjVRaR9OZq%&S%S-ry&4unx&5KRx^Uj;K=M1&&CFkqU*}`1+D&|HR5D+S}WkL?Tst z+zd_`OPk11mM~%*-AyOH zFWKNHBIRVRrWfwul{6bJRSWaLH3OOlWKXlnYR|MrtDe}9z?+)8Wqqo0vfjSXS-@Fz zwxS`HyYy!+kt-x@xx?m%v24nXjX^;S)&|ipn%4p6Oekz_SFQn+8GVW@dugzD0Uk1FjeWPF7(gF7KxEt)FuZZL`JBSev9QPDweuWfdR;Cdrh)#YSEcSmNjNa6?UxH@|ca zEDW?pVldP%bb^`h-&d^ZR4@z<6_dXZnVo>F6H3)Y05cj zp8It(&C|Zpj3O1r%fsX7=%}fw$-~3*&_hmDwb^Cl<6G%wGe}WU5#T^h-TbLz%at9R z{)v7{wu10OYZpi<*IImCz50t{9ZaevWH7p;|9*5 zA;9E-ytl#N@bK`0Zwr7$3@Wlo5FU26MDX*Zk(TV&iZcO2`?dM`cOldM%jG8$m{r=% z4`hvuj3_85NJ&XCF)>kh)`Hi+(2b!iC4{oFvbdgjemOd%3I^@Yj*bGW*K_(tTn}Xx za93|=yDp|5nfAWQXMEG|=Sx*C>wwByjF6C1PGH?u2Cw@kTNvyir0no_R8-WI2R6{f z1vkIh-`D5cWW~Bi6jQqt2{{PDQtsi}%Pf7Nv45m*MnyQMYgZ*8vLI6b$O$cd>57Zy z!u`XcVk!zcjZW9m&r9OhA)tVzj(g6gJGIxf*y6epJv!OncCbZ4bai!QZ%oT=AaPV~0P^e+pm5om#5Rof;Ia+f+_{-2}{S=Jxat{oxWxl#6n2j{SWE702gvgeH zS6od@>g&ZV$o_JSGVL~80zigmAnX>dqRa~0kBGYOoXx;uVMy*aeR+FyC^bm?!T5Bq z*J9*F=iCdV*hcmrN$(R{&#@Q-3F@Pv3+P%%j)4IZ3e{G8LFp!pc>xqQRB#7JefN4L8>g zZOnDeQwt&^Bh4gJoK*SRiLrhh9W6(XbCfGAj{MCa#E&=kT9aY!z0Qihh_j`$heg$y(QK zK|=8Hp>)&l?+XhHfjb2Fkgno2@Jn_;L?!lKW~yr-Y)+UhO=%AcX2w~w^scGBc#O4v z(ZM0zylZD{JW`lym98a{?Y~s2bG?E1E5m~`zPY&>9k}W`W`$Uls>lBvw(K^~#;gm( z&3|4-O;y@=t*8OLi^8s_!}r2gvgr{!%p#AL*BUni*AO%9?d_(`l;r*WeH7}tgp;rD zZpO^PugHsHqbN(nOyG{&R9*jPaiY0X*2wU1&C^Jo;2L||m4-Q$M{*lKCvEF}>}B_U z1elbWT@ixD2^jktolU`q?id?AdHGF)jzyqxXBvUsi19f1$%VZTZ+%2bU0q$Z2Cmis z!4Ydsikxp%j&|pG72O+;kEk9EB+lh>ouK`poaq-`{AvfYEO0V@5AQW7nh5;xn{un) zXmpFu$G`Xvga9NQ9}B!1yZ-Y zmq#PJHMlJ0Wo4^?5Wx@jdEz@8Dpcja<>GpGra6$Bt%3;J%$N!Ic`-+e+``Dj#INkT zj>3>(`vX^?&wGC~@o&?6wDyBnsx3#Zf|&<4zVtP1jat;!)&hIog{6~moFda=`|lvdP_Y{-BeLg0ao_g{KatnlR88b3N5c^J)Yia-Wfp$)pEm; zHnuQnZ{VniHO!mqda-6>suf;7SaL>`vPAEaETUU|r`{!Hf$1HcPt!GH7*3Ezsfxgg!Zw4TC z=I}t(nc6Wc>UtYXTy!*_5{$UfCCC-;5x8t2c(fi4Y>aqesDK}lwRnva_JwHm893Ax zED>HZ{K9kY9g1-(alind4A_=Ez6r4t);4=|bVR_w1;IWm?U5G&$9-buL8W=V;#lkQ z($Z3~oB&FU;`GAG$^rU#QWa1Hd~MpoqiHJ}LEf7f3>ux7*ejF{EX~jV`6y?9*b+|g zUacK_JSD5IcKACBKJ7Q31-J5k;gI-65kba_}Vda5cFg2z#8rMK7?2j}`aR?)zA@M7$;w>Q^Qkz%l|vCt^Q-yR*21 z%^^!mII_K^Mg88r7XKN~WoxN;t@im%KV^ZqE@7ccGf6(?;-+hUCQuIg&|e6a5o{Bz zrmE`u<5j=uNAM$WA1C%iM9;MpukmWAHcvPdc$}v$cS)GkKtbx2I09xxxZBy8Z=13 zMhq(r7G)|sHHX&5bS5~!!6&?XB;5G30EiqtDsUaxpbAeUcbIMYwsJ7)($$*TS#W&X{ve+>a*SIQipZA(W@@;HZh|_DFb$WOHQ36TP>j%lt`1fN`nzc zgwfX4)}~6{FV?gXNV00H%1^$qaS`N)o_H(BCecpTVBX3Xy~f18OSgHGP{5 z$&Vu+^#9(DYqXy4PcwA*DK$&v&mlM@(_?qP^f_KL`%7r zINF4ZyXb!~teuRP!ZoRX4tqL-p)9>v)2H|qApxX66Yl?+o{dZGE5 ztMG9Pp2Ov-)J2(!>P<46Ne-`&(n>3D*f$kPX~9&WHol+B$6}5o+-jK2@G@WRK1FiJ zII;8&dGyGI7s5xEqv$#vY@S%DUOnEcD*KpK*Nv)&l)i16lgE;+esxxoZg} z>RVFA;6#F6)?9T(w!vI7q8 zI(xjQI#vT&%%-?ZN356B6QRwE&eP|4u#?hp*y~>+BtM(y%(_5TL2!&R!tCf!!>v@>XD5{!`AVQAhH2D@&|c^9I+1cl(953-(7Y zYKLSbc}!bkf>NLcKn5cX9A#Z>3?{F5N`k5fK`-&$o?-2*u-Y+Qo5yh)LaO z)$bs_)ra`-2YJw4m8doaC*|Tt5Ze_8Hu~g#cR%Wz1DiM_<8B8Dg-hd387i&C%xgnK zLy?d_gW$=(y=Ix_+I@c>6<^H>u-9Z9Ed3JT(dc8e`nY= zkn)8g&#!GKfN%t`wI)tfa?VN>$RX| z#M483R(dQyFW(w(cIG==7xT>(Fqxw*vC$IeQT3i^y_^_~uzI{a5o{U;GhLZScaz~s zg(@@4&Pe25&T$2#I7CI)B|}VFIJ+!?sPIKaK)-4<@pMofherwKmdPSS^mO9!G8(k@ z%;k_dV&KRyN42o+g?^%vMHfEN@$)vmE9HtUhp{ z_@SJtxRfbI7=WIbe{;Ehs^%oz(+_l%UhS`~CJkJg$TDO|r7oX_?bl>au84U&JroO< zF5hsG7QtDjp=Rxv;BiV4stsa^E5G-w8gWGTWn%VcIj>vq8$X+ASLb{-&bDrXm|zGe z(@!-BxiP`DCxX&a&9hJ;>ZADzZfsiDppLV|O5mGlk%zF<+cOskK0p;M5*)1*I&^N`aICN=+ z#Ka5d`8!4ArVrWlN;8WI_tWH?8Q}GnybAMm(S?lZ&m-+>9a1C^KaMu@fP3OXk30+cobVkM?AzOZAqyyHj6WOe5Sbb*Kh~yxi_Z z3A=npJ&_W`s_Q1*q%7{(;b~rYu_Fon@9`Y29`E$6ug=$jB5v1fsCp@RTsyx5pr@Be zsOiz0d&RvDgsR57qf&{%pEDI-hfe_j?OV4?7SAoDjX5Z5(l|LnL%{wBKP4`;V$_fi zfGPnj+QBuWX0&%ghg5lJ-dO39`_LV%uNrjaSXD#Dwia`NzKqQGXa>cUu>Y0FtPOy*05*XVFNuRS6fJ#O{9aIS_u39p8-P1R02W4O zjNSik*8ef_;$JifKaF;IT>v7qF}SaH4Wc2$Y8=-HUb}q4y?CBv3i%`C)yG@;vc#Qm zpbGF&o@@Hf2}xw){}LieV4`|Mb0uTpT|{)1F?5yEwN2_v@7Lp#0m$i zv+KLf$4_;T&)rf-{hi|2Ujc|Gf}c0pn*ZI5kM`Kt#(E`Xp>QY>j_yq_gl%f>bze0% zH2wPeOs*h~pov{hI;KXgD5Wp1V#0{Q$7&OSkfW|p$}H2i051)Y+WYL)Hvv=`S-ye& zi#vSD_4?f#GJAcT7R-$5*K+Z08w3I+3#%fskZ^3vh+ zoP%XDK7){OVQ-8;sSfohv%T`)E^Mfro~D`q{sH^TfM`+l!*Alzy+XV6xjob z@}l9k_?gA?ID4wY?IOtw>XEs4<9*%YgIV2I7om(RQq|S9c20iMeNHx>EGgD*)O8_%7Esl=b7&pvqU!+YZU+Kgk~A%o}Tcq zzeRHh#S7k2t9 zOIBW=HdwmV-Sr>CYs3*5<#)q@1`?_-tjLb(uZhwa20){pH2Zn@r)=tZj!aU6gLb8tQJV5c04?UH-}2vQJZU_~wXh`uUBsbpy4$~Mjz{EIsj@m&}o z4t1#dX9u`cSJmjdrNm_yOEReg=s(9Mf>fQ_qsz+hdDkMd=|Qu9jQm)){x7G5?tb1A?nvx4x_#1m#P86jH{?m1drODkA4<3} zRh62XcsbvoSu!ApR^MK?dY$nLa`}hX?kk#wx7Gg%X5HU?%il?Jxal(yCxpT%kRqf~ z>hyn{Iry&t_TNL{--u=5e=T34{LdKi4}K#PkUY}-pCdH>x5vsmW`T`^wrmG}LjfWv O&3z3$2qudnnVgs$nx7 zx1RAgc2ngiSNUGDchleEE=mn$xK!_Zj)9-vBa~aKFP&-a5lfxEoJe~66+(U( zr>mm&;f{4HOqV7_vLh+EgKt8XzSppKC)sVa)l>=fBpH>A;mFHBw#MMxJMh7w4DvQf zk~2nl`#594EKrGvsYUukk4;SQfjW&04JFTR$3Dv~FCU6T>lUwQ9P?CHRmtcHvC~C7 zx~2a7z20%s`%YVJL!V;0JoV-TRIDHfq;qSQqV>UBdC-k_U@8#hUpUZ{L(}6DD{=dEHB(5F(?N0UW*mrmQ_*II$g&40~gk^MUMs46U~cW+}-$L zvoXc@tSC%uHTgPqSel@!fco=dP`OEek?VZW-w9?%dYXNp|2R#jvs`j_i!*Evt$+Gk z^yHokt{yL{*4ALEmwk|RJ*}~@<*&n!&I+P^daZ~gv+9Ww0TXO&wuPnaY)twDB6_Q~ zz&A^^`Oar+kFv2Y$XvxEBvM`E)k!W$6TFgky01m9F{cU~h2GjP@$hRXx2*Nuh}JH1 z5gokryk@O;ne24nh1lYY7?V)gvLlKXA~fV1N6iX2O8zEz4cC-du<$PmAh-6m`m0DE%DEL$_`*;7S<6J1{#*6lPjya7;l%5^N1qT~&&6Mi68%iRV+hGz-y>yvl zwfK!5%CLkjmht3}Jn4%EKc(h=Vp@+tIug4A8O38 zaDmkv>$sFk5K0A^TU{&rR>846j?3_^|rSLA0U z%SbXxg{n}L^pwZhUO4Am*`0NE{tNzXu|5I5grN92eE(mH4d&fc9Q+sh9nI>LYmAi@ zu_l}8mI3aFxacv;_q+nYkmVcMg^or#fOB?Zq3+2Y%BT-;p2Ps+E+;}G8e+(^O8%x@jk0h zxzb`r`DMO>>B?=n{PSL#Z5Nc(8g($zbD}uAGnVE>M_8+3qfW!2M%lLXT)>`hUX=WU z`d@0q^i+i9%;^Yx=N!1>T$3+7-ng!XK7lJUcYst0$15c@ z)!C3^PTtw)Uzis20^LKw5&J>-yuvvM{_pUw@SO|baa{u^pB6cjwu@bLPIPJOwbpH( zsVSo)XRj1CGW+-T;^X5P83!#IL-i5_>qecMY;7w>Vl7cN{0jo)spgZNm1pV z$a0V84)8`67M7xt5+8eH!dHW~JW*{E@3ZS}QSK2mCviz*Jlgb)q$nGGE$>)mqt!mi zS)n*PLnSBC%Q^r$SoL~Ij*VXVP@mf35w!$>%I=AV%5B+&*RS>RlYePoE*)#5f)PBb zzHdvD*1DRcE{8qR-cJABD+ASMly=(NL5{|fKibxvyF%pi)=+$#0fe36^4Dz8totIZ z8c=ea#X{6M@AdeqqPfF4_oeYzel42a;pSkq*+W>?$q~1QRsJ9^Goifcxp}>tl={ec zNtFWfa_5P!)Ml4aJICO@8pe4v-BXm%P^n)scY>yC_;GRXu%cxWQAa;2rm2zM8_0dP20>)S%qmVpe9#l4%#wVoK$951Yf)llW@Oi)BY#CSm{=??boSfuNeC{;n zV8L^ud5cHHwUpMEK$p3}^4{Wx^^nm*g^bJsCbW-8rP>%6(a|xK=y51CjVYl#4NSex zIilQ>mGLTbZ(^trH9P1oKf8)LB2Y>9u8&MurA%0s8NWT)G(XJNKzbWdoopG2dKS_E zFXvJSfH(wpyssfXU`&<`WBv&AH!wm6e?@Zif)w0xc-r?>UK`gOH&Vx%RNb)tGB0$H z7eFju+Cid+29P_FmJv zmSi4NRPFAT%Ch3G?3k3fC1a*L?NCk6NBoo~h!Zs%ehB2-uAzd4yB4Yrao@wFSeNUQC!y@!W%B~WKQ^%`m7 z3}$pHmu=d2TTeO@bT>-qZD4P;h8XfGfrIm1H-p#APbn0vRti}6G8~TMYnbZum6>m- zH6oMcg81QH6e8o01T!D6wXk7pb-2*-C0&oBFe|LPJyoZJF5&I%h(?jdFvL=q9r&X@ zt^A^c)hkn{s$C7%lQxyAU9gG!?V&M_d>d8SUG=&dJ&}`geM*KNpZ@lahvYXostc?v zQIX*stF1l=uf{NffRz8O9$^Lc*e^Ep2E5X6OZARu@5Op>t)%q%q}s8{PZ#%-l{)5i z<8Mwq=M9oo$d|z~1uswYSQuOjQ$v<6cU5bQms#{9LixEkw`cWDXkyyGabI*86cb<{ z++l?dIp62hvG-z(>?f(WRVJ8Czc@CS^9goe$d1LYeF2W*UW<@*u$Mu?FUO+ECtuf3 zOi;(=(^(;g=2|{)B&FoUr5YaOynmV#j1)uI30{jl|E4X7zN6`>N|CtmAA0d?I=?}T zDTdE+R&YVj=LbI{wacp+d+5&3o_TcS$`dP*rof!92@~pv^wE$^HahxlW9a>X$#0E{ z#TZQC!cHGl>^Cuaok)a!($h{9%v&%ND+@yat73bLRvbJm@ClyKJ~uvn-My6dE=`PY z|2Ik&^_$q&ehK9X zp%n?*WW5p1u_de8LATP`XdISSfO*i_keK~(%dD=@ezQVs%fBj6#?MqW4tPB}LfuVY zcdsG4oZ1nsq3s&p++6PnTfb(@@gwS(N;9k2)>5M|TeS7j7roef9h|$J&mcFgSH1hI zq%N?zl{5sZI-?au1JyKT*oE*}p~L#3Xwf3MMeEu)rujybYm_pDAKK@h((GuJ+EAq# zMn_x~X)5^njnb398(cfo9!LQgVqAq%k~2E$T{s8W&=fGz<}mu0C&o3>KieBW0_WJM z67y?!JF0f!Uu~$EE~0~3M4D!RabM^dlB5-B()it6`^_i!r+HaFb}dI@%K+l5-y!H( z6sjYXbQB0nwB$n_W<&{_Uvn=;2oej`Ye!qbrRyh?yPc5^1CkzEi4@kth_4Zyxl3~B`s;vxjf=Rf^>WH0~e7KUD?`Q;Yf<6 z$j9YihxvA&VSz=5x1^v%CmH}m{Moy6DSL~<3T&v<&!KTC-p=R(R($q764ec09ZlKO zw8iVfGRMCm%+-J2jK(SKoB6XsvR1$e4ecpR89;ZRzio_CZ~pTJ=7^F;0B3qKvah+K zT_#`asn&_T#WJ<@3gS~70o%P1x(s=3{||)9a6{WMM8a`*Y2{yl+F=j#%#Q5l*7@Sr zjjF~1otRoezy}-VckGtcSRt(-_Ewq!Pc-a3?FViJahl9O809iaA#oMgIW((V-BdQ1 z5f)={L(G;Y^B*8ZApz#g|5|Q-^l>YR_gc@_*=YfCmm1rIh-IyJ`NTXWNcXNB;kJhL zv&v5*3L3Vc%443e4s=sZ6g#}G)r79tPC| zyW=5Fg|24zKK8+@+q2DXry>fp^13xL(~4z_f0oVXZQ$h}wZE6G)yA6D+Z|Xhipfg^ zJ#px7ySN#)jQnn2yGtv1nH2(JxkQLdm~YpJH#E#CH%SZUctC9&CTuuHan_3`>OMYP z1Y@y_&V|{I4uOutS4*mA1vN5>BdgocrTt>7eU>p{)Y)QQyQaAK7N> z7RnkdcQLwv68PJjzvr^z6SoiXR{{ahS@J_u_%oob&c*fEKkW$an05rf=+`y1*TwIv zg;=-TgaEF>B{OXOfF@IL^+(D_0SD8hJAqV8*U&hXDT;)-Z>Sm%w~?=1(;$(iH{vZE z_KLqv`<8@MB8vpJzSIU;#nghaN8PR5jjlv$X_Lh%>fVA0Z>QqE0w!mV(zWMrSkmjr zApe-kXEkG@Vq-G&BSfdka9oag;^TsjQleOz(vyH}K?GP|{qCl{grk=D=PEWlE+d=R z&`Mh`iJclYKQfNauzW~kU3lQ7|MUyP-Y#!@bBcJJ;vs6((AKDGVES8umn2j=pgv5Q zA=2U45q-DsxT!nI_D-9X=+=rJNqg@ z<3%3R@V}Bdb6B^gNE6g{^i7^{pSnkC#;+nxz6G?v+rXFoDOeaKGgU-a>SIYaV=2QU z?~jpdYisNhtHnv-C1$MGdhEpoG%8B_x4p$y7Yo(P4T$uS2b$}aM*dW2tMgMmoE$Mw z0$oHz&Wu!*@lg;QAEMN!t`0mEt4T}|7JcVn8jMsPtB>E=y@|{P%cM<6{hOzMlE#|U zmWO?_AnFhj6ztptec|qGZ%ayIWK=2%4dUPU&Mr{&M9)b4Gv^MSWBgQ}ppXz171g$; z&YApg)Ng5uE_%j&?aIY{#VbxiKx!sIJSV|gkNM>m7x7CwHPblEJSk4CF)SI?i+!-(bEMby;_lT;_S}sQrekBjUBb>5 zHvr#3S-{omKsRt7jH&bFpvZIH>7WjH|{(yo!W+#{xt&k{o#Adaz8K#tEmV z#bi6Xu1NnZ{!4`0H~t0CgPjXo;I18C*2eQy=tM6GJeU@43c8)>ov04}%Fg+uPV7D@ zcf@DCTTY&7rr>&>^+}0IzS+@};xN8axNx9@eZW55b!K!67|U~yFEAYARx%DRG;g+j z>D^#Uubaf9!9?TYeV=fl(|P^5#aq(3ayAI5BATPjFNZlo-};w!9qK-f&VvEL93LkR zZop`u>9E8U2B+J22afG{ZB;StKHr_UjUTIb>z`S#dxr3m`whQ9fiJ|tNBt((>(oC; zi`~+o2~jChgPgi8K789~E)MU=2rh#k3=XEI2U3fiuw!@3ihYm=naiC!cY3d)aqqEg zd=A!6?kX-t-S1EP*^Ux{J+v?HK4htMFm4`Rcsaq(_M{B57s6o^&UTiF5a6 zyP{X8<+Vz$g_h<(!o2T@78O0G1u~etUArK<*~x6p&c?>Z%4$~QJ~duu&O^r6B55N! zAf@I>iO;wpJM$hPjwJjxEiLU}v|u39{1g%NtFuaz6Yvpm+-_TrQ+&@#m)~!K+_W3T zVT`a+l#`nt8rq8pIf@9xVAa*hWaj#}( z<{iDp=H@J|twT_X1Op=@P8o1(Uiq#U=LorSnE`i>a|O9+%@^tN8R2Y#CNZTU%Syv?4b@|5NJm zD*sqJU%WbZuiGu!D5ph%x1_{8$4Sb>9>9+_-0M)vdx^RVUfyeKYXceu z%4_XmG=0fp3l$Dgu*ShBx6zm4XKV5W=(-9=N)K8uIMxD>y>t@o$%E|4#! zmW}w)B7OWi)y0cO2&-n-v0}UKXv-%5drVPk@4J^vjLPJEH-5IZwjLcFX^1sf-F4}E z#G3VPW7a9LnVXlLjyX_QF6ihR8G7^TC*PGHv8i4yucj&-8X?=oWo2b0C2nqR=1u;4 zZEvW~R;^DRwx_G(=s4u6Tt@QWP|*hy8W*-VHtLq6<(Wzo9dFU<3cn@wD+DH{h?v(* zRtm+z>(^KtlS5E}^Ab{0ygeB@^yklUkvCNB%1sOn$5vKyVHZcbmGI@x_suLVn_DT@ ze2udBPvU-gJj)q!R>=4jcZF**@*1`;fiqa{MPkUwd?7+Wbua* z5^sD@c6RoY+p!HSBP%QR;-5=PN`&fK(x*2^xDpc+d+dkT_w$rkE}e(VC@(FZR}TQN zy1+TECpXcc|7`Ewh~rTyctLxl_0f6+zgD}RR{jR^makshiBi``$o zk(+SmcaB)sYQ})jxwQBFgZZktE1inKn6k_?%u`0;y7k=9eY*~;$-;^b#m|~j_wRGZ ztI02IgT+b<{J^o{GkJog8YSHEz0F|+EQ7ndyJlD;YQ6Fd4(BA0gn$?0(4NS??NOSR z(U^zovWC4vm9#3xf_&*oa*pWNVGry2`XsPVZJ9VtK&u(H_pzOiXz1&TOFPoTo9mp> z6odqqw4rZ-R3ZwC;oeuEtPn(Y7!|v*RsGWroI}-a=$l^tS(nM?7$#1zy6Jg#+CY~c z;Hm4aCt$9Bghf_n`PIA0N|ZODywXpX%X@%bH-tmqo|n12Z?iBhAnWJG=LEkzGBP4D zlPqf0BqJ-k`NJyotArF*PXY^&x-I$Sl#V8Rs zbb^1y16x^%q+xxs#7x*(P`-cvpdJOowUBT=FE$=L|IF}8z(oQ(2GCM~6NU4T0yA~* zD;=VMj4DoRYk~78tw%DK&x}Xy_Ee?Q&{q}dg`mpZOfuGKrp6<1fi%xoR9j2%I>UQG zv6FkJ1SHZ#CmR$koV#}%BoDz(*84c&lQUkeeOBdsm79~Z%#1whEKoWuYQ#0T4c(qk zdFUd+cj!K)VcU*2DW_V^RfSq>L(K2PRiRsv^qQpH#POx+37=YEjIe_oE^?Q2%icVp zrM9BN+olWqj)i&nNzM@RjD%G7!;BglTORIbo}H9flQHrO$Af4z8UVMDLO2|U!36BK zQF%2Vjz=+zogP}B`Ptbmm)mt?T`4Iktxta?qtfMTA=}fy#P|^A0}FTY=T*Rw1cWpqA+ZD;$1#A=dD<@yl-MMoo z&ehE=O7YV<)A6dtN~4LZ2 zbKEYS?-}uVlbON9&4rUSX=UP245$C%>(MLy$NTGz4Go+D8`-wXDyk}Qd^ zXkK34i&RvAbvC$+@Txt2{21@?{cMlxY|ra-%}d>_hB`&d1&VW+{1&nXG1NJWwAh|- z@1>iB;XO1$Oj`Kr|Th}t^IH2+GnD;86xc!l&atF0hT;d z=@diG{5dm2`aOU1fjcl@A%QpT1nhr>*SM&WPxetzKR>mAy-sG} zg(M;oaJaCw{?9(^gCbT|HpaNlj?U=l=voej zH3$nKXLo?*Pu~ez|C#;R`ly}x-0N08rM<`q&H(4`XbvG`c)~#xuut2lxPpG0YyE0c z;7&6^!FIn)E~QMd8&~qr(h3U;`DLakc7*c4>lM){Y|M%0_0;ch zxlG2&!)fR>!vcwd1wi>cQO2TL>n%qDQvDcGQL88Wcw=J7c*S= zD^77;>Hd9tRLu-lmT=sE##EEtmM5&Co)3Sv+*=4lxvqnAYyZ;)`DFUUJh=@p?u8C; zjo!Z4BLNBZW1dy{5bJ;BTUJNSDIi4}v?P-O^ zcY|4PMS|wENK;UfaGy8C;N!=SQ(lEe#xW!^BM?Xvz_rO_nL|MlBEaA|1y<)=SR5zs zg_u_Ec5+_*rrVT~0=Rvn&!k$&Tkt?G^ zu?a^nUoZ6vXcE%L`xNCy5qF%%LLS2Ih{PqG>XjWfI7Y(VhVmgE<{1mo`m}*Ck5Ha_ z{7J{PyCm(cL?sTT0yqUw7oDQt43lW3Mt$VxR8S*^ApVdtTmwh?v*NEi&PTx)nNBw+s8WQ^^lVb>&6^IqRm%f9_nz75|dTU^SO0pddmkxi% z&&jvH(v3rilH1B<5w)9WkEKT1T8FN!jkhhQZZL9~DQCu;3>(;HvYc|U&RlVLkvvaA zV29GotOmxF4^KsBb$tFpYEGDr`kq=RL)=08WfEIJkyqw6n>2afi*^%nW=UyB94({v zMi|w(nAP#c6Q^m21=jMq1U96hc+pvKgrJb~qz~iAr*5vU?-i;0r`rqS6j>)GGNYI* z&bGufIXFTC^H;HF^UInQGcCtoSt)y<&?mh$HmIbz_5$f01_p*6`-=Sh@Y6XOakHbI zTt*#!)(INEIO`Bk>{v#f@Iqdf0*T}owzH^AHq&~j=v`_4hDtPogkvBMT1Av7*H|as zYtD%eSJZbEAPWi<=a zTaag$lXfkrcQ92lZ5dr2R{F7A@C%B^Oy@O_F+rK_*gAys>UEE#EIxsvAmNa(qUQJ3v-) zeWE6QKoEmu(=0yCJmXJ1eL>o>=9s|gCnm=!ii|s@$M=lISdRczzvkw}bFHPK5siTm zjytoG%iJHxc?T-ucq#}g;~Dp`@lbzV5^wVs)c%u$xoa0{Ci4Wvpf9U5RykAdX*meh z7(bBxtk7_jZuwT;0-bzdhCC&wM3E-_G)XAueQ{>!bMN6C`2{ZKHG3Ys0y)~zqxRWOu9$jJ7g@u?9b8FokQj(Yi~Z{gr*J7 ze9y%m zkzWR@QIRYb2Kp4?J73@C^7=TXJx2N4cMVimejd6LSef8pionl!5c4(obn+fL9_fYa zprAr_)tXOdGEh1>@sAxsUbJ2aR=X3HshVCgnX64ueI!|LH6wNHrF&2Fg!#g-}Pvq zcuDg}Fi%a~=KF-5@iSc@;zrb0 zElq8h>|yKZ#m|`n{8Mg}vt<_s_cbsUrDUhsxd99>?`*;6d*7Wt90@T2R9ZW|Rjud> z1=2`*YuA)yUCDD46o^lxjlbZ2p?^Tk#jlctp2vEU^%CY%P%qaovwrGuRncrc7j#DY z+{l^f#!X`p?dvtS?ouIhKb8^E524V?NQP3UxR+-70i~$sQ^lNjEcY-2da2Wg@g1KX z9EB}+3*m0j%uOP9*)nreajA<8`yp;pSGUtYZ(P8eYsWqDGq1WQB4JZ5#5P3?uXh1*n1^Sdne+|axytS*Uz3r zRM~xmyJUQ*e?_KGC@7Ln##wDGDdEhPZC}R!EDIco{=odrVW84nXC0Fr;SG_U-Dw;I zgcs|q&2H&JiL{Pd##nShBh=Ea*{ONeJ%0w*7!8gv&)2psE*g#S8aImXuw;KbLa(Dk{mfbX--+<;>;K5^R56Uyk(<&1R@CKtNqpm2GHRBj8I87mWa{ ztI~o(h@(x>53q2=TxJ&nn(}K9Lb<5m!X$5`Zf}b8v8;^0T-YJ5%ZC zdTmM3(}n4r+HmTkNuz>4-i2FUb40gH?4;$(8zSE7@Cvh@&8r+hUK3FphXv1FF%GK| zCie`Ojg)e=d@b0wI62L5J7{C`Wu^hWDD0(%{bDL}87JVoPRRo&gJjYhw?p9Ky6eP6 zs@%_qaY(2yh9K`Z!K&zEfNIGeimWd_e*)B?XOgW?e$=QW&Kh3KpDBOJ#%vt~FTi}h z1Gw|9b%l=mCVBGmeki#)DES08d49zXzEy8NOoC4UN6Sb+y=nx}Z>5wdR_OI4&dha8 zufQ#7qk+l8b)sgKI>IioMTJHhlI1T5YY2IRTgX!M+Fa#q9!K0+AC)exWaa^6RwC66 zjzw^<-xQ?2c(Oy~xM$Om!yMjlg3(kbS?$DhGe~j;^Kps zt1v#^t>LK&Zuw8I-<+FoJ1_fB6>-0HY+$RIe`B3XlZSr`s1#KQ0(!PTdEV9U@Aw@a z2_SQI`4c9j#$e_C3ip6aIG*d| zs#Q!!my}thM^$HQP1M#YXo4On!LQ;h*AKBT6$$x}OkIV0u#(#RQyVoTv>Fg(t?@8Q z%R{I|L0RC+aMBb_0EUsgA=x}15_U~(vz{RU_g3z4l+sTGy}Pb?^FHMT(2D08I0W}L zW3s?0dd+ytzivqgIeaP+u*lnNS1qwj3A+9w92|l%gl%8o;Nt(b1Q+E{EYPw38R40> zvj_r(^;{uvagOFR`nd)tU&qmHg(x8WABFgiG+{s>Y6%a4l|fN5YJEKwTa#e}h$I1F z5KB-T-9bZ3YBH*>g$DHCz4h?xk-b?ldef9- zo$d~F>?{{oq)ho_27n5&zfl3mU+DEl{`I`~El{e4G0$m1)S8)3+8T6kW|H4Ning?g z(@OxV+y5(`Wv;A5YE;T97e_v!i=hO`wDAdiws@!5$;WIf1_~_#dlf=p(us@}!V`pZ zpl`?ABBm=u&!lnD?=e?Fp#yxIU-IBPj?JV-5GePE+j9MMy9{w>mf{7)G56dkj_4_6 zy-E}XfdrJ)YOTv^5#X0xsmYu8MA307ko~UOFVhCMk5Qz$z%o}s%TjvKkGp<73xN}E z`{KrsZ`p=B^<9a*pwKDJFp?oH@dvLV5q_|H0$X|NCnamm1iBm1_wp3YcG#WD0YB}Q z-h3@WH*p>Wq*AQN4Ca120p02g%|@A%f`b|tLc^p*X62c;Bx<)f`SO1()SG42TTTeS z%(@P`Y5kcQZHfFXq6N53JQcMV@ip5Ym9E@E8wFq-9gO1>Vq?h*d1G2cTyT2te*50` zA#u_X78Hd!7ft&Y=o8|zSLXBW2|ll(0S;pA1V95ZEG^#I<8s?3lEG*Bty0=gFNE5r zZp}YSO?KHAhxx__`&l3r^C%TD0}c9CZe>z6TS<&WvZ3o)ua`t!EvN|v0Zb`LvnO`#E2KKw+O$rd*k7ani)@)5gSLD2J1mG|0xuOA$eAuH~1-*bcK0)w- zFq2R57wOEKc}y+WM*t)JuTaAO@8}D3@weFu0bW@z0_a99V8}mXJAPLF(ZK)l$+Cvg kdVp`B_-~>QXIzUdOgl%xm>