mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-31 18:36:30 +01:00 
			
		
		
		
	client-ts: Port services/utils
This commit is contained in:
		
							
								
								
									
										20
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										20
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							| @@ -94,6 +94,7 @@ | ||||
|         "@electron-forge/plugin-auto-unpack-natives": "^6.4.2", | ||||
|         "@types/archiver": "^6.0.2", | ||||
|         "@types/better-sqlite3": "^7.6.9", | ||||
|         "@types/bootstrap": "^5.2.10", | ||||
|         "@types/cls-hooked": "^4.3.8", | ||||
|         "@types/compression": "^1.7.5", | ||||
|         "@types/cookie-parser": "^1.4.7", | ||||
| @@ -2060,6 +2061,16 @@ | ||||
|         "node": ">=14" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@popperjs/core": { | ||||
|       "version": "2.11.8", | ||||
|       "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", | ||||
|       "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", | ||||
|       "dev": true, | ||||
|       "funding": { | ||||
|         "type": "opencollective", | ||||
|         "url": "https://opencollective.com/popperjs" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@sindresorhus/is": { | ||||
|       "version": "4.6.0", | ||||
|       "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", | ||||
| @@ -2153,6 +2164,15 @@ | ||||
|         "@types/node": "*" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@types/bootstrap": { | ||||
|       "version": "5.2.10", | ||||
|       "resolved": "https://registry.npmjs.org/@types/bootstrap/-/bootstrap-5.2.10.tgz", | ||||
|       "integrity": "sha512-F2X+cd6551tep0MvVZ6nM8v7XgGN/twpdNDjqS1TUM7YFNEtQYWk+dKAnH+T1gr6QgCoGMPl487xw/9hXooa2g==", | ||||
|       "dev": true, | ||||
|       "dependencies": { | ||||
|         "@popperjs/core": "^2.9.2" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@types/cacheable-request": { | ||||
|       "version": "6.0.2", | ||||
|       "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.2.tgz", | ||||
|   | ||||
| @@ -128,6 +128,7 @@ | ||||
|     "@electron-forge/plugin-auto-unpack-natives": "^6.4.2", | ||||
|     "@types/archiver": "^6.0.2", | ||||
|     "@types/better-sqlite3": "^7.6.9", | ||||
|     "@types/bootstrap": "^5.2.10", | ||||
|     "@types/cls-hooked": "^4.3.8", | ||||
|     "@types/compression": "^1.7.5", | ||||
|     "@types/cookie-parser": "^1.4.7", | ||||
|   | ||||
| @@ -1,4 +1,6 @@ | ||||
| function reloadFrontendApp(reason) { | ||||
| import dayjs from "dayjs"; | ||||
| 
 | ||||
| function reloadFrontendApp(reason: string) { | ||||
|     if (reason) { | ||||
|         logInfo(`Frontend app reload: ${reason}`); | ||||
|     } | ||||
| @@ -6,33 +8,33 @@ function reloadFrontendApp(reason) { | ||||
|     window.location.reload(); | ||||
| } | ||||
| 
 | ||||
| function parseDate(str) { | ||||
| function parseDate(str: string) { | ||||
|     try { | ||||
|         return new Date(Date.parse(str)); | ||||
|     } | ||||
|     catch (e) { | ||||
|     catch (e: any) { | ||||
|         throw new Error(`Can't parse date from '${str}': ${e.message} ${e.stack}`); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| function padNum(num) { | ||||
| function padNum(num: number) { | ||||
|     return `${num <= 9 ? "0" : ""}${num}`; | ||||
| } | ||||
| 
 | ||||
| function formatTime(date) { | ||||
| function formatTime(date: Date) { | ||||
|     return `${padNum(date.getHours())}:${padNum(date.getMinutes())}`; | ||||
| } | ||||
| 
 | ||||
| function formatTimeWithSeconds(date) { | ||||
| function formatTimeWithSeconds(date: Date) { | ||||
|     return `${padNum(date.getHours())}:${padNum(date.getMinutes())}:${padNum(date.getSeconds())}`; | ||||
| } | ||||
| 
 | ||||
| function formatTimeInterval(ms) { | ||||
| function formatTimeInterval(ms: number) { | ||||
|     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 plural = (count: number, name: string) => `${count} ${name}${count > 1 ? 's' : ''}`; | ||||
|     const segments = []; | ||||
| 
 | ||||
|     if (days > 0) { | ||||
| @@ -60,20 +62,20 @@ function formatTimeInterval(ms) { | ||||
|     return segments.join(", "); | ||||
| } | ||||
| 
 | ||||
| // this is producing local time!
 | ||||
| function formatDate(date) { | ||||
| /** this is producing local time! **/ | ||||
| function formatDate(date: Date) { | ||||
|     //    return padNum(date.getDate()) + ". " + padNum(date.getMonth() + 1) + ". " + date.getFullYear();
 | ||||
|     // instead of european format we'll just use ISO as that's pretty unambiguous
 | ||||
| 
 | ||||
|     return formatDateISO(date); | ||||
| } | ||||
| 
 | ||||
| // this is producing local time!
 | ||||
| function formatDateISO(date) { | ||||
| /** this is producing local time! **/ | ||||
| function formatDateISO(date: Date) { | ||||
|     return `${date.getFullYear()}-${padNum(date.getMonth() + 1)}-${padNum(date.getDate())}`; | ||||
| } | ||||
| 
 | ||||
| function formatDateTime(date) { | ||||
| function formatDateTime(date: Date) { | ||||
|     return `${formatDate(date)} ${formatTime(date)}`; | ||||
| } | ||||
| 
 | ||||
| @@ -93,7 +95,7 @@ function isMac() { | ||||
|     return navigator.platform.indexOf('Mac') > -1; | ||||
| } | ||||
| 
 | ||||
| function isCtrlKey(evt) { | ||||
| function isCtrlKey(evt: KeyboardEvent) { | ||||
|     return (!isMac() && evt.ctrlKey) | ||||
|         || (isMac() && evt.metaKey); | ||||
| } | ||||
| @@ -106,7 +108,7 @@ function assertArguments() { | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| const entityMap = { | ||||
| const entityMap: Record<string, string> = { | ||||
|     '&': '&', | ||||
|     '<': '<', | ||||
|     '>': '>', | ||||
| @@ -117,11 +119,11 @@ const entityMap = { | ||||
|     '=': '=' | ||||
| }; | ||||
| 
 | ||||
| function escapeHtml(str) { | ||||
| function escapeHtml(str: string) { | ||||
|     return str.replace(/[&<>"'`=\/]/g, s => entityMap[s]); | ||||
| } | ||||
| 
 | ||||
| function formatSize(size) { | ||||
| function formatSize(size: number) { | ||||
|     size = Math.max(Math.round(size / 1024), 1); | ||||
| 
 | ||||
|     if (size < 1024) { | ||||
| @@ -132,8 +134,8 @@ function formatSize(size) { | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| function toObject(array, fn) { | ||||
|     const obj = {}; | ||||
| function toObject<T>(array: T[], fn: (arg0: T) => [key: string, value: T]) { | ||||
|     const obj: Record<string, T> = {}; | ||||
| 
 | ||||
|     for (const item of array) { | ||||
|         const [key, value] = fn(item); | ||||
| @@ -144,7 +146,7 @@ function toObject(array, fn) { | ||||
|     return obj; | ||||
| } | ||||
| 
 | ||||
| function randomString(len) { | ||||
| function randomString(len: number) { | ||||
|     let text = ""; | ||||
|     const possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; | ||||
| 
 | ||||
| @@ -167,21 +169,22 @@ function isDesktop() { | ||||
|         || (!window.glob?.device && !/Mobi/.test(navigator.userAgent)); | ||||
| } | ||||
| 
 | ||||
| // 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
 | ||||
| 
 | ||||
| function setCookie(name, value) { | ||||
| /** | ||||
|  * 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 | ||||
|  */ | ||||
| function setCookie(name: string, value: string) { | ||||
|     const date = new Date(Date.now() + 10 * 365 * 24 * 60 * 60 * 1000); | ||||
|     const expires = `; expires=${date.toUTCString()}`; | ||||
| 
 | ||||
|     document.cookie = `${name}=${value || ""}${expires};`; | ||||
| } | ||||
| 
 | ||||
| function getNoteTypeClass(type) { | ||||
| function getNoteTypeClass(type: string) { | ||||
|     return `type-${type}`; | ||||
| } | ||||
| 
 | ||||
| function getMimeTypeClass(mime) { | ||||
| function getMimeTypeClass(mime: string) { | ||||
|     if (!mime) { | ||||
|         return ""; | ||||
|     } | ||||
| @@ -203,7 +206,7 @@ function closeActiveDialog() { | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| let $lastFocusedElement = null; | ||||
| let $lastFocusedElement: JQuery<HTMLElement> | null; | ||||
| 
 | ||||
| // perhaps there should be saved focused element per tab?
 | ||||
| function saveFocusedElement() { | ||||
| @@ -235,7 +238,7 @@ function focusSavedElement() { | ||||
|     $lastFocusedElement = null; | ||||
| } | ||||
| 
 | ||||
| async function openDialog($dialog, closeActDialog = true) { | ||||
| async function openDialog($dialog: JQuery<HTMLElement>, closeActDialog = true) { | ||||
|     if (closeActDialog) { | ||||
|         closeActiveDialog(); | ||||
|         glob.activeDialog = $dialog; | ||||
| @@ -253,11 +256,13 @@ async function openDialog($dialog, closeActDialog = true) { | ||||
|         } | ||||
|     }); | ||||
| 
 | ||||
|     // TODO: Fix once keyboard_actions is ported.
 | ||||
|     // @ts-ignore
 | ||||
|     const keyboardActionsService = (await import("./keyboard_actions.js")).default; | ||||
|     keyboardActionsService.updateDisplayedShortcuts($dialog); | ||||
| } | ||||
| 
 | ||||
| function isHtmlEmpty(html) { | ||||
| function isHtmlEmpty(html: string) { | ||||
|     if (!html) { | ||||
|         return true; | ||||
|     } else if (typeof html !== 'string') { | ||||
| @@ -281,13 +286,13 @@ async function clearBrowserCache() { | ||||
| } | ||||
| 
 | ||||
| function copySelectionToClipboard() { | ||||
|     const text = window.getSelection().toString(); | ||||
|     if (navigator.clipboard) { | ||||
|     const text = window?.getSelection()?.toString(); | ||||
|     if (text && navigator.clipboard) { | ||||
|         navigator.clipboard.writeText(text); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| function dynamicRequire(moduleName) { | ||||
| function dynamicRequire(moduleName: string) { | ||||
|     if (typeof __non_webpack_require__ !== 'undefined') { | ||||
|         return __non_webpack_require__(moduleName); | ||||
|     } | ||||
| @@ -296,7 +301,7 @@ function dynamicRequire(moduleName) { | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| function timeLimit(promise, limitMs, errorMessage) { | ||||
| function timeLimit(promise: Promise<void>, limitMs: number, errorMessage: string) { | ||||
|     if (!promise || !promise.then) { // it's not actually a promise
 | ||||
|         return promise; | ||||
|     } | ||||
| @@ -321,7 +326,7 @@ function timeLimit(promise, limitMs, errorMessage) { | ||||
|     }); | ||||
| } | ||||
| 
 | ||||
| function initHelpDropdown($el) { | ||||
| function initHelpDropdown($el: JQuery<HTMLElement>) { | ||||
|     // stop inside clicks from closing the menu
 | ||||
|     const $dropdownMenu = $el.find('.help-dropdown .dropdown-menu'); | ||||
|     $dropdownMenu.on('click', e => e.stopPropagation()); | ||||
| @@ -332,7 +337,7 @@ function initHelpDropdown($el) { | ||||
| 
 | ||||
| const wikiBaseUrl = "https://github.com/zadam/trilium/wiki/"; | ||||
| 
 | ||||
| function openHelp($button) { | ||||
| function openHelp($button: JQuery<HTMLElement>) { | ||||
|     const helpPage = $button.attr("data-help-page"); | ||||
| 
 | ||||
|     if (helpPage) { | ||||
| @@ -342,7 +347,7 @@ function openHelp($button) { | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| function initHelpButtons($el) { | ||||
| function initHelpButtons($el: JQuery<HTMLElement>) { | ||||
|     // for some reason, the .on(event, listener, handler) does not work here (e.g. Options -> Sync -> Help button)
 | ||||
|     // so we do it manually
 | ||||
|     $el.on("click", e => { | ||||
| @@ -351,35 +356,38 @@ function initHelpButtons($el) { | ||||
|     }); | ||||
| } | ||||
| 
 | ||||
| function filterAttributeName(name) { | ||||
| function filterAttributeName(name: string) { | ||||
|     return name.replace(/[^\p{L}\p{N}_:]/ug, ""); | ||||
| } | ||||
| 
 | ||||
| const ATTR_NAME_MATCHER = new RegExp("^[\\p{L}\\p{N}_:]+$", "u"); | ||||
| 
 | ||||
| function isValidAttributeName(name) { | ||||
| function isValidAttributeName(name: string) { | ||||
|     return ATTR_NAME_MATCHER.test(name); | ||||
| } | ||||
| 
 | ||||
| function sleep(time_ms) { | ||||
| function sleep(time_ms: number) { | ||||
|     return new Promise((resolve) => { | ||||
|         setTimeout(resolve, time_ms); | ||||
|     }); | ||||
| } | ||||
| 
 | ||||
| function escapeRegExp(str) { | ||||
| function escapeRegExp(str: string) { | ||||
|     return str.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1"); | ||||
| } | ||||
| 
 | ||||
| function areObjectsEqual() { | ||||
|     let i, l, leftChain, rightChain; | ||||
| function areObjectsEqual () { | ||||
|     let i; | ||||
|     let l; | ||||
|     let leftChain: Object[]; | ||||
|     let rightChain: Object[]; | ||||
| 
 | ||||
|     function compare2Objects(x, y) { | ||||
|     function compare2Objects (x: unknown, y: unknown) { | ||||
|         let p; | ||||
| 
 | ||||
|         // remember that NaN === NaN returns false
 | ||||
|         // and isNaN(undefined) returns true
 | ||||
|         if (isNaN(x) && isNaN(y) && typeof x === 'number' && typeof y === 'number') { | ||||
|         if (typeof x === 'number' && typeof y === 'number' && isNaN(x) && isNaN(y)) { | ||||
|             return true; | ||||
|         } | ||||
| 
 | ||||
| @@ -414,7 +422,7 @@ function areObjectsEqual() { | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         if (x.prototype !== y.prototype) { | ||||
|         if ((x as any).prototype !== (y as any).prototype) { | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
| @@ -429,7 +437,7 @@ function areObjectsEqual() { | ||||
|             if (y.hasOwnProperty(p) !== x.hasOwnProperty(p)) { | ||||
|                 return false; | ||||
|             } | ||||
|             else if (typeof y[p] !== typeof x[p]) { | ||||
|             else if (typeof (y as any)[p] !== typeof (x as any)[p]) { | ||||
|                 return false; | ||||
|             } | ||||
|         } | ||||
| @@ -438,18 +446,18 @@ function areObjectsEqual() { | ||||
|             if (y.hasOwnProperty(p) !== x.hasOwnProperty(p)) { | ||||
|                 return false; | ||||
|             } | ||||
|             else if (typeof y[p] !== typeof x[p]) { | ||||
|             else if (typeof (y as any)[p] !== typeof (x as any)[p]) { | ||||
|                 return false; | ||||
|             } | ||||
| 
 | ||||
|             switch (typeof (x[p])) { | ||||
|             switch (typeof ((x as any)[p])) { | ||||
|                 case 'object': | ||||
|                 case 'function': | ||||
| 
 | ||||
|                     leftChain.push(x); | ||||
|                     rightChain.push(y); | ||||
| 
 | ||||
|                     if (!compare2Objects(x[p], y[p])) { | ||||
|                     if (!compare2Objects((x as any)[p], (y as any)[p])) { | ||||
|                         return false; | ||||
|                     } | ||||
| 
 | ||||
| @@ -458,7 +466,7 @@ function areObjectsEqual() { | ||||
|                     break; | ||||
| 
 | ||||
|                 default: | ||||
|                     if (x[p] !== y[p]) { | ||||
|                     if ((x as any)[p] !== (y as any)[p]) { | ||||
|                         return false; | ||||
|                     } | ||||
|                     break; | ||||
| @@ -486,10 +494,12 @@ function areObjectsEqual() { | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| function copyHtmlToClipboard(content) { | ||||
|     function listener(e) { | ||||
|         e.clipboardData.setData("text/html", content); | ||||
|         e.clipboardData.setData("text/plain", content); | ||||
| function copyHtmlToClipboard(content: string) { | ||||
|     function listener(e: ClipboardEvent) { | ||||
|         if (e.clipboardData) { | ||||
|             e.clipboardData.setData("text/html", content); | ||||
|             e.clipboardData.setData("text/plain", content); | ||||
|         } | ||||
|         e.preventDefault(); | ||||
|     } | ||||
|     document.addEventListener("copy", listener); | ||||
| @@ -497,11 +507,8 @@ function copyHtmlToClipboard(content) { | ||||
|     document.removeEventListener("copy", listener); | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * @param {FNote} note | ||||
|  * @return {string} | ||||
|  */ | ||||
| function createImageSrcUrl(note) { | ||||
| // TODO: Set to FNote once the file is ported.
 | ||||
| function createImageSrcUrl(note: { noteId: string; title: string }) { | ||||
|     return `api/images/${note.noteId}/${encodeURIComponent(note.title)}?timestamp=${Date.now()}`; | ||||
| } | ||||
| 
 | ||||
							
								
								
									
										46
									
								
								src/public/app/types.d.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								src/public/app/types.d.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,46 @@ | ||||
| import FNote from "./entities/fnote"; | ||||
|  | ||||
| interface ElectronProcess { | ||||
|     type: string; | ||||
| } | ||||
|  | ||||
| interface CustomGlobals { | ||||
|     isDesktop: boolean; | ||||
|     isMobile: boolean; | ||||
|     device: "mobile" | "desktop"; | ||||
|     getComponentsByEl: (el: unknown) => unknown; | ||||
|     getHeaders: Promise<Record<string, string>>; | ||||
|     getReferenceLinkTitle: (href: string) => Promise<string>; | ||||
|     getReferenceLinkTitleSync: (href: string) => string; | ||||
|     getActiveContextNote: FNote; | ||||
|     requireLibrary: (library: string) => Promise<void>; | ||||
|     ESLINT: { js: string[]; }; | ||||
|     appContext: AppContext; | ||||
|     froca: Froca; | ||||
|     treeCache: Froca; | ||||
|     importMarkdownInline: () => Promise<unknown>; | ||||
|     SEARCH_HELP_TEXT: string; | ||||
|     activeDialog: JQuery<HTMLElement> | null; | ||||
| } | ||||
|  | ||||
| type RequireMethod = (moduleName: string) => any; | ||||
|  | ||||
| declare global { | ||||
|     interface Window { | ||||
|         logError(message: string); | ||||
|         logInfo(message: string); | ||||
|      | ||||
|         process?: ElectronProcess; | ||||
|         glob?: CustomGlobals; | ||||
|     } | ||||
|  | ||||
|     interface JQuery { | ||||
|         autocomplete: (action: "close") => void; | ||||
|     } | ||||
|  | ||||
|     declare var logError: (message: string) => void; | ||||
|     declare var logInfo: (message: string) => void; | ||||
|     declare var glob: CustomGlobals; | ||||
|     declare var require: RequireMethod; | ||||
|     declare var __non_webpack_require__: RequireMethod | undefined; | ||||
| } | ||||
| @@ -21,6 +21,7 @@ | ||||
| 	], | ||||
| 	"exclude": ["./node_modules/**/*"], | ||||
| 	"files": [ | ||||
| 		"src/types.d.ts" | ||||
| 		"src/types.d.ts", | ||||
| 		"src/public/app/types.d.ts" | ||||
| 	] | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user