| 
									
										
										
										
											2021-09-17 22:34:23 +02:00
										 |  |  | function reloadFrontendApp(reason) { | 
					
						
							|  |  |  |     if (reason) { | 
					
						
							| 
									
										
										
										
											2022-12-21 15:19:05 +01:00
										 |  |  |         logInfo(`Frontend app reload: ${reason}`); | 
					
						
							| 
									
										
										
										
											2021-09-17 22:34:23 +02:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-15 21:04:17 +01:00
										 |  |  |     window.location.reload(); | 
					
						
							| 
									
										
										
										
											2018-03-25 11:09:17 -04:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function parseDate(str) { | 
					
						
							|  |  |  |     try { | 
					
						
							|  |  |  |         return new Date(Date.parse(str)); | 
					
						
							| 
									
										
										
										
											2018-03-24 23:37:55 -04:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-03-25 11:09:17 -04:00
										 |  |  |     catch (e) { | 
					
						
							| 
									
										
										
										
											2023-04-20 00:11:09 +02:00
										 |  |  |         throw new Error(`Can't parse date from '${str}': ${e.message} ${e.stack}`); | 
					
						
							| 
									
										
										
										
											2018-03-24 23:37:55 -04:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-03-25 11:09:17 -04:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2018-03-24 23:37:55 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-25 11:09:17 -04:00
										 |  |  | function padNum(num) { | 
					
						
							| 
									
										
										
										
											2022-12-21 15:19:05 +01:00
										 |  |  |     return `${num <= 9 ? "0" : ""}${num}`; | 
					
						
							| 
									
										
										
										
											2018-03-25 11:09:17 -04:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2018-03-24 23:37:55 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-25 11:09:17 -04:00
										 |  |  | function formatTime(date) { | 
					
						
							| 
									
										
										
										
											2022-12-21 15:19:05 +01:00
										 |  |  |     return `${padNum(date.getHours())}:${padNum(date.getMinutes())}`; | 
					
						
							| 
									
										
										
										
											2018-03-25 11:09:17 -04:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2018-03-24 23:37:55 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-25 11:09:17 -04:00
										 |  |  | function formatTimeWithSeconds(date) { | 
					
						
							| 
									
										
										
										
											2022-12-21 15:19:05 +01:00
										 |  |  |     return `${padNum(date.getHours())}:${padNum(date.getMinutes())}:${padNum(date.getSeconds())}`; | 
					
						
							| 
									
										
										
										
											2018-03-25 11:09:17 -04:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2017-12-23 11:02:38 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-21 00:19:17 +02:00
										 |  |  | function formatTimeInterval(ms) { | 
					
						
							|  |  |  |     const seconds = Math.round(ms / 1000); | 
					
						
							|  |  |  |     const minutes = Math.floor(seconds / 60); | 
					
						
							|  |  |  |     const hours = Math.floor(minutes / 60); | 
					
						
							|  |  |  |     const days = Math.floor(hours / 24); | 
					
						
							|  |  |  |     const plural = (count, name) => `${count} ${name}${count > 1 ? 's' : ''}`; | 
					
						
							|  |  |  |     const segments = []; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (days > 0) { | 
					
						
							|  |  |  |         segments.push(plural(days, 'day')); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (days < 2) { | 
					
						
							|  |  |  |         if (hours % 24 > 0) { | 
					
						
							|  |  |  |             segments.push(plural(hours % 24, 'hour')); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (hours < 4) { | 
					
						
							|  |  |  |             if (minutes % 60 > 0) { | 
					
						
							|  |  |  |                 segments.push(plural(minutes % 60, 'minute')); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (minutes < 5) { | 
					
						
							|  |  |  |                 if (seconds % 60 > 0) { | 
					
						
							|  |  |  |                     segments.push(plural(seconds % 60, 'second')); | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return segments.join(", "); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-16 22:03:30 +01:00
										 |  |  | // this is producing local time!
 | 
					
						
							| 
									
										
										
										
											2018-03-25 11:09:17 -04:00
										 |  |  | function formatDate(date) { | 
					
						
							| 
									
										
										
										
											2023-04-08 21:07:48 +08:00
										 |  |  |     //    return padNum(date.getDate()) + ". " + padNum(date.getMonth() + 1) + ". " + date.getFullYear();
 | 
					
						
							| 
									
										
										
										
											2018-08-15 08:48:16 +02:00
										 |  |  |     // instead of european format we'll just use ISO as that's pretty unambiguous
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return formatDateISO(date); | 
					
						
							| 
									
										
										
										
											2018-03-25 11:09:17 -04:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2017-12-23 12:19:15 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-16 22:03:30 +01:00
										 |  |  | // this is producing local time!
 | 
					
						
							| 
									
										
										
										
											2018-03-25 11:09:17 -04:00
										 |  |  | function formatDateISO(date) { | 
					
						
							| 
									
										
										
										
											2022-12-21 15:19:05 +01:00
										 |  |  |     return `${date.getFullYear()}-${padNum(date.getMonth() + 1)}-${padNum(date.getDate())}`; | 
					
						
							| 
									
										
										
										
											2018-03-25 11:09:17 -04:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2017-12-23 12:19:15 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-25 11:09:17 -04:00
										 |  |  | function formatDateTime(date) { | 
					
						
							| 
									
										
										
										
											2022-12-21 15:19:05 +01:00
										 |  |  |     return `${formatDate(date)} ${formatTime(date)}`; | 
					
						
							| 
									
										
										
										
											2018-03-25 11:09:17 -04:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2017-12-28 19:00:31 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-26 14:26:57 +02:00
										 |  |  | function localNowDateTime() { | 
					
						
							| 
									
										
										
										
											2023-04-08 21:07:48 +08:00
										 |  |  |     return dayjs().format('YYYY-MM-DD HH:mm:ss.SSSZZ'); | 
					
						
							| 
									
										
										
										
											2020-04-26 14:26:57 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-25 11:09:17 -04:00
										 |  |  | function now() { | 
					
						
							|  |  |  |     return formatTimeWithSeconds(new Date()); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2018-01-22 23:18:08 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-16 21:41:15 +03:00
										 |  |  | /** | 
					
						
							|  |  |  |  * Returns `true` if the client is currently running under Electron, or `false` if running in a web browser. | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2018-03-25 11:09:17 -04:00
										 |  |  | function isElectron() { | 
					
						
							| 
									
										
										
										
											2019-02-09 19:25:55 +01:00
										 |  |  |     return !!(window && window.process && window.process.type); | 
					
						
							| 
									
										
										
										
											2018-03-25 11:09:17 -04:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2018-01-25 23:49:03 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-12-02 14:04:53 +01:00
										 |  |  | function isMac() { | 
					
						
							|  |  |  |     return navigator.platform.indexOf('Mac') > -1; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-09 16:48:00 +01:00
										 |  |  | function isCtrlKey(evt) { | 
					
						
							|  |  |  |     return (!isMac() && evt.ctrlKey) | 
					
						
							|  |  |  |         || (isMac() && evt.metaKey); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-25 11:09:17 -04:00
										 |  |  | function assertArguments() { | 
					
						
							|  |  |  |     for (const i in arguments) { | 
					
						
							|  |  |  |         if (!arguments[i]) { | 
					
						
							| 
									
										
										
										
											2018-08-06 22:29:03 +02:00
										 |  |  |             console.trace(`Argument idx#${i} should not be falsy: ${arguments[i]}`); | 
					
						
							| 
									
										
										
										
											2018-03-24 23:37:55 -04:00
										 |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-03-25 11:09:17 -04:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2018-03-06 23:04:35 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-24 21:07:55 +02:00
										 |  |  | const entityMap = { | 
					
						
							|  |  |  |     '&': '&', | 
					
						
							|  |  |  |     '<': '<', | 
					
						
							|  |  |  |     '>': '>', | 
					
						
							|  |  |  |     '"': '"', | 
					
						
							|  |  |  |     "'": ''', | 
					
						
							|  |  |  |     '/': '/', | 
					
						
							|  |  |  |     '`': '`', | 
					
						
							|  |  |  |     '=': '=' | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-25 11:09:17 -04:00
										 |  |  | function escapeHtml(str) { | 
					
						
							| 
									
										
										
										
											2020-06-24 21:07:55 +02:00
										 |  |  |     return str.replace(/[&<>"'`=\/]/g, s => entityMap[s]); | 
					
						
							| 
									
										
										
										
											2018-03-25 11:09:17 -04:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2018-03-24 23:37:55 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-24 10:57:32 +01:00
										 |  |  | function formatSize(size) { | 
					
						
							|  |  |  |     size = Math.max(Math.round(size / 1024), 1); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (size < 1024) { | 
					
						
							|  |  |  |         return `${size} KiB`; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else { | 
					
						
							|  |  |  |         return `${Math.round(size / 102.4) / 10} MiB`; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-25 11:09:17 -04:00
										 |  |  | function toObject(array, fn) { | 
					
						
							|  |  |  |     const obj = {}; | 
					
						
							| 
									
										
										
										
											2018-03-04 23:28:26 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-25 11:09:17 -04:00
										 |  |  |     for (const item of array) { | 
					
						
							| 
									
										
										
										
											2018-03-25 23:25:17 -04:00
										 |  |  |         const [key, value] = fn(item); | 
					
						
							| 
									
										
										
										
											2018-03-24 23:37:55 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-25 23:25:17 -04:00
										 |  |  |         obj[key] = value; | 
					
						
							| 
									
										
										
										
											2018-03-04 23:28:26 -05:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-25 11:09:17 -04:00
										 |  |  |     return obj; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2018-03-12 23:27:21 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-25 11:09:17 -04:00
										 |  |  | function randomString(len) { | 
					
						
							|  |  |  |     let text = ""; | 
					
						
							|  |  |  |     const possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; | 
					
						
							| 
									
										
										
										
											2018-03-12 23:27:21 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-25 11:09:17 -04:00
										 |  |  |     for (let i = 0; i < len; i++) { | 
					
						
							|  |  |  |         text += possible.charAt(Math.floor(Math.random() * possible.length)); | 
					
						
							| 
									
										
										
										
											2018-03-12 23:27:21 -04:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-25 11:09:17 -04:00
										 |  |  |     return text; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-12-24 10:10:36 +01:00
										 |  |  | function isMobile() { | 
					
						
							| 
									
										
										
										
											2023-05-03 22:49:24 +02:00
										 |  |  |     return window.glob?.device === "mobile" | 
					
						
							|  |  |  |         // window.glob.device is not available in setup
 | 
					
						
							|  |  |  |         || (!window.glob?.device && /Mobi/.test(navigator.userAgent)); | 
					
						
							| 
									
										
										
										
											2018-12-24 10:10:36 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function isDesktop() { | 
					
						
							| 
									
										
										
										
											2023-05-03 22:49:24 +02:00
										 |  |  |     return window.glob?.device === "desktop" | 
					
						
							|  |  |  |         // window.glob.device is not available in setup
 | 
					
						
							|  |  |  |         || (!window.glob?.device && !/Mobi/.test(navigator.userAgent)); | 
					
						
							| 
									
										
										
										
											2018-12-24 10:10:36 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-30 11:18:34 +02:00
										 |  |  | // the cookie code below works for simple use cases only - ASCII only
 | 
					
						
							|  |  |  | // not setting a path so that cookies do not leak into other websites if multiplexed with reverse proxy
 | 
					
						
							| 
									
										
										
										
											2019-03-13 21:53:09 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-12-29 00:09:16 +01:00
										 |  |  | function setCookie(name, value) { | 
					
						
							|  |  |  |     const date = new Date(Date.now() + 10 * 365 * 24 * 60 * 60 * 1000); | 
					
						
							| 
									
										
										
										
											2022-12-21 15:19:05 +01:00
										 |  |  |     const expires = `; expires=${date.toUTCString()}`; | 
					
						
							| 
									
										
										
										
											2018-12-29 00:09:16 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-21 15:19:05 +01:00
										 |  |  |     document.cookie = `${name}=${value || ""}${expires};`; | 
					
						
							| 
									
										
										
										
											2019-03-13 21:53:09 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-28 21:42:37 +01:00
										 |  |  | function getNoteTypeClass(type) { | 
					
						
							| 
									
										
										
										
											2022-12-21 15:19:05 +01:00
										 |  |  |     return `type-${type}`; | 
					
						
							| 
									
										
										
										
											2019-01-28 21:42:37 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function getMimeTypeClass(mime) { | 
					
						
							| 
									
										
										
										
											2021-02-22 21:59:37 +01:00
										 |  |  |     if (!mime) { | 
					
						
							|  |  |  |         return ""; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-28 21:42:37 +01:00
										 |  |  |     const semicolonIdx = mime.indexOf(';'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (semicolonIdx !== -1) { | 
					
						
							|  |  |  |         // stripping everything following the semicolon
 | 
					
						
							|  |  |  |         mime = mime.substr(0, semicolonIdx); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-21 15:19:05 +01:00
										 |  |  |     return `mime-${mime.toLowerCase().replace(/[\W_]+/g, "-")}`; | 
					
						
							| 
									
										
										
										
											2019-01-28 21:42:37 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-10 22:45:03 +02:00
										 |  |  | function closeActiveDialog() { | 
					
						
							|  |  |  |     if (glob.activeDialog) { | 
					
						
							|  |  |  |         glob.activeDialog.modal('hide'); | 
					
						
							| 
									
										
										
										
											2020-02-09 10:00:13 +01:00
										 |  |  |         glob.activeDialog = null; | 
					
						
							| 
									
										
										
										
											2019-06-10 22:45:03 +02:00
										 |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-09 10:00:13 +01:00
										 |  |  | let $lastFocusedElement = null; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-04 23:35:10 +02:00
										 |  |  | // perhaps there should be saved focused element per tab?
 | 
					
						
							| 
									
										
										
										
											2020-02-09 10:00:13 +01:00
										 |  |  | function saveFocusedElement() { | 
					
						
							|  |  |  |     $lastFocusedElement = $(":focus"); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function focusSavedElement() { | 
					
						
							|  |  |  |     if (!$lastFocusedElement) { | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-23 22:47:20 +01:00
										 |  |  |     if ($lastFocusedElement.hasClass("ck")) { | 
					
						
							|  |  |  |         // must handle CKEditor separately because of this bug: https://github.com/ckeditor/ckeditor5/issues/607
 | 
					
						
							|  |  |  |         // the bug manifests itself in resetting the cursor position to the first character - jumping above
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         const editor = $lastFocusedElement | 
					
						
							|  |  |  |             .closest('.ck-editor__editable') | 
					
						
							|  |  |  |             .prop('ckeditorInstance'); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-03 13:26:33 +01:00
										 |  |  |         if (editor) { | 
					
						
							|  |  |  |             editor.editing.view.focus(); | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |             console.log("Could not find CKEditor instance to focus last element"); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2022-11-23 22:47:20 +01:00
										 |  |  |     } else { | 
					
						
							|  |  |  |         $lastFocusedElement.focus(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-09 10:00:13 +01:00
										 |  |  |     $lastFocusedElement = null; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-01-10 17:09:20 +01:00
										 |  |  | async function openDialog($dialog, closeActDialog = true) { | 
					
						
							|  |  |  |     if (closeActDialog) { | 
					
						
							|  |  |  |         closeActiveDialog(); | 
					
						
							|  |  |  |         glob.activeDialog = $dialog; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2020-02-09 10:00:13 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     saveFocusedElement(); | 
					
						
							| 
									
										
										
										
											2022-11-23 22:47:20 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-09 10:00:13 +01:00
										 |  |  |     $dialog.modal(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     $dialog.on('hidden.bs.modal', () => { | 
					
						
							| 
									
										
										
										
											2022-10-26 16:52:44 +02:00
										 |  |  |         $(".aa-input").autocomplete("close"); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-09 10:00:13 +01:00
										 |  |  |         if (!glob.activeDialog || glob.activeDialog === $dialog) { | 
					
						
							|  |  |  |             focusSavedElement(); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2020-04-18 11:14:09 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     const keyboardActionsService = (await import("./keyboard_actions.js")).default; | 
					
						
							|  |  |  |     keyboardActionsService.updateDisplayedShortcuts($dialog); | 
					
						
							| 
									
										
										
										
											2020-02-09 10:00:13 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-05 20:27:30 +02:00
										 |  |  | function isHtmlEmpty(html) { | 
					
						
							| 
									
										
										
										
											2020-03-26 16:59:40 +01:00
										 |  |  |     if (!html) { | 
					
						
							|  |  |  |         return true; | 
					
						
							| 
									
										
										
										
											2023-06-14 22:21:22 +02:00
										 |  |  |     } else if (typeof html !== 'string') { | 
					
						
							|  |  |  |         logError(`Got object of type '${typeof html}' where string was expected.`); | 
					
						
							|  |  |  |         return false; | 
					
						
							| 
									
										
										
										
											2020-03-26 16:59:40 +01:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-04 13:22:07 +01:00
										 |  |  |     html = html.toLowerCase(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-29 17:11:44 +02:00
										 |  |  |     return !html.includes('<img') | 
					
						
							|  |  |  |         && !html.includes('<section') | 
					
						
							| 
									
										
										
										
											2023-06-14 22:21:22 +02:00
										 |  |  |         // the line below will actually attempt to load images so better to check for images first
 | 
					
						
							| 
									
										
										
										
											2020-03-29 17:11:44 +02:00
										 |  |  |         && $("<div>").html(html).text().trim().length === 0; | 
					
						
							| 
									
										
										
										
											2019-10-05 20:27:30 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-08 23:09:57 +01:00
										 |  |  | async function clearBrowserCache() { | 
					
						
							|  |  |  |     if (isElectron()) { | 
					
						
							| 
									
										
										
										
											2022-06-13 22:38:59 +02:00
										 |  |  |         const win = dynamicRequire('@electron/remote').getCurrentWindow(); | 
					
						
							| 
									
										
										
										
											2019-11-08 23:09:57 +01:00
										 |  |  |         await win.webContents.session.clearCache(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-19 11:16:36 +03:00
										 |  |  | function copySelectionToClipboard() { | 
					
						
							| 
									
										
										
										
											2020-01-19 09:25:35 +01:00
										 |  |  |     const text = window.getSelection().toString(); | 
					
						
							| 
									
										
										
										
											2020-01-19 11:16:36 +03:00
										 |  |  |     if (navigator.clipboard) { | 
					
						
							| 
									
										
										
										
											2020-01-19 09:25:35 +01:00
										 |  |  |         navigator.clipboard.writeText(text); | 
					
						
							| 
									
										
										
										
											2020-01-19 11:16:36 +03:00
										 |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-12 14:22:51 +02:00
										 |  |  | function dynamicRequire(moduleName) { | 
					
						
							|  |  |  |     if (typeof __non_webpack_require__ !== 'undefined') { | 
					
						
							|  |  |  |         return __non_webpack_require__(moduleName); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else { | 
					
						
							|  |  |  |         return require(moduleName); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-20 23:17:29 +01:00
										 |  |  | function timeLimit(promise, limitMs, errorMessage) { | 
					
						
							|  |  |  |     if (!promise || !promise.then) { // it's not actually a promise
 | 
					
						
							|  |  |  |         return promise; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-29 23:34:49 +02:00
										 |  |  |     // better stack trace if created outside of promise
 | 
					
						
							| 
									
										
										
										
											2021-02-20 23:17:29 +01:00
										 |  |  |     const error = new Error(errorMessage || `Process exceeded time limit ${limitMs}`); | 
					
						
							| 
									
										
										
										
											2020-07-29 23:34:49 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-11 00:13:56 +02:00
										 |  |  |     return new Promise((res, rej) => { | 
					
						
							|  |  |  |         let resolved = false; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-13 22:34:15 +02:00
										 |  |  |         promise.then(result => { | 
					
						
							| 
									
										
										
										
											2020-06-11 00:13:56 +02:00
										 |  |  |             resolved = true; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-13 22:34:15 +02:00
										 |  |  |             res(result); | 
					
						
							| 
									
										
										
										
											2020-06-11 00:13:56 +02:00
										 |  |  |         }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         setTimeout(() => { | 
					
						
							|  |  |  |             if (!resolved) { | 
					
						
							| 
									
										
										
										
											2020-07-29 23:34:49 +02:00
										 |  |  |                 rej(error); | 
					
						
							| 
									
										
										
										
											2020-06-11 00:13:56 +02:00
										 |  |  |             } | 
					
						
							|  |  |  |         }, limitMs); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-26 14:44:53 +01:00
										 |  |  | function initHelpDropdown($el) { | 
					
						
							|  |  |  |     // stop inside clicks from closing the menu
 | 
					
						
							|  |  |  |     const $dropdownMenu = $el.find('.help-dropdown .dropdown-menu'); | 
					
						
							|  |  |  |     $dropdownMenu.on('click', e => e.stopPropagation()); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-15 21:04:17 +01:00
										 |  |  |     // previous propagation stop will also block help buttons from being opened, so we need to re-init for this element
 | 
					
						
							| 
									
										
										
										
											2021-01-26 14:44:53 +01:00
										 |  |  |     initHelpButtons($dropdownMenu); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-10 00:26:39 +03:00
										 |  |  | const wikiBaseUrl = "https://triliumnext.github.io/Docs/Wiki/"; | 
					
						
							| 
									
										
										
										
											2021-01-26 14:44:53 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-14 21:59:43 +02:00
										 |  |  | function openHelp($button) { | 
					
						
							|  |  |  |     const helpPage = $button.attr("data-help-page"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (helpPage) { | 
					
						
							|  |  |  |         const url = wikiBaseUrl + helpPage; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         window.open(url, '_blank'); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-12-20 17:30:47 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-26 14:44:53 +01:00
										 |  |  | function initHelpButtons($el) { | 
					
						
							| 
									
										
										
										
											2023-06-29 23:32:19 +02:00
										 |  |  |     // for some reason, the .on(event, listener, handler) does not work here (e.g. Options -> Sync -> Help button)
 | 
					
						
							| 
									
										
										
										
											2022-12-08 15:18:41 +01:00
										 |  |  |     // so we do it manually
 | 
					
						
							|  |  |  |     $el.on("click", e => { | 
					
						
							| 
									
										
										
										
											2023-07-14 21:59:43 +02:00
										 |  |  |         const $helpButton = $(e.target).closest("[data-help-page]"); | 
					
						
							|  |  |  |         openHelp($helpButton); | 
					
						
							| 
									
										
										
										
											2022-12-08 15:18:41 +01:00
										 |  |  |     }); | 
					
						
							| 
									
										
										
										
											2021-01-26 14:44:53 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-17 23:22:14 +01:00
										 |  |  | function filterAttributeName(name) { | 
					
						
							|  |  |  |     return name.replace(/[^\p{L}\p{N}_:]/ug, ""); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const ATTR_NAME_MATCHER = new RegExp("^[\\p{L}\\p{N}_:]+$", "u"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function isValidAttributeName(name) { | 
					
						
							|  |  |  |     return ATTR_NAME_MATCHER.test(name); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-09 16:50:06 +02:00
										 |  |  | function sleep(time_ms) { | 
					
						
							|  |  |  |     return new Promise((resolve) => { | 
					
						
							|  |  |  |         setTimeout(resolve, time_ms); | 
					
						
							|  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2022-05-13 23:20:56 +02:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2022-05-09 16:50:06 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-25 23:38:06 +02:00
										 |  |  | function escapeRegExp(str) { | 
					
						
							|  |  |  |     return str.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1"); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-16 18:33:39 +03:00
										 |  |  | function areObjectsEqual() { | 
					
						
							| 
									
										
										
										
											2023-05-05 23:17:23 +02:00
										 |  |  |     let i, l, leftChain, rightChain; | 
					
						
							| 
									
										
										
										
											2023-04-11 17:45:51 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-16 18:33:39 +03:00
										 |  |  |     function compare2Objects(x, y) { | 
					
						
							| 
									
										
										
										
											2023-05-05 23:17:23 +02:00
										 |  |  |         let p; | 
					
						
							| 
									
										
										
										
											2023-04-11 17:45:51 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         // remember that NaN === NaN returns false
 | 
					
						
							|  |  |  |         // and isNaN(undefined) returns true
 | 
					
						
							|  |  |  |         if (isNaN(x) && isNaN(y) && typeof x === 'number' && typeof y === 'number') { | 
					
						
							|  |  |  |             return true; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Compare primitives and functions.
 | 
					
						
							|  |  |  |         // Check if both arguments link to the same object.
 | 
					
						
							|  |  |  |         // Especially useful on the step where we compare prototypes
 | 
					
						
							|  |  |  |         if (x === y) { | 
					
						
							|  |  |  |             return true; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Works in case when functions are created in constructor.
 | 
					
						
							|  |  |  |         // Comparing dates is a common scenario. Another built-ins?
 | 
					
						
							|  |  |  |         // We can even handle functions passed across iframes
 | 
					
						
							|  |  |  |         if ((typeof x === 'function' && typeof y === 'function') || | 
					
						
							|  |  |  |             (x instanceof Date && y instanceof Date) || | 
					
						
							|  |  |  |             (x instanceof RegExp && y instanceof RegExp) || | 
					
						
							|  |  |  |             (x instanceof String && y instanceof String) || | 
					
						
							|  |  |  |             (x instanceof Number && y instanceof Number)) { | 
					
						
							|  |  |  |             return x.toString() === y.toString(); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-30 11:18:34 +02:00
										 |  |  |         // At last, checking prototypes as good as we can
 | 
					
						
							| 
									
										
										
										
											2023-04-11 17:45:51 +02:00
										 |  |  |         if (!(x instanceof Object && y instanceof Object)) { | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (x.isPrototypeOf(y) || y.isPrototypeOf(x)) { | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (x.constructor !== y.constructor) { | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (x.prototype !== y.prototype) { | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Check for infinitive linking loops
 | 
					
						
							|  |  |  |         if (leftChain.indexOf(x) > -1 || rightChain.indexOf(y) > -1) { | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Quick checking of one object being a subset of another.
 | 
					
						
							|  |  |  |         // todo: cache the structure of arguments[0] for performance
 | 
					
						
							|  |  |  |         for (p in y) { | 
					
						
							|  |  |  |             if (y.hasOwnProperty(p) !== x.hasOwnProperty(p)) { | 
					
						
							|  |  |  |                 return false; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             else if (typeof y[p] !== typeof x[p]) { | 
					
						
							|  |  |  |                 return false; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         for (p in x) { | 
					
						
							|  |  |  |             if (y.hasOwnProperty(p) !== x.hasOwnProperty(p)) { | 
					
						
							|  |  |  |                 return false; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             else if (typeof y[p] !== typeof x[p]) { | 
					
						
							|  |  |  |                 return false; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             switch (typeof (x[p])) { | 
					
						
							|  |  |  |                 case 'object': | 
					
						
							|  |  |  |                 case 'function': | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                     leftChain.push(x); | 
					
						
							|  |  |  |                     rightChain.push(y); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-16 18:33:39 +03:00
										 |  |  |                     if (!compare2Objects(x[p], y[p])) { | 
					
						
							| 
									
										
										
										
											2023-04-11 17:45:51 +02:00
										 |  |  |                         return false; | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                     leftChain.pop(); | 
					
						
							|  |  |  |                     rightChain.pop(); | 
					
						
							|  |  |  |                     break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 default: | 
					
						
							|  |  |  |                     if (x[p] !== y[p]) { | 
					
						
							|  |  |  |                         return false; | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                     break; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return true; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (arguments.length < 1) { | 
					
						
							|  |  |  |         return true; //Die silently? Don't know how to handle such case, please help...
 | 
					
						
							|  |  |  |         // throw "Need two or more arguments to compare";
 | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for (i = 1, l = arguments.length; i < l; i++) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         leftChain = []; //Todo: this can be cached
 | 
					
						
							|  |  |  |         rightChain = []; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (!compare2Objects(arguments[0], arguments[i])) { | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return true; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-29 00:19:54 +02:00
										 |  |  | function copyHtmlToClipboard(content) { | 
					
						
							| 
									
										
										
										
											2024-03-27 20:42:36 +01:00
										 |  |  |     function listener(e) { | 
					
						
							|  |  |  |         e.clipboardData.setData("text/html", content); | 
					
						
							|  |  |  |         e.clipboardData.setData("text/plain", content); | 
					
						
							|  |  |  |         e.preventDefault(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     document.addEventListener("copy", listener); | 
					
						
							|  |  |  |     document.execCommand("copy"); | 
					
						
							|  |  |  |     document.removeEventListener("copy", listener); | 
					
						
							| 
									
										
										
										
											2023-05-29 00:19:54 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-19 09:20:23 +02:00
										 |  |  | /** | 
					
						
							|  |  |  |  * @param {FNote} note | 
					
						
							|  |  |  |  * @return {string} | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | function createImageSrcUrl(note) { | 
					
						
							|  |  |  |     return `api/images/${note.noteId}/${encodeURIComponent(note.title)}?timestamp=${Date.now()}`; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-01 23:20:41 +03:00
										 |  |  | /** | 
					
						
							|  |  |  |  * Given a string representation of an SVG, triggers a download of the file on the client device. | 
					
						
							|  |  |  |  *  | 
					
						
							|  |  |  |  * @param {string} nameWithoutExtension the name of the file. The .svg suffix is automatically added to it. | 
					
						
							|  |  |  |  * @param {string} svgContent the content of the SVG file download. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | function downloadSvg(nameWithoutExtension, svgContent) { | 
					
						
							|  |  |  |     const filename = `${nameWithoutExtension}.svg`; | 
					
						
							|  |  |  |     const element = document.createElement('a'); | 
					
						
							|  |  |  |     element.setAttribute('href', `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svgContent)}`); | 
					
						
							|  |  |  |     element.setAttribute('download', filename); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     element.style.display = 'none'; | 
					
						
							|  |  |  |     document.body.appendChild(element); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     element.click(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     document.body.removeChild(element); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-16 18:33:39 +03:00
										 |  |  | export default { | 
					
						
							| 
									
										
										
										
											2021-08-24 22:59:51 +02:00
										 |  |  |     reloadFrontendApp, | 
					
						
							| 
									
										
										
										
											2018-03-25 11:09:17 -04:00
										 |  |  |     parseDate, | 
					
						
							|  |  |  |     formatDateISO, | 
					
						
							|  |  |  |     formatDateTime, | 
					
						
							| 
									
										
										
										
											2023-04-21 00:19:17 +02:00
										 |  |  |     formatTimeInterval, | 
					
						
							| 
									
										
										
										
											2023-03-24 10:57:32 +01:00
										 |  |  |     formatSize, | 
					
						
							| 
									
										
										
										
											2020-04-26 14:26:57 +02:00
										 |  |  |     localNowDateTime, | 
					
						
							| 
									
										
										
										
											2018-03-25 11:09:17 -04:00
										 |  |  |     now, | 
					
						
							|  |  |  |     isElectron, | 
					
						
							| 
									
										
										
										
											2018-12-02 14:04:53 +01:00
										 |  |  |     isMac, | 
					
						
							| 
									
										
										
										
											2022-12-09 16:48:00 +01:00
										 |  |  |     isCtrlKey, | 
					
						
							| 
									
										
										
										
											2018-03-25 11:09:17 -04:00
										 |  |  |     assertArguments, | 
					
						
							|  |  |  |     escapeHtml, | 
					
						
							|  |  |  |     toObject, | 
					
						
							| 
									
										
										
										
											2018-03-25 19:49:33 -04:00
										 |  |  |     randomString, | 
					
						
							| 
									
										
										
										
											2018-12-24 10:10:36 +01:00
										 |  |  |     isMobile, | 
					
						
							| 
									
										
										
										
											2018-12-29 00:09:16 +01:00
										 |  |  |     isDesktop, | 
					
						
							| 
									
										
										
										
											2019-01-28 21:42:37 +01:00
										 |  |  |     setCookie, | 
					
						
							|  |  |  |     getNoteTypeClass, | 
					
						
							| 
									
										
										
										
											2019-06-10 22:45:03 +02:00
										 |  |  |     getMimeTypeClass, | 
					
						
							| 
									
										
										
										
											2019-10-05 20:27:30 +02:00
										 |  |  |     closeActiveDialog, | 
					
						
							| 
									
										
										
										
											2020-02-09 10:00:13 +01:00
										 |  |  |     openDialog, | 
					
						
							|  |  |  |     saveFocusedElement, | 
					
						
							|  |  |  |     focusSavedElement, | 
					
						
							| 
									
										
										
										
											2019-11-08 23:09:57 +01:00
										 |  |  |     isHtmlEmpty, | 
					
						
							| 
									
										
										
										
											2019-11-22 20:35:17 +01:00
										 |  |  |     clearBrowserCache, | 
					
						
							| 
									
										
										
										
											2020-02-02 16:28:19 +08:00
										 |  |  |     copySelectionToClipboard, | 
					
						
							| 
									
										
										
										
											2020-06-11 00:13:56 +02:00
										 |  |  |     dynamicRequire, | 
					
						
							| 
									
										
										
										
											2021-01-26 14:44:53 +01:00
										 |  |  |     timeLimit, | 
					
						
							|  |  |  |     initHelpDropdown, | 
					
						
							| 
									
										
										
										
											2021-02-17 23:22:14 +01:00
										 |  |  |     initHelpButtons, | 
					
						
							| 
									
										
										
										
											2021-12-20 17:30:47 +01:00
										 |  |  |     openHelp, | 
					
						
							| 
									
										
										
										
											2021-02-17 23:22:14 +01:00
										 |  |  |     filterAttributeName, | 
					
						
							| 
									
										
										
										
											2022-05-09 16:50:06 +02:00
										 |  |  |     isValidAttributeName, | 
					
						
							|  |  |  |     sleep, | 
					
						
							| 
									
										
										
										
											2023-04-11 17:45:51 +02:00
										 |  |  |     escapeRegExp, | 
					
						
							| 
									
										
										
										
											2023-05-29 00:19:54 +02:00
										 |  |  |     areObjectsEqual, | 
					
						
							| 
									
										
										
										
											2023-10-19 09:20:23 +02:00
										 |  |  |     copyHtmlToClipboard, | 
					
						
							| 
									
										
										
										
											2024-09-01 23:20:41 +03:00
										 |  |  |     createImageSrcUrl, | 
					
						
							|  |  |  |     downloadSvg | 
					
						
							| 
									
										
										
										
											2020-05-20 00:03:33 +02:00
										 |  |  | }; |