From 9508da31b086d3b1d5e95e85b0b51cf6144ec9a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andre=CC=81=20Rivet?= Date: Mon, 27 Jan 2020 11:16:40 -0500 Subject: [PATCH 1/7] - use _dashprivate event to communicate between link and location components - use component plugins to emit global event from location component --- package-lock.json | 5 ++- package.json | 2 +- plotly-dash-component-plugins-1.1.0-rc1.tgz | Bin 0 -> 2252 bytes src/components/Link.react.js | 2 +- src/components/Location.react.js | 35 +++++++++++--------- 5 files changed, 24 insertions(+), 20 deletions(-) create mode 100644 plotly-dash-component-plugins-1.1.0-rc1.tgz diff --git a/package-lock.json b/package-lock.json index d1d0b810e..7a0bf4e3f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1995,9 +1995,8 @@ } }, "@plotly/dash-component-plugins": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@plotly/dash-component-plugins/-/dash-component-plugins-1.0.2.tgz", - "integrity": "sha512-KBO+rZk0MAht3/cqdzKd5GUc6EJDWj7OTPw9u6Q4HALbaO9KzvYFXN0PdiNGPv4y1bINjPBI3bw6i4smqVrrEw==", + "version": "file:plotly-dash-component-plugins-1.1.0-rc1.tgz", + "integrity": "sha512-BtJpIkQz9h44WQmcdwDLVngFKVUu8niXaNtMQNTBS8VG6KMQykBcBibHuJ9XREbd4/+glxkBamA6sVkhxqUn1Q==", "dev": true }, "@plotly/webpack-dash-dynamic-import": { diff --git a/package.json b/package.json index aaae4805a..74d0b952c 100644 --- a/package.json +++ b/package.json @@ -59,7 +59,7 @@ "@babel/plugin-syntax-dynamic-import": "^7.2.0", "@babel/preset-env": "^7.4.1", "@babel/preset-react": "^7.0.0", - "@plotly/dash-component-plugins": "^1.0.2", + "@plotly/dash-component-plugins": "file:plotly-dash-component-plugins-1.1.0-rc1.tgz", "@plotly/webpack-dash-dynamic-import": "^1.1.4", "babel-eslint": "^10.0.1", "babel-jest": "^24.5.0", diff --git a/plotly-dash-component-plugins-1.1.0-rc1.tgz b/plotly-dash-component-plugins-1.1.0-rc1.tgz new file mode 100644 index 0000000000000000000000000000000000000000..cb561944dc5236b72bd6e91773501f5716e13643 GIT binary patch literal 2252 zcmV;-2s8H|iwFP!000006YW^ z;q0exJQ|J0`PmscM2FFQ@$!`X^y1}tquD$^ZvY>>ygYja&udBoU zyXXJO*wzTqn75)@i)e4-h|6em5_rk-*ypR5Gs@&~9HonpC7Ox6cT^-HXUN{PnpWdj z_|1GKpouTSSQfLRT#C%OAU#k>x~0NGlb|tALdnGj*bxWN@0T zDwl&WktZPw=x3;-d@2vg9->(qMgcOf=?sgG93O*N9C@2#K|QFx1Sm5vj4C)@DwkZK z%#|nn;|>eNzyC$X;T_F`q(z}mS)$-xxzlnr?s_z)0EYP?-2?D;=FMny;&Y({etl)V zvQEoj7n8A|36;l`-R)SKhgkr#FLz9hl1cF#N8Agj0{-i3t6`m&vz1Wu+5BDVLxo z;d8n61O?`RcuB(j&;xxW;C{M-jl#pdgs9pkx?EDCO$rIkP(}jkMYWI-oGYiu8ag#k zC4mMdMd+grlZ4EV(f~1((^0qz3kxW&{7!11G)=$*#A%Wh55hS6=D5`-5=(|MuTA@=s2tCGt$|6JwQr58T-1j6*YV%YuFiDk6 zz#(MH;&0TKNI~{Fk0QQC*L==`5Iss-wb~S>ycxfv%9ngu8J7Sxhky$ZSF2h~B}*@g z$cz>N1G^AjZ_gcp=AmYo484fNTqt`F-L$~|r9&pei|L2X*dgwOjK;%X+@8}TT4w^^ znn^ym)63!YlmN%LGnjrP!wb?Gd?bH!2R)NGe;=x2kziv3|5>gXE3$ED$t1Y3w)5tWvAaqQ?<@DpdTYX(jAUIj@|c{ zQ*t@%_Z%R;a{yWAUEj$~fm7Xn$GtL1uXELT?cPpvUB8kr88X~5T}Ud6`P z3MyeMCi|p2PY`Brvvx@&QWwTHoa^jH+!}=hzL()?qX@T=WN*WkFr(H{(<~-i+bnAr zLKKC3*mrea%9`A)-lYK_k$u#tXjvgDt(8qpY56vT{{wq}flYuXEw0x3%a@@Fu9XLV zBsiP~rYvH%xgk)qm;I^E0tebW|&V2Yo9*Rq+lz>jv6^!Y2VLpSes^@)$2O7B{*Q1 zuj{N_DB6bEGn{>#cJ(^`S(T+q2$xjY1vnpaO;VkuQDmSx2pFWq6VQbTxAeJ}MsSUP z5b@j#3SOToK#7V=6=Z76xzJTM;Q@^rpd9pZbLTGGr7XQ+x0yAAoL0C@ZWv{n35*f5 zA1Cz#Z{O@VDy>$5J!>79+$A#NVy5P45Pg%OIsRZt3dSNtvEY)Ulh)EphHF*^D=!8V zKpnBIX{LneCUz6*^``O~Dr3ub-H2x9z4VuuvI(4W)x|(-i6oDBM~O=@N878KjQnVw}RUd)`X_0E5nuj#6g%|5mKWac0DY4TQw~F9EKgww@TKLc?3$#jUB6+ME83YN^ z8bVFgo3Trkr|TP=b}Yg>D6LOn(ubh#eoIt#p?omxk3+LV23Da{0#jzD_JIiJ`Tj?C z_Bwr-r$^+4tkd8C>}A$5yzbvjucY-`-Z}F z_8`YHGgr%5w{ZV~t|}D@on8 { - return () => { - const {setProps} = this.props; - setProps({ - pathname: window.location.pathname, - href: window.location.href, - hash: window.location.hash, - search: window.location.search, - }); - }; - }; - window.addEventListener('onpopstate', listener()); - window.onpopstate = listener('POP'); + window.onpopstate = this.onLocationChange; - // non-standard, emitted by Link.react - window.addEventListener('onpushstate', listener()); + window.addEventListener( + '_dashprivate_onpushstate', + this.onLocationChange + ); this.updateLocation(this.props); } From 4d62d0f89a0de6ebe1ff61a889e37e1b843ff75d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andre=CC=81=20Rivet?= Date: Mon, 27 Jan 2020 12:11:09 -0500 Subject: [PATCH 2/7] remove`on` in event name --- src/components/Link.react.js | 2 +- src/components/Location.react.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/Link.react.js b/src/components/Link.react.js index 8210f9797..6839bc2b9 100644 --- a/src/components/Link.react.js +++ b/src/components/Link.react.js @@ -47,7 +47,7 @@ export default class Link extends Component { window.location.pathname = href; } else { window.history.pushState({}, '', href); - window.dispatchEvent(new CustomEvent('_dashprivate_onpushstate')); + window.dispatchEvent(new CustomEvent('_dashprivate_pushstate')); } // scroll back to top window.scrollTo(0, 0); diff --git a/src/components/Location.react.js b/src/components/Location.react.js index 4a329098f..69bc18b35 100644 --- a/src/components/Location.react.js +++ b/src/components/Location.react.js @@ -100,7 +100,7 @@ export default class Location extends Component { window.onpopstate = this.onLocationChange; window.addEventListener( - '_dashprivate_onpushstate', + '_dashprivate_pushstate', this.onLocationChange ); this.updateLocation(this.props); From 9f0aa42230a82248a68b8aadcf6e36a0bb313bd5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andre=CC=81=20Rivet?= Date: Tue, 28 Jan 2020 16:18:32 -0500 Subject: [PATCH 3/7] link / location event test --- plotly-dash-component-plugins-1.1.0-rc1.tgz | Bin 2252 -> 2246 bytes tests/integration/link/test_link_event.py | 55 ++++++++++++++++++++ 2 files changed, 55 insertions(+) create mode 100644 tests/integration/link/test_link_event.py diff --git a/plotly-dash-component-plugins-1.1.0-rc1.tgz b/plotly-dash-component-plugins-1.1.0-rc1.tgz index cb561944dc5236b72bd6e91773501f5716e13643..974666a72f08907a41bd52a4e0399ecec4ce6e57 100644 GIT binary patch literal 2246 zcmV;%2s!s3iwFP!000006YUu7a@t68fAbV${h*{;Sq8^(a&W4IfE=q1110e0QZ8>w zNCRkB%;-iV6OQme_Z;_P*FBODwsH2yWs|!qPdSm)-`&&QGfmCJ0(vOtO zVU)^~h=ues)KQMgL$YVlG>hU8iC1)rMMsX0fh~#s^|7EnR9^~^sUO9aIesP=Tp-Vt zFM{I^3&p?xMZD;a=0VcpD4;A==3cpzayRaJG@)P&3nIE_z}u-mrSVC?g<|;imG#Ox zErVSk6G2ldk14y`ku(pJfX#v25j9F8MROc;Kcvd=Ute1d>%7EPOwDuabp8r+b|NkBf`UUCd?W0!f@?} zDOpfKr)x47K9e*w$xKj6_>2S#U(BgVBqu&wlLXERVBk{;4KiYL;*$VW)PPbhKupSK za^(vO#3Aw1ln0Ry@<_;oYzYU2k7o%{wM}%nphO!N9Gan&gw&5~5hI8zQDg<3nrD(g zgOVZ&P=-k&7Q|VIHk9Z%T1JHiWLJKtH4vJm-~rlck|htL89u3UE6JvDlrBty{Y6C6 zOad@PpbDUgew@H@m(n<{fduHgMEP1-r)bdh1OqCI5TyuN!Lf7SlPImtGQmJ4RXibw zkSUG7(Lf>u$!9!{`3hYNI13~6C~ehh6F~V>en*ur`L;4H!PMLY+<>Io)dH0+{5U34 zS_BOALU{c>cLb`3nqe~XW0G*8^gVRb0{xc`84oWeA3B~x+%Xw>!(ZH<(<54E4Bwhb zKDd+1;q`<7h1VHOK9b=D=?p%Szqx~+Nu0lrJZC&6L$Bsujry(wFn7@HU-#U>d-4wW z21CeQ7cv-xPKE>(7NT5djKZ!QuX_ofop)~EoqRNF7w%+$GB1W6>5x&!o4DQUe#axD zYi~3hJD|P?Vh8Tv!UHYNl{1)FpcP=m`2{}6__EXQqpDix8m#xwKGGeIK6>u^%L%z0 z_InNh-#K7e=Uv~)Re@98e#gBsNw0I&dG9E$AxQCRNS0eiK3qBoLVX?h>rUL^0Nv;g z2NMrIP4Lp2>{vgzW5*;N&mChxF1+EDS;KGw$506YzJZfV!BCTXI|3y5eLZ$|0!h#5 z^g-4bnbF0P*sA^LHub#zq*b2(_ijwzSjX4T|Cf!^$MfI$`SbkuUDnYoW9o?1smY9u z#?{fH*)~pM(A4Q?Z1HKm{s;qSV;0jl`NJycY^DqPk1P^YFK+pU(b7sC6>~^u5u^2b z{a2rAPCTa;YZXGqv0@>PZx9x(7B2xh8 zuFgwdlbhAMG~gq)j~W$hTd+!NX;V{Lfz9Cm(B9u*6U>tqS9|^C%TNW^$^$UI3HDoqs=ZmF;f;{wPvNp+URv4P?sV2~0|m@Z71(r11a!x;Y{;<*saFEB%ox5z8vh;@CZmlWgw6e?OhEb-Oz!)+6aZ*3< z_RWr>l4=#UZ(9eDyF^C2n5p?1MBiknjz8Fvg0KitBDmz}q_yzV;fj^P%8LORP)1^F znkgZ=sojKny{Wv0%Gj`7H=A=Fg8 z8M{<@y1ub#CnCCo()tvoeF*CAw?w5E$_K;#I5a!UKr3`gAljOleIUYlzWR>tS7ybO&E=vkfBrd79Ra;w-nNBRmAAeB@#PEje__GkHjCz2j)(HsRQ+pE z*#df%uvP*_)g1S99~9Q_bPXMTyG6~ueLrq6?%_|ZFrl+K^vS^9i`LF73Li(0I_8m% z;~>s_0rnnd7;+)_ieM>Bpi~_pAPid9XD~z(EL0ryV;JaRsE60x@eienW2Z?CWg^t0 zDazf4o?{C8W&=L7@b|mKhdk{nYn?mT0IleWduHdyv{e!-G98WT!ljNc^KTzAY-{FX zIqMegKaf==LLt)+L&e^Qs9_1{J{WC2POySp&?OAok2$v6?X9UxTb&2>dZe?e!QmM$ z6b$wZia|HpWwXTU?a_d%%G1mPHP5lt6y!yUAEe9n=q~dNI7k;@A>Lv+wtw`E^m#q6 U=k>gv*Y{Ze0T1~-Jpdp804nBLrvLx| literal 2252 zcmV;-2s8H|iwFP!000006YW^ z;q0exJQ|J0`PmscM2FFQ@$!`X^y1}tquD$^ZvY>>ygYja&udBoU zyXXJO*wzTqn75)@i)e4-h|6em5_rk-*ypR5Gs@&~9HonpC7Ox6cT^-HXUN{PnpWdj z_|1GKpouTSSQfLRT#C%OAU#k>x~0NGlb|tALdnGj*bxWN@0T zDwl&WktZPw=x3;-d@2vg9->(qMgcOf=?sgG93O*N9C@2#K|QFx1Sm5vj4C)@DwkZK z%#|nn;|>eNzyC$X;T_F`q(z}mS)$-xxzlnr?s_z)0EYP?-2?D;=FMny;&Y({etl)V zvQEoj7n8A|36;l`-R)SKhgkr#FLz9hl1cF#N8Agj0{-i3t6`m&vz1Wu+5BDVLxo z;d8n61O?`RcuB(j&;xxW;C{M-jl#pdgs9pkx?EDCO$rIkP(}jkMYWI-oGYiu8ag#k zC4mMdMd+grlZ4EV(f~1((^0qz3kxW&{7!11G)=$*#A%Wh55hS6=D5`-5=(|MuTA@=s2tCGt$|6JwQr58T-1j6*YV%YuFiDk6 zz#(MH;&0TKNI~{Fk0QQC*L==`5Iss-wb~S>ycxfv%9ngu8J7Sxhky$ZSF2h~B}*@g z$cz>N1G^AjZ_gcp=AmYo484fNTqt`F-L$~|r9&pei|L2X*dgwOjK;%X+@8}TT4w^^ znn^ym)63!YlmN%LGnjrP!wb?Gd?bH!2R)NGe;=x2kziv3|5>gXE3$ED$t1Y3w)5tWvAaqQ?<@DpdTYX(jAUIj@|c{ zQ*t@%_Z%R;a{yWAUEj$~fm7Xn$GtL1uXELT?cPpvUB8kr88X~5T}Ud6`P z3MyeMCi|p2PY`Brvvx@&QWwTHoa^jH+!}=hzL()?qX@T=WN*WkFr(H{(<~-i+bnAr zLKKC3*mrea%9`A)-lYK_k$u#tXjvgDt(8qpY56vT{{wq}flYuXEw0x3%a@@Fu9XLV zBsiP~rYvH%xgk)qm;I^E0tebW|&V2Yo9*Rq+lz>jv6^!Y2VLpSes^@)$2O7B{*Q1 zuj{N_DB6bEGn{>#cJ(^`S(T+q2$xjY1vnpaO;VkuQDmSx2pFWq6VQbTxAeJ}MsSUP z5b@j#3SOToK#7V=6=Z76xzJTM;Q@^rpd9pZbLTGGr7XQ+x0yAAoL0C@ZWv{n35*f5 zA1Cz#Z{O@VDy>$5J!>79+$A#NVy5P45Pg%OIsRZt3dSNtvEY)Ulh)EphHF*^D=!8V zKpnBIX{LneCUz6*^``O~Dr3ub-H2x9z4VuuvI(4W)x|(-i6oDBM~O=@N878KjQnVw}RUd)`X_0E5nuj#6g%|5mKWac0DY4TQw~F9EKgww@TKLc?3$#jUB6+ME83YN^ z8bVFgo3Trkr|TP=b}Yg>D6LOn(ubh#eoIt#p?omxk3+LV23Da{0#jzD_JIiJ`Tj?C z_Bwr-r$^+4tkd8C>}A$5yzbvjucY-`-Z}F z_8`YHGgr%5w{ZV~t|}D@on8 Date: Tue, 28 Jan 2020 16:20:42 -0500 Subject: [PATCH 4/7] changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d381b1920..507a9c177 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] ### Changed +- [#743](https://github.com/plotly/dash-core-components/pull/743) Location component now emits an event on link update - [#739](https://github.com/plotly/dash-core-components/pull/739) Async Slider and RangeSlider - [#729](https://github.com/plotly/dash-core-components/pull/729) Handle case where dcc fails to load when used inside an iframe with a sandbox attribute that only has allow-scripts From f1ea5392cb143253d9ec2435f4bb97661419f2c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andre=CC=81=20Rivet?= Date: Tue, 28 Jan 2020 16:22:26 -0500 Subject: [PATCH 5/7] update lockfile --- package-lock.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package-lock.json b/package-lock.json index 7a0bf4e3f..2c084bd3a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1996,7 +1996,7 @@ }, "@plotly/dash-component-plugins": { "version": "file:plotly-dash-component-plugins-1.1.0-rc1.tgz", - "integrity": "sha512-BtJpIkQz9h44WQmcdwDLVngFKVUu8niXaNtMQNTBS8VG6KMQykBcBibHuJ9XREbd4/+glxkBamA6sVkhxqUn1Q==", + "integrity": "sha512-M6wn+pwQE2uBWkNgRUCx2s8dZRGC6Y7mXtIIskfVnhU7L8VaJC8Ojn/tngcZZ7/iBzQXAL8lmvN2iNHpIZ4CWA==", "dev": true }, "@plotly/webpack-dash-dynamic-import": { From 6ea3c55a44f0366ce62e356894be6d4aefeddc56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andre=CC=81=20Rivet?= Date: Tue, 28 Jan 2020 16:25:46 -0500 Subject: [PATCH 6/7] lint --- tests/integration/link/test_link_event.py | 32 ++++++++++++++--------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/tests/integration/link/test_link_event.py b/tests/integration/link/test_link_event.py index c3441cd5c..72c9365cb 100644 --- a/tests/integration/link/test_link_event.py +++ b/tests/integration/link/test_link_event.py @@ -9,12 +9,13 @@ def test_link001_event(dash_dcc): app = dash.Dash(__name__) - app.layout = html.Div([ - dcc.Link("Page 1", id="link1", href="/page-1"), - dcc.Location(id="url", refresh=False), - html.Div(id='content') - ]) - + app.layout = html.Div( + [ + dcc.Link("Page 1", id="link1", href="/page-1"), + dcc.Location(id="url", refresh=False), + html.Div(id='content'), + ] + ) @app.callback(Output("content", "children"), [Input("url", "pathname")]) def display_page(pathname): @@ -25,9 +26,9 @@ def display_page(pathname): else: return "404" - dash_dcc.start_server(app) - dash_dcc.driver.execute_script(''' + dash_dcc.driver.execute_script( + ''' window.addEventListener('_dashprivate_pushstate', function() { window._test_link_event_counter = (window._test_link_event_counter || 0) + 1; }); @@ -35,7 +36,8 @@ def display_page(pathname): window.addEventListener('_dashprivate_historychange', function() { window._test_history_event_counter = (window._test_history_event_counter || 0) + 1; }); - ''') + ''' + ) dash_dcc.wait_for_element_by_id("div0") @@ -43,13 +45,17 @@ def display_page(pathname): dash_dcc.wait_for_element_by_id("div1") - link_counter = dash_dcc.driver.execute_script(''' + link_counter = dash_dcc.driver.execute_script( + ''' return window._test_link_event_counter; - ''') + ''' + ) - history_counter = dash_dcc.driver.execute_script(''' + history_counter = dash_dcc.driver.execute_script( + ''' return window._test_history_event_counter; - ''') + ''' + ) assert link_counter == 1 assert history_counter == 1 From f75785128db53927c0042f836cc9433345dfa3e0 Mon Sep 17 00:00:00 2001 From: Marc-Andre-Rivet Date: Tue, 28 Jan 2020 20:27:40 -0500 Subject: [PATCH 7/7] bump to dcp 1.1.0 --- package-lock.json | 5 +++-- package.json | 2 +- plotly-dash-component-plugins-1.1.0-rc1.tgz | Bin 2246 -> 0 bytes 3 files changed, 4 insertions(+), 3 deletions(-) delete mode 100644 plotly-dash-component-plugins-1.1.0-rc1.tgz diff --git a/package-lock.json b/package-lock.json index 2c084bd3a..b3402d9e5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1995,8 +1995,9 @@ } }, "@plotly/dash-component-plugins": { - "version": "file:plotly-dash-component-plugins-1.1.0-rc1.tgz", - "integrity": "sha512-M6wn+pwQE2uBWkNgRUCx2s8dZRGC6Y7mXtIIskfVnhU7L8VaJC8Ojn/tngcZZ7/iBzQXAL8lmvN2iNHpIZ4CWA==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@plotly/dash-component-plugins/-/dash-component-plugins-1.1.0.tgz", + "integrity": "sha512-9jLmMpKFaHEvO+OqF4eYH4KJ6dhtgM3D6xc8P6F/7lAHGKuj3lHenqyWzsKym0U1QjV0hQZJiEgXo4X3h6bvFA==", "dev": true }, "@plotly/webpack-dash-dynamic-import": { diff --git a/package.json b/package.json index 74d0b952c..488bc0bba 100644 --- a/package.json +++ b/package.json @@ -59,7 +59,7 @@ "@babel/plugin-syntax-dynamic-import": "^7.2.0", "@babel/preset-env": "^7.4.1", "@babel/preset-react": "^7.0.0", - "@plotly/dash-component-plugins": "file:plotly-dash-component-plugins-1.1.0-rc1.tgz", + "@plotly/dash-component-plugins": "^1.1.0", "@plotly/webpack-dash-dynamic-import": "^1.1.4", "babel-eslint": "^10.0.1", "babel-jest": "^24.5.0", diff --git a/plotly-dash-component-plugins-1.1.0-rc1.tgz b/plotly-dash-component-plugins-1.1.0-rc1.tgz deleted file mode 100644 index 974666a72f08907a41bd52a4e0399ecec4ce6e57..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2246 zcmV;%2s!s3iwFP!000006YUu7a@t68fAbV${h*{;Sq8^(a&W4IfE=q1110e0QZ8>w zNCRkB%;-iV6OQme_Z;_P*FBODwsH2yWs|!qPdSm)-`&&QGfmCJ0(vOtO zVU)^~h=ues)KQMgL$YVlG>hU8iC1)rMMsX0fh~#s^|7EnR9^~^sUO9aIesP=Tp-Vt zFM{I^3&p?xMZD;a=0VcpD4;A==3cpzayRaJG@)P&3nIE_z}u-mrSVC?g<|;imG#Ox zErVSk6G2ldk14y`ku(pJfX#v25j9F8MROc;Kcvd=Ute1d>%7EPOwDuabp8r+b|NkBf`UUCd?W0!f@?} zDOpfKr)x47K9e*w$xKj6_>2S#U(BgVBqu&wlLXERVBk{;4KiYL;*$VW)PPbhKupSK za^(vO#3Aw1ln0Ry@<_;oYzYU2k7o%{wM}%nphO!N9Gan&gw&5~5hI8zQDg<3nrD(g zgOVZ&P=-k&7Q|VIHk9Z%T1JHiWLJKtH4vJm-~rlck|htL89u3UE6JvDlrBty{Y6C6 zOad@PpbDUgew@H@m(n<{fduHgMEP1-r)bdh1OqCI5TyuN!Lf7SlPImtGQmJ4RXibw zkSUG7(Lf>u$!9!{`3hYNI13~6C~ehh6F~V>en*ur`L;4H!PMLY+<>Io)dH0+{5U34 zS_BOALU{c>cLb`3nqe~XW0G*8^gVRb0{xc`84oWeA3B~x+%Xw>!(ZH<(<54E4Bwhb zKDd+1;q`<7h1VHOK9b=D=?p%Szqx~+Nu0lrJZC&6L$Bsujry(wFn7@HU-#U>d-4wW z21CeQ7cv-xPKE>(7NT5djKZ!QuX_ofop)~EoqRNF7w%+$GB1W6>5x&!o4DQUe#axD zYi~3hJD|P?Vh8Tv!UHYNl{1)FpcP=m`2{}6__EXQqpDix8m#xwKGGeIK6>u^%L%z0 z_InNh-#K7e=Uv~)Re@98e#gBsNw0I&dG9E$AxQCRNS0eiK3qBoLVX?h>rUL^0Nv;g z2NMrIP4Lp2>{vgzW5*;N&mChxF1+EDS;KGw$506YzJZfV!BCTXI|3y5eLZ$|0!h#5 z^g-4bnbF0P*sA^LHub#zq*b2(_ijwzSjX4T|Cf!^$MfI$`SbkuUDnYoW9o?1smY9u z#?{fH*)~pM(A4Q?Z1HKm{s;qSV;0jl`NJycY^DqPk1P^YFK+pU(b7sC6>~^u5u^2b z{a2rAPCTa;YZXGqv0@>PZx9x(7B2xh8 zuFgwdlbhAMG~gq)j~W$hTd+!NX;V{Lfz9Cm(B9u*6U>tqS9|^C%TNW^$^$UI3HDoqs=ZmF;f;{wPvNp+URv4P?sV2~0|m@Z71(r11a!x;Y{;<*saFEB%ox5z8vh;@CZmlWgw6e?OhEb-Oz!)+6aZ*3< z_RWr>l4=#UZ(9eDyF^C2n5p?1MBiknjz8Fvg0KitBDmz}q_yzV;fj^P%8LORP)1^F znkgZ=sojKny{Wv0%Gj`7H=A=Fg8 z8M{<@y1ub#CnCCo()tvoeF*CAw?w5E$_K;#I5a!UKr3`gAljOleIUYlzWR>tS7ybO&E=vkfBrd79Ra;w-nNBRmAAeB@#PEje__GkHjCz2j)(HsRQ+pE z*#df%uvP*_)g1S99~9Q_bPXMTyG6~ueLrq6?%_|ZFrl+K^vS^9i`LF73Li(0I_8m% z;~>s_0rnnd7;+)_ieM>Bpi~_pAPid9XD~z(EL0ryV;JaRsE60x@eienW2Z?CWg^t0 zDazf4o?{C8W&=L7@b|mKhdk{nYn?mT0IleWduHdyv{e!-G98WT!ljNc^KTzAY-{FX zIqMegKaf==LLt)+L&e^Qs9_1{J{WC2POySp&?OAok2$v6?X9UxTb&2>dZe?e!QmM$ z6b$wZia|HpWwXTU?a_d%%G1mPHP5lt6y!yUAEe9n=q~dNI7k;@A>Lv+wtw`E^m#q6 U=k>gv*Y{Ze0T1~-Jpdp804nBLrvLx|