diff --git a/app/config/routes.php b/app/config/routes.php index e8e0561..00f803c 100644 --- a/app/config/routes.php +++ b/app/config/routes.php @@ -145,6 +145,7 @@ return [ array('/admin/media/overlay/alpha', 'POST', 'admin@save_overlay_alpha'), array('/admin/media/sound/message', 'POST', 'admin@upload_media_sound_message'), array('/admin/mail/save', 'POST', 'admin@save_mail_settings'), + array('/admin/mail/test', 'ANY', 'admin@send_test_email'), array('/admin/themes/import', 'POST', 'admin@import_theme'), array('/admin/themes/remove', 'POST', 'admin@remove_theme'), array('/admin/backup/cronjob/save', 'POST', 'admin@save_backup_cronjob_settings'), diff --git a/app/controller/admin.php b/app/controller/admin.php index 38e617b..472e77b 100644 --- a/app/controller/admin.php +++ b/app/controller/admin.php @@ -782,6 +782,35 @@ class AdminController extends BaseController { } } + /** + * Handles URL: /admin/mail/test + * + * @param Asatru\Controller\ControllerArg $request + * @return Asatru\View\JsonHandler + */ + public function send_test_email($request) + { + try { + $user = UserModel::getAuthUser(); + + $mailobj = new Asatru\SMTPMailer\SMTPMailer(); + $mailobj->setRecipient($user->get('email')); + $mailobj->setSubject('[' . env('APP_NAME') . '] Test E-Mail'); + $mailobj->setView('mail/mail_layout', [['mail_content', 'mail/mail_admintest']], ['user' => $user]); + $mailobj->setProperties(mail_properties()); + $mailobj->send(); + + return json([ + 'code' => 200 + ]); + } catch (\Exception $e) { + return json([ + 'code' => 500, + 'msg' => $e->getMessage() + ]); + } + } + /** * Handles URL: /admin/cronjob/token * diff --git a/app/lang/en/app.php b/app/lang/en/app.php index 0d78386..f1e9fce 100644 --- a/app/lang/en/app.php +++ b/app/lang/en/app.php @@ -484,5 +484,7 @@ return [ 'confirm_set_gallery_photo_as_main' => 'Do you want to replace the main photo with this one?', 'enable_quick_add' => 'Enable Quick-Add widget', 'clear_cache' => 'Clear cache', - 'schema_attribute_already_exists' => 'There is already an attribute with the given label' + 'schema_attribute_already_exists' => 'There is already an attribute with the given label', + 'send_test_mail' => 'Send test mail', + 'confirm_test_mail' => 'Do you want to send a test e-mail to {mail}?' ]; \ No newline at end of file diff --git a/app/resources/js/app.js b/app/resources/js/app.js index 8326d0b..455c760 100644 --- a/app/resources/js/app.js +++ b/app/resources/js/app.js @@ -96,6 +96,7 @@ window.vue = new Vue({ loadMore: 'Load more', operationSucceeded: 'Operation succeeded', copiedToClipboard: 'Content has been copied to clipboard.', + origTestMailButtonContent: '', chatTypingEnable: false, chatTypingTimer: null, chatTypingHide: null, @@ -1937,6 +1938,22 @@ window.vue = new Vue({ }); }, + sendTestMail: function(button) { + if (window.vue.origTestMailButtonContent === '') { + window.vue.origTestMailButtonContent = button.innerHTML; + } + + button.innerHTML = ' ' + window.vue.origTestMailButtonContent; + + window.vue.ajaxRequest('post', window.location.origin + '/admin/mail/test', {}, function(response) { + if (response.code == 200) { + button.innerHTML = ' ' + window.vue.origTestMailButtonContent; + } else { + alert(response.msg); + } + }); + }, + scrollTo: function(target) { let elem = document.querySelector(target); if (elem) { diff --git a/app/views/admin.php b/app/views/admin.php index 0d482d6..c4bd5bd 100644 --- a/app/views/admin.php +++ b/app/views/admin.php @@ -695,7 +695,10 @@
- + +   + {{ __('app.send_test_mail') }} +
diff --git a/app/views/mail/mail_admintest.php b/app/views/mail/mail_admintest.php new file mode 100644 index 0000000..6c7e017 --- /dev/null +++ b/app/views/mail/mail_admintest.php @@ -0,0 +1,9 @@ +

{{ app('workspace') }}

+ +

+ {{ __('app.hortusfox_version', ['version' => config('version')]) }}
+ {{ __('app.php_version', ['version' => phpversion()]) }}
+ {{ __('app.mysql_version', ['version' => VersionModel::getSqlVersion()]) }}
+ {{ __('app.server_system_info', ['osn' => php_uname('s'), 'osv' => php_uname('v'), 'mt' => php_uname('m')]) }}
+ {{ __('app.server_timezone', ['time' => date('Y-m-d H:i') . ' (' . date_default_timezone_get() . ')']) }}
+

diff --git a/public/js/app.js b/public/js/app.js index c02edc1..c10707a 100644 --- a/public/js/app.js +++ b/public/js/app.js @@ -16,7 +16,7 @@ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; -eval("{__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _sass_app_scss__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./../sass/app.scss */ \"./app/resources/sass/app.scss\");\n/* harmony import */ var _sass_app_scss__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_sass_app_scss__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var fontawesome_free_scss_fontawesome_scss__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! fontawesome-free/scss/fontawesome.scss */ \"./node_modules/fontawesome-free/scss/fontawesome.scss\");\n/* harmony import */ var fontawesome_free_scss_fontawesome_scss__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(fontawesome_free_scss_fontawesome_scss__WEBPACK_IMPORTED_MODULE_1__);\n/* harmony import */ var fontawesome_free_js_all_min_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! fontawesome-free/js/all.min.js */ \"./node_modules/fontawesome-free/js/all.min.js\");\n/* harmony import */ var fontawesome_free_js_all_min_js__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(fontawesome_free_js_all_min_js__WEBPACK_IMPORTED_MODULE_2__);\n/* harmony import */ var fontawesome_free_js_solid_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! fontawesome-free/js/solid.js */ \"./node_modules/fontawesome-free/js/solid.js\");\n/* harmony import */ var fontawesome_free_js_solid_js__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(fontawesome_free_js_solid_js__WEBPACK_IMPORTED_MODULE_3__);\n/* harmony import */ var fontawesome_free_js_brands_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! fontawesome-free/js/brands.js */ \"./node_modules/fontawesome-free/js/brands.js\");\n/* harmony import */ var fontawesome_free_js_brands_js__WEBPACK_IMPORTED_MODULE_4___default = /*#__PURE__*/__webpack_require__.n(fontawesome_free_js_brands_js__WEBPACK_IMPORTED_MODULE_4__);\n/* harmony import */ var chart_js_auto__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! chart.js/auto */ \"./node_modules/chart.js/auto/auto.js\");\n/* harmony import */ var chartjs_adapter_date_fns__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! chartjs-adapter-date-fns */ \"./node_modules/chartjs-adapter-date-fns/dist/chartjs-adapter-date-fns.esm.js\");\n/**\r\n * app.js\r\n * \r\n * Put here your application specific JavaScript implementations\r\n */\r\n\r\n\r\n\r\nwindow.axios = __webpack_require__(/*! axios */ \"./node_modules/axios/dist/browser/axios.cjs\");\r\nwindow.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\nwindow.constChatMessageQueryRefreshRate = 1000 * 15;\r\nwindow.constChatUserListRefreshRate = 1000 * 15;\r\nwindow.constChatTypingRefreshRate = 2000;\r\n\r\nwindow.vue = new Vue({\r\n el: '#app',\r\n\r\n data: {\r\n bShowAddPlant: false,\r\n bShowEditText: false,\r\n bShowEditMultilineText: false,\r\n bShowEditBoolean: false,\r\n bShowEditInteger: false,\r\n bShowEditDate: false,\r\n bShowEditCombo: false,\r\n bShowEditPhoto: false,\r\n bShowEPUrl: false,\r\n bShowEditLinkText: false,\r\n bShowUploadPhoto: false,\r\n bShowSetPhotoURL: false,\r\n bShowCreateTask: false,\r\n bShowEditTask: false,\r\n bShowEditPreferences: false,\r\n bShowAddInventoryItem: false,\r\n bShowEditInventoryItem: false,\r\n bShowInvItemQRCode: false,\r\n bShowInventoryBulkPrint: false,\r\n bShowInventoryExport: false,\r\n bShowManageGroups: false,\r\n bShowRestorePassword: false,\r\n bShowCreateNewUser: false,\r\n bShowCreateNewLocation: false,\r\n bShowRemoveLocation: false,\r\n bShowPreviewImageModal: false,\r\n bShowSharePhoto: false,\r\n bShowAddFirstLocation: false,\r\n bShowAddCalendarItem: false,\r\n bShowEditCalendarItem: false,\r\n bShowCreateNewCalendarClass: false,\r\n bShowPlantQRCode: false,\r\n bShowPlantBulkPerformUpdate: false,\r\n bShowPlantBulkPrint: false,\r\n bShowAddCustomPlantAttribute: false,\r\n bShowEditCustomPlantAttribute: false,\r\n bShowCreateNewAttributeSchema: false,\r\n bShowAddPlantLogEntry: false,\r\n bShowEditPlantLogEntry: false,\r\n bShowAddLocationLogEntry: false,\r\n bShowEditLocationLogEntry: false,\r\n bShowCreateNewBulkCmd: false,\r\n bShowSelectRecognizedPlant: false,\r\n bShowQuickScanPlant: false,\r\n clsLastImagePreviewAspect: '',\r\n comboLocation: [],\r\n comboCuttingMonth: [],\r\n comboLightLevel: [],\r\n comboHealthState: [],\r\n loading_please_wait: 'Please wait...',\r\n confirmPhotoRemoval: 'Are you sure you want to remove this photo?',\r\n confirmPlantRemoval: 'Are you sure you want to remove this plant?',\r\n confirmSetAllWatered: 'Are you sure you want to update the last watered date of all these plants?',\r\n confirmSetAllRepotted: 'Are you sure you want to update the last repotted date of all these plants?',\r\n confirmSetAllFertilised: 'Are you sure you want to update the last fertilised date of all these plants?',\r\n confirmInventoryItemRemoval: 'Are you sure you want to remove this item?',\r\n confirmPlantAddHistory: 'Please confirm if you want to do this action.',\r\n confirmPlantRemoveHistory: 'Please confirm if you want to do this action.',\r\n confirmRemovePlantLogEntry: 'Do you really want to remove this entry?',\r\n confirmRemoveLocationLogEntry: 'Do you really want to remove this entry?',\r\n confirmRemoveSharedPlantPhoto: 'Do you really want to remove this item?',\r\n confirmSetGalleryPhotoAsMain: 'Do you want to replace the main photo with this one?',\r\n addItem: 'Add',\r\n newChatMessage: 'New',\r\n currentlyOnline: 'Currently online: ',\r\n loadingPleaseWait: 'Please wait...',\r\n noListItemsSelected: 'No items selected',\r\n editProperty: 'Edit property',\r\n loadMore: 'Load more',\r\n operationSucceeded: 'Operation succeeded',\r\n copiedToClipboard: 'Content has been copied to clipboard.',\r\n chatTypingEnable: false,\r\n chatTypingTimer: null,\r\n chatTypingHide: null,\r\n chatTypingCounter: 1\r\n },\r\n\r\n methods: {\r\n ajaxRequest: function (method, url, data = {}, successfunc = function(data){}, finalfunc = function(){}, config = {})\r\n {\r\n let func = window.axios.get;\r\n if (method == 'post') {\r\n func = window.axios.post;\r\n } else if (method == 'patch') {\r\n func = window.axios.patch;\r\n } else if (method == 'delete') {\r\n func = window.axios.delete;\r\n }\r\n\r\n func(url, data, config)\r\n .then(function(response){\r\n successfunc(response.data);\r\n })\r\n .catch(function (error) {\r\n console.log(error);\r\n })\r\n .finally(function(){\r\n finalfunc();\r\n }\r\n );\r\n },\r\n\r\n initNavBar: function()\r\n {\r\n const $navbarBurgers = Array.prototype.slice.call(document.querySelectorAll('.navbar-burger'), 0);\r\n\r\n if ($navbarBurgers.length > 0) {\r\n $navbarBurgers.forEach( el => {\r\n el.addEventListener('click', () => {\r\n const target = el.dataset.target;\r\n const $target = document.getElementById(target);\r\n\r\n el.classList.toggle('is-active');\r\n $target.classList.toggle('is-active');\r\n });\r\n });\r\n }\r\n },\r\n\r\n showEditText: function(plant, property, defval, anchor = '')\r\n {\r\n document.getElementById('inpEditTextPlantId').value = plant;\r\n document.getElementById('inpEditTextAttribute').value = property;\r\n document.getElementById('inpEditTextValue').value = defval;\r\n document.getElementById('inpEditTextAnchor').value = anchor;\r\n window.vue.bShowEditText = true;\r\n },\r\n\r\n showEditMultilineText: function(plant, property, defval, anchor = '')\r\n {\r\n document.getElementById('inpEditMultilineTextPlantId').value = plant;\r\n document.getElementById('inpEditMultilineTextAttribute').value = property;\r\n document.getElementById('inpEditMultilineTextValue').value = defval;\r\n document.getElementById('inpEditMultilineTextAnchor').value = anchor;\r\n window.vue.bShowEditMultilineText = true;\r\n },\r\n\r\n showEditBoolean: function(plant, property, hint, defval)\r\n {\r\n document.getElementById('inpEditBooleanPlantId').value = plant;\r\n document.getElementById('inpEditBooleanAttribute').value = property;\r\n document.getElementById('property-hint').innerHTML = hint;\r\n\r\n if (defval) {\r\n document.getElementById('inpEditBooleanValue_yes').checked = true;\r\n } else {\r\n document.getElementById('inpEditBooleanValue_no').checked = true;\r\n }\r\n \r\n window.vue.bShowEditBoolean = true;\r\n },\r\n\r\n showEditInteger: function(plant, property, defval)\r\n {\r\n document.getElementById('inpEditIntegerPlantId').value = plant;\r\n document.getElementById('inpEditIntegerAttribute').value = property;\r\n document.getElementById('inpEditIntegerValue').value = defval;\r\n window.vue.bShowEditInteger = true;\r\n },\r\n\r\n showEditDate: function(plant, property, defval)\r\n {\r\n document.getElementById('inpEditDatePlantId').value = plant;\r\n document.getElementById('inpEditDateAttribute').value = property;\r\n document.getElementById('inpEditDateValue').value = defval;\r\n window.vue.bShowEditDate = true;\r\n },\r\n\r\n showEditCombo: function(plant, property, combo, defval)\r\n {\r\n document.getElementById('inpEditComboPlantId').value = plant;\r\n document.getElementById('inpEditComboAttribute').value = property;\r\n \r\n if (typeof combo !== 'object') {\r\n console.error('Invalid combo specified');\r\n return;\r\n }\r\n\r\n let sel = document.getElementById('selEditCombo');\r\n if (sel) {\r\n for (let i = sel.options.length - 1; i >= 0; i--) {\r\n sel.remove(i);\r\n }\r\n\r\n combo.forEach(function(elem, index){\r\n let opt = document.createElement('option');\r\n opt.value = elem.ident;\r\n opt.text = elem.label;\r\n sel.add(opt);\r\n });\r\n }\r\n\r\n document.getElementById('selEditCombo').value = defval;\r\n\r\n window.vue.bShowEditCombo = true;\r\n },\r\n\r\n showEditLinkText: function(plant, text, link)\r\n {\r\n document.getElementById('inpEditLinkTextPlantId').value = plant;\r\n document.getElementById('inpEditLinkTextValue').value = text;\r\n document.getElementById('inpEditLinkTextLink').value = link;\r\n window.vue.bShowEditLinkText = true;\r\n },\r\n\r\n selectDataTypeInputField: function(elem, field) {\r\n if (elem.selectedIndex <= 0) {\r\n return;\r\n }\r\n \r\n field.classList.remove('is-hidden');\r\n\r\n if (!field.children[1].children[0].classList.contains('is-hidden')) {\r\n field.children[1].children[0].classList.add('is-hidden');\r\n }\r\n\r\n if (!field.children[1].children[1].classList.contains('is-hidden')) {\r\n field.children[1].children[1].classList.add('is-hidden');\r\n }\r\n\r\n if (!field.children[1].children[2].classList.contains('is-hidden')) {\r\n field.children[1].children[2].classList.add('is-hidden');\r\n }\r\n\r\n field.children[1].children[0].children[0].children[1].children[0].children[0].disabled = true;\r\n field.children[1].children[0].children[0].children[2].children[0].children[0].disabled = true;\r\n field.children[1].children[1].disabled = true;\r\n field.children[1].children[2].disabled = true;\r\n\r\n if (elem.value === 'bool') {\r\n field.children[1].children[0].classList.remove('is-hidden');\r\n field.children[1].children[0].children[0].children[1].children[0].children[0].disabled = false;\r\n field.children[1].children[0].children[0].children[2].children[0].children[0].disabled = false;\r\n } else if (elem.value === 'datetime') {\r\n field.children[1].children[2].classList.remove('is-hidden');\r\n field.children[1].children[2].disabled = false;\r\n } else {\r\n field.children[1].children[1].classList.remove('is-hidden');\r\n field.children[1].children[1].disabled = false;\r\n }\r\n },\r\n\r\n showEditCustomPlantAttribute: function(id, plant, label, datatype, content, is_global = false)\r\n {\r\n document.getElementById('edit-plant-attribute-attr').value = id;\r\n document.getElementById('edit-plant-attribute-plant').value = plant;\r\n document.getElementById('edit-plant-attribute-label').value = label;\r\n document.getElementById('edit-plant-attribute-datatype').value = datatype;\r\n\r\n let elFieldTarget = document.getElementById('field-custom-edit-attribute-content');\r\n\r\n if (datatype === 'bool') {\r\n if (content == 1) {\r\n elFieldTarget.children[1].children[0].children[0].children[1].children[0].children[0].checked = true;\r\n } else {\r\n elFieldTarget.children[1].children[0].children[0].children[2].children[0].children[0].checked = true;\r\n }\r\n } else if (datatype === 'datetime') {\r\n elFieldTarget.children[1].children[2].value = content;\r\n } else {\r\n elFieldTarget.children[1].children[1].value = content;\r\n }\r\n\r\n window.vue.selectDataTypeInputField(document.querySelector('#edit-plant-attribute-datatype'), elFieldTarget);\r\n\r\n if (is_global) {\r\n document.getElementById('field-custom-edit-attribute-datatype').style.display = 'none';\r\n document.getElementById('plant-custom-attribute-removal-field').style.display = 'none';\r\n } else {\r\n document.getElementById('field-custom-edit-attribute-datatype').style.display = 'inherit';\r\n document.getElementById('plant-custom-attribute-removal-field').style.display = 'inherit';\r\n }\r\n\r\n window.vue.bShowEditCustomPlantAttribute = true;\r\n },\r\n\r\n removeCustomPlantAttribute: function(id, target)\r\n {\r\n window.vue.ajaxRequest('post', window.location.origin + '/plants/attributes/remove?id=' + id, {}, function(response){\r\n if (response.code == 200) {\r\n let elem = document.getElementById(target);\r\n if (elem) {\r\n elem.remove();\r\n }\r\n window.vue.bShowEditCustomPlantAttribute = false;\r\n } else {\r\n alert(response.msg);\r\n }\r\n });\r\n },\r\n\r\n saveAllAttributes: function(source) {\r\n const forms = document.querySelector(source).getElementsByTagName('form');\r\n window.vue.bulkSubmitForm(0, forms, 100);\r\n },\r\n\r\n bulkSubmitForm: function(index, forms, delay) {\r\n if (index >= forms.length) {\r\n alert(window.vue.operationSucceeded);\r\n location.reload();\r\n return;\r\n }\r\n\r\n const form = forms[index];\r\n\r\n form.addEventListener('submit', function(event) {\r\n event.preventDefault();\r\n });\r\n\r\n const formData = new FormData(form);\r\n\r\n setTimeout(function() {\r\n fetch(form.action, {\r\n method: form.method || 'POST',\r\n body: formData\r\n }).then(function(response){\r\n return response.text();\r\n }).then(function(data){\r\n window.vue.bulkSubmitForm(index + 1, forms, delay);\r\n }).catch(function(error){\r\n console.error(error);\r\n window.vue.bulkSubmitForm(index + 1, forms, delay);\r\n });\r\n }, delay);\r\n },\r\n\r\n showEditPhoto: function(plant, property, hint = '')\r\n {\r\n document.getElementById('inpEditPhotoPlantId').value = plant;\r\n document.getElementById('inpEditPhotoAttribute').value = property;\r\n\r\n if (hint.length > 0) {\r\n document.getElementById('inpEditPhotoHint').innerHTML = hint;\r\n }\r\n\r\n window.vue.bShowEditPhoto = true;\r\n },\r\n\r\n showPhotoUpload: function(plant)\r\n {\r\n document.getElementById('inpUploadPhotoPlantId').value = plant;\r\n window.vue.bShowUploadPhoto = true;\r\n },\r\n\r\n removePlantPreviewPhoto: function(plant, target) {\r\n window.vue.ajaxRequest('post', window.location.origin + '/plants/details/photo/remove', { plant: plant }, function(response){\r\n if (response.code == 200) {\r\n let elem = document.querySelector(target);\r\n if (elem) {\r\n elem.style.backgroundImage = 'url(' + response.placeholder + ')';\r\n }\r\n } else {\r\n alert(response.msg);\r\n }\r\n });\r\n },\r\n\r\n deletePhoto: function(photo, plant, target)\r\n {\r\n if (!confirm(window.vue.confirmPhotoRemoval)) {\r\n return;\r\n }\r\n\r\n window.vue.ajaxRequest('post', window.location.origin + '/plants/details/gallery/photo/remove', { photo: photo, plant: plant }, function(response){\r\n if (response.code == 200) {\r\n let elem = document.getElementById(target);\r\n if (elem) {\r\n elem.remove();\r\n }\r\n } else {\r\n alert(response.msg);\r\n }\r\n });\r\n },\r\n\r\n showAddPlantLogEntry: function(plant, anchor = '') {\r\n document.getElementById('inpAddPlantLogEntryPlantId').value = plant;\r\n document.getElementById('inpAddPlantLogEntryAnchor').value = anchor;\r\n window.vue.bShowAddPlantLogEntry = true;\r\n },\r\n\r\n showEditPlantLogEntry: function(id, plant, content, anchor = '') {\r\n document.getElementById('inpEditPlantLogEntryItemId').value = id;\r\n document.getElementById('inpEditPlantLogEntryPlantId').value = plant;\r\n document.getElementById('inpEditPlantLogEntryContent').value = content;\r\n document.getElementById('inpEditPlantLogEntryAnchor').value = anchor;\r\n window.vue.bShowEditPlantLogEntry = true;\r\n },\r\n\r\n removePlantLogEntry: function(id, table_entry) {\r\n window.vue.ajaxRequest('post', window.location.origin + '/plants/log/remove', { item: id }, function(response) {\r\n if (response.code == 200) {\r\n document.getElementById(table_entry).remove();\r\n } else {\r\n alert(response.msg);\r\n }\r\n });\r\n },\r\n\r\n loadNextPlantLogEntries: function(obj, plant, table) {\r\n window.vue.ajaxRequest('post', window.location.origin + '/plants/log/fetch', { plant: plant, paginate: obj.dataset.paginate }, function(response) {\r\n if (response.code == 200) {\r\n let tbody = table.getElementsByTagName('tbody')[0];\r\n\r\n response.data.forEach(function(elem, index) {\r\n let newRow = document.createElement('tr');\r\n newRow.id = 'plant-log-entry-table-row-' + elem.id;\r\n newRow.innerHTML = `\r\n ` + elem.content + `\r\n ` + elem.created_at + ` / ` + elem.updated_at + `\r\n \r\n \r\n  \r\n \r\n \r\n `;\r\n\r\n tbody.appendChild(newRow);\r\n });\r\n\r\n obj.parentNode.parentNode.remove();\r\n\r\n let actionRow = document.createElement('tr');\r\n actionRow.id = 'plant-log-load-more';\r\n actionRow.classList.add('plant-log-paginate');\r\n actionRow.innerHTML = `` + window.vue.loadMore + ``;\r\n tbody.appendChild(actionRow);\r\n } else {\r\n alert(response.msg);\r\n }\r\n });\r\n },\r\n\r\n showAddLocationLogEntry: function(location, anchor = '') {\r\n document.getElementById('inpAddLocationLogEntryLocationId').value = location;\r\n document.getElementById('inpAddLocationLogEntryAnchor').value = anchor;\r\n window.vue.bShowAddLocationLogEntry = true;\r\n },\r\n\r\n showEditLocationLogEntry: function(id, location, content, anchor = '') {\r\n document.getElementById('inpEditLocationLogEntryItemId').value = id;\r\n document.getElementById('inpEditLocationLogEntryLocationId').value = location;\r\n document.getElementById('inpEditLocationLogEntryContent').value = content;\r\n document.getElementById('inpEditLocationLogEntryAnchor').value = anchor;\r\n window.vue.bShowEditLocationLogEntry = true;\r\n },\r\n\r\n removeLocationLogEntry: function(id, table_entry) {\r\n window.vue.ajaxRequest('post', window.location.origin + '/plants/location/log/remove', { item: id }, function(response) {\r\n if (response.code == 200) {\r\n document.getElementById(table_entry).remove();\r\n } else {\r\n alert(response.msg);\r\n }\r\n });\r\n },\r\n\r\n loadNextLocationLogEntries: function(obj, location, table) {\r\n window.vue.ajaxRequest('post', window.location.origin + '/plants/location/log/fetch', { location: location, paginate: obj.dataset.paginate }, function(response) {\r\n if (response.code == 200) {\r\n let tbody = table.getElementsByTagName('tbody')[0];\r\n\r\n response.data.forEach(function(elem, index) {\r\n let newRow = document.createElement('tr');\r\n newRow.id = 'location-log-entry-table-row-' + elem.id;\r\n newRow.innerHTML = `\r\n ` + elem.content + `\r\n ` + elem.created_at + ` / ` + elem.updated_at + `\r\n \r\n \r\n  \r\n \r\n \r\n `;\r\n\r\n tbody.appendChild(newRow);\r\n });\r\n\r\n obj.parentNode.parentNode.remove();\r\n\r\n let actionRow = document.createElement('tr');\r\n actionRow.id = 'location-log-load-more';\r\n actionRow.classList.add('location-log-paginate');\r\n actionRow.innerHTML = `` + window.vue.loadMore + ``;\r\n tbody.appendChild(actionRow);\r\n } else {\r\n alert(response.msg);\r\n }\r\n });\r\n },\r\n\r\n markHistorical: function(plant) {\r\n if (!confirm(window.vue.confirmPlantAddHistory)) {\r\n return;\r\n }\r\n\r\n location.href = window.location.origin + '/plants/history/add?plant=' + plant;\r\n },\r\n\r\n unmarkHistorical: function(plant) {\r\n if (!confirm(window.vue.confirmPlantRemoveHistory)) {\r\n return;\r\n }\r\n\r\n location.href = window.location.origin + '/plants/history/remove?plant=' + plant;\r\n },\r\n\r\n deletePlant: function(plant, retloc)\r\n {\r\n if (!confirm(window.vue.confirmPlantRemoval)) {\r\n return;\r\n }\r\n\r\n location.href = window.location.origin + '/plants/remove?plant=' + plant + '&location=' + retloc;\r\n },\r\n\r\n toggleTaskStatus: function(id)\r\n {\r\n window.vue.ajaxRequest('post', window.location.origin + '/tasks/toggle', { task: id }, function(response){\r\n if (response.code == 200) {\r\n let elem = document.getElementById('task-item-' + id);\r\n if (elem) {\r\n elem.remove();\r\n }\r\n } else {\r\n alert(response.msg);\r\n }\r\n });\r\n },\r\n\r\n editTask: function(id)\r\n {\r\n document.getElementById('inpEditTaskId').value = id;\r\n document.getElementById('inpEditTaskTitle').value = document.getElementById('task-item-title-' + id).childNodes[1].textContent;\r\n document.getElementById('inpEditTaskDescription').value = document.getElementById('task-item-description-' + id).innerText;\r\n\r\n let dueDate = document.getElementById('task-item-due-' + id);\r\n if ((dueDate) && (dueDate.childNodes.length > 0)) {\r\n document.getElementById('inpEditTaskDueDate').value = dueDate.childNodes[0].innerText;\r\n document.getElementById('inpEditTaskRecurringTime').value = dueDate.childNodes[2].dataset.time;\r\n\r\n document.getElementById('edit-recurring-flag').classList.remove('is-hidden');\r\n document.getElementById('edit-recurring-time').classList.remove('is-hidden');\r\n\r\n document.getElementById('inpEditTaskRecurringFlag').checked = document.getElementById('inpEditTaskRecurringTime').value.length > 0;\r\n } else {\r\n document.getElementById('inpEditTaskDueDate').value = '';\r\n\r\n if (!document.getElementById('edit-recurring-flag').classList.contains('is-hidden')) {\r\n document.getElementById('edit-recurring-flag').classList.add('is-hidden');\r\n }\r\n\r\n if (!document.getElementById('edit-recurring-time').classList.contains('is-hidden')) {\r\n document.getElementById('edit-recurring-time').classList.add('is-hidden');\r\n }\r\n }\r\n\r\n window.vue.bShowEditTask = true;\r\n },\r\n\r\n removeTask: function(id)\r\n {\r\n window.vue.ajaxRequest('post', window.location.origin + '/tasks/remove', { task: id }, function(response){\r\n if (response.code == 200) {\r\n let elem = document.getElementById('task-item-' + id);\r\n if (elem) {\r\n elem.remove();\r\n }\r\n } else {\r\n alert(response.msg);\r\n }\r\n });\r\n },\r\n\r\n updateLastWatered: function(id)\r\n {\r\n if (!confirm(window.vue.confirmSetAllWatered)) {\r\n return;\r\n }\r\n\r\n location.href = window.location.origin + '/plants/location/' + id + '/water';\r\n },\r\n\r\n updateLastRepotted: function(id)\r\n {\r\n if (!confirm(window.vue.confirmSetAllRepotted)) {\r\n return;\r\n }\r\n\r\n location.href = window.location.origin + '/plants/location/' + id + '/repot';\r\n },\r\n\r\n updateLastFertilised: function(id)\r\n {\r\n if (!confirm(window.vue.confirmSetAllFertilised)) {\r\n return;\r\n }\r\n\r\n location.href = window.location.origin + '/plants/location/' + id + '/fertilise';\r\n },\r\n\r\n expandInventoryItem: function(id)\r\n {\r\n let elem = document.getElementById(id);\r\n if (elem) {\r\n elem.classList.toggle('expand');\r\n }\r\n },\r\n\r\n incrementInventoryItem: function(id, target)\r\n {\r\n window.vue.ajaxRequest('get', window.location.origin + '/inventory/amount/increment?id=' + id, {}, function(response) {\r\n if (response.code == 200) {\r\n let elem = document.getElementById(target);\r\n if (elem) {\r\n elem.innerHTML = response.amount;\r\n\r\n if (response.amount == 0) {\r\n elem.classList.add('is-inventory-item-empty');\r\n } else {\r\n elem.classList.remove('is-inventory-item-empty');\r\n }\r\n }\r\n } else {\r\n alert(response.msg);\r\n }\r\n });\r\n },\r\n\r\n decrementInventoryItem: function(id, target)\r\n {\r\n window.vue.ajaxRequest('get', window.location.origin + '/inventory/amount/decrement?id=' + id, {}, function(response) {\r\n if (response.code == 200) {\r\n let elem = document.getElementById(target);\r\n if (elem) {\r\n elem.innerHTML = response.amount;\r\n\r\n if (response.amount == 0) {\r\n elem.classList.add('is-inventory-item-empty');\r\n } else {\r\n elem.classList.remove('is-inventory-item-empty');\r\n }\r\n }\r\n } else {\r\n alert(response.msg);\r\n }\r\n });\r\n },\r\n\r\n editInventoryItem: function(id, name, group, location, description, tags, amount)\r\n {\r\n document.getElementById('inpInventoryItemId').value = id;\r\n document.getElementById('inpInventoryItemName').value = document.getElementById(name).children[1].innerText;\r\n document.getElementById('inpInventoryItemGroup').value = group;\r\n document.getElementById('inpInventoryItemLocation').value = document.getElementById(location).children[0].innerText;\r\n document.getElementById('inpInventoryItemDescription').value = document.getElementById(description).innerText;\r\n document.getElementById('inpInventoryItemTags').value = document.getElementById(tags).innerText;\r\n document.getElementById('inpInventoryItemAmount').value = amount;\r\n\r\n window.vue.bShowEditInventoryItem = true;\r\n },\r\n\r\n deleteInventoryItem: function(id, target)\r\n {\r\n if (!confirm(window.vue.confirmInventoryItemRemoval)) {\r\n return;\r\n }\r\n\r\n window.vue.ajaxRequest('get', window.location.origin + '/inventory/remove?id=' + id, {}, function(response) {\r\n if (response.code == 200) {\r\n let elem = document.getElementById(target);\r\n if (elem) {\r\n elem.remove();\r\n }\r\n } else {\r\n alert(response.msg);\r\n }\r\n });\r\n },\r\n\r\n createInventoryGroup: function(token, label, tbody, button)\r\n {\r\n window.vue.ajaxRequest('post', window.location.origin + '/inventory/group/add', { token: token, label: label }, function(response) {\r\n if (response.code == 200) {\r\n button.innerText = window.vue.addItem;\r\n\r\n let newRow = document.createElement('tr');\r\n newRow.id = 'inventory-group-item-' + response.itemid;\r\n newRow.innerHTML = `\r\n ` + token + `\r\n ` + label + `\r\n \r\n `;\r\n\r\n tbody.appendChild(newRow);\r\n } else {\r\n button.innerText = window.vue.addItem;\r\n alert(response.msg);\r\n }\r\n });\r\n },\r\n\r\n editInventoryGroupItem: function(id, what, def)\r\n {\r\n let input = prompt(what, def);\r\n\r\n if (input.length > 0) {\r\n window.vue.ajaxRequest('post', window.location.origin + '/inventory/group/edit', {\r\n id: id,\r\n what: what,\r\n value: input\r\n }, function(response) {\r\n if (response.code == 200) {\r\n if (what === 'token') {\r\n document.getElementById('inventory-group-elem-token-' + id).innerText = input;\r\n } else if (what === 'label') {\r\n document.getElementById('inventory-group-elem-label-' + id).innerText = input;\r\n }\r\n } else {\r\n alert(response.msg);\r\n }\r\n });\r\n }\r\n },\r\n\r\n removeInventoryGroupItem: function(id, target)\r\n {\r\n if (!confirm(window.vue.confirmInventoryItemRemoval)) {\r\n return;\r\n }\r\n\r\n window.vue.ajaxRequest('get', window.location.origin + '/inventory/group/remove?id=' + id, {}, function(response) {\r\n if (response.code == 200) {\r\n let elem = document.getElementById(target);\r\n if (elem) {\r\n elem.remove();\r\n }\r\n } else {\r\n alert(response.msg);\r\n }\r\n });\r\n },\r\n\r\n refreshChat: function(auth_user)\r\n {\r\n window.vue.ajaxRequest('get', window.location.origin + '/chat/query', {}, function(response) {\r\n if (response.code == 200) {\r\n response.messages.forEach(function(elem, index) {\r\n document.getElementById('chat').innerHTML = window.vue.renderNewChatMessage(elem, auth_user) + document.getElementById('chat').innerHTML;\r\n \r\n let audio = new Audio(window.location.origin + '/snd/new_message.wav');\r\n audio.onloadeddata = function() {\r\n audio.play();\r\n };\r\n });\r\n }\r\n });\r\n\r\n setTimeout(window.vue.refreshChat, window.constChatMessageQueryRefreshRate);\r\n },\r\n\r\n renderNewChatMessage: function(elem, auth_user)\r\n {\r\n let chatmsgright = '';\r\n if (elem.userId == auth_user) {\r\n chatmsgright = 'chat-message-right';\r\n }\r\n\r\n let html = '';\r\n\r\n if (!elem.system) {\r\n html = `\r\n
\r\n
\r\n
` + elem.userName + `
\r\n
` + window.vue.newChatMessage + `
\r\n
\r\n\r\n
\r\n
` + elem.message + `
\r\n
\r\n\r\n
\r\n ` + elem.diffForHumans + `\r\n
\r\n
\r\n `;\r\n } else {\r\n html = `\r\n
\r\n
\r\n
` + ((elem.userName) ? elem.userName : 'System') + ` @ ` + elem.created_at + `
\r\n \r\n
` + elem.message + `
\r\n
\r\n\r\n
\r\n
` + window.vue.newChatMessage + `
\r\n
\r\n
\r\n `;\r\n }\r\n\r\n return html;\r\n },\r\n\r\n refreshUserList: function()\r\n {\r\n window.vue.ajaxRequest('get', window.location.origin + '/chat/user/online', {}, function(response) {\r\n if (response.code == 200) {\r\n let target = document.getElementById('chat-user-list');\r\n target.innerHTML = window.vue.currentlyOnline;\r\n\r\n response.users.forEach(function(elem, index) {\r\n let comma = '';\r\n if (index < response.users.length - 1) {\r\n comma = ', ';\r\n }\r\n \r\n target.innerHTML += elem.name + comma;\r\n });\r\n }\r\n });\r\n\r\n setTimeout(window.vue.refreshUserList, window.constChatUserListRefreshRate);\r\n },\r\n\r\n refreshTypingStatus: function()\r\n {\r\n if (!window.vue.chatTypingEnable) {\r\n return;\r\n }\r\n\r\n window.vue.ajaxRequest('get', window.location.origin + '/chat/typing/update', {}, function(response){\r\n if (response.code != 200) {\r\n console.error(response.msg);\r\n }\r\n });\r\n\r\n setTimeout(function(){\r\n window.vue.chatTypingTimer = null;\r\n }, 5000);\r\n },\r\n\r\n handleChatInput: function()\r\n {\r\n if (!window.vue.chatTypingTimer) {\r\n window.vue.chatTypingTimer = setTimeout(function(){\r\n window.vue.refreshTypingStatus();\r\n }, 1000);\r\n }\r\n },\r\n\r\n handleTypingIndicator: function()\r\n {\r\n window.vue.ajaxRequest('get', window.location.origin + '/chat/typing', {}, function(response){\r\n if (response.code == 200) {\r\n if (response.status) {\r\n let elem = document.getElementsByClassName('chat-typing-indicator')[0];\r\n elem.style.display = 'block';\r\n\r\n window.vue.chatTypingHide = setTimeout(window.vue.hideChatTypingIndicator, 6550);\r\n }\r\n }\r\n });\r\n\r\n setTimeout(window.vue.handleTypingIndicator, window.constChatTypingRefreshRate);\r\n },\r\n\r\n hideChatTypingIndicator: function()\r\n {\r\n if (window.vue.chatTypingHide !== null) {\r\n let elem = document.getElementsByClassName('chat-typing-indicator')[0];\r\n elem.style.display = 'none';\r\n\r\n window.vue.chatTypingHide = null;\r\n }\r\n },\r\n\r\n animateChatTypingIndicator: function()\r\n {\r\n let indicator = document.getElementsByClassName('chat-typing-indicator')[0];\r\n if (indicator.style.display === 'block') {\r\n window.vue.removePreviousChatIndicatorCircleStyle();\r\n\r\n let elem = document.getElementById('chat-typing-circle-' + window.vue.chatTypingCounter.toString());\r\n elem.style.color = 'rgb(50, 50, 50)';\r\n elem.classList.add('fa-lg');\r\n\r\n window.vue.chatTypingCounter++;\r\n if (window.vue.chatTypingCounter > 3) {\r\n window.vue.chatTypingCounter = 1;\r\n }\r\n }\r\n\r\n setTimeout(window.vue.animateChatTypingIndicator, 350);\r\n },\r\n\r\n removePreviousChatIndicatorCircleStyle: function()\r\n {\r\n let previous = window.vue.chatTypingCounter - 1;\r\n if (previous == 0) {\r\n previous = 3;\r\n }\r\n\r\n let elem = document.getElementById('chat-typing-circle-' + previous.toString());\r\n if (elem.classList.contains('fa-lg')) {\r\n elem.classList.remove('fa-lg');\r\n elem.style.color = 'inherit';\r\n }\r\n },\r\n\r\n fetchUnreadMessageCount: function(target) {\r\n window.vue.ajaxRequest('get', window.location.origin + '/chat/messages/count', {}, function(response) {\r\n if (response.code == 200) {\r\n if (response.count > 0) {\r\n target.classList.remove('is-hidden');\r\n target.children[0].innerText = response.count;\r\n } else {\r\n target.classList.add('is-hidden');\r\n }\r\n }\r\n });\r\n\r\n setTimeout(window.vue.fetchUnreadMessageCount.bind(null, target), window.constChatMessageQueryRefreshRate);\r\n },\r\n\r\n fetchNewSystemMessage: function(target) {\r\n window.vue.ajaxRequest('get', window.location.origin + '/chat/system/message/latest', {}, function(response) {\r\n if (response.code == 200) {\r\n if (response.message) {\r\n window.vue.fadeSystemMessage(target, window.vue.renderNewSystemMessage(response.message), response.message.id);\r\n \r\n let audio = new Audio(window.location.origin + '/snd/new_message.wav');\r\n audio.onloadeddata = function() {\r\n audio.play();\r\n };\r\n }\r\n }\r\n });\r\n\r\n setTimeout(window.vue.fetchNewSystemMessage.bind(null, target), window.constChatMessageQueryRefreshRate);\r\n },\r\n\r\n renderNewSystemMessage: function(elem) {\r\n let html = `\r\n
\r\n
` + ((elem.userName) ? elem.userName : 'System') + ` @ ` + elem.created_at + `
\r\n\r\n
` + elem.message + `
\r\n
\r\n `;\r\n\r\n return html;\r\n },\r\n\r\n fadeSystemMessage: function(target, code, id) {\r\n target.innerHTML = code + target.innerHTML;\r\n\r\n let fadeElem = document.getElementById('system-message-small-' + id);\r\n\r\n setTimeout(function() {\r\n fadeElem.classList.replace('fade-out', 'fade-in');\r\n }, 250);\r\n \r\n setTimeout(function() {\r\n fadeElem.classList.replace('fade-in', 'fade-out');\r\n }, 5000);\r\n },\r\n\r\n textFilterElements: function(token) {\r\n let elems = document.getElementsByClassName('plant-filter-text-target');\r\n for (let i = 0; i < elems.length; i++) {\r\n let target = elems[i].parentNode;\r\n \r\n while (!target.classList.contains('plant-filter-text-root')) {\r\n target = target.parentNode;\r\n }\r\n\r\n if (!elems[i].innerText.toLowerCase().includes(token.toLowerCase())) {\r\n target.classList.add('is-hidden');\r\n } else {\r\n target.classList.remove('is-hidden');\r\n }\r\n }\r\n },\r\n\r\n filterTasks: function(token) {\r\n let elems = document.getElementsByClassName('task');\r\n for (let i = 0; i < elems.length; i++) {\r\n let elemTitle = elems[i].children[1].children[0];\r\n let elemDescription = elems[i].children[2].children[0];\r\n\r\n if ((elemTitle.innerText.toLowerCase().includes(token.toLowerCase())) || (elemDescription.innerText.toLowerCase().includes(token.toLowerCase()))) {\r\n elems[i].classList.remove('is-hidden'); \r\n } else {\r\n elems[i].classList.add('is-hidden'); \r\n }\r\n }\r\n },\r\n\r\n filterInventory: function(token) {\r\n let elems = document.getElementsByClassName('inventory-item');\r\n for (let i = 0; i < elems.length; i++) {\r\n let elemName = elems[i].children[1].children[0];\r\n let elemDescription = elems[i].children[2].children[1];\r\n let elemTags = elems[i].children[2].children[0].children[0];\r\n let elemLocation = elems[i].children[2].children[3].children[0];\r\n \r\n if ((elemName.innerText.toLowerCase().includes(token.toLowerCase())) || (elemDescription.innerText.toLowerCase().includes(token.toLowerCase())) || (elemTags.innerText.toLowerCase().includes(token.toLowerCase())) || (elemLocation.innerText.toLowerCase().includes(token.toLowerCase()))) {\r\n elems[i].classList.remove('is-hidden'); \r\n } else {\r\n elems[i].classList.add('is-hidden'); \r\n }\r\n }\r\n },\r\n\r\n toggleDropdown: function(elem) {\r\n if (elem.classList.contains('is-active')) {\r\n elem.classList.remove('is-active');\r\n } else {\r\n elem.classList.add('is-active');\r\n }\r\n },\r\n\r\n selectAdminTab: function(tab) {\r\n const tabs = ['environment', 'media', 'users', 'locations', 'auth', 'attributes', 'calendar', 'mail', 'themes', 'backup', 'weather', 'api', 'info'];\r\n\r\n let selEl = document.querySelector('.admin-' + tab);\r\n if (selEl) {\r\n tabs.forEach(function(elem, index) {\r\n let otherEl = document.querySelector('.admin-' + elem);\r\n if (otherEl) {\r\n otherEl.classList.add('is-hidden');\r\n }\r\n\r\n let otherTabs = document.querySelector('.admin-tab-' + elem);\r\n if (otherTabs) {\r\n otherTabs.classList.remove('is-active');\r\n }\r\n });\r\n\r\n selEl.classList.remove('is-hidden');\r\n\r\n let selTab = document.querySelector('.admin-tab-' + tab);\r\n if (selTab) {\r\n selTab.classList.add('is-active');\r\n }\r\n }\r\n },\r\n\r\n switchAdminTab: function(tab) {\r\n window.vue.selectAdminTab(tab);\r\n\r\n const url = new URL(window.location);\r\n url.searchParams.set('tab', tab);\r\n history.replaceState(null, '', url.toString());\r\n },\r\n\r\n showImagePreview: function(asset, aspect = 'is-3by5') {\r\n let img = document.getElementById('preview-image-modal-img');\r\n if (img) {\r\n img.src = asset;\r\n\r\n if (window.vue.clsLastImagePreviewAspect.length > 0) {\r\n img.parentNode.classList.remove(window.vue.clsLastImagePreviewAspect);\r\n }\r\n\r\n window.vue.clsLastImagePreviewAspect = aspect;\r\n img.parentNode.classList.add(window.vue.clsLastImagePreviewAspect);\r\n\r\n window.vue.bShowPreviewImageModal = true;\r\n }\r\n },\r\n\r\n showSharePhoto: function(asset, title, type) {\r\n document.getElementById('share-photo-title').value = title;\r\n document.getElementById('share-photo-id').value = asset;\r\n document.getElementById('share-photo-type').value = type;\r\n\r\n document.getElementById('share-photo-result').classList.add('is-hidden');\r\n document.getElementById('share-photo-error').classList.add('is-hidden');\r\n document.getElementById('share-photo-submit-action').classList.remove('is-hidden');\r\n\r\n window.vue.bShowSharePhoto = true;\r\n },\r\n\r\n performPhotoShare: function(asset, title, _public, description, keywords, type, result, button, error) {\r\n let origButtonHtml = button.innerHTML;\r\n button.innerHTML = ' ' + window.vue.loadingPleaseWait;\r\n\r\n window.vue.ajaxRequest('post', window.location.origin + '/share/photo/post', { asset: asset, title: title, public: _public, description: description, keywords: keywords, type: type }, function(response) {\r\n button.innerHTML = origButtonHtml;\r\n\r\n if (response.code == 200) {\r\n result.value = response.data.url;\r\n result.parentNode.parentNode.classList.remove('is-hidden');\r\n button.classList.add('is-hidden');\r\n error.classList.add('is-hidden');\r\n } else {\r\n error.innerHTML = response.msg;\r\n error.classList.remove('is-hidden');\r\n }\r\n });\r\n },\r\n\r\n generateNewToken: function(target, button) {\r\n let oldTxt = button.innerHTML;\r\n button.innerHTML = '';\r\n\r\n window.vue.ajaxRequest('post', window.location.origin + '/admin/cronjob/token', {}, function(response) {\r\n button.innerHTML = oldTxt;\r\n\r\n if (response.code == 200) {\r\n target.value = response.token;\r\n } else {\r\n alert(response.msg);\r\n }\r\n });\r\n },\r\n\r\n startBackup: function(button, plants, gallery, tasks, inventory, calendar) {\r\n let oldText = button.innerHTML;\r\n button.innerHTML = ' ' + oldText;\r\n\r\n window.vue.ajaxRequest('post', window.location.origin + '/export/start', {\r\n plants: plants,\r\n gallery: gallery,\r\n tasks: tasks,\r\n inventory: inventory,\r\n calendar: calendar\r\n }, function(response) {\r\n button.innerHTML = oldText;\r\n\r\n if (response.code == 200) {\r\n let export_result = document.getElementById('export-result');\r\n if (export_result) {\r\n export_result.classList.remove('is-hidden');\r\n\r\n export_result.children[1].href = response.file;\r\n export_result.children[1].innerHTML = response.file;\r\n }\r\n }\r\n });\r\n },\r\n\r\n startImport: function(button, file, locations, plants, gallery, tasks, inventory, calendar) {\r\n let oldText = button.innerHTML;\r\n button.innerHTML = ' ' + oldText;\r\n \r\n let formData = new FormData();\r\n formData.append('import', file.files[0]);\r\n formData.append('locations', ((locations) ? 1 : 0));\r\n formData.append('plants', ((plants) ? 1 : 0));\r\n formData.append('gallery', ((gallery) ? 1 : 0));\r\n formData.append('tasks', ((tasks) ? 1 : 0));\r\n formData.append('inventory', ((inventory) ? 1 : 0));\r\n formData.append('calendar', ((calendar) ? 1 : 0));\r\n\r\n window.vue.ajaxRequest('post', window.location.origin + '/import/start', formData, function(response) {\r\n button.innerHTML = oldText;\r\n\r\n if (response.code == 200) {\r\n let import_result = document.getElementById('import-result');\r\n if (import_result) {\r\n import_result.classList.remove('is-hidden');\r\n }\r\n }\r\n });\r\n },\r\n\r\n startThemeImport: function(file, button) {\r\n let oldText = button.innerHTML;\r\n button.innerHTML = ' ' + oldText;\r\n \r\n let formData = new FormData();\r\n formData.append('theme', file.files[0]);\r\n\r\n window.vue.ajaxRequest('post', window.location.origin + '/admin/themes/import', formData, function(response) {\r\n button.innerHTML = oldText;\r\n \r\n if (response.code == 200) {\r\n let import_result = document.getElementById('themes-import-result');\r\n if (import_result) {\r\n import_result.innerText = import_result.innerText.replace('{count}', response.themes.length);\r\n import_result.classList.remove('is-hidden');\r\n }\r\n } else {\r\n alert(response.msg);\r\n }\r\n });\r\n },\r\n\r\n removeTheme: function(theme) {\r\n window.vue.ajaxRequest('post', window.location.origin + '/admin/themes/remove', { theme: theme }, function(response) {\r\n if (response.code == 200) {\r\n let tableElem = document.getElementById('admin-themes-list-item-' + theme);\r\n if (tableElem) {\r\n tableElem.remove();\r\n }\r\n } else {\r\n alert(response.msg);\r\n }\r\n });\r\n },\r\n\r\n renderCalendar: function(elem, date_from, date_till = null) {\r\n window.vue.ajaxRequest('post', window.location.origin + '/calendar/query', { date_from: date_from, date_till: date_till }, function(response){\r\n if (response.code == 200) {\r\n let content = document.getElementById(elem);\r\n if (content) {\r\n let data = response.data;\r\n\r\n data.sort(function (a, b) {\r\n return new Date(a.date_from) - new Date(b.date_from);\r\n });\r\n\r\n const labels = data.map(x => {\r\n return [x.name];\r\n });\r\n\r\n const newData = data.map(x => {\r\n return [x.date_from.split(' ')[0], x.date_till.split(' ')[0], x.class_name, x.id, x.class_descriptor]\r\n });\r\n \r\n let colorsBackground = [];\r\n data.forEach(function(elem, index) {\r\n colorsBackground.push(elem.color_background);\r\n });\r\n \r\n let colorsBorder = [];\r\n data.forEach(function(elem, index) {\r\n colorsBorder.push(elem.color_border);\r\n });\r\n\r\n const config = {\r\n type: 'bar',\r\n data: {\r\n labels: labels,\r\n datasets: [\r\n {\r\n data: newData,\r\n backgroundColor: colorsBackground,\r\n borderColor: colorsBorder,\r\n borderWidth: 1,\r\n fill: false,\r\n barPercentage: 0.3\r\n }\r\n ]\r\n },\r\n options: {\r\n indexAxis: 'y',\r\n responsive: true,\r\n scales: {\r\n x: {\r\n min: response.date_from,\r\n max: response.date_till,\r\n type: 'time',\r\n time: {\r\n unit: 'day'\r\n }\r\n },\r\n y: {\r\n beginAtZero: true\r\n }\r\n },\r\n plugins: {\r\n legend: {\r\n display: false,\r\n },\r\n tooltip: {\r\n callbacks: {\r\n label: function(context) {\r\n return context.dataset.data[context.dataIndex][2];\r\n },\r\n afterBody: function(context) {\r\n return context[0].raw[0] + ' - ' + context[0].raw[1];\r\n }\r\n }\r\n }\r\n },\r\n\r\n }\r\n };\r\n\r\n if (window.calendarChart !== null) {\r\n window.calendarChart.destroy();\r\n }\r\n \r\n window.calendarChart = new chart_js_auto__WEBPACK_IMPORTED_MODULE_5__[\"default\"](\r\n content,\r\n config\r\n );\r\n\r\n content.onclick = function(event) {\r\n let points = window.calendarChart.getElementsAtEventForMode(event, 'nearest', { intersect: true }, true);\r\n if (points.length) {\r\n const firstPoint = points[0];\r\n const label = window.calendarChart.data.labels[firstPoint.index];\r\n const value = window.calendarChart.data.datasets[firstPoint.datasetIndex].data[firstPoint.index];\r\n console.log(value);\r\n if (value.length) {\r\n document.getElementById('inpEditCalendarItemIdent').innerText = '#' + value[3] + ' ' + label;\r\n document.getElementById('inpEditCalendarItemId').value = value[3];\r\n document.getElementById('inpEditCalendarItemName').value = label;\r\n document.getElementById('inpEditCalendarItemDateFrom').value = value[0];\r\n document.getElementById('inpEditCalendarItemDateTill').value = value[1];\r\n document.getElementById('inpEditCalendarItemClass').value = value[4];\r\n window.vue.bShowEditCalendarItem = true;\r\n }\r\n }\r\n };\r\n }\r\n } else {\r\n alert(response.msg);\r\n }\r\n });\r\n },\r\n\r\n removeCalendarItem: function(ident) {\r\n window.vue.ajaxRequest('post', window.location.origin + '/calendar/remove', { ident: ident }, function(response) {\r\n if (response.code == 200) {\r\n location.reload();\r\n } else {\r\n alert(response.msg);\r\n }\r\n });\r\n },\r\n\r\n removeCalendarClass: function(id) {\r\n window.vue.ajaxRequest('post', window.location.origin + '/admin/calendar/class/remove', { id: id }, function(response) {\r\n if (response.code == 200) {\r\n let elItem = document.getElementById('admin-calendar-class-item-' + id);\r\n if (elItem) {\r\n elItem.remove();\r\n }\r\n } else {\r\n alert(response.msg);\r\n }\r\n });\r\n },\r\n\r\n clonePlant: function(id) {\r\n window.vue.ajaxRequest('post', window.location.origin + '/plants/clone', { id: id }, function(response) {\r\n if (response.code == 200) {\r\n location.href = window.location.origin + '/plants/details/' + response.clone_id;\r\n } else {\r\n alert(response.msg);\r\n }\r\n });\r\n },\r\n\r\n showPerformBulkUpdate: function(operation, title, button, location, is_custom = false, datatype = 'datetime') {\r\n document.getElementById('plant-bulk-perform-operation-operation').value = operation;\r\n document.getElementById('plant-bulk-perform-operation-location').value = location;\r\n document.getElementById('plant-bulk-perform-operation-title').innerText = title;\r\n document.getElementById('plant-bulk-perform-operation-button').innerText = button;\r\n document.getElementById('plant-bulk-perform-operation-custom').checked = is_custom;\r\n document.getElementById('plant-bulk-perform-operation-datatype').value = datatype;\r\n\r\n if (document.getElementById('plant-bulk-perform-operation-bulkvalue').tagName.toLowerCase() === 'select') {\r\n let parentElem = document.getElementById('plant-bulk-perform-operation-bulkvalue').parentElement;\r\n parentElem.innerHTML = ``;\r\n }\r\n\r\n if (datatype === 'datetime') {\r\n let curDate = new Date();\r\n document.getElementById('plant-bulk-perform-operation-bulkvalue').type = 'date';\r\n document.getElementById('plant-bulk-perform-operation-bulkvalue').value = curDate.toISOString().split('T')[0];\r\n } else if (datatype == 'string') {\r\n document.getElementById('plant-bulk-perform-operation-bulkvalue').type = 'text';\r\n document.getElementById('plant-bulk-perform-operation-bulkvalue').value = '';\r\n } else if (datatype == 'int') {\r\n document.getElementById('plant-bulk-perform-operation-bulkvalue').type = 'number';\r\n document.getElementById('plant-bulk-perform-operation-bulkvalue').value = 0;\r\n } else if (datatype == 'double') {\r\n document.getElementById('plant-bulk-perform-operation-bulkvalue').type = 'text';\r\n document.getElementById('plant-bulk-perform-operation-bulkvalue').value = '0.0';\r\n } else if (datatype == 'bool') {\r\n document.getElementById('plant-bulk-perform-operation-bulkvalue').type = 'number';\r\n document.getElementById('plant-bulk-perform-operation-bulkvalue').value = 0;\r\n } else {\r\n document.getElementById('plant-bulk-perform-operation-bulkvalue').type = 'text';\r\n document.getElementById('plant-bulk-perform-operation-bulkvalue').value = '';\r\n }\r\n \r\n window.vue.bulkChecked('plant-bulk-perform-operation', false);\r\n\r\n window.vue.bShowPlantBulkPerformUpdate = true;\r\n },\r\n\r\n bulkPerformPlantUpdate: function(target, attribute, location, bulkvalue, is_custom = false, bulktype = 'datetime') {\r\n let plantIds = [];\r\n\r\n let elems = document.getElementsByClassName(target);\r\n if (elems) {\r\n Array.prototype.forEach.call(elems, function(elem) {\r\n if (elem.checked) {\r\n plantIds.push([elem.dataset.plantid, elem.dataset.plantname]);\r\n }\r\n });\r\n \r\n if (plantIds.length > 0) {\r\n window.vue.ajaxRequest('post', window.location.origin + '/plants/update/bulk', { attribute: attribute, list: JSON.stringify(plantIds), location: location, bulkvalue: bulkvalue, bulktype: bulktype, custom: is_custom }, function(response) {\r\n if (response.code == 200) {\r\n alert(window.vue.operationSucceeded);\r\n window.vue.bShowPlantBulkPerformUpdate = false;\r\n } else {\r\n alert(response.msg);\r\n }\r\n });\r\n } else {\r\n alert(window.vue.noListItemsSelected); \r\n }\r\n }\r\n },\r\n\r\n setBulkComboValues: function(list) {\r\n let parentElement = document.getElementById('plant-bulk-perform-operation-bulkvalue').parentElement;\r\n \r\n let listitems = '';\r\n\r\n list.forEach(function(elem, index) {\r\n listitems += '';\r\n });\r\n\r\n parentElement.innerHTML = ``;\r\n },\r\n\r\n generateAndShowQRCode: function(plant) {\r\n window.vue.ajaxRequest('get', window.location.origin + '/plants/qrcode?plant=' + plant, {}, function(response) {\r\n if (response.code == 200) {\r\n let elTarget = document.getElementById('image-plant-qr-code');\r\n if (elTarget) {\r\n elTarget.src = response.qrcode;\r\n window.vue.bShowPlantQRCode = true;\r\n }\r\n } else {\r\n alert(response.msg);\r\n }\r\n });\r\n },\r\n\r\n printQRCode: function(content, title) {\r\n let wnd = window.open('', title, 'height=auto, width=auto');\r\n\r\n wnd.document.write('' + title + '');\r\n wnd.document.write('');\r\n wnd.document.write('');\r\n\r\n wnd.print();\r\n wnd.close();\r\n },\r\n\r\n bulkChecked: function(target, flag) {\r\n let elems = document.getElementsByClassName(target);\r\n \r\n if (elems) {\r\n Array.prototype.forEach.call(elems, function(elem){ \r\n elem.checked = flag; \r\n });\r\n }\r\n },\r\n\r\n bulkPrintQRCodes: function(target, location) {\r\n let plantIds = [];\r\n\r\n let elems = document.getElementsByClassName(target);\r\n if (elems) {\r\n Array.prototype.forEach.call(elems, function(elem) {\r\n if (elem.checked) {\r\n plantIds.push([elem.dataset.plantid, elem.dataset.plantname]);\r\n }\r\n });\r\n\r\n if (plantIds.length > 0) {\r\n window.vue.ajaxRequest('post', window.location.origin + '/plants/qrcode/bulk', { list: JSON.stringify(plantIds) }, function(response) {\r\n if (response.code == 200) {\r\n let wnd = window.open('', location, 'height=auto, width=auto');\r\n\r\n wnd.document.write('' + location + '');\r\n\r\n response.list.forEach(function(elem, index) {\r\n wnd.document.write('
#' + elem.plantid + ' ' + elem.plantname + '
');\r\n });\r\n\r\n wnd.document.write('');\r\n\r\n wnd.print();\r\n wnd.close();\r\n } else {\r\n alert(response.msg);\r\n }\r\n });\r\n } else {\r\n alert(window.vue.noListItemsSelected); \r\n }\r\n }\r\n },\r\n\r\n queryInvQrCode: function(item) {\r\n window.vue.ajaxRequest('get', window.location.origin + '/inventory/qrcode?item=' + item, {}, function(response) {\r\n if (response.code == 200) {\r\n let elTarget = document.getElementById('image-inventory-qr-code');\r\n if (elTarget) {\r\n elTarget.src = response.qrcode;\r\n window.vue.bShowInvItemQRCode = true;\r\n }\r\n } else {\r\n alert(response.msg);\r\n }\r\n });\r\n },\r\n\r\n bulkPrintInvQRCodes: function(target, title) {\r\n let invIds = [];\r\n\r\n let elems = document.getElementsByClassName(target);\r\n if (elems) {\r\n Array.prototype.forEach.call(elems, function(elem) {\r\n if (elem.checked) {\r\n invIds.push([elem.dataset.invitemid, elem.dataset.invitemname, elem.dataset.invgroup]);\r\n }\r\n });\r\n\r\n if (invIds.length > 0) {\r\n window.vue.ajaxRequest('post', window.location.origin + '/inventory/qrcode/bulk', { list: JSON.stringify(invIds) }, function(response) {\r\n if (response.code == 200) {\r\n let wnd = window.open('', title, 'height=auto, width=auto');\r\n\r\n wnd.document.write('' + title + '');\r\n\r\n response.list.forEach(function(elem, index) {\r\n wnd.document.write('
#' + elem.invitemid + ' [' + elem.invgroup + '] ' + elem.invitemname + '
');\r\n });\r\n\r\n wnd.document.write('');\r\n\r\n wnd.print();\r\n wnd.close();\r\n } else {\r\n alert(response.msg);\r\n }\r\n });\r\n } else {\r\n alert(window.vue.noListItemsSelected); \r\n }\r\n }\r\n },\r\n\r\n bulkExportInventory: function(target, format, title) {\r\n let invIds = [];\r\n\r\n let elems = document.getElementsByClassName(target);\r\n if (elems) {\r\n Array.prototype.forEach.call(elems, function(elem) {\r\n if (elem.checked) {\r\n invIds.push([elem.dataset.invitemid, elem.dataset.invitemname, document.getElementById(elem.dataset.invdescription).innerText, elem.dataset.invgroup, elem.dataset.invamount, elem.dataset.invlocation, elem.dataset.invphoto, elem.dataset.invcreated, elem.dataset.invupdated]);\r\n }\r\n });\r\n\r\n if (invIds.length > 0) {\r\n window.vue.ajaxRequest('post', window.location.origin + '/inventory/export', { list: JSON.stringify(invIds), format: document.getElementById(format).value }, function(response) {\r\n if (response.code == 200) {\r\n const dlanchor = document.createElement('a');\r\n dlanchor.href = response.resource;\r\n dlanchor.target = '_blank';\r\n dlanchor.setAttribute('download', response.resource);\r\n dlanchor.click();\r\n } else {\r\n alert(response.msg);\r\n }\r\n });\r\n } else {\r\n alert(window.vue.noListItemsSelected); \r\n }\r\n }\r\n },\r\n\r\n editGalleryPhotoLabel: function(id, plant, old) {\r\n let newLabel = prompt(window.vue.editProperty, old);\r\n if (newLabel.length) {\r\n window.vue.ajaxRequest('post', window.location.origin + '/plants/details/gallery/photo/label/edit', { id: id, label: newLabel, plant: plant }, function(response) {\r\n if (response.code == 200) {\r\n document.getElementById('photo-gallery-item-' + id).children[0].children[0].innerHTML = newLabel;\r\n } else {\r\n alert(response.msg);\r\n }\r\n });\r\n }\r\n },\r\n\r\n setGalleryPhotoAsMain: function(id, plant) {\r\n let query = confirm(window.vue.confirmSetGalleryPhotoAsMain);\r\n if (!query) {\r\n return;\r\n }\r\n\r\n window.vue.ajaxRequest('post', window.location.origin + '/plants/details/gallery/photo/setmain', { id: id, plant: plant }, function(response) {\r\n if (response.code == 200) {\r\n location.href = window.location.origin + '/plants/details/' + plant;\r\n } else {\r\n alert(response.msg);\r\n }\r\n });\r\n },\r\n\r\n removeSharedPhoto: function(ident) {\r\n window.vue.ajaxRequest('get', window.location.origin + '/share/photo/remove?ident=' + ident, {}, function(response) {\r\n if (response.code == 200) {\r\n let elem = document.getElementById('photo-share-entry-' + ident);\r\n if (elem) {\r\n elem.remove();\r\n }\r\n } else {\r\n alert(response.msg);\r\n }\r\n });\r\n },\r\n\r\n loadNextShareLogEntries: function(table, action) {\r\n window.vue.ajaxRequest('post', window.location.origin + '/profile/sharelog/fetch', { paginate: action.dataset.paginate }, function(response) {\r\n if (response.code == 200) {\r\n let tbody = table.getElementsByTagName('tbody')[0];\r\n\r\n response.data.forEach(function(elem, index) {\r\n let newRow = document.createElement('tr');\r\n newRow.id = 'photo-share-entry-' + elem.id;\r\n newRow.innerHTML = `\r\n ` + elem.title + `\r\n ` + elem.diffForHumans + `\r\n \r\n `;\r\n\r\n tbody.appendChild(newRow);\r\n });\r\n\r\n action.parentNode.parentNode.remove();\r\n\r\n let actionRow = document.createElement('tr');\r\n actionRow.id = 'share-log-load-more';\r\n actionRow.classList.add('share-log-paginate');\r\n actionRow.innerHTML = `` + window.vue.loadMore + ``;\r\n tbody.appendChild(actionRow);\r\n } else {\r\n alert(response.msg);\r\n }\r\n });\r\n },\r\n\r\n acquireGeoPosition: function(destLatitude, destLongitude, button) {\r\n let oldText = button.innerHTML;\r\n button.innerHTML = ' ' + button.innerHTML;\r\n\r\n if (navigator.geolocation) {\r\n navigator.geolocation.getCurrentPosition(function(position) {\r\n destLatitude.value = position.coords.latitude;\r\n destLongitude.value = position.coords.longitude;\r\n\r\n button.innerHTML = oldText;\r\n });\r\n } else {\r\n button.innerHTML = oldText;\r\n \r\n alert('Geolocation is not available');\r\n }\r\n },\r\n\r\n toggleApiKey: function(id) {\r\n window.vue.ajaxRequest('get', window.location.origin + '/admin/api/' + id + '/toggle', {}, function(response) {\r\n if (response.code == 200) {\r\n document.getElementById('api-key-checkbox-' + id).checked = response.active;\r\n } else {\r\n alert(response.msg);\r\n }\r\n });\r\n },\r\n\r\n toggleAdminPlantAttribute: function(name) {\r\n window.vue.ajaxRequest('get', window.location.origin + '/admin/attribute/update?name=' + name, {}, function(response) {\r\n if (response.code == 200) {\r\n document.getElementById('admin-attributes-checkbox-' + name).checked = response.active;\r\n } else {\r\n alert(response.msg);\r\n }\r\n });\r\n },\r\n\r\n toggleAdminBoolSetting: function(name) {\r\n window.vue.ajaxRequest('get', window.location.origin + '/admin/environment/boolean/toggle?name=' + name, {}, function(response) {\r\n if (response.code == 200) {\r\n document.getElementById('admin-attributes-checkbox-allow-custom-attributes').checked = response.value;\r\n } else {\r\n alert(response.msg);\r\n }\r\n });\r\n },\r\n\r\n toggleAdminAuthInfoMessages: function(checked, warning, caution) {\r\n let elWarning = document.querySelector(warning);\r\n let elCaution = document.querySelector(caution);\r\n\r\n if (checked) {\r\n if (elWarning) {\r\n elWarning.style.display = 'block';\r\n }\r\n\r\n if (elCaution) {\r\n elCaution.style.display = 'block';\r\n }\r\n } else {\r\n if (elWarning) {\r\n elWarning.style.display = 'none';\r\n }\r\n\r\n if (elCaution) {\r\n elCaution.style.display = 'none';\r\n }\r\n }\r\n },\r\n\r\n performPlantRecognition: function(target, plantid) {\r\n const form = document.getElementById(target);\r\n const data = new FormData(form);\r\n\r\n window.vue.ajaxRequest('post', window.location.origin + '/plants/details/identify', data, function(response) {\r\n if (response.code == 200) {\r\n let dest = document.getElementById('recognized-plant-selection');\r\n\r\n dest.innerHTML = '
';\r\n\r\n response.data.forEach(function(elem, index) {\r\n dest.innerHTML += `\r\n
\r\n
\r\n  ` + elem.species.scientificNameWithoutAuthor + ` (` + (elem.score * 100).toFixed(2) + '%)' + `\r\n
\r\n
\r\n `;\r\n });\r\n\r\n dest.innerHTML += '
';\r\n\r\n document.getElementById('plant-rec-action-icon').classList.remove('fa-spinner');\r\n document.getElementById('plant-rec-action-icon').classList.remove('fa-spin');\r\n document.getElementById('plant-rec-action-icon').classList.add('fa-microscope');\r\n\r\n window.vue.bShowSelectRecognizedPlant = true;\r\n } else {\r\n alert(response.msg);\r\n }\r\n });\r\n },\r\n\r\n storeRecognizedPlantData: function(target, update_name, update_scientific_name) {\r\n const selection = document.getElementById(target).getElementsByTagName('input');\r\n\r\n for (let i = 0; i < selection.length; i++) {\r\n if (selection[i].checked) {\r\n const item = selection[i];\r\n\r\n window.plantRecStorageStep = 0;\r\n window.plantRecErrorCount = 0;\r\n\r\n if (update_name) {\r\n window.vue.ajaxRequest('post', window.location.origin + '/plants/details/edit/ajax', {\r\n plant: item.dataset.plantid,\r\n attribute: 'name',\r\n value: item.dataset.plantname\r\n }, function(response) {\r\n window.plantRecStorageStep++;\r\n \r\n if (response.code == 500) {\r\n window.plantRecErrorCount++;\r\n alert(response.msg);\r\n }\r\n });\r\n } else {\r\n window.plantRecStorageStep++;\r\n }\r\n\r\n if (update_scientific_name) {\r\n window.vue.ajaxRequest('post', window.location.origin + '/plants/details/edit/ajax', {\r\n plant: item.dataset.plantid,\r\n attribute: 'scientific_name',\r\n value: item.dataset.plantscientificname\r\n }, function(response) {\r\n window.plantRecStorageStep++;\r\n\r\n if (response.code == 500) {\r\n window.plantRecErrorCount++;\r\n alert(response.msg);\r\n }\r\n });\r\n } else {\r\n window.plantRecStorageStep++;\r\n }\r\n\r\n setTimeout(function awaitPlantAttributeStorage() {\r\n if (window.plantRecStorageStep < 2) {\r\n setTimeout(awaitPlantAttributeStorage, 1000);\r\n } else {\r\n location.reload();\r\n }\r\n }, 100);\r\n\r\n break;\r\n }\r\n }\r\n },\r\n\r\n recognizedPlantsGroupSelectionValid: function(group, type) {\r\n let elems = document.getElementById(group).querySelectorAll('input[type=\"' + type + '\"]');\r\n\r\n for (let i = 0; i < elems.length; i++) {\r\n if (elems[i].checked) {\r\n return true;\r\n }\r\n }\r\n\r\n return false;\r\n },\r\n\r\n allRecognizedPlantSelectionGroupsValid: function() {\r\n return (window.vue.recognizedPlantsGroupSelectionValid('recognized-plant-selection', 'radio')) && (window.vue.recognizedPlantsGroupSelectionValid('plants-attribute-selection', 'checkbox'));\r\n },\r\n\r\n quickPlantRecognition: function(target, actionIcon, destContent) {\r\n const form = document.getElementById(target);\r\n const data = new FormData(form);\r\n\r\n window.vue.ajaxRequest('post', window.location.origin + '/plants/details/identify', data, function(response) {\r\n if (response.code == 200) {\r\n let dest = document.getElementById(destContent);\r\n\r\n dest.innerHTML = '
';\r\n\r\n response.data.forEach(function(elem, index) {\r\n dest.innerHTML += `\r\n
\r\n
\r\n
` + elem.species.scientificNameWithoutAuthor + ` (` + (elem.score * 100).toFixed(2) + '%)' + `
\r\n
\r\n
\r\n `;\r\n });\r\n\r\n dest.innerHTML += '
';\r\n\r\n document.getElementById(actionIcon).classList.remove('fa-spinner');\r\n document.getElementById(actionIcon).classList.remove('fa-spin');\r\n document.getElementById(actionIcon).classList.add('fa-microscope');\r\n\r\n window.vue.bShowQuickScanPlant = true;\r\n } else {\r\n alert(response.msg);\r\n }\r\n });\r\n },\r\n\r\n saveLocationNotes: function(location, notes, reselem) {\r\n let elem = document.getElementById(notes);\r\n if (elem) {\r\n let elNotes = document.getElementById(notes);\r\n\r\n window.vue.ajaxRequest('post', window.location.origin + '/plants/location/' + location + '/notes/save', { notes: elNotes.value }, function(response) {\r\n if (response.code == 200) {\r\n let elResult = document.getElementById(reselem);\r\n if (elResult) {\r\n elResult.innerHTML = '';\r\n }\r\n } else {\r\n alert(response.msg);\r\n }\r\n });\r\n }\r\n },\r\n\r\n validateAndSubmitForm: function(form, button) {\r\n let origtext = button.innerHTML;\r\n button.innerHTML = ' ' + window.vue.loading_please_wait; \r\n\r\n if (form.checkValidity()) {\r\n form.submit();\r\n } else {\r\n form.reportValidity();\r\n button.innerHTML = origtext;\r\n }\r\n },\r\n\r\n fixQuickScanPos: function(pwa = false) {\r\n let quickscanwidget = document.querySelector('.quickscan');\r\n if (quickscanwidget) {\r\n quickscanwidget.style.bottom = '12px';\r\n\r\n if ((pwa) && (window.innerWidth <= 1089)) {\r\n quickscanwidget.style.bottom = '83px';\r\n }\r\n }\r\n },\r\n\r\n clearCache: function(button) {\r\n window.vue.ajaxRequest('post', window.location.origin + '/admin/cache/clear', {}, function(response) {\r\n if (response.code == 200) {\r\n button.innerHTML = ' ' + button.innerHTML;\r\n button.setAttribute('disabled', 'disabled');\r\n } else {\r\n alert(response.msg);\r\n }\r\n });\r\n },\r\n\r\n scrollTo: function(target) {\r\n let elem = document.querySelector(target);\r\n if (elem) {\r\n elem.scrollIntoView({ behavior: 'smooth' });\r\n }\r\n },\r\n\r\n copyToClipboard: function(text) {\r\n const el = document.createElement('textarea');\r\n el.value = text;\r\n document.body.appendChild(el);\r\n el.select();\r\n document.execCommand('copy');\r\n document.body.removeChild(el);\r\n alert(window.vue.copiedToClipboard);\r\n },\r\n\r\n isProgressiveWebApp: function() {\r\n return window.matchMedia('(display-mode: standalone)').matches;\r\n },\r\n }\r\n});\n\n//# sourceURL=webpack://asatruphp/./app/resources/js/app.js?\n}"); +eval("{__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _sass_app_scss__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./../sass/app.scss */ \"./app/resources/sass/app.scss\");\n/* harmony import */ var _sass_app_scss__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_sass_app_scss__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var fontawesome_free_scss_fontawesome_scss__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! fontawesome-free/scss/fontawesome.scss */ \"./node_modules/fontawesome-free/scss/fontawesome.scss\");\n/* harmony import */ var fontawesome_free_scss_fontawesome_scss__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(fontawesome_free_scss_fontawesome_scss__WEBPACK_IMPORTED_MODULE_1__);\n/* harmony import */ var fontawesome_free_js_all_min_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! fontawesome-free/js/all.min.js */ \"./node_modules/fontawesome-free/js/all.min.js\");\n/* harmony import */ var fontawesome_free_js_all_min_js__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(fontawesome_free_js_all_min_js__WEBPACK_IMPORTED_MODULE_2__);\n/* harmony import */ var fontawesome_free_js_solid_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! fontawesome-free/js/solid.js */ \"./node_modules/fontawesome-free/js/solid.js\");\n/* harmony import */ var fontawesome_free_js_solid_js__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(fontawesome_free_js_solid_js__WEBPACK_IMPORTED_MODULE_3__);\n/* harmony import */ var fontawesome_free_js_brands_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! fontawesome-free/js/brands.js */ \"./node_modules/fontawesome-free/js/brands.js\");\n/* harmony import */ var fontawesome_free_js_brands_js__WEBPACK_IMPORTED_MODULE_4___default = /*#__PURE__*/__webpack_require__.n(fontawesome_free_js_brands_js__WEBPACK_IMPORTED_MODULE_4__);\n/* harmony import */ var chart_js_auto__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! chart.js/auto */ \"./node_modules/chart.js/auto/auto.js\");\n/* harmony import */ var chartjs_adapter_date_fns__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! chartjs-adapter-date-fns */ \"./node_modules/chartjs-adapter-date-fns/dist/chartjs-adapter-date-fns.esm.js\");\n/**\r\n * app.js\r\n * \r\n * Put here your application specific JavaScript implementations\r\n */\r\n\r\n\r\n\r\nwindow.axios = __webpack_require__(/*! axios */ \"./node_modules/axios/dist/browser/axios.cjs\");\r\nwindow.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\nwindow.constChatMessageQueryRefreshRate = 1000 * 15;\r\nwindow.constChatUserListRefreshRate = 1000 * 15;\r\nwindow.constChatTypingRefreshRate = 2000;\r\n\r\nwindow.vue = new Vue({\r\n el: '#app',\r\n\r\n data: {\r\n bShowAddPlant: false,\r\n bShowEditText: false,\r\n bShowEditMultilineText: false,\r\n bShowEditBoolean: false,\r\n bShowEditInteger: false,\r\n bShowEditDate: false,\r\n bShowEditCombo: false,\r\n bShowEditPhoto: false,\r\n bShowEPUrl: false,\r\n bShowEditLinkText: false,\r\n bShowUploadPhoto: false,\r\n bShowSetPhotoURL: false,\r\n bShowCreateTask: false,\r\n bShowEditTask: false,\r\n bShowEditPreferences: false,\r\n bShowAddInventoryItem: false,\r\n bShowEditInventoryItem: false,\r\n bShowInvItemQRCode: false,\r\n bShowInventoryBulkPrint: false,\r\n bShowInventoryExport: false,\r\n bShowManageGroups: false,\r\n bShowRestorePassword: false,\r\n bShowCreateNewUser: false,\r\n bShowCreateNewLocation: false,\r\n bShowRemoveLocation: false,\r\n bShowPreviewImageModal: false,\r\n bShowSharePhoto: false,\r\n bShowAddFirstLocation: false,\r\n bShowAddCalendarItem: false,\r\n bShowEditCalendarItem: false,\r\n bShowCreateNewCalendarClass: false,\r\n bShowPlantQRCode: false,\r\n bShowPlantBulkPerformUpdate: false,\r\n bShowPlantBulkPrint: false,\r\n bShowAddCustomPlantAttribute: false,\r\n bShowEditCustomPlantAttribute: false,\r\n bShowCreateNewAttributeSchema: false,\r\n bShowAddPlantLogEntry: false,\r\n bShowEditPlantLogEntry: false,\r\n bShowAddLocationLogEntry: false,\r\n bShowEditLocationLogEntry: false,\r\n bShowCreateNewBulkCmd: false,\r\n bShowSelectRecognizedPlant: false,\r\n bShowQuickScanPlant: false,\r\n clsLastImagePreviewAspect: '',\r\n comboLocation: [],\r\n comboCuttingMonth: [],\r\n comboLightLevel: [],\r\n comboHealthState: [],\r\n loading_please_wait: 'Please wait...',\r\n confirmPhotoRemoval: 'Are you sure you want to remove this photo?',\r\n confirmPlantRemoval: 'Are you sure you want to remove this plant?',\r\n confirmSetAllWatered: 'Are you sure you want to update the last watered date of all these plants?',\r\n confirmSetAllRepotted: 'Are you sure you want to update the last repotted date of all these plants?',\r\n confirmSetAllFertilised: 'Are you sure you want to update the last fertilised date of all these plants?',\r\n confirmInventoryItemRemoval: 'Are you sure you want to remove this item?',\r\n confirmPlantAddHistory: 'Please confirm if you want to do this action.',\r\n confirmPlantRemoveHistory: 'Please confirm if you want to do this action.',\r\n confirmRemovePlantLogEntry: 'Do you really want to remove this entry?',\r\n confirmRemoveLocationLogEntry: 'Do you really want to remove this entry?',\r\n confirmRemoveSharedPlantPhoto: 'Do you really want to remove this item?',\r\n confirmSetGalleryPhotoAsMain: 'Do you want to replace the main photo with this one?',\r\n addItem: 'Add',\r\n newChatMessage: 'New',\r\n currentlyOnline: 'Currently online: ',\r\n loadingPleaseWait: 'Please wait...',\r\n noListItemsSelected: 'No items selected',\r\n editProperty: 'Edit property',\r\n loadMore: 'Load more',\r\n operationSucceeded: 'Operation succeeded',\r\n copiedToClipboard: 'Content has been copied to clipboard.',\r\n origTestMailButtonContent: '',\r\n chatTypingEnable: false,\r\n chatTypingTimer: null,\r\n chatTypingHide: null,\r\n chatTypingCounter: 1\r\n },\r\n\r\n methods: {\r\n ajaxRequest: function (method, url, data = {}, successfunc = function(data){}, finalfunc = function(){}, config = {})\r\n {\r\n let func = window.axios.get;\r\n if (method == 'post') {\r\n func = window.axios.post;\r\n } else if (method == 'patch') {\r\n func = window.axios.patch;\r\n } else if (method == 'delete') {\r\n func = window.axios.delete;\r\n }\r\n\r\n func(url, data, config)\r\n .then(function(response){\r\n successfunc(response.data);\r\n })\r\n .catch(function (error) {\r\n console.log(error);\r\n })\r\n .finally(function(){\r\n finalfunc();\r\n }\r\n );\r\n },\r\n\r\n initNavBar: function()\r\n {\r\n const $navbarBurgers = Array.prototype.slice.call(document.querySelectorAll('.navbar-burger'), 0);\r\n\r\n if ($navbarBurgers.length > 0) {\r\n $navbarBurgers.forEach( el => {\r\n el.addEventListener('click', () => {\r\n const target = el.dataset.target;\r\n const $target = document.getElementById(target);\r\n\r\n el.classList.toggle('is-active');\r\n $target.classList.toggle('is-active');\r\n });\r\n });\r\n }\r\n },\r\n\r\n showEditText: function(plant, property, defval, anchor = '')\r\n {\r\n document.getElementById('inpEditTextPlantId').value = plant;\r\n document.getElementById('inpEditTextAttribute').value = property;\r\n document.getElementById('inpEditTextValue').value = defval;\r\n document.getElementById('inpEditTextAnchor').value = anchor;\r\n window.vue.bShowEditText = true;\r\n },\r\n\r\n showEditMultilineText: function(plant, property, defval, anchor = '')\r\n {\r\n document.getElementById('inpEditMultilineTextPlantId').value = plant;\r\n document.getElementById('inpEditMultilineTextAttribute').value = property;\r\n document.getElementById('inpEditMultilineTextValue').value = defval;\r\n document.getElementById('inpEditMultilineTextAnchor').value = anchor;\r\n window.vue.bShowEditMultilineText = true;\r\n },\r\n\r\n showEditBoolean: function(plant, property, hint, defval)\r\n {\r\n document.getElementById('inpEditBooleanPlantId').value = plant;\r\n document.getElementById('inpEditBooleanAttribute').value = property;\r\n document.getElementById('property-hint').innerHTML = hint;\r\n\r\n if (defval) {\r\n document.getElementById('inpEditBooleanValue_yes').checked = true;\r\n } else {\r\n document.getElementById('inpEditBooleanValue_no').checked = true;\r\n }\r\n \r\n window.vue.bShowEditBoolean = true;\r\n },\r\n\r\n showEditInteger: function(plant, property, defval)\r\n {\r\n document.getElementById('inpEditIntegerPlantId').value = plant;\r\n document.getElementById('inpEditIntegerAttribute').value = property;\r\n document.getElementById('inpEditIntegerValue').value = defval;\r\n window.vue.bShowEditInteger = true;\r\n },\r\n\r\n showEditDate: function(plant, property, defval)\r\n {\r\n document.getElementById('inpEditDatePlantId').value = plant;\r\n document.getElementById('inpEditDateAttribute').value = property;\r\n document.getElementById('inpEditDateValue').value = defval;\r\n window.vue.bShowEditDate = true;\r\n },\r\n\r\n showEditCombo: function(plant, property, combo, defval)\r\n {\r\n document.getElementById('inpEditComboPlantId').value = plant;\r\n document.getElementById('inpEditComboAttribute').value = property;\r\n \r\n if (typeof combo !== 'object') {\r\n console.error('Invalid combo specified');\r\n return;\r\n }\r\n\r\n let sel = document.getElementById('selEditCombo');\r\n if (sel) {\r\n for (let i = sel.options.length - 1; i >= 0; i--) {\r\n sel.remove(i);\r\n }\r\n\r\n combo.forEach(function(elem, index){\r\n let opt = document.createElement('option');\r\n opt.value = elem.ident;\r\n opt.text = elem.label;\r\n sel.add(opt);\r\n });\r\n }\r\n\r\n document.getElementById('selEditCombo').value = defval;\r\n\r\n window.vue.bShowEditCombo = true;\r\n },\r\n\r\n showEditLinkText: function(plant, text, link)\r\n {\r\n document.getElementById('inpEditLinkTextPlantId').value = plant;\r\n document.getElementById('inpEditLinkTextValue').value = text;\r\n document.getElementById('inpEditLinkTextLink').value = link;\r\n window.vue.bShowEditLinkText = true;\r\n },\r\n\r\n selectDataTypeInputField: function(elem, field) {\r\n if (elem.selectedIndex <= 0) {\r\n return;\r\n }\r\n \r\n field.classList.remove('is-hidden');\r\n\r\n if (!field.children[1].children[0].classList.contains('is-hidden')) {\r\n field.children[1].children[0].classList.add('is-hidden');\r\n }\r\n\r\n if (!field.children[1].children[1].classList.contains('is-hidden')) {\r\n field.children[1].children[1].classList.add('is-hidden');\r\n }\r\n\r\n if (!field.children[1].children[2].classList.contains('is-hidden')) {\r\n field.children[1].children[2].classList.add('is-hidden');\r\n }\r\n\r\n field.children[1].children[0].children[0].children[1].children[0].children[0].disabled = true;\r\n field.children[1].children[0].children[0].children[2].children[0].children[0].disabled = true;\r\n field.children[1].children[1].disabled = true;\r\n field.children[1].children[2].disabled = true;\r\n\r\n if (elem.value === 'bool') {\r\n field.children[1].children[0].classList.remove('is-hidden');\r\n field.children[1].children[0].children[0].children[1].children[0].children[0].disabled = false;\r\n field.children[1].children[0].children[0].children[2].children[0].children[0].disabled = false;\r\n } else if (elem.value === 'datetime') {\r\n field.children[1].children[2].classList.remove('is-hidden');\r\n field.children[1].children[2].disabled = false;\r\n } else {\r\n field.children[1].children[1].classList.remove('is-hidden');\r\n field.children[1].children[1].disabled = false;\r\n }\r\n },\r\n\r\n showEditCustomPlantAttribute: function(id, plant, label, datatype, content, is_global = false)\r\n {\r\n document.getElementById('edit-plant-attribute-attr').value = id;\r\n document.getElementById('edit-plant-attribute-plant').value = plant;\r\n document.getElementById('edit-plant-attribute-label').value = label;\r\n document.getElementById('edit-plant-attribute-datatype').value = datatype;\r\n\r\n let elFieldTarget = document.getElementById('field-custom-edit-attribute-content');\r\n\r\n if (datatype === 'bool') {\r\n if (content == 1) {\r\n elFieldTarget.children[1].children[0].children[0].children[1].children[0].children[0].checked = true;\r\n } else {\r\n elFieldTarget.children[1].children[0].children[0].children[2].children[0].children[0].checked = true;\r\n }\r\n } else if (datatype === 'datetime') {\r\n elFieldTarget.children[1].children[2].value = content;\r\n } else {\r\n elFieldTarget.children[1].children[1].value = content;\r\n }\r\n\r\n window.vue.selectDataTypeInputField(document.querySelector('#edit-plant-attribute-datatype'), elFieldTarget);\r\n\r\n if (is_global) {\r\n document.getElementById('field-custom-edit-attribute-datatype').style.display = 'none';\r\n document.getElementById('plant-custom-attribute-removal-field').style.display = 'none';\r\n } else {\r\n document.getElementById('field-custom-edit-attribute-datatype').style.display = 'inherit';\r\n document.getElementById('plant-custom-attribute-removal-field').style.display = 'inherit';\r\n }\r\n\r\n window.vue.bShowEditCustomPlantAttribute = true;\r\n },\r\n\r\n removeCustomPlantAttribute: function(id, target)\r\n {\r\n window.vue.ajaxRequest('post', window.location.origin + '/plants/attributes/remove?id=' + id, {}, function(response){\r\n if (response.code == 200) {\r\n let elem = document.getElementById(target);\r\n if (elem) {\r\n elem.remove();\r\n }\r\n window.vue.bShowEditCustomPlantAttribute = false;\r\n } else {\r\n alert(response.msg);\r\n }\r\n });\r\n },\r\n\r\n saveAllAttributes: function(source) {\r\n const forms = document.querySelector(source).getElementsByTagName('form');\r\n window.vue.bulkSubmitForm(0, forms, 100);\r\n },\r\n\r\n bulkSubmitForm: function(index, forms, delay) {\r\n if (index >= forms.length) {\r\n alert(window.vue.operationSucceeded);\r\n location.reload();\r\n return;\r\n }\r\n\r\n const form = forms[index];\r\n\r\n form.addEventListener('submit', function(event) {\r\n event.preventDefault();\r\n });\r\n\r\n const formData = new FormData(form);\r\n\r\n setTimeout(function() {\r\n fetch(form.action, {\r\n method: form.method || 'POST',\r\n body: formData\r\n }).then(function(response){\r\n return response.text();\r\n }).then(function(data){\r\n window.vue.bulkSubmitForm(index + 1, forms, delay);\r\n }).catch(function(error){\r\n console.error(error);\r\n window.vue.bulkSubmitForm(index + 1, forms, delay);\r\n });\r\n }, delay);\r\n },\r\n\r\n showEditPhoto: function(plant, property, hint = '')\r\n {\r\n document.getElementById('inpEditPhotoPlantId').value = plant;\r\n document.getElementById('inpEditPhotoAttribute').value = property;\r\n\r\n if (hint.length > 0) {\r\n document.getElementById('inpEditPhotoHint').innerHTML = hint;\r\n }\r\n\r\n window.vue.bShowEditPhoto = true;\r\n },\r\n\r\n showPhotoUpload: function(plant)\r\n {\r\n document.getElementById('inpUploadPhotoPlantId').value = plant;\r\n window.vue.bShowUploadPhoto = true;\r\n },\r\n\r\n removePlantPreviewPhoto: function(plant, target) {\r\n window.vue.ajaxRequest('post', window.location.origin + '/plants/details/photo/remove', { plant: plant }, function(response){\r\n if (response.code == 200) {\r\n let elem = document.querySelector(target);\r\n if (elem) {\r\n elem.style.backgroundImage = 'url(' + response.placeholder + ')';\r\n }\r\n } else {\r\n alert(response.msg);\r\n }\r\n });\r\n },\r\n\r\n deletePhoto: function(photo, plant, target)\r\n {\r\n if (!confirm(window.vue.confirmPhotoRemoval)) {\r\n return;\r\n }\r\n\r\n window.vue.ajaxRequest('post', window.location.origin + '/plants/details/gallery/photo/remove', { photo: photo, plant: plant }, function(response){\r\n if (response.code == 200) {\r\n let elem = document.getElementById(target);\r\n if (elem) {\r\n elem.remove();\r\n }\r\n } else {\r\n alert(response.msg);\r\n }\r\n });\r\n },\r\n\r\n showAddPlantLogEntry: function(plant, anchor = '') {\r\n document.getElementById('inpAddPlantLogEntryPlantId').value = plant;\r\n document.getElementById('inpAddPlantLogEntryAnchor').value = anchor;\r\n window.vue.bShowAddPlantLogEntry = true;\r\n },\r\n\r\n showEditPlantLogEntry: function(id, plant, content, anchor = '') {\r\n document.getElementById('inpEditPlantLogEntryItemId').value = id;\r\n document.getElementById('inpEditPlantLogEntryPlantId').value = plant;\r\n document.getElementById('inpEditPlantLogEntryContent').value = content;\r\n document.getElementById('inpEditPlantLogEntryAnchor').value = anchor;\r\n window.vue.bShowEditPlantLogEntry = true;\r\n },\r\n\r\n removePlantLogEntry: function(id, table_entry) {\r\n window.vue.ajaxRequest('post', window.location.origin + '/plants/log/remove', { item: id }, function(response) {\r\n if (response.code == 200) {\r\n document.getElementById(table_entry).remove();\r\n } else {\r\n alert(response.msg);\r\n }\r\n });\r\n },\r\n\r\n loadNextPlantLogEntries: function(obj, plant, table) {\r\n window.vue.ajaxRequest('post', window.location.origin + '/plants/log/fetch', { plant: plant, paginate: obj.dataset.paginate }, function(response) {\r\n if (response.code == 200) {\r\n let tbody = table.getElementsByTagName('tbody')[0];\r\n\r\n response.data.forEach(function(elem, index) {\r\n let newRow = document.createElement('tr');\r\n newRow.id = 'plant-log-entry-table-row-' + elem.id;\r\n newRow.innerHTML = `\r\n ` + elem.content + `\r\n ` + elem.created_at + ` / ` + elem.updated_at + `\r\n \r\n \r\n  \r\n \r\n \r\n `;\r\n\r\n tbody.appendChild(newRow);\r\n });\r\n\r\n obj.parentNode.parentNode.remove();\r\n\r\n let actionRow = document.createElement('tr');\r\n actionRow.id = 'plant-log-load-more';\r\n actionRow.classList.add('plant-log-paginate');\r\n actionRow.innerHTML = `` + window.vue.loadMore + ``;\r\n tbody.appendChild(actionRow);\r\n } else {\r\n alert(response.msg);\r\n }\r\n });\r\n },\r\n\r\n showAddLocationLogEntry: function(location, anchor = '') {\r\n document.getElementById('inpAddLocationLogEntryLocationId').value = location;\r\n document.getElementById('inpAddLocationLogEntryAnchor').value = anchor;\r\n window.vue.bShowAddLocationLogEntry = true;\r\n },\r\n\r\n showEditLocationLogEntry: function(id, location, content, anchor = '') {\r\n document.getElementById('inpEditLocationLogEntryItemId').value = id;\r\n document.getElementById('inpEditLocationLogEntryLocationId').value = location;\r\n document.getElementById('inpEditLocationLogEntryContent').value = content;\r\n document.getElementById('inpEditLocationLogEntryAnchor').value = anchor;\r\n window.vue.bShowEditLocationLogEntry = true;\r\n },\r\n\r\n removeLocationLogEntry: function(id, table_entry) {\r\n window.vue.ajaxRequest('post', window.location.origin + '/plants/location/log/remove', { item: id }, function(response) {\r\n if (response.code == 200) {\r\n document.getElementById(table_entry).remove();\r\n } else {\r\n alert(response.msg);\r\n }\r\n });\r\n },\r\n\r\n loadNextLocationLogEntries: function(obj, location, table) {\r\n window.vue.ajaxRequest('post', window.location.origin + '/plants/location/log/fetch', { location: location, paginate: obj.dataset.paginate }, function(response) {\r\n if (response.code == 200) {\r\n let tbody = table.getElementsByTagName('tbody')[0];\r\n\r\n response.data.forEach(function(elem, index) {\r\n let newRow = document.createElement('tr');\r\n newRow.id = 'location-log-entry-table-row-' + elem.id;\r\n newRow.innerHTML = `\r\n ` + elem.content + `\r\n ` + elem.created_at + ` / ` + elem.updated_at + `\r\n \r\n \r\n  \r\n \r\n \r\n `;\r\n\r\n tbody.appendChild(newRow);\r\n });\r\n\r\n obj.parentNode.parentNode.remove();\r\n\r\n let actionRow = document.createElement('tr');\r\n actionRow.id = 'location-log-load-more';\r\n actionRow.classList.add('location-log-paginate');\r\n actionRow.innerHTML = `` + window.vue.loadMore + ``;\r\n tbody.appendChild(actionRow);\r\n } else {\r\n alert(response.msg);\r\n }\r\n });\r\n },\r\n\r\n markHistorical: function(plant) {\r\n if (!confirm(window.vue.confirmPlantAddHistory)) {\r\n return;\r\n }\r\n\r\n location.href = window.location.origin + '/plants/history/add?plant=' + plant;\r\n },\r\n\r\n unmarkHistorical: function(plant) {\r\n if (!confirm(window.vue.confirmPlantRemoveHistory)) {\r\n return;\r\n }\r\n\r\n location.href = window.location.origin + '/plants/history/remove?plant=' + plant;\r\n },\r\n\r\n deletePlant: function(plant, retloc)\r\n {\r\n if (!confirm(window.vue.confirmPlantRemoval)) {\r\n return;\r\n }\r\n\r\n location.href = window.location.origin + '/plants/remove?plant=' + plant + '&location=' + retloc;\r\n },\r\n\r\n toggleTaskStatus: function(id)\r\n {\r\n window.vue.ajaxRequest('post', window.location.origin + '/tasks/toggle', { task: id }, function(response){\r\n if (response.code == 200) {\r\n let elem = document.getElementById('task-item-' + id);\r\n if (elem) {\r\n elem.remove();\r\n }\r\n } else {\r\n alert(response.msg);\r\n }\r\n });\r\n },\r\n\r\n editTask: function(id)\r\n {\r\n document.getElementById('inpEditTaskId').value = id;\r\n document.getElementById('inpEditTaskTitle').value = document.getElementById('task-item-title-' + id).childNodes[1].textContent;\r\n document.getElementById('inpEditTaskDescription').value = document.getElementById('task-item-description-' + id).innerText;\r\n\r\n let dueDate = document.getElementById('task-item-due-' + id);\r\n if ((dueDate) && (dueDate.childNodes.length > 0)) {\r\n document.getElementById('inpEditTaskDueDate').value = dueDate.childNodes[0].innerText;\r\n document.getElementById('inpEditTaskRecurringTime').value = dueDate.childNodes[2].dataset.time;\r\n\r\n document.getElementById('edit-recurring-flag').classList.remove('is-hidden');\r\n document.getElementById('edit-recurring-time').classList.remove('is-hidden');\r\n\r\n document.getElementById('inpEditTaskRecurringFlag').checked = document.getElementById('inpEditTaskRecurringTime').value.length > 0;\r\n } else {\r\n document.getElementById('inpEditTaskDueDate').value = '';\r\n\r\n if (!document.getElementById('edit-recurring-flag').classList.contains('is-hidden')) {\r\n document.getElementById('edit-recurring-flag').classList.add('is-hidden');\r\n }\r\n\r\n if (!document.getElementById('edit-recurring-time').classList.contains('is-hidden')) {\r\n document.getElementById('edit-recurring-time').classList.add('is-hidden');\r\n }\r\n }\r\n\r\n window.vue.bShowEditTask = true;\r\n },\r\n\r\n removeTask: function(id)\r\n {\r\n window.vue.ajaxRequest('post', window.location.origin + '/tasks/remove', { task: id }, function(response){\r\n if (response.code == 200) {\r\n let elem = document.getElementById('task-item-' + id);\r\n if (elem) {\r\n elem.remove();\r\n }\r\n } else {\r\n alert(response.msg);\r\n }\r\n });\r\n },\r\n\r\n updateLastWatered: function(id)\r\n {\r\n if (!confirm(window.vue.confirmSetAllWatered)) {\r\n return;\r\n }\r\n\r\n location.href = window.location.origin + '/plants/location/' + id + '/water';\r\n },\r\n\r\n updateLastRepotted: function(id)\r\n {\r\n if (!confirm(window.vue.confirmSetAllRepotted)) {\r\n return;\r\n }\r\n\r\n location.href = window.location.origin + '/plants/location/' + id + '/repot';\r\n },\r\n\r\n updateLastFertilised: function(id)\r\n {\r\n if (!confirm(window.vue.confirmSetAllFertilised)) {\r\n return;\r\n }\r\n\r\n location.href = window.location.origin + '/plants/location/' + id + '/fertilise';\r\n },\r\n\r\n expandInventoryItem: function(id)\r\n {\r\n let elem = document.getElementById(id);\r\n if (elem) {\r\n elem.classList.toggle('expand');\r\n }\r\n },\r\n\r\n incrementInventoryItem: function(id, target)\r\n {\r\n window.vue.ajaxRequest('get', window.location.origin + '/inventory/amount/increment?id=' + id, {}, function(response) {\r\n if (response.code == 200) {\r\n let elem = document.getElementById(target);\r\n if (elem) {\r\n elem.innerHTML = response.amount;\r\n\r\n if (response.amount == 0) {\r\n elem.classList.add('is-inventory-item-empty');\r\n } else {\r\n elem.classList.remove('is-inventory-item-empty');\r\n }\r\n }\r\n } else {\r\n alert(response.msg);\r\n }\r\n });\r\n },\r\n\r\n decrementInventoryItem: function(id, target)\r\n {\r\n window.vue.ajaxRequest('get', window.location.origin + '/inventory/amount/decrement?id=' + id, {}, function(response) {\r\n if (response.code == 200) {\r\n let elem = document.getElementById(target);\r\n if (elem) {\r\n elem.innerHTML = response.amount;\r\n\r\n if (response.amount == 0) {\r\n elem.classList.add('is-inventory-item-empty');\r\n } else {\r\n elem.classList.remove('is-inventory-item-empty');\r\n }\r\n }\r\n } else {\r\n alert(response.msg);\r\n }\r\n });\r\n },\r\n\r\n editInventoryItem: function(id, name, group, location, description, tags, amount)\r\n {\r\n document.getElementById('inpInventoryItemId').value = id;\r\n document.getElementById('inpInventoryItemName').value = document.getElementById(name).children[1].innerText;\r\n document.getElementById('inpInventoryItemGroup').value = group;\r\n document.getElementById('inpInventoryItemLocation').value = document.getElementById(location).children[0].innerText;\r\n document.getElementById('inpInventoryItemDescription').value = document.getElementById(description).innerText;\r\n document.getElementById('inpInventoryItemTags').value = document.getElementById(tags).innerText;\r\n document.getElementById('inpInventoryItemAmount').value = amount;\r\n\r\n window.vue.bShowEditInventoryItem = true;\r\n },\r\n\r\n deleteInventoryItem: function(id, target)\r\n {\r\n if (!confirm(window.vue.confirmInventoryItemRemoval)) {\r\n return;\r\n }\r\n\r\n window.vue.ajaxRequest('get', window.location.origin + '/inventory/remove?id=' + id, {}, function(response) {\r\n if (response.code == 200) {\r\n let elem = document.getElementById(target);\r\n if (elem) {\r\n elem.remove();\r\n }\r\n } else {\r\n alert(response.msg);\r\n }\r\n });\r\n },\r\n\r\n createInventoryGroup: function(token, label, tbody, button)\r\n {\r\n window.vue.ajaxRequest('post', window.location.origin + '/inventory/group/add', { token: token, label: label }, function(response) {\r\n if (response.code == 200) {\r\n button.innerText = window.vue.addItem;\r\n\r\n let newRow = document.createElement('tr');\r\n newRow.id = 'inventory-group-item-' + response.itemid;\r\n newRow.innerHTML = `\r\n ` + token + `\r\n ` + label + `\r\n \r\n `;\r\n\r\n tbody.appendChild(newRow);\r\n } else {\r\n button.innerText = window.vue.addItem;\r\n alert(response.msg);\r\n }\r\n });\r\n },\r\n\r\n editInventoryGroupItem: function(id, what, def)\r\n {\r\n let input = prompt(what, def);\r\n\r\n if (input.length > 0) {\r\n window.vue.ajaxRequest('post', window.location.origin + '/inventory/group/edit', {\r\n id: id,\r\n what: what,\r\n value: input\r\n }, function(response) {\r\n if (response.code == 200) {\r\n if (what === 'token') {\r\n document.getElementById('inventory-group-elem-token-' + id).innerText = input;\r\n } else if (what === 'label') {\r\n document.getElementById('inventory-group-elem-label-' + id).innerText = input;\r\n }\r\n } else {\r\n alert(response.msg);\r\n }\r\n });\r\n }\r\n },\r\n\r\n removeInventoryGroupItem: function(id, target)\r\n {\r\n if (!confirm(window.vue.confirmInventoryItemRemoval)) {\r\n return;\r\n }\r\n\r\n window.vue.ajaxRequest('get', window.location.origin + '/inventory/group/remove?id=' + id, {}, function(response) {\r\n if (response.code == 200) {\r\n let elem = document.getElementById(target);\r\n if (elem) {\r\n elem.remove();\r\n }\r\n } else {\r\n alert(response.msg);\r\n }\r\n });\r\n },\r\n\r\n refreshChat: function(auth_user)\r\n {\r\n window.vue.ajaxRequest('get', window.location.origin + '/chat/query', {}, function(response) {\r\n if (response.code == 200) {\r\n response.messages.forEach(function(elem, index) {\r\n document.getElementById('chat').innerHTML = window.vue.renderNewChatMessage(elem, auth_user) + document.getElementById('chat').innerHTML;\r\n \r\n let audio = new Audio(window.location.origin + '/snd/new_message.wav');\r\n audio.onloadeddata = function() {\r\n audio.play();\r\n };\r\n });\r\n }\r\n });\r\n\r\n setTimeout(window.vue.refreshChat, window.constChatMessageQueryRefreshRate);\r\n },\r\n\r\n renderNewChatMessage: function(elem, auth_user)\r\n {\r\n let chatmsgright = '';\r\n if (elem.userId == auth_user) {\r\n chatmsgright = 'chat-message-right';\r\n }\r\n\r\n let html = '';\r\n\r\n if (!elem.system) {\r\n html = `\r\n
\r\n
\r\n
` + elem.userName + `
\r\n
` + window.vue.newChatMessage + `
\r\n
\r\n\r\n
\r\n
` + elem.message + `
\r\n
\r\n\r\n
\r\n ` + elem.diffForHumans + `\r\n
\r\n
\r\n `;\r\n } else {\r\n html = `\r\n
\r\n
\r\n
` + ((elem.userName) ? elem.userName : 'System') + ` @ ` + elem.created_at + `
\r\n \r\n
` + elem.message + `
\r\n
\r\n\r\n
\r\n
` + window.vue.newChatMessage + `
\r\n
\r\n
\r\n `;\r\n }\r\n\r\n return html;\r\n },\r\n\r\n refreshUserList: function()\r\n {\r\n window.vue.ajaxRequest('get', window.location.origin + '/chat/user/online', {}, function(response) {\r\n if (response.code == 200) {\r\n let target = document.getElementById('chat-user-list');\r\n target.innerHTML = window.vue.currentlyOnline;\r\n\r\n response.users.forEach(function(elem, index) {\r\n let comma = '';\r\n if (index < response.users.length - 1) {\r\n comma = ', ';\r\n }\r\n \r\n target.innerHTML += elem.name + comma;\r\n });\r\n }\r\n });\r\n\r\n setTimeout(window.vue.refreshUserList, window.constChatUserListRefreshRate);\r\n },\r\n\r\n refreshTypingStatus: function()\r\n {\r\n if (!window.vue.chatTypingEnable) {\r\n return;\r\n }\r\n\r\n window.vue.ajaxRequest('get', window.location.origin + '/chat/typing/update', {}, function(response){\r\n if (response.code != 200) {\r\n console.error(response.msg);\r\n }\r\n });\r\n\r\n setTimeout(function(){\r\n window.vue.chatTypingTimer = null;\r\n }, 5000);\r\n },\r\n\r\n handleChatInput: function()\r\n {\r\n if (!window.vue.chatTypingTimer) {\r\n window.vue.chatTypingTimer = setTimeout(function(){\r\n window.vue.refreshTypingStatus();\r\n }, 1000);\r\n }\r\n },\r\n\r\n handleTypingIndicator: function()\r\n {\r\n window.vue.ajaxRequest('get', window.location.origin + '/chat/typing', {}, function(response){\r\n if (response.code == 200) {\r\n if (response.status) {\r\n let elem = document.getElementsByClassName('chat-typing-indicator')[0];\r\n elem.style.display = 'block';\r\n\r\n window.vue.chatTypingHide = setTimeout(window.vue.hideChatTypingIndicator, 6550);\r\n }\r\n }\r\n });\r\n\r\n setTimeout(window.vue.handleTypingIndicator, window.constChatTypingRefreshRate);\r\n },\r\n\r\n hideChatTypingIndicator: function()\r\n {\r\n if (window.vue.chatTypingHide !== null) {\r\n let elem = document.getElementsByClassName('chat-typing-indicator')[0];\r\n elem.style.display = 'none';\r\n\r\n window.vue.chatTypingHide = null;\r\n }\r\n },\r\n\r\n animateChatTypingIndicator: function()\r\n {\r\n let indicator = document.getElementsByClassName('chat-typing-indicator')[0];\r\n if (indicator.style.display === 'block') {\r\n window.vue.removePreviousChatIndicatorCircleStyle();\r\n\r\n let elem = document.getElementById('chat-typing-circle-' + window.vue.chatTypingCounter.toString());\r\n elem.style.color = 'rgb(50, 50, 50)';\r\n elem.classList.add('fa-lg');\r\n\r\n window.vue.chatTypingCounter++;\r\n if (window.vue.chatTypingCounter > 3) {\r\n window.vue.chatTypingCounter = 1;\r\n }\r\n }\r\n\r\n setTimeout(window.vue.animateChatTypingIndicator, 350);\r\n },\r\n\r\n removePreviousChatIndicatorCircleStyle: function()\r\n {\r\n let previous = window.vue.chatTypingCounter - 1;\r\n if (previous == 0) {\r\n previous = 3;\r\n }\r\n\r\n let elem = document.getElementById('chat-typing-circle-' + previous.toString());\r\n if (elem.classList.contains('fa-lg')) {\r\n elem.classList.remove('fa-lg');\r\n elem.style.color = 'inherit';\r\n }\r\n },\r\n\r\n fetchUnreadMessageCount: function(target) {\r\n window.vue.ajaxRequest('get', window.location.origin + '/chat/messages/count', {}, function(response) {\r\n if (response.code == 200) {\r\n if (response.count > 0) {\r\n target.classList.remove('is-hidden');\r\n target.children[0].innerText = response.count;\r\n } else {\r\n target.classList.add('is-hidden');\r\n }\r\n }\r\n });\r\n\r\n setTimeout(window.vue.fetchUnreadMessageCount.bind(null, target), window.constChatMessageQueryRefreshRate);\r\n },\r\n\r\n fetchNewSystemMessage: function(target) {\r\n window.vue.ajaxRequest('get', window.location.origin + '/chat/system/message/latest', {}, function(response) {\r\n if (response.code == 200) {\r\n if (response.message) {\r\n window.vue.fadeSystemMessage(target, window.vue.renderNewSystemMessage(response.message), response.message.id);\r\n \r\n let audio = new Audio(window.location.origin + '/snd/new_message.wav');\r\n audio.onloadeddata = function() {\r\n audio.play();\r\n };\r\n }\r\n }\r\n });\r\n\r\n setTimeout(window.vue.fetchNewSystemMessage.bind(null, target), window.constChatMessageQueryRefreshRate);\r\n },\r\n\r\n renderNewSystemMessage: function(elem) {\r\n let html = `\r\n
\r\n
` + ((elem.userName) ? elem.userName : 'System') + ` @ ` + elem.created_at + `
\r\n\r\n
` + elem.message + `
\r\n
\r\n `;\r\n\r\n return html;\r\n },\r\n\r\n fadeSystemMessage: function(target, code, id) {\r\n target.innerHTML = code + target.innerHTML;\r\n\r\n let fadeElem = document.getElementById('system-message-small-' + id);\r\n\r\n setTimeout(function() {\r\n fadeElem.classList.replace('fade-out', 'fade-in');\r\n }, 250);\r\n \r\n setTimeout(function() {\r\n fadeElem.classList.replace('fade-in', 'fade-out');\r\n }, 5000);\r\n },\r\n\r\n textFilterElements: function(token) {\r\n let elems = document.getElementsByClassName('plant-filter-text-target');\r\n for (let i = 0; i < elems.length; i++) {\r\n let target = elems[i].parentNode;\r\n \r\n while (!target.classList.contains('plant-filter-text-root')) {\r\n target = target.parentNode;\r\n }\r\n\r\n if (!elems[i].innerText.toLowerCase().includes(token.toLowerCase())) {\r\n target.classList.add('is-hidden');\r\n } else {\r\n target.classList.remove('is-hidden');\r\n }\r\n }\r\n },\r\n\r\n filterTasks: function(token) {\r\n let elems = document.getElementsByClassName('task');\r\n for (let i = 0; i < elems.length; i++) {\r\n let elemTitle = elems[i].children[1].children[0];\r\n let elemDescription = elems[i].children[2].children[0];\r\n\r\n if ((elemTitle.innerText.toLowerCase().includes(token.toLowerCase())) || (elemDescription.innerText.toLowerCase().includes(token.toLowerCase()))) {\r\n elems[i].classList.remove('is-hidden'); \r\n } else {\r\n elems[i].classList.add('is-hidden'); \r\n }\r\n }\r\n },\r\n\r\n filterInventory: function(token) {\r\n let elems = document.getElementsByClassName('inventory-item');\r\n for (let i = 0; i < elems.length; i++) {\r\n let elemName = elems[i].children[1].children[0];\r\n let elemDescription = elems[i].children[2].children[1];\r\n let elemTags = elems[i].children[2].children[0].children[0];\r\n let elemLocation = elems[i].children[2].children[3].children[0];\r\n \r\n if ((elemName.innerText.toLowerCase().includes(token.toLowerCase())) || (elemDescription.innerText.toLowerCase().includes(token.toLowerCase())) || (elemTags.innerText.toLowerCase().includes(token.toLowerCase())) || (elemLocation.innerText.toLowerCase().includes(token.toLowerCase()))) {\r\n elems[i].classList.remove('is-hidden'); \r\n } else {\r\n elems[i].classList.add('is-hidden'); \r\n }\r\n }\r\n },\r\n\r\n toggleDropdown: function(elem) {\r\n if (elem.classList.contains('is-active')) {\r\n elem.classList.remove('is-active');\r\n } else {\r\n elem.classList.add('is-active');\r\n }\r\n },\r\n\r\n selectAdminTab: function(tab) {\r\n const tabs = ['environment', 'media', 'users', 'locations', 'auth', 'attributes', 'calendar', 'mail', 'themes', 'backup', 'weather', 'api', 'info'];\r\n\r\n let selEl = document.querySelector('.admin-' + tab);\r\n if (selEl) {\r\n tabs.forEach(function(elem, index) {\r\n let otherEl = document.querySelector('.admin-' + elem);\r\n if (otherEl) {\r\n otherEl.classList.add('is-hidden');\r\n }\r\n\r\n let otherTabs = document.querySelector('.admin-tab-' + elem);\r\n if (otherTabs) {\r\n otherTabs.classList.remove('is-active');\r\n }\r\n });\r\n\r\n selEl.classList.remove('is-hidden');\r\n\r\n let selTab = document.querySelector('.admin-tab-' + tab);\r\n if (selTab) {\r\n selTab.classList.add('is-active');\r\n }\r\n }\r\n },\r\n\r\n switchAdminTab: function(tab) {\r\n window.vue.selectAdminTab(tab);\r\n\r\n const url = new URL(window.location);\r\n url.searchParams.set('tab', tab);\r\n history.replaceState(null, '', url.toString());\r\n },\r\n\r\n showImagePreview: function(asset, aspect = 'is-3by5') {\r\n let img = document.getElementById('preview-image-modal-img');\r\n if (img) {\r\n img.src = asset;\r\n\r\n if (window.vue.clsLastImagePreviewAspect.length > 0) {\r\n img.parentNode.classList.remove(window.vue.clsLastImagePreviewAspect);\r\n }\r\n\r\n window.vue.clsLastImagePreviewAspect = aspect;\r\n img.parentNode.classList.add(window.vue.clsLastImagePreviewAspect);\r\n\r\n window.vue.bShowPreviewImageModal = true;\r\n }\r\n },\r\n\r\n showSharePhoto: function(asset, title, type) {\r\n document.getElementById('share-photo-title').value = title;\r\n document.getElementById('share-photo-id').value = asset;\r\n document.getElementById('share-photo-type').value = type;\r\n\r\n document.getElementById('share-photo-result').classList.add('is-hidden');\r\n document.getElementById('share-photo-error').classList.add('is-hidden');\r\n document.getElementById('share-photo-submit-action').classList.remove('is-hidden');\r\n\r\n window.vue.bShowSharePhoto = true;\r\n },\r\n\r\n performPhotoShare: function(asset, title, _public, description, keywords, type, result, button, error) {\r\n let origButtonHtml = button.innerHTML;\r\n button.innerHTML = ' ' + window.vue.loadingPleaseWait;\r\n\r\n window.vue.ajaxRequest('post', window.location.origin + '/share/photo/post', { asset: asset, title: title, public: _public, description: description, keywords: keywords, type: type }, function(response) {\r\n button.innerHTML = origButtonHtml;\r\n\r\n if (response.code == 200) {\r\n result.value = response.data.url;\r\n result.parentNode.parentNode.classList.remove('is-hidden');\r\n button.classList.add('is-hidden');\r\n error.classList.add('is-hidden');\r\n } else {\r\n error.innerHTML = response.msg;\r\n error.classList.remove('is-hidden');\r\n }\r\n });\r\n },\r\n\r\n generateNewToken: function(target, button) {\r\n let oldTxt = button.innerHTML;\r\n button.innerHTML = '';\r\n\r\n window.vue.ajaxRequest('post', window.location.origin + '/admin/cronjob/token', {}, function(response) {\r\n button.innerHTML = oldTxt;\r\n\r\n if (response.code == 200) {\r\n target.value = response.token;\r\n } else {\r\n alert(response.msg);\r\n }\r\n });\r\n },\r\n\r\n startBackup: function(button, plants, gallery, tasks, inventory, calendar) {\r\n let oldText = button.innerHTML;\r\n button.innerHTML = ' ' + oldText;\r\n\r\n window.vue.ajaxRequest('post', window.location.origin + '/export/start', {\r\n plants: plants,\r\n gallery: gallery,\r\n tasks: tasks,\r\n inventory: inventory,\r\n calendar: calendar\r\n }, function(response) {\r\n button.innerHTML = oldText;\r\n\r\n if (response.code == 200) {\r\n let export_result = document.getElementById('export-result');\r\n if (export_result) {\r\n export_result.classList.remove('is-hidden');\r\n\r\n export_result.children[1].href = response.file;\r\n export_result.children[1].innerHTML = response.file;\r\n }\r\n }\r\n });\r\n },\r\n\r\n startImport: function(button, file, locations, plants, gallery, tasks, inventory, calendar) {\r\n let oldText = button.innerHTML;\r\n button.innerHTML = ' ' + oldText;\r\n \r\n let formData = new FormData();\r\n formData.append('import', file.files[0]);\r\n formData.append('locations', ((locations) ? 1 : 0));\r\n formData.append('plants', ((plants) ? 1 : 0));\r\n formData.append('gallery', ((gallery) ? 1 : 0));\r\n formData.append('tasks', ((tasks) ? 1 : 0));\r\n formData.append('inventory', ((inventory) ? 1 : 0));\r\n formData.append('calendar', ((calendar) ? 1 : 0));\r\n\r\n window.vue.ajaxRequest('post', window.location.origin + '/import/start', formData, function(response) {\r\n button.innerHTML = oldText;\r\n\r\n if (response.code == 200) {\r\n let import_result = document.getElementById('import-result');\r\n if (import_result) {\r\n import_result.classList.remove('is-hidden');\r\n }\r\n }\r\n });\r\n },\r\n\r\n startThemeImport: function(file, button) {\r\n let oldText = button.innerHTML;\r\n button.innerHTML = ' ' + oldText;\r\n \r\n let formData = new FormData();\r\n formData.append('theme', file.files[0]);\r\n\r\n window.vue.ajaxRequest('post', window.location.origin + '/admin/themes/import', formData, function(response) {\r\n button.innerHTML = oldText;\r\n \r\n if (response.code == 200) {\r\n let import_result = document.getElementById('themes-import-result');\r\n if (import_result) {\r\n import_result.innerText = import_result.innerText.replace('{count}', response.themes.length);\r\n import_result.classList.remove('is-hidden');\r\n }\r\n } else {\r\n alert(response.msg);\r\n }\r\n });\r\n },\r\n\r\n removeTheme: function(theme) {\r\n window.vue.ajaxRequest('post', window.location.origin + '/admin/themes/remove', { theme: theme }, function(response) {\r\n if (response.code == 200) {\r\n let tableElem = document.getElementById('admin-themes-list-item-' + theme);\r\n if (tableElem) {\r\n tableElem.remove();\r\n }\r\n } else {\r\n alert(response.msg);\r\n }\r\n });\r\n },\r\n\r\n renderCalendar: function(elem, date_from, date_till = null) {\r\n window.vue.ajaxRequest('post', window.location.origin + '/calendar/query', { date_from: date_from, date_till: date_till }, function(response){\r\n if (response.code == 200) {\r\n let content = document.getElementById(elem);\r\n if (content) {\r\n let data = response.data;\r\n\r\n data.sort(function (a, b) {\r\n return new Date(a.date_from) - new Date(b.date_from);\r\n });\r\n\r\n const labels = data.map(x => {\r\n return [x.name];\r\n });\r\n\r\n const newData = data.map(x => {\r\n return [x.date_from.split(' ')[0], x.date_till.split(' ')[0], x.class_name, x.id, x.class_descriptor]\r\n });\r\n \r\n let colorsBackground = [];\r\n data.forEach(function(elem, index) {\r\n colorsBackground.push(elem.color_background);\r\n });\r\n \r\n let colorsBorder = [];\r\n data.forEach(function(elem, index) {\r\n colorsBorder.push(elem.color_border);\r\n });\r\n\r\n const config = {\r\n type: 'bar',\r\n data: {\r\n labels: labels,\r\n datasets: [\r\n {\r\n data: newData,\r\n backgroundColor: colorsBackground,\r\n borderColor: colorsBorder,\r\n borderWidth: 1,\r\n fill: false,\r\n barPercentage: 0.3\r\n }\r\n ]\r\n },\r\n options: {\r\n indexAxis: 'y',\r\n responsive: true,\r\n scales: {\r\n x: {\r\n min: response.date_from,\r\n max: response.date_till,\r\n type: 'time',\r\n time: {\r\n unit: 'day'\r\n }\r\n },\r\n y: {\r\n beginAtZero: true\r\n }\r\n },\r\n plugins: {\r\n legend: {\r\n display: false,\r\n },\r\n tooltip: {\r\n callbacks: {\r\n label: function(context) {\r\n return context.dataset.data[context.dataIndex][2];\r\n },\r\n afterBody: function(context) {\r\n return context[0].raw[0] + ' - ' + context[0].raw[1];\r\n }\r\n }\r\n }\r\n },\r\n\r\n }\r\n };\r\n\r\n if (window.calendarChart !== null) {\r\n window.calendarChart.destroy();\r\n }\r\n \r\n window.calendarChart = new chart_js_auto__WEBPACK_IMPORTED_MODULE_5__[\"default\"](\r\n content,\r\n config\r\n );\r\n\r\n content.onclick = function(event) {\r\n let points = window.calendarChart.getElementsAtEventForMode(event, 'nearest', { intersect: true }, true);\r\n if (points.length) {\r\n const firstPoint = points[0];\r\n const label = window.calendarChart.data.labels[firstPoint.index];\r\n const value = window.calendarChart.data.datasets[firstPoint.datasetIndex].data[firstPoint.index];\r\n console.log(value);\r\n if (value.length) {\r\n document.getElementById('inpEditCalendarItemIdent').innerText = '#' + value[3] + ' ' + label;\r\n document.getElementById('inpEditCalendarItemId').value = value[3];\r\n document.getElementById('inpEditCalendarItemName').value = label;\r\n document.getElementById('inpEditCalendarItemDateFrom').value = value[0];\r\n document.getElementById('inpEditCalendarItemDateTill').value = value[1];\r\n document.getElementById('inpEditCalendarItemClass').value = value[4];\r\n window.vue.bShowEditCalendarItem = true;\r\n }\r\n }\r\n };\r\n }\r\n } else {\r\n alert(response.msg);\r\n }\r\n });\r\n },\r\n\r\n removeCalendarItem: function(ident) {\r\n window.vue.ajaxRequest('post', window.location.origin + '/calendar/remove', { ident: ident }, function(response) {\r\n if (response.code == 200) {\r\n location.reload();\r\n } else {\r\n alert(response.msg);\r\n }\r\n });\r\n },\r\n\r\n removeCalendarClass: function(id) {\r\n window.vue.ajaxRequest('post', window.location.origin + '/admin/calendar/class/remove', { id: id }, function(response) {\r\n if (response.code == 200) {\r\n let elItem = document.getElementById('admin-calendar-class-item-' + id);\r\n if (elItem) {\r\n elItem.remove();\r\n }\r\n } else {\r\n alert(response.msg);\r\n }\r\n });\r\n },\r\n\r\n clonePlant: function(id) {\r\n window.vue.ajaxRequest('post', window.location.origin + '/plants/clone', { id: id }, function(response) {\r\n if (response.code == 200) {\r\n location.href = window.location.origin + '/plants/details/' + response.clone_id;\r\n } else {\r\n alert(response.msg);\r\n }\r\n });\r\n },\r\n\r\n showPerformBulkUpdate: function(operation, title, button, location, is_custom = false, datatype = 'datetime') {\r\n document.getElementById('plant-bulk-perform-operation-operation').value = operation;\r\n document.getElementById('plant-bulk-perform-operation-location').value = location;\r\n document.getElementById('plant-bulk-perform-operation-title').innerText = title;\r\n document.getElementById('plant-bulk-perform-operation-button').innerText = button;\r\n document.getElementById('plant-bulk-perform-operation-custom').checked = is_custom;\r\n document.getElementById('plant-bulk-perform-operation-datatype').value = datatype;\r\n\r\n if (document.getElementById('plant-bulk-perform-operation-bulkvalue').tagName.toLowerCase() === 'select') {\r\n let parentElem = document.getElementById('plant-bulk-perform-operation-bulkvalue').parentElement;\r\n parentElem.innerHTML = ``;\r\n }\r\n\r\n if (datatype === 'datetime') {\r\n let curDate = new Date();\r\n document.getElementById('plant-bulk-perform-operation-bulkvalue').type = 'date';\r\n document.getElementById('plant-bulk-perform-operation-bulkvalue').value = curDate.toISOString().split('T')[0];\r\n } else if (datatype == 'string') {\r\n document.getElementById('plant-bulk-perform-operation-bulkvalue').type = 'text';\r\n document.getElementById('plant-bulk-perform-operation-bulkvalue').value = '';\r\n } else if (datatype == 'int') {\r\n document.getElementById('plant-bulk-perform-operation-bulkvalue').type = 'number';\r\n document.getElementById('plant-bulk-perform-operation-bulkvalue').value = 0;\r\n } else if (datatype == 'double') {\r\n document.getElementById('plant-bulk-perform-operation-bulkvalue').type = 'text';\r\n document.getElementById('plant-bulk-perform-operation-bulkvalue').value = '0.0';\r\n } else if (datatype == 'bool') {\r\n document.getElementById('plant-bulk-perform-operation-bulkvalue').type = 'number';\r\n document.getElementById('plant-bulk-perform-operation-bulkvalue').value = 0;\r\n } else {\r\n document.getElementById('plant-bulk-perform-operation-bulkvalue').type = 'text';\r\n document.getElementById('plant-bulk-perform-operation-bulkvalue').value = '';\r\n }\r\n \r\n window.vue.bulkChecked('plant-bulk-perform-operation', false);\r\n\r\n window.vue.bShowPlantBulkPerformUpdate = true;\r\n },\r\n\r\n bulkPerformPlantUpdate: function(target, attribute, location, bulkvalue, is_custom = false, bulktype = 'datetime') {\r\n let plantIds = [];\r\n\r\n let elems = document.getElementsByClassName(target);\r\n if (elems) {\r\n Array.prototype.forEach.call(elems, function(elem) {\r\n if (elem.checked) {\r\n plantIds.push([elem.dataset.plantid, elem.dataset.plantname]);\r\n }\r\n });\r\n \r\n if (plantIds.length > 0) {\r\n window.vue.ajaxRequest('post', window.location.origin + '/plants/update/bulk', { attribute: attribute, list: JSON.stringify(plantIds), location: location, bulkvalue: bulkvalue, bulktype: bulktype, custom: is_custom }, function(response) {\r\n if (response.code == 200) {\r\n alert(window.vue.operationSucceeded);\r\n window.vue.bShowPlantBulkPerformUpdate = false;\r\n } else {\r\n alert(response.msg);\r\n }\r\n });\r\n } else {\r\n alert(window.vue.noListItemsSelected); \r\n }\r\n }\r\n },\r\n\r\n setBulkComboValues: function(list) {\r\n let parentElement = document.getElementById('plant-bulk-perform-operation-bulkvalue').parentElement;\r\n \r\n let listitems = '';\r\n\r\n list.forEach(function(elem, index) {\r\n listitems += '';\r\n });\r\n\r\n parentElement.innerHTML = ``;\r\n },\r\n\r\n generateAndShowQRCode: function(plant) {\r\n window.vue.ajaxRequest('get', window.location.origin + '/plants/qrcode?plant=' + plant, {}, function(response) {\r\n if (response.code == 200) {\r\n let elTarget = document.getElementById('image-plant-qr-code');\r\n if (elTarget) {\r\n elTarget.src = response.qrcode;\r\n window.vue.bShowPlantQRCode = true;\r\n }\r\n } else {\r\n alert(response.msg);\r\n }\r\n });\r\n },\r\n\r\n printQRCode: function(content, title) {\r\n let wnd = window.open('', title, 'height=auto, width=auto');\r\n\r\n wnd.document.write('' + title + '');\r\n wnd.document.write('');\r\n wnd.document.write('');\r\n\r\n wnd.print();\r\n wnd.close();\r\n },\r\n\r\n bulkChecked: function(target, flag) {\r\n let elems = document.getElementsByClassName(target);\r\n \r\n if (elems) {\r\n Array.prototype.forEach.call(elems, function(elem){ \r\n elem.checked = flag; \r\n });\r\n }\r\n },\r\n\r\n bulkPrintQRCodes: function(target, location) {\r\n let plantIds = [];\r\n\r\n let elems = document.getElementsByClassName(target);\r\n if (elems) {\r\n Array.prototype.forEach.call(elems, function(elem) {\r\n if (elem.checked) {\r\n plantIds.push([elem.dataset.plantid, elem.dataset.plantname]);\r\n }\r\n });\r\n\r\n if (plantIds.length > 0) {\r\n window.vue.ajaxRequest('post', window.location.origin + '/plants/qrcode/bulk', { list: JSON.stringify(plantIds) }, function(response) {\r\n if (response.code == 200) {\r\n let wnd = window.open('', location, 'height=auto, width=auto');\r\n\r\n wnd.document.write('' + location + '');\r\n\r\n response.list.forEach(function(elem, index) {\r\n wnd.document.write('
#' + elem.plantid + ' ' + elem.plantname + '
');\r\n });\r\n\r\n wnd.document.write('');\r\n\r\n wnd.print();\r\n wnd.close();\r\n } else {\r\n alert(response.msg);\r\n }\r\n });\r\n } else {\r\n alert(window.vue.noListItemsSelected); \r\n }\r\n }\r\n },\r\n\r\n queryInvQrCode: function(item) {\r\n window.vue.ajaxRequest('get', window.location.origin + '/inventory/qrcode?item=' + item, {}, function(response) {\r\n if (response.code == 200) {\r\n let elTarget = document.getElementById('image-inventory-qr-code');\r\n if (elTarget) {\r\n elTarget.src = response.qrcode;\r\n window.vue.bShowInvItemQRCode = true;\r\n }\r\n } else {\r\n alert(response.msg);\r\n }\r\n });\r\n },\r\n\r\n bulkPrintInvQRCodes: function(target, title) {\r\n let invIds = [];\r\n\r\n let elems = document.getElementsByClassName(target);\r\n if (elems) {\r\n Array.prototype.forEach.call(elems, function(elem) {\r\n if (elem.checked) {\r\n invIds.push([elem.dataset.invitemid, elem.dataset.invitemname, elem.dataset.invgroup]);\r\n }\r\n });\r\n\r\n if (invIds.length > 0) {\r\n window.vue.ajaxRequest('post', window.location.origin + '/inventory/qrcode/bulk', { list: JSON.stringify(invIds) }, function(response) {\r\n if (response.code == 200) {\r\n let wnd = window.open('', title, 'height=auto, width=auto');\r\n\r\n wnd.document.write('' + title + '');\r\n\r\n response.list.forEach(function(elem, index) {\r\n wnd.document.write('
#' + elem.invitemid + ' [' + elem.invgroup + '] ' + elem.invitemname + '
');\r\n });\r\n\r\n wnd.document.write('');\r\n\r\n wnd.print();\r\n wnd.close();\r\n } else {\r\n alert(response.msg);\r\n }\r\n });\r\n } else {\r\n alert(window.vue.noListItemsSelected); \r\n }\r\n }\r\n },\r\n\r\n bulkExportInventory: function(target, format, title) {\r\n let invIds = [];\r\n\r\n let elems = document.getElementsByClassName(target);\r\n if (elems) {\r\n Array.prototype.forEach.call(elems, function(elem) {\r\n if (elem.checked) {\r\n invIds.push([elem.dataset.invitemid, elem.dataset.invitemname, document.getElementById(elem.dataset.invdescription).innerText, elem.dataset.invgroup, elem.dataset.invamount, elem.dataset.invlocation, elem.dataset.invphoto, elem.dataset.invcreated, elem.dataset.invupdated]);\r\n }\r\n });\r\n\r\n if (invIds.length > 0) {\r\n window.vue.ajaxRequest('post', window.location.origin + '/inventory/export', { list: JSON.stringify(invIds), format: document.getElementById(format).value }, function(response) {\r\n if (response.code == 200) {\r\n const dlanchor = document.createElement('a');\r\n dlanchor.href = response.resource;\r\n dlanchor.target = '_blank';\r\n dlanchor.setAttribute('download', response.resource);\r\n dlanchor.click();\r\n } else {\r\n alert(response.msg);\r\n }\r\n });\r\n } else {\r\n alert(window.vue.noListItemsSelected); \r\n }\r\n }\r\n },\r\n\r\n editGalleryPhotoLabel: function(id, plant, old) {\r\n let newLabel = prompt(window.vue.editProperty, old);\r\n if (newLabel.length) {\r\n window.vue.ajaxRequest('post', window.location.origin + '/plants/details/gallery/photo/label/edit', { id: id, label: newLabel, plant: plant }, function(response) {\r\n if (response.code == 200) {\r\n document.getElementById('photo-gallery-item-' + id).children[0].children[0].innerHTML = newLabel;\r\n } else {\r\n alert(response.msg);\r\n }\r\n });\r\n }\r\n },\r\n\r\n setGalleryPhotoAsMain: function(id, plant) {\r\n let query = confirm(window.vue.confirmSetGalleryPhotoAsMain);\r\n if (!query) {\r\n return;\r\n }\r\n\r\n window.vue.ajaxRequest('post', window.location.origin + '/plants/details/gallery/photo/setmain', { id: id, plant: plant }, function(response) {\r\n if (response.code == 200) {\r\n location.href = window.location.origin + '/plants/details/' + plant;\r\n } else {\r\n alert(response.msg);\r\n }\r\n });\r\n },\r\n\r\n removeSharedPhoto: function(ident) {\r\n window.vue.ajaxRequest('get', window.location.origin + '/share/photo/remove?ident=' + ident, {}, function(response) {\r\n if (response.code == 200) {\r\n let elem = document.getElementById('photo-share-entry-' + ident);\r\n if (elem) {\r\n elem.remove();\r\n }\r\n } else {\r\n alert(response.msg);\r\n }\r\n });\r\n },\r\n\r\n loadNextShareLogEntries: function(table, action) {\r\n window.vue.ajaxRequest('post', window.location.origin + '/profile/sharelog/fetch', { paginate: action.dataset.paginate }, function(response) {\r\n if (response.code == 200) {\r\n let tbody = table.getElementsByTagName('tbody')[0];\r\n\r\n response.data.forEach(function(elem, index) {\r\n let newRow = document.createElement('tr');\r\n newRow.id = 'photo-share-entry-' + elem.id;\r\n newRow.innerHTML = `\r\n ` + elem.title + `\r\n ` + elem.diffForHumans + `\r\n \r\n `;\r\n\r\n tbody.appendChild(newRow);\r\n });\r\n\r\n action.parentNode.parentNode.remove();\r\n\r\n let actionRow = document.createElement('tr');\r\n actionRow.id = 'share-log-load-more';\r\n actionRow.classList.add('share-log-paginate');\r\n actionRow.innerHTML = `` + window.vue.loadMore + ``;\r\n tbody.appendChild(actionRow);\r\n } else {\r\n alert(response.msg);\r\n }\r\n });\r\n },\r\n\r\n acquireGeoPosition: function(destLatitude, destLongitude, button) {\r\n let oldText = button.innerHTML;\r\n button.innerHTML = ' ' + button.innerHTML;\r\n\r\n if (navigator.geolocation) {\r\n navigator.geolocation.getCurrentPosition(function(position) {\r\n destLatitude.value = position.coords.latitude;\r\n destLongitude.value = position.coords.longitude;\r\n\r\n button.innerHTML = oldText;\r\n });\r\n } else {\r\n button.innerHTML = oldText;\r\n \r\n alert('Geolocation is not available');\r\n }\r\n },\r\n\r\n toggleApiKey: function(id) {\r\n window.vue.ajaxRequest('get', window.location.origin + '/admin/api/' + id + '/toggle', {}, function(response) {\r\n if (response.code == 200) {\r\n document.getElementById('api-key-checkbox-' + id).checked = response.active;\r\n } else {\r\n alert(response.msg);\r\n }\r\n });\r\n },\r\n\r\n toggleAdminPlantAttribute: function(name) {\r\n window.vue.ajaxRequest('get', window.location.origin + '/admin/attribute/update?name=' + name, {}, function(response) {\r\n if (response.code == 200) {\r\n document.getElementById('admin-attributes-checkbox-' + name).checked = response.active;\r\n } else {\r\n alert(response.msg);\r\n }\r\n });\r\n },\r\n\r\n toggleAdminBoolSetting: function(name) {\r\n window.vue.ajaxRequest('get', window.location.origin + '/admin/environment/boolean/toggle?name=' + name, {}, function(response) {\r\n if (response.code == 200) {\r\n document.getElementById('admin-attributes-checkbox-allow-custom-attributes').checked = response.value;\r\n } else {\r\n alert(response.msg);\r\n }\r\n });\r\n },\r\n\r\n toggleAdminAuthInfoMessages: function(checked, warning, caution) {\r\n let elWarning = document.querySelector(warning);\r\n let elCaution = document.querySelector(caution);\r\n\r\n if (checked) {\r\n if (elWarning) {\r\n elWarning.style.display = 'block';\r\n }\r\n\r\n if (elCaution) {\r\n elCaution.style.display = 'block';\r\n }\r\n } else {\r\n if (elWarning) {\r\n elWarning.style.display = 'none';\r\n }\r\n\r\n if (elCaution) {\r\n elCaution.style.display = 'none';\r\n }\r\n }\r\n },\r\n\r\n performPlantRecognition: function(target, plantid) {\r\n const form = document.getElementById(target);\r\n const data = new FormData(form);\r\n\r\n window.vue.ajaxRequest('post', window.location.origin + '/plants/details/identify', data, function(response) {\r\n if (response.code == 200) {\r\n let dest = document.getElementById('recognized-plant-selection');\r\n\r\n dest.innerHTML = '
';\r\n\r\n response.data.forEach(function(elem, index) {\r\n dest.innerHTML += `\r\n
\r\n
\r\n  ` + elem.species.scientificNameWithoutAuthor + ` (` + (elem.score * 100).toFixed(2) + '%)' + `\r\n
\r\n
\r\n `;\r\n });\r\n\r\n dest.innerHTML += '
';\r\n\r\n document.getElementById('plant-rec-action-icon').classList.remove('fa-spinner');\r\n document.getElementById('plant-rec-action-icon').classList.remove('fa-spin');\r\n document.getElementById('plant-rec-action-icon').classList.add('fa-microscope');\r\n\r\n window.vue.bShowSelectRecognizedPlant = true;\r\n } else {\r\n alert(response.msg);\r\n }\r\n });\r\n },\r\n\r\n storeRecognizedPlantData: function(target, update_name, update_scientific_name) {\r\n const selection = document.getElementById(target).getElementsByTagName('input');\r\n\r\n for (let i = 0; i < selection.length; i++) {\r\n if (selection[i].checked) {\r\n const item = selection[i];\r\n\r\n window.plantRecStorageStep = 0;\r\n window.plantRecErrorCount = 0;\r\n\r\n if (update_name) {\r\n window.vue.ajaxRequest('post', window.location.origin + '/plants/details/edit/ajax', {\r\n plant: item.dataset.plantid,\r\n attribute: 'name',\r\n value: item.dataset.plantname\r\n }, function(response) {\r\n window.plantRecStorageStep++;\r\n \r\n if (response.code == 500) {\r\n window.plantRecErrorCount++;\r\n alert(response.msg);\r\n }\r\n });\r\n } else {\r\n window.plantRecStorageStep++;\r\n }\r\n\r\n if (update_scientific_name) {\r\n window.vue.ajaxRequest('post', window.location.origin + '/plants/details/edit/ajax', {\r\n plant: item.dataset.plantid,\r\n attribute: 'scientific_name',\r\n value: item.dataset.plantscientificname\r\n }, function(response) {\r\n window.plantRecStorageStep++;\r\n\r\n if (response.code == 500) {\r\n window.plantRecErrorCount++;\r\n alert(response.msg);\r\n }\r\n });\r\n } else {\r\n window.plantRecStorageStep++;\r\n }\r\n\r\n setTimeout(function awaitPlantAttributeStorage() {\r\n if (window.plantRecStorageStep < 2) {\r\n setTimeout(awaitPlantAttributeStorage, 1000);\r\n } else {\r\n location.reload();\r\n }\r\n }, 100);\r\n\r\n break;\r\n }\r\n }\r\n },\r\n\r\n recognizedPlantsGroupSelectionValid: function(group, type) {\r\n let elems = document.getElementById(group).querySelectorAll('input[type=\"' + type + '\"]');\r\n\r\n for (let i = 0; i < elems.length; i++) {\r\n if (elems[i].checked) {\r\n return true;\r\n }\r\n }\r\n\r\n return false;\r\n },\r\n\r\n allRecognizedPlantSelectionGroupsValid: function() {\r\n return (window.vue.recognizedPlantsGroupSelectionValid('recognized-plant-selection', 'radio')) && (window.vue.recognizedPlantsGroupSelectionValid('plants-attribute-selection', 'checkbox'));\r\n },\r\n\r\n quickPlantRecognition: function(target, actionIcon, destContent) {\r\n const form = document.getElementById(target);\r\n const data = new FormData(form);\r\n\r\n window.vue.ajaxRequest('post', window.location.origin + '/plants/details/identify', data, function(response) {\r\n if (response.code == 200) {\r\n let dest = document.getElementById(destContent);\r\n\r\n dest.innerHTML = '
';\r\n\r\n response.data.forEach(function(elem, index) {\r\n dest.innerHTML += `\r\n
\r\n
\r\n
` + elem.species.scientificNameWithoutAuthor + ` (` + (elem.score * 100).toFixed(2) + '%)' + `
\r\n
\r\n
\r\n `;\r\n });\r\n\r\n dest.innerHTML += '
';\r\n\r\n document.getElementById(actionIcon).classList.remove('fa-spinner');\r\n document.getElementById(actionIcon).classList.remove('fa-spin');\r\n document.getElementById(actionIcon).classList.add('fa-microscope');\r\n\r\n window.vue.bShowQuickScanPlant = true;\r\n } else {\r\n alert(response.msg);\r\n }\r\n });\r\n },\r\n\r\n saveLocationNotes: function(location, notes, reselem) {\r\n let elem = document.getElementById(notes);\r\n if (elem) {\r\n let elNotes = document.getElementById(notes);\r\n\r\n window.vue.ajaxRequest('post', window.location.origin + '/plants/location/' + location + '/notes/save', { notes: elNotes.value }, function(response) {\r\n if (response.code == 200) {\r\n let elResult = document.getElementById(reselem);\r\n if (elResult) {\r\n elResult.innerHTML = '';\r\n }\r\n } else {\r\n alert(response.msg);\r\n }\r\n });\r\n }\r\n },\r\n\r\n validateAndSubmitForm: function(form, button) {\r\n let origtext = button.innerHTML;\r\n button.innerHTML = ' ' + window.vue.loading_please_wait; \r\n\r\n if (form.checkValidity()) {\r\n form.submit();\r\n } else {\r\n form.reportValidity();\r\n button.innerHTML = origtext;\r\n }\r\n },\r\n\r\n fixQuickScanPos: function(pwa = false) {\r\n let quickscanwidget = document.querySelector('.quickscan');\r\n if (quickscanwidget) {\r\n quickscanwidget.style.bottom = '12px';\r\n\r\n if ((pwa) && (window.innerWidth <= 1089)) {\r\n quickscanwidget.style.bottom = '83px';\r\n }\r\n }\r\n },\r\n\r\n clearCache: function(button) {\r\n window.vue.ajaxRequest('post', window.location.origin + '/admin/cache/clear', {}, function(response) {\r\n if (response.code == 200) {\r\n button.innerHTML = ' ' + button.innerHTML;\r\n button.setAttribute('disabled', 'disabled');\r\n } else {\r\n alert(response.msg);\r\n }\r\n });\r\n },\r\n\r\n sendTestMail: function(button) {\r\n if (window.vue.origTestMailButtonContent === '') {\r\n window.vue.origTestMailButtonContent = button.innerHTML;\r\n }\r\n\r\n button.innerHTML = ' ' + window.vue.origTestMailButtonContent;\r\n\r\n window.vue.ajaxRequest('post', window.location.origin + '/admin/mail/test', {}, function(response) {\r\n if (response.code == 200) {\r\n button.innerHTML = ' ' + window.vue.origTestMailButtonContent;\r\n } else {\r\n alert(response.msg);\r\n }\r\n });\r\n },\r\n\r\n scrollTo: function(target) {\r\n let elem = document.querySelector(target);\r\n if (elem) {\r\n elem.scrollIntoView({ behavior: 'smooth' });\r\n }\r\n },\r\n\r\n copyToClipboard: function(text) {\r\n const el = document.createElement('textarea');\r\n el.value = text;\r\n document.body.appendChild(el);\r\n el.select();\r\n document.execCommand('copy');\r\n document.body.removeChild(el);\r\n alert(window.vue.copiedToClipboard);\r\n },\r\n\r\n isProgressiveWebApp: function() {\r\n return window.matchMedia('(display-mode: standalone)').matches;\r\n },\r\n }\r\n});\n\n//# sourceURL=webpack://asatruphp/./app/resources/js/app.js?\n}"); /***/ }),