From 0e410dbd34d168bed3a59b45f6e1d247dfa804e8 Mon Sep 17 00:00:00 2001 From: Matthias Osswald Date: Tue, 18 Feb 2025 08:55:06 +0100 Subject: [PATCH] fix: Check bindings of all ManagedObject subclasses Follow-up of https://github.com/SAP/ui5-linter/pull/513 --- src/linter/ui5Types/SourceFileLinter.ts | 2 +- .../NoGlobals/FormatterGlobalBindingString.js | 13 ++++- .../rules/NoGlobals/ModelDataTypes_New.js | 19 +++++- .../NoGlobals/ModelDataTypes_NewString.js | 13 ++++- .../linter/rules/snapshots/NoGlobals.ts.md | 54 +++++++++++++++++- .../linter/rules/snapshots/NoGlobals.ts.snap | Bin 5425 -> 5649 bytes 6 files changed, 91 insertions(+), 10 deletions(-) diff --git a/src/linter/ui5Types/SourceFileLinter.ts b/src/linter/ui5Types/SourceFileLinter.ts index 6725a50b3..7f4ca752f 100644 --- a/src/linter/ui5Types/SourceFileLinter.ts +++ b/src/linter/ui5Types/SourceFileLinter.ts @@ -700,7 +700,7 @@ export default class SourceFileLinter { } else if (moduleDeclaration?.name.text === "sap/ui/model/odata/v4/ODataModel") { this.#analyzeNewOdataModelV4(node); } else if (nodeType.symbol.declarations?.some( - (declaration) => this.isUi5ClassDeclaration(declaration, "sap/ui/core/Control"))) { + (declaration) => this.isUi5ClassDeclaration(declaration, "sap/ui/base/ManagedObject"))) { const originalFilename = this.#metadata?.xmlCompiledResource; // Do not process xml-s. This case would be handled separately within the BindingParser if (!originalFilename || diff --git a/test/fixtures/linter/rules/NoGlobals/FormatterGlobalBindingString.js b/test/fixtures/linter/rules/NoGlobals/FormatterGlobalBindingString.js index b90dbd68d..fe7f087eb 100644 --- a/test/fixtures/linter/rules/NoGlobals/FormatterGlobalBindingString.js +++ b/test/fixtures/linter/rules/NoGlobals/FormatterGlobalBindingString.js @@ -1,6 +1,6 @@ sap.ui.define( - ["sap/m/Input", "ui5/walkthrough/model/formatter"], - (Input, formatter) => { + ["sap/m/Input", "sap/ui/table/RowSettings", "ui5/walkthrough/model/formatter"], + (Input, RowSettings, formatter) => { // The following two cases using global notations should be detected: const input = new Input({ value: "{ path: 'invoice>Status', formatter: 'ui5.walkthrough.model.formatter.statusText' }" @@ -14,5 +14,14 @@ sap.ui.define( const input2 = new Input({ value: "{ path: 'invoice>Status', formatter: 'formatter.statusText' }" }); + + // Note: RowSettings is an element, not a control (bindings are supported for all ManagedObject sub-classes) + const rowSettings = new RowSettings({ + highlight: "{ path: 'invoice>Status', formatter: 'ui5.walkthrough.model.formatter.statusText' }", + }); + + rowSettings.applySettings({ + highlight: "{ path: 'invoice>Status', formatter: 'ui5.walkthrough.model.formatter.statusText' }", + }); } ); diff --git a/test/fixtures/linter/rules/NoGlobals/ModelDataTypes_New.js b/test/fixtures/linter/rules/NoGlobals/ModelDataTypes_New.js index 887a2b679..1cad5c5ed 100644 --- a/test/fixtures/linter/rules/NoGlobals/ModelDataTypes_New.js +++ b/test/fixtures/linter/rules/NoGlobals/ModelDataTypes_New.js @@ -1,4 +1,4 @@ -sap.ui.define(["sap/m/Input"], (Input) => { +sap.ui.define(["sap/m/Input", "sap/ui/table/RowSettings"], (Input, RowSettings) => { const input = new Input({ value: { path: "/names/0/amount", @@ -7,7 +7,7 @@ sap.ui.define(["sap/m/Input"], (Input) => { constraints: { maximum: 1000 }, }, }); - + input.applySettings({ value: { path: "/names/0/amount", @@ -16,4 +16,19 @@ sap.ui.define(["sap/m/Input"], (Input) => { constraints: { maximum: 1000 }, }, }); + + // Note: RowSettings is an element, not a control (bindings are supported for all ManagedObject sub-classes) + const rowSettings = new RowSettings({ + highlight: { + path: "/names/0/amount", + type: "sap.ui.model.type.Integer" + } + }); + + rowSettings.applySettings({ + highlight: { + path: "/names/0/amount", + type: "sap.ui.model.type.Integer" + } + }); }); diff --git a/test/fixtures/linter/rules/NoGlobals/ModelDataTypes_NewString.js b/test/fixtures/linter/rules/NoGlobals/ModelDataTypes_NewString.js index 728e8e057..561e2e1c6 100644 --- a/test/fixtures/linter/rules/NoGlobals/ModelDataTypes_NewString.js +++ b/test/fixtures/linter/rules/NoGlobals/ModelDataTypes_NewString.js @@ -1,9 +1,18 @@ -sap.ui.define(["sap/m/Input"], (Input) => { +sap.ui.define(["sap/m/Input", "sap/ui/table/RowSettings"], (Input, RowSettings) => { const input = new Input({ value: "{ path: '/names/0/amount', type: 'sap.ui.model.type.Integer' }", }); - + input.applySettings({ value: "{ path: '/names/0/amount', type: 'sap.ui.model.type.Integer' }", }); + + // Note: RowSettings is an element, not a control (bindings are supported for all ManagedObject sub-classes) + const rowSettings = new RowSettings({ + highlight: "{ path: '/names/0/amount', type: 'sap.ui.model.type.Integer' }", + }); + + rowSettings.applySettings({ + highlight: "{ path: '/names/0/amount', type: 'sap.ui.model.type.Integer' }", + }); }); diff --git a/test/lib/linter/rules/snapshots/NoGlobals.ts.md b/test/lib/linter/rules/snapshots/NoGlobals.ts.md index 0899ec6c2..2863c3a57 100644 --- a/test/lib/linter/rules/snapshots/NoGlobals.ts.md +++ b/test/lib/linter/rules/snapshots/NoGlobals.ts.md @@ -35,7 +35,7 @@ Generated by [AVA](https://avajs.dev). [ { coverageInfo: [], - errorCount: 3, + errorCount: 5, fatalErrorCount: 0, filePath: 'FormatterGlobalBindingString.js', messages: [ @@ -63,6 +63,22 @@ Generated by [AVA](https://avajs.dev). ruleId: 'no-globals', severity: 2, }, + { + column: 15, + line: 20, + message: 'Access of global variable \'ui5\' (ui5.walkthrough.model.formatter.statusText)', + messageDetails: 'Do not use global variables to access UI5 modules or APIs. See Best Practices for Developers (https://ui5.sap.com/#/topic/28fcd55b04654977b63dacbee0552712)', + ruleId: 'no-globals', + severity: 2, + }, + { + column: 15, + line: 24, + message: 'Access of global variable \'ui5\' (ui5.walkthrough.model.formatter.statusText)', + messageDetails: 'Do not use global variables to access UI5 modules or APIs. See Best Practices for Developers (https://ui5.sap.com/#/topic/28fcd55b04654977b63dacbee0552712)', + ruleId: 'no-globals', + severity: 2, + }, ], warningCount: 0, }, @@ -279,7 +295,7 @@ Generated by [AVA](https://avajs.dev). [ { coverageInfo: [], - errorCount: 2, + errorCount: 4, fatalErrorCount: 0, filePath: 'ModelDataTypes_New.js', messages: [ @@ -299,6 +315,22 @@ Generated by [AVA](https://avajs.dev). ruleId: 'no-globals', severity: 2, }, + { + column: 10, + line: 24, + message: 'Access of global variable \'sap.ui.model.type.Integer\' (sap/ui/model/type/Integer)', + messageDetails: 'Do not use global variables to access UI5 modules or APIs. See Best Practices for Developers (https://ui5.sap.com/#/topic/28fcd55b04654977b63dacbee0552712)', + ruleId: 'no-globals', + severity: 2, + }, + { + column: 10, + line: 31, + message: 'Access of global variable \'sap.ui.model.type.Integer\' (sap/ui/model/type/Integer)', + messageDetails: 'Do not use global variables to access UI5 modules or APIs. See Best Practices for Developers (https://ui5.sap.com/#/topic/28fcd55b04654977b63dacbee0552712)', + ruleId: 'no-globals', + severity: 2, + }, ], warningCount: 0, }, @@ -326,7 +358,7 @@ Generated by [AVA](https://avajs.dev). [ { coverageInfo: [], - errorCount: 2, + errorCount: 4, fatalErrorCount: 0, filePath: 'ModelDataTypes_NewString.js', messages: [ @@ -346,6 +378,22 @@ Generated by [AVA](https://avajs.dev). ruleId: 'no-globals', severity: 2, }, + { + column: 14, + line: 12, + message: 'Access of global variable \'sap\' (sap.ui.model.type.Integer)', + messageDetails: 'Do not use global variables to access UI5 modules or APIs. See Best Practices for Developers (https://ui5.sap.com/#/topic/28fcd55b04654977b63dacbee0552712)', + ruleId: 'no-globals', + severity: 2, + }, + { + column: 14, + line: 16, + message: 'Access of global variable \'sap\' (sap.ui.model.type.Integer)', + messageDetails: 'Do not use global variables to access UI5 modules or APIs. See Best Practices for Developers (https://ui5.sap.com/#/topic/28fcd55b04654977b63dacbee0552712)', + ruleId: 'no-globals', + severity: 2, + }, ], warningCount: 0, }, diff --git a/test/lib/linter/rules/snapshots/NoGlobals.ts.snap b/test/lib/linter/rules/snapshots/NoGlobals.ts.snap index eb758c3c96ff65982a59621b11459463ee7102b1..8f280c043a1ce42042ac8ad1eb3a1633b99dd4f1 100644 GIT binary patch literal 5649 zcmV+s7VhamRzVxQTGy7hxUX~@l?XhKB@WE+EBu(1KVmTh9= z7!n(1cSrKTYG&D;l`T>rq)8JXkOxWH#*mZ-@*oeIcS4hfB#<_NCY+pgd=l=Q_(Jtil2s#-$T`VYjeR^mqV>WrGvPg3FoJ+kCkWeyg#ssm>w(k2SAm}c z?E-8U@?Yf7-Cqz3hfPgQ2=xelga$#lN`Tpd2f^1YMB@6Ol9v0GJzAfR&`66Q4qpo^ z>9n4{P|s?Hb;@fF<%~}>_Q{5v+->_9woe87)TDAyHckwmSmK~IYm|R6VwAxFC6kf+ zm5lA1BPZpwrfU6X-aL4QK0RmXGbo663-A%)Or?8ASNf-gVWmGOz>5MjiLh3LJ4Bf8 zf&Ctv(tV>T{ft5T<9aeXpygG%&UV$CR5c}k%`O?pt6})>i+AagrW;Z=qevMet!n)l zsZUQ!9eu<4?T`lLWLC*YswVA~2jxTYw3;#`SxZQVhf+%JJ-4f)&bQVdy5W`<8+ zbx2X9ol3@#4yNU}p~jWm-F7L1N>WcL>5SBQ!Z1>qbGy5<>ZWK$PDSJTKzDn$p{LY% z_l9%&;)zY0V(WS~Z|d2)WlL=H#)KS?DayJ{n>K7&zhRYKPw8w@*^?Olvo)(_vZ<7w zHk3q{oKm~8<{0gdT1FXE(yB3(*PvLb$sV%QH>%0j-8qv@)>l)LEvT)gChIrt@QVU` zU4Tag_@Mwlb24F{1;T&lOt?-EIz@(&iJOO_b@&1E&|@9`yLsrf0GsW}nfEy!IP8H3 zJ@A)a_=LB-xd(;7qh9#F7hdo}#0RH*aGoC$ew(PjMD)#mxWx}2^}|DckOJ`Y07QZ| z(Ljml+8}HQ!oeUUgYZNUPKV&3kWDmLBKk}So((}T49mmt&M+LRgTJV=iH62Gwbe~x zYTLIX9#=9MN$->Ttp`pTl+&slODa-FR^8Mgb(&MmNjZ6qaU!i}`%gp%^n{X(7G}O^ z#*mF{=CE?guum+b&u`*sMog!+n`R_Km*m`#F59z78qgD2^Q@khb{yQ3iAqyDH-q`hxs^RG_)ZK%MW?Uj`iKLRwL1uLpxNC`YoOu*RK-<(}mAF3pbmGe(Ufd^Dtl?{>fu`C??eBdf@^u9QVR?Uig(4KJ9}i zeU5&?D?Y#vi~Vq}A3o)WYXk89fTLgVKmZ;Lz)u43N&qem!t4-ix0Cx)zu;I1;vu*x z1b2m?ISfAt!ym$qe!-$TXs?4Eb#SZ>zElTqh`>FO@_s>32s{*l??m925%AW-_4RO1 z13cAW6AhP${-FUpjj*&4wl~5T8sVuXXlu5K)|H5EZia2ma78l=HpBDH(AEM6T5O_` z5>dScvaf#ZjuyDT1^lz&+q2=t**4Moar!{vGHZs{Va+%^&Nah05zQKEG863`xH$J4}_Pl|ChHXE+W(*Mj#GXFMIH43qpk}0m z(x+-lLee$sN?g~Bw4O{V>9rQr=DDOIo0}BYAT-^O4OQ2o(gEXyl0KN`eHo^Wy{9RJ$*f~cr3PV(?)sgR$1QMvq|pjD|yy!56XN^?~(^%YJXPGX1Wyf z?p^u8Iy^YSBp;k6_rY-=oQXU*E$)NkJ~*{_a9q+}y_jmWTuk-K{Yt-VsDnzG;Hq#} zRfDTv0e%K_3b3=%;Homw;Ho`pI>FfLii&h+4fUqlMnX!F7bFMP-geLi@H zePhdC#SIXrWTH#U&kv)|47pjOJF9kE*SgJX-R`VW^_Y>9>+gbhHm3aUN~Zh*+Z=7` z7d}uEj=oBO3oD)a`=*$qudGaY)>z*(MI?Q5B}gBU)8@(YfZtmaz}E?|w^G0drWo+9 zap}++^379(`>L^Uuf+fUo0>p(-JbF?K}=gmRhBijbd3nQ??VEZ{`6-B_$L9r?L^db*=0W{5cS4lPTJmLPTE`Y zIcY&5@Sq6a65$y$jm-nsdZ5P($DE5!uJ`8ZQ||V{SG^GR!LNNV-?`|-%tgDv56As* zogePvy3*IyaZ;1=uG;r5fmZ5v)owoPZgvpPqOySrRGx7h zYC`6h2(YzMR9`>UWPU*zm02S^RVzww()s&e)zy~;cqm_jQURrZvOwuuiuUV-MUf!;VpG`y<}#uaUiRkzjC zFvuMO?5Gsv#1wz04x~I`BkbX#}=M;71X-q8{E}?^wV8iF)`$q z&(*^%4G?OC^BT+7?>llSdjNAhp2^7~m&%u2bmZffSxxOzl|=V0*-&01YyFB@GMYQL zPmQJJ^iZ^LX_ZBml)Md@)h5Z@M@qgef4-w49wr<;m{jCZi$hc+qa^#Jgql|3Msi3} zwTz+23G;X4xS3h|p&G^dko0RnTDyha*ql%nYDuYwd#cA5$)IN#C z%o{7h$>a=Wuqs@3@u~!`*&1}*GBRq3~u+@5=qIu6&TbLW9kYu-9CT^60^{HS=m> zoJ|6(uGBbtCv2Sd#e0Ir85yY~Ye@G^+!BQ*v$>@pLY)Y0A}q}t&=#{SSA<>>j*4J5 zs@Fsq65-7vyhDWd+nQn8O}YMF5k4-$=S47ciXRc-aS@&t;W-h0BZBCGdJoL?K)dJ6 zFKn^H*=WHT^$f@1%q)gH#_#bI;&A`$Da7GkDR$TF@)qK7S9uHFHSh2ix@&$bH#wPy zFPn$CR-S8pShubdge%R%I_vP==3&G-eAqm!w+?@69yVAX&s|&NA0BC+zcA9f{e_YK z*k2gwoIqiuuL%@JdRw3{(timQM(PO`M*3&L!bpdLg^_+Pm>X$O@I4=dmx9n5f{h_K zV9%fM+9PMamqYNU5G)D9_Ap#-5Al`6Bz?aP!ym)2xDK|}!BKl?uOt@fGsAkJ2&{_0 z#Sw_xyLpzxAbtLNh}6T{de~bJefB6|N$k+J!V*I_t-m-Hk63$n+>m-4e8nN&e`w@d*{){5|O>F zaIh6pt#ErQ+}COoX(|!f-Ub)8LA(uaYJe}Gng_p|2Xp4b@%gZH0qk916KyRKy?Ozp z7QpQb;JyX$#|7~9h4Ab`n`m2!Xm}AcErRGG*t-bcy$CK_3^y#ciOwkz{lH?lXEA(Z zG5lmPY+VAsUji#6o9NsU(cKdKxdf^NZ;{{`2|ltE9$RV?omV3I!czF(r7(LLtXl>* zFN1yUkZre#&My&tZ#$fBhcCCozqLbjIsAG#%wJ&>T~JyovH~tx0moOsbt~W-E8s0F z;ZrMZq6K^oXbjSSCV#bG9m!w2%kx+9 zW-imLG!HWaLz9z3*O|?r)Qn`dD$)~DTuvsXm=c$VyF{6(A11|B6N+Y-R5mRw%XgDH zG^E9snQft#ncrYi&-0^M_5A3F%bly1EtBGUHkq*e*q9b~1yBKz!iqA{KR~eZ zI=nH%1};q1gUKNdBAo9CQ`wk?3sv=?a(ajHR_9e{bpP4~sd^we1Pm$wX$z-CS5*toD%4;H7O?CQ%hR?sO? zOx)dZaL+I#BV(%-=H$*9kpgU(3eJD5yh6-O+TswpoRv%sSdYSQRZwc({QGc*v>lbPJB9P?WT*^H8qhKrDrLzS(= zA7)RnQmW&Orlh)QrQO#3cb`g`Z;@1WZAT)ZYnhHhQB|&pYO0sy8db}c1I{k#0yjf| zbE?sIfpdX#fg6>suW_Jx4&43yHR0|n1USD^z89Nf?!L@fan?9*m?9#-w1Du@-hGFa zfmG6}&^)rC%*%4>o~sFuUns!lN^!k>!aUyoT8s$-J9jL7S;NcLh{pTPVoI}B*aHGQ zEWl#|{K$#Q=W6wSQK0fmi&XygMJoT-#r9y|&$kB)3IVhBvBd-HJ<#ic?|9%gFMPq7 z)j#fqAA8}IJfpwGYz&sC=?<*^eSWyp4-fd^akDvd0OkbY0_R3xR|TOj2yYI;-vwb# z2%Zdq$GH($dl*)Q;o>mF!|)$r_)r}@>d*+xag|#+iLLHqm8K$?%@d*-c_5WkqUMXu z^G~M|&BPT=POEzK603?K+NY-nWaFTmHZpGTZhUmbezTE4wmJZLczk_oEs7D{p=r7y zn?)yi@cMN_->n&{VU^yy_>B)gdy)Hi5+sjT-4NsYKupyX*AmY%lCQ0fB`U^#c~I@Q z&Y9G}h4Ab_Sp8Kop&!!CWM5V5cYz$m7aYq|=qckVG=6#}dkR%*Cp>p7ebv-c2!2L@ zFO3NHo00sV3-BueUaqX6u(!}q`23>Ba9z=3_Z@ZBExhzB0_ zz|$VM!V8OhaFKIEVe|Fs$-MvYVIQ>m;R(OhZN$lsSQ>zi0PGHc9Dv7O6}ck_Uvc&$ zejJ3K1%VJO48biS=ncc5vmf!kFx(l22g2}p7!K6ItO%TEZy{3JQ22EbkRxzo1Wrfb zl?Z&b9)986P}tu9kp@`Z02ejDy$x`p5#BY~hQjuY!trDqUOthfcCcsE)z<3twNK3$ zF7qA#Vr!qwMuIrU0mK8<0x{=#y3maeI(r7%6TcPbf@nLMT^kC}voL1pCT~K$IfXWK8qN|JRlcPshWp~w8IUbjFRdi9-<9axv zoP!FAu2tRBGpU~G>X~$BCTBZ;rMU^*}0~))P0H@BKT)QmwRd zvga~IQw%x1L%LKqMr6xUjLXt`TuxuCYDrZaIuyS_Nm#KPOx4unj5(}Z=4}rold>gW z?bCdOu>T2RLEHdr23mz+ixAjt81k4Pj2|^MH6cV01cZ7)*f}l~Isx1aoC3Z8{1oUC zV3&~pqG0a+f>=0gZf-_sKnNl<3c~dQv&MKy)YSMmrRK7A{s2NL=wWM@pETiQ9jJa;pn7_>)#gf&uIez;3D-}h$ zSTQZ>upuWbHKFA0wnrIN(t1WQOsQwsvNGn5zP_wF5HsaWETNC|b@f?#Mosi>K0lR6 z4h+OM_HP~N-@a{IeCw8^oQNyR#({y&+cs@p>(-NzO)LA74gJlzY_h(pnruOBRW(`vhXwe!0G|`!t6rw- zbCmz>oaxpJLPTU7k-hez=s~{5j~l;&+3M>`|v(9Ce7_S6w&nF+=$&%yJ+z_L)_|WxSf-@ zHF@B+vjlFFJaVk96Ij6m&ie_CdYss`?n6Xh+IT1TKk}me_?F*{n*(CvX zlAX zMGt>p=Rndej{E!ggulN(@9*mc;d}OBE;MNMVJpTyw8M8H;2hp=9|oPnuiA$p=kQNH zCpxjA-rh% zA^1@UUJAh#VQ8y^U3H}+hOiJiUI&RfxUCNEuY;Bdd^Z9wM_i)eBGF~_&{Yq+>*07k ze6}857lj9-F44Lo(XU3~>rwc56#NZva|1ls2v0S-L?cC_FE@g(305}2t|s_&6Fk)n z9W5@=`XbS-EwHl%u4#eM7I>}&I$GgSt4lOmB&xSU_Rk-?rxiZl3c)t`S{ppy<`Qj~ zVN4b&;UoMWKH|s>eY-Ur%UWvMj17)muB0=H5!1E9hGLqR%35OV;;d!qTFrON%*U6) zFI2npJ zIhYzeV7l7b8A!vqt%1~CY&6}{NY=EZxFTf?C8eHJl2Uw3lBJ|>N!_t-Tcw7QQVd7q zQd&>QX-Shu+_~FnK>QQ?Qj#^S6h@$$Qc_8&nv#@s&AF1$HOtV`X~o#!Ky9B(E3%!S zbOxd6mTalI7LyKH!-{c2H5Ex6u~W&;M|03iqpB<=bVJ!;D6h?`hT>$&$G>j;=Jto2 zuP^+H+)QzHPWJsAZ8`b&V}l2zqpEVkW1_@2rsDlcW!sjm{i*(VA|Bs3utna!U6!}@ z56G!h(Oh>;l*O9fE04t0p{$-Ydlmccz4^gPBkV@ zEoiS?PBm64r+VE%Wk|NvQKdw3Rk**R$<@yTKLL6KxVYTpsxsT;syk~s%iQYfvUKMR z^|tCfJDoOlI*zkcz|0)$04PlQ`UxY!3bo@woh`vK+TwOTxb-JdCEx}_d_4a}vi`uc6^;(sd3n_$83#FS>F=)cvZ}Kl>j@-HRtPQZO-m_ zHcN9}URrC;AYWG{tM)x8z#{^D(#x`QkFvg4C~bL3$UW)`3!woKE)d~r5waruOoR{n z;5!~h9u`8+``~vz=~42p{ml*SrJt|IOE?H~3+#AMW+T@c_J~ngM#C7UP|}0eWK% zym=6yuPL#>mk!h3Qx)Gmqr&9lbL_j9mDHIt#IZSY+~<|RwL~LX;MS_R?-c@UFBjF< z%(eSoQbJ|U2v1haiI4YvK3bj=|9ruT|8~NO+YbDE5sr#ri11Aj-sOYOc{}m{^1+XN z@UjmU`N8tTwg6mTO(*W|{;Or-=cT*$swS?%R9VNo!E*sG?Ve9n#XYYP;G%LD^28jw z=dLm|=M40gIdamyB`_{4Rd!!hK<*Y`cex-Z=NRPk%2Ay&(p%>U%o+`HodwkQP^Su`pd=jRkM!5+@4~Vz;;ZnEoX3Ml>)HvKMG){M!qD#Hw1W6 zfTsnpxix<46_n-L@x(%x->!)+zmpSPe*ZMl<@Zam(B-#wqRTIt@A3-^q4)dXAs>7t zADE5$;RQc*d-wBQ7J$J3+!%nj26BCU_XpwILC=1^=YsHp{jm`Ah2UEu7!1QLVb6ZP z_lM!3FnlEpKMupqb?}EehoV0&OE;h4kK~-y_UNk?RoVc6g#T7%fYI?6al1Zxx z)v933E0w@0T*GPJ;M8>ng&Mb%g_9jKl)$QRxrSFcc%4@QFR!+%*H4R=U5egkjbze& zdkzimwk$)9XDu5r`@%R{_Vw80>)wbt-~0a=uAUNbxrsz-aLsGDqBXeY8(gbPz~$B4 zSc9vwa23<-O|j9uu4LZDmp+&2_A14Y>7llBfFbkUHg}H?4xDM1f%_T8Og0gh?@=;_ zl8|jy~t%*(X^SAaV6IT;m*^wQ;(g_biPwI$1}~kRF-6B?`?p*_9wdy$Bs5 ztjrtGHoHwngh3IGiI5OM6Jbn*H;M305$<)7W4TSa;UN(|BEqNgG?(8H;Yksm7U5YD zek+1#mloO7mp*1I? zwO5pj4A^PCx+tuT!evoNxY@mma*%;w14J8OLj&w@fRsB6Sd@DVv^K(`M%dm6uWAI- zO$1hya||qNg03dGqzU9ExY?aBEXp+o);GhZX1Jmm^k#T_vlnMrU~>zc-vY<-oMCsn zX~v4U!U8)Nz{Lw7z5s4r0QW5@%@GzB0()9ve=FS33U6zL2i=ThjYT2{+TfLKV6?%z z+Tf#ZCbFg?k^Sv(xE(U>a92A#((V#zE)v<*0he?@q62R0fcraKA}vKC7j(kzPLMm{ zmQFa;=@MB`lmp(i5H48=@1-Gw)16`2qa)~Z35`9k>oa%zlb-{mhLF_#E^?9&(wM%qKk?4-qaM5bG zb~W6z8os<5-n<4rw#Fs8v`F+@Yv8+U;Q2Ms*bUcq!^$4m-{TTpR*Y*8WP0GP9(be& zUg&|(u7#)9x3i3;l=v<_CUgWc=k?d#xc>)^$8F3}Z5afh3%FKh>tL%K0GP0)XO z{OZ0sIezUc9lwgVbCu?ldDs;gnw%cH$tH(TP06M%(34U^PN$`~l90z)Bka--JH%C! zie}lNtRbz+vrAk#rX^O{G!d)pZ?Hqp3u9UJ!q}wCJ!@C3k`j70opkoGaYafhmST+9 z)E8COgqsJyD9u~}MdV04rqV&~N|k@UTY3grcJYo%OoU;F_UzKEmaw@=a(YN)O)HjG zziYL~?~RK+Ip1ypCx7)q(YgF#w+M^#jCLe-M`iC5dISap2iAAQGr!c}L5fr*^T^1CqFM7x6Kx)v zcx!bWhmuasFFkb3i5{$Bd7jM&ca>1l!=K8jA*X^HuE;9Jn4^UJ`8oXc?wStwuQx03 z>rE>g&s%}2=~~SGdk%lHJ3C$tz)#ylBy_-G?`A+Motg-P*2hx&SyfE)DS>}Ps#uAw1l11H8Pd;lT-yol?VN*G5mV02czI<$I zuv88fucYj{t4t^9l$Yc1|h*wrmCGKUP{Hb|vk^5bXEOd+DW^ z)vr({TDss}xU(um0}BP%Ty9P6RdY-<&_1QEoT0sTj+g~jO~P?sz6~Nj#zQtE!jxFn z{e4yHqn8P=tz0axnRR`%+iqtG=Avm8=8SK&N^J`PyJg`Y{;a9b2=HYAzUkG%kgN6m zUV-STb%Imi-~^|@?RicC``xx8d|iZ}i{SUcaUZO=$r$~flmR#U;f;QHzz<*WLq`Cf z2|$N8Wx&=T>5G+nwnk#j|SK>Q%LIS80xo#Pzh9d$m*k(LvT!lG1n+QhKaBKl~x~O>|0i zpGH}z+g94=+<))MjQv(ARo8YWle%Vh7n-VaO;mI3lB?7$R|+_{WDU4;1URoA{TgsJ z;A+548LqGQ2=hE*_j{`nyRR1D!gAxi_#DUXtGpHGjPvz#6v(eEAbf1_z!7C6lXf~Z zk8UnOo?riLRpR)i0&Fc8*Q;k8$GfS!X9?`W>Gb6cFI%NBK4>>n+EihW3Glc8PYCb> zuTVbMtM{`)D8F(dlz+`cDF4?J^kCn~(}RVDklp*(>Vr)_81%u{eQ>8AKJ6XVKk0`b z`r)N~M1O}(43-bmJ)-*e2I0OSJQjo}gK#7Song4hn+WXsFr>oprZ9XU44rlG{W|b@ z6M=O_U~L30i$Ed*{~du3*2BMh5P^Amxix3-sQY-ir3f~8Ld=v$GHE4dzvwhSJC&H3 zP&C<4^;rJR?kU|Ek*&kBVVSk$-OR!j_su2)+3o@4k(u$-Iw3}Ew>JK^+&p+gx~1>c zEY)&a?`!zY3_o|1`%DTXudTQtCiIcGswp)~Jj+PFsxp?SSO?`%b;vnqhXyqW&mM%8 z_lmW5UYFhrPPbF&FR@c-dh}f0DO89~c;R&Vs_IT5{0RX*J1N<3XYzk4z%K=Ou{=Uy ze}Pc=!igQjO%pqYPfhF?elF(S_gO3!C(YN zz4sCCjlg{ocq{@>M&M9AEQrDdZVHiNLg80OL5{*LQ8*Qam!j~62Kbpbp>VJfqK&Yw z5iV_nhZ#=Z|g999Z*fH7WvNH*}50A$sl%mfOx1< zAm(qmP>79chx`~Wu(kJ1@ b#crCYK~*)V*4LmqYf$|kjTrcZ(5nCdvU8$x