From 6c33d7f57af6bfedbd1a2b61c64e78004a61e727 Mon Sep 17 00:00:00 2001 From: Ricardo Marques Date: Thu, 28 Jun 2018 16:11:36 +0100 Subject: [PATCH] mgr/dashboard: Add "About" modal Fixes: https://tracker.ceph.com/issues/24646 Signed-off-by: Ricardo Marques --- .../navigation/about/about.component.html | 26 ++++++++++ .../navigation/about/about.component.scss | 22 +++++++++ .../navigation/about/about.component.spec.ts | 46 ++++++++++++++++++ .../core/navigation/about/about.component.ts | 36 ++++++++++++++ .../dashboard-help.component.html | 6 +++ .../dashboard-help.component.ts | 16 ++++-- .../app/core/navigation/navigation.module.ts | 3 ++ ...eph_Logo_Stacked_RGB_120411_fa_348x348.png | Bin 0 -> 18960 bytes 8 files changed, 152 insertions(+), 3 deletions(-) create mode 100644 src/pybind/mgr/dashboard/frontend/src/app/core/navigation/about/about.component.html create mode 100644 src/pybind/mgr/dashboard/frontend/src/app/core/navigation/about/about.component.scss create mode 100644 src/pybind/mgr/dashboard/frontend/src/app/core/navigation/about/about.component.spec.ts create mode 100644 src/pybind/mgr/dashboard/frontend/src/app/core/navigation/about/about.component.ts create mode 100644 src/pybind/mgr/dashboard/frontend/src/assets/Ceph_Logo_Stacked_RGB_120411_fa_348x348.png diff --git a/src/pybind/mgr/dashboard/frontend/src/app/core/navigation/about/about.component.html b/src/pybind/mgr/dashboard/frontend/src/app/core/navigation/about/about.component.html new file mode 100644 index 0000000000000..ed570492a4dd1 --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/core/navigation/about/about.component.html @@ -0,0 +1,26 @@ + + + diff --git a/src/pybind/mgr/dashboard/frontend/src/app/core/navigation/about/about.component.scss b/src/pybind/mgr/dashboard/frontend/src/app/core/navigation/about/about.component.scss new file mode 100644 index 0000000000000..f80b0e1e1e296 --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/core/navigation/about/about.component.scss @@ -0,0 +1,22 @@ +.product-versions { + margin-top: 30px; +} +.product-versions strong { + margin-right: 10px; +} +.modal-header { + border-bottom: none; +} +.modal-body { + padding-left: 80px; + padding-right: 80px; +} +.modal-footer { + border-top: none; +} +h1 { + font-size: 4em; +} +.ceph-logo { + width: 25%; +} diff --git a/src/pybind/mgr/dashboard/frontend/src/app/core/navigation/about/about.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/core/navigation/about/about.component.spec.ts new file mode 100644 index 0000000000000..d2c53707f09e9 --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/core/navigation/about/about.component.spec.ts @@ -0,0 +1,46 @@ +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { BsModalRef } from 'ngx-bootstrap'; +import 'rxjs/add/observable/of'; +import { Observable } from 'rxjs/Observable'; + +import { SummaryService } from '../../../shared/services/summary.service'; +import { SharedModule } from '../../../shared/shared.module'; +import { configureTestBed } from '../../../shared/unit-test-helper'; +import { AboutComponent } from './about.component'; + +class SummaryServiceMock { + summaryData$ = Observable.of({ + version: + 'ceph version 14.0.0-855-gb8193bb4cd ' + + '(b8193bb4cda16ccc5b028c3e1df62bc72350a15d) nautilus (dev)' + }); +} + +describe('AboutComponent', () => { + let component: AboutComponent; + let fixture: ComponentFixture; + + configureTestBed({ + imports: [SharedModule, HttpClientTestingModule], + declarations: [AboutComponent], + providers: [BsModalRef, { provide: SummaryService, useClass: SummaryServiceMock }] + }); + + beforeEach(() => { + fixture = TestBed.createComponent(AboutComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + it('should parse version', () => { + expect(component.versionNumber).toBe('14.0.0-855-gb8193bb4cd'); + expect(component.versionHash).toBe('(b8193bb4cda16ccc5b028c3e1df62bc72350a15d)'); + expect(component.versionName).toBe('nautilus (dev)'); + }); +}); diff --git a/src/pybind/mgr/dashboard/frontend/src/app/core/navigation/about/about.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/core/navigation/about/about.component.ts new file mode 100644 index 0000000000000..471e4cef98b38 --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/core/navigation/about/about.component.ts @@ -0,0 +1,36 @@ +import { Component, OnDestroy, OnInit } from '@angular/core'; + +import { BsModalRef } from 'ngx-bootstrap'; +import { Subscription } from 'rxjs'; + +import { SummaryService } from '../../../shared/services/summary.service'; + +@Component({ + selector: 'cd-about', + templateUrl: './about.component.html', + styleUrls: ['./about.component.scss'] +}) +export class AboutComponent implements OnInit, OnDestroy { + versionNumber: string; + versionHash: string; + versionName: string; + subs: Subscription; + + constructor(public modalRef: BsModalRef, private summaryService: SummaryService) {} + + ngOnInit() { + this.subs = this.summaryService.summaryData$.subscribe((summary: any) => { + if (!summary) { + return; + } + const version = summary.version.replace('ceph version ', '').split(' '); + this.versionNumber = version[0]; + this.versionHash = version[1]; + this.versionName = version.slice(2, version.length).join(' '); + }); + } + + ngOnDestroy(): void { + this.subs.unsubscribe(); + } +} diff --git a/src/pybind/mgr/dashboard/frontend/src/app/core/navigation/dashboard-help/dashboard-help.component.html b/src/pybind/mgr/dashboard/frontend/src/app/core/navigation/dashboard-help/dashboard-help.component.html index 629c62418a0f8..b2f0945691179 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/core/navigation/dashboard-help/dashboard-help.component.html +++ b/src/pybind/mgr/dashboard/frontend/src/app/core/navigation/dashboard-help/dashboard-help.component.html @@ -24,5 +24,11 @@ target="_blank">API +
  • + About + +
  • diff --git a/src/pybind/mgr/dashboard/frontend/src/app/core/navigation/dashboard-help/dashboard-help.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/core/navigation/dashboard-help/dashboard-help.component.ts index 74a554a177ecf..563f9a24349bb 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/core/navigation/dashboard-help/dashboard-help.component.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/core/navigation/dashboard-help/dashboard-help.component.ts @@ -1,7 +1,10 @@ import { Component, OnInit } from '@angular/core'; +import { BsModalRef, BsModalService } from 'ngx-bootstrap'; + import { CephReleaseNamePipe } from '../../../shared/pipes/ceph-release-name.pipe'; import { SummaryService } from '../../../shared/services/summary.service'; +import { AboutComponent } from '../about/about.component'; @Component({ selector: 'cd-dashboard-help', @@ -9,11 +12,14 @@ import { SummaryService } from '../../../shared/services/summary.service'; styleUrls: ['./dashboard-help.component.scss'] }) export class DashboardHelpComponent implements OnInit { - docsUrl: string; + modalRef: BsModalRef; - constructor(private summaryService: SummaryService, - private cephReleaseNamePipe: CephReleaseNamePipe) {} + constructor( + private summaryService: SummaryService, + private cephReleaseNamePipe: CephReleaseNamePipe, + private modalService: BsModalService + ) {} ngOnInit() { const subs = this.summaryService.summaryData$.subscribe((summary: any) => { @@ -25,4 +31,8 @@ export class DashboardHelpComponent implements OnInit { subs.unsubscribe(); }); } + + openAboutModal() { + this.modalRef = this.modalService.show(AboutComponent); + } } diff --git a/src/pybind/mgr/dashboard/frontend/src/app/core/navigation/navigation.module.ts b/src/pybind/mgr/dashboard/frontend/src/app/core/navigation/navigation.module.ts index 54bf549d2eeef..aaf9bb91fe80a 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/core/navigation/navigation.module.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/core/navigation/navigation.module.ts @@ -7,12 +7,14 @@ import { BsDropdownModule, CollapseModule, PopoverModule, TooltipModule } from ' import { AppRoutingModule } from '../../app-routing.module'; import { SharedModule } from '../../shared/shared.module'; import { AuthModule } from '../auth/auth.module'; +import { AboutComponent } from './about/about.component'; import { DashboardHelpComponent } from './dashboard-help/dashboard-help.component'; import { NavigationComponent } from './navigation/navigation.component'; import { NotificationsComponent } from './notifications/notifications.component'; import { TaskManagerComponent } from './task-manager/task-manager.component'; @NgModule({ + entryComponents: [AboutComponent], imports: [ CommonModule, AuthModule, @@ -25,6 +27,7 @@ import { TaskManagerComponent } from './task-manager/task-manager.component'; RouterModule ], declarations: [ + AboutComponent, NavigationComponent, NotificationsComponent, TaskManagerComponent, diff --git a/src/pybind/mgr/dashboard/frontend/src/assets/Ceph_Logo_Stacked_RGB_120411_fa_348x348.png b/src/pybind/mgr/dashboard/frontend/src/assets/Ceph_Logo_Stacked_RGB_120411_fa_348x348.png new file mode 100644 index 0000000000000000000000000000000000000000..d46bd73f748721d5e12252e8377a2ee42ec53bac GIT binary patch literal 18960 zcmeFZgLkA+(=R%)F|j6`XyQz4n;lPVc5G*&iOr5}+qP|66B{@4e&0Fgo^$VCaM$g% zp4HFZT^qGo^{c85QIHcyg2#jZ^5qMXq=X3I%NH=Me-AA5XHEbXH0|dXm;*pu=u5=} z!SQDT&Q?Oh;ma3#vVRYlYd*j8C&8$>vbv+XtPGc-4Uk^n$i~2!-W6#3N&V#uk1N;b zE6~_cpV$>>W$nP_%1inm3a-!hf7uMA#Q!02wB#jKmsKDZwy`%RW~FDLXC&o=CnhH5 zu{Sc|0*HwHck#~@FR7WMqb(N$gNus`y~|H}8+%g*CQeRH21aHEW@fri3OWZjYe#)o zI%@~A|ElDF)FWc-U}$e{>u7FcP5iH3eFGaOM_y9We+~W5&wuw5X!}1KvUd1yu0Hu= zaMibEV4`Pa_j9bPQKIn^ohsZnR1D%T4EQR{kGb#EXTw73arxyG2+~%R@v!FQfFXGDzJOaS zWt7Omph+i$;te&a3YDH;rQrUjC3*GEuQf~DO^?~X*(vlQzrbiYlr7J|f}gDImh6N~ zA<`KCjX2``7V}s5u`^8JTKJDYk*`@ltR1`eJ(<-9LW8RC6MrC}G~c*AlbnmI0A;$of}3e=&lg&7m)`JW;`2 zKbr(IR2y8&_!aJ!>Jk%?o=SHxWVSjXIkw)Dpgu@RQFRsgwaMr80VBc*3c)+1Ah3C? z7BeOiX%BYfz7XT*eksQ1YVON7Se< zEbk??f1;k0%)FO;Va7A9NNnPqx9)8{JRXuKSxgyizzJ+`#8f-r(58>Rw~lZLN^t*3 z;KBO7_Fkn@Ql$jj6(v!edd@e^NsT2PZy~m1^Xs#KxmzSB@MMV0|0=yO!xv$n)-fmb zco#+6+qe>hdl3s*Q{eU*_`4rVZsxRIuJvyu7h@-5{?;Sax*!kfLG*P5bfO%f9?l}e zW-Ywftl5^2$m!sl>t4yjNU9r!f8Fxs9)Pb)J|iX`${0ARmKs9HI>dRsx>H1)8Z%b{ z6t@D&eXz{`x)aypWa3&vJ_g}glK&F$W~?b?)-fLMCibn|{k>H91v4#A5Bvl!+_#na zb%6Wae!j%0+k!)167_R%(faoyn zb|#J{-f>Fx3XzNZLRb%DYFr_AHoGn~rAEw~>ht}!5n8?Pw=_ZufZXWPi^&Y~->p3C zl9rgKEaBLC&Lfs-$_62$%^Q%cASY!_9wAd$)kOS;dqs%5@Z_&MXvc3Z&ejX+kRAeC zbP?T&6h2KoG0~SeQJH$zccHCPO@eI;c%tk;w>}PU&dZ#~``!h%NNmL*(|;(65})|U z=}vewt=E?D11ylFFY)yxFLAFt} zZn+p5>L*P)Y8@5j_ZqK_)fB`H9mcbvOw|8cu}Er}+y-i7lGp;W{7f4hN6c5NA+VLM z9AkDT$aFao55R0f2G1O)>Z2S$1Pjy{vEsfH&7i1p42_4dK8{xrlb??zSKUc4Wf0*$ ze^AI`#f5t6Q~&N;0QGcdYk7!d$mC#1C;wSto5qNyE#A&hhpGf<6wE~J}h0Ciq6N6g!56NNH z&}bcazq2pt-q4ISmUs|f4nT^bDI~*Rm#obilQl#R5Xh18Cc4Jnjv5`K=181{ytU^~3f63OM8SI98B^8BM~h^9d7r zHWP#&@FvwZ#OLi}WxlV~R!6q`_9ptvpK_6Hv!Q_zDXm2PbMu9f9|4|S4d1|yb{w!t zMjJelYAS= zutI{SFf8dEP9%5f(}HXjV0g!P-*bGWZ~XFxR#Cb;?R18@m(Je%d7$7yU-W6^6kpuH zBBn>DZ;5m`8t;t`73#>nQbTr8`U_sw9Y?-A(~+v{`zSA4=;~__^%7tN`yQ(6vfOx$ z0w@>Q1_|lcEelzA4}DK-?Zem?=DYJ%UsWE*>J8SXD?j9S;x>uC8hxO}`}?FX*{`FQULz^D2(H%1;#b(Ahx}` z(I{)~!|?kQ_E%G%K+kvsjoTm8iTb~W^7lJ#r44j$(MM{qcIooLj5R`Kk*Pfy)6lKM z)&VCE@tJQ7{h+l;o||jBX$&Ahie5MeU@7K`4@_B<=5|$SYH#PRl!>z2-VmLpE*5la zq3csqD9UeOz9wFAG<5(~cwke*Xgw^M%=5G%PT0dHOu;Ze)x_T$*R#eTH#;8dEg&X7 z4YN+0$pTryE73OlWE+%2)Mf}J_i5I@dGe^(H zhL?UANr4}4)(#b5FoavT^^*0r`c9s)ReKwtpQeMM@GoC)k6(QSYo9100J{d`!P=OX4E>?!o$>*|VF#2S5igz)!e2J)`7S`eu|v`vS=VFXQf1ka%-3 zYq?D}8|g0zcQlvB^>`y6$i+ZzD_LOj^@xO8Qzr^9YLb!yP%8Acqwd{nr&Xr`_4XDp ze&T+dHg~fO-KUo{=?`QOYfeYsa2nVquv+hIxaEP~w%dm2er>H<_}kGhL^njXn}Agb zt3}nJc-$nZkAceWyo=<-FT|k9>(->(=%FNOd9@cA$?EN7OZzha^I?wnTwsf0?@F)p z03AYeBC2kn;=0r)e+L^zD@nS@miP==B%ciqnAnUM%?$*9b@raxO+3 z8l79d9rd1}H}k{2GD}-m<0M%^`fO5%2@k>f4F_@nd@-BnmTmrT0^mgPOy*XzU`q9c zWkC$2c0tzns!Gp`-45B0AbiKmCzTeks)``A$Qd+ z`rzH~sk+%jyBQte%ya9~o|JPdcVxOx3Ee3VhvF_ZXII`wtj@26^?4*K|Y* zFFSRYUZJk^1hcZ)^Y@Q1{!QiZt{bHgq^rN(g*H#%sv(?U-ffEL5-jtRic-h=t==me z)d8%n4fPPU?oT$S=HVAHfNMRiXc#ipi?A&&2%RA9Ye%|UKL2S4d5s45z6#h^)|xWI zdg+Ex0LUzyFp4s`Bk}dNAV3x6^hm;Tuq20BEhvO@3k~~u$^uOFaya`JPW1)pTs!Ic z+@bJ!V~17%@=RXKJ)Xp{;WXWeI@LSkdjlD3bRn}Rlc)5LCm89_7h29s5B_qo>WX4- zB5CUNzOBN1pN;4@i%tdF27389W4Z~jq^igN+|~1kj86?5p$b67l1qzc1!7E&X6L8t zhcJLhG)G64cZQ~Yqs4zvOlvc z_AA$vIUa<4V~}R6+)q8zvE(gZY4!Nz)%#P}@B9?X8f7J#LT@GAql!YOIn&W9bcdv5 zVW_lxidI295>XU-chTj0Hf#}_7^`;2+W5Se`a^l|sG3X)PLvhZWbN1bb=oX%EJ44x zSH*}9-)=u|rGmrQ2gUXa%!39mJb^bRrjYpaY}UWDmln>I7jDy08Z*zKbSmGH!$sp`SXZk7&*>EYWHWridK`%uz7LGpRi5eRpy>f(~TdJQ+s-wf>n zTPCf*lB5+S_vUF{EMXku!Y@Zh5aZ&U_vOPzlyS)i-nulaAr)GAA=aZ`nKraS@rCKM zO>^|F2pAj7K}5V`HTw_xSGEqS?TvY>L(^}6R|~}Tj-NQuRBFCKI$qv+_DX%Nb&TSyELb%B4!O+TBY@>)366My zyt0FjzH0FNi?N2}NR^_b@7%SUYvND0l^CO>mu{9troDy$3x258VUr{Y{x_IIfMhYu zS64!-?OtHLO`Athu^#A;7^M^YoOYnV4Wm_(Q`LMDTuXdX#MnID{r_?0J+}F zbARgv`Gzh_>o>Em-U;iEmC-Hh6+Y-jk9*_l#lu&$rv%$Z*?L}=(Jd`0?SiTKx1pTi zId@%ly2i#}r0)+1gE?l{UaPV%Rl?fyE2$20JwewRtavjo`)_Y;BxGe>e}sG{1T?+` z|1ZyGjaGP_R|wW9X;a*RDE9>ZmdAo`C1XEL5yclUxSpj^S@4yin?R z5fa_F;>-F%T$qt^-v3i86P0m#PS=A9!W$Gc4_U3xlklsJX<0o{-PkcDWyuTW4hPIh|;nnlEV5lq&wIoCg|P=%sy}u&&$(Dd92%4VeRb z+omzpGP$tYAo{sCDZ5bq@ceD1z3lTm@(WSW<16W%kxYf|J{`?+faU%sZr~b!wTFBH z>!8=qd}BWdYr_3IYZfjrSwr1XGPruP&KmFtqCfp?iZlz)w*FFx8gC>mx<}cmp1>wB zx7rCQ&oix{`vr3Alp5hEg>WCBK{pxrIL8wk z9l8VlK9F~?FCX*cn`(g#BIK&cnRhSJTbTnsxNAj-G@7gv{~}5YZ~~3sPn`?zmwA3N z1(l*u?OkqJAQj7%y38QhqJHs-Mydi7&tx`7dotQXpr&DGmRfi!-O9=ix4iZM(coGZ zNV^cQ;|aNbdZ#tAOpd^ff70mWae*F~Pa#3xzqsa}h3aam6-X0k#e)rvNU?+N*jQu^ zEdw+J$3BDBFaE?ucPP&bup#@tQ+=BnXZ`xCZla#rPIYJh(0gA@Z>HA=NYjKmfym|Z zM-FXqo{tylE;DyleI=D+i;Jo=8^61ErJQ`O|DOzmtTiqza!6CnR3RR5Rrcl<;~=OW z%FqWoxg8R{1~%c2)D}RIpCb(aRLOzo{4CuxE^pRc389EBGM8Kbw(|=c1EFo?M56<% zIUD43AphkQF*7X7L-C#3p|DE}KS_`A8KIecJvv(4Cc~uU{gfX}! zRbL<&&CLj06uM>@Z#l)rqiQ-3)V1h0@5_Q3QxoJXu`#PaGWNJf5^7YzhB2|q6GKO^ zS%y8zcVN^QrHQ`U*>*(2I)1Td`+(;R&Tr%omK1a3*Stv9ffaa|z12v3mH|4*$a_h) zkmH?R2q?{jAw4c>J8%#vZQpd2Pudd5HI{#9ZoK9C>fxT9FT#wi zNQ!gIgqrqNmKV&|d5Cmpq1Y#o@-Etksub|H*uCApX1|iGL%pzFuP%@IgtLQfsyHM(|-b2CUMtgcGfYv?0Tt^);gyW(JH(cXMF?1g@llQ~4k?o95wqD6?%bB(A|(lVldpV`AAoc{HP`|z54vw)$|^0{5zz7AZ6bd6>qe;5UlmprdnWit!Q zwRkP!{@hQO3+ILecZ7PXS`>2H0c<>Fk0az$T&>ZU%ax#IZWyCW^Y65Q!)6OPocn|_ zm-dT#{RJL(7SL{{9MZqpu_y8VOO;~w03V1+LsXm$vM{xCxGjDF$F%qx!rs4u@0By< z)qatxLZ{oi+MOrlgpo{7c%rtdq_YY2ye-LG+uavOi)Dh*eUGu*ja5Hl~)~4UYM!L zeoV7oh&C$|*Q|JkPv8r7cNglmTOBnXeI^)*!ezS1k1n7(*E?aE9fxaa2#(zDl-rf- z4py$8lP-UxS;|rKquck6X_$EOMrgph%{y4?tg`|93f6o0>NO_53Nt+$;P@=VO;stv zT~ToiTC><4M*1m>cN<2B+;l7@1lRstJdLDM45Aj_MvTx_+d<4t88VRJSC|rd%U94` z9FwF=pEUY<*I%q4J(&C0cu&zQOUv`x17@(t0_2`e_NN+mZAl)>hX=Ax1H+#7f@pal=BO=c z%E|07=M+xIE980c%r9I?Tf=W^)$=ZGwMGWHx7*<^w)U zF;baIyf}Xh-*p^L(xmMPY{mWhPZOrYzSJkk`eVLn=dMw?Hnvi`L;mi`hN8)m_M@I#aU-etx^A<$TJV)x{sLG+iC+LLof|*6>HCDo)EdNf5Piaz*7Mxz zkSg*BWXTie-hQ;N0nB1Gd{gO`h5JUw@@l_X96w`Pm>%YHn_ea-pq*GM-;kOUHk#m0gC0LmRN6QYq*LK%R&5VjUjH!&SCHMJK+kJi}ocv zPb%_7toPC^Dbdc;BixlzEFJv(ElL~5%-;nzk0uZc*SNGaH0VYwnDLRD$mq%QgPwIF zId(4O+v2Rx&=VdMG`b;n!=?h^3)u0wrgTxwPiHC%jH|PU_XdZKqIkxvj01UDa1lcg z66_UVv}77gtb6g73&%8WHqc48)~jngLJAqs+$}CaSF*oKLPn?x%2~eITDIzXc|#|BcrW&Xxk$rloOIFg%f5>H zdWuD@7FNyk+S~_EVai0kv3G1+zBTrIYLq5xRg;K8n%qRe()?FL<-2#U4HylUE~}?Z zM26;;XWjcrAE56j;?nMm&tJQ9V1Yg zO3L=;a$CQuEJz^MWB$B!GF5xc%*jjjO-;#Aa)BV^`TIk^vnbD%o&e{gXc)Jc5n{nO zWKT?cmUX2b^}fV7fffJ5W(1Bg*w>mggngm>w>nEO)v~z^+lL~vF`rr=m`jsyJm4iY zTwQ9}hoqH_)-AN#v>&SxhVgV!;~9G0zN`Ghy+y`&*&$Hb3AQA&&*c{W&1?;z^EmUC zd6rw)H-!sn;J3AiZBz7u;7ukTv_=FRV_!2=5vpLstYcK4UH>t@o zvr-N3uMs}k5m>mvD}8zMnxO6;rUnch(ueO9;Dmk>T;xwksd2+QkM49EB858 zvrFzAxKu4(e70@uPLWW-bGz2$MmWy{;J}z4HH%tRzd-~aB9{<)N1gMNJe6~lcP%Sq zY6%u@yhfX?4Rcc(%})X8-OLD|5V;w*NNgJc;zi}5kei-`Vhr447Nxz16J}UpW0B^m zW_x3lM&#ao`<@h~rUI(eHLe8--Y<)k=+8#Hu713@%sFePz~T$1F*tM|m;I=wMP07< z)$fouW)<@SXb@$q9*cVs*ed>L;^Z@`vCR{8U^-=!n_CzfnBms<{o?cYQiM{j@7w72 z)RPW>ih$D?-!@fHe+b-`3j#+*jA%iY()K-$#h*~CNJCS(A+O?=V14r4!t~|5Sb*4H z!5@`3^Sxkd=t>4jp52&anRRjB>tW16M8T0?oRpZ{*%K&iJ=?aVKNhGLp?c z$X~?^*`Z4AVueOx82wNY&xNuE9)67QZRKPsnkx}8sskz&mxX+Wy^0+SA5tOI?!JoCl#tFpD6hV|K?!lpf5WKA%(bR4+ zH7HG|Ai3#v#n21qy+hQZ< z+%uZbJj5$wmZq8fOwyVho%8`?hS`t3*?JxRXCPBt?jNMg1zQ9dxRe~@U2RmSh6>B^ z0wB>D+C`X&MC3FH;p4r!z9IgRqnX3!v)f_ zI})3{{V8c+t@~9CWV7veNfBdBjm)#(steC!grGOj;j}0hdKv_hC!j% zdHxZKQu_8-B*L~XcNdEjUh}~ z>5tKpBLN-!8aFo$^9A1OtQpMjebiKL=j?_}Q$XntJ7GYDFQ*N46mVY+Hdw`G#e9P8 zIP-&&u&!y1isDDK(+czH^{jpbVwCCErH$Q2&W=`XSf zP4E}Y1;fTY^wo4ce$fJ(?>}Y)K~x1lidIoO&>$L4Xvg}7h5IBEA5y3c>5->Qen(6< zB20t$S2dz4kQvW*n;m9ND-dvfn9riR+XI!5w*#du_(PlXPC^pE-McC>9Pu3O7W z9USw1I+!f7@D!9sU}l9+qZDe(i{fZawaYke$d-P{;cG-;os#ec@!fi5t;35HH7hJS zH%Q(Airlw}w-cLO%bUA#D`qhgppC$gHS zWmpJVA!1rQFl4U)&yC6Golf%kwSZP~mYlve_tx~c>NH>OlX&GWVSx*MyVw$0GuyM0 z2?YHsn|g@EJJ*K ztA>#K)%$1<@jR&xx$JLV(UJv)jwfLNN#rIu{e+S<0CjF)_zLo{sFq^!Eew?*zQtUo z7J3BMdaqE$uYB>)gnMlK{6nTZc>g_nb0vAzhM{7ot*S{t}H zdw3P2f7;bDnYD)iv5;$UqXsSM6GrlZsUZmqh162CILc~SrQq|v;P6Rer>;I#y00d; z-9q;;F#FXK!I6YPY}C5=-bI)|)KZ;jtx`FH?z<{G3)+T_5G(4@c(1sgJat+>aF$0W z<*#yEIXWzESKEeThty}s!GYv^^%yN{Q+}1NNlNRlGqHos+lFH4Qti=KS{an_@vVC) zd3ttbdIojom3rb~GV1qWF3FkACQ#(}eY0tK1O6!9J`6RlH&PjBC32#<-){sFm92k) zBY#Ycf8j9xg z`uFHe#YQO}4NC_rn;wv7igL^?6noIVa)kYvR#L}${cUSWIjjeB-jv^WEcWw@Au}#< z@wU_|+f%lD)>F?BuUU&$!_uyAYf2xDrZ3KuO<0uTQVOFN!+MaVGqokT+_4UaDK`8^ zedJWd&yGMJZRO|CDF-CNFW+V1R)YK5jwq*U{rAWW3EkX745+T7d8r>Jd#$H)63aQZ zuw<)6FtYqxk9+u{!6US4AlF6B#e_NUVdARPC7n79p9p?)p=3+^_52fq$ULM^e{Mc! zl|*ux3A#7(gGnG~pk*}g9-i92e1M|dpvd-`Vhdj5(bFjSE6DB;r^vo&>nE1Na291s z8{;W2aW&`}he2b5Q15!4#%ZJ-^v~)dyxT*W+g;VHzIiQI*%$R`-IpiwcHK|s(f;j( zG#$0`Xns7=>@Fi2>swlgDtex_M>l5Y{Q@Ac{nq=#t{cg#!MbX(UQ}uIR)j^0x$w6{ zzPIVfF`Y9ijwQID({Q3!=*P3LN>RNfn6=|8?s4vFnCBQmqmviJha~@+KH!DOH{^I^ zEDNtrO)}+ljbt?%w_q+DWg`1N& zi7!#+MOx`#{Nt=`=5@|EWL!5{sA_B!IC;i|cw4+N=hl1SZTvJ}`O%}#o{tRn6NJNa z1r*LkW#KGU^7!wH!uz4u?&+p+-UwDQeGs^HlnpMX>zzY+DlAJj2LU5TG<+sgtF;ZR zCXUHmP^ChSI9jH~s1IZLxCF{kYO2E+w6TcVPFtKiTe006hXogM&|@1OXt$mdAcjjGt%o-4z=k|Xman|7bYdf z;}mWeZqKgg?|~IOj<0km%dyz|zT7#m)g|3;WX`g$h<8?@K8Yi!)`^*l$3G-c4v>$m z!@cvf>d2F+rWGE%nzRqIPt6KcjZio}l{ui?nCA1?!e!s4g}n!49lE~x@e~1CyihT* z9YE!MT0bn^cQ8E#o)Ho{gl>tCbU*c47kd(OX=>S=YHNDF8SS&`NtzzI>cwHts-p_g zV7@yWg2jhu;bkV00WcR4rpSLFjk7L}-B918(uTDKp1d&?Id7(y^kVd{R{Yb5ie1tE z$q+)`CNTMZ5J}h}$OTk4FJD|{W0uwqQGyq&bnH-cn7xgXjodG-8xC<%#N3DNqCLvm zupRkLmkfHS15w`MxCLRtV}-upCZKZtp$wSZUdt36gxHTgColdW$lIDyNCf-!(g)un zidQV*985(KMLP(DJ~jy<)L*K=`>D~#NlWemrIO3f!l$h^u9*+D+piNwdDUy&{hk`8rUR=R^*~C6+{Qny^`&cimCCa5NIYDB z&VVTQsV9K7-k)U@4EL^8!jRmD3?tgqN<#&ic1LyVySco0K6p+$2?vG134>c7W zS}+KTuSKuqdFUkcP^^2(_|c3ZCDUkEaN{AcQf!mQt$4(gQiFw^+OXv)wdVKL*F8&7 z14!`c>+?{4jMD0=T>Nw4)A*>2WdSloF@%gIQUy$Dv=kL)>r4hK#A>!tyF+JaA-rz! zxcj{_&w0}6)>>7$V_P-royj|0!_y4IoL225A{Ru$M~3OM%Ka z%bwz)t;}^Hy-ul}u!^Kt0P9&c7vQrViB?6$;=?q&4tkcoHZMJ43Dkz%Wyqi|Tw|k)(o+s4gpj`HCt7RB3)mn}qC0h<kybWOUe)KJP`}pT%clMikM=BxaelBJ)ekzkOLndS0;CUrCSR_Ffh<8hTjTl!zcAaYD+{mhKB>b+~;HCkM8g zWk1h{!BI*A5lWdfxXc%s3oFz|o+Ff&tEmRT*rkhu7Ta=RP}|Z)`x2C3Z#^E`o~< za#rWA$z1&Tc~KD7i8HaKs>c=j>Z2CMbgEbxvTx7ptyEl4Z?(MUIzlgRm3RL9mKB4KpMp0hJWgEI z+Ly1p*l_e?=@w@l2o6n2562;&HW9Mv5mM@IcH-kTL7mzy6ob6 z+Xi%%^{N%SDwZ75kaSFQGz=~Q>@WY8Fl`MR0$T@+kINlro!eo|BW>Gh1=OK!!;Zq~ z_9InKOfL3KNBSZ0J-vQxttrju5ntf(_EZ53Sol3Arl6Z@Ui+!vG-c?zF1yb~$CRB( zwL83w&r4t8>INO-d9vgH*rK4K!9AUPM@eoXH{q$V_Gwptn`qU@1*^pEY#Ta&GWy!{ z?Q8HaV8`>VjNZ|(omj_l(f)StB58U0_?|nHUvz?$_Myro0?(F@TzVm3?$dlOSd28; zk+0j|zQnh=j!J$`(Lj!#y-*Q0r8?7KyqWv4N(s%LqvkuPSwD9^yb(ziw-YL1IhGLY zFZ@%8ldJ-eQ|#dW_xxR#ZNjB=`uKVSQR03u9=4gx2qU(BqHzN0ZP_uW_d{QO;-Gigh-#8N^}rMD*|~7;SYh!6jQeP%t!o|02|*z|&)>MO1KXi{hwoeJ zseT@J#hYqFIX?#8W>wGYcyC~hPhCyK-^H4Q3c z{^Y%^u3#lrUe&Ct8Y(R;Hk%4Pp9Vu%yVG+<1!)?nXG*`Z7G|}Aw<4sR;r;RmTbj5; zThJv_Jr!C1YVCzVVE)=ZE1&iUjs}ImZLG`2IHU`zWjSaiQ74mD56Wf`sU4#JqV1Nt zo^);DskLn;K86uy4IDvplcgxa3#>6Rqi4ERHq>y3TeiIi35L=9=`XKqkqDZ~g17aa zY2L(0_lf-OJU7R6@=ND@dwu!0Jrr{h8%}G(aik_JS$=^AA!vTK;?S-@&2M|@<*>G0 z>+s^<+;J-g@`g3GlhDFeS6E1yhyC~`w=KrEplgHQ_z${w5p3FsN$^2U?*SdYQ%r5e z7Y0Y^r$HBkhHy`&3wA$ zxSS2SN%VR#H{f!9Ijmt~I>QiCb-958uN>DaC)w-m3Ic2@A&MvERY>+YH)TSRU=e+v zSia)+a%hj}+R?DUA&_v9NXA%TE&NtzbFiy;V!C$|VO+X7@1`Yd0Mc~bPg~NdaWO?u ze2;cTg@L zitp&*vvEAa2nM5-BUGte+^G?(tc#je-*!Qua)OKIo|_ z)LMRXOq?&tijncsmL7NH#tbh-uGl8NA}V~v#la=Z+4G2~4tj3U{0=imwquQ@)^jNxr8pAG#DXaheh2S}9}(OI4tj#u0q$s-y-kg}unS^Vv{kMNfO`36w@>KApCl>)FqH zBA*%MVL3^tzVTc$ktdbKLn;MCA}h)6Z;%`>jb)rXe;z?wh`^C=f))LTSyH?yfAnTL zAs&~xI1Hjfy6$d|p<7vxP=|Wceptl1UW&k%@YqPVrrN!#yK;IY!Izt7da`fr((L<` zLtuEyueK)e{$`X2Qz&yc^Qszo7oWXyfzL=21jxhh%TrwXJ*kJ=knGuyJ#oTvo=&9x zchy6`#pOJt0;q|wG|%oXs{AX~V|fD_j>cbgW9l6SVBDR5r=N^9ta1C-Ed5z=|K)i=&iEE*(y@XL_)j&e1Czx@=v)6+3 z?{&C7Kpp+-sAAmt-eYamJE&BHXU-TxF2cKX$z?0qM0eo!VUJQHXB`Vc&pC4oFKp9R zYS}C{2qF`(hW!-b3m=N2b4wbcm7xlbX&AK^jJ!a<#USXd8JSJ+{ddQ_>RuW8_v_2G zy^|x(Itddl8IYTT;N>fRC15yL5gzEU7@;90oP3qioGM&Jl?7Hnu2IMQ`NT}{E+Un? z*d9C*4q6W2?#!q-E*$4 zcIsOHrUt%yNeo%zJf8*||EwUF!UKu=bTKO2hBLkBFKAbz(^-i_p5|=%Ib)3n+s`^h zZwpCR^$)d-d-p}<9eUB$zxp$p9U6s2gIQdS0_ple8k5*|Xo9Ebq_Kwy7!CKUYw0wV zM!12~tbY%}DUapaf~h&9KvlFR;ppk7?+c$UBL3IYx*klgE^IQb>K@VTiW`h0H7LxX zQX%u!%e|r>Et)Ojr~Rj{vYv(p14p}o7r~lb?~!%Z$VHM=n&V}_2wcnOAMv8p1-rhd zip5AsADAT@`Ft&ZVSb!ROVm=#Q%csiJFO-<0N`B{ZIm)2W1;hoVdDRySa1XNZLkC**}9q#(+>t-YBGf2Gv@FV zHqJvRrlA6#N%w@aN&wB#j?9~XW~TY~%&yaP5n=f~u2g!nf12(HM1iHc%BKxdYHZxZlC7hhw)Pg26H1YG#E0r4 zDDd5F&<@dAOB$`vw(E0&nq0$+S`KL`%xHVQr*S=ES+A@s&#Ca%vk5zk+uEkIwuO^< zR-a}IH2D_JMT>vlRJQiz*dko24LxaieV~j*0VtSARiU?ffq96LM5ll5!U!^x!`GjI zhV1fs)}g25?x?U>-C}%iBABUP=~3z()60`s3h!-zy=UoHs2sn4*|2#%+U~j|Vbr&X z*FFh&$w@hd;?sN;Ph2M?I>R2^X&El%{PMIJ;73a-y2AZ8Yl7x<0#PVD0#{gFJ}wuj~wcEBmRwSeSX&Oet5)ddUV%SI+x%P4ENv|5q%B~2Rean{4%sNh8BmC@#X@1PbKpdxA%)kEvIB)^~PrR%^jYX&Acy(v;rOE@Y>>1CU;1btTK>JrVccl`8 z!(kwhmn^dRUdaa)acIi|2e!%NdxXK7qf5}#LFz#~P4G^{^kfj}DN(dqrQ@}B7bcPV zvB~Gb^(3_vB#={mf!5|tzkCqz0#gCk=-hm*PaStUboy=RG$duB)kP~KFu}&gR-WJT zo(#+yaA{cVdZg4=t;4|c1Z2>vN@$sA(k35Y6``1RxgY6kQ*!y4q04qe{ka1(b{>+h zvUT7ZS}=9=oz!<%Gb!r(GuVLj&W-IC!t8C>GOPWi`>w`N2EDxnvLI6r!hZ+CSJ%#z zd3JBnsdqZ}wTqoVn@3{TVr=KTL2>idBsYD0wujmu`y>CnSr22p*esJjkMiEGH#2Kl zmS5;aczDIPO!BY2*th>;sWO&=rdQVQFQ&8$c&ZutNp{&692MlPcO`X}%;Y}#cOb=E z1z$&yN0u*(Po@BPujHl~Sl?uS1rVl)|6lV@k3IeMnexMcd4g}!7lwPxuKQ5Q*2pWZ z*zme%dyy(=cllkvmDV4xs76mzh@Zw`mOH~{sp0*Syc5TzU%hjk|2+Bbg||M-tCXhl z%sEnu=QSKQjAS&7DfAX*#M*zf#QDi>n^Y_jO%7wNq>5^1Lt4m>+S7%#i;Xsma`(9c|4u_kCH5*}s?U zOm~(%Xb&=Y;QalNsk2>TB`yy!oLM{HO5%6NGAT$WhBx;g%e4t&-(SKCN$73s09k{Vs3*=N(Kqyo6!;~#e6-%CTs?2n~}8d*$cfFtYGfU%Klc6`;I|q>EbC1Bp)8U z_29?MqMMuc1un1IyPCbNBjsuHj=*n)hXc>DR@H~xmfFp3wkPLMfpYblVi91tEN+iaoKNmY~XZ>&wf$7+L!Gt{pf$O~{2#KaA{C#lL)i*==XmZ*7S)lU+Dkk!4&4%n;DiZzj zHgJRA8LwqiYUgtB-CGlWTQ`DVT_+M`#6Y0a4pnQdX`2(}ru%a} zhu-o2sSg${Uw_17!|#I2PcHBIV0Uf7>n$xgyjx3m?fbL)@}@+~d|*%VYg@HvR4BK2 znEE8?udg_YFW#^a7Wdzl$!ZN;3f=9s_N-a>!55+B%t;%xkI&(@eNnevXzR^y!1-P; zIo)a9SwH6h*Mr!l`pycr-5i=b^#{)`gR@Q_Y#GHb`5k5pl29zo%#D6yBqKdpzVS;) z*T=cb;u`PCK36&){chp)z{z{P*^b06^nJx>+t4Ipp)D=UvMoj@S;=?~XFi9*s#7k> zfByyDRKGFp$L8t9w`ZwbxExT=