From d4dea24876d474284b72f01a555fc298c8e5fefd Mon Sep 17 00:00:00 2001 From: Alex P Date: Fri, 4 May 2018 14:40:05 -0700 Subject: [PATCH 1/4] Add "tree" structure design to IFQL schema explorer --- ui/src/ifql/components/DatabaseListItem.tsx | 3 +- ui/src/ifql/components/TagListItem.tsx | 11 +- .../time-machine/ifql-explorer.scss | 105 ++++++++++++++++-- 3 files changed, 106 insertions(+), 13 deletions(-) diff --git a/ui/src/ifql/components/DatabaseListItem.tsx b/ui/src/ifql/components/DatabaseListItem.tsx index 17ac4c948f..7c81291533 100644 --- a/ui/src/ifql/components/DatabaseListItem.tsx +++ b/ui/src/ifql/components/DatabaseListItem.tsx @@ -26,8 +26,9 @@ class DatabaseListItem extends PureComponent { return (
- +
{db} + Bucket
{this.state.isOpen && }
diff --git a/ui/src/ifql/components/TagListItem.tsx b/ui/src/ifql/components/TagListItem.tsx index 76599367b7..2b224bc474 100644 --- a/ui/src/ifql/components/TagListItem.tsx +++ b/ui/src/ifql/components/TagListItem.tsx @@ -26,8 +26,9 @@ class TagListItem extends PureComponent { return (
- +
{this.tagItemLabel} + Tag Key
{isOpen && this.renderTagValues}
@@ -40,8 +41,8 @@ class TagListItem extends PureComponent { } private get tagItemLabel(): string { - const {tagKey, tagValues} = this.props - return `${tagKey} — ${tagValues.length}` + const {tagKey} = this.props + return `${tagKey}` } private get renderTagValues(): JSX.Element[] | JSX.Element { @@ -52,7 +53,7 @@ class TagListItem extends PureComponent { return tagValues.map(v => { return ( -
+
{v}
) @@ -61,7 +62,7 @@ class TagListItem extends PureComponent { private get className(): string { const {isOpen} = this.state - return classnames('ifql-schema-tree', {expanded: isOpen}) + return classnames('ifql-schema-tree ifql-tree-node', {expanded: isOpen}) } } diff --git a/ui/src/style/components/time-machine/ifql-explorer.scss b/ui/src/style/components/time-machine/ifql-explorer.scss index 0f5067032b..502621aa32 100644 --- a/ui/src/style/components/time-machine/ifql-explorer.scss +++ b/ui/src/style/components/time-machine/ifql-explorer.scss @@ -3,15 +3,18 @@ ---------------------------------------------------------------------------- */ -$ifql-tree-indent: 28px; +$ifql-tree-indent: 26px; +$ifql-tree-line: 2px; .ifql-schema-explorer { width: 100%; height: 100%; background-color: $g2-kevlar; + min-width: 200px; } .ifql-schema-tree { + position: relative; display: flex; flex-direction: column; align-items: stretch; @@ -33,6 +36,30 @@ $ifql-tree-indent: 28px; font-style: italic; } +.ifql-schema-item-toggle { + width: $ifql-tree-indent; + height: $ifql-tree-indent; + position: relative; + + // Plus Sign + &:before, + &:after { + content: ''; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + background-color: $g11-sidewalk; + width: $ifql-tree-indent / 3; + height: $ifql-tree-line; + transition: transform 0.25s ease, background-color 0.25s ease; + } + // Vertical Line + &:after { + transform: translate(-50%, -50%) rotate(90deg); + } +} + .ifql-schema-item { @include no-user-select(); position: relative; @@ -40,10 +67,11 @@ $ifql-tree-indent: 28px; display: flex; align-items: center; padding: 0 11px; - padding-left: $ifql-tree-indent; + padding-left: 0; font-size: 12px; font-weight: 600; color: $g11-sidewalk; + white-space: nowrap; transition: color 0.25s ease, background-color 0.25s ease; > span.icon { @@ -51,32 +79,80 @@ $ifql-tree-indent: 28px; top: 50%; left: $ifql-tree-indent / 2; transform: translate(-50%, -50%); - transition: transform 0.25s ease; } &:hover { - color: $g15-platinum; + color: $g17-whisper; cursor: pointer; background-color: $g4-onyx; + + .ifql-schema-item-toggle:before, + .ifql-schema-item-toggle:after { + background-color: $g17-whisper; + } } .expanded > & { color: $c-pool; - background-color: $g3-castle; - > span.icon { - transform: translate(-50%, -50%) rotate(90deg); + .ifql-schema-item-toggle:before, + .ifql-schema-item-toggle:after { + background-color: $c-pool; + } + .ifql-schema-item-toggle:before { + transform: translate(-50%, -50%) rotate(-90deg); + width: $ifql-tree-line; + } + .ifql-schema-item-toggle:after { + transform: translate(-50%, -50%) rotate(0deg); + } + + &:hover { + color: $c-laser; + + .ifql-schema-item-toggle:before, + .ifql-schema-item-toggle:after { + background-color: $c-laser; + } } } &.readonly, &.readonly:hover { + padding-left: $ifql-tree-indent + 8px; background-color: transparent; color: $g11-sidewalk; cursor: default; } } +/* Tree Node Lines */ +.ifql-tree-node:before, +.ifql-tree-node:after { + content: ''; + background-color: $g4-onyx; + position: absolute; +} + +// Vertical Line +.ifql-tree-node:before { + top: 0; + left: $ifql-tree-indent / 2; + width: $ifql-tree-line; + height: 100%; +} +.ifql-tree-node:last-child:before { + height: $ifql-tree-indent / 2; +} + +// Horizontal Line +.ifql-tree-node:after { + top: $ifql-tree-indent / 2; + left: $ifql-tree-indent / 2; + width: $ifql-tree-indent / 2; + height: $ifql-tree-line; +} + /* Controls ---------------------------------------------------------------------------- @@ -92,3 +168,18 @@ $ifql-tree-indent: 28px; flex: 1 0 0; margin-right: 4px; } + + + +// Hints +.ifql-schema-type { + color: $g11-sidewalk; + display: inline-block; + margin-left: 8px; + opacity: 0; + transition: opacity 0.25s ease; + + .ifql-schema-item:hover & { + opacity: 1; + } +} From 5b55cc4fa735de3814cf6cec015f6bfa4e547598 Mon Sep 17 00:00:00 2001 From: Alex P Date: Fri, 4 May 2018 15:26:45 -0700 Subject: [PATCH 2/4] First pass at highlighting variable syntax --- ui/src/ifql/components/BodyBuilder.tsx | 30 +++++++++++++++++-- .../components/time-machine/ifql-builder.scss | 13 ++++++-- 2 files changed, 39 insertions(+), 4 deletions(-) diff --git a/ui/src/ifql/components/BodyBuilder.tsx b/ui/src/ifql/components/BodyBuilder.tsx index dc327f0975..3904c9a2e3 100644 --- a/ui/src/ifql/components/BodyBuilder.tsx +++ b/ui/src/ifql/components/BodyBuilder.tsx @@ -22,7 +22,9 @@ class BodyBuilder extends PureComponent { if (d.funcs) { return (
-
{d.name}
+
+ {d.name} +
{ return (
-
{b.source}
+
+ {this.colorVariableSyntax(b.source)} +
) }) @@ -55,6 +59,28 @@ class BodyBuilder extends PureComponent { return
{_.flatten(bodybuilder)}
} + private colorVariableSyntax = (varString: string) => { + const split = varString.split('=') + const varName = split[0].substring(0, split[0].length - 1) + const varValue = split[1].substring(1) + + const valueIsString = varValue.endsWith('"') + + return ( + <> + {varName} + {' = '} + + {varValue} + + + ) + } + private get funcNames() { return this.props.suggestions.map(f => f.name) } diff --git a/ui/src/style/components/time-machine/ifql-builder.scss b/ui/src/style/components/time-machine/ifql-builder.scss index bdb0ba17d1..7731e73cd9 100644 --- a/ui/src/style/components/time-machine/ifql-builder.scss +++ b/ui/src/style/components/time-machine/ifql-builder.scss @@ -37,14 +37,23 @@ $ifql-arg-min-width: 120px; } } -.variable-name { +.variable-string { @extend %ifql-node; - color: $c-laser; + color: $g11-sidewalk; line-height: $ifql-node-height; white-space: nowrap; background-color: $g3-castle; @include no-user-select(); } +.variable-name { + color: $c-pool; +} +.variable-value--string { + color: $c-honeydew +} +.variable-value--number { + color: $c-neutrino; +} .func-node { @extend %ifql-node; From 9ead3ee1a9a0b3c31ffc2c4a16e28201d2eae9e4 Mon Sep 17 00:00:00 2001 From: Alex P Date: Fri, 4 May 2018 16:04:00 -0700 Subject: [PATCH 3/4] Color builder arguments based on type --- ui/src/ifql/components/FuncNode.tsx | 46 ++++++++++++++++--- .../components/time-machine/ifql-builder.scss | 8 +++- 2 files changed, 46 insertions(+), 8 deletions(-) diff --git a/ui/src/ifql/components/FuncNode.tsx b/ui/src/ifql/components/FuncNode.tsx index 747d630d40..36e8b695e7 100644 --- a/ui/src/ifql/components/FuncNode.tsx +++ b/ui/src/ifql/components/FuncNode.tsx @@ -1,4 +1,5 @@ import React, {PureComponent, MouseEvent} from 'react' +import uuid from 'uuid' import FuncArgs from 'src/ifql/components/FuncArgs' import {OnDeleteFuncNode, OnChangeArg, Func} from 'src/types/ifql' import {ErrorHandling} from 'src/shared/decorators/errors' @@ -46,7 +47,7 @@ export default class FuncNode extends PureComponent { onMouseLeave={this.handleMouseLeave} >
{func.name}
-
{this.stringifyArgs}
+ {this.coloredSyntaxArgs} {isExpanded && ( { ) } - private get stringifyArgs(): string { + private get coloredSyntaxArgs(): JSX.Element { const { func: {args}, } = this.props @@ -70,15 +71,46 @@ export default class FuncNode extends PureComponent { return } - return args.reduce((acc, arg, i) => { + const coloredSyntax = args.map((arg, i): JSX.Element => { if (!arg.value) { - return acc + return } - const separator = i === 0 ? '' : ', ' + const separator = i === 0 ? null : ', ' - return `${acc}${separator}${arg.key}: ${arg.value}` - }, '') + return ( + + {separator} + {arg.key}: {this.colorArgType(`${arg.value}`, arg.type)} + + ) + }) + + return
{coloredSyntax}
+ } + + private colorArgType = (argument: string, type: string): JSX.Element => { + switch (type) { + case 'time': + case 'number': + case 'period': + case 'duration': + case 'array': { + return {argument} + } + case 'bool': { + return {argument} + } + case 'string': { + return "{argument}" + } + case 'invalid': { + return {argument} + } + default: { + return {argument} + } + } } private handleDelete = (): void => { diff --git a/ui/src/style/components/time-machine/ifql-builder.scss b/ui/src/style/components/time-machine/ifql-builder.scss index 7731e73cd9..b9b187deb9 100644 --- a/ui/src/style/components/time-machine/ifql-builder.scss +++ b/ui/src/style/components/time-machine/ifql-builder.scss @@ -51,9 +51,15 @@ $ifql-arg-min-width: 120px; .variable-value--string { color: $c-honeydew } +.variable-value--boolean { + color: $c-viridian +} .variable-value--number { color: $c-neutrino; } +.variable-value--invalid { + color: $c-dreamsicle; +} .func-node { @extend %ifql-node; @@ -97,7 +103,7 @@ $ifql-arg-min-width: 120px; } .func-node--preview { - color: $g13-mist; + color: $g11-sidewalk; margin-left: 4px; .func-node:hover & { From 478a68f5245362b172c208866d49ac8995317685 Mon Sep 17 00:00:00 2001 From: Alex P Date: Mon, 7 May 2018 09:36:41 -0700 Subject: [PATCH 4/4] Remove function icons, add "Collapse" icon Also replace function icon with plus --- ui/src/ifql/components/FuncSelector.tsx | 2 +- ui/src/ifql/components/SchemaExplorer.tsx | 2 +- ui/src/style/fonts/icomoon.eot | Bin 13720 -> 13548 bytes ui/src/style/fonts/icomoon.svg | 3 +-- ui/src/style/fonts/icomoon.ttf | Bin 13556 -> 13384 bytes ui/src/style/fonts/icomoon.woff | Bin 13632 -> 13460 bytes ui/src/style/fonts/icomoon.woff2 | Bin 6808 -> 6632 bytes ui/src/style/fonts/icon-font.scss | 3 +-- 8 files changed, 4 insertions(+), 6 deletions(-) diff --git a/ui/src/ifql/components/FuncSelector.tsx b/ui/src/ifql/components/FuncSelector.tsx index 35dfe9ea9f..7ed896d792 100644 --- a/ui/src/ifql/components/FuncSelector.tsx +++ b/ui/src/ifql/components/FuncSelector.tsx @@ -55,7 +55,7 @@ export class FuncSelector extends PureComponent { onClick={this.handleOpenList} tabIndex={0} > - + )}
diff --git a/ui/src/ifql/components/SchemaExplorer.tsx b/ui/src/ifql/components/SchemaExplorer.tsx index 62db36dffe..8bbc32ba74 100644 --- a/ui/src/ifql/components/SchemaExplorer.tsx +++ b/ui/src/ifql/components/SchemaExplorer.tsx @@ -23,7 +23,7 @@ class SchemaExplorer extends PureComponent { disabled={true} title="Collapse YO tree" > - +
diff --git a/ui/src/style/fonts/icomoon.eot b/ui/src/style/fonts/icomoon.eot index 233e332b9ca7f272ac033ab87b2d1a8ef7c8d60e..6a466d0f50adc72382b1d36a04b327f8336b326a 100755 GIT binary patch delta 741 zcmY*XO-NKx6h7z9%zHCW$NOfScOQs7Z6;^3!sh4sCd|e*nL)V-gK$wyW*GiPAI(6 z0(2BSX}7+zoUlKqLWQmyuE>R%!b0tMvF_0*0ILURn(XV%UJaa88UW%?0l?~`f?V&q zM0}0-fxf}q*!Ld+H$cKmd~0C1C);tlH3yI+3bzKcVhH_otTo2nFi3hn?jG+`_rO@yP7a5Q!Z36W!%Akd)=R7&B0hQ`Ldqf`n8VY3e7fRNQLLU>jOyEIJ=e_rGz^g3N_( z_L6hnsE*b-Hu9_Lxy8A?VKh=Afm0n_*9=1|<-M}(CH>OGKh4giocWCul`rk+%Bf2L z#pji2R*a}0_SYtsSMyWC--p^m^O~tm2}87qA#qPv^>)gpj|Can2F1ArF!}}6#h8ZS5-Mm3me2~-+6P*p;GrTY zDk8MGs24>~LeWA&5D}z$5cE(~5X3{&iVD7vS}I8EMDXUp!|>1deZ%+v-!Sv(;Kc#% zVhVtT2`R7GfT0R#d0x>0!<2gA1sEuOhXYZ@M))UQ!i)GT{)|835BNQPhv#wGnzqgw zhN0>6`fL3bHGriF6f0dt(#$=SmBt~svSKquG!H!5B@P7*>ad}nx2muQ84U`&867BLg`9#00r)^s_)@*kic0s*T33WYi&L zHdgMS9i|;5l5m)e*Q;{9%&$`YNy`%ap+Y>f%hYou9112qsX^jbX?hA7eWV1lObktiFpxf-%M#yaArkcLHpLk?dtR0-A{eY z7Ax*5+Mzx93cu{VN;T6fF==lzX}OJvRR_AD*Ri%v@reqe)^CrK7)$FDCcIISE^;9*&l~(rp<384oD;W-wGfl}^_!~Muu|@y@ diff --git a/ui/src/style/fonts/icomoon.svg b/ui/src/style/fonts/icomoon.svg index 7c65529bfe..7673d95bd5 100755 --- a/ui/src/style/fonts/icomoon.svg +++ b/ui/src/style/fonts/icomoon.svg @@ -23,11 +23,10 @@ - + - diff --git a/ui/src/style/fonts/icomoon.ttf b/ui/src/style/fonts/icomoon.ttf index 889175549ab9b43746c65f7237865fe62c84d381..6baf37bbdb2c4114974e48db587276c6644d4569 100755 GIT binary patch delta 727 zcmY*XPe>GT6n^iw>&)!v_M2UIej{R|?&fZGu$|p?Hq6#8nL!x@L3k*(QaAF?g1d`d z+UOu1B#fm)5aFS2k->s=>EcPmD55`?B0;GLk%}aV$lk0PG<@*p``-7x_h9D7rTNTM zb1`&!1^^oX;P+s9EK5}bV0BcL!J#Vy<;Ht?;!OZ;lbQbXMc;9?1t9v6`gVo{xyf~w z_zLkonc>{{m#;oIfTa-M7#h8h?m60-1BelYtHbH>EF425evNq3NP4)x?oO{~lZ0tH zVj?>_mID{HAhGw>liTPo_2T6SGf-@ZpJH9CiB<7gd=ekUidYsO^cnqz#x*Zr=1crh zc?t8LBE$Mk8bL#jz;%F@HjG5WRxlh2h9mos5ITSsfnF+*gHoshj#9u7u`bjDA|A63 zptO^n`v> hn4&|Bh$%xex@k83MUa9`P@OA)mA+wZ`DFY7{sjQIj++1g delta 898 zcma)4O=uHA7@gT{_BYv|q&wRmY6vmqfI@Ho@k47zWdV2}bfVqtzm8n+gNc-Dumg5AL)?39kd$`tbP*+cBIJLt}G z8h4wo;1BR;g>AxJ(Gn-6tn^Tpvi!!HnXhH3V{#ak~g?&^0djF}i q4P{phVoVrs$Wd~h%#ua3+q{C{!=qNTilovch$(!-{+SyMH}P)~c(7;y diff --git a/ui/src/style/fonts/icomoon.woff b/ui/src/style/fonts/icomoon.woff index 17d2da624931a8b5f102f318b2bc8af227185f6d..b69d8bf5ccdbbadeb49c1978995a356279f4eb11 100755 GIT binary patch delta 771 zcmY*WT}TvB6u#%~I5RuC-Pv{LPDqWmo4Z(IJGt-(s zJxC7{$@)@M^w4uYEch0L^blqg(a%d!U{u5+5l9k}of$P~_&&}(-}%ltcesx)&0O4m zX#=&>F8JC*tY6bUh7nD2Hd)p<@24CcpBJAt|5UH$&UFJy<$qc7fx`MAT5!>#$j z%vi1w#YGhATpj@!$_)0QRwe`{9p38luIGkF@_7IJ^x9nr6mP@jy;KLj%75_{zRb(~ z8~@6`@XvgSf7E8R>nc-yY>9nh4~d5W{-{;zrET5`3N`DfcnCQ}t`Sn-1kqT;3`N4B zNOTV%!u!EQ;K79y62mSSh(Vpl+d&KRMBHuz-2jY9+u8?F1N0cG4)hj4BwQr19=8)Q zyEz5zU|Z41M2JDNh1&Kz!O%3LAO{pB zATzem;r9LCxYYvG0|I@{7;8{RYn?XiSJk5nkG7WKN)Zi?I=ZYGhE^`fk|d*laq^#M z<5JH41{_r=Z}{A)_+q8*sw6EvV;^?yFkigU&#=}NQYqjo67r76@U1su@rjv)RB_Eb zz71-y+#?V;;Mj_13z!HkPGP!z?5AXb~FA+lw%QRp>^F7xQl*=W1CJIi60;XZHMcs5(1?5E+J=_zC)o?_Qg%9-c`flmjt}ZJ>}Kz`KS4_uIs9{qCL_sA|JwFG`}=VzLMrKI=OE7 zs$>PZ&^dzz6>GS;i8mwE#Xc1ayzOkTRs+tp6-%AC6oy#H+bQfNMG6JjIKFCZZ>D1~ z8m+KolBN1XtpPD4YemZv_02mRe}H!fqeB(uI4rFjDMbuRdckUsN;+A{q(ymh-=M=- za_vD!ju0bgi5iq!(wIn7dvQ6*n6uS|impOG=(_YWP$Ki?b=!B1+=1E0Vfz0X>wh}( zXFStF20J#d;dY!AcxusN7cO*yZO@~If!XHWK^HFCL}ZBsn{NurO)_5>4CPEy2*n38 zrCo+rW{G$-=Ss~IU#DsXYVtRK zr_Owiwfbxcch&l{l@K}!=%9!d&SF=EiK3u z`HCVdeah*e9PAGs3offY>J4o`kL#<(b>mGa89E54qYmatd3vqmm4Z1ALeAOCxD}UHfsyau9LbH_$ehC+uJ)# zxF#$x6pXI|khh+GmE;exAw;nDXwGVEE6hD28}kyVvAY(M0^tGCfEjC+wHv!h4lIx` zbv3S*T>C)k=eF5QR#@u-ylO>!{!ju(SH0X4=3a7Ro4NkHzdmm6^(S_;1q%qe^O zNkZo8xNms@22aERlL`3Q)PPKIx9=A(o8CL-GUhqvJ?1|aI2JY*ITkaPFs2<#9n+5) z#)=-Vethg@(96)5#V^ZWZlwREX$hp_WEc}kA&_)uy0P4`!p~dLzYW!j8Db>HL{p+k z(YWZj=&9%r(Ie5Q=y%a?qMw9Eg{uX8ffN5X{?Gg)y6$N}@Is8HA&(`%GPJ*q|8cjg zyYzh!wgFs2V;@09I0FE2mIhpBCyF?5GE)y2oWKN;DM6Ar!AY*4do+RLIO)XWIR$x9 zo`lHawS5PEGDT>JSmc+P9xoQNn1M+=e_sz*aTLMh<2b?hB5($SAP6rnH;K%d5(p@2 zq6xw=p-{#?=13%75>E*bdN^?fl($!)4OB!E-6q6UU@5N2I3ORk?N|Ap|-Grc9^Eyv2*YA$fP^kBW4;DQ|Vy zt+eHEBgkdp_WqHIC|(cF?v-6TD{E{0|1JAP4$`X=8)}b*Q@xwfq))DluPfaiKHjzF zk6nc%-H3tfF>p)het8R%j35$09!Fqc+XxhR$B?o*d1;gF9y!~&Q|6cF=( zL+%&g*Yo9$T^aCLLJ~h>)WBfE9a#g-iuda+mFm`MDtc_(8Qu(V2kgyC!^&SaB-YNP z#kwhCMO+y+*S3<8I+a{O z=DzHA6H%Silyqq(!Hh8J7(_!OCl#j?Hps5HXNoE+CPE)PH3b^rR?Cz{jmm?#4oACe z8384k3I{rr7-mr<=3GT{bB9p?PkD3(GB9{@X(X_dr ztx+sytzMhd9Po9s>?KHyL*$LXCWyrMM_|tL@P}hO&nKTw*|reP$HakYNlvrV=Jvo>y1>aV5>Khrxx>u3=#Fnm!jBY5ptTr@|J zcU*caB=OxjH^{?mpFCMdqo0h$Ov%)q0urBYkU3x#6Lby8ixA7}G?z_Op>aBFeEup1 zxH-oh4;q$8sV^Cj6f&=WHA#rv2lkPP{2@#tyV?*+U|Z%2l+uwFFcmV_0qf8iW(}T; z=2R?EqRv|1NUnr|=4g)|0U2Q(JYVK&ZM3U`)`J%L5T>jk_!a{64Ja744K;P>@QgS8 zP~_+q_X@q-I+C(MFoM8B1X?;Uur4u6E11VUzPK~$4Gj9>Ds=Ux=e|XZWv=Fer&28P-=v&pfuJFet^K$bL=9^%dRK*eS$Wot zIdATX-sWlA4W9A50egbo^P1vEj2aT{(r8nqZP&OC$U8II^jD}Jah#1}APW*M%XIBD zI>ewcf9TK&RtS>hsf;>z(82B&(+RKAS%ZO*W@eU`kY&XvA;cJcnISWish==c#mtRN zo~g2u+%SkxC6vI>=4IQR&7D4&_?H@Ps(= z-uN!#?^sLFyh%odtTq+1;wq~WO2#=v!9)zwc?{+i=9yHK%kzYjSj=>SG&{9z#*F6? zxnucos&J$Dg(&%84u+d|kozq`2_2Z`f?ye5h5|XbN1OH4m`CAtU7Gjo0IfHE{>`)Y zKy81IQp+%jYf!`Mv>=;kp0ia{rfZXh_B**SXbU6=g7ZndeB*zH4w>a)SnMbqM!0CJ z(7om`fkdoYip2>@<@?h?5i&45C)xF=x&-q>rV3pTc49zD!xYV${~2g$l)4X6*PQx@ zN4+q8BaCo3md^fAee^L3jTZf+*g)XB^Ktgaj}(NcY#+tZz5fDp3qY&!4fEV2bmqbcj#< zm{pFl$<7p$Osiv;#5n!Fa6Il}>Qqy`;+^evz`Pz}h#)#>XvkbWRX3OhRoCa|)c1+2 z6XcF~}0Vv$L zIYW(>eQq#?xClp|=LC)Z>EKt!(HZv1rBRM1k6o_3px3t9ML3i^h@dgF?zJbbk;byA z2qB53(-0~iaXnkkai(Gn7AjQGyD2}AbFAXP9L$s|>*h(%L?YTo)IJ)JIiT&k3$)(o z^((4<#7Cna(|6`;^};~hDf~TaDGdGb{32s^eXw=^Y#ZoM1f=^aTp~NFy;?iS=1YGo zSC@yd;E#D&&z12)G-n9fv*I0=0>t{a6_EBl9p{9%JLW=U|bcvEpiD z(v~=qQ8=1^;cTmAYw(;wVot~6duothUxqcL$-DO!`EmO3?e>dpQEDV|N0>Vz#{;e!6_=E3S#YJDKc^c*Jh=9{1i~xY&<7@T9qfU0+-o+^>&T z=DsIy59pp@-{Uv?c;j|&wYNNx9|L<2~1Gi!DC z@K(6}p*;lH-4Wr_0RDRMIgbC_t*zC_#Sv0sQec>Mtr0-v5GMFom&%Xa@x#@vfm@Nvl6ZkE9!=@${UOgm&ZWv%J+|kKYGWkEIUrJ zB@jc|I1aUFwzolf0&}W&GDb(w`v_}TeN*xE!L^%f`ybr+emau(lNtZ7wt|0K@84S9 zf8)OQ@;&LsrGP;dw0}FT$&ek1%G2@nw5Ggo@+$Mci4*U66(|{8qV9|$tFbXVBC6ob zqu+n`9-Rp8X8(`%khO!`&6$|+c{@I(pHyz%{7fQIDkYNV2Uk9te?-G?Q1+Tbvmdc( zj%4x9VP;Lo7uwb+Hk)KiiV|DD5Lo#Qe5*k3)Iq7fzwP$h$SLNi*_^;s>7^K7xu5~XDl>m5F{HY>k0e7vEbBX}Ly&*h6-wvigu8h9 z#9&1CRirsGyP+XlnIGC27Feta^2#}4&b8-MJ!kFUbq9@1c>g}mlG#&HY3S~Nk&&h| z-~T@1Gd>Zrk#m&!a2drO9SO_CPv=+OpFJ2vyU=|nLD`M!6PHy#2--BbcFALSASAn3Xi0Wg(`s^>m@Mq`r`c!JxcIsVVRUgdqeJ z!l*t0z(CA5`NLc})G<5y%jxrmkLj5%~Q1ynaFZQ{l4-#?j~h1=6zxC3~(Pinz>qY~42LVBdc4{cRDI zQVHgPYWI_8g4*RhsefjEWMrmk+TM+ky8I%kS!ZaEnjvu?erNLxzZ7Bj7Z#z$V)iTW z>saBp4w-9e0(|iTG%Yn|G*)y3>M>g=xzrw3rU|ea(!A4snwosDU@-WcERFGW7MZU| z;ygJvs%emTCfaN!lMPISCcz>4+0>g6GL&JgYS%Tta=N!6^-@l2YmOfU34*%8!56ZY z_yTE`H1owXWvZZnvdlMYZBss(7x!UowvWk#ZH{e~{g?Ue4s$6N?zKk9+OK)FJ7M8M zgQ1}zA;H#QG}u-q#RfI%C=*3RPzxG^Vv`zd2IE3oBE;+hO;CT3!{O-37b&G&9c-Gd z*%^);&o3s&0VRce=hZ~ktnGT}+rMAU!7p6s)5D=IMrNOn_ffw?5My~%^U@+}5pJ9J z4AT0?g2gZa)yGp-jU?5QX!$n$T!BKjB1_Z5evI$E;5jfV>nh;d3?k~|!byZ7>$ zUoic@C^gj#|Hs0$FrYnAVG873xbkCeVPRBj6eDFCr}^F+gjR%WYa@$q&~* zoEHtFJmV%R?#PKQ=@WLiUM?xl=@Ai|jBxmB6?4=$Ic1J#?X@Z+CMV6mF{@bjOjDC- zxy_rkPMexC-Y?K6{QUDa5_$C^p%ozr;qaoBu~ zlji)@hdph^D2OfWB4(}kbndffept8&iqy5tVY)-IPMW~3RTsgc7)D2Hbn7Cl2bMn* zV)EO1_MGLWajtEK<1{Z#vc6@>vYyUD0!5oHfMOCjDc{UCwGKp`J%=N{sn}I{EOlUx zXj2rz;=&J)*83UVj@4%io1jqLHecB<<6hqx_y&yrFw@$(t72i8QQ6Ps_rjKIoE~hx zw$mC5(U!LP$?OziCwurEI8Jjr6@dd4@5=E3wya-=C3^Y1eq~tRqFt34qXWV}q+fP& z>ErOuFuPNj!mc~6wwfr2wRXS@X+hC^+1{b1|FbR?zJklTIK#_KBNWKNf^-Uj5?{#XVxsA$uJsErej>k z?v;@wyr$j%NnzodF0Hm}&DQqG{vDv>5Yyu@P1Q_o58f~r>}7Z!LH)td7X-oNEAxPB zI64k)#j;;M-C=XH>2%E6@#!y^BXtDzL#)bSm0y%OWEbV@S@t2)N)D9r0pMzDtY62t z2V~Z*PmsuoykTzMp7`RMSB>UMsyV$bL{@unXtsk*bFRWE1fOTm6Xtp=;SU%oqLj}&}<8ORaffFbk+as@=7`Bv02 zfXh`DB45;2+lh)lzf!$rTBaZnsW4U-CBYVZ~NLnQhPelW!bhlp0A;1j1^lJzFRo zAvyYYIZ;pSCCtQXvHsu7=^F7Yi4U$tN=rC0pzvx68BusrI8}{N)zn^)Bh{Uy{A<`j z1_8#98qjMy_cAU37zV|Kgqd@FOWC^OTu$KJ1x4JJmhDTI{6(qIUS5iX)MHa;r2JnK zZ(zVLDK2LOdM*&2y*GKD1bMSyxz~T^W~;A7a?ZdeX+QO=RYoh)6(%x$%QuvW|(c#8nRce8{N!dc7A0nVTze z(=`^)R8RAPt8&}2&VzJ>yqw^cC7%A~*XZ=)y~987c&3`lTwPUfit_2Ne~U@)?S1&r z2Nr=i%gyJ>r5+y6C_%2G+_vm(;c30`+ed$wcq#?C<)#Pf$+~dA;3j%~B zJ9*Woj?GuKK|-T2Aa!qKp?x3FbZH(pm)job=_%VQ^Ym;F$mQnoN`HXY5&(e?Zi%W$ z%hirjsim676{DpxE$5*XB^hTNt+sG^1p6s-MEIpTM@4Dbk8o9rc}D3~UU<*f+)oJN zUVWxI6}2Af=4z89#dtQ#R!$OCM}8fpYDC>y0#8tqKH#fnVHeDvhCa9CHB13 z8e=}bQ@tEzcr-pz_g^G#W?VyLjP zK=nQAj`@PLpQihy7Y<7M$WX)`mW)wFN?KYH$ltuf(t8eMOmXS*s+&7wFenP(cEAMRS2fufNf9ds5hc+3t#0 z9CjqXNtA2?&)MK_1>-#MC?`vzr~|vVZBLF$$juD$Ozn#1W~71^Whx&evE*{W9vt}L zfK+ZqbXQ~rL0P%++Nf>ocK?5#LIc@OoL7o$_oOhoe1#BiL9>5_e|c9|g;Fkv#yX+{ zR*E!Ut}}9TW|-RtZXXCC;b+Yb=(oLtmqVc_5f8 zK`F%J+oALu*bX5y10O{XAcgkvev|_oT8xjNDR84X_*3)-GUz0J4mrSs4#lnLD%7+! z;qosKS^pIrfTR!Of1wNjI)ngp8$4++BY>Y!${hgCD|-VjbO-(%(VzeTvszovqzzZ` zJbGkXB?9 zZ_S0AN>3CxCj#D~yFkTH19st4K!cy`1X#{+NNl zZJtP*Iy+vftw1%LX}1O{s8%-MS(~WQHp59 zj(|OiV!;9`-5x8b(KHjuHSvlmrkjrDPLuAXb#t$&<%-F97e7^#eyN(PyEtcJ8+RDu z2|>@^0xs?MdRRF4*H>Uyh3IMo!B!wnY+?t@Zv2D0w!UHD5@Jy69vo3)vAMm&`cb-I z%VH>Io6RJA2+3G)NUwrdFE-8B|3wK6lI$a%2b6ZU9jp!xPQu`v)!I#%CtyYPz=@~E zCYCw`4yd{JwWv)cJISMC0aAoY7h41MzScIVPs~uroTrBpOa(&e+xCAp({{deYlhAa zQ|*_gN}HFanHj;&U)>5Q+Ea7}TC=31(HaE?Z2+KheO1D<#+6wY(p{KKx2dRT(v|7^ z6h)I|O=|)Hag-{!1|#yn*A24GZ-SAtVz9(DatHcM55VJ})e1xaWJ9j%G%W9o1oxb> zsDN&dV?qjclwL1yAtrKPP{sLsb|Ye7;O7Njxb<8-;ARuxo~a_%$i9F1;(PB?FEW)c zE~BEZWAHTnsqU%asqNXqXZv0!UWdOfd|mo_`To0WffdY$r(7)sy*tuA&3kJ7lqB|W zxSB`g;du!6Pwq$VIQI|k=WrkQxXT@GM;B)i^EcR&@#wgJ?Dciu0wylRVVLAsLo3-i ziE(ne*HgRagc|@pf^?j5U@j4m**Ks8-k|2U;DH8s4wj`B&_KjUMe=B91n4UL-bQPk z7ej~*wJZVQ;>`8=`v?$A!^3#ofb3fpJW6%_K;l+gkiU-?FP4xpOcE^1PU$6nC%+} zv3)^_Rl*J77l|ax5sy0o`g21L10qQxaFULJ-YpRU-pv_tI7Pknj=xokng9}!8RVl& z_4`D4i_kZt+f=6p54(2tF)P2HIAnWteAK;fj358U?ubGn3(v&FSqzD=2MQ9zXw(G6 zBI5)k#(gt>BN!=)$*@iFSR_u=GmoaAw^%+s9!Zc$K!W!ATI$8UX#PcY&JSgV9%PeE z-3EU*)$hZNAV-DUY6~}!f8E%?l_kiBh0OGS%l){|(|6in8n1GYVwg|j#Kc-&kyvFTIzmWiiBelwK2Mi_( zC8w0fAvXxx>jmh-5(%D5Bon_9ltN>_GQ5QV_T%-IN^wV#$;w`N=nz2$)r9nW-tZFhCw z2fPd^H}X3IBki%06*)wf#7)`^rYk6BC{5um;tEd%O@|w_5&K!$N}1vlpf-=o7ZIFQ zm4$e0TrxU3#IPit$46XJ`1Hh9Mt4paYoDGR3sM;ga$(_SovnSGzvTNWa=lBQ_KEr0 zl3yDx4x|ABT>YOI=rd!9ma!5Z5z%`HzUQy12cu*ZIHyYwjg!LTwlUv(bYz5*(a;ez z+p7(D0E5}o($<)8X@klTAxPSot`vt1x~vR>?rX8rT-LL+<*RKk09MvvhWHH5ygxf%4DSC zmuO28iFm+cpBcLJz7;4*aRg+)@ zfsF{X4Pau!Qc7xA$KAe)f=~rIZ4h9G&T!(7{iE@uTtO@%C$KY|oTfN5CW2$8vdxJg z>a#SQuCSMkgeU1SWq)5Vg3E$FuBKzfbSIc~OLvsvJ3uF$(PsvGP8^4Xo3;-)!!c%V zZLMh%FzIA)N8!-D8&r?O2Q6VP;JXChL zNJ;y6nU8-YXe9Bww%)?>?yZyva$-u48!-Hw#N{Xf${^vWED6$VhqKNM8PK!W2$p3_ zEe2Zj0{cjFGY#euguVN;KkJu^_47aKjR*3B4?3-=2in2~ zc9ih}^|@3MHkBVn{?L^1^G_Ed1fzVGuH64mQ_~EzV5A*bZ2SJx8NSdh!BJ3~4l-d% zRJN+c;0$u0F^SA+++BW$9(ILG_Dm80Yb}na>3<)H*5Tj*_@SWDh8(()TZ%R<4_cA9 zm>=HZ7X2kl0W06c{NgeKL%(7_){{Z`{8 zLZ<&iL!4ZAudpLMev9`nx-Q`j*fU8QpTl9u)~Dc-+0RzJtzjmyfggPQC=LycHB`1)L?KUl~=Ihd)0+ynv zS9>Dzecj35j*0S+m0ypj=EVI=#`80ctuzjciSXKK1#kQ0nWWTNI5h#Ouwa@3>j@{{ z0=u2A89iBza(a*D`*VlaJ2#7du+Dm_VRt_eMH7zhg|g7!Ti@ojSZ|G2OzUnS@_m^6 zI@hQNdI6=vKV=8=UeFU4OlbVURz9uufnp$AEs$Wgr>AtGLH)Vb)m(B;bG1>8X1pEC zH79hM{BB&ISD8uiV9FhzUx6ki!pZj|r!CzFZ$|mF>{;kSv&7kCVHistS}*qJhIhW8 ziUZG0lJJa3F7W|%`6~1UUES#mzMg#Vx5gIZZ93kL)OP1G^$A3yuLNE|$L@A0hd zo>sgp@;nK_6`>>W?LPb~iFY3Q_-yqFU-&U$LBw&*Z{+E5^Ei9s+$**DrDxI}rw7t4-E4GS7m;7LhIGUo`z35wv40djamz`sL25l5 z_(C(?inn=KDo{L)fZ}L?%3Nv~7*H=ffPcYYn3v`ga4{gw_tUM&uCc?ttCeD*k0$V3 zfUjmp-gQkH7!=9m3xPgEdNj7klyiZ;{BQFr`M>c4U;CQnM3hi-M4D4qmm3{xKJxg{ zBfqin&`#=ilslA-XeVuaT=@RON8N;c<;s_QzFf}dzgkg#>iRz!IY!A_8b*Cg#c2HD zyN86Tw$qxXSSpo7<=fOvrBF{$WR(SQ zzS|4=ghqqPvhsfMaqLj2wv_1zsudQ-usW=wqK`xh3v;4xZ4Sf!T_-51*QX6`J~bHC zo{h1@!)$mP_l6rSr>|%4Wswtf+gR(em7bH{^laefpk>jpU5ZX!IOCBL_L_0%ANBus0n1I+XfAVPAs>}PVfe~^&EbM9+zc_s0 zcZ7s6hHuZUj<2Ot%W8#xG`t1`+oec9b-E+sd=Q#PM$Ig0PRYO}(=smyT;^ zENdT!qOF0G=*t|(E$I?nnzh4k$E4_Tt%xc|CHVBopq%uzC@8xiCMMeyxNUiiw!kK` zXbp2?Bl+GVU#^S{NaJ*#uH>jHEdl0$w$^~D*HTrL&>hWaR#sUs(b^Vi$5P3Xh+GjR zDw)%e;g>0_uNPv@U=VI1gD{gk3#2Ku=ljM~4!%I`bec@gQN1<^G{R8IdN)c6CmM?; zYa3oZ)6bATpl@o@&r5(@5Tpxm9R8QrFjpE{Pq712QmWZWC6j+^%ByFZJqBJ#?+BD~1`?u^)2z>wm+)s0U z-@N*VgupBKIjQjho`v)3vxR{-ihtWoAQE_?R@n`$mjRNQHJ2{^A#O}Ei*iKSO-OvAz98QC=#i@q)&7cbD4{^Ia-&7q8W_h`(7*#b-xb=ZwjouAK zR)*(7Le8{q9rW$kp`_tYoaocRt`1^$pU`h_z%Gcdd|dUqlH17XnssK8;a#;MxBB*M zPg~@5Tk_U2n>jzI?h5x=G25RPmhNuM^)s##LO_Uzltrn{SiJqdNI30y9%=pm@5h&m zj_r~yah|d9c@gQKe$M*UoB2tXI-FB3Ew1*oMP9S1w{Bn774E&vot0KIVW|GY<8MP< z?3wCmZ?on|s_JD6EPet+)}z$}RTl_#@KxVwM+`UjfO3pEM5f`e$8RI_s^e zBFCcUHQwTVZ#WhqM1=IU(u}rwy7w-#dFHekT<|HbY5b_Wkff4y_RKqy&RX?p()HD! zCSU)_wMu+)zih?Oj3Ie>_b>AMAX>TlNJvQ4>Rpd#vp)ZF@VWV8z89{4iT!5S<}PrkqQk@ zH++;rvKYD?ytsHB+8Ixg;yclhk~mJiDYlR(*aj@NM|+W#I2Oln%o3=JI3t{cTqh2} zZ0b6OtMQn& zRN&d?%3fog2R3Cj6`6h}Q4X5Gs#e-yUW#H-Q(V(LTn~&s0pbgqd$t}$GicXV!hVdN z!C%_=>E}J|M=7MuS{aK>z>_4i*{sxps=a4-^!c*Q<@?eHzaGs}D|}*Iv$r?^x!kSgx?;SjwHdcQm(f8OSDNpD%y8tipDitUFlN2u+bG-}zHnyX?V z(cV5=GZ-udWt6@KeNAs??PP5V=?@uj)23{vzea293KwUD!v$)PYX&L~_~>-GIusoI z4nCliZ?nra8u{WS)?iF~*A1{GMy*IIoHk9^C5*;RXbH7+kBl_sHO-FoR_me;jizJK zJZb%o8u%)R?{UXwC{8UO`Unc{GIXDi{!o||0-@|an+|Bg-L|3~EAD^0(aCmdwN%*n z?R~)lafh^YnWsw4Zg)eK&4A8T|(#s$t6p3oHLg`f@WRnZPlP@ ztWZ>`>8KD>;X}iyZYoB*x?U?EbZlVv+a@_F-cH;ni0*TNzQo<$vR!;qvQ2_9pHcJ* zvV23yD}Yi7c1gcve$GB=qw%vRKoYK+=AsHmcch(T6~jTOZNJg{yWLue)foG=-O82dE0AfEhW&I{SO^{6!< z__k64Ktgo=#X%OEm5q{E4kh2M-*mqv1jkY$d?XsdRb^XOvG52YLH_*_YR$H>n1xr} zzn3Q}-ZZ`tuTk>KD8!(mDp-hdv{Is26}U>UEaeKtQKUWVxaDkM77sP-*B6} z`IfP0ZtpXfMAa7N(Sm2rv7wEP-JgB>7fXR`Q-cu>>zS!N#{T(49~=xwnW7(so)ZK? z?@hXZKssQWP{fuT-zOmWyy+zfNJY^Tis%Cz+`~jhVU|I1^jLWCf)<^xD&VgTee0g3 zX$m+!D~{TfNjUrA&nNTCpR*RRdsZG>gc~*fkLf8a_ne$@O-ECqjtlA*-Sgb&9F1a)(_ll7l?q-CTljpShmI${ zAzrGy=G|(f$gH@RaZrsJ~JHo6Agy?e45OV!_M*g zDmNoTs`UK^zncG(hb;77R%w>O-6P@9o*4jF3EjKHErTlC;A?nAlKGBKq zMSXK`^v@Izss1qs5A71Yt}3 zl|Y^CmFkF`v^1Mmm%4s~#oE!KsH5|VH>^+1tx@!Mw0qVSTKu1UR7DE}X=PBxZtXP8 zC=MtJX!}N5;$PCl&+-TCEepz6#42kr^L72VTIx!P_%GL6`i4f0VbpU|O(2*KL3S|C z0-sV+Q7micmagv9*yOzI5J7rJ9GaC58YEkOJB1>VXmNX`r#Pes;&B~`afIaLC23;2 zmTdVgU#fy!584}Pu6IfVR@!RBZf*!F3o7mCD051JI9ywtY(BTA<8?-FCPc7TvsWu2 z`JVXjf6H#NST~nxv(>-VU?VY=+bwGQ*;Cq}4F(2WuAUr+01W;QFzk>}yTVkk-LoS$ zF=v-BDlcCyFUXG^jP&O$XRY{Rkvf?rFDuK`4h_jGa24{QA+6S;{0iE~lIarGytUox8f}YC1mIzz3_5*xTw{C1N!>mu{2 zZFklmy`XWw{n30BSz0gh&TQ>{EN(s<-gb3W;@0=M+!wRNt~3A%+udK83BbYzke>(^ z^e8X^#pnj`bbBYXZM_Nx(pWhAqQ8N0nHOWq2MTet?lyC7UU!$H06@70<^(4BZd#8Wl_){D8PX>7w~Pyq;+7P1YGIY!oYRw zpn@mtsjgW#AcyvVXcd6hVt}B{y_g6naF7(zayQVxM@PYI9D!Q!1Tq9fa{yjT0)qDW z#YEr|BMCL5jRq=P-g7fP9V