From 9861369b7f01f3551c7f508e86a565afc06ec428 Mon Sep 17 00:00:00 2001 From: Mariusz Krzaczkowski Date: Fri, 13 Mar 2020 10:58:20 +0100 Subject: [PATCH] Add html5_notifier plugin --- plugins/html5_notifier/.gitignore | 1 + plugins/html5_notifier/LICENSE | 16 ++ plugins/html5_notifier/README.md | 8 + plugins/html5_notifier/changelog.txt | 14 ++ plugins/html5_notifier/composer.json | 31 ++++ plugins/html5_notifier/html5_notifier.js | 145 +++++++++++++++++ plugins/html5_notifier/html5_notifier.php | 146 ++++++++++++++++++ plugins/html5_notifier/images/new_mail.png | Bin 0 -> 1590 bytes plugins/html5_notifier/localization/cs_CZ.inc | 26 ++++ plugins/html5_notifier/localization/de_DE.inc | 26 ++++ plugins/html5_notifier/localization/en_US.inc | 26 ++++ plugins/html5_notifier/localization/es_ES.inc | 23 +++ plugins/html5_notifier/localization/fr_FR.inc | 22 +++ plugins/html5_notifier/localization/it_IT.inc | 21 +++ plugins/html5_notifier/localization/ja_JP.inc | 22 +++ plugins/html5_notifier/localization/nl_NL.inc | 22 +++ plugins/html5_notifier/localization/pl_PL.inc | 25 +++ plugins/html5_notifier/localization/pt_BR.inc | 26 ++++ plugins/html5_notifier/localization/ru_RU.inc | 26 ++++ plugins/html5_notifier/localization/sk_SK.inc | 22 +++ plugins/html5_notifier/localization/zh_CN.inc | 26 ++++ plugins/html5_notifier/screenshot.png | Bin 0 -> 9155 bytes version.php | 2 +- 23 files changed, 675 insertions(+), 1 deletion(-) create mode 100644 plugins/html5_notifier/.gitignore create mode 100644 plugins/html5_notifier/LICENSE create mode 100644 plugins/html5_notifier/README.md create mode 100644 plugins/html5_notifier/changelog.txt create mode 100644 plugins/html5_notifier/composer.json create mode 100644 plugins/html5_notifier/html5_notifier.js create mode 100644 plugins/html5_notifier/html5_notifier.php create mode 100644 plugins/html5_notifier/images/new_mail.png create mode 100644 plugins/html5_notifier/localization/cs_CZ.inc create mode 100644 plugins/html5_notifier/localization/de_DE.inc create mode 100644 plugins/html5_notifier/localization/en_US.inc create mode 100644 plugins/html5_notifier/localization/es_ES.inc create mode 100644 plugins/html5_notifier/localization/fr_FR.inc create mode 100644 plugins/html5_notifier/localization/it_IT.inc create mode 100644 plugins/html5_notifier/localization/ja_JP.inc create mode 100644 plugins/html5_notifier/localization/nl_NL.inc create mode 100644 plugins/html5_notifier/localization/pl_PL.inc create mode 100644 plugins/html5_notifier/localization/pt_BR.inc create mode 100644 plugins/html5_notifier/localization/ru_RU.inc create mode 100644 plugins/html5_notifier/localization/sk_SK.inc create mode 100644 plugins/html5_notifier/localization/zh_CN.inc create mode 100644 plugins/html5_notifier/screenshot.png diff --git a/plugins/html5_notifier/.gitignore b/plugins/html5_notifier/.gitignore new file mode 100644 index 00000000..13d7c5db --- /dev/null +++ b/plugins/html5_notifier/.gitignore @@ -0,0 +1 @@ +/config/config.inc.php diff --git a/plugins/html5_notifier/LICENSE b/plugins/html5_notifier/LICENSE new file mode 100644 index 00000000..e13283af --- /dev/null +++ b/plugins/html5_notifier/LICENSE @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2013 Tilman Stremlau + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + diff --git a/plugins/html5_notifier/README.md b/plugins/html5_notifier/README.md new file mode 100644 index 00000000..5dd9000b --- /dev/null +++ b/plugins/html5_notifier/README.md @@ -0,0 +1,8 @@ +![ScreenShot](/screenshot.png) + +HTML5_Notifier is a Roundcube plugin. + +It displays Desktop Notifications like the ones you might know from Google Mail. Just keep Roundcube opened in a (minimized) tab and enjoy getting notifications every time a new mail arrives. + +It just works in modern browsers like Google Chrome, SRWare Iron or Firefox, because "Desktop Notification" is a new feature in HTML5. + diff --git a/plugins/html5_notifier/changelog.txt b/plugins/html5_notifier/changelog.txt new file mode 100644 index 00000000..fdd85f3d --- /dev/null +++ b/plugins/html5_notifier/changelog.txt @@ -0,0 +1,14 @@ +0.2 +- added Listbox to select showing duration +- added color to browser-conf-button + +0.3 +- updated to work with roundcube 0.8 and 0.9 +- added display of mailbox + +0.4 +- updated to work with Firefox and the new Notification API +- fixed UTF-8 issues + +0.5 (thanks to nicolas-joubert!) +- add ability to exclude some directories diff --git a/plugins/html5_notifier/composer.json b/plugins/html5_notifier/composer.json new file mode 100644 index 00000000..a2df0f2a --- /dev/null +++ b/plugins/html5_notifier/composer.json @@ -0,0 +1,31 @@ +{ + "name": "kitist/html5_notifier", + "type": "roundcube-plugin", + "description": "Desktop Notifications for Roundcube", + "keywords": ["notification","desktop","mail"], + "homepage": "https://github.com/kitist/html5_notifier", + "license": "GPL-3.0+", + "authors": [ + { + "name": "Tilman Stremlau", + "email": "tilman@stremlau.net", + "homepage": "http://stremlau.net", + "role": "Developer" + } + ], + "repositories": [ + { + "type": "composer", + "url": "http://plugins.roundcube.net" + } + ], + "require": { + "php": ">=5.3.0", + "roundcube/plugin-installer": ">=0.1.3" + }, + "extra": { + "roundcube": { + "min-version": "0.8.0" + } + } +} diff --git a/plugins/html5_notifier/html5_notifier.js b/plugins/html5_notifier/html5_notifier.js new file mode 100644 index 00000000..5a5f8131 --- /dev/null +++ b/plugins/html5_notifier/html5_notifier.js @@ -0,0 +1,145 @@ +/** + * html5_notifier + * Shows a desktop notification every time a new (recent) mail comes in + * + * @version 0.5.0 - 19.12.2013 + * @author Tilman Stremlau + * @website stremlau.net/html5_notifier + * @licence GNU GPL + * + **/ + +function rcmail_show_notification(message) +{ + if (use_notifications) + { + if ("Notification" in window) { + var notification = new Notification(rcmail.gettext('notification_title', 'html5_notifier').replace('[from]', message.from), { + icon: './plugins/html5_notifier/images/new_mail.png', + body: message.subject + }); + notification.onclick = function() { + if(message.opentype == '1') { + rcmail.open_window('?_task=mail&_action=show&_uid='+message.uid); + } else { + window.open('?_task=mail&_action=show&_extwin=1&_uid='+message.uid); + } + } + if (parseInt(message.duration) > 0) + { + setTimeout(function(){ notification.close(); }, (parseInt(message.duration)*1000)); + } + } + } +} + +function rcmail_browser_notifications() +{ + if ("Notification" in window && Notification.permission) { + if (Notification.permission === "granted") { + rcmail.display_message(rcmail.gettext('ok_notifications', 'html5_notifier'), 'notice'); + } + else { + Notification.requestPermission(rcmail_check_notifications); + } + } + else if (window.webkitNotifications) { + if (window.webkitNotifications.checkPermission() == 0) + { + rcmail.display_message(rcmail.gettext('ok_notifications', 'html5_notifier'), 'notice'); + } + else + { + window.webkitNotifications.requestPermission(rcmail_check_notifications); + } + } + else + { + rcmail.display_message(rcmail.gettext('no_notifications', 'html5_notifier'), 'error'); + } +} + +function rcmail_browser_notifications_test() { + if (use_notifications) + { + rcmail.display_message(rcmail.gettext('check_ok', 'html5_notifier'), 'notice'); + + var message = new Object(); + message.duration = 8; + message.uid = 0; + message.subject = 'It Works!'; + message.from = 'TESTMAN'; + message.opentype = $('select[name=_html5_notifier_popuptype]').val(); + rcmail_show_notification(message); + } + else + { + if ("Notification" in window && Notification.permission) { + if (Notification.permission == 'denied') { + rcmail.display_message(rcmail.gettext('check_fail_blocked', 'html5_notifier'), 'error'); + return false; + } + } + else if (window.webkitNotifications) + { + if (window.webkitNotifications.checkPermission() == 2) + { + rcmail.display_message(rcmail.gettext('check_fail_blocked', 'html5_notifier'), 'error'); + return false; + } + } + rcmail.display_message(rcmail.gettext('check_fail', 'html5_notifier'), 'error'); + } +} + +function rcmail_browser_notifications_colorate() { + if ("Notification" in window && Notification.permission) { + var broco = $('#rcmfd_html5_notifier_browser_conf'); + if (broco) + { + switch (Notification.permission) + { + case 'granted': broco.css('color', 'green'); break; + case 'default': broco.css('color', 'orange'); break; + case 'denied': broco.css('color', 'red'); break; + } + } + } + else if (window.webkitNotifications) + { + var broco = $('#rcmfd_html5_notifier_browser_conf'); + if (broco) + { + switch (window.webkitNotifications.checkPermission()) + { + case 0: broco.css('color', 'green'); break; + case 1: broco.css('color', 'orange'); break; + case 2: broco.css('color', 'red'); break; + } + } + } +} + +var use_notifications = false; + +var rcmail_check_notifications = function(e) +{ + if ("Notification" in window && Notification.permission) { + if (Notification.permission === "granted") { + use_notifications = true; + } + } + else if (window.webkitNotifications) + { + if (window.webkitNotifications.checkPermission() == 0) { + use_notifications = true; + } + } + rcmail_browser_notifications_colorate(); +} + +if (window.rcmail) +{ + rcmail.addEventListener('plugin.showNotification', rcmail_show_notification); + rcmail.addEventListener('init', rcmail_check_notifications); +} diff --git a/plugins/html5_notifier/html5_notifier.php b/plugins/html5_notifier/html5_notifier.php new file mode 100644 index 00000000..7a53bb55 --- /dev/null +++ b/plugins/html5_notifier/html5_notifier.php @@ -0,0 +1,146 @@ + + * @website stremlau.net/html5_notifier + * @licence GNU GPL + * + **/ + +class html5_notifier extends rcube_plugin +{ + public $task = '?(?!login|logout).*'; + + function init() + { + $RCMAIL = rcmail::get_instance(); + + if(file_exists("./plugins/html5_notifier/config/config.inc.php")) + { + $this->load_config('config/config.inc.php'); + } + + $this->add_hook('preferences_list', array($this, 'prefs_list')); + $this->add_hook('preferences_save', array($this, 'prefs_save')); + + if ($RCMAIL->config->get('html5_notifier_duration').'' != '0') + { + $this->add_hook('new_messages', array($this, 'show_notification')); + } + + $this->include_script("html5_notifier.js"); + + if ($RCMAIL->action != 'check-recent') + { + $this->add_texts('localization', array('notification_title', 'ok_notifications', 'no_notifications', 'check_ok', 'check_fail', 'check_fail_blocked')); //PR�ZESIEREN + } + } + + function show_notification($args) + { + $RCMAIL = rcmail::get_instance(); + + //$search = $RCMAIL->config->get('html5_notifier_only_new', false) ?'NEW' : 'RECENT'; + $deleted = $RCMAIL->config->get('skip_deleted') ? 'UNDELETED ' : ''; + $search = $deleted . 'UNSEEN UID ' . $args['diff']['new']; + + $RCMAIL->storage->set_folder($args['mailbox']); + $RCMAIL->storage->search($args['mailbox'], $search, null); + $msgs = (array) $RCMAIL->storage->list_messages($args['mailbox']); + $excluded_directories = preg_split("/(,|;| )+/", $RCMAIL->config->get('html5_notifier_excluded_directories')); + + foreach ($msgs as $msg) { + $from = $msg->get('from'); + $mbox = ''; + switch ($RCMAIL->config->get('html5_notifier_smbox')) { + case 1: $mbox = array_pop(explode('.', str_replace('INBOX.', '', $args['mailbox']))); break; + case 2: $mbox = str_replace('.', '/', str_replace('INBOX.', '', $args['mailbox'])); break; + } + $subject = ((!empty($mbox)) ? rcube_charset::convert($mbox, 'UTF7-IMAP') . ': ' : '') . $msg->get('subject'); + + if(strtolower($_SESSION['username']) == strtolower($RCMAIL->user->data['username']) && !in_array($args['mailbox'], $excluded_directories)) + { + $RCMAIL->output->command("plugin.showNotification", array( + 'duration' => $RCMAIL->config->get('html5_notifier_duration'), + 'opentype' => $RCMAIL->config->get('html5_notifier_popuptype'), + 'subject' => $subject, + 'from' => $from, + 'uid' => $msg->uid.'&_mbox='.$args['mailbox'], + )); + } + } + $RCMAIL->storage->search($args['mailbox'], "ALL", null); + } + + function prefs_list($args) + { + if($args['section'] == 'mailbox') + { + $RCMAIL = rcmail::get_instance(); + + $field_id = 'rcmfd_html5_notifier'; + + $select_duration = new html_select(array('name' => '_html5_notifier_duration', 'id' => $field_id)); + $select_duration->add($this->gettext('off'), '0'); + $times = array('3', '5', '8', '10', '12', '15', '20', '25', '30'); + foreach ($times as $time) + $select_duration->add($time.' '.$this->gettext('seconds'), $time); + $select_duration->add($this->gettext('durable'), '-1'); + + $select_smbox = new html_select(array('name' => '_html5_notifier_smbox', 'id' => $field_id)); + $select_smbox->add($this->gettext('no_mailbox'), '0'); + $select_smbox->add($this->gettext('short_mailbox'), '1'); + $select_smbox->add($this->gettext('full_mailbox'), '2'); + + $content = $select_duration->show($RCMAIL->config->get('html5_notifier_duration').''); + $content .= $select_smbox->show($RCMAIL->config->get('html5_notifier_smbox').''); + $content .= html::a(array('href' => '#', 'id' => 'rcmfd_html5_notifier_browser_conf', 'onclick' => 'rcmail_browser_notifications(); return false;'), $this->gettext('conf_browser')).' '; + $content .= html::a(array('href' => '#', 'onclick' => 'rcmail_browser_notifications_test(); return false;'), $this->gettext('test_browser')); + $args['blocks']['new_message']['options']['html5_notifier'] = array( + 'title' => html::label($field_id, rcube::Q($this->gettext('shownotifies'))), + 'content' => $content, + ); + + $check_only_new = new html_checkbox(array('name' => '_html5_notifier_only_new', 'id' => $field_id . '_only_new', 'value' => 1)); + $content = $check_only_new->show($RCMAIL->config->get('html5_notifier_only_new', false)); + $args['blocks']['new_message']['options']['html5_notifier_only_new'] = array( + 'title' => html::label($field_id, rcube::Q($this->gettext('onlynew'))), + 'content' => $content, + ); + + $input_excluded = new html_inputfield(array('name' => '_html5_notifier_excluded_directories', 'id' => $field_id . '_excluded')); + $args['blocks']['new_message']['options']['html5_notifier_excluded_directories'] = array( + 'title' => html::label($field_id, rcube::Q($this->gettext('excluded_directories'))), + 'content' => $input_excluded->show($RCMAIL->config->get('html5_notifier_excluded_directories').''), + ); + + $select_type = new html_select(array('name' => '_html5_notifier_popuptype', 'id' => $field_id . '_popuptype')); + $select_type->add($this->gettext('new_tab'), '0'); + $select_type->add($this->gettext('new_window'), '1'); + $args['blocks']['new_message']['options']['html5_notifier_popuptype'] = array( + 'title' => html::label($field_id, rcube::Q($this->gettext('notifier_popuptype'))), + 'content' => $select_type->show($RCMAIL->config->get('html5_notifier_popuptype').'') + ); + + $RCMAIL->output->add_script("$(document).ready(function(){ rcmail_browser_notifications_colorate(); });"); + } + return $args; + } + + function prefs_save($args) + { + if($args['section'] == 'mailbox') + { + $args['prefs']['html5_notifier_only_new'] = !empty($_POST['_html5_notifier_only_new']); + $args['prefs']['html5_notifier_duration'] = rcube_utils::get_input_value('_html5_notifier_duration', rcube_utils::INPUT_POST); + $args['prefs']['html5_notifier_smbox'] = rcube_utils::get_input_value('_html5_notifier_smbox', rcube_utils::INPUT_POST); + $args['prefs']['html5_notifier_excluded_directories'] = rcube_utils::get_input_value('_html5_notifier_excluded_directories', rcube_utils::INPUT_POST); + $args['prefs']['html5_notifier_popuptype'] = rcube_utils::get_input_value('_html5_notifier_popuptype', rcube_utils::INPUT_POST); + return $args; + } + } +} +?> diff --git a/plugins/html5_notifier/images/new_mail.png b/plugins/html5_notifier/images/new_mail.png new file mode 100644 index 0000000000000000000000000000000000000000..e31f2a496ccc90dfbed708a453816440b041750b GIT binary patch literal 1590 zcmV-62Fdw}P)Vl&|00001b5ch_0Itp) z=>Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2ipi4 z4_CX>@2HM@dakWG-a~000Gz zNklA6O8HS&8?wvb#JejdQiJiE1n>Kb*H%d`jTms=o&`ngTq7YPeP_Y7a z11n&Ggn$(hNQe!vssyVdDpg2`6a)pSNlDUxX=?@BF>=F=J;e zem059Chz9nb9C={zwiCtbIu3%aUb{bK0pD?{`~9LmD2AUsdK`~FP?wQ2WVH`n?Cua zAK}D;bA6$yd%?74J*7QnU-}vVK7b@Kt5;rS>daTV--{q7=?0?|YG)0_op;mpW7DYj z+&h*uMof1sY1Ad?5my(laN+z5ctO}J_D<&3aOmV2jLxxk_1CPv`75SB`wg;%{fG&$ zNvEpa%T7oHc#qVsK&*n^Ky5isJY*nRi|%s@-Dl|b(a$q<_;GX)VxngEXXkcv zRZ5{eA3xlOQi{#xS<2Ub!pMQE3?KdsBafdW$PFSkK`V_Dvp4e{MQs|PGg%(`$miHz zTck4gXUc#7J+*)SmGLK^As8IRCS4yWv7kM?Oo$hRB=vP}Ui%FuS)epHN6rbw6X!^i zI&s<{lN+U<&j+x5QmUR+(@ukM@E{YXpQk#1iN^9QmtXlYW78kv;U_*tkRQUv9lUG~ z)7fV6;%k%_-r(TK2E&6*@;9zwgDIl$k5HMzd+aO>90b5xdxzW@+1-a?QpB3ByR88y zWD5rfizCEqb8Oytiy*C2I(C|UW0N%3me`!XNd5LTw7y9xvqD(iMBPmB^a4`-A+9ur zD!hop;hbQs^pV@HgH6)G#%;tTfFo(ui8fb>8x^#l<%_G( zX{SNqfuj^3Jb_XQor^j2mr*W7Y7S{-(9WTKL$I<) zy8V63pd8?TE1hVuCl(GPBmt4}AxL&5cxr$3;N>fjf z9Xg1Zgt$>bOx(AtZ7&GG2_%;gw}#jpR1(}qfU1ISYsh=ZVNfO}DE|d{^eC-%&{uPD zB#jDbbE7Nq*fglF2NVuJJAexF|KUzlIG12!@GGk*B1Cz(SmV_U-b##2022Y22+y;0 z{E=Ocb&pqpwfU5~lsiGT@JTWQ7jU@_QuNUU1N$}d zu)^&VR5n9W8fB{r)~Y?3|Ks(SzJn9HyWsz90+b?3EKzC+Gag~!ku40AsF1LpH+1qPef99(sYR7PQi$5Jl)qw-+h#zp>A0qbK)ClmLW1tq?MBQg diff --git a/plugins/html5_notifier/localization/de_DE.inc b/plugins/html5_notifier/localization/de_DE.inc new file mode 100644 index 00000000..d07d4064 --- /dev/null +++ b/plugins/html5_notifier/localization/de_DE.inc @@ -0,0 +1,26 @@ + diff --git a/plugins/html5_notifier/localization/en_US.inc b/plugins/html5_notifier/localization/en_US.inc new file mode 100644 index 00000000..ce714e3e --- /dev/null +++ b/plugins/html5_notifier/localization/en_US.inc @@ -0,0 +1,26 @@ + diff --git a/plugins/html5_notifier/localization/es_ES.inc b/plugins/html5_notifier/localization/es_ES.inc new file mode 100644 index 00000000..23f6f80a --- /dev/null +++ b/plugins/html5_notifier/localization/es_ES.inc @@ -0,0 +1,23 @@ + diff --git a/plugins/html5_notifier/localization/fr_FR.inc b/plugins/html5_notifier/localization/fr_FR.inc new file mode 100644 index 00000000..31ad4449 --- /dev/null +++ b/plugins/html5_notifier/localization/fr_FR.inc @@ -0,0 +1,22 @@ + diff --git a/plugins/html5_notifier/localization/it_IT.inc b/plugins/html5_notifier/localization/it_IT.inc new file mode 100644 index 00000000..3f135ea1 --- /dev/null +++ b/plugins/html5_notifier/localization/it_IT.inc @@ -0,0 +1,21 @@ + diff --git a/plugins/html5_notifier/localization/ja_JP.inc b/plugins/html5_notifier/localization/ja_JP.inc new file mode 100644 index 00000000..18a8ded2 --- /dev/null +++ b/plugins/html5_notifier/localization/ja_JP.inc @@ -0,0 +1,22 @@ + diff --git a/plugins/html5_notifier/localization/nl_NL.inc b/plugins/html5_notifier/localization/nl_NL.inc new file mode 100644 index 00000000..8e236521 --- /dev/null +++ b/plugins/html5_notifier/localization/nl_NL.inc @@ -0,0 +1,22 @@ + diff --git a/plugins/html5_notifier/localization/pl_PL.inc b/plugins/html5_notifier/localization/pl_PL.inc new file mode 100644 index 00000000..179479c8 --- /dev/null +++ b/plugins/html5_notifier/localization/pl_PL.inc @@ -0,0 +1,25 @@ + diff --git a/plugins/html5_notifier/localization/ru_RU.inc b/plugins/html5_notifier/localization/ru_RU.inc new file mode 100644 index 00000000..69ae721a --- /dev/null +++ b/plugins/html5_notifier/localization/ru_RU.inc @@ -0,0 +1,26 @@ + diff --git a/plugins/html5_notifier/localization/sk_SK.inc b/plugins/html5_notifier/localization/sk_SK.inc new file mode 100644 index 00000000..b1359e43 --- /dev/null +++ b/plugins/html5_notifier/localization/sk_SK.inc @@ -0,0 +1,22 @@ + diff --git a/plugins/html5_notifier/localization/zh_CN.inc b/plugins/html5_notifier/localization/zh_CN.inc new file mode 100644 index 00000000..ef785b00 --- /dev/null +++ b/plugins/html5_notifier/localization/zh_CN.inc @@ -0,0 +1,26 @@ + diff --git a/plugins/html5_notifier/screenshot.png b/plugins/html5_notifier/screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..512fa8ddf68839d8522d56d44d5ebb9281b16c55 GIT binary patch literal 9155 zcmbVybyQrjx9$LgLvbxGMT@(;7I$|q?hJz!DPAbj;ts`KN^u!|nNNxmdjQ(X}ogB$|@0AMRC$!P-s2=Et>g@y?CeLx0t@DGBw zwxSH67D{mdkDz-f8F>Q$m<0b`1VB~}DLfI?M_ElCbp?(56+VfIl(i!~iPA~e&_~wY z)z!|;2Ob3gWWDUHeC%v#{GEIpX%v;!G;KNTPyqlMfU=ylZou+ME-09AZV8gl%L~F; zGL2mYTKQws2DB|FYSy#}l+bY!85@I5ECNVeTa-EHEAW1yBrdnTsi-OWxoi-h(WY0x z*uZMuUag`fEfY3L2Mh-+t@<^!b>zUFxjRSSEHPxWN#~t0ZJi!}<>lSW*$B*oO$hT2 z9$g_u(CiV`{kRyT^k3!PUlV4R1`2OTEzzTmQAy*biAT^dgMiYS*Ju$1$9O<#czQ&m zA}-J_ndbi~@ITJ@ujKz$jbs@8O4MQ8ez$)&*E;EGnF(HdMpS@NMl!XeTZ3W!CEm_^ZkyqR z&rBL)jbh~L5~aFYghm;^X6cnQmRD9JAC7tT5)Tdzn(QaZ3y}`4f{ZfkjEI5Zdv|zk z*J;|-X24igZSCp7SOP|6j3^m+XV>9%L>3b2i1(xVo(n7Pc0 z@}r5@<{_5Jac!os+Apc+h2qc^xXCZ6!B1 zSKM=J(A)f)?7CDHp_l)i8QL}C$RdrX=*k|9C9WSPHU&%Imd1q-lT6{yW~UkaDTnLh zrJ%0o-+dnr_XcmoUpL+eEeKoLZ@0Q8e)Y~=!^oa@a{-PR8H#P7{9ZP_HhwF$)9C^e zNd`MrfAx1Y_s1LfSrv4W$$}Swmkrc^|AcZxaG*X8SD69>F3#i-JAD|)aAr1yUhV!A{3D9-8AzV1(k<{m{Z;W zK=xKBDO<>Mt3>%TO%Sg})f~Res$1K_QP*K*75?Z>Z#a@$8wjo|v8l7ACPl3@AojCS zDi}%uLIK#?+C!V|08$DH_QepS)`V9sIKr6QXO*X$=X`%`rwrN_8@*dWVpLP%6T)U< zNEKk$hgCbzq2qHt0XGwqRyA?$;m5l+`)W2eP|Xa9)Tfka?lNc&FL`37saGs{j;gx) zb$}Rk$n9p!g89nLDcsl$Ah60=BKf5@_xwJ~k+Crm$nZ7Qo}Zzi>3bar0{GPfFOLh( z_dhG|0l!5Iv%iWY*F=}`F|R*RS1~{vN6UtYx7pzG9y@z0NaO?JA~2=$O~*rvd4M|qgo57jLTmy`GeO~RaNz@ z>&E=^Xvr_f;i4ABPcRC3Erm^HAyph{sOUdf{oiOhl9_;qbI=6`7{f6%uaMF1f&zdl znKYD6)c;hDiCWs@{r?AX{}&{O+C7ry;5`$H%0s0Z#y^H$96r~I`}K@C-DK$%$rFm(W?GYOvMMa)H|b_1B{0wlRT?sh zVuHYv{)wvMm~B-k$9@lgD=rS`m=?#6?#2FUj#w0%8<=%a&LVLSFJ?VT)UBDN2-fjj zn4}}I?(OQeKTK9`(0REp{RVcfy-I&wrPvXtb-hOiEh?Y#x^edxI)*Nb000$LzRTX; z4;v8e_NGpDDBhuSE+7f{j`B%?4y+w2zq8)hhn|$0x(Z#ldZ))d7V>_RnX?VTjcfJ; z`Z08QNM5%`PJH2TSN5XB+sli-X6~did^IP>92JuivcF)Vm&h8{G@=4m!y(z9-4r+3 z54G!F3=45d`56ZO83P_75YPDFpYnuSPZ5ug$ge!y7Pr)^<5JKZV0c+E&*xY=g#Dfg z4RTai>w?a2613HeTZkT;Zz5a!@ljc7+Btg)s{ZWhEsr1h5gQi-34e8%{O~b)IsX#vx7OY zEh7gN)-D%wO|17;^3pOKouwWQdalQMfG`5$g@)XWedLI&x=y+&kz^^om@`cy7n_6* zwu@@{bePKc5ouHVzc4I=lk`(4-pev-`m`kY*lX9T|u)-Ghr7(`oUOYnSXCKB?Ycvc< ze8@j>@IZU&R?Ur?t>Qa1=fH{M*mu6K+4?p;boJ-w5yGm<1m_gf=BV{eINp17^c}*7 z0(stTV2}TI?Fha6;cJ=MR>_fHa+efynCv~A7AG_+6!I`uQQyL#-^3;yVNIFiDtxtg zH}JM|wU)uvX<%f_C@;%V^zm>UlO3R0zi6&IYj)#VNlHeqF7dg9pwoOIQI*$Dox4P1 z`YXicvo?Pj;YmAR4U4>u%G{e$N40O{HgWT|^H~%BzG;uk>|T#jl)HB0wVe zF)1{+y%gW8^=TH_>(6D(&56(#uh-gOA4L!OCg=%Af2)|ooMI)~Dr*iV1$>5)n=A>nI-xRvqeyBY@yh$NUG?A(|Vw|}Re4&@s~nf4czBDyF- zmJ+2R{HJ$}DJoX1F+|)Wws>d|7hk~T;%Q|Kk^d2-lzaZA3Yk5MF=?!*XdknUz18$1 zIuit#uD7<}w$#|?G}REn4>?_}w^Ka{)1|(t&HC55VZDZi>AabtPkQdvKo8xa$gb~< zq=bja!?EB*fsBZuYo*LA&E)|3l1eJpy>F$JD11SO-7ZA*HuBeT>`7Bk-%q|ry^YDq zuVuT;6Jpp;i0GR@7Xcvl&h{*>e5ZVk5JrF%VGcnroVR(9G%{KAyWME*XtVocsMFowbtUQ;mVm-!=M0Vw2jYiVB`u zbp36u)3|9BC0+U_vr|MxhhUS*MJ;V1*5RJ<8XvvJwQ7IrT)iAR?WT_h#YbZ&=RO-X zaQoicVQZXE6F4~xB&fU9AG;hssth%_89v($)38z6YEeicC>prt7zi8=+eS8bnI=tW z-0(eKqoxA)(D@GQ!Sl>FSHbv)3Cl)eS8_>ln#b_YFGOXX8xE|8dwymf7AsV2Shn*+ zs2~}8b>Fps#| zCVvb2i26(C#;l4o_Pj~)o-*Lqg3j>f6QnzEjq?R3AY98*SH!GSeAY+or(r(#k&982 z`wqXcFok9bSL}~hq-Y2OaRe0nGRS zKpmcsLejP|PH52KPY`)uy1;3@mb)4hN&>d<_@SODhD@sZbD4f5aC|APONO4-Uj~gf zhw$>kcp$RM-ATN4z@0chfBS@w%l(IS;;L)&+CE3VS$qBx-0b%M$6exb+0m(m3XLR^RLA~s`KraUtv z>W)fgT|aky%Un(9ywy|38o6#yvZ``Tzvn+r9>HphJIXPNAkdyC4WL9}(Rc}bz; z)vZ1uKo?nWj#q*MJp_Zs&}gN;Q8?lV%s;$-r6W(C&nrj|;^1_YcQ2k@r)zCyGb+68uu-6_g54$<)s^Ta)LVh5 zYoA*25kX$t@YegyiS|b&q#(bnaw=?plv^7L(IMWS9)$v~+Z@i*D34iv-=?!{lDibm zyBel9JqF)pcy>BR+l^5~@zSg)z(is`5g8?0ElQh6 z01~#H8Vv|}m(L@C8ygrd`8Jg}WjUY{Er;C;G4gIgI_8r7-*ifOWL_z#FG5_8EH#T9 zP{8qE{Oa8dDnRGaD3(CFNPpQWwY<+j12-ad*c-i3qpk!X2VUyI+2i z$Y0oSdj+%j{F&1(C@(7@i@n~H!uH$;0~G~B!E8jVyCp??{^qdjaCe@LNf&z5VY+TT zequ;+>Nh9%W>b+e3_gETdJl+X)++LP8tm-8wOIF74n9^Y^kxK6NjxD7Yt(?JZ=Ldg zbmRdlbgDoc9S#Ye6k3GDxOOtV6?0yQYAUodlYiKo6fa|9iJHmfze-6%2y=fwo8A>U zEwooVfvE&S)yx7DTUr^Q$FuOY2(xbo?Z(!6BLeZu5+WRz{|>x8?U#vP(L?IMWdD^! z-xhbGYcOb%xiVXwldd^rC;@0Uqs+=T&@qQ#%n_YY=2vAB_!5C_YdIrw1RHyNhuDx- z!ns-FG04~1VMgo@S2?T)CWeqtxuAem-~bI1P@p;~a9*_@ z_swwgBq8>zeevmZ?$t`&Xer)5sO@CEh;#lfp-=bSMSYMbeQdx+Dga2a z?4~Xavc>LJtzK4j7zv&Ww|Taj(1@c-4TSx28}~<%8~j$cZb)Xvn9>sH+=q=zqgG=8ep)1B7{4MHjyFicenGb$E5u<`r! zVy`^bVBbVCII95)XXU{RlzPA2vfa__5{dce;&ZKykp%+G2g*m3p#Mrp=0mLA{ED-F z{M&_&LLVd_j^k!R_UPrrmRu8S`p~)8Y!4UH@P*bc@l$<2wliV91H+VdJIpkgl*212 z`NSe{H$7#!=l*s#Ksk+4U8Dn09>z@0RKxdA&B#X>Y2` zFjUq$Hb0FMbJ21ml0Bd=J`_Sl42Rci=KgZrG$rwTyZvupL444$PPGvpLnGAse<5+8 z7E72BxL7C+X>eu&?^R4-9<}(AH$`T8`mWzLz79~>rQNg^lcT+0CBfHihff1-6bfDc z?#o6V3>0Pqe(0+)X|s25Ads<6mu#F5xxYNHZwkCRoCDpQUfl&7Vx#eh+y-~UDT-S2 zuI2Xv8u=1k*)7!HMmrL?wFp1-S(=z+kMD{E-+HL3#kIJ9JMf918FUT-)_|)g zGwtA1i^{iqA{S(71>*#Q7KUuqW)F%e?_8OF;ZVMC ze>~u$5iq(blSi2YYQYBR?RJ8_r$KSDDe}trHzE4Pno+E}X$+N`QI$G7X(I;m9Tyj> zD_u1aQ!C4|(+sSS6~B!#^%s_Ku%t4ukdt$fdH9eo88KK@;}}|6S`>UZRPct_;)F7*X`9$b_{n3)B$1pTFtS!=|TmU=s7etaqj}G zm}@BsF5|2a`E7X;$p9Z`hml|3St6y#GJ`B>;;e-TwdXjLQ;V3pul@2DNFD;RHJO1} zkqx2T14kJW7SGM<m?R%om_$QdU0uPq4NUkG&ZFY#wZ(ZMpO?z)eq7`f zw{awB8DSvvi4<4+%wyA1Y_@aIDbtWI(gv_l80n2-lFMfRYmz*E5Mnr9Dga#RB2WSR zUF@$u+>&vzs^Sp6IO{Fxe1OIsYVX*x@!KNwrUovk=YBD7^~0P?F2Bd>Gl4{zXx$Vs zMD<(ohf$*eO^l5Rx)NuUi%duusUi)LPeTQC@koc9)Et4D7|2`C+^jci z1iS^h8C0y@XDLLCYaG9CdO)X@Y@FqBX+H}FxC&)ql!L4m-0w(Rhz%}&*JWu?_ne4g zTDJka@(LE3LeEZ{n`hVYE#5cfNqEYskzEEAt)My#ZqY;!)iX!SHzY&mD(B5fhq$i{ z4+#4=xk}Ua0#%&6ym+QyGvykRe#c+f#|wXQCE({j>xC#(IvJ|Jbi_H#_gox8bJgdj z_JqQ>AJ7qS^H2c_MDj_@hg~=8@j`@!0Kh+sQQJzauDnm=Rhtx#zx1D~IbGLRcEI4b zM*uM5Nc1qflBx&)l@q;B>cW?OyBIBP99EYt?hFvFGXOYB9~p>D$T% z*o1H>X)wmLwpw{+_DO~6N9gDcaTg20><;(fV%6xM306})#2}b#R2&DbPYb100X4KK6jb0#t*Z}^8K3=6E(PJF zzV(0ii={)-`JH4t?Ewx~_1Jo||_-8&5O&7IdBujfNHqHYj0NCK8L+Ug!Wm zzCeWE`>_fDw8+6EU~zA?YTfrmdvMi#KBY;>^$choeDma%g&OwfPli#Z`O;fY)Vq!Q zKY8N${DN&*Hdt(g7^+3gDqvyFRCa_gTz7kQLCHj^oQKn5{Lmvsn5mG5f&&GP6uKZ_ z_vq?=V&Yxh0w%z|$*69jt)->3wDjv_@r)13Qt``~Dkm?^6LBr+N1*^VgId!LFS|Ai zgLLt^rwoL6Z@*-*3nd6jXz#}2;^H|$>BJEXu9YiaFgR~l=S9an_@yo`?Efq0wcDYt@50(#SKhaP+J!P{ly!5`NEzBIKQJ}1 zNd^&r$->!BIuU7VL&(Ob0hcF*lOS}+kYo26NF~*&^M=rdq8Mr4WjOJ5W?C9UToDbL zLa|ovfF;k@YJC?(+QIHxK|iq#-}h7ZIAd3%w3$c0AR%f(Az!jNkAxL)`9$bB9rbHX zzh>pZA7!+S^;38Eo3`dwDoM}jr@Kv^a*de&!tg(yPyrTi3+dg=f&ft4lMkT z{vu(g2hseAuCB{r7kfl0t0fD4$%i00n8WLV_DektG2b(BfA}watyhW#u>9qU2|nre zUk7v2@-vuz-PCL!-)7xD;TY%n)Br-NTQHKXbPp|AUt27C_4W6N){Xp(J-`lq`LVTC zplRtby)Kv$7mb0DG4Zva+iJTfHa7OAhY&BXH?#ZMdGoDkPVklY*22SyiJQ3BWyQE2 zDu9ga0OsDoXx$}$zS~pH85j^XMZMFGFl*rD)wpn^rmlXv=(V*ox-U{?z8Q%9H_L+t z3-Ih`p<-MnW903vDny}Q!SlnnnmMi)6RDo0apItPgY&lXzNY9F6;K~!Mzqi8(AMzm zJFtQ9+n-aH<8Hl3zU?hL_;GX^l7Dgsldtpo#j%;_1puK&2(8U1{? z03SY43V+YoJIqKAxWO|-!df1df4GKQT9WdILU1yE+%I59Zaci|*`E;yp(!`xIkSkp z+=gzn>?q8QzMSM&vc-k|ohgM4CpBP1^`kz-f2g2(KFWUP{t@#A5NpC-`0{(g@I&uz zW^j~muA@2{eXtBTi8&5_ghyf?PMSemK*}J~)?W zSdRI(%7V}gHg_bH*K?KO{<5+0tRTeE_nuAvdB2uX5DK@&XcvdIfXNA2#n%8DaYiTL z1d%cgx6TKc1;xvjL)6XV$ywdYEe8aypzD*Ba)pCEQ|i$7Jw-d=A!3hxIlKsfP#VBf zO%E;*Aq?%jDrP7ehGI1_SMf?&2s(h@<0~Z;g-gyf^2Jc>n|Dge%EF#o za_|#TW8-D`@no0zSh)=(;rXVHwHd<$mGJi#^+|0YC5TrW(Ggx;+PQ z-CR$fxCi?Tv@`)MCyQUM7BRE)jGK(qxT-VfUGyI$9|G>>Nf3HfIcq_eDXGE>8$RO^ z@+%u4wtW{26xpQCP_Gfa^0IfTs%y{dCcg$x3)1CrB(0$yX5&#+{L${}LnlxS0n!MF%2MgMSEG zLEPUJEH4($lEo$og_wPJo+C*4nMU~PdLrkqOehjh|GA9DE>bTNjzqhA=-VkK!o=+- zY~6fmY?2C#BaI*`kkw4(o_VtKGao)rl50BiHvlW>!{JVH>zN2KaY)8X*@SgN zMb~x|iFv!@ucE7?g{kiW!_TrYZNmP@AEfj4LSfk(K46eRTLX1=PWt2M^R_tenSd5V z;Oz%SO}gqcZlfMOC^~trG?4>)UHAQA+2qS_w1u0=9=B6sCdcQ7QVg_%3@eC1B9vw@ z`e`siqDLsH2S6v7&M5Z!_8FCK8Y9m@<5axzXiL6pRjvI|O5 zI7v`G6k52{&G=#;G~j(Y^H?-MEcTm0mxh|5ag$;_iHsSad{n%M(YZs5xJ>-zc '2020.03.13', - 'version' => '0.0.86' + 'version' => '0.0.87' ];