From 839c79405f09cbbbfd4d66323c8625c1b13b05ce Mon Sep 17 00:00:00 2001 From: wanglin2 <1013335014@qq.com> Date: Tue, 25 Jul 2023 09:25:42 +0800 Subject: [PATCH 01/11] =?UTF-8?q?Fix=EF=BC=9A=E4=BF=AE=E5=A4=8D=E7=BB=99?= =?UTF-8?q?=E6=A6=82=E8=A6=81=E8=8A=82=E7=82=B9=E8=AE=BE=E7=BD=AE=E6=A0=B7?= =?UTF-8?q?=E5=BC=8F=E6=A6=82=E8=A6=81=E8=8A=82=E7=82=B9=E4=BC=9A=E6=B6=88?= =?UTF-8?q?=E5=A4=B1=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/core/render/node/nodeGeneralization.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/simple-mind-map/src/core/render/node/nodeGeneralization.js b/simple-mind-map/src/core/render/node/nodeGeneralization.js index ba204186..ec7f95aa 100644 --- a/simple-mind-map/src/core/render/node/nodeGeneralization.js +++ b/simple-mind-map/src/core/render/node/nodeGeneralization.js @@ -36,15 +36,14 @@ function createGeneralizationNode () { // 更新概要节点 function updateGeneralization () { + if (this.isGeneralization) return this.removeGeneralization() this.createGeneralizationNode() } // 渲染概要节点 function renderGeneralization () { - if (this.isGeneralization) { - return - } + if (this.isGeneralization) return if (!this.checkHasGeneralization()) { this.removeGeneralization() this._generalizationNodeWidth = 0 @@ -67,6 +66,7 @@ function renderGeneralization () { // 删除概要节点 function removeGeneralization () { + if (this.isGeneralization) return if (this._generalizationLine) { this._generalizationLine.remove() this._generalizationLine = null @@ -87,6 +87,7 @@ function removeGeneralization () { // 隐藏概要节点 function hideGeneralization () { + if (this.isGeneralization) return if (this._generalizationLine) { this._generalizationLine.hide() } @@ -97,6 +98,7 @@ function hideGeneralization () { // 显示概要节点 function showGeneralization () { + if (this.isGeneralization) return if (this._generalizationLine) { this._generalizationLine.show() } From 8c79ffd723fe4523ca0ea3e0aaae30b91e68b6d0 Mon Sep 17 00:00:00 2001 From: wanglin2 <1013335014@qq.com> Date: Tue, 25 Jul 2023 09:38:50 +0800 Subject: [PATCH 02/11] =?UTF-8?q?Feat=EF=BC=9A=E5=AF=BC=E5=87=BAsvg?= =?UTF-8?q?=E6=97=B6=E6=9B=BF=E6=8D=A2svg=E4=B8=AD=E5=AD=98=E5=9C=A8?= =?UTF-8?q?=E7=9A=84 =E5=AD=97=E7=AC=A6=EF=BC=8C=E9=98=B2=E6=AD=A2?= =?UTF-8?q?=E5=AF=BC=E5=87=BA=E7=9A=84svg=E6=8A=A5=E9=94=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- simple-mind-map/src/plugins/Export.js | 4 +++- simple-mind-map/src/utils/index.js | 8 ++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/simple-mind-map/src/plugins/Export.js b/simple-mind-map/src/plugins/Export.js index 1f719d8f..626f73c7 100644 --- a/simple-mind-map/src/plugins/Export.js +++ b/simple-mind-map/src/plugins/Export.js @@ -1,4 +1,4 @@ -import { imgToDataUrl, downloadFile, readBlob } from '../utils' +import { imgToDataUrl, downloadFile, readBlob, removeHTMLEntities } from '../utils' import { SVG } from '@svgdotjs/svg.js' import drawBackgroundImageToCanvas from '../utils/simulateCSSBackgroundInCanvas' import { transformToMarkdown } from '../parse/toMarkdown' @@ -154,6 +154,7 @@ class Export { */ async png(name, transparent = false) { let { node, str } = await this.getSvgData() + str = removeHTMLEntities(str) // 如果开启了富文本,则使用htmltocanvas转换为图片 if (this.mindMap.richText) { let res = await this.mindMap.richText.handleExportPng(node.node) @@ -207,6 +208,7 @@ class Export { node.first().before(SVG(`
ImlH~9dYXI%z1}n3UV~MB(TWxLjCL0k*`6#vq!X6Y$pZb;DbBhH(zY&zWB 4{leZt;7Umz YH5cH<6!k~%if#bu^#lW4-D)@*^HRF#2MAG3J9OkpoIN^ID=<7yAI zY)41voD?72=f5jvg2dTwM~^Ap$|0I=5bu+37MsHtghzA~nO=}xGyJ<~&FmwRo}-0! zfwX 594G5Q7fl{@PopD2!nW!RQKN^9?o5F; z2I@o!=WpCAw4e(|fV+k_g5P9U^~y!Im8RI_!tPMqb?_wG;DV;n?BSkMSm+Yj*^SNA zGVa%24ehSM%}o8)7vj$@py?X@l6ZGYWTLjO+~$4E3hGGfzl2M~rG(gVU& %sUHSwvykzyZYE8S9ocQQGd*tC2qcblB zj84Cz%QrPt=1HtNmq`(9?}`d(x*F3Tr8{!)8-sOPQE`~O{;; ZTDGI8vr}!k$y&HE^G*dSiwaDQRn0t1+Yih-!9xwdK2y`yf z4Bd~OZX@n+S1jq+(2|vpjekxo$yNEecVH%f0=K#Zs@6+AIn5Ckg?C?ocV7{1LkRqI z?vria2iiAlQ@m| e@h)|m*Tq(=?SZm(@g_CJtw`Lu(S;yp!R?4{G&sP@kvlAb~j;yLP=C5!Z~pZ zcpL!jpGZ&aK-;ynmIC8c* LugHNJ2 zs 5A;P_J{VJXj>KDEjqZMZ I^ETWu>ilDA z5bXnW->eX}LMtnl2Z_4?RCM3b-w}Xk0NT`8%KD{we6#m45zuNtJSN%mX9d{CX8M~F z3ci_;`q`+Zo2JZ!6Y%YBbO$*ch<}ic+?zNNyKoy_+=a#HkNbdt8?o;c>z35jjmaAW zu5}4;ix0>bYROL3v}BS8I5`jj^(VVK%cvo1itMrj_h!KDPH}@5$-uh!x=|QJ2bP5M zT1<+?@?#F{CBN0*SqPP%WRS}B0_9@)iFD{q^jotX7wkqtxG~3eh7zzb{*VHtk?=PB zks`Vi5=^=hP9F*iV6Bt5TC~+$u$6>N= Chris
++\ No newline at end of file diff --git a/web/src/pages/Doc/en/introduction/index.vue b/web/src/pages/Doc/en/introduction/index.vue index 7f99b20d..419c1a6a 100644 --- a/web/src/pages/Doc/en/introduction/index.vue +++ b/web/src/pages/Doc/en/introduction/index.vue @@ -8,21 +8,21 @@+
水车
+Features
- -
- +
+- organizational structure diagrams, directory organization diagrams, timeline, and fishbone diagrams
- - -- +
+ +- summaries
- - - - - - - - + + + + + + + +Repository Catalog Introduction
1.
@@ -32,16 +32,16 @@ frameworks such as Vue and React, or without a framework.simple-mind-mapThis is an online mind map built using the
simple-mind-maplibrary and based onVue2.xandElementUI. Features include:-
@@ -119,6 +119,10 @@ full screen, support mini map- +
- images, icons, hyperlinks, notes, tags, and summaries
-- +
- outline, theme selection, and structure selection
-- +
- storage by default, but it also supports creating, opening, and editing local files on the computer directly
-- +
- and organizing layout
-- +
- between edit and read-only modes, zooming in and out, and switching to full screen, support mini map
![]()
Chris
++diff --git a/web/src/pages/Doc/zh/introduction/index.md b/web/src/pages/Doc/zh/introduction/index.md index f175c478..40186852 100644 --- a/web/src/pages/Doc/zh/introduction/index.md +++ b/web/src/pages/Doc/zh/introduction/index.md @@ -151,4 +151,8 @@+
水车
+![]()
Chris
++\ No newline at end of file diff --git a/web/src/pages/Doc/zh/introduction/index.vue b/web/src/pages/Doc/zh/introduction/index.vue index d3cca190..71f3fe74 100644 --- a/web/src/pages/Doc/zh/introduction/index.vue +++ b/web/src/pages/Doc/zh/introduction/index.vue @@ -8,19 +8,19 @@+
水车
+特性
- - - - - - - - - - -
- - - + + + + + + + + + + +
json、png、svg、markdown,支持从json、xmind、markdown导入- + +
json、png、svg、markdown,支持从json、xmind、markdown导入仓库目录介绍
1.
@@ -28,11 +28,11 @@simple-mind-map2.
web使用
simple-mind-map库,基于vue2.x、ElementUI搭建的在线思维导图。特性:- - - - - + + + + +
提供文档页面服务。
3.
@@ -111,6 +111,10 @@dist![]()
Chris
++From 8e30d4a94fb3fb0bbe1febb382680c0231cf2236 Mon Sep 17 00:00:00 2001 From: wanglin2 <1013335014@qq.com> Date: Tue, 25 Jul 2023 18:35:41 +0800 Subject: [PATCH 04/11] =?UTF-8?q?Fix=EF=BC=9A=E4=BF=AE=E5=A4=8D=E8=87=AA?= =?UTF-8?q?=E5=AE=9A=E4=B9=89=E8=8A=82=E7=82=B9=E5=86=85=E5=AE=B9=E6=97=B6?= =?UTF-8?q?=EF=BC=8C=E4=BA=8C=E6=AC=A1=E5=88=9B=E5=BB=BA=E6=A0=B9=E5=AE=9E?= =?UTF-8?q?=E4=BE=8B=E6=97=B6=E8=8A=82=E7=82=B9=E5=86=85=E5=AE=B9=E4=B8=8D?= =?UTF-8?q?=E6=B8=B2=E6=9F=93=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- simple-mind-map/index.js | 24 +++++++++++++++++-- simple-mind-map/src/constants/constant.js | 7 +++++- .../core/render/node/nodeCreateContents.js | 17 +++++++------ simple-mind-map/src/utils/index.js | 5 ++++ 4 files changed, 41 insertions(+), 12 deletions(-) diff --git a/simple-mind-map/index.js b/simple-mind-map/index.js index 731edd37..babb799c 100644 --- a/simple-mind-map/index.js +++ b/simple-mind-map/index.js @@ -7,9 +7,9 @@ import Style from './src/core/render/node/Style' import KeyCommand from './src/core/command/KeyCommand' import Command from './src/core/command/Command' import BatchExecution from './src/utils/BatchExecution' -import { layoutValueList, CONSTANTS } from './src/constants/constant' +import { layoutValueList, CONSTANTS, commonCaches } from './src/constants/constant' import { SVG } from '@svgdotjs/svg.js' -import { simpleDeepClone } from './src/utils' +import { simpleDeepClone, getType } from './src/utils' import defaultTheme, { checkIsNodeSizeIndependenceConfig } from './src/themes/default' import { defaultOpt } from './src/constants/defaultOptions' @@ -35,6 +35,9 @@ class MindMap { // 初始化主题 this.initTheme() + // 初始化缓存数据 + this.initCache() + // 事件类 this.event = new Event({ mindMap: this @@ -129,6 +132,23 @@ class MindMap { this.event.off(event, fn) } + // 初始化缓存数据 + initCache() { + Object.keys(commonCaches).forEach((key) => { + let type = getType(commonCaches[key]) + let value = '' + switch(type) { + case 'Boolean': + value = false + break + default: + value = null + break + } + commonCaches[key] = value + }) + } + // 设置主题 initTheme() { // 合并主题配置 diff --git a/simple-mind-map/src/constants/constant.js b/simple-mind-map/src/constants/constant.js index cf2ce546..1593bf81 100644 --- a/simple-mind-map/src/constants/constant.js +++ b/simple-mind-map/src/constants/constant.js @@ -324,4 +324,9 @@ export const nodeDataNoStylePropList = [ 'resetRichText', 'uid', 'activeStyle' -] \ No newline at end of file +] + +// 数据缓存 +export const commonCaches = { + measureCustomNodeContentSizeEl: null +} \ No newline at end of file diff --git a/simple-mind-map/src/core/render/node/nodeCreateContents.js b/simple-mind-map/src/core/render/node/nodeCreateContents.js index 9822484a..c521d2d4 100644 --- a/simple-mind-map/src/core/render/node/nodeCreateContents.js +++ b/simple-mind-map/src/core/render/node/nodeCreateContents.js @@ -1,7 +1,7 @@ import { measureText, resizeImgSize, getTextFromHtml } from '../../../utils' import { Image, SVG, A, G, Rect, Text, ForeignObject } from '@svgdotjs/svg.js' import iconsSvg from '../../../svg/icons' -import { CONSTANTS } from '../../../constants/constant' +import { CONSTANTS, commonCaches } from '../../../constants/constant' // 创建图片节点 function createImgNode() { @@ -293,20 +293,19 @@ function createNoteNode() { } // 测量自定义节点内容元素的宽高 -let warpEl = null function measureCustomNodeContentSize (content) { - if (!warpEl) { - warpEl = document.createElement('div') - warpEl.style.cssText = ` + if (!commonCaches.measureCustomNodeContentSizeEl) { + commonCaches.measureCustomNodeContentSizeEl = document.createElement('div') + commonCaches.measureCustomNodeContentSizeEl.style.cssText = ` position: fixed; left: -99999px; top: -99999px; ` - this.mindMap.el.appendChild(warpEl) + this.mindMap.el.appendChild(commonCaches.measureCustomNodeContentSizeEl) } - warpEl.innerHTML = '' - warpEl.appendChild(content) - let rect = warpEl.getBoundingClientRect() + commonCaches.measureCustomNodeContentSizeEl.innerHTML = '' + commonCaches.measureCustomNodeContentSizeEl.appendChild(content) + let rect = commonCaches.measureCustomNodeContentSizeEl.getBoundingClientRect() return { width: rect.width, height: rect.height diff --git a/simple-mind-map/src/utils/index.js b/simple-mind-map/src/utils/index.js index f28d757e..886e5385 100644 --- a/simple-mind-map/src/utils/index.js +++ b/simple-mind-map/src/utils/index.js @@ -460,4 +460,9 @@ export const removeHTMLEntities = (str) => { str = str.replaceAll(item[0], item[1]) }) return str +} + +// 获取一个数据的类型 +export const getType = (data) => { + return Object.prototype.toString.call(data).slice(7, -1) } \ No newline at end of file From 5ff9137745dd954aabad4f1c71373b340d82db66 Mon Sep 17 00:00:00 2001 From: wanglin2 <1013335014@qq.com> Date: Wed, 26 Jul 2023 09:04:24 +0800 Subject: [PATCH 05/11] =?UTF-8?q?Fix=EF=BC=9A1.=E4=BF=AE=E5=A4=8D=E8=8A=82?= =?UTF-8?q?=E7=82=B9=E5=A4=84=E4=BA=8E=E7=BC=96=E8=BE=91=E4=B8=AD=E6=97=B6?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=96=B0=E8=8A=82=E7=82=B9=E6=97=B6=E6=96=B0?= =?UTF-8?q?=E8=8A=82=E7=82=B9=E7=9A=84=E7=84=A6=E7=82=B9=E4=B8=A2=E5=A4=B1?= =?UTF-8?q?=E9=97=AE=E9=A2=98=EF=BC=9B2.=E4=BF=AE=E5=A4=8D=E8=BF=9E?= =?UTF-8?q?=E7=BB=AD=E6=8C=89tab=E9=94=AE=E6=97=A0=E6=B3=95=E8=BF=9E?= =?UTF-8?q?=E7=BB=AD=E5=88=9B=E5=BB=BA=E5=AD=90=E8=8A=82=E7=82=B9=E7=9A=84?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- simple-mind-map/src/core/render/Render.js | 2 ++ simple-mind-map/src/core/render/TextEdit.js | 3 +++ simple-mind-map/src/plugins/RichText.js | 6 ++++++ 3 files changed, 11 insertions(+) diff --git a/simple-mind-map/src/core/render/Render.js b/simple-mind-map/src/core/render/Render.js index 1e2664ea..9e06c260 100644 --- a/simple-mind-map/src/core/render/Render.js +++ b/simple-mind-map/src/core/render/Render.js @@ -444,6 +444,7 @@ class Render { if (this.activeNodeList.length <= 0 && appointNodes.length <= 0) { return } + this.textEdit.hideEditTextBox() let { defaultInsertSecondLevelNodeText, defaultInsertBelowSecondLevelNodeText @@ -486,6 +487,7 @@ class Render { if (this.activeNodeList.length <= 0 && appointNodes.length <= 0) { return } + this.textEdit.hideEditTextBox() let { defaultInsertSecondLevelNodeText, defaultInsertBelowSecondLevelNodeText diff --git a/simple-mind-map/src/core/render/TextEdit.js b/simple-mind-map/src/core/render/TextEdit.js index 523cf4ba..e426ecf6 100644 --- a/simple-mind-map/src/core/render/TextEdit.js +++ b/simple-mind-map/src/core/render/TextEdit.js @@ -106,6 +106,9 @@ export default class TextEdit { this.mindMap.keyCommand.addShortcut('Enter', () => { this.hideEditTextBox() }) + this.mindMap.keyCommand.addShortcut('Tab', () => { + this.hideEditTextBox() + }) } // 显示文本编辑框 diff --git a/simple-mind-map/src/plugins/RichText.js b/simple-mind-map/src/plugins/RichText.js index d501c282..c40acac7 100644 --- a/simple-mind-map/src/plugins/RichText.js +++ b/simple-mind-map/src/plugins/RichText.js @@ -268,6 +268,12 @@ class RichText { handler: function () { // 覆盖默认的回车键换行 } + }, + tab: { + key: 9, + handler: function () { + // 覆盖默认的tab键 + } } } } From 7e1a43143d723505bff3618d03a8542f964396fa Mon Sep 17 00:00:00 2001 From: wanglin2 <1013335014@qq.com> Date: Wed, 26 Jul 2023 09:17:48 +0800 Subject: [PATCH 06/11] =?UTF-8?q?Demo=EF=BC=9A=E5=88=87=E6=8D=A2=E4=B8=BB?= =?UTF-8?q?=E9=A2=98=E6=97=B6=E6=94=AF=E6=8C=81=E9=80=89=E6=8B=A9=E6=98=AF?= =?UTF-8?q?=E5=90=A6=E8=A6=86=E7=9B=96=E8=AE=BE=E7=BD=AE=E8=BF=87=E7=9A=84?= =?UTF-8?q?=E5=9F=BA=E7=A1=80=E6=A0=B7=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web/src/pages/Edit/components/Theme.vue | 34 ++++++++++++++++++------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/web/src/pages/Edit/components/Theme.vue b/web/src/pages/Edit/components/Theme.vue index b294e411..b5f09302 100644 --- a/web/src/pages/Edit/components/Theme.vue +++ b/web/src/pages/Edit/components/Theme.vue @@ -42,7 +42,7 @@ export default { }, data() { return { - themeList: [...themeList].reverse(),// ...customThemeList + themeList: [...themeList].reverse(), // ...customThemeList themeMap, theme: '' } @@ -61,32 +61,48 @@ export default { } } }, - created () { + created() { this.theme = this.mindMap.getTheme() this.handleDark() }, methods: { ...mapMutations(['setIsDark']), - /** - * @Author: 王林 - * @Date: 2021-06-24 23:04:38 - * @Desc: 使用主题 - */ useTheme(theme) { this.theme = theme.value this.handleDark() + const customThemeConfig = this.mindMap.getCustomThemeConfig() + const hasCustomThemeConfig = Object.keys(customThemeConfig).length > 0 + if (hasCustomThemeConfig) { + this.$confirm('你当前自定义过基础样式,是否覆盖?', '提示', { + confirmButtonText: '覆盖', + cancelButtonText: '保留', + type: 'warning' + }) + .then(() => { + this.mindMap.setThemeConfig({}) + this.changeTheme(theme, {}) + }) + .catch(() => { + this.changeTheme(theme, customThemeConfig) + }) + } else { + this.changeTheme(theme, customThemeConfig) + } + }, + + changeTheme(theme, config) { this.mindMap.setTheme(theme.value) storeConfig({ theme: { template: theme.value, - config: this.mindMap.getCustomThemeConfig() + config } }) }, handleDark() { - let target = themeList.find((item) => { + let target = themeList.find(item => { return item.value === this.theme }) this.setIsDark(target.dark) From 5a5c7702f5a3077f20f2bb85c9f26fcfd8057e1e Mon Sep 17 00:00:00 2001 From: wanglin2 <1013335014@qq.com> Date: Fri, 28 Jul 2023 08:48:51 +0800 Subject: [PATCH 07/11] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- simple-mind-map/src/plugins/AssociativeLine.js | 2 +- simple-mind-map/src/plugins/Drag.js | 3 +-- simple-mind-map/src/plugins/Export.js | 2 +- simple-mind-map/src/plugins/ExportPDF.js | 2 +- simple-mind-map/src/plugins/ExportXMind.js | 2 +- simple-mind-map/src/plugins/KeyboardNavigation.js | 2 +- simple-mind-map/src/plugins/MiniMap.js | 2 +- simple-mind-map/src/plugins/RichText.js | 2 +- simple-mind-map/src/plugins/Select.js | 3 +-- simple-mind-map/src/plugins/TouchEvent.js | 3 +-- simple-mind-map/src/plugins/Watermark.js | 2 +- 11 files changed, 11 insertions(+), 14 deletions(-) diff --git a/simple-mind-map/src/plugins/AssociativeLine.js b/simple-mind-map/src/plugins/AssociativeLine.js index 28223265..2a7ebba1 100644 --- a/simple-mind-map/src/plugins/AssociativeLine.js +++ b/simple-mind-map/src/plugins/AssociativeLine.js @@ -11,7 +11,7 @@ import { import associativeLineControlsMethods from './associativeLine/associativeLineControls' import associativeLineTextMethods from './associativeLine/associativeLineText' -// 关联线类 +// 关联线插件 class AssociativeLine { constructor(opt = {}) { this.mindMap = opt.mindMap diff --git a/simple-mind-map/src/plugins/Drag.js b/simple-mind-map/src/plugins/Drag.js index d85ea667..78a4b8aa 100644 --- a/simple-mind-map/src/plugins/Drag.js +++ b/simple-mind-map/src/plugins/Drag.js @@ -1,8 +1,7 @@ import { bfsWalk, throttle } from '../utils' import Base from '../layouts/Base' -// 节点拖动类 - +// 节点拖动插件 class Drag extends Base { // 构造函数 constructor({ mindMap }) { diff --git a/simple-mind-map/src/plugins/Export.js b/simple-mind-map/src/plugins/Export.js index 626f73c7..a5747595 100644 --- a/simple-mind-map/src/plugins/Export.js +++ b/simple-mind-map/src/plugins/Export.js @@ -3,7 +3,7 @@ import { SVG } from '@svgdotjs/svg.js' import drawBackgroundImageToCanvas from '../utils/simulateCSSBackgroundInCanvas' import { transformToMarkdown } from '../parse/toMarkdown' -// 导出类 +// 导出插件 class Export { // 构造函数 constructor(opt) { diff --git a/simple-mind-map/src/plugins/ExportPDF.js b/simple-mind-map/src/plugins/ExportPDF.js index 2fcc254d..e46b3a3c 100644 --- a/simple-mind-map/src/plugins/ExportPDF.js +++ b/simple-mind-map/src/plugins/ExportPDF.js @@ -1,6 +1,6 @@ import JsPDF from 'jspdf' -// 导出PDF类,需要通过Export插件使用 +// 导出PDF插件,需要通过Export插件使用 class ExportPDF { // 构造函数 constructor(opt) { diff --git a/simple-mind-map/src/plugins/ExportXMind.js b/simple-mind-map/src/plugins/ExportXMind.js index 5f54e220..42ea665c 100644 --- a/simple-mind-map/src/plugins/ExportXMind.js +++ b/simple-mind-map/src/plugins/ExportXMind.js @@ -1,6 +1,6 @@ import xmind from '../parse/xmind' -// 导出XMind类,需要通过Export插件使用 +// 导出XMind插件,需要通过Export插件使用 class ExportXMind { // 构造函数 constructor(opt) { diff --git a/simple-mind-map/src/plugins/KeyboardNavigation.js b/simple-mind-map/src/plugins/KeyboardNavigation.js index 0a3f6098..5ed4b593 100644 --- a/simple-mind-map/src/plugins/KeyboardNavigation.js +++ b/simple-mind-map/src/plugins/KeyboardNavigation.js @@ -1,7 +1,7 @@ import { bfsWalk } from '../utils' import { CONSTANTS } from '../constants/constant' -// 键盘导航类 +// 键盘导航插件 class KeyboardNavigation { // 构造函数 constructor(opt) { diff --git a/simple-mind-map/src/plugins/MiniMap.js b/simple-mind-map/src/plugins/MiniMap.js index 9bc1d6ba..78198685 100644 --- a/simple-mind-map/src/plugins/MiniMap.js +++ b/simple-mind-map/src/plugins/MiniMap.js @@ -1,4 +1,4 @@ -// 小地图类 +// 小地图插件 class MiniMap { // 构造函数 constructor(opt) { diff --git a/simple-mind-map/src/plugins/RichText.js b/simple-mind-map/src/plugins/RichText.js index c40acac7..2652428f 100644 --- a/simple-mind-map/src/plugins/RichText.js +++ b/simple-mind-map/src/plugins/RichText.js @@ -28,7 +28,7 @@ let fontSizeList = new Array(100).fill(0).map((_, index) => { return index + 'px' }) -// 节点支持富文本编辑功能 +// 富文本编辑插件 class RichText { constructor({ mindMap, pluginOpt }) { this.mindMap = mindMap diff --git a/simple-mind-map/src/plugins/Select.js b/simple-mind-map/src/plugins/Select.js index b2a58c14..bba83b0c 100644 --- a/simple-mind-map/src/plugins/Select.js +++ b/simple-mind-map/src/plugins/Select.js @@ -1,7 +1,6 @@ import { bfsWalk, throttle } from '../utils' -// 选择节点类 - +// 节点选择插件 class Select { // 构造函数 constructor({ mindMap }) { diff --git a/simple-mind-map/src/plugins/TouchEvent.js b/simple-mind-map/src/plugins/TouchEvent.js index 5afc1f04..2d4f78d1 100644 --- a/simple-mind-map/src/plugins/TouchEvent.js +++ b/simple-mind-map/src/plugins/TouchEvent.js @@ -1,5 +1,4 @@ -// 手势事件支持类 - +// 手势事件支持插件 class TouchEvent { // 构造函数 constructor({ mindMap }) { diff --git a/simple-mind-map/src/plugins/Watermark.js b/simple-mind-map/src/plugins/Watermark.js index 7d2ddaca..cd6a3c98 100644 --- a/simple-mind-map/src/plugins/Watermark.js +++ b/simple-mind-map/src/plugins/Watermark.js @@ -2,7 +2,7 @@ import { Text, G } from '@svgdotjs/svg.js' import { degToRad, camelCaseToHyphen } from '../utils' import merge from 'deepmerge' -// 水印类 +// 水印插件 class Watermark { constructor(opt = {}) { this.mindMap = opt.mindMap From 07be48d3423d03cc64f76060b86bb19cbbf8d79b Mon Sep 17 00:00:00 2001 From: wanglin2 <1013335014@qq.com> Date: Fri, 28 Jul 2023 08:50:29 +0800 Subject: [PATCH 08/11] =?UTF-8?q?Feat=EF=BC=9A=E6=94=AF=E6=8C=81=E6=90=9C?= =?UTF-8?q?=E7=B4=A2=E5=92=8C=E6=9B=BF=E6=8D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- simple-mind-map/full.js | 2 + simple-mind-map/src/core/render/Render.js | 10 +- simple-mind-map/src/plugins/Search.js | 143 ++++++++++++++++ web/src/pages/Edit/components/Edit.vue | 7 +- web/src/pages/Edit/components/Search.vue | 188 ++++++++++++++++++++++ web/src/pages/Edit/components/Sidebar.vue | 11 +- 6 files changed, 353 insertions(+), 8 deletions(-) create mode 100644 simple-mind-map/src/plugins/Search.js create mode 100644 web/src/pages/Edit/components/Search.vue diff --git a/simple-mind-map/full.js b/simple-mind-map/full.js index c119beb8..2c78b8be 100644 --- a/simple-mind-map/full.js +++ b/simple-mind-map/full.js @@ -10,6 +10,7 @@ import AssociativeLine from './src/plugins/AssociativeLine' import RichText from './src/plugins/RichText' import NodeImgAdjust from './src/plugins/NodeImgAdjust.js' import TouchEvent from './src/plugins/TouchEvent.js' +import Search from './src/plugins/Search.js' import xmind from './src/parse/xmind.js' import markdown from './src/parse/markdown.js' import icons from './src/svg/icons.js' @@ -36,5 +37,6 @@ MindMap .usePlugin(RichText) .usePlugin(TouchEvent) .usePlugin(NodeImgAdjust) + .usePlugin(Search) export default MindMap \ No newline at end of file diff --git a/simple-mind-map/src/core/render/Render.js b/simple-mind-map/src/core/render/Render.js index 9e06c260..217751de 100644 --- a/simple-mind-map/src/core/render/Render.js +++ b/simple-mind-map/src/core/render/Render.js @@ -975,7 +975,8 @@ class Render { setNodeText(node, text, richText) { this.setNodeDataRender(node, { text, - richText + richText, + resetRichText: richText }) } @@ -1101,7 +1102,7 @@ class Render { } // 定位到指定节点 - goTargetNode(node) { + goTargetNode(node, callback = () => {}) { let uid = typeof node === 'string' ? node : node.nodeData.data.uid if (!uid) return this.expandToNodeUid(uid, () => { @@ -1109,6 +1110,7 @@ class Render { if (targetNode) { targetNode.active() this.moveNodeToCenter(targetNode) + callback() } }) } @@ -1121,7 +1123,7 @@ class Render { } // 设置节点数据,并判断是否渲染 - setNodeDataRender(node, data) { + setNodeDataRender(node, data, notRender = false) { this.setNodeData(node, data) let changed = node.reRender() if (changed) { @@ -1129,7 +1131,7 @@ class Render { // 概要节点 node.generalizationBelongNode.updateGeneralization() } - this.mindMap.render() + if (!notRender) this.mindMap.render() } } diff --git a/simple-mind-map/src/plugins/Search.js b/simple-mind-map/src/plugins/Search.js new file mode 100644 index 00000000..5bbd3e77 --- /dev/null +++ b/simple-mind-map/src/plugins/Search.js @@ -0,0 +1,143 @@ +import { bfsWalk, getTextFromHtml } from '../utils/index' + +// 搜索插件 +class Search { + // 构造函数 + constructor({ mindMap }) { + this.mindMap = mindMap + // 是否正在搜索 + this.isSearching = false + // 搜索文本 + this.searchText = '' + // 匹配的节点列表 + this.matchNodeList = [] + // 当前所在的节点列表索引 + this.currentIndex = -1 + // 是否正在跳转中 + this.isJumping = false + this.onDataChange = this.onDataChange.bind(this) + this.mindMap.on('data_change', this.onDataChange) + } + + // 节点数据改变了,需要重新搜索 + onDataChange() { + if (this.isJumping) return + this.searchText = '' + } + + // 搜索 + search(text, callback) { + text = String(text).trim() + if (!text) return this.endSearch() + this.isSearching = true + if (this.searchText === text) { + // 和上一次搜索文本一样,那么搜索下一个 + this.searchNext(callback) + } else { + // 和上次搜索文本不一样,那么重新开始 + this.searchText = text + this.doSearch() + this.searchNext(callback) + } + this.emitEvent() + } + + // 结束搜索 + endSearch() { + if (!this.isSearching) return + this.searchText = '' + this.matchNodeList = [] + this.currentIndex = -1 + this.isJumping = false + this.isSearching = false + this.emitEvent() + } + + // 搜索匹配的节点 + doSearch() { + this.matchNodeList = [] + this.currentIndex = -1 + bfsWalk(this.mindMap.renderer.root, node => { + let { richText, text } = node.nodeData.data + if (richText) { + text = getTextFromHtml(text) + } + if (text.includes(this.searchText)) { + this.matchNodeList.push(node) + } + }) + } + + // 搜索下一个,定位到下一个匹配节点 + searchNext(callback) { + if (!this.isSearching || this.matchNodeList.length <= 0) return + if (this.currentIndex < this.matchNodeList.length - 1) { + this.currentIndex++ + } else { + this.currentIndex = 0 + } + let currentNode = this.matchNodeList[this.currentIndex] + this.isJumping = true + this.mindMap.execCommand('GO_TARGET_NODE', currentNode, () => { + this.isJumping = false + callback() + }) + } + + // 替换当前节点 + replace(replaceText) { + replaceText = String(replaceText).trim() + if (!replaceText || !this.isSearching || this.matchNodeList.length <= 0) + return + let currentNode = this.matchNodeList[this.currentIndex] + if (!currentNode) return + let text = this.getReplacedText(currentNode, this.searchText, replaceText) + currentNode.setText(text, currentNode.nodeData.data.richText) + this.matchNodeList = this.matchNodeList.filter(node => { + return currentNode !== node + }) + this.emitEvent() + } + + // 替换所有 + replaceAll(replaceText) { + replaceText = String(replaceText).trim() + if (!replaceText || !this.isSearching || this.matchNodeList.length <= 0) + return + this.matchNodeList.forEach(node => { + let text = this.getReplacedText(node, this.searchText, replaceText) + this.mindMap.renderer.setNodeDataRender( + node, + { + text, + resetRichText: !!node.nodeData.data.richText + }, + true + ) + }) + this.mindMap.render() + this.mindMap.command.addHistory() + this.endSearch() + } + + // 获取某个节点替换后的文本 + getReplacedText(node, searchText, replaceText) { + let { richText, text } = node.nodeData.data + if (richText) { + text = getTextFromHtml(text) + } + return text.replaceAll(searchText, replaceText) + } + + // 发送事件 + emitEvent() { + this.mindMap.emit('search_info_change', { + currentIndex: this.currentIndex, + total: this.matchNodeList.length + }) + } +} + +Search.instanceName = 'search' + +export default Search diff --git a/web/src/pages/Edit/components/Edit.vue b/web/src/pages/Edit/components/Edit.vue index 94f3ea52..7dc8c17b 100644 --- a/web/src/pages/Edit/components/Edit.vue +++ b/web/src/pages/Edit/components/Edit.vue @@ -18,6 +18,7 @@ >+
水车
++ @@ -35,6 +36,7 @@ import RichText from 'simple-mind-map/src/plugins/RichText.js' import AssociativeLine from 'simple-mind-map/src/plugins/AssociativeLine.js' import TouchEvent from 'simple-mind-map/src/plugins/TouchEvent.js' import NodeImgAdjust from 'simple-mind-map/src/plugins/NodeImgAdjust.js' +import SearchPlugin from 'simple-mind-map/src/plugins/Search.js' import Outline from './Outline' import Style from './Style' import BaseStyle from './BaseStyle' @@ -59,6 +61,7 @@ import Vue from 'vue' import router from '../../../router' import store from '../../../store' import i18n from '../../../i18n' +import Search from './Search.vue' // 注册插件 MindMap @@ -73,6 +76,7 @@ MindMap .usePlugin(AssociativeLine) .usePlugin(NodeImgAdjust) .usePlugin(TouchEvent) + .usePlugin(SearchPlugin) // 注册自定义主题 // customThemeList.forEach((item) => { @@ -100,7 +104,8 @@ export default { NodeNoteContentShow, Navigator, NodeImgPreview, - SidebarTrigger + SidebarTrigger, + Search }, data() { return { diff --git a/web/src/pages/Edit/components/Search.vue b/web/src/pages/Edit/components/Search.vue new file mode 100644 index 00000000..9fdda8a4 --- /dev/null +++ b/web/src/pages/Edit/components/Search.vue @@ -0,0 +1,188 @@ + + ++ + + + + diff --git a/web/src/pages/Edit/components/Sidebar.vue b/web/src/pages/Edit/components/Sidebar.vue index 5f0cec24..d82b5153 100644 --- a/web/src/pages/Edit/components/Sidebar.vue +++ b/web/src/pages/Edit/components/Sidebar.vue @@ -39,7 +39,7 @@ export default { } }, computed: { - ...mapState(['isDark']), + ...mapState(['isDark']) }, watch: { show(val, oldVal) { @@ -48,6 +48,11 @@ export default { } } }, + created() { + this.$bus.$on('closeSideBar', () => { + this.close() + }) + }, methods: { ...mapMutations(['setActiveSidebar']), @@ -74,10 +79,10 @@ export default { &.isDark { background-color: #262a2e; - border-left-color: hsla(0,0%,100%,.1); + border-left-color: hsla(0, 0%, 100%, 0.1); .sidebarHeader { - border-bottom-color: hsla(0,0%,100%,.1); + border-bottom-color: hsla(0, 0%, 100%, 0.1); color: #fff; } From d06f57a5dcd0d6d00edfaa7af59650eb9084239a Mon Sep 17 00:00:00 2001 From: wanglin2 <1013335014@qq.com> Date: Fri, 28 Jul 2023 08:55:35 +0800 Subject: [PATCH 09/11] =?UTF-8?q?Demo=EF=BC=9A=E4=BC=98=E5=8C=96=E6=9A=97?= =?UTF-8?q?=E9=BB=91=E6=A8=A1=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web/src/pages/Edit/Index.vue | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/web/src/pages/Edit/Index.vue b/web/src/pages/Edit/Index.vue index 997d69ac..bc81551a 100644 --- a/web/src/pages/Edit/Index.vue +++ b/web/src/pages/Edit/Index.vue @@ -79,6 +79,14 @@ export default { body { &.isDark { + /* el-button */ + .el-button { + background-color: #363b3f; + color: hsla(0,0%,100%,.9); + border-color: hsla(0, 0%, 100%, 0.1); + } + + /* el-input */ .el-input__inner { background-color: #363b3f; border-color: hsla(0, 0%, 100%, 0.1); @@ -91,6 +99,15 @@ body { color: hsla(0,0%,100%,.3); } + .el-input-group__append, .el-input-group__prepend { + background-color: #363b3f; + border-color: hsla(0, 0%, 100%, 0.1); + } + + .el-input-group__append button.el-button { + color: hsla(0, 0%, 100%, 0.9); + } + /* el-select */ .el-select-dropdown { background-color: #36393d; From 8e59677623359fafe6ac155ca60c3af1a5f36331 Mon Sep 17 00:00:00 2001 From: wanglin2 <1013335014@qq.com> Date: Fri, 28 Jul 2023 09:33:18 +0800 Subject: [PATCH 10/11] Doc: update --- README.md | 4 ++ simple-mind-map/package.json | 2 +- web/src/assets/avatar/仓鼠.jpg | Bin 0 -> 39928 bytes web/src/assets/icon-font/iconfont.css | 10 ++- web/src/assets/icon-font/iconfont.ttf | Bin 26060 -> 26300 bytes web/src/assets/icon-font/iconfont.woff | Bin 16432 -> 16600 bytes web/src/assets/icon-font/iconfont.woff2 | Bin 14076 -> 14196 bytes web/src/config/en.js | 5 ++ web/src/config/zh.js | 5 ++ web/src/lang/en_us.js | 7 ++ web/src/lang/zh_cn.js | 7 ++ web/src/pages/Doc/catalogList.js | 1 + web/src/pages/Doc/en/changelog/index.md | 8 +++ web/src/pages/Doc/en/changelog/index.vue | 4 ++ web/src/pages/Doc/en/constructor/index.md | 2 +- web/src/pages/Doc/en/constructor/index.vue | 2 +- web/src/pages/Doc/en/introduction/index.md | 4 ++ web/src/pages/Doc/en/introduction/index.vue | 40 ++++++----- web/src/pages/Doc/en/render/index.md | 4 +- web/src/pages/Doc/en/render/index.vue | 5 +- web/src/pages/Doc/en/search/index.md | 68 ++++++++++++++++++ web/src/pages/Doc/en/search/index.vue | 74 ++++++++++++++++++++ web/src/pages/Doc/en/utils/index.md | 6 ++ web/src/pages/Doc/en/utils/index.vue | 5 ++ web/src/pages/Doc/routerList.js | 4 +- web/src/pages/Doc/zh/changelog/index.md | 8 +++ web/src/pages/Doc/zh/changelog/index.vue | 4 ++ web/src/pages/Doc/zh/constructor/index.md | 2 +- web/src/pages/Doc/zh/constructor/index.vue | 2 +- web/src/pages/Doc/zh/introduction/index.md | 4 ++ web/src/pages/Doc/zh/introduction/index.vue | 40 ++++++----- web/src/pages/Doc/zh/render/index.md | 4 +- web/src/pages/Doc/zh/render/index.vue | 5 +- web/src/pages/Doc/zh/search/index.md | 68 ++++++++++++++++++ web/src/pages/Doc/zh/search/index.vue | 74 ++++++++++++++++++++ web/src/pages/Doc/zh/utils/index.md | 6 ++ web/src/pages/Doc/zh/utils/index.vue | 5 ++ web/src/pages/Edit/components/Search.vue | 20 +++--- 38 files changed, 452 insertions(+), 57 deletions(-) create mode 100644 web/src/assets/avatar/仓鼠.jpg create mode 100644 web/src/pages/Doc/en/search/index.md create mode 100644 web/src/pages/Doc/en/search/index.vue create mode 100644 web/src/pages/Doc/zh/search/index.md create mode 100644 web/src/pages/Doc/zh/search/index.vue diff --git a/README.md b/README.md index 27232255..0255d124 100644 --- a/README.md +++ b/README.md @@ -144,4 +144,8 @@ const mindMap = new MindMap({+ +++++ + +替换 ++ {{ currentIndex }} / {{ total }} +++ + +取消 +++替换 +全部替换 +水车 + +
+ 仓鼠 + \ No newline at end of file diff --git a/simple-mind-map/package.json b/simple-mind-map/package.json index 007c8ee4..0ef87aee 100644 --- a/simple-mind-map/package.json +++ b/simple-mind-map/package.json @@ -1,6 +1,6 @@ { "name": "simple-mind-map", - "version": "0.6.8", + "version": "0.6.9", "description": "一个简单的web在线思维导图", "authors": [ { diff --git a/web/src/assets/avatar/仓鼠.jpg b/web/src/assets/avatar/仓鼠.jpg new file mode 100644 index 0000000000000000000000000000000000000000..1eabba76031c1d974f4e3c0acbe8a3febfbc31fc GIT binary patch literal 39928 zcmbTe2V4`|_68a{BAo>3y(I{T5|koJFM$AQlz>VTNkF8l*eHS;0qH_Sgph=UrXUDd zjvyUDDFG2dkt$#ZL{afgK<_#C{_lP7-Z%V)8ImwFYwuOQ^{ut@bM)sVNEBh`Xb0lp z0D&BUAJETNAZOd-gdh+Ii3GtwAP_%@lS3TD1)OmJKcMpj5clRe2&4fVK_Jdz&fl+a zRTgvob
@uj9dm(;X z{+lmWXMQ$=#Q3=?fgcVP5T_Uiml(&-HjpCl6g(W8+vdLkzc@I#xOsT___qmc2R=|Q z3Op_s7biCt4-fk`9H)WzLEK_I;>!A#yb>rsK9vZtK|*E;zp7PD3j{szLCw%VGI5)L z 1sx715Tl}FVvij^5tno> zIpzF?i=?dVoZP%C`2}Q3DV0VqV=&8WZ`IY`ZfI<}^RTt8y`!_MyJv8SH9Yck^x4>} zsp*;7x%t-%iyuENudIGv`|@?2Ef)ufYfCKP_m*V;B9|B-7biD27dIbUE)LFPY{A92 zd6e~e#Vt{Mei0HX1_}INtIU#`mTjtr=noM8$N>RKHTWy_k8II4CHwCLOZ@*O**^sP zORnc2YmnULrY9$-XQQX5XX9f7Y(CLG>@7MqdUH$twJ|ogHug6DkL@RL5e^kKHDkE3 zFC6YWR~TP7_djnxCjhlH`#A;@ b3hLh=QpZ1n`RC>cCplrtwut9WaN z27{ `#?;Jk|*isk&(?sX}n%B*V{{6>)H`#a)6iU$I2>xr9xWT`rMYE-K+M2_^ z#^PTjW=}RS^NtpP+Wlj1qvVl5>{+w|bb^bmdmO(7`)`hj-}<&S2%7`I@pJIE;PC>$ zBLh c_c#)OBm6?=0~*aM&s4Q87d9>G?dP1X6g!@?e-HX85=Kr?^O?*C+w z>``RvHt6inA@*qa{zF3n3I5scf7}0mIQ}~=0iNt{)9hsNkOU A0s^{?oH9KN~n+o#PQ|>+I#EXL2i#6v1yQzwbjvav_g^aS{z!N zt_K*G-zLp|B?C|oz^QCa1^YA9V#{C7 FS6WW^f|Ke(SStJl1`1CL3Vp~b@-yV|f3hGe60O7SnF$3G>1dibAJ2r?}5;`s0 zm6(AOsW_T8DbXE2GA53@G#f_zkQw1MdzTf2LFY@B0q2_8XtO+cPl0}kz8y4}DV1Le zUew}f-1u8|NRXEcVXB{jxY7Pe^%An*w5O 8nBH0GnDLjYUFxB;b0xabJPD8M$+Qh*2N1jaHUdDCs7-8cugUSJ2hgo}VW zat=5GHl?nkz7+!g#qIrNc)vr$ruTCY`8l#Vf|6VFz>ZXy|Hec8%>(EC6>m4EVN(m) z9t#=_O#0wdK1gor_FF%k&&VH$2STmW!4Q;&U4_9P;Mo|JTD8NPYOIUO?Oy6lJz}I@ zn60zVttd2v8%>-HYS74rs8-8l2wW1hfKKABp7hcc-jVz+ 8tKG$H~bZU0f`fyRsc%@CiX=e;gxR e>wz)4XDDd8VU!dTdgP@%b_M=M>{<^tXBkNi z-UawrTcJ(&0gML-4QTwPK>%~M&Gzq8wHbSVJD)8@{H>7x`3RWRrd#Iv`IJ40zTmn| zB=!F_iNJFVvOoAYxUr@Fd2B3Z!K1?l40Y(KctI2?9Nb9fB1E{fn-P|hEG~SsgMKLm zw<$?ax)+LpnpBD#QYEtS_QZ5;2Z}e;$2S~C4V @I?dNpO*+&QB%I6@t_2F3I*h z50GYZa&>oCNZyp5<@#c;x!^H1WEZ7 xLkX`>P^qyy%MQn{SS z1fqYea!jWR!$+l2U|LnB&U2nr8u4($s8X1Csa)$(nO&>CJiS&U`PGqH!ISGxk%gqd z3t76)cx}xjWU9g ?NThhs%aUC!dxJOZYs15Q!jG(wa`B49Os zY!e0NZ2iH>et~U_Y=p7h(N>3Tyqm!CuUP#TIQrXb>OhF>p9d!XpOMl(yyO4%X@LJi zfS`n5lgM^rz|E O=WSuCMBDWtGqE(2lp#07*e)Hl5Z=9P|4F8! zs3%yY*TCF1hBa7{BBC_ZSQ*jNC*3kdGsyOC*yXsp_nw|qrQ_QJT O1(Qf^5@xqRq?=g(# zcDusa`$S^jtzdfw04CEoDVd*1c}(YLp*Y+9nP_+GX(_H3Rak+&zUDlx*5F+NTJ&tR zXWd`~QU{fd%@og=Vm)My4@F@!voqcdd#5Ljldc;b2 {ob2Ne_j+%}&{ZZKr@CV_ zgh#0&Qpj(@8!nVPD`NdaJA4`GTbV4I5lo!LT-W|&LPF%(1do@-K%^_vXV}(kp$?3$ zAP|6{f&hSLkNID5cFS1zEf@?q>VJTL5T1Qm00US63Ree04*+ @JAm-li`)uSH|R|j_5$|IxyfLSG~ZHSTb z-k?1+C*-!@)&rH_h~|qcRjceIljw8hJTL1NSg#_9$;NJbv7xO74bl}! #!{AcY*THCtqH-Rz2Om?|MC #YqjwCI?rc z9%;s!@6ez|4V)(Q>>?3`+G^gTq5>-`9e8=6EEGBzh)i5kot%uz1&eKuLG6|zJ|mfU zxncQ %yY3+3^JVC==_RiU48%p%R)n^5DIiJyGyaitJ{=d=%2jGNl>UD0)>;TQ4 zdiKtKm6MHQwmbn4c>gP#M*}d3Z!<~A%inanTWRpWbKYAHn}-Tul+9WGr9f;w0qh+N zssrw+W6bHohjGN4hWb0D-h5?uR>nDav>_G$JmI^26*}{ggnL<}3# d@d8r1 z@3p;aY7~k!us(c0Rk=yKP#|1NigEGLNr8xPZ8O;kpR80C??J@j{Jyml>u1x<_)K+z z@*jg3yLu-a6s!Ko{4u9-X Y@BZZ=}jOt2=GPFRz`qUh;C$JY;%BYru)5t?E~YVX!CL z4k~yA-1sYh`h`R{f%j(42q4p6ngRWV#(%qjU-`p7p(_9;XD9->(LykV@k^iny#fGU zuvu#hA8g%=@XOrUsMSITh)3I<0Q}buxnI^f+kD6zf77Cy^G@>q444R_00OocCkMf- zwAzD0?Qds8c4@WiH3@6AJKerY@{8Pq9=bqn2l>=$_A14>mx`8Z)V_J}^b91{oqIC2 zN~Jmqmz!cJ9)+Y*>T4cOv@^MQAi{*6)QRI@{ToUzTsezWHJMC^1=E5NI->vQjFYJR ziFH;p$_W#N%@QYjgQ+Y#GMK7N3?hu9B{~tMl3n;xrW|A1NX%~uuPUufwA27sM-;6J zze1{Eh?CT5Mn_?>XTwpGrMKw0(y^v4@S7pT!@}x!?bJSvV0w`5v|CR_?v)Y@8oPsU z+n0V)T6Y82;8n8dd7qo}BN iTKec9YZ873Q7-2Q^jSlO6%`m10^psDz~@^a 4($zHME;Tj?4LFC_ZHN(Z(Ki$-35qB8{n4%$18n`4{{ayj m@@P)m2%PX(Wn&Qyo4X%hnKRX_wj4LP< z=jF3_^htq)IqVX)XwlBhUwSVq#~VK96yO?}PN#%0o-#R^I`>)EPv0ih-Z^!!J^RZ* za8*ZPQQMI-Np#o2l912)qGkHMwRTWglo!eawsHe+_BtNZPn+qau#OxHHxv8fzLr z%gx9vITc-w*?%$mB?k1O@5kcNv3DYNMsR~|UIjU4aK52^m;g7!Qw#SOPJKOJb+9K@ z;zfynQ|I6n6Q2v!x-;^GsSj(kBlms~#lfrKuWbvI!HFAjSKjhZESdSog1=DgGPEU< zkdC4Y6`q6QWdSZPi*i2Syd>?j5rce7b!{AWqz6v>KaRq^i{rpOqhI*WN ?-I>KTCnf(B5UevjNY^dN>OT2BvNS0?mGq$5A_&*_oG-27=9``OMC zdXht-K3Foi687=1+%m??+m`bXUvJ#?1)TDAzn0g;DPxbGm80`M%g(QpyeX^dh9*l$ z9WT$Z=|`CDmv0P~9)^g;z9Cj>XTM(oPfUg%FQ;9OOU~~%VwTQLXZNXPwnko61N{gw z>^-#L;?<|p YkkzFNzv)6ZVd|Fc+Nyujb_}B{IQ8trL zfYqfIZ$>M?H*IFsn-J(Xf3oSh{?bXd`T;(0_&@3GR!*{+m#FKoqwnAG5Svm1T;K2X zcRK>A1s2Q{mQsvcv>Y$KjF{D?*Ps3X{mjS=zQR`A9D%HJoSSD1o8#1WN0Kv!b0?b7 zsZnGLFB+z*MhUE%;NovXd+xnE`jA}uB~820G8 &b@+9ngI7JYafQC(HyYA!(nt3)LKoH=C5)GB;UmszDLV7ce zAU= 92nT0^bYAAoDNcK zcUzc*JfQ_2P1VZ1uzl?6IHmk~mT24zYp=CL%OdjNHyf_B)@1tE4?I$;@5t7My+TjE zDUp*)VG($`r*OEvF=T(DB--^uwuN*)Q)LdjPCCP )AgU^>!y?>AZY29YZs?vQ+nhoJ(uBoCY)2 zN!wi;*CU(c7kAE)kV0nWLXC6cO2iP!MHg6F-Bq6(RX%%Gt50alJB}MKIUhzLGe5TD z4znYR8i9{^;l2$%A88%bqn6BUnkyhXv&JjL&vzQdO1H==(t?5tWx+%0ZDA#cZ+`tK zlC#<#=z#<$u-d+-Ox+Cg)kC;_GLtDa^St2*BxmO+00~W{QR%->_GW#66AdIyoPV*y z|2K7u|2e`|)V~p#n|unpkg=)AzY=Kn)&50K0ob^CD>emI2S9W|ls7)G!2QDKxf M{s0#_F}a6m5b`=?JQMG!Wr*>jFhZ>}&O@yzshIeFJwKwtRZK=c-URsCw;dz$C4 z ^c3)qAAi&&vjv(px__RM~w+b(i+iyS`lk`@|}z+evd=SeHTDTXhu7 zUdD%CHp=benprKKS%u%_i$f-XeYnzlgTH>$FZJ)2NEg}xT2_ ?NAv8S zJ6Ej&gT6^7a}$gme*u46A;sX+$Z7C%K-wW4 z8Pvg`e8;yg+n3PK5X#pPCS@Ehw20B5A-k7xr**W`TXzZUBAo`gaUnie-1N>$ZHt|v zByayyJ7UMS%W>S#F3FeHD5d99oa}8Ln`o|7Cu0S#<3oy#KNkxh?mjgsa5P)PFsU!v zO04shOK|gn9G=I%=}puz p4n0_cD zPG-QY@DsIvLDZqq{3K2ZZ S;h)QBb!yk&j?n>zDVg@{Z2cu-m9wFY{;nV*p zxX7VFceVUnMq`y ztWvozp)ce8S;c6sy+6(#S7f=L4ml_?0bMc5#Y@#_QL+-PNLqAGwpP{U08}5qWo%Xk zB-9QyRWR})f;Rq@VVw0O JfF~C~UIBoRg__Ss?@^U=C#f{qk)j0UFhl^{Y !d(+Id3XWSNq;rjs9GV|3!o|ShJyNq!0Bs>nMP+CEd>qcvqS2)#%sd+K$Bo7c zRND`Ceb1{%f9^)6@XmF=-ghk;v){$p`Zf0jw=RG%Sp5kS=*+Tj40DxF_Plqs? 50Io<;1AS7fy`9eSOj?}oku&- zl=UYz6cNDp^1i-L*BIf2SC#865OnFNMe6W5a_*5OM?X%QSbQ)iRg}l1;*~v^XYRTM zXu*;u4;8^%Xh5lf{zcF#mK%?ul=|g3l6TB2TOZW21TrEE^xoMHn<1> bW*k?oR}Vd?d_4p<=Uh15SUGaL5tWzLye{)Pwc6TUiE;&u<7ZO{ zK *y^We~~=_gLO*=BU4?c^1dhxAE9&J z%*h&$ZItG9Xt#W+k 7E6Je5 ?i0z`TqS54^$+c zm{j&=MV_d>1Fvc_$c%h3I~^*K5fTY)S@P8DEJ7Yo)S9|$UX*gGqweu8k+@R$pV$L) zc^U?S4VGSOI}hb2^t;!%+v~W-*`N|5=W@sq(+Q|-OGhD%e3Q9!I+gQQ=zub)ECIpe zk~v4EC{c$Q0gs|EouZwH+nFSua48gt(0Z!ugOTAMQa@ng9FzSjxIKR5zAz9q!d=wY zjdLnB63TMpV56j1`Vg}Ygbh7I%!n}(!K@Zr2!k{2_(MN)rZg*^94JESff~W0W*8^A zNmQXwXZ$`q$S+yM&G;v1?5gm5Xo$YyBI#&n!E=XxOc05eess {=5g@)pQQlR+y;Lv-|N-C{ZX8AoAQ^VYC>@&N=bdY#ovK z7>Cs7pJ&}~5hs?y8bhO{6lvHh!F)Dls02_(|7;rw(?K)|G;od+_Lvuz B z3Luwv4^qdGmdaDL*)rN>1aJ#>4Y-Ysdw=a*KL_W}^H!A#1ZF05F%l~33(UxfW`P{h z5c9xhrlhYnw)^JSPTpHc?<_Yv9&F_t9-ujyQh<5IvmN&)7kMl6=Dn7nBNaGiaIi4l z%PV3wmuq>7=>$*R8`U0AIjHrprjw@XsG6#;=3!bIaNzVdYGY6+bMj2R|Co0-^%+Nt zV*Ga^?at0@%04?^k@@|B{#q$*ZpE=x`!kE?uorh;*!z_K%nN@7`==w!7oM^fQoCj~ z<@GFAs?Xg-gX9#ZN*Wt>i0(O*;yAwT4FSrN?n27)q$b}+=$$QVsJF-SZxSiL$|J=* z#WDyyuem2v@Sa%zNFBb8)R=4iZKaav!$NF(2k*N*?63O6V;{amXWFs (O6oH?!Htcfs6CK~hmB0y9_s9_7)Y&UzOy|lX>%!c!$x6#eHbhy`y?)JcCEWE z8r<5 <~KGMzf~kqt(fgpmPzePmE&xIjIgGG0DZ5q csgpDWuqNL+3CM-;LE`km3}Bx^vp3d6j9bsP+Bq`s)5a1G3lbEti}Hob{W( zFUkeZS9u>I2v6ymVb%4QoNY8oK`{S6a~=wTtZd<=9FiY6#g0J5<_to?R(4QA8^#o; zp|Q89(vs)6Y6)Y(9uk~Fq$Z-~|KL{4KU>sM>E+;?|GYu@jyu-i1e$k4qD5gi*6q=4 z)LO>!57?T!ew^8rKaRiHcdw}uKYmBW!5z1 ~ zM8JSu;Vn$ksJ_qv%L&siSiyd>Ps(&d2M6@OV#=n#lak0;mWd)&4ubAuAy&f;l4rU^ z=Dq4^QlBOTOdn{jyJx7J xZh2xm3cZ;?d}%9rxg7J_11GO&+55wRg=_oQSp$@_Lk^#$CMOlTxNnm z@kQx(6A93DLho0GsYHGG34&$bi9(5Gx9jtnjGx4aXlm9Q&lLXgY6CvhIq?&u*;LG{ zH+?v4W3Hes^F3PlggoC_yq&+X{MlT6(c3J=-D<;!GqAfG6ud(Z-hVlEtL?FiLvbwJ zI_Qe@TzP3qh0|burJV(%tHK6*D43H89)v~ynJW!Wv5G(e1xy(~s#}W_@-^XMmH3{h zcB0jbXjStw3mdsPu!#ytWK!dkDQ$-WWH7-p?^evdhseWX_;9g14&U_p4$Bm%ysm{= zyTHeKU(33Hb5%%rXf+h3%8pXzrk~x9u5Y<36eL!X@maCT_wEk=x2%!e0fO<{1+U)x z8QiDQzMg3)K&K^=eit8pIeQwAAm}{*G+GsXKwCy~Y?x7->RvP2cdirS6pN>26bSQd z7l{ky@1A1p!w((*QaO*N*G9n9uz*TyZCb})^)5EY+>y4n+lU(PUt`(QRk1z1l{8|Y zUTS$olf?CA$p+aNl;zhR>?oJbJBhPPeqyBN(~YO-*&?gW(@*3iB{_PFkENKD8?@fU zHs%zdftqTsAG>z(s|W_Ld5&_f(C0FFdxvlF6h?Gj>^D>#8lzyGB6GqHUzN5wKz!?$ zI;jwWJ6Fqic$Z_fI@ziX l!tc72_{?g?oj{S=kXk xsHGbC8t=^eXp4yjS9mnlhRU#N^cc#Aff<-4M<@TlE zxoQrtJdi)YfC3FDUTe!2{Ee6Jn&}}|Suc$h==qT8`=9)_^S7lvGFf_>im9sSyExwK z)p~NR@{L%c*`VA!nb-Ty-IH|gv~p7p=X1A^iq)^aFW%pO6a6$IVd;8LyqOH3eVbV& z8>?_2%g}Dzfq_4u=cY;7^@izXALx$KYJ$RL-#?;~A4i`3aUd7U{c(NB^CyV&?DaTM zh+IbLCk8ds-)eraNRO9qFP-u7wvuYK1NV?v$~&)14eI6Q@0YyS@f2&FwrhN!xX2F+ z+OlqyzZHxGy>+OObmuDyL65mj%P%Qhm(3s5E0!^J{Ua57h!`y8DCLLffo7l&F}#RN zf7Dy_m<@@!ky3|KxCTp9vUNoD-dmL2WOVvqF6giB(YYPCcNJ7Nh>USKSqAhj4CM z!&i4%PSMZE9&3b8HSNI^z^{?K3A=`Cn@S%+($drY8P@1?M#h&13$5&Sn^4-L1h*wL zrSsSfro4*7qPqrHC*yFD4i$)wFl4rwAkKupeNR0uw!5Uu;Sd<*mB+3sZ>HtH2_JMn zFR|AxY-Y&8LrHoG)6w?PT}D*0*fOQO{$l6@nN;j|f$FLRyA9%yT5i0`ElvD`Mx0j- zNuky5G`Orv-9K0z% sLeuOgrcVbIu*m_1ihU{?^huoD$%jrP&fc_Yd> zQVF=iV@Rkk_+nsNsyl8-P|p2QszC1*-m5Z(!DWr0A#GkyHuhs9s?8*QciqTD80K5q z*WnIVL`8sDqEOrX8Ciu~Grc|J$%>DL^lhWbEfB=Y#sXhc1-b|HD7t(faU!!pJKggA z`?nql-8F&qp4OEws2PIVf~h*l?$tJ}%5>kAK0R2SMk z(0XT2I|6^MZ(A^%Ih+OgBb^#sezGM0Z6PG$yTw_3e0hln@dM1^@yN3NWBu#I3#`|e zgiN`!hr=8K&rCn4DUY(Bb-+e1iqs!Dket3NgJV=|>HR9CDE5gc7vtNXO)FKU+KWNM z=W4I^yv)pq7`$fm?$vEwL)N#GIA6i=nbo+&?8N&W)P8_D!Hb*QA$?N)%a!M i4EP89-1 zNt!2zwp|q!D9~>^jz6PCV`?D~F<=ha(?cspk6sF TxF7lBmCXp$%FX@4vNu9l;a{UEw#Ln1VknoX>n2tH^a z-CiLVN>?OuTeV#$_Oha8ZXCTzBL>Ky#h0A9Th+ayG$i&UPgO}l1fQOJXAljSys_~4 z<9N@2V+VF}UFJ#EX=%eq?{DR2@!Z{&+w6~fe~H|uiVr+Y3v5;SL8B^myuC~h31HA2 zx9eE-zy=g@I`F|+NmaN*?L-5v!SX$aZ_?o?6^h?uVk?x7x7ZKEDh5vvW?(K%)!T)# zj@~I7#eU26_@jB1;kr)!35s^dwRUCIGL;mD(W9i3SNFy=&NMQp_Zm_IMPDMCDK$;^ z)3(KIlk?wq yu5wn>L8x9bl z{QEna%-dUECI)iH$Fv6*8&~<-TvYQ*#VN1d!Z y> z%CX2)a$`*$vD?@$0XuP39@dLW#ZWCI>(#o{=E;at*W*enrP eTt^_FqaIDBw^2FkXYu_*B^kFIux~s0 }+M4QJ z6;J2Dco#j_s(=psj m%q!Jo zL}3nd-nnthJLTogpj)fj$J=-V-Ds}H@3hd{rFOF7Z(S?jwwHAx^5!_BEpDpr?OPd< z7l+l-b0wI8J^_(ZZxSq?6lOoU$xu80Xhx1S%>6@z=6+r7=}zM9zCs9zJ4~8!URVfW zI+folL|U>p(@luyt8ylJPGb1f+;i*fQ#yEv&&akHl(#)LslW~-G}9{I1)IS!gkTO9 zCdw|DV;Jb2dQ{)O5ALR_voiAJ-DtpzOnB?{7rPaPE1X Zur4J)u)8o<9{k`k=L!xG^RqRCazaQ?$DxifOi*N7s?9k#oo~~{q_g1kgn$WZC z>AvgDlSb0(+;C`C#}wJti4>+Kw(TG>VaO<) DMV*Y=447ciYD6Cn7F?i}yl9%w)CALa3Y;r&%s?TI8&j$JzdN-2ez)10 z3(Fc&()GQfKg *i`3nWt`5zjwutjHvhge#wYE<;Xs26Q54BbmD zM=AtvW^rIR3r@T6*cxV0vHmDhqL+c5w%rPZR5fzdX@t~P{Hu|Zuvy>%T5|ye3{<+M z+k>C`g!1fZl{Wv_bXM&7k{9bf6wX_B<4napy_k(p@%Eo8zZZB%6|TWYXO2IQ6fa|8 zlT$0b Csxr(7~eNS&FTr$$1v-i01EI_7rETK7wr;DmkI{eap%I?L(xg}&KFbD=q@ za_K2Ke(ACe2=IB?vyTa*Ez)x2XVSM^Ms(&Q u2y4n&!S2QRS3$DD3%&9ywG|FN- z*?BOCj0BnOtp{JI6_nZES`WX;r4esHvrD@ xd7@ZE_^?azL=l zx_(pfpD%dq9Oboa4x7NIBr@TML9wFT+I&<3mcqf~C9IE^dz-gg#&8MOAg~*A^WrBc zm%#ZL+0Hmnt&s6#s)8khO9T-fF)Cv#7~*8VB(s6NbclF8Rv#0#KG1k_ki#apQ~+2C z5Ok)l0RFJG#=%yvQK2pj%o9(tf<`SkAPwv?$z>6uEk?2VCnj @PbTPpAfgKTWcZ^yz6mctOWz3Cg6` z97 Ec9p1~O<(YI2V3La0MUTmpKP;@Olq&+kjIGudB@6#ycmOoap$tj&HE zizs>8|9M@3^%JB!;@bG;{6LER9E<^>9g(@vmOlf~0R?8-Y1$cA?c` v1v%RMC1FkZ&l>v2VLo<6hk6U5~qT#?(OVLm#7Z?oTdmcupBL)NP4u`5-RV)r*JUa4;LPZfL2Gdz`hHRH)9>V X^qGx%hvrecDeTd)qXzhD!jp6Sb0MQm-N|rY2 zc#hufWUbd(Yjyp~V3k_nczNc-w3i7Bm`Q_Bzm>f?oCYeIvT@gs9<4gMZTzBLM|n|e zm6`DpDwT)hI89--LV0`l%mA4X`T=^kruqAVPszqgAjXhnMex~a7O{?M9NNakppH*8 zuA{ A~gYN+34?3~gKcoutJOgEw 1PnPkB;{S(XiIpc8@Y~q`K2F z8DPO36zCj}aNJrq@T;!Hc|%R0KP?Jd`fd2Mu$GO()kjkmK%;bHC4c$yWEFAb9Hls) zgwZ96o*P1lVPf|uJl80&g$M#Qs(&qVb+)uyVCSknGrAyknW~WEv_05(j1Sh6Kgt=t z?gxZKpa93N-2PJ&F}r1allTPKN`P0cLpcOgphuGgzD3~PzIhSmf;%1Ybire~UUWW0 z;yV6eWo{(_+!;U$gE@0Gt1dWq8ub!)k 9-OvIyxIeC$Vw+^5gg}N`KPibpNLI}~)Us9Pv(F!&micNtO z?Wy<4+Y={{WtN{EPhIwO*)ck(`<#4!4z1D=_aj*BHnyf4|9FQ|cZMFlx>LnuV(qfu z7)3wg&@S>HNk!LQYYvjPaTV3QjC&p0C#=bNwP-G5xtlI-O1_%w_wCkDg5%wrbO|!D zSug nGTp z-|ocZ9(a`L61!6$=U+jDvCg%q_L)#1rW|Nd{;DIz)dAhon08BiB%^X|$*Wtk0JxN! z+uuT1{LjIju67$&BjIJoM)Gi%yAE?IvmCw$L#9lr8c8#WyJ!( Glir zxx7R$O7s&epFhYu{+_jdl4A+iMRgx*CLP&kSep9%-iOoX& vXb(p1U|iriP*hhL z%OI;%>G0D9q
DU{F)^& zV3yTfQr?$4Rp$u8N#8_@TiFJM4#B!EtCYly5oLBB0;5?8Dx`dYG#I`wQ$v}dKo7u; z4q1YCW#wg6_Op=;XZi}J{}7YE_C8G7m3X}#7nMi7Xp@O6j==ddFC&7T>(boKaQsD8 zCz2rjMTts b`kFhO)DP)(c%v3vKGeIOK z-04qA8E~@IzsRnm|2C`N{a+mH{NJDbH3&L7>N;Gy`1J=Zj9 zcD|E*vA@@VzonD-k`zprPD}F83Hc1a=*a~4^E!OrD9R<;Ijpam^F=ue#Lli4xoBTM z6y=ngFVV&5>rb~!OK9FB=5ae=xxv!2b6)+{Q}S5W+;~Ax2gM=%Zq6KI+`)^act^;6 zw0TB+Z_Wl*aE3gLaU4$%$>=(JeyGY$DxH9MfaYH(9SU{~h;lG#J-ct?zR8)G=EZ$i zXxMH(CD!ZN$Mz!Me}dLmhHeV&6fsgbNvcu#niLoFZeNvqALf|a{SbXC`>4Tsaf?>f z)#}r-AzXNYZ%D~I@^AW7qMTJq94+Nm&C3Hk>p~>;1~F;7s5Q|0gkUNsn%(p9^$pCz zie)`=jZ6(1&~RLGnLr%|H@(G0?%cPxZhY=4S*sC1976yug+GL{a(t{XNYjjwi&_1x z&h2}z+CFU6cO;#llRY@{sqK46N0dwICJ92|G|{)C3xlb11r{sCB@qZiG9Uho8BWFX z%`pfpb;w?Y>g;|YWsg9HFDW&g=mss_WsGUp3jnVNUK**m^(pQnCJSU_c{R)RKuWb% zfdOsWU++(P?c(h8q2bd!7}{ ;mY&kW^H1XdV#S0Xg< Q+$b#l@y2cZCWv6)%7Ymo7U}7BQYq zLhA!xmf$4S-DitwGwu_V2A1MlKQ%h^ 78!RT3nQKWX`=sY82bI`5CU~oH zdppH2CiMIEyFs%!NOIBJ3sx}szC$4@f6EA<99wG@e4XTxZkqU-v268JBd!?cSVd8t zaxo< 0gg*V`(@ystIcc?kr;6Rs3@w%p|c5<#-GC{BA0*Vnp5ZZrN|Id@LnAym;yf5V7C*Y-?P zd#(7vNV|RR)WX`G%%yiQNs{eMy16}kCgX (@Ej7der-q|&9t(_*X!^TouSk5Y|8x7NVA+Wqjd7gq1M?XC6LzM!Q)RS7R|InX`}wZ*zMeMCtQ;q&&1ay4+@$ zgIze$a%ddGr&44LvC&_Vh bnK{XR>K7ywCw>r8C-3<~oI62xs-Js3x< zCyvn}Ss`2>8V=PrFmVOY$j&lN2m}sy+JUc)!+CD3;@>*t*$J7&*b&Z+kSx4JoHM_a zze-c~CoBh>@w=R$57=6=3EoXKU<*cbZLL@EH8&m394x )ge&N<5x5o&0P($F$q H*_A|C zwmN0b9)Z~NQL8OUXwmFPFt^O0!8>b9I~P4574it|ZY&uU6d*K4G}ES-MM#@0sKKX+ z!|$;VedA mZ8 lwDyXiuW;b-e)i24pvVbC)mvs%T53H$w z hM#kI(rJ*t!qm^19b=%>S$?QzeDrOM-#7Usmtfy0?2)do}f_K=}%5?Te=dU$tz zt*H#@wq8aDW6_Qt4-obiNWl)nq_q=2i1rPSj{0!#dop-Uj$`lP8CKiJE@Si4w_$d`lHgLKhLWOEYl9Uy#rh?gKxYYFIlfOzt_TL1oM9!$CPzkOcitE& zoi3pQZ9H5cSWJ {uX_lNM)SE(y&aQb1)ara!j1I1ST{Kq{nLuQKm|D~11&o=1l-;O&Syq_*(rG)|e zsJY%^Wl&D?b`_7rynCxFL>1J9fFdTB`!tAAuQd;Vgz(#S6YBun0W!*W8_vcDwJSKa zn`U}+b6nl$UE*KcAF-+CQzH1*XZMu)_CTIcS~g2iS*FEzvw~i`jXl}ypi%yi9(i`T zo@uYAwiMwZ0V}L>@zZJkbMlW4?Z;AbgzAB@7dsUrA7rdY%nbioUZO;wI|sF@v~?8A zHW#@mm2xdV{gqMa>;2AG21->u*S*=@?w-E*WBcc^^~uA <4cXWv zJ_%MV(k~aeaVUW7CsNE9f?zWEg85d~7oo-0aWoX2^(L~COK2}|LhN&ZF1N3>ACn3H zpv3vXxUF>?D#=Fun+3f2kIS>=f6Nmx&}6R_aywy_GZJqhpiybsxFibpBwYOe%nfhU z90scR{b)&KNvCRmdtvQc;(YB+c8#f^LXC1M0I{Sec;Z&)-*dIwjl~SJ-ky{X`2cU_ zKs7kg;l>(_>uUE^yaH{2!S*nqHzqD9GWmrzMk%rLk?u2?vZc1GXHiZ*$>8hL`RRf7 z`>I85Q`yi6v=ukrM$>fk3shmg^X5ft$9 ?PWWB 7%ebOql)MOeP@8KRt{g7v+% z97FCV6fW48p^up87@v53WNY{vSMVRzna-h|aO<462dxP!D-ne}Xa@rUeSy+zKYEL- z(IZi^X2%Mr!%w+DWU9kYn{I)b*E0;jeh-o7OH__lF$Xy2;4Rn`nEnp>3VwY&$Xg<8 zn|>VW9vc6B8SFPKvAQxi-+}2tA0vbhVXyvyR6a$*8V~2Y)rCN*g{k}k!4j>~yg@(1 zq+m)kC(oLksV?N?T^?; +t`F2 tqeb_IMDo=cwPXfrI^a>_>Le%(>#20W%vX4 zEr}x>$ao0!f_z*DT;**J&LZbrE5|IEWQS=qPR8WFMX+?X>0G2;+0~Qa+!f7f2D9Rt zHQ;wd@x-SU<6iXO^01V)6V$bW>sBmQd01rt+Qjr~!%VGAmu5z+hU&! 7SHyBFI^m)(EZLqjzEi0| zztlj^ue!6mw+hMNyerDoW>cYNC9L}o_OopY(nnT#mwYGc-?2}s+WVtTn&s^ni2e-& zSCq)#c{(PVDI3hX8`{;LPY8QusOhIiP*vA6n`-w(nb?}o^15i2)adIYb%$8_6N-TRQwTQeIzV)&`UP`(4S_WU~;Y1q>~s9ShIK^szKJNSji zqD8_=7$>KAbMj{zhr2^l&c&Voy8yB65j)%>%5H_&)D|a`-F$moTy^LIuoykS9FqX* zvN~ti%*02l7+R45 Ryw(L>@Mt`*6f3BvXH%*}h1% z_gd$tcwx5%^hR-F0%VcM*RTtuVP84z%4M_U^ogfj6X+5!Oa#+&>% J2GdVi~N9c7)nP zWH6mWsuc~-sNz5O{v7|$l?kPJRYi0L0%b{Y4cFUE{%KVZ*{S6JOtk$C>7*c(@}KWc z2~1J3b@bk(0{KqDEBXTUxROo-;2X}W1(UFS@Q$Rd_kl~0{^k(K6F>}?U=WB5L3}_r zCpYoI{H219s5>CHU9=~o-uEJHupwWaE`<8|qg?cPS96K#)@nMv!S2Ge@EhZ|`7GC9 zJzizlx18Dph}_4&wmU!?hHMVsXK8bHsysI#Dd*(u9ki)d#!8rC8e7~3w!X>I%B I5K;=trS$SGY^ zi
dKhLp@>7{-vW(OX%k&q&c^5D_lRCDLwx~Bl{#iA zsh)V8D$U~M`R@H_QI2IoefrKp+5`0mGoM`cJ}3Cw4-0RPdjESb+3S4yPta6*IXi)h zo_=m*HYWFf@Im*$>EQ)%-OCCc$7IRJ{0NmWy;
EEJy%e@9yd8}jD3rD^JZr5M0&5Yl_SEmE $mgw&`WUFO8za zn=KPH0VsI%N8fZ_?|c{16lj|2uDJ;Bh0}?Q>R@xJ?J}dfsxav^EkTz9(KPkJP#uH4 zO|1l?NP(!l6@0agajDI+<4a3X+3*>1`YVqKYVm)VG8?bGeT0b@|HzeWwWDuqu{FHP(7ih?=zUi)+Rth$Bh9V6W}O{`TrlyA}efu zrd*aTsO$t?w-|zMJ|X;vtT~mpDcJ>_jLoSX0Bm&gaE=UUc)^e)n2L=te@tm(>M8XN z=Fkgnz#43c;7n2XOKPnyj|D(P9DDf(ntcd_tp|(iLlE|K`UC}sp<664=At-0rDT7V zG;X;3%&X5rmz40V^>Xq fL _t#O62hU_qIXUlKgukWUxuf@LDI(rS ze2CnyHK~Q>5EMz{p)W%0aL+K%Oh!|pyP0cSmFG|zCdEJy;<7|hZMC-?jT7e_E$Y@m z^{U!b7;FuF>Fcly@;MBtvBiL~^=J(G$j&zB#HinX4>4u`1f6WkoZ{gUfE`?2osj}x zjpJ4q4hx@Ob(jxyAuY)#Hc6>%In8@;^eB1ePFe8?l%Q$`I*afYzl=V?HNE20^8k*9 zB(k@eLzW8- neX%P(H=0C$rCN<8|;$@ZF5KVXpkp$Zye5vpQ zzY1Ba|Hu_G(narF+pT3S=funjgfj(u<^+#~1iHFdRZJtTUqIwT>NK`svmesxMAw>< zlna0|4mg@N1bRjctlfT9G~Y1 wC98fvOi z(!VGW7{t|DZk&B#THO`_Zt`bB^>Si~xMz{2m85646xM{txsfv#vrWXT7r15)pyNXd z{`-K1Ynrl8>p}9**g+BOT$CwWp~7CEiKCgql1zt$e0DL7BrB`ehr7!rbmwcw9d^+& zMf{d_y-rihUw~pOde@}y^cpnHtBQ5QM(5C}HrOWR(jVPnjzQ-6 d Gze%!{}FsIq ;1g5K?U6@2sat@t17!l2M^uW1f{{`)y({>s|vhg)=wJ|HZD<9d(6*P#3A z&U6V%Mn9;pPg+_6J{#Pm3-|CW<{$nIzKkzT=chFhu1qdQ%dZ-hF$%mm6AiBzS9KRR zXO~Ztw`BD|t()j!!x^o2L-(uPN*L XfRkp({p{GhTnTLjQjfOQ zT0B^hCJj$fNja*<9;}U>KEs|ZDn3xC87S6fSJ%4f&q$I`KUO@}cvrsRUDi0xSDs)g zSlu$9gA#0`P8ka@@pf 1JS^8t14^TQXEf%}*%o=bOINljI-c8B MukzHv=i=_Q) g }uZ*kd^ag`OWE>jJN7AonxcU9>@VN1AJAN9iS zJv1h`jHvshc$t#_T3^{yH0+TmEYaLYBG=(=Y)+>2H84x6ST615LFKswlw445Vr@dS z!vH8P$17FL AlcU=ct_$p;*0fF|yd3a3yohg%V6Q`d2_7Ke6c}WJ=s5vq@^eVVc&3OLn-p_Yy ze|hyIV*>Z+;H*)je?rfi2v@TmgCAMaH6r1yQ%_q&{?!?PTbUFVnEK2Ht*%cAnh0e% z70GHRGt4v7q@mhg2fcXhL+-7*Ba(eewCw%;gJ-mSLFN<6UDuLT*`pxD897*Vt3>?L zsi+wFXFaRoFq`2gA)mC!{-w3!Pnj^}bxp`aKVpB}?x}p@HQhY$P3wl44ahd=wYAP% zT)_I&_|m=|@n89`tttEK uh{xVzWG CK{I0keGK5g%`T-m8@cwoR2p#(fuDrsiv5HJ=Pj#kBG zu`ed 3WRo88Ums z$SH-|c_sYbXH4t`zENZNKYk8JQQPtR8wlXt*KGn^GV?oq;%kPV&~X~~`hvCpdA!=e zMJ0o;p_5k10uH^;{VuX;6_*D-e){^isxXPax#w`#5_XHnL>FF?!m%t6$D6Fr=QgqC z=y#Sv55CiXo!1c$n`t*KFqlkLY;dBz0Gt7JSr|((>zJ_NfH?lwZnachHLiK1x3DA# zs=bq|SRdir_qaEuhj|tuK^yrH7q8glTV=YB %o}$y{n6g{vLFC;_%4aU}o8PZ?oK84%e&(6G!jqe3I~!zi58a>bZPm(Qi_|v% z%?4LJ&xXiC?8#4x+;jif8OimTZKC#cSXftA<#{5EIr2^!rx; K>9FPNzA)wc1BYbEsKd#&PfqP=5HdiEyaa%tiW$z}u~T{M z)_f)8hn!kX4==bQeD=jY74(f{T(o|?c97N%Q;&~W0lc5I9ySC|*C2;70)I^x_-&_A zwd~JS6Fb7w+ff)^Bt5)zwmk(Q{6`L{< es}o|nmru|`Ld1;S3^8E}731Q$ELftP0`0~&e*PEMEztx& z52OHQ3FCQ>T=^=GQTMvIO`s4efkBNai$|||kOf{5>!chx c-Ht2CZ`eJV@W z*HhF*Ni8dnVlWw3Bic-FT+n@u+mGF=^1EDP)7+XWk74&dS9USA+3PI&gjM>XukaZs zr=B6d@QcTrQ-0j;j(eV_b#{~J{$+QOv(nxl*8&}l5EcQmj{foKe+yMN>-4#wh1;Be zAJiFriTUlQG;b-CMA(C^(S=s*gy_TJp36l_GT{N{;FiAqDFKMh<+Y{P5iPwA(?g!r zK8fqiiBQ6UK+hcZ9~q1j&VLWdvM{Mjz|pOiNg4~+u>ekP#j?+U(pmgS)|wJPdq^p& zk#XDnhgoxzS&m^bry$8jSjj}Mm=<;O0=wsY>{H#DS2y^*9nQ)I+xAm@zSW)e-dh7< z9o4BCcHY~hao$z2Bd0lCX^9YR`FJvLVr^a=F$wzf^M0XuNs#5^a#!VraM~a6eL>T9 zZd*Qzlq14-kSNE3)iKohN ~RlLwI?31z$B!BS6&oRrGvO)U#MSRtpSIe-V^YR9$H) zlVEwDKvCRC!wf+NjZh)C!13`$Tr}fM6IBfGpYETl3coqYUkaCE9kL|tRGFxxo9Y(s zP+N=>>bG%B?&f0Pz}QsnJ$42s7PobVc$uQ2)M>msSAuM!s$q|1@&-c0;z=&5(yn@5 zpE)57IE@_VY$ZGBi`&ZyLA$6k;`=Ppb!V>Sl%bEPw{u{Fp8z@`w8#C@ZR^6(@Cp4z zTq}$(`m3DyEdR(`>*I(nT~w<8^rzNYgkH?>7TxcN)8n*yKQj+ )m}Pq7?sa!(j@}A$$l l@~(p7PjfYf0lL4L{R{i^*~`e67asKj;#>HID_~Vmw{xo8qTIV3 z?u`1^ntBe#zTuhR7UO&R@*Z8<1J-d(Dk@{ Tb^ohYqQ#ExNLo)l$pD7D5Q0i<78{I-Uj&t#g0W}7 zvCU#neZFS6uwO1d46C#kt>lJUwl3!7z%u2)dY(j2pQNb))0QIrt?7dLP8`D0V9Y*( z!-Mh1lru*7O)AE3{0zV`zM;q`aJbgc{qnRhl7_+Ytn2)R=4gW?72_xF@itiwW#9 zd197A2#=Jnv